Compare commits
10 Commits
ba6d8eeffc
...
2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 284615573f | |||
| ba96584300 | |||
| 832ff99a7b | |||
| 1940d67c37 | |||
| 4153fcd8d5 | |||
| 163fccb9b2 | |||
| cd72390f42 | |||
| b923785c9f | |||
| 08f8cc7fc3 | |||
| 273314739f |
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
|
||||||
|
|||||||
32
index.js
32
index.js
@@ -1,25 +1,21 @@
|
|||||||
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 { initDbConnection, db } from './src/db.js';
|
||||||
import { initUsers } from "./src/user.js";
|
import { initUsers } from './src/user.js';
|
||||||
|
import { initCdn } from './src/cdn.js';
|
||||||
|
import { initGames } from './src/games.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() {
|
app.use(cors({ credentials: true, origin: process.env.JEOPARDY_URL }));
|
||||||
console.log('Shutting down...');
|
|
||||||
console.log('Closing db connection...');
|
|
||||||
closeDbConnection();
|
|
||||||
});
|
|
||||||
|
|
||||||
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 +25,8 @@ await initDbConnection();
|
|||||||
initAuth(app, db);
|
initAuth(app, db);
|
||||||
initUsers(app, db);
|
initUsers(app, db);
|
||||||
initWebsocket(app);
|
initWebsocket(app);
|
||||||
|
initCdn(app, db);
|
||||||
|
initGames(app, db);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Listening on port ${port}`);
|
console.log(`Listening on port ${port}`);
|
||||||
|
|||||||
196
package-lock.json
generated
196
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "jeopardyserver",
|
"name": "jeopardyserver",
|
||||||
"version": "1.0.4",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "jeopardyserver",
|
"name": "jeopardyserver",
|
||||||
"version": "1.0.4",
|
"version": "2.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/express": "^5.0.3",
|
"@types/express": "^5.0.3",
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jeopardyserver",
|
"name": "jeopardyserver",
|
||||||
"version": "1.0.4",
|
"version": "2.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ content-type: application/json
|
|||||||
|
|
||||||
{
|
{
|
||||||
"username": "jonas",
|
"username": "jonas",
|
||||||
"password": "paula"
|
"password": "jonas"
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|||||||
50
src/auth.js
50
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,14 +41,14 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAuthorization(role) {
|
function checkAuthorization(role) {
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
if (req.user === undefined) {
|
if (req.user === undefined) {
|
||||||
res.status(403).send();
|
res.status(403).send();
|
||||||
return;
|
return;
|
||||||
@@ -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,24 +79,42 @@ 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);
|
||||||
|
|
||||||
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: '/',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
328
src/cdn.js
Normal file
328
src/cdn.js
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
import { rmSync } from 'fs';
|
||||||
|
import { copyFile, mkdir, readdir, rm } 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;
|
||||||
|
|
||||||
|
function buildPath(userid, path) {
|
||||||
|
return `${dataPath}/${userid}${path.length === '/' ? '' : path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ressourcePath(res) {
|
||||||
|
return `${res.fullpath}/${res.filename}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
app.put('/cdn/:userid/:resid', renameFile);
|
||||||
|
app.delete('/cdn/:userid/:resid', deleteFile);
|
||||||
|
app.get('/ressource', fetchRessource);
|
||||||
|
app.post('/directory', fetchDirectory);
|
||||||
|
app.put('/directory', addDirectory);
|
||||||
|
app.delete('/directory', deleteDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @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 = buildPath(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 fetchRessource(req, res) {
|
||||||
|
if (req.query.id === undefined || req.query.id.length <= 0) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = new ObjectId(req.query.id);
|
||||||
|
|
||||||
|
ressources
|
||||||
|
.findOne({
|
||||||
|
_id: id,
|
||||||
|
user: req.user._id,
|
||||||
|
})
|
||||||
|
.then((ressource) => {
|
||||||
|
if (ressource) {
|
||||||
|
res.status(200).send(ressource);
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @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),
|
||||||
|
_id: new ObjectId(req.params.resid),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ressource) {
|
||||||
|
res.sendFile(ressourcePath(ressource));
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function renameFile(req, res) {
|
||||||
|
if (req.user._id.toString() !== req.params.userid) {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
req.body === undefined ||
|
||||||
|
req.body.name === undefined ||
|
||||||
|
req.body.name.length <= 0 ||
|
||||||
|
/\.{2}|\/|\\/.test(req.body.name)
|
||||||
|
) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = req.body.name;
|
||||||
|
|
||||||
|
let ressource = await ressources.findOne({
|
||||||
|
user: new ObjectId(req.params.userid),
|
||||||
|
filename: req.params.resid,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ressource) {
|
||||||
|
console.log('Ressource could not be found');
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ressources
|
||||||
|
.updateOne(
|
||||||
|
{
|
||||||
|
_id: ressource._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function deleteFile(req, res) {
|
||||||
|
if (req.user._id.toString() !== req.params.userid) {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ressource = await ressources.findOne({
|
||||||
|
user: new ObjectId(req.params.userid),
|
||||||
|
filename: req.params.resid,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ressource) {
|
||||||
|
console.log('Ressource could not be found');
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rm(ressourcePath(ressource))
|
||||||
|
.then(() => {
|
||||||
|
return ressources.deleteOne({ _id: ressource._id });
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchDirectory(req, res) {
|
||||||
|
if (!req.body) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const path = req.body.path;
|
||||||
|
if (!path) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = ressources.find({
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
|
||||||
|
readdir(buildPath(req.user._id, path), {
|
||||||
|
withFileTypes: true,
|
||||||
|
})
|
||||||
|
.then(async (value) => {
|
||||||
|
let directories = value
|
||||||
|
.filter((dir) => dir.isDirectory())
|
||||||
|
.map((dir) => {
|
||||||
|
return {
|
||||||
|
name: dir.name,
|
||||||
|
isDir: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).send([...directories, ...(await files.toArray())]);
|
||||||
|
})
|
||||||
|
.catch(async (err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(200).send(await files.toArray());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function addDirectory(req, res) {
|
||||||
|
if (!req.body) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = req.body.name;
|
||||||
|
const path = req.body.path;
|
||||||
|
if (!name || !path || !/^[a-zA-Z0-9-_]+$/.test(name)) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir(buildPath(req.user._id, path + '/' + name))
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function deleteDirectory(req, res) {
|
||||||
|
console.log(req.body);
|
||||||
|
if (
|
||||||
|
req.body === undefined ||
|
||||||
|
req.body.path === undefined ||
|
||||||
|
req.body.path.length <= 0
|
||||||
|
) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(req.body);
|
||||||
|
|
||||||
|
const path = req.body.path;
|
||||||
|
|
||||||
|
rm(buildPath(req.user._id, path), {
|
||||||
|
force: true,
|
||||||
|
recursive: true,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return ressources.deleteMany({
|
||||||
|
user: req.user._id,
|
||||||
|
path: {
|
||||||
|
$regex: '^' + path + '($|\\/.*)',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
13
src/db.js
13
src/db.js
@@ -1,5 +1,8 @@
|
|||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from 'mongodb';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {MongoClient}
|
||||||
|
*/
|
||||||
let client;
|
let client;
|
||||||
|
|
||||||
const dbName = `jeopardy`;
|
const dbName = `jeopardy`;
|
||||||
@@ -10,12 +13,10 @@ const dbName = `jeopardy`;
|
|||||||
export let db;
|
export let db;
|
||||||
|
|
||||||
export async function initDbConnection() {
|
export async function initDbConnection() {
|
||||||
client = new MongoClient(`mongodb://${process.env.JEOPARDYSERVER_MONGO_USERNAME}:${process.env.JEOPARDYSERVER_MONGO_PASSWORD}@${process.env.JEOPARDYSERVER_MONGO_URL}/`);
|
client = new MongoClient(
|
||||||
|
`mongodb://${process.env.JEOPARDYSERVER_MONGO_USERNAME}:${process.env.JEOPARDYSERVER_MONGO_PASSWORD}@${process.env.JEOPARDYSERVER_MONGO_URL}/`,
|
||||||
|
);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
console.log('Connected successfully to mongodb');
|
console.log('Connected successfully to mongodb');
|
||||||
db = client.db(dbName);
|
db = client.db(dbName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function close() {
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
|
|||||||
761
src/games.js
Normal file
761
src/games.js
Normal file
@@ -0,0 +1,761 @@
|
|||||||
|
import { Collection, Db, ObjectId } from 'mongodb';
|
||||||
|
import { checkNumberProp, checkObjectProp, checkStringProp } from './util.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Collection}
|
||||||
|
*/
|
||||||
|
let cGames;
|
||||||
|
/**
|
||||||
|
* @type {Collection}
|
||||||
|
*/
|
||||||
|
let cWalls;
|
||||||
|
/**
|
||||||
|
* @type {Collection}
|
||||||
|
*/
|
||||||
|
let cCategories;
|
||||||
|
/**
|
||||||
|
* @type {Collection}
|
||||||
|
*/
|
||||||
|
let cQuestions;
|
||||||
|
|
||||||
|
const QuestionType = {
|
||||||
|
SIMPLE: 'SIMPLE',
|
||||||
|
MULTIPLE_CHOICE: 'MULTIPLE_CHOICE',
|
||||||
|
IMAGE: 'IMAGE',
|
||||||
|
IMAGE_MULTIPLE_CHOICE: 'IMAGE_MULTIPLE_CHOICE',
|
||||||
|
AUDIO: 'AUDIO',
|
||||||
|
AUDIO_MULTIPLE_CHOICE: 'AUDIO_MULTIPLE_CHOICE',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} points
|
||||||
|
* @param {string} question
|
||||||
|
* @param {string} answer
|
||||||
|
* @param {ObjectId} owner
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function createSimpleQuestion(points, question, answer, owner) {
|
||||||
|
return {
|
||||||
|
owner,
|
||||||
|
points,
|
||||||
|
type: QuestionType.SIMPLE,
|
||||||
|
data: createSimpleData(question, answer),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} question
|
||||||
|
* @param {string} answer
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function createSimpleData(question, answer) {
|
||||||
|
return {
|
||||||
|
question,
|
||||||
|
answer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ObjectId[]} ids
|
||||||
|
*/
|
||||||
|
function splitQuestionIds(ids) {
|
||||||
|
let res = [];
|
||||||
|
for (let i = 0; i < ids.length; i += 5) {
|
||||||
|
res.push(ids.slice(i, i + 5));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} app
|
||||||
|
* @param {Db} db
|
||||||
|
*/
|
||||||
|
export function initGames(app, db) {
|
||||||
|
cGames = db.collection('games');
|
||||||
|
cWalls = db.collection('walls');
|
||||||
|
cCategories = db.collection('categories');
|
||||||
|
cQuestions = db.collection('questions');
|
||||||
|
app.get('/game', fetchGame);
|
||||||
|
app.post('/game', createGame);
|
||||||
|
app.delete('/game/:gameid', deleteGameRoute);
|
||||||
|
app.get('/games', fetchGames);
|
||||||
|
app.post('/game/rename', renameGame);
|
||||||
|
|
||||||
|
app.get('/wall', fetchWall);
|
||||||
|
app.get('/walls/:gameid', fetchWalls);
|
||||||
|
app.post('/wall', createWall);
|
||||||
|
app.delete('/wall/:wallid', deleteWallRoute);
|
||||||
|
app.post('/wall/rename', renameWall);
|
||||||
|
|
||||||
|
app.get('/category', fetchCategory);
|
||||||
|
app.post('/category/rename', renameCategory);
|
||||||
|
|
||||||
|
app.get('/question', fetchQuestion);
|
||||||
|
app.post('/question', updateQuestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function createGame(req, res) {
|
||||||
|
if (!checkStringProp(req.body, 'name')) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = req.body.name;
|
||||||
|
|
||||||
|
cGames
|
||||||
|
.insertOne({
|
||||||
|
name,
|
||||||
|
walls: [],
|
||||||
|
owner: req.user._id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
function renameGame(req, res) {
|
||||||
|
if (!checkStringProp(req.body, 'name')) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!checkStringProp(req.body, 'gameid')) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let gameid = req.body.gameid;
|
||||||
|
let _id = new ObjectId(gameid);
|
||||||
|
let name = req.body.name;
|
||||||
|
|
||||||
|
cGames
|
||||||
|
.updateOne(
|
||||||
|
{
|
||||||
|
_id,
|
||||||
|
owner: req.user._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.modifiedCount === 1) res.sendStatus(200);
|
||||||
|
else {
|
||||||
|
console.error(
|
||||||
|
`Failed to modify exactly one Document. Instead modified: ${result.modifiedCount}`,
|
||||||
|
);
|
||||||
|
res.sendStatus(400);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchGames(req, res) {
|
||||||
|
let list = cGames.find({
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).send(await list.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchGame(req, res) {
|
||||||
|
if (req.query.id === undefined || req.query.id.length <= 0) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = new ObjectId(req.query.id);
|
||||||
|
|
||||||
|
let game = await cGames.findOne({
|
||||||
|
_id: id,
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (game) {
|
||||||
|
res.status(200).send(game);
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function deleteGameRoute(req, res) {
|
||||||
|
let game = await cGames.findOne({
|
||||||
|
owner: req.user._id,
|
||||||
|
_id: new ObjectId(req.params.gameid),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!game) {
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGame(game._id)
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchQuestion(req, res) {
|
||||||
|
if (req.query.id === undefined || req.query.id.length <= 0) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = new ObjectId(req.query.id);
|
||||||
|
|
||||||
|
let question = await cQuestions.findOne({
|
||||||
|
_id: id,
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (question) {
|
||||||
|
res.status(200).send(question);
|
||||||
|
} else {
|
||||||
|
res.sendStatus(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchCategory(req, res) {
|
||||||
|
if (req.query.id === undefined || req.query.id.length <= 0) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = new ObjectId(req.query.id);
|
||||||
|
|
||||||
|
let category = await cCategories.findOne({
|
||||||
|
_id: id,
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (category) {
|
||||||
|
let questions = await cQuestions
|
||||||
|
.find(
|
||||||
|
{
|
||||||
|
_id: {
|
||||||
|
$in: category.questions,
|
||||||
|
},
|
||||||
|
owner: req.user._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projection: {
|
||||||
|
_id: 1,
|
||||||
|
points: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
if (questions.length !== 5) {
|
||||||
|
res.sendStatus(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
category.questions = questions;
|
||||||
|
|
||||||
|
res.status(200).send(category);
|
||||||
|
} else {
|
||||||
|
res.sendStatus(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
function renameCategory(req, res) {
|
||||||
|
if (!checkStringProp(req.body, 'name')) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!checkStringProp(req.body, 'categoryid')) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let categoryid = req.body.categoryid;
|
||||||
|
let _id = new ObjectId(categoryid);
|
||||||
|
let name = req.body.name;
|
||||||
|
|
||||||
|
cCategories
|
||||||
|
.updateOne(
|
||||||
|
{
|
||||||
|
_id,
|
||||||
|
owner: req.user._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.modifiedCount === 1) res.sendStatus(200);
|
||||||
|
else {
|
||||||
|
console.error(
|
||||||
|
`Failed to modify exactly one Document. Instead modified: ${result.modifiedCount}`,
|
||||||
|
);
|
||||||
|
res.sendStatus(400);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchWall(req, res) {
|
||||||
|
if (req.query.id === undefined || req.query.id.length <= 0) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = new ObjectId(req.query.id);
|
||||||
|
|
||||||
|
let wall = await cWalls.findOne({
|
||||||
|
_id: id,
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (wall) {
|
||||||
|
res.status(200).send(wall);
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function fetchWalls(req, res) {
|
||||||
|
let game = await cGames.findOne({
|
||||||
|
owner: req.user._id,
|
||||||
|
_id: new ObjectId(req.params.gameid),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!game) {
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fetchedWalls = cWalls.find({
|
||||||
|
_id: {
|
||||||
|
$in: game.walls,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).send(await fetchedWalls.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
function renameWall(req, res) {
|
||||||
|
if (!checkStringProp(req.body, 'name')) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!checkStringProp(req.body, 'wallid')) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let wallid = req.body.wallid;
|
||||||
|
let _id = new ObjectId(wallid);
|
||||||
|
let name = req.body.name;
|
||||||
|
|
||||||
|
cWalls
|
||||||
|
.updateOne(
|
||||||
|
{
|
||||||
|
_id,
|
||||||
|
owner: req.user._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.modifiedCount === 1) res.sendStatus(200);
|
||||||
|
else {
|
||||||
|
console.error(
|
||||||
|
`Failed to modify exactly one Document. Instead modified: ${result.modifiedCount}`,
|
||||||
|
);
|
||||||
|
res.sendStatus(400);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function createWall(req, res) {
|
||||||
|
if (
|
||||||
|
!checkStringProp(req.body, 'gameid') &&
|
||||||
|
!checkStringProp(req.body, 'name')
|
||||||
|
) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
const gameid = req.body.gameid;
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
const wallname = req.body.name;
|
||||||
|
|
||||||
|
let game = await cGames.findOne({
|
||||||
|
owner: req.user._id,
|
||||||
|
_id: new ObjectId(gameid),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!game) {
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newQuestions = [];
|
||||||
|
|
||||||
|
for (let id = 1; id <= 5; id++) {
|
||||||
|
for (let j = 1; j <= 5; j++) {
|
||||||
|
newQuestions.push(
|
||||||
|
createSimpleQuestion(
|
||||||
|
j * -100,
|
||||||
|
'Frage',
|
||||||
|
'Antwort',
|
||||||
|
req.user._id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cQuestions
|
||||||
|
.insertMany(newQuestions)
|
||||||
|
.then((insertedQuestions) => {
|
||||||
|
let questionsIds = splitQuestionIds(
|
||||||
|
Object.values(insertedQuestions.insertedIds),
|
||||||
|
);
|
||||||
|
let newCategories = [];
|
||||||
|
|
||||||
|
for (let i = 1; i <= 5; i++) {
|
||||||
|
newCategories.push({
|
||||||
|
name: `Kategorie ${i}`,
|
||||||
|
questions: questionsIds[i - 1],
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return cCategories.insertMany(newCategories);
|
||||||
|
})
|
||||||
|
.then((insertedCategories) => {
|
||||||
|
return cWalls.insertOne({
|
||||||
|
name: wallname,
|
||||||
|
categories: Object.values(insertedCategories.insertedIds),
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((insertedWall) => {
|
||||||
|
return cGames.updateOne(
|
||||||
|
{
|
||||||
|
_id: game._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$push: {
|
||||||
|
walls: insertedWall.insertedId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function deleteWallRoute(req, res) {
|
||||||
|
let wall = await cWalls.findOne({
|
||||||
|
_id: new ObjectId(req.params.wallid),
|
||||||
|
owner: req.user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!wall) {
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let game = await cGames.findOne({
|
||||||
|
owner: req.user._id,
|
||||||
|
walls: wall._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!game) {
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteWall(wall._id)
|
||||||
|
.then(() => {
|
||||||
|
return cGames.updateOne(
|
||||||
|
{
|
||||||
|
_id: game._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$pull: {
|
||||||
|
walls: wall._id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async function updateQuestion(req, res) {
|
||||||
|
if (
|
||||||
|
!checkStringProp(req.body, '_id') &&
|
||||||
|
!checkStringProp(req.body, 'owner') &&
|
||||||
|
!checkStringProp(req.body, 'type') &&
|
||||||
|
!checkNumberProp(req.body, 'points') &&
|
||||||
|
!checkObjectProp(req.body, 'data')
|
||||||
|
) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.body.owner !== req.user._id.toString()) {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let _id = req.body._id;
|
||||||
|
|
||||||
|
let replacement;
|
||||||
|
|
||||||
|
if (
|
||||||
|
req.body.type === QuestionType.SIMPLE ||
|
||||||
|
req.body.type === QuestionType.MULTIPLE_CHOICE
|
||||||
|
) {
|
||||||
|
replacement = toNormalQuestion(req.body);
|
||||||
|
} else if (
|
||||||
|
req.body.type === QuestionType.IMAGE ||
|
||||||
|
req.body.type === QuestionType.IMAGE_MULTIPLE_CHOICE
|
||||||
|
) {
|
||||||
|
replacement = toImageQuestion(req.body);
|
||||||
|
} else if (
|
||||||
|
req.body.type === QuestionType.AUDIO ||
|
||||||
|
req.body.type === QuestionType.AUDIO_MULTIPLE_CHOICE
|
||||||
|
) {
|
||||||
|
replacement = toAudioQuestion(req.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
cQuestions
|
||||||
|
.replaceOne(
|
||||||
|
{
|
||||||
|
_id: new ObjectId(_id),
|
||||||
|
owner: req.user._id,
|
||||||
|
},
|
||||||
|
replacement,
|
||||||
|
)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.modifiedCount === 1) {
|
||||||
|
res.sendStatus(200);
|
||||||
|
} else res.sendStatus(500);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNormalQuestion(body) {
|
||||||
|
return {
|
||||||
|
...body,
|
||||||
|
_id: new ObjectId(body._id),
|
||||||
|
owner: new ObjectId(body.owner),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function toImageQuestion(body) {
|
||||||
|
return {
|
||||||
|
...body,
|
||||||
|
_id: new ObjectId(body._id),
|
||||||
|
owner: new ObjectId(body.owner),
|
||||||
|
data: {
|
||||||
|
...body.data,
|
||||||
|
image:
|
||||||
|
body.data.image === null ? null : new ObjectId(body.data.image),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function toAudioQuestion(body) {
|
||||||
|
return {
|
||||||
|
...body,
|
||||||
|
_id: new ObjectId(body._id),
|
||||||
|
owner: new ObjectId(body.owner),
|
||||||
|
data: {
|
||||||
|
...body.data,
|
||||||
|
audio:
|
||||||
|
body.data.audio === null ? null : new ObjectId(body.data.audio),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ObjectId} _id
|
||||||
|
*/
|
||||||
|
function deleteGame(_id) {
|
||||||
|
return cGames
|
||||||
|
.findOne({ _id })
|
||||||
|
.then((game) => {
|
||||||
|
let wallDeletions = [];
|
||||||
|
for (const wallId of game.walls) {
|
||||||
|
wallDeletions.push(deleteWall(wallId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(wallDeletions);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return cGames.deleteOne({ _id });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ObjectId} _id
|
||||||
|
*/
|
||||||
|
function deleteWall(_id) {
|
||||||
|
return cWalls
|
||||||
|
.findOne({ _id })
|
||||||
|
.then((wall) => {
|
||||||
|
let categoryDeletions = [];
|
||||||
|
for (const catId of wall.categories) {
|
||||||
|
categoryDeletions.push(deleteCategory(catId));
|
||||||
|
}
|
||||||
|
return Promise.all(categoryDeletions);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return cWalls.deleteOne({ _id: _id });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ObjectId} _id
|
||||||
|
*/
|
||||||
|
function deleteCategory(_id) {
|
||||||
|
return cCategories
|
||||||
|
.findOne({
|
||||||
|
_id,
|
||||||
|
})
|
||||||
|
.then((category) => {
|
||||||
|
return cQuestions.deleteMany({
|
||||||
|
_id: {
|
||||||
|
$in: category.questions,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return cCategories.deleteOne({
|
||||||
|
_id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
37
src/util.js
Normal file
37
src/util.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {any} body
|
||||||
|
* @param {string} property
|
||||||
|
*/
|
||||||
|
export function checkStringProp(body, property) {
|
||||||
|
if (body === undefined) return false;
|
||||||
|
if (Object.hasOwn(body, property)) {
|
||||||
|
if (typeof body[property] === 'string') {
|
||||||
|
return body[property].length > 0;
|
||||||
|
} else return false;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {any} body
|
||||||
|
* @param {string} property
|
||||||
|
*/
|
||||||
|
export function checkNumberProp(body, property) {
|
||||||
|
if (body === undefined) return false;
|
||||||
|
if (Object.hasOwn(body, property)) {
|
||||||
|
return typeof body[property] === 'number';
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {any} body
|
||||||
|
* @param {string} property
|
||||||
|
*/
|
||||||
|
export function checkObjectProp(body, property) {
|
||||||
|
if (body === undefined) return false;
|
||||||
|
if (Object.hasOwn(body, property)) {
|
||||||
|
return typeof body[property] === 'object';
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user