my nvim and vscode are finally using the same settings
This commit is contained in:
parent
58077a5d63
commit
a5e03facbe
11 changed files with 757 additions and 757 deletions
142
app/app.ts
142
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
|
194
app/lib/db.ts
194
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<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 */
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
];
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue