diff --git a/.gitignore b/.gitignore
index f9a75c5..3190fb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules/
videos/*
-
+!videos/.gitkeep
+tmp/
diff --git a/index.js b/index.js
index 498265a..1b0d56c 100644
--- a/index.js
+++ b/index.js
@@ -4,6 +4,9 @@ import bcrypt from "bcrypt";
import fileUpload from "express-fileupload";
import path from "path";
import childProcess from "child_process";
+import levenshtein from "js-levenshtein";
+import cookieParser from "cookie-parser";
+import { fileURLToPath } from 'url';
const users = [];
let sessions = [];
@@ -13,14 +16,19 @@ function randomString(length) {
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
let result = "";
for (let i = 0; i < length; ++i) {
- result += chars[chars.length * Math.random()];
+ result += chars[Math.floor(chars.length * Math.random())];
}
return result;
}
+function dirname() {
+ return path.dirname(fileURLToPath(import.meta.url));
+}
+
const app = express();
app.use(cors());
app.use(express.json());
+app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
app.use("/", express.static("public/"));
@@ -32,12 +40,12 @@ app.post("/api/register", async (req, res) => {
return res.status(400).json({ ok: false, error: "bad request" });
}
const existingUser = users.find(user => user.username === username);
- if (existingUser === undefined) {
+ if (existingUser !== undefined) {
return res.status(400).json({ ok: false, error: "username taken" });
}
const passwordHash = await bcrypt.hash(password, 10);
const id = users.length;
- const user = { id, username, passwordHash };
+ const user = { id, username, password: passwordHash };
users.push(user);
return res.status(200).json({ ok: true, user });
});
@@ -63,10 +71,33 @@ app.post("/api/login", async (req, res) => {
return res.status(200).json({ ok: true, session });
});
+
+app.get("/api/search", async (req, res) => {
+ const page = +req.query.page || 0;
+ const search = req.query.query;
+ if (!search) {
+ return res.status(400).json({ ok: false, error: "bad request" });
+ }
+ const [start, end] = [20 * page, 20 * (page + 1)];
+ const withDistance = videos
+ .map(video => ({dist: levenshtein(search, video.title), ...video}));
+ withDistance.sort((a, b) => a.dist - b.dist);
+ const returnedVideos = withDistance
+ .slice(start, end)
+ .map(video => {
+ const user = users.find(user => user.id === video.userId);
+ if (!user) {
+ return {...video, author: "[Liberal]"};
+ }
+ return {...video, author: user.username};
+ });
+ return res.status(200).json({ ok: true, videos: returnedVideos, total: videos.length });
+});
+
function authorized() {
return (req, res, next) => {
const token = (() => {
- if ("token" in req.cookies) {
+ if (req.cookies && "token" in req.cookies) {
return req.cookies["token"];
} else if ("token" in req.query) {
return req.query["token"];
@@ -77,13 +108,13 @@ function authorized() {
}
})();
if (token === null) {
- return res.status(400).json({ ok: false, error: "unathorized" });
+ return res.status(400).json({ ok: false, error: "unauthorized" });
}
const session = sessions.find(session => session.token === token);
if (session === undefined) {
- return res.status(400).json({ ok: false, error: "unathorized" });
+ return res.status(400).json({ ok: false, error: "unauthorized" });
}
- const user = user.find(user => user.id === session.id);
+ const user = users.find(user => user.id === session.userId);
if (user === undefined) {
throw new Error("error: session with invalid userId");
}
@@ -99,26 +130,27 @@ app.get("/api/logout", authorized(), (req, res) => {
app.post("/api/upload_video", authorized(), fileUpload({ limits: { fileSize: 2 ** 26 }, useTempFiles: true }), async (req, res) => {
const { title } = req.body;
- if (req.files === undefined || req.files === null || req.files.length !== 1) {
+ if (!req.files || !req.files.video) {
return res.status(400).json({ ok: false, error: "bad request" });
}
- if (req.files[0].mimetype !== "video/mp4") {
+ if (req.files.video.mimetype !== "video/mp4") {
return res.status(400).json({ ok: false, error: "bad mimetype" });
}
const userId = req.user.id;
const id = randomString(4);
- const tempPath = req.files[0].tempFilePath;
- const newPath = path.join("/videos", id, ".mp4");
+ const tempPath = req.files.video.tempFilePath;
+ const newPath = path.join(dirname(), "videos", `${id}.mp4`);
+ console.log(newPath);
const exitCode = await new Promise((resolve, _reject) => {
- const process = childProcess.spawn("HandBrakeCLI", ["-i", tempPath, "-o", newPath, "-Z", "Social 25 MB 5 Minutes 360p60"]);
+ const process = childProcess.spawn("HandBrakeCLI", ["-i", tempPath, "-o", newPath, "-Z", "Social 50 MB 10 Minutes 480p30"]);
process.stderr.on("data", (data) => {
- conole.error(data);
+ console.error(data.toString());
});
process.on("close", (code) => {
resolve(code);
})
- });
+ })
if (exitCode !== 0) {
throw new Error("handbrake failed");
}
diff --git a/package-lock.json b/package-lock.json
index 0d304d4..0f269a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,9 +10,11 @@
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.1",
+ "cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"express": "^4.18.2",
- "express-fileupload": "^1.4.3"
+ "express-fileupload": "^1.4.3",
+ "js-levenshtein": "^1.1.6"
}
},
"node_modules/@mapbox/node-pre-gyp": {
@@ -248,6 +250,26 @@
"node": ">= 0.6"
}
},
+ "node_modules/cookie-parser": {
+ "version": "1.4.6",
+ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+ "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
+ "dependencies": {
+ "cookie": "0.4.1",
+ "cookie-signature": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/cookie-parser/node_modules/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -668,6 +690,14 @@
"node": ">=8"
}
},
+ "node_modules/js-levenshtein": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
+ "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
diff --git a/package.json b/package.json
index 75792e9..557fd78 100644
--- a/package.json
+++ b/package.json
@@ -12,8 +12,10 @@
"type": "module",
"dependencies": {
"bcrypt": "^5.1.1",
+ "cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"express": "^4.18.2",
- "express-fileupload": "^1.4.3"
+ "express-fileupload": "^1.4.3",
+ "js-levenshtein": "^1.1.6"
}
}
diff --git a/public/header.js b/public/header.js
new file mode 100644
index 0000000..90f7a86
--- /dev/null
+++ b/public/header.js
@@ -0,0 +1,37 @@
+function displayHeader() {
+ const links = [
+ {
+ href: "/",
+ name: "Home",
+ },
+ {
+ href: "/register/",
+ name: "Register",
+ },
+ {
+ href: "/login/",
+ name: "Login",
+ },
+ {
+ href: "/upload/",
+ name: "Upload",
+ },
+ ].map(({name, href}) => {
+ if (href === window.location.pathname) {
+ return `${name}`
+ } else {
+ return `${name}`
+ }
+ }).join(" - ");
+
+ document.querySelector("h1").outerHTML = `
+ MaoTube
+
+