diff --git a/.eslintrc.js b/.eslintrc.js index d77c96b..c217b05 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,21 +1,21 @@ module.exports = { - env: { - browser: true, - es2021: true, - node: true, - }, - extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - overrides: [], - parser: "@typescript-eslint/parser", - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - }, - plugins: ["@typescript-eslint"], - rules: { - indent: ["error", 2, { SwitchCase: 1 }], - "linebreak-style": ["error", "unix"], - quotes: ["error", "double"], - semi: ["error", "always"], - }, + env: { + browser: true, + es2021: true, + node: true, + }, + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + overrides: [], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: ["@typescript-eslint"], + rules: { + indent: ["error", 4, { SwitchCase: 1 }], + "linebreak-style": ["error", "unix"], + quotes: ["error", "double"], + semi: ["error", "always"], + }, }; diff --git a/app/app.ts b/app/app.ts index 9b3ddca..a160a43 100644 --- a/app/app.ts +++ b/app/app.ts @@ -25,17 +25,17 @@ const server = http.createServer(app); const port = normalizePort(process.env.EBPORT || "3000"); function normalizePort(val: string) { - const port = parseInt(val, 10); + const port = parseInt(val, 10); - if (isNaN(port)) { - return val; - } + if (isNaN(port)) { + return val; + } - if (port >= 0) { - return port; - } + if (port >= 0) { + return port; + } - return false; + return false; } app.set("port", port); @@ -44,56 +44,56 @@ server.on("error", onError); server.on("listening", onListening); function onError(error: any) { - if (error.syscall !== "listen") { - throw error; - } + if (error.syscall !== "listen") { + throw error; + } - const bind = typeof port === "string" - ? "Pipe " + port - : "Port " + port; + const bind = typeof port === "string" + ? "Pipe " + port + : "Port " + port; - // handle specific listen errors with friendly messages - switch (error.code) { - case "EACCES": - console.error(bind + " requires elevated privileges"); - process.exit(1); - break; - case "EADDRINUSE": - console.error(bind + " is already in use"); - process.exit(1); - break; - default: - throw error; - } + // handle specific listen errors with friendly messages + switch (error.code) { + case "EACCES": + console.error(bind + " requires elevated privileges"); + process.exit(1); + break; + case "EADDRINUSE": + console.error(bind + " is already in use"); + process.exit(1); + break; + default: + throw error; + } } // Check if there is an existing DB or not, then check if it needs to be updated to new schema db.get("SELECT * FROM sqlite_master WHERE name ='users' and type='table'", async (err, row) => { - if (!row) createDatabase(3); - else checkVersion(); + if (!row) createDatabase(3); + else checkVersion(); }); function checkVersion () { - db.get("PRAGMA user_version", (err: Error, row: any) => { - if (row && row.user_version) { - const version = row.user_version; - if (version != 3) console.log("DATABASE IS OUTDATED"); - updateDatabase(version, 3); - } else { - // Because ver 1 does not have user_version set, we can safely assume that it is ver 1 - updateDatabase(1, 3); - } - }); + db.get("PRAGMA user_version", (err: Error, row: any) => { + if (row && row.user_version) { + const version = row.user_version; + if (version != 3) console.log("DATABASE IS OUTDATED"); + updateDatabase(version, 3); + } else { + // Because ver 1 does not have user_version set, we can safely assume that it is ver 1 + updateDatabase(1, 3); + } + }); } function onListening() { - const addr = server.address(); - const bind = typeof addr === "string" - ? "pipe " + addr - : "port " + addr.port; - console.log("Embedder version: " + version); - console.log("Listening on " + bind); + const addr = server.address(); + const bind = typeof addr === "string" + ? "pipe " + addr + : "port " + addr.port; + console.log("Embedder version: " + version); + console.log("Listening on " + bind); } app.enable("trust proxy"); @@ -104,20 +104,20 @@ app.set("view engine", "ejs"); app.use(express.json()); app.use(express.urlencoded({ - extended: false + extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, "public"))); app.use(express.static(path.join(__dirname, "public"))); app.use(session({ - secret: process.env.EBSECRET || "pleasechangeme", - resave: false, - saveUninitialized: false, - store: new SQLiteStore({ - db: "sessions.db", - dir: "./var/db" - }) as session.Store + secret: process.env.EBSECRET || "pleasechangeme", + resave: false, + saveUninitialized: false, + store: new SQLiteStore({ + db: "sessions.db", + dir: "./var/db" + }) as session.Store })); app.use(passport.authenticate("session")); @@ -129,26 +129,26 @@ app.use("/", settingsRouter); app.use("/uploads", express.static("uploads")); async function prune () { - db.all("SELECT * FROM media", (err: Error, rows: []) => { - console.log("Uploaded files: " + rows.length); - console.log(rows); - }); - - console.log("Vacuuming database..."); - db.run("VACUUM"); - - db.each("SELECT path FROM media WHERE expire < ?", [Date.now()], (err: Error, row: MediaRow) => { - console.log(`Expired row: ${row}`); - fs.unlink(`uploads/${row.path}`, (err) => { - if (err && err.errno == -4058) { - console.log("File already deleted"); - } else { - if (err) console.log(err); - } + db.all("SELECT * FROM media", (err: Error, rows: []) => { + console.log("Uploaded files: " + rows.length); + console.log(rows); }); - }); - await expire("media", "expire", Date.now()); + console.log("Vacuuming database..."); + db.run("VACUUM"); + + db.each("SELECT path FROM media WHERE expire < ?", [Date.now()], (err: Error, row: MediaRow) => { + console.log(`Expired row: ${row}`); + fs.unlink(`uploads/${row.path}`, (err) => { + if (err && err.errno == -4058) { + console.log("File already deleted"); + } else { + if (err) console.log(err); + } + }); + }); + + await expire("media", "expire", Date.now()); } setInterval(prune, 1000 * 60); //prune every minute \ No newline at end of file diff --git a/app/lib/db.ts b/app/lib/db.ts index 0b54403..96c5d46 100644 --- a/app/lib/db.ts +++ b/app/lib/db.ts @@ -9,176 +9,176 @@ export const db = new sqlite3.Database("./var/db/media.db"); /**Create the database schema for the embedders app*/ export function createDatabase(version: number) { - console.log("Creating database"); + console.log("Creating database"); - db.run( - "CREATE TABLE IF NOT EXISTS users ( \ + db.run( + "CREATE TABLE IF NOT EXISTS users ( \ id INTEGER PRIMARY KEY, \ username TEXT UNIQUE, \ hashed_password BLOB, \ expire INTEGER, \ salt BLOB \ )", - () => createUser("admin", process.env.EBPASS || "changeme"), - ); + () => createUser("admin", process.env.EBPASS || "changeme"), + ); - db.run( - "CREATE TABLE IF NOT EXISTS media ( \ + db.run( + "CREATE TABLE IF NOT EXISTS media ( \ id INTEGER PRIMARY KEY, \ path TEXT NOT NULL, \ expire INTEGER, \ username TEXT \ )", - ); + ); - db.run( - "CREATE TABLE IF NOT EXISTS settings ( \ + db.run( + "CREATE TABLE IF NOT EXISTS settings ( \ id INTEGER PRIMARY KEY, \ downsclaing BOOLEAN, \ namerandomization BOOLEAN \ )", - ); + ); - db.run(`PRAGMA user_version = ${version}`); + db.run(`PRAGMA user_version = ${version}`); } /**Updates old Database schema to new */ export function updateDatabase(oldVersion: number, newVersion: number) { - if (oldVersion == 1) { - console.log(`Updating database from ${oldVersion} to ${newVersion}`); - db.run("PRAGMA user_version = 3", (err) => { - if (err) return; - }); + if (oldVersion == 1) { + console.log(`Updating database from ${oldVersion} to ${newVersion}`); + db.run("PRAGMA user_version = 3", (err) => { + if (err) return; + }); - db.run("ALTER TABLE media ADD COLUMN username TEXT", (err) => { - if (err) return; - }); + db.run("ALTER TABLE media ADD COLUMN username TEXT", (err) => { + if (err) return; + }); - db.run("ALTER TABLE users ADD COLUMN expire TEXT", (err) => { - if (err) return; - }); + db.run("ALTER TABLE users ADD COLUMN expire TEXT", (err) => { + if (err) return; + }); - db.run( - "CREATE TABLE IF NOT EXISTS settings ( \ + db.run( + "CREATE TABLE IF NOT EXISTS settings ( \ id INTEGER PRIMARY KEY, \ downsclaing BOOLEAN, \ namerandomization BOOLEAN \ )", - ); - } - if (oldVersion == 2) { - console.log(`Updating database from ${oldVersion} to ${newVersion}`); - db.run("PRAGMA user_version = 3", (err) => { - if (err) return; - }); + ); + } + if (oldVersion == 2) { + console.log(`Updating database from ${oldVersion} to ${newVersion}`); + db.run("PRAGMA user_version = 3", (err) => { + if (err) return; + }); - db.run( - "CREATE TABLE IF NOT EXISTS settings ( \ + db.run( + "CREATE TABLE IF NOT EXISTS settings ( \ id INTEGER PRIMARY KEY, \ downsclaing BOOLEAN, \ namerandomization BOOLEAN \ )", - ); - } + ); + } } /**Inserts into the media table */ export function insertToDB( - filename: string, - expireDate: Date, - username: string, + filename: string, + expireDate: Date, + username: string, ): Promise { - return new Promise((resolve, reject) => { - const params: MediaParams = [filename, expireDate, username]; + return new Promise((resolve, reject) => { + const params: MediaParams = [filename, expireDate, username]; - db.run( - "INSERT INTO media (path, expire, username) VALUES (?, ?, ?)", - params, - function (err) { - if (err) { - console.log(err); - reject(err); - } else { - console.log(`Uploaded ${filename} to database`); - if (expireDate == null) console.log("It will not expire"); - else if (expireDate != null || expireDate != undefined) - console.log(`It will expire on ${expireDate}`); - resolve(); - } - }, - ); - }); + db.run( + "INSERT INTO media (path, expire, username) VALUES (?, ?, ?)", + params, + function (err) { + if (err) { + console.log(err); + reject(err); + } else { + console.log(`Uploaded ${filename} to database`); + if (expireDate == null) console.log("It will not expire"); + else if (expireDate != null || expireDate != undefined) + console.log(`It will expire on ${expireDate}`); + resolve(); + } + }, + ); + }); } /**Searches the database and returns images with partial or exact keysearches */ export function searchImages(imagename: string, partial: boolean) { - return new Promise((resolve, reject) => { - console.log(`searching for ${imagename}`); - }); + return new Promise((resolve, reject) => { + console.log(`searching for ${imagename}`); + }); } export function updateImageName(oldimagename: string, newname: string) { - return new Promise((resolve, reject) => { - console.log(`updating ${oldimagename} to ${newname}`); - }); + return new Promise((resolve, reject) => { + console.log(`updating ${oldimagename} to ${newname}`); + }); } /**Inserts a new user to the database */ export function createUser(username: string, password: string) { - return new Promise((resolve, reject) => { - console.log(`Creating user ${username}`); - const salt = crypto.randomBytes(16); + return new Promise((resolve, reject) => { + console.log(`Creating user ${username}`); + const salt = crypto.randomBytes(16); - db.run( - "INSERT OR IGNORE INTO users (username, hashed_password, salt) VALUES (?, ?, ?)", - [username, crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256"), salt], - ); + db.run( + "INSERT OR IGNORE INTO users (username, hashed_password, salt) VALUES (?, ?, ?)", + [username, crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256"), salt], + ); - resolve(null); - }); + resolve(null); + }); } /**Selects the path for a file given ID */ export function getPath(id: number | string) { - return new Promise((resolve, reject) => { - const query = "SELECT path FROM media WHERE id = ?"; + return new Promise((resolve, reject) => { + const query = "SELECT path FROM media WHERE id = ?"; - db.get(query, [id], (err: Error, path: object) => { - if (err) { - reject(err); - } - resolve(path); + db.get(query, [id], (err: Error, path: object) => { + if (err) { + reject(err); + } + resolve(path); + }); }); - }); } /**Deletes from database given an ID */ export function deleteId(database: string, id: number | string) { - return new Promise((resolve, reject) => { - const query = `DELETE FROM ${database} WHERE id = ?`; + return new Promise((resolve, reject) => { + const query = `DELETE FROM ${database} WHERE id = ?`; - db.run(query, [id], (err: Error) => { - if (err) { - reject(err); - return; - } - resolve(null); + db.run(query, [id], (err: Error) => { + if (err) { + reject(err); + return; + } + resolve(null); + }); }); - }); } /**Expires a database row given a Date in unix time */ export function expire(database: string, column: string, expiration: number) { - return new Promise((resolve, reject) => { - const query = `SELECT * FROM ${database} WHERE ${column} < ?`; + return new Promise((resolve, reject) => { + const query = `SELECT * FROM ${database} WHERE ${column} < ?`; - db.each(query, [expiration], async (err: Error, row: GenericRow) => { - if (err) reject(err); - await deleteId(database, row.id); + db.each(query, [expiration], async (err: Error, row: GenericRow) => { + if (err) reject(err); + await deleteId(database, row.id); - resolve(null); + resolve(null); + }); }); - }); } /**A generic database row */ diff --git a/app/lib/ffmpeg.ts b/app/lib/ffmpeg.ts index f9c215f..489dad7 100644 --- a/app/lib/ffmpeg.ts +++ b/app/lib/ffmpeg.ts @@ -37,7 +37,7 @@ export let currentEncoding: EncodingType = EncodingType.CPU; * @param {EncodingType} type - The encoding type to set. */ export const setEncodingType = (type: EncodingType) => { - currentEncoding = type; + currentEncoding = type; }; /** @@ -52,31 +52,31 @@ export const setEncodingType = (type: EncodingType) => { * @throws Will throw an error if the executable is not found and the installer path is not available. */ const getExecutablePath = ( - envVar: string, - executable: string, - installer: { path: string }, + envVar: string, + executable: string, + installer: { path: string }, ): string => { - if (process.env[envVar]) { - return process.env[envVar]; - } + if (process.env[envVar]) { + return process.env[envVar]; + } - try { - return which.sync(executable); - } catch (error) { - return installer.path; - } + try { + return which.sync(executable); + } catch (error) { + return installer.path; + } }; const ffmpegPath = getExecutablePath( - "EB_FFMPEG_PATH", - "ffmpeg", - ffmpegInstaller, + "EB_FFMPEG_PATH", + "ffmpeg", + ffmpegInstaller, ); const ffprobePath = getExecutablePath( - "EB_FFPROBE_PATH", - "ffprobe", - ffprobeInstaller, + "EB_FFPROBE_PATH", + "ffprobe", + ffprobeInstaller, ); console.log(`Using ffmpeg from path: ${ffmpegPath}`); @@ -86,20 +86,20 @@ ffmpeg.setFfmpegPath(ffmpegPath!); ffmpeg.setFfprobePath(ffprobePath!); const checkEnvForEncoder = () => { - const envEncoder = process.env.EB_ENCODER?.toUpperCase(); + const envEncoder = process.env.EB_ENCODER?.toUpperCase(); - if (envEncoder && Object.keys(EncodingType).includes(envEncoder)) { - setEncodingType( + if (envEncoder && Object.keys(EncodingType).includes(envEncoder)) { + setEncodingType( EncodingType[envEncoder as keyof typeof EncodingType] as EncodingType, - ); - console.log( - `Setting encoding type to ${envEncoder} based on environment variable.`, - ); - } else if (envEncoder) { - console.warn( - `Invalid encoder value "${envEncoder}" in environment variable, defaulting to CPU.`, - ); - } + ); + console.log( + `Setting encoding type to ${envEncoder} based on environment variable.`, + ); + } else if (envEncoder) { + console.warn( + `Invalid encoder value "${envEncoder}" in environment variable, defaulting to CPU.`, + ); + } }; checkEnvForEncoder(); @@ -120,57 +120,57 @@ checkEnvForEncoder(); * }); */ export const ffmpegDownscale = ( - path: string, - filename: string, - extension: string, + path: string, + filename: string, + extension: string, ): Promise => { - const startTime = Date.now(); - const outputOptions = [ - "-vf", - "scale=-2:720", - "-c:v", - currentEncoding, - "-c:a", - "copy", - "-pix_fmt", - "yuv420p", - ]; + const startTime = Date.now(); + const outputOptions = [ + "-vf", + "scale=-2:720", + "-c:v", + currentEncoding, + "-c:a", + "copy", + "-pix_fmt", + "yuv420p", + ]; - return new Promise((resolve, reject) => { - const progressFile = `uploads/${filename}${extension}-progress.json`; + return new Promise((resolve, reject) => { + const progressFile = `uploads/${filename}${extension}-progress.json`; - ffmpeg() - .input(path) - .outputOptions(outputOptions) - .output(`uploads/720p-${filename}${extension}`) - .on("progress", function (progress) { - fs.writeFileSync( - progressFile, - JSON.stringify({ progress: progress.percent / 100 }), - ); - }) - .on("end", () => { - console.log( - `720p copy complete using ${currentEncoding}, took ${ - Date.now() - startTime - }ms to complete`, - ); + ffmpeg() + .input(path) + .outputOptions(outputOptions) + .output(`uploads/720p-${filename}${extension}`) + .on("progress", function (progress) { + fs.writeFileSync( + progressFile, + JSON.stringify({ progress: progress.percent / 100 }), + ); + }) + .on("end", () => { + console.log( + `720p copy complete using ${currentEncoding}, took ${ + Date.now() - startTime + }ms to complete`, + ); - // Delete the .processing file - fs.unlinkSync(progressFile); + // Delete the .processing file + fs.unlinkSync(progressFile); - resolve(); - }) - .on("error", (e) => { - // Ensure to delete the .processing file even on error - if (fs.existsSync(progressFile)) { - fs.unlinkSync(progressFile); - } + resolve(); + }) + .on("error", (e) => { + // Ensure to delete the .processing file even on error + if (fs.existsSync(progressFile)) { + fs.unlinkSync(progressFile); + } - reject(new Error(e)); - }) - .run(); - }); + reject(new Error(e)); + }) + .run(); + }); }; /** @@ -189,80 +189,80 @@ export const ffmpegDownscale = ( * }); */ export const ffmpegConvert = ( - path: string, - filename: string, - extension: string, + path: string, + filename: string, + extension: string, ): Promise => { - const startTime = Date.now(); - const outputOptions = [ - "-vf", - "scale=-2:720", - "-c:v", - currentEncoding, - "-c:a", - "copy", - "-movflags", - "+faststart", - "-pix_fmt", - "yuv420p", - ]; + const startTime = Date.now(); + const outputOptions = [ + "-vf", + "scale=-2:720", + "-c:v", + currentEncoding, + "-c:a", + "copy", + "-movflags", + "+faststart", + "-pix_fmt", + "yuv420p", + ]; - let outputFormat: string; + let outputFormat: string; + + if (videoExtensions.includes(extension)) { + outputFormat = ".gif"; + } else if (extension == ".gif") { + outputFormat = ".mp4"; + } else { + return new Promise((resolve, reject) => { + reject(`Submitted file is neither a video nor a gif: ${path}`); + }); + } - if (videoExtensions.includes(extension)) { - outputFormat = ".gif"; - } else if (extension == ".gif") { - outputFormat = ".mp4"; - } else { return new Promise((resolve, reject) => { - reject(`Submitted file is neither a video nor a gif: ${path}`); + const progressFile = `uploads/${filename}${extension}-progress.json`; + + ffmpeg() + .input(path) + .outputOptions(outputOptions) + .output("uploads/") + .outputFormat(outputFormat) + .output(`uploads/${filename}${outputFormat}`) + .on("progress", function (progress) { + fs.writeFileSync( + progressFile, + JSON.stringify({ progress: progress.percent / 100 }), + ); + }) + .on("end", function () { + console.log( + `Conversion complete, took ${Date.now() - startTime} to complete`, + ); + console.log(`uploads/${filename}${outputFormat}`); + resolve(); + }) + .on("error", (e) => reject(e)) + .run(); }); - } - - return new Promise((resolve, reject) => { - const progressFile = `uploads/${filename}${extension}-progress.json`; - - ffmpeg() - .input(path) - .outputOptions(outputOptions) - .output("uploads/") - .outputFormat(outputFormat) - .output(`uploads/${filename}${outputFormat}`) - .on("progress", function (progress) { - fs.writeFileSync( - progressFile, - JSON.stringify({ progress: progress.percent / 100 }), - ); - }) - .on("end", function () { - console.log( - `Conversion complete, took ${Date.now() - startTime} to complete`, - ); - console.log(`uploads/${filename}${outputFormat}`); - resolve(); - }) - .on("error", (e) => reject(e)) - .run(); - }); }; export const ffProbe = async ( - path: string, - filename: string, - extension: string, + path: string, + filename: string, + extension: string, ) => { - return new Promise((resolve, reject) => { - if ( - !videoExtensions.includes(extension) && + return new Promise((resolve, reject) => { + if ( + !videoExtensions.includes(extension) && !imageExtensions.includes(extension) - ) { - console.log(`Extension is ${extension}`); - reject(`Submitted file is neither a video nor an image: ${path}`); - } + ) { + console.log(`Extension is ${extension}`); + reject(`Submitted file is neither a video nor an image: ${path}`); + } - ffprobe(path, (err, data) => { - if (err) reject(err); - resolve(data); + ffprobe(path, (err, data) => { + if (err) reject(err); + resolve(data); + }); }); - }); }; diff --git a/app/lib/lib.ts b/app/lib/lib.ts index 74576ce..9322aef 100644 --- a/app/lib/lib.ts +++ b/app/lib/lib.ts @@ -11,11 +11,11 @@ declare global { } /**Splits a file name into its name and then its extension */ export function extension(str: string) { - const file = str.split("/").pop(); - return [ - file.substr(0, file.lastIndexOf(".")), - file.substr(file.lastIndexOf("."), file.length).toLowerCase(), - ]; + const file = str.split("/").pop(); + return [ + file.substr(0, file.lastIndexOf(".")), + file.substr(file.lastIndexOf("."), file.length).toLowerCase(), + ]; } /**Type for user data */ export interface User { @@ -37,21 +37,21 @@ export interface oembedObj { } export const videoExtensions = [ - ".mp4", - ".mov", - ".avi", - ".flv", - ".mkv", - ".wmv", - ".webm", + ".mp4", + ".mov", + ".avi", + ".flv", + ".mkv", + ".wmv", + ".webm", ]; export const imageExtensions = [ - ".jpg", - ".jpeg", - ".png", - ".gif", - ".bmp", - ".svg", - ".tiff", - ".webp", + ".jpg", + ".jpeg", + ".png", + ".gif", + ".bmp", + ".svg", + ".tiff", + ".webp", ]; diff --git a/app/lib/middleware.ts b/app/lib/middleware.ts index a4bb6d4..bef0077 100644 --- a/app/lib/middleware.ts +++ b/app/lib/middleware.ts @@ -8,40 +8,40 @@ import { insertToDB } from "./db"; import { ffmpegDownscale, ffProbe } from "./ffmpeg"; export const checkAuth: Middleware = (req, res, next) => { - if (!req.user) { - return res.status(401); - } - next(); + if (!req.user) { + return res.status(401); + } + next(); }; /**Checks shareX auth key */ export const checkSharexAuth: Middleware = (req, res, next) => { - const auth = + const auth = process.env.EBAPI_KEY || process.env.EBPASS || "pleaseSetAPI_KEY"; - let key = null; + let key = null; - if (req.headers["key"]) { - key = req.headers["key"]; - } else { - return res - .status(400) - .send( - "{success: false, message: 'No key provided', fix: 'Provide a key'}", - ); - } + if (req.headers["key"]) { + key = req.headers["key"]; + } else { + return res + .status(400) + .send( + "{success: false, message: 'No key provided', fix: 'Provide a key'}", + ); + } - if (auth != key) { - return res - .status(401) - .send( - "{success: false, message: 'Invalid key', fix: 'Provide a valid key'}", - ); - } + if (auth != key) { + return res + .status(401) + .send( + "{success: false, message: 'Invalid key', fix: 'Provide a valid key'}", + ); + } - const shortKey = key.substr(0, 3) + "..."; - console.log(`Authenicated user with key: ${shortKey}`); + const shortKey = key.substr(0, 3) + "..."; + console.log(`Authenicated user with key: ${shortKey}`); - next(); + next(); }; /** @@ -53,50 +53,50 @@ export const checkSharexAuth: Middleware = (req, res, next) => { * */ export const createEmbedData: Middleware = async (req, res, next) => { - const files = req.files as Express.Multer.File[]; - for (const file in files) { - const [filename, fileExtension] = extension(files[file].filename); - const isMedia = + const files = req.files as Express.Multer.File[]; + for (const file in files) { + const [filename, fileExtension] = extension(files[file].filename); + const isMedia = videoExtensions.includes(fileExtension) || imageExtensions.includes(fileExtension); - const oembed: oembedObj = { - type: "video", - version: "1.0", - provider_name: "embedder", - provider_url: "https://github.com/WaveringAna/embedder", - cache_age: 86400, - html: ``, - }; + const oembed: oembedObj = { + type: "video", + version: "1.0", + provider_name: "embedder", + provider_url: "https://github.com/WaveringAna/embedder", + cache_age: 86400, + html: ``, + }; - if (isMedia) { - let ffProbeData; - try { - ffProbeData = await ffProbe( - `uploads/${files[file].filename}`, - filename, - fileExtension, + if (isMedia) { + let ffProbeData; + try { + ffProbeData = await ffProbe( + `uploads/${files[file].filename}`, + filename, + fileExtension, + ); + } catch (error) { + console.log(`Error: ${error}`); + } + + oembed.width = ffProbeData.streams[0].width; + oembed.height = ffProbeData.streams[0].height; + } + + fs.writeFile( + `uploads/oembed-${filename}${fileExtension}.json`, + JSON.stringify(oembed), + function (err) { + if (err) return next(err); + console.log(`oembed file created ${filename}${fileExtension}.json`); + }, ); - } catch (error) { - console.log(`Error: ${error}`); - } - - oembed.width = ffProbeData.streams[0].width; - oembed.height = ffProbeData.streams[0].height; } - - fs.writeFile( - `uploads/oembed-${filename}${fileExtension}.json`, - JSON.stringify(oembed), - function (err) { - if (err) return next(err); - console.log(`oembed file created ${filename}${fileExtension}.json`); - }, - ); - } - next(); + next(); }; /** @@ -108,59 +108,59 @@ export const createEmbedData: Middleware = async (req, res, next) => { * */ export const convertTo720p: Middleware = (req, res, next) => { - const files = req.files as Express.Multer.File[]; - console.log("convert to 720p running"); - for (const file in files) { - const [filename, fileExtension] = extension(files[file].filename); + const files = req.files as Express.Multer.File[]; + console.log("convert to 720p running"); + for (const file in files) { + const [filename, fileExtension] = extension(files[file].filename); - //Skip if not a video - if (!videoExtensions.includes(fileExtension) && fileExtension !== ".gif") { - console.log(`${files[file].filename} is not a video file`); - continue; + //Skip if not a video + if (!videoExtensions.includes(fileExtension) && fileExtension !== ".gif") { + console.log(`${files[file].filename} is not a video file`); + continue; + } + + console.log(`Creating 720p for ${files[file].filename}`); + + ffmpegDownscale( + `uploads/${filename}${fileExtension}`, + filename, + fileExtension, + ) + .then(() => { + //Nothing for now, can fire event flag that it is done to front end when react conversion is done + }) + .catch((error) => { + console.log(`Error: ${error}`); + }); } - console.log(`Creating 720p for ${files[file].filename}`); - - ffmpegDownscale( - `uploads/${filename}${fileExtension}`, - filename, - fileExtension, - ) - .then(() => { - //Nothing for now, can fire event flag that it is done to front end when react conversion is done - }) - .catch((error) => { - console.log(`Error: ${error}`); - }); - } - - next(); + next(); }; /**Middleware for handling uploaded files. Inserts it into the database */ export const handleUpload: Middleware = async (req, res, next) => { - if (!req.file && !req.files) { - console.log("No files were uploaded"); - return res.status(400).send("No files were uploaded."); - } - - const files = req.files ? (req.files as Express.Multer.File[]) : req.file; - const username = req.user ? req.user.username : "sharex"; - const expireDate: Date = req.body.expire - ? new Date(Date.now() + req.body.expire * 24 * 60 * 60 * 1000) - : null; - - try { - if (files instanceof Array) { - await Promise.all( - files.map((file) => insertToDB(file.filename, expireDate, username)), - ); - } else { - await insertToDB(files.filename, expireDate, username); + if (!req.file && !req.files) { + console.log("No files were uploaded"); + return res.status(400).send("No files were uploaded."); + } + + const files = req.files ? (req.files as Express.Multer.File[]) : req.file; + const username = req.user ? req.user.username : "sharex"; + const expireDate: Date = req.body.expire + ? new Date(Date.now() + req.body.expire * 24 * 60 * 60 * 1000) + : null; + + try { + if (files instanceof Array) { + await Promise.all( + files.map((file) => insertToDB(file.filename, expireDate, username)), + ); + } else { + await insertToDB(files.filename, expireDate, username); + } + next(); + } catch (error) { + console.error("Error in handleUpload:", error); + res.status(500).send("Error processing files."); } - next(); - } catch (error) { - console.error("Error in handleUpload:", error); - res.status(500).send("Error processing files."); - } }; diff --git a/app/lib/multer.ts b/app/lib/multer.ts index cab0845..b3af015 100644 --- a/app/lib/multer.ts +++ b/app/lib/multer.ts @@ -14,126 +14,126 @@ export type FileNameCallback = (error: Error | null, filename: string) => void; let randomizeNames = false; if (process.env["EB_RANDOMIZE_NAMES"] === "true") { - randomizeNames = true; + randomizeNames = true; } console.log(`Randomize names is set ${randomizeNames}`); export const fileStorage = multer.diskStorage({ - destination: ( - request: Request, - file: Express.Multer.File, - callback: DestinationCallback, - ): void => { - callback(null, __dirname + "/../../uploads"); - }, - filename: ( - request: Request, - file: Express.Multer.File, - callback: FileNameCallback, - ): void => { - const [filename, fileExtension] = extension(file.originalname); - console.log(`Uploading ${file}`); - db.all( - "SELECT * FROM media WHERE path = ?", - [filename + fileExtension], - (err: Error, exists: []) => { - if (err) { - console.log(err); - callback(err, null); - } + destination: ( + request: Request, + file: Express.Multer.File, + callback: DestinationCallback, + ): void => { + callback(null, __dirname + "/../../uploads"); + }, + filename: ( + request: Request, + file: Express.Multer.File, + callback: FileNameCallback, + ): void => { + const [filename, fileExtension] = extension(file.originalname); + console.log(`Uploading ${file}`); + db.all( + "SELECT * FROM media WHERE path = ?", + [filename + fileExtension], + (err: Error, exists: []) => { + if (err) { + console.log(err); + callback(err, null); + } - let filenameSet = true; - let existsBool = false; - let suffix: number; + let filenameSet = true; + let existsBool = false; + let suffix: number; - if ( - request.body.title != "" || + if ( + request.body.title != "" || request.body.title != null || request.body.title != undefined - ) { - filenameSet = false; - } + ) { + filenameSet = false; + } - if (exists.length != 0) { - existsBool = true; - suffix = new Date().getTime() / 1000; - } + if (exists.length != 0) { + existsBool = true; + suffix = new Date().getTime() / 1000; + } - console.log(request.body.title); + console.log(request.body.title); - if (randomizeNames) { - //Random string of 8 alphanumeric characters - //Chance of collision is extremely low, not worth checking for - console.log("Randomizing name"); - callback( - null, - Math.random().toString(36).slice(2, 10) + fileExtension, - ); - return; - } + if (randomizeNames) { + //Random string of 8 alphanumeric characters + //Chance of collision is extremely low, not worth checking for + console.log("Randomizing name"); + callback( + null, + Math.random().toString(36).slice(2, 10) + fileExtension, + ); + return; + } - if (filenameSet && existsBool) { - console.log( - `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, - ); - callback(null, request.body.title + "-" + suffix + fileExtension); - return; - } + if (filenameSet && existsBool) { + console.log( + `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, + ); + callback(null, request.body.title + "-" + suffix + fileExtension); + return; + } - if (!filenameSet && existsBool) { - console.log( - `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, - ); - callback(null, filename + "-" + suffix + fileExtension); - return; - } + if (!filenameSet && existsBool) { + console.log( + `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, + ); + callback(null, filename + "-" + suffix + fileExtension); + return; + } - if (filenameSet && !existsBool) { - console.log( - `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, - ); - callback(null, request.body.title + fileExtension); - return; - } + if (filenameSet && !existsBool) { + console.log( + `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, + ); + callback(null, request.body.title + fileExtension); + return; + } - if (!filenameSet && !existsBool) { - console.log( - `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, - ); - callback(null, filename + fileExtension); - return; - } - }, - ); - }, + if (!filenameSet && !existsBool) { + console.log( + `filenameSet is ${filenameSet} and existsBool is ${existsBool}`, + ); + callback(null, filename + fileExtension); + return; + } + }, + ); + }, }); export let allowedMimeTypes = [ - "image/png", - "image/jpg", - "image/jpeg", - "image/gif", - "image/webp", - "video/mp4", - "video/mov", - "video/webm", - "audio/mpeg", - "audio/ogg", + "image/png", + "image/jpg", + "image/jpeg", + "image/gif", + "image/webp", + "video/mp4", + "video/mov", + "video/webm", + "audio/mpeg", + "audio/ogg", ]; export const setAllowedMimeTypes = (mimeTypes: string[]): void => { - allowedMimeTypes = mimeTypes; + allowedMimeTypes = mimeTypes; }; export const fileFilter = ( - request: Request, - file: Express.Multer.File, - callback: FileFilterCallback, + request: Request, + file: Express.Multer.File, + callback: FileFilterCallback, ): void => { - if (allowedMimeTypes.includes(file.mimetype)) { - callback(null, true); - } else { - callback(null, false); - } + if (allowedMimeTypes.includes(file.mimetype)) { + callback(null, true); + } else { + callback(null, false); + } }; diff --git a/app/routes/adduser.ts b/app/routes/adduser.ts index 926b0cb..c91be27 100644 --- a/app/routes/adduser.ts +++ b/app/routes/adduser.ts @@ -6,24 +6,24 @@ import {createUser} from "../lib/db"; const router: Router = express.Router(); /**Middleware to check if a user is actually signed in */ const adminCheck: Middleware = (req: Request, res: Response, next: NextFunction) => { - if (!req.user) - return res.status(403).send("You are not authorized to perform this action"); - else { - if (req.user.username != "admin") - return res.status(403).send("You are not authorized to perform this action"); - } + if (!req.user) + return res.status(403).send("You are not authorized to perform this action"); + else { + if (req.user.username != "admin") + return res.status(403).send("You are not authorized to perform this action"); + } - next(); + next(); }; router.get("/adduser", adminCheck, (req: Request, res: Response) => { - res.locals.filter = null; - res.render("adduser", { user: req.user }); + res.locals.filter = null; + res.render("adduser", { user: req.user }); }); router.post("/adduser", adminCheck, (req: Request, res: Response) => { - createUser(req.body.username, req.body.password); - res.redirect("/"); + createUser(req.body.username, req.body.password); + res.redirect("/"); }); export default router; \ No newline at end of file diff --git a/app/routes/auth.ts b/app/routes/auth.ts index 0a3164f..04fe16b 100644 --- a/app/routes/auth.ts +++ b/app/routes/auth.ts @@ -9,77 +9,77 @@ import { db, UserRow } from "../lib/db"; const router = express.Router(); passport.use( - new LocalStrategy(function verify(username, password, cb) { - db.get( - "SELECT * FROM users WHERE username = ?", - [username], - function (err: Error, row: UserRow) { - if (err) { - return cb(err); - } - if (!row) { - return cb(null, false, { - message: "Incorrect username or password.", - }); - } + new LocalStrategy(function verify(username, password, cb) { + db.get( + "SELECT * FROM users WHERE username = ?", + [username], + function (err: Error, row: UserRow) { + if (err) { + return cb(err); + } + if (!row) { + return cb(null, false, { + message: "Incorrect username or password.", + }); + } - crypto.pbkdf2( - password, - row.salt, - 310000, - 32, - "sha256", - function (err, hashedPassword) { - if (err) { - return cb(err); - } - if (!crypto.timingSafeEqual(row.hashed_password, hashedPassword)) { - return cb(null, false, { - message: "Incorrect username or password.", - }); - } - return cb(null, row); - }, + crypto.pbkdf2( + password, + row.salt, + 310000, + 32, + "sha256", + function (err, hashedPassword) { + if (err) { + return cb(err); + } + if (!crypto.timingSafeEqual(row.hashed_password, hashedPassword)) { + return cb(null, false, { + message: "Incorrect username or password.", + }); + } + return cb(null, row); + }, + ); + }, ); - }, - ); - }), + }), ); passport.serializeUser(function (user: User, cb) { - process.nextTick(function () { - cb(null, { - id: user.id, - username: user.username, + process.nextTick(function () { + cb(null, { + id: user.id, + username: user.username, + }); }); - }); }); passport.deserializeUser(function (user: User, cb) { - process.nextTick(function () { - return cb(null, user); - }); + process.nextTick(function () { + return cb(null, user); + }); }); router.get("/login", function (req, res) { - res.render("login"); + res.render("login"); }); router.post( - "/login/password", - passport.authenticate("local", { - successRedirect: "/", - failureRedirect: "/login", - }), + "/login/password", + passport.authenticate("local", { + successRedirect: "/", + failureRedirect: "/login", + }), ); router.post("/logout", function (req, res, next) { - req.logout(function (err) { - if (err) { - return next(err); - } - res.redirect("/"); - }); + req.logout(function (err) { + if (err) { + return next(err); + } + res.redirect("/"); + }); }); export default router; diff --git a/app/routes/index.ts b/app/routes/index.ts index 7b20ff1..9e796aa 100644 --- a/app/routes/index.ts +++ b/app/routes/index.ts @@ -1,8 +1,8 @@ import type { - RequestHandler as Middleware, - Request, - Response, - NextFunction, + RequestHandler as Middleware, + Request, + Response, + NextFunction, } from "express"; import multer from "multer"; @@ -18,184 +18,184 @@ import { extension, videoExtensions } from "../lib/lib"; import { db, MediaRow, getPath, deleteId } from "../lib/db"; import { fileStorage } from "../lib/multer"; import { - checkAuth, - checkSharexAuth, - convertTo720p, - createEmbedData, - handleUpload, + checkAuth, + checkSharexAuth, + convertTo720p, + createEmbedData, + handleUpload, } from "../lib/middleware"; const upload = multer({ storage: fileStorage /**, fileFilter: fileFilter**/ }); //maybe make this a env variable? /**Middleware to grab media from media database */ const fetchMedia: Middleware = (req, res, next) => { - const admin: boolean = req.user.username == "admin" ? true : false; - /**Check if the user is an admin, if so, show all posts from all users */ - const query: string = + const admin: boolean = req.user.username == "admin" ? true : false; + /**Check if the user is an admin, if so, show all posts from all users */ + const query: string = admin == true - ? "SELECT * FROM media" - : `SELECT * FROM media WHERE username = '${req.user.username}'`; + ? "SELECT * FROM media" + : `SELECT * FROM media WHERE username = '${req.user.username}'`; - db.all(query, (err: Error, rows: []) => { - if (err) return next(err); - const files = rows.map((row: MediaRow) => { - return { - id: row.id, - path: row.path, - expire: row.expire, - username: row.username, - url: "/" + row.id, - }; + db.all(query, (err: Error, rows: []) => { + if (err) return next(err); + const files = rows.map((row: MediaRow) => { + return { + id: row.id, + path: row.path, + expire: row.expire, + username: row.username, + url: "/" + row.id, + }; + }); + res.locals.files = files.reverse(); //reverse so newest files appear first + res.locals.Count = files.length; + next(); }); - res.locals.files = files.reverse(); //reverse so newest files appear first - res.locals.Count = files.length; - next(); - }); }; const router = express.Router(); router.get( - "/", - (req: Request, res: Response, next: NextFunction) => { - if (!req.user) return res.render("home"); - next(); - }, - fetchMedia, - (req: Request, res: Response) => { - res.locals.filter = null; - res.render("index", { user: req.user }); - } + "/", + (req: Request, res: Response, next: NextFunction) => { + if (!req.user) return res.render("home"); + next(); + }, + fetchMedia, + (req: Request, res: Response) => { + res.locals.filter = null; + res.render("index", { user: req.user }); + } ); router.get("/media-list", fetchMedia, (req: Request, res: Response) => { - res.render("partials/_fileList", { user: req.user }); + res.render("partials/_fileList", { user: req.user }); }); router.get( - "/gifv/:file", - async (req: Request, res: Response, next: NextFunction) => { - const url = `${req.protocol}://${req.get("host")}/uploads/${ - req.params.file - }`; - let width; - let height; + "/gifv/:file", + async (req: Request, res: Response, next: NextFunction) => { + const url = `${req.protocol}://${req.get("host")}/uploads/${ + req.params.file + }`; + let width; + let height; - const [filename, fileExtension] = extension(`uploads/${req.params.file}`); - if ( - fileExtension == ".mp4" || + const [filename, fileExtension] = extension(`uploads/${req.params.file}`); + if ( + fileExtension == ".mp4" || fileExtension == ".mov" || fileExtension == ".webm" || fileExtension == ".gif" - ) { - const imageData = ffProbe( - `uploads/${req.params.file}`, - filename, - fileExtension - ); + ) { + const imageData = ffProbe( + `uploads/${req.params.file}`, + filename, + fileExtension + ); - width = (await imageData).streams[0].width; - height = (await imageData).streams[0].height; + width = (await imageData).streams[0].width; + height = (await imageData).streams[0].height; - return res.render("gifv", { - url: url, - host: `${req.protocol}://${req.get("host")}`, - width: width, - height: height, - }); - } else { - const imageData = await imageProbe( - fs.createReadStream(`uploads/${req.params.file}`) - ); - return res.render("gifv", { - url: url, - host: `${req.protocol}://${req.get("host")}`, - width: imageData.width, - height: imageData.height, - }); + return res.render("gifv", { + url: url, + host: `${req.protocol}://${req.get("host")}`, + width: width, + height: height, + }); + } else { + const imageData = await imageProbe( + fs.createReadStream(`uploads/${req.params.file}`) + ); + return res.render("gifv", { + url: url, + host: `${req.protocol}://${req.get("host")}`, + width: imageData.width, + height: imageData.height, + }); + } } - } ); router.post( - "/", - [ - checkAuth, - upload.array("fileupload"), - convertTo720p, - createEmbedData, - handleUpload, - fetchMedia, - ], - (req: Request, res: Response) => { - return res.render("partials/_fileList", { user: req.user }); // Render only the file list partial - } + "/", + [ + checkAuth, + upload.array("fileupload"), + convertTo720p, + createEmbedData, + handleUpload, + fetchMedia, + ], + (req: Request, res: Response) => { + return res.render("partials/_fileList", { user: req.user }); // Render only the file list partial + } ); router.post( - "/sharex", - [checkSharexAuth, upload.single("fileupload"), createEmbedData, handleUpload], - (req: Request, res: Response) => { - return res.send( - `${req.protocol}://${req.get("host")}/uploads/${req.file.filename}` - ); - } + "/sharex", + [checkSharexAuth, upload.single("fileupload"), createEmbedData, handleUpload], + (req: Request, res: Response) => { + return res.send( + `${req.protocol}://${req.get("host")}/uploads/${req.file.filename}` + ); + } ); router.get( - "/:id(\\d+)/delete", - [checkAuth], - async (req: Request, res: Response, next: NextFunction) => { - const filename: any = await getPath(req.params.id); - const filePath = path.join(__dirname , "../../uploads/" + filename.path); - const oembed = path.join( - __dirname , "../../uploads/oembed-" + filename.path + ".json" - ); + "/:id(\\d+)/delete", + [checkAuth], + async (req: Request, res: Response, next: NextFunction) => { + const filename: any = await getPath(req.params.id); + const filePath = path.join(__dirname , "../../uploads/" + filename.path); + const oembed = path.join( + __dirname , "../../uploads/oembed-" + filename.path + ".json" + ); - const [fileName, fileExtension] = extension(filePath); - const filesToDelete = [filePath, oembed]; + const [fileName, fileExtension] = extension(filePath); + const filesToDelete = [filePath, oembed]; - if ( - videoExtensions.includes(fileExtension) || + if ( + videoExtensions.includes(fileExtension) || fileExtension == ".gif" - ) { - filesToDelete.push( - path.join(__dirname , "../../uploads/720p-" + filename.path) - ); - } + ) { + filesToDelete.push( + path.join(__dirname , "../../uploads/720p-" + filename.path) + ); + } - // Wait for all file deletions and database operations to complete - await Promise.all( - filesToDelete.map(async (path) => { - return new Promise((resolve, reject) => { - fs.unlink(path, async (err) => { - console.log(`Deleting ${path}`); - if (err) { - if ([-4058, -2].includes(err.errno)) { - //file not found - console.log("File not found, deleting from database"); - await deleteId("media", req.params.id); - } - console.error(`Error deleting file ${path}:`, err); - reject(err); - return; - } - await deleteId("media", req.params.id); - resolve(); - }); + // Wait for all file deletions and database operations to complete + await Promise.all( + filesToDelete.map(async (path) => { + return new Promise((resolve, reject) => { + fs.unlink(path, async (err) => { + console.log(`Deleting ${path}`); + if (err) { + if ([-4058, -2].includes(err.errno)) { + //file not found + console.log("File not found, deleting from database"); + await deleteId("media", req.params.id); + } + console.error(`Error deleting file ${path}:`, err); + reject(err); + return; + } + await deleteId("media", req.params.id); + resolve(); + }); + }); + }) + ).catch((err) => { + console.error("Error deleting files:", err); + return next(err); }); - }) - ).catch((err) => { - console.error("Error deleting files:", err); - return next(err); - }); - next(); - }, - [fetchMedia], - (req: Request, res: Response) => { - return res.render("partials/_fileList", { user: req.user }); - } + next(); + }, + [fetchMedia], + (req: Request, res: Response) => { + return res.render("partials/_fileList", { user: req.user }); + } ); export default router; diff --git a/app/routes/settings.ts b/app/routes/settings.ts index 5d83ca0..95e924f 100644 --- a/app/routes/settings.ts +++ b/app/routes/settings.ts @@ -20,7 +20,7 @@ const fetchUsers = (): Promise<[UserRow]> => { resolve(rows); }); }); -} +}; const fetchSettings: Middleware = async (req, res, next) => { res.locals.users = req.user.username == "admin" ? await fetchUsers() : null;