admin can see every post; guest users see only own

This commit is contained in:
waveringana 2022-12-07 04:28:18 +00:00
parent d130b12418
commit 5290f4e960
13 changed files with 266 additions and 213 deletions

View file

@ -1,5 +1,3 @@
import type {MediaRow, UserRow} from './types';
require("dotenv").config(); require("dotenv").config();
import express from "express"; import express from "express";
@ -17,7 +15,7 @@ import authRouter from "./routes/auth";
import indexRouter from "./routes/index"; import indexRouter from "./routes/index";
import adduserRouter from "./routes/adduser"; import adduserRouter from "./routes/adduser";
import {db, createUser} from "./db"; import {db, createUser, MediaRow} from "./db";
let app = express(); let app = express();
let server = http.createServer(app); let server = http.createServer(app);
@ -80,9 +78,15 @@ db.serialize(function() {
db.run("CREATE TABLE IF NOT EXISTS media ( \ db.run("CREATE TABLE IF NOT EXISTS media ( \
id INTEGER PRIMARY KEY, \ id INTEGER PRIMARY KEY, \
path TEXT NOT NULL, \ path TEXT NOT NULL, \
expire INTEGER \ expire INTEGER \, \
username TEXT \
)"); )");
db.run("ALTER TABLE media ADD COLUMN username TEXT", (err) => {
if(err)
return;
}); //TODO, version new DB, run this command when detecting old DB
createUser("admin", process.env.EBPASS || "changeme"); createUser("admin", process.env.EBPASS || "changeme");
}); });

View file

@ -15,3 +15,17 @@ export function createUser(username: string, password: string) {
salt salt
]); ]);
} }
export interface MediaRow {
id? : Number,
path: String,
expire: Number,
username: String
}
export interface UserRow {
id? : Number,
username: String,
hashed_password: any,
salt: any
}

4
app/lib.ts Normal file
View file

@ -0,0 +1,4 @@
export function extension(str: String){
let file = str.split("/").pop();
return [file.substr(0,file.lastIndexOf(".")),file.substr(file.lastIndexOf("."),file.length).toLowerCase()];
}

72
app/multer.ts Normal file
View file

@ -0,0 +1,72 @@
import {Request} from 'express';
import multer, {FileFilterCallback} from 'multer';
import {db, MediaRow} from './db'
import {extension} from './lib'
export type DestinationCallback = (error: Error | null, destination: string) => void
export type FileNameCallback = (error: Error | null, filename: string) => void
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 => {
let nameAndExtension = extension(file.originalname);
console.log(`Uploading ${file}`);
db.all("SELECT * FROM media WHERE path = ?", [nameAndExtension[0] + nameAndExtension[1]], (err: Error, exists: []) => {
if (err) {
console.log(err)
callback(err, null)
}
if (exists.length != 0) {
let suffix = new Date().getTime() / 1000;
if (request.body.title == "" || request.body.title == null || request.body.title == undefined) {
callback(null, nameAndExtension[0] + "-" + suffix + nameAndExtension[1]);
} else {
callback(null, request.body.title + "-" + suffix + nameAndExtension[1]);
}
} else {
if (request.body.title == "" || request.body.title == null || request.body.title == undefined) {
callback(null, nameAndExtension[0] + nameAndExtension[1]);
} else {
callback(null, request.body.title + nameAndExtension[1]);
}
}
});
}
});
export let allowedMimeTypes = [
"image/png",
"image/jpg",
"image/jpeg",
"image/gif",
"image/webp",
"video/mp4",
"video/mov",
"video/webm",
"audio/mpeg",
"audio/ogg"
];
export const fileFilter = (
request: Request,
file: Express.Multer.File,
callback: FileFilterCallback
): void => {
if (allowedMimeTypes.includes(file.mimetype)) {
callback(null, true)
} else {
callback(null, false)
}
}

View file

