my nvim and vscode are finally using the same settings

This commit is contained in:
waveringana 2024-01-25 13:03:29 -05:00
parent 58077a5d63
commit a5e03facbe
11 changed files with 757 additions and 757 deletions

View file

@ -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"],
},
};

View file

@ -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

View file

@ -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<void> {
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 */

View file

@ -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<void> => {
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<void>((resolve, reject) => {
const progressFile = `uploads/${filename}${extension}-progress.json`;
return new Promise<void>((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<void> => {
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<void>((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<void>((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<void>((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<FfprobeData>((resolve, reject) => {
if (
!videoExtensions.includes(extension) &&
return new Promise<FfprobeData>((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);
});
});
});
};

View file

@ -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",
];

View file

@ -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: `<iframe src='${req.protocol}://${req.get(
"host",
)}/gifv/${filename}${fileExtension}'></iframe>`,
};
const oembed: oembedObj = {
type: "video",
version: "1.0",
provider_name: "embedder",
provider_url: "https://github.com/WaveringAna/embedder",
cache_age: 86400,
html: `<iframe src='${req.protocol}://${req.get(
"host",
)}/gifv/${filename}${fileExtension}'></iframe>`,
};
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.");
}
};

View file

@ -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);
}
};

View file

@ -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;

View file

@ -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;

View file

@ -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<void>((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<void>((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;

View file

@ -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;