Added File uploads and fetching of said files
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -143,3 +143,4 @@ vite.config.ts.timestamp-*
|
|||||||
# user files
|
# user files
|
||||||
|
|
||||||
/responses
|
/responses
|
||||||
|
/data
|
||||||
|
|||||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ COPY . .
|
|||||||
RUN npm prune --production
|
RUN npm prune --production
|
||||||
|
|
||||||
FROM node:${NODE_VERSION}-alpine
|
FROM node:${NODE_VERSION}-alpine
|
||||||
|
RUN mkdir -p /data
|
||||||
|
RUN chown node /data
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/node_modules node_modules/
|
COPY --from=builder /app/node_modules node_modules/
|
||||||
COPY --from=builder /app/index.js .
|
COPY --from=builder /app/index.js .
|
||||||
@@ -18,4 +20,5 @@ COPY --from=builder /app/src src/
|
|||||||
USER node
|
USER node
|
||||||
EXPOSE 12345
|
EXPOSE 12345
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
ENV JEOPARDY_CDN_DATA_PATH=/data
|
||||||
CMD [ "node", "."]
|
CMD [ "node", "."]
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Ansonsten kann man auch mit `npm run dev` entwickeln.
|
|||||||
cd /opt/jeopardy/Jeopardy-Server
|
cd /opt/jeopardy/Jeopardy-Server
|
||||||
git fetch --tags
|
git fetch --tags
|
||||||
git checkout <versionsnummer>
|
git checkout <versionsnummer>
|
||||||
docker build -t jeopardy .
|
docker build -t jeopardyserver .
|
||||||
docker tag jeopardyserver:latest jeopardyserver:<versionsnummer>
|
docker tag jeopardyserver:latest jeopardyserver:<versionsnummer>
|
||||||
cd ..
|
cd ..
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|||||||
26
index.js
26
index.js
@@ -1,25 +1,26 @@
|
|||||||
import dotenv from "dotenv";
|
import dotenv from 'dotenv';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
import express from "express";
|
import express from 'express';
|
||||||
import expressWs from "express-ws";
|
import expressWs from 'express-ws';
|
||||||
import morgan from "morgan";
|
import morgan from 'morgan';
|
||||||
import cookieParser from "cookie-parser";
|
import cookieParser from 'cookie-parser';
|
||||||
import cors from "cors";
|
import cors from 'cors';
|
||||||
import { initWebsocket } from "./src/websocket.js";
|
import { initWebsocket } from './src/websocket.js';
|
||||||
import { initAuth } from "./src/auth.js";
|
import { initAuth } from './src/auth.js';
|
||||||
import { close as closeDbConnection, initDbConnection, db } from "./src/db.js";
|
import { close as closeDbConnection, initDbConnection, db } from './src/db.js';
|
||||||
import { initUsers } from "./src/user.js";
|
import { initUsers } from './src/user.js';
|
||||||
|
import { initCdn } from './src/cdn.js';
|
||||||
const app = express();
|
const app = express();
|
||||||
const appWs = expressWs(app);
|
const appWs = expressWs(app);
|
||||||
const port = 12345;
|
const port = 12345;
|
||||||
|
|
||||||
process.on('exit', function() {
|
process.on('exit', function () {
|
||||||
console.log('Shutting down...');
|
console.log('Shutting down...');
|
||||||
console.log('Closing db connection...');
|
console.log('Closing db connection...');
|
||||||
closeDbConnection();
|
closeDbConnection();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(cors({credentials: true, origin: process.env.JEOPARDY_URL}));
|
app.use(cors({ credentials: true, origin: process.env.JEOPARDY_URL }));
|
||||||
app.use(morgan(process.env.production ? 'common' : 'dev'));
|
app.use(morgan(process.env.production ? 'common' : 'dev'));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
@@ -29,6 +30,7 @@ await initDbConnection();
|
|||||||
initAuth(app, db);
|
initAuth(app, db);
|
||||||
initUsers(app, db);
|
initUsers(app, db);
|
||||||
initWebsocket(app);
|
initWebsocket(app);
|
||||||
|
initCdn(app, db);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Listening on port ${port}`);
|
console.log(`Listening on port ${port}`);
|
||||||
|
|||||||
192
package-lock.json
generated
192
package-lock.json
generated
@@ -17,10 +17,12 @@
|
|||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
"mongodb": "^6.20.0",
|
"mongodb": "^6.20.0",
|
||||||
"morgan": "^1.10.1",
|
"morgan": "^1.10.1",
|
||||||
|
"multer": "^2.0.2",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.6.0"
|
"@types/node": "^24.6.0",
|
||||||
|
"prettier": "^3.7.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mongodb-js/saslprep": {
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
@@ -156,6 +158,12 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/append-field": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/basic-auth": {
|
"node_modules/basic-auth": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
@@ -203,6 +211,23 @@
|
|||||||
"node": ">=16.20.1"
|
"node": ">=16.20.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/busboy": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"streamsearch": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -241,6 +266,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 6.0"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.0.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
||||||
@@ -734,6 +774,27 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minimist": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mongodb": {
|
"node_modules/mongodb": {
|
||||||
"version": "6.20.0",
|
"version": "6.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
|
||||||
@@ -839,6 +900,67 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/multer": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"append-field": "^1.0.0",
|
||||||
|
"busboy": "^1.6.0",
|
||||||
|
"concat-stream": "^2.0.0",
|
||||||
|
"mkdirp": "^0.5.6",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"type-is": "^1.6.18",
|
||||||
|
"xtend": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multer/node_modules/media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multer/node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multer/node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multer/node_modules/type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||||
@@ -918,6 +1040,22 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
|
||||||
|
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@@ -995,6 +1133,20 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/router": {
|
"node_modules/router": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||||
@@ -1170,6 +1322,23 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/streamsearch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/toidentifier": {
|
"node_modules/toidentifier": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
@@ -1205,6 +1374,12 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.13.0",
|
"version": "7.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz",
|
||||||
@@ -1220,6 +1395,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
@@ -1277,6 +1458,15 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,11 @@
|
|||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
"mongodb": "^6.20.0",
|
"mongodb": "^6.20.0",
|
||||||
"morgan": "^1.10.1",
|
"morgan": "^1.10.1",
|
||||||
|
"multer": "^2.0.2",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.6.0"
|
"@types/node": "^24.6.0",
|
||||||
|
"prettier": "^3.7.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/auth.js
46
src/auth.js
@@ -1,4 +1,4 @@
|
|||||||
import { createUser, generateHash, updateSessionToken } from "./userHelper.js";
|
import { createUser, generateHash, updateSessionToken } from './userHelper.js';
|
||||||
|
|
||||||
let db;
|
let db;
|
||||||
let users;
|
let users;
|
||||||
@@ -19,19 +19,19 @@ async function getUserInfo(req, res) {
|
|||||||
res.status(200).send({
|
res.status(200).send({
|
||||||
username: req.user.username,
|
username: req.user.username,
|
||||||
role: req.user.role,
|
role: req.user.role,
|
||||||
_id: req.user._id
|
_id: req.user._id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkSessionToken(req, res, next) {
|
async function checkSessionToken(req, res, next) {
|
||||||
if (req.path.startsWith("/auth/")) {
|
if (req.path.startsWith('/auth/')) {
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = req.cookies.jeopardytoken;
|
const token = req.cookies.jeopardytoken;
|
||||||
|
|
||||||
let user = await users.findOne({sessiontoken: token});
|
let user = await users.findOne({ sessiontoken: token });
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
res.sendStatus(401);
|
res.sendStatus(401);
|
||||||
@@ -41,8 +41,8 @@ async function checkSessionToken(req, res, next) {
|
|||||||
req.user = {
|
req.user = {
|
||||||
role: user.role,
|
role: user.role,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
_id: user._id
|
_id: user._id,
|
||||||
}
|
};
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ function checkAuthorization(role) {
|
|||||||
} else {
|
} else {
|
||||||
res.status(403).send();
|
res.status(403).send();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loginUser(req, res) {
|
async function loginUser(req, res) {
|
||||||
@@ -79,14 +79,22 @@ async function loginUser(req, res) {
|
|||||||
if (userobj !== null) {
|
if (userobj !== null) {
|
||||||
setTokenCookie(res, userobj.sessiontoken);
|
setTokenCookie(res, userobj.sessiontoken);
|
||||||
|
|
||||||
res.status(200).send({username: userobj.username, role: userobj.role, _id: userobj._id});
|
res.status(200).send({
|
||||||
|
username: userobj.username,
|
||||||
|
role: userobj.role,
|
||||||
|
_id: userobj._id,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(403);
|
res.sendStatus(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function authenticateUser(username, password, updateSession = true) {
|
export async function authenticateUser(
|
||||||
let foundUser = await users.findOne({username});
|
username,
|
||||||
|
password,
|
||||||
|
updateSession = true,
|
||||||
|
) {
|
||||||
|
let foundUser = await users.findOne({ username });
|
||||||
if (foundUser === null) return null;
|
if (foundUser === null) return null;
|
||||||
|
|
||||||
const hash = generateHash(password, foundUser.salt, foundUser.iterations);
|
const hash = generateHash(password, foundUser.salt, foundUser.iterations);
|
||||||
@@ -94,9 +102,19 @@ export async function authenticateUser(username, password, updateSession = true)
|
|||||||
if (hash === foundUser.hash) {
|
if (hash === foundUser.hash) {
|
||||||
if (updateSession) {
|
if (updateSession) {
|
||||||
let sessiontoken = await updateSessionToken(users, foundUser._id);
|
let sessiontoken = await updateSessionToken(users, foundUser._id);
|
||||||
return {sessiontoken, username, role: foundUser.role, _id: foundUser._id};
|
return {
|
||||||
|
sessiontoken,
|
||||||
|
username,
|
||||||
|
role: foundUser.role,
|
||||||
|
_id: foundUser._id,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return {sessiontoken: foundUser.sessiontoken, username, role: foundUser.role, _id: foundUser._id};
|
return {
|
||||||
|
sessiontoken: foundUser.sessiontoken,
|
||||||
|
username,
|
||||||
|
role: foundUser.role,
|
||||||
|
_id: foundUser._id,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +127,6 @@ function setTokenCookie(res, sessiontoken) {
|
|||||||
|
|
||||||
res.cookie('jeopardytoken', sessiontoken, {
|
res.cookie('jeopardytoken', sessiontoken, {
|
||||||
maxAge: 1e3 * 60 * 60 * 24 * 7,
|
maxAge: 1e3 * 60 * 60 * 24 * 7,
|
||||||
path: "/"
|
path: '/',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
91
src/cdn.js
Normal file
91
src/cdn.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { rmSync } from 'fs';
|
||||||
|
import { copyFile, mkdir } from 'fs/promises';
|
||||||
|
import { Collection, Db, ObjectId } from 'mongodb';
|
||||||
|
import multer from 'multer';
|
||||||
|
|
||||||
|
const dataPath = process.env.JEOPARDY_CDN_DATA_PATH;
|
||||||
|
const upload = multer({ dest: dataPath });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Collection}
|
||||||
|
*/
|
||||||
|
let ressources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} app
|
||||||
|
* @param {Db} db
|
||||||
|
*/
|
||||||
|
export function initCdn(app, db) {
|
||||||
|
ressources = db.collection('ressources');
|
||||||
|
app.post('/upload', upload.single('file'), uploadFile);
|
||||||
|
app.get('/cdn/:userid/:resid', fetchFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
* @param {import('express').NextFunction} next
|
||||||
|
*/
|
||||||
|
function uploadFile(req, res, next) {
|
||||||
|
console.log(req.file, req.body);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {string | undefined}
|
||||||
|
*/
|
||||||
|
const path = req.body.path;
|
||||||
|
|
||||||
|
if (path !== undefined && path.startsWith('/') && !path.includes('.')) {
|
||||||
|
let destinationPath = `${dataPath}/${req.user._id}${req.body.path}`;
|
||||||
|
mkdir(destinationPath, {
|
||||||
|
recursive: true,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return copyFile(
|
||||||
|
req.file.path,
|
||||||
|
`${destinationPath}/${req.file.filename}`,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
rmSync(req.file.path);
|
||||||
|
|
||||||
|
await ressources.insertOne({
|
||||||
|
fullpath: destinationPath,
|
||||||
|
path: path,
|
||||||
|
user: req.user._id,
|
||||||
|
mimetype: req.file.mimetype,
|
||||||
|
name: req.file.originalname,
|
||||||
|
filename: req.file.filename,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
rmSync(req.file.path);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rmSync(req.file.path);
|
||||||
|
res.sendStatus(400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchFile(req, res) {
|
||||||
|
let ressource = await ressources.findOne({
|
||||||
|
user: new ObjectId(req.params.userid),
|
||||||
|
filename: req.params.resid,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ressource) {
|
||||||
|
res.sendFile(ressource.fullpath + ressource.filename);
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ObjectId } from "mongodb";
|
import { Db, ObjectId } from "mongodb";
|
||||||
import { createUser as userHelperCreateUser, generateSessionToken, updatePassword, userExists } from "./userHelper.js";
|
import { createUser as userHelperCreateUser, generateSessionToken, updatePassword, userExists } from "./userHelper.js";
|
||||||
import { isValidRole, roles } from "./roles.js";
|
import { isValidRole, roles } from "./roles.js";
|
||||||
import { authenticateUser } from "./auth.js";
|
import { authenticateUser } from "./auth.js";
|
||||||
@@ -6,6 +6,11 @@ import { authenticateUser } from "./auth.js";
|
|||||||
let db;
|
let db;
|
||||||
let users;
|
let users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} app
|
||||||
|
* @param {Db} db
|
||||||
|
*/
|
||||||
export function initUsers(app, db) {
|
export function initUsers(app, db) {
|
||||||
users = db.collection('users');
|
users = db.collection('users');
|
||||||
app.put('/admin/user', createUser);
|
app.put('/admin/user', createUser);
|
||||||
|
|||||||
Reference in New Issue
Block a user