@ -39,7 +39,7 @@
} }
/* background image by Cole Bemis <https://feathericons.com> */ /* background image by Cole Bemis <https://feathericons.com> */
.nav .user { .user {
padding-left: 20px; 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'%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-repeat: no-repeat;
@ -47,7 +47,16 @@
} }
/* background image by Cole Bemis <https://feathericons.com> */ /* background image by Cole Bemis <https://feathericons.com> */
.nav .logout { .username {
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='%23000' 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;
color: #73AD21;
}
/* background image by Cole Bemis <https://feathericons.com> */
.logout {
padding-left: 20px; 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-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-repeat: no-repeat;
@ -55,7 +64,7 @@
} }
/* background image by Cole Bemis <https://feathericons.com> */ /* background image by Cole Bemis <https://feathericons.com> */
.nav .adduser { .adduser {
padding-left: 20px; 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-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-repeat: no-repeat;

View file

@ -1,11 +1,9 @@
import type {UserRow} from '../types';
import crypto from "crypto"; import crypto from "crypto";
import express from "express"; import express from "express";
import passport from "passport"; import passport from "passport";
import {Strategy as LocalStrategy} from "passport-local"; import {Strategy as LocalStrategy} from "passport-local";
import {db} from "../db"; import {db, UserRow} from "../db";
let router = express.Router(); let router = express.Router();

View file

@ -1,5 +1,4 @@
import type {RequestHandler as Middleware, Router, Request, Response, NextFunction} from 'express'; import type {RequestHandler as Middleware, Request, Response, NextFunction} from 'express';
import multer from "multer"; import multer from "multer";
import express from "express"; import express from "express";
import ffmpeg from "fluent-ffmpeg"; import ffmpeg from "fluent-ffmpeg";
@ -13,70 +12,27 @@ ffmpeg.setFfprobePath(ffprobepath.path);
import fs from "fs"; import fs from "fs";
import {db, createUser} from "../db"; import {extension} from "../lib";
import {db, MediaRow} from "../db";
import {fileStorage, fileFilter} from "../multer";
import {checkAuth, checkSharexAuth, createEmbedData, handleUpload} from "./middleware"; import {checkAuth, checkSharexAuth, createEmbedData, handleUpload} from "./middleware";
import { MediaRow } from '../types';
function extension(str: String){ let upload = multer({ storage: fileStorage /**, fileFilter: fileFilter**/ }); //maybe make this a env variable?
let file = str.split("/").pop();
return [file.substr(0,file.lastIndexOf(".")),file.substr(file.lastIndexOf("."),file.length).toLowerCase()];
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename : (req, file, cb) => {
let nameAndExtension = extension(file.originalname);
db.all("SELECT * FROM media WHERE path = ?", [nameAndExtension[0] + nameAndExtension[1]], (err: Error, exists: []) => {
if (exists.length != 0) {
let suffix = new Date().getTime() / 1000;
if (req.body.title == "" || req.body.title == null || req.body.title == undefined)
cb(null, nameAndExtension[0] + "-" + suffix + nameAndExtension[1]);
else
cb(null, req.body.title + "-" + suffix + nameAndExtension[1]);
} else {
if (req.body.title == "" || req.body.title == null || req.body.title == undefined)
cb(null, nameAndExtension[0] + nameAndExtension[1]);
else
cb(null, req.body.title + nameAndExtension[1]);
}
});
}
});
/**let allowedMimeTypes = [
"image/png",
"image/jpg",
"image/jpeg",
"image/gif",
"image/webp",
"video/mp4",
"video/mov",
"video/webm",
"audio/mpeg",
"audio/ogg"
];
const fileFilter = function(req, file, cb) {
if (allowedMimeTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(null, false);
}
};**/
let upload = multer({ storage: storage /**, fileFilter: fileFilter**/ }); //maybe make this a env variable?
const fetchMedia: Middleware = (req, res, next) => { const fetchMedia: Middleware = (req, res, next) => {
db.all("SELECT * FROM media", (err: Error, rows: []) => { //@ts-ignore
let admin: boolean = req.user.username == "admin" ? true : false
//@ts-ignore
let query: string = admin == true ? "SELECT * FROM media" : `SELECT * FROM media WHERE username = '${req.user.username}'`;
db.all(query, (err:Error, rows: []) => {
if (err) return next(err); if (err) return next(err);
let files = rows.map((row: MediaRow)=> { let files = rows.map((row: MediaRow)=> {
return { return {
id: row.id, id: row.id,
path: row.path, path: row.path,
expire: row.expire, expire: row.expire,
username: row.username,
url: "/" + row.id url: "/" + row.id
}; };
}); });
@ -102,7 +58,7 @@ router.get("/gifv/:file", async (req, res, next) => {
let width; let height; 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") { if (nameAndExtension[1] == ".mp4" || nameAndExtension[1] == ".mov" || nameAndExtension[1] == ".webm" || nameAndExtension[1] == ".gif") {
ffmpeg() ffmpeg()
.input(`uploads/${req.params.file}`) .input(`uploads/${req.params.file}`)
.inputFormat(nameAndExtension[1].substring(1)) .inputFormat(nameAndExtension[1].substring(1))
@ -112,16 +68,6 @@ router.get("/gifv/:file", async (req, res, next) => {
height = data.streams[0].height; height = data.streams[0].height;
return res.render("gifv", { url: url, host: `${req.protocol}://${req.get("host")}`, width: width, height: height }); return res.render("gifv", { url: url, host: `${req.protocol}://${req.get("host")}`, width: width, height: height });
}); });
} else if (nameAndExtension[1] == ".gif") {
ffmpeg()
.input(`uploads/${req.params.file}`)
.inputFormat("gif")
.ffprobe((err: Error, data: ffmpeg.FfprobeData) => {
if (err) return next(err);
width = data.streams[0].width;
height = data.streams[0].height;
return res.render("gifv", { url: url, host: `${req.protocol}://${req.get("host")}`, width: width, height: height });
});
} else { } else {
let imageData = await imageProbe(fs.createReadStream(`uploads/${req.params.file}`)); let 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: imageData.width, height: imageData.height });

View file

@ -11,11 +11,7 @@ import fs from "fs";
import process from "process"; import process from "process";
import {db} from "../db"; import {db} from "../db";
import {extension} from "../lib";
function extension(str: String){
let file = str.split("/").pop();
return [file.substr(0,file.lastIndexOf(".")),file.substr(file.lastIndexOf("."),file.length).toLowerCase()];
}
export const checkAuth: Middleware = (req, res, next) => { export const checkAuth: Middleware = (req, res, next) => {
if (!req.user) { if (!req.user) {
@ -120,11 +116,10 @@ export const handleUpload: Middleware = (req, res, next) => {
let expireDate: Date; let expireDate: Date;
if (req.body.expire) { if (req.body.expire) {
expireDate = new Date(currentdate + (req.body.expire * 24 * 60 * 60 * 1000)); expireDate = new Date(currentdate + (req.body.expire * 24 * 60 * 60 * 1000));
console.log(req.body.expire);
console.log(expireDate);
} else } else
expireDate = null; expireDate = null;
db.run("INSERT INTO media (path, expire) VALUES (?, ?)", [files[file].filename, expireDate], function (err) { //@ts-ignore
db.run("INSERT INTO media (path, expire, username) VALUES (?, ?, ?)", [files[file].filename, expireDate, req.user.username], function (err) {
if (err) { if (err) {
console.log(err); console.log(err);
return next(err); return next(err);

View file

@ -1,12 +0,0 @@
export interface MediaRow {
id? : Number,
path: String,
expire: Number
}
export interface UserRow {
id? : Number,
username: String,
hashed_password: any,
salt: any
}

View file

@ -43,7 +43,9 @@ return string.slice((string.lastIndexOf(".") - 2 >>> 0) + 2);
<p class="dragregion"><input class="" type="file" id="fileupload" name="fileupload"><input type="button" value="Upload" id="submit" onclick="uploadFile()"></p> <p class="dragregion"><input class="" type="file" id="fileupload" name="fileupload"><input type="button" value="Upload" id="submit" onclick="uploadFile()"></p>
<br> <br>
<br> <br>
<p class="dragregion">Select file expiration date: <select name="expire" id="expire"> <p class="dragregion">
Select file expiration date:
<select name="expire" id="expire">
<option value="0.00069">1 minute</option> <option value="0.00069">1 minute</option>
<option value="0.00347">5 minutes</option> <option value="0.00347">5 minutes</option>
<option value="0.0417">1 hour</option> <option value="0.0417">1 hour</option>
@ -53,7 +55,8 @@ return string.slice((string.lastIndexOf(".") - 2 >>> 0) + 2);
<option value="14">14 days</option> <option value="14">14 days</option>
<option value="30">30 days</option> <option value="30">30 days</option>
<option selected value="">never</option> <option selected value="">never</option>
</select></p> </select>
</p>
<p class="dragregion">Click the file to copy the url</p> <p class="dragregion">Click the file to copy the url</p>
</div> </div>
</form> </form>
@ -71,6 +74,10 @@ return string.slice((string.lastIndexOf(".") - 2 >>> 0) + 2);
<source src="/uploads/<%= file.path %>" loading="lazy"> <source src="/uploads/<%= file.path %>" loading="lazy">
</video> </video>
<div class="overlay"> <div class="overlay">
<% if(user.username == "admin" && file.username != "admin") { %>
<small class="username"><%= file.username %></small>
<br>
<% } %>
<a href="/gifv/<%=file.path %>" onclick="copyA(event)">Copy as GIFv</a> <a href="/gifv/<%=file.path %>" onclick="copyA(event)">Copy as GIFv</a>
</div> </div>
</div> </div>
@ -78,14 +85,30 @@ return string.slice((string.lastIndexOf(".") - 2 >>> 0) + 2);
<div class="video"> <div class="video">
<img class="image" src="/uploads/<%=file.path %>" width="100%" onclick="copyURI(event);" loading="lazy"> <img class="image" src="/uploads/<%=file.path %>" width="100%" onclick="copyURI(event);" loading="lazy">
<div class="overlay"> <div class="overlay">
<% if(user.username == "admin" && file.username != "admin") { %>
<small class="username"><%= file.username %></small>
<br>
<% } %>
<a href="/gifv/<%=file.path %>" onclick="copyA(event)">Copy as GIFv</a> <a href="/gifv/<%=file.path %>" onclick="copyA(event)">Copy as GIFv</a>
</div> </div>
</div> </div>
<% } else if (extension(file.path) == ".jpg" || extension(file.path) == ".jpeg" || extension(file.path) == ".png" || extension(file.path) == ".gif" || extension(file.path) == ".webp" ) { %> <% } else if (extension(file.path) == ".jpg" || extension(file.path) == ".jpeg" || extension(file.path) == ".png" || extension(file.path) == ".gif" || extension(file.path) == ".webp" ) { %>
<div class="video">
<img class="image" src="/uploads/<%=file.path %>" width="100%" onclick="copyURI(event)" loading="lazy"> <img class="image" src="/uploads/<%=file.path %>" width="100%" onclick="copyURI(event)" loading="lazy">
<div class="overlay">
<% if(user.username == "admin" && file.username != "admin") { %>
<small class="username"><%= file.username %></small>
<% } %>
</div>
</div>
<% } else {%> <!-- non-media file --> <% } else {%> <!-- non-media file -->
<div class="nonmedia" onclick="copyPath('/uploads/<%=file.path%>')"> <div class="nonmedia" onclick="copyPath('/uploads/<%=file.path%>')">
<p><%=extension(file.path)%> file</p> <p><%=extension(file.path)%> file</p>
<div class="overlay">
<% if(user.username == "admin" && file.username != "admin") { %>
<small class="username"><%= file.username %></small>
<% } %>
</div>
</div> </div>
<% } %> <% } %>
<label><%= file.path %></label> <label><%= file.path %></label>

View file

@ -2,7 +2,7 @@ const { defineConfig } = require("cypress");
module.exports = defineConfig({ module.exports = defineConfig({
e2e: { e2e: {
baseUrl: "http://localhost:3000", baseUrl: "http://localhost:4000",
}, },
chromeWebSecurity: false, chromeWebSecurity: false,
"video": false "video": false

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB