diff --git a/app/app.ts b/app/app.ts index 1fa2ff4..32d54f7 100644 --- a/app/app.ts +++ b/app/app.ts @@ -15,9 +15,9 @@ import path from "path"; import authRouter from "./routes/auth"; import indexRouter from "./routes/index"; +import adduserRouter from "./routes/adduser"; -import {createUser} from "./db"; -import db from "./db" +import {db, createUser} from "./db"; let app = express(); let server = http.createServer(app); @@ -122,6 +122,7 @@ app.use(passport.authenticate("session")); app.use("/", indexRouter); app.use("/", authRouter); +app.use("/", adduserRouter); app.use("/uploads", express.static("uploads")); diff --git a/app/db.ts b/app/db.ts index d7f8c13..09a348b 100644 --- a/app/db.ts +++ b/app/db.ts @@ -1,13 +1,11 @@ -import type {RequestHandler as Middleware} from 'express'; - -const sqlite3 = require("sqlite3"); -const mkdirp = require("mkdirp"); -const crypto = require("crypto"); +import sqlite3 from "sqlite3"; +import mkdirp from "mkdirp"; +import crypto from "crypto"; mkdirp.sync("./uploads"); mkdirp.sync("./var/db"); -let db = new sqlite3.Database("./var/db/media.db"); +export const db = new sqlite3.Database("./var/db/media.db"); export function createUser(username: string, password: string) { var salt = crypto.randomBytes(16); @@ -16,6 +14,4 @@ export function createUser(username: string, password: string) { crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256"), salt ]); -} - -export default db; +} \ No newline at end of file diff --git a/app/public/css/app.css b/app/public/css/app.css index f871b6d..f7deb68 100644 --- a/app/public/css/app.css +++ b/app/public/css/app.css @@ -41,7 +41,7 @@ /* background image by Cole Bemis */ .nav .user { padding-left: 20px; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-user'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-user'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: center left; } @@ -49,7 +49,15 @@ /* background image by Cole Bemis */ .nav .logout { padding-left: 20px; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-log-out'%3E%3Cpath d='M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4'%3E%3C/path%3E%3Cpolyline points='16 17 21 12 16 7'%3E%3C/polyline%3E%3Cline x1='21' y1='12' x2='9' y2='12'%3E%3C/line%3E%3C/svg%3E%0A"); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-log-out'%3E%3Cpath d='M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4'%3E%3C/path%3E%3Cpolyline points='16 17 21 12 16 7'%3E%3C/polyline%3E%3Cline x1='21' y1='12' x2='9' y2='12'%3E%3C/line%3E%3C/svg%3E%0A"); + background-repeat: no-repeat; + background-position: center left; +} + +/* background image by Cole Bemis */ +.nav .adduser { + padding-left: 20px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-user-plus'%3E%3Cpath d='M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3Ccircle cx='8.5' cy='7' r='4'%3E%3C/circle%3E%3Cline x1='20' y1='8' x2='20' y2='14'%3E%3C/line%3E%3Cline x1='23' y1='11' x2='17' y2='11'%3E%3C/line%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: center left; } diff --git a/app/public/js/index.js b/app/public/js/index.js index 6033f43..bb96da2 100644 --- a/app/public/js/index.js +++ b/app/public/js/index.js @@ -131,8 +131,6 @@ function uploadFile(file) { }); if (file == null || file == undefined) { - //file = reader.readAsDataURL(document.getElementById("fileupload").files[0]); - //file = reader.readAsDataURL(document.querySelector("#fileupload").files[0]); file = document.querySelector("#fileupload").files[0]; } diff --git a/app/routes/adduser.ts b/app/routes/adduser.ts new file mode 100644 index 0000000..b63276f --- /dev/null +++ b/app/routes/adduser.ts @@ -0,0 +1,30 @@ +import type {RequestHandler as Middleware, Router, Request, Response, NextFunction} from 'express'; +import express from "express"; + +import {db, createUser} from "../db"; + +const router: Router = express.Router(); + +const adminCheck: Middleware = (req: Request, res: Response, next: NextFunction) => { + //@ts-ignore + if (!req.user) + return res.status(403).send("You are not authorized to perform this action"); + else { + //@ts-ignore + if (req.user.username != "admin") + return res.status(403).send("You are not authorized to perform this action"); + next(); + } +} + +router.get("/adduser", adminCheck, (req: Request, res: Response, next: NextFunction) => { + res.locals.filter = null; + res.render("adduser", { user: req.user }); +}); + +router.post("/adduser", adminCheck, (req: Request, res: Response, next: NextFunction) => { + createUser(req.body.username, req.body.password); + res.redirect('/'); +}); + +export default router; \ No newline at end of file diff --git a/app/routes/auth.ts b/app/routes/auth.ts index 1356dd8..b6bb312 100644 --- a/app/routes/auth.ts +++ b/app/routes/auth.ts @@ -1,13 +1,11 @@ -import type {MediaRow, UserRow} from '../types'; -import type {RequestHandler as Middleware} from 'express'; +import type {UserRow} from '../types'; import crypto from "crypto"; import express from "express"; import passport from "passport"; +import {Strategy as LocalStrategy} from "passport-local"; -import { Strategy as LocalStrategy } from "passport-local"; - -import db from "../db"; +import {db} from "../db"; let router = express.Router(); diff --git a/app/routes/index.ts b/app/routes/index.ts index 7b49ba0..05cef1e 100644 --- a/app/routes/index.ts +++ b/app/routes/index.ts @@ -1,5 +1,4 @@ -import type {RequestHandler as Middleware, Router, Request, Response} from 'express'; -import types from 'multer'; +import type {RequestHandler as Middleware, Router, Request, Response, NextFunction} from 'express'; import multer from "multer"; import express from "express"; @@ -14,8 +13,8 @@ ffmpeg.setFfprobePath(ffprobepath.path); import fs from "fs"; -import db from "../db"; -import {checkAuth, convert, handleUpload} from "./middleware"; +import {db, createUser} from "../db"; +import {checkAuth, checkSharexAuth, createEmbedData, handleUpload} from "./middleware"; import { MediaRow } from '../types'; function extension(str: String){ @@ -89,13 +88,12 @@ const fetchMedia: Middleware = (req, res, next) => { let router = express.Router(); -router.get("/", (req, res, next) => { - // @ts-ignore, user is part of req header - if (!req.user) { return res.render("home"); } +router.get("/", (req: Request, res: Response, next: NextFunction) => { + if (!req.user) + return res.render("home") next(); -}, fetchMedia, (req, res) => { +}, fetchMedia, (req: Request, res: Response) => { res.locals.filter = null; - // @ts-ignore, user is part of req header res.render("index", { user: req.user }); }); @@ -103,10 +101,10 @@ router.get("/gifv/:file", async (req, res, next) => { let url = `${req.protocol}://${req.get("host")}/uploads/${req.params.file}`; let width; let height; - let nameAndExtension = extension("uploads/" + req.params.file); + let nameAndExtension = extension(`uploads/${req.params.file}`); if (nameAndExtension[1] == ".mp4" || nameAndExtension[1] == ".mov" || nameAndExtension[1] == ".webm") { ffmpeg() - .input("uploads/" + req.params.file) + .input(`uploads/${req.params.file}`) .inputFormat(nameAndExtension[1].substring(1)) .ffprobe((err: Error, data: ffmpeg.FfprobeData) => { if (err) return next(err); @@ -116,7 +114,7 @@ router.get("/gifv/:file", async (req, res, next) => { }); } else if (nameAndExtension[1] == ".gif") { ffmpeg() - .input("uploads/" + req.params.file) + .input(`uploads/${req.params.file}`) .inputFormat("gif") .ffprobe((err: Error, data: ffmpeg.FfprobeData) => { if (err) return next(err); @@ -130,16 +128,16 @@ router.get("/gifv/:file", async (req, res, next) => { } }); -router.post("/", [upload.array("fileupload"), convert, handleUpload], (req: Request, res: Response) => { - return res.redirect("/"); +router.post("/", [checkAuth, upload.array("fileupload"), createEmbedData, handleUpload], (req: Request, res: Response) => { + res.redirect("/") }); -router.post("/sharex", [checkAuth, upload.array("fileupload"), convert, handleUpload], (req: Request, res: Response) => { +router.post("/sharex", [checkSharexAuth, upload.array("fileupload"), createEmbedData, handleUpload], (req: Request, res: Response) => { // @ts-ignore return res.send(`${req.protocol}://${req.get("host")}/uploads/${req.files[0].filename}`); }); -router.post("/:id(\\d+)/delete", (req, res, next) => { +router.post("/:id(\\d+)/delete", [checkAuth], (req: Request, res: Response, next: NextFunction) => { db.all("SELECT path FROM media WHERE id = ?", [ req.params.id ], (err: Error, path: Array) => { if (err) { return next(err); } fs.unlink(`uploads/${path[0].path}`, (err => { diff --git a/app/routes/middleware.ts b/app/routes/middleware.ts index 6b2a322..f94278c 100644 --- a/app/routes/middleware.ts +++ b/app/routes/middleware.ts @@ -10,15 +10,21 @@ ffmpeg.setFfprobePath(ffprobepath.path); import fs from "fs"; import process from "process"; -import db from "../db"; +import {db} from "../db"; function extension(str: String){ let file = str.split("/").pop(); return [file.substr(0,file.lastIndexOf(".")),file.substr(file.lastIndexOf("."),file.length).toLowerCase()]; } -//Checks ShareX key -export const checkAuth: Middleware = (req: Request, res: Response, next: Function) => { +export const checkAuth: Middleware = (req, res, next) => { + if (!req.user) { + return res.status(401); + } + next(); +} + +export const checkSharexAuth: Middleware = (req, res, next) => { let auth = process.env.EBAPI_KEY || process.env.EBPASS || "pleaseSetAPI_KEY"; let key = null; @@ -38,8 +44,8 @@ export const checkAuth: Middleware = (req: Request, res: Response, next: Functio next(); } -//Converts mp4 to gif and vice versa with ffmpeg -export const convert: Middleware = (req: Request, res: Response, next: Function) => { +//createEmbedDatas mp4 to gif and vice versa with ffmpeg +export const createEmbedData: Middleware = (req, res, next) => { for (let file in req.files) { // @ts-ignore let nameAndExtension = extension(req.files[file].originalname); @@ -58,13 +64,20 @@ export const convert: Middleware = (req: Request, res: Response, next: Function) if (err) return next(err); console.log(`oembed file created ${nameAndExtension[0]}${nameAndExtension[1]}.json`); }); - - /**if (nameAndExtension[1] == ".mp4") { + } + next(); +} + +export const convert: Middleware = (req, res, next) => { + for (let file in req.files) { + // @ts-ignore + let nameAndExtension = extension(req.files[file].originalname); + if (nameAndExtension[1] == ".mp4" || nameAndExtension[1] == ".webm" || nameAndExtension[1] == ".mkv" || nameAndExtension[1] == ".avi" || nameAndExtension[1] == ".mov") { console.log("Converting " + nameAndExtension[0] + nameAndExtension[1] + " to gif"); console.log(nameAndExtension[0] + nameAndExtension[1]); ffmpeg() .input(`uploads/${nameAndExtension[0]}${nameAndExtension[1]}`) - .inputFormat("mp4") + .inputFormat(nameAndExtension[1].substring(1)) .outputFormat("gif") .output(`uploads/${nameAndExtension[0]}.gif`) .on("end", function() { @@ -90,13 +103,11 @@ export const convert: Middleware = (req: Request, res: Response, next: Function) console.log(`Uploaded to uploads/${nameAndExtension[0]}.mp4`); }) .run(); - }**/ + } } - - next(); } - -export const handleUpload: Middleware = (req: Request, res: Response, next: Function) => { + +export const handleUpload: Middleware = (req, res, next) => { if (!req.files || Object.keys(req.files).length === 0) { console.log("No files were uploaded"); return res.status(400).send("No files were uploaded."); diff --git a/app/views/adduser.ejs b/app/views/adduser.ejs new file mode 100644 index 0000000..751d024 --- /dev/null +++ b/app/views/adduser.ejs @@ -0,0 +1,37 @@ + + + + + + Embedder + + + + + + + + + +
+

Embedder

+

Add User

+
+
+ + +
+
+ + +
+ +
+
+
+ + + diff --git a/app/views/index.ejs b/app/views/index.ejs index cb2de8c..bde9c2a 100644 --- a/app/views/index.ejs +++ b/app/views/index.ejs @@ -27,6 +27,11 @@ return string.slice((string.lastIndexOf(".") - 2 >>> 0) + 2); + <% if (user.name == "admin" || user.username == "admin") { %> +
  • + +
  • + <% } %>
    diff --git a/package.json b/package.json index c5d10ee..612526a 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "copy-files": "copyfiles -a -u 1 app/public/* app/views/* app/public/**/* app/views/**/* dist/", "tsc": "tsc", "postinstall": "npm run tsc && npm run copy-files", - "dev": "tsc-node-dev --respawn --pretty --transpile-only index.ts" + "build": "npm run clean && npm run copy-files && npm run tsc" }, "dependencies": { "@ffmpeg-installer/ffmpeg": "^1.1.0",