Added changes I forgot to commit

This commit is contained in:
Gu://em_ 2025-05-12 16:11:11 +02:00
parent 32c0ffd715
commit fbb486e697
54 changed files with 1397 additions and 3786 deletions

145
.gitignore vendored
View file

@ -1,145 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Vscode files
.vscode
# Data
data/*
# Temporary
tests/

View file

@ -12,7 +12,7 @@ First make sure you have the following dependencies installed:
- **Node.js** - **Node.js**
- **pnpm** - **pnpm**
Then download all needed packages using Then, for each of frontend and backend, download all needed packages using
```sh ```sh
pnpm install pnpm install
``` ```
@ -28,7 +28,7 @@ node server.js
## 🔧 Configuration ## 🔧 Configuration
All settings are located in [config/config.json](config/config.json). For more explainations and possible values, give a look to the documentation (incomming). All settings are located in [backend/config/config.json](backend/config/config.json). For more explainations and possible values, give a look to the documentation (incomming).
## 🔗 Documentation ## 🔗 Documentation

View file

@ -1,19 +0,0 @@
{
"port": 8000,
"users": {
"admin": {
"username": "admin",
"password": "admin"
}
},
"database": {
"type": "sqlite"
},
"auth": {
"JWT_secret": "HGF7654EGBNKJNBJH6754356788GJHGY",
"tokenExpiry": "1h"
}
}

24
frontend/.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

13
frontend/index.html Normal file
View file

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Preact</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

18
frontend/package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "wf-radio-frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"preact": "^10.26.5"
},
"devDependencies": {
"@preact/preset-vite": "^2.10.1",
"vite": "^6.3.5"
}
}

1188
frontend/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
onlyBuiltDependencies:
- esbuild

1
frontend/public/vite.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

43
frontend/src/app.jsx Normal file
View file

@ -0,0 +1,43 @@
import { useState } from 'preact/hooks'
import preactLogo from './assets/preact.svg'
import viteLogo from '/vite.svg'
import './styles/app.css'
export function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} class="logo" alt="Vite logo" />
</a>
<a href="https://preactjs.com" target="_blank">
<img src={preactLogo} class="logo preact" alt="Preact logo" />
</a>
</div>
<h1>Vite + Preact</h1>
<div class="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/app.jsx</code> and save to test HMR
</p>
</div>
<p>
Check out{' '}
<a
href="https://preactjs.com/guide/v10/getting-started#create-a-vite-powered-preact-app"
target="_blank"
>
create-preact
</a>
, the official Preact + Vite starter
</p>
<p class="read-the-docs">
Click on the Vite and Preact logos to learn more
</p>
</>
)
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="27.68" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 296"><path fill="#673AB8" d="m128 0l128 73.9v147.8l-128 73.9L0 221.7V73.9z"></path><path fill="#FFF" d="M34.865 220.478c17.016 21.78 71.095 5.185 122.15-34.704c51.055-39.888 80.24-88.345 63.224-110.126c-17.017-21.78-71.095-5.184-122.15 34.704c-51.055 39.89-80.24 88.346-63.224 110.126Zm7.27-5.68c-5.644-7.222-3.178-21.402 7.573-39.253c11.322-18.797 30.541-39.548 54.06-57.923c23.52-18.375 48.303-32.004 69.281-38.442c19.922-6.113 34.277-5.075 39.92 2.148c5.644 7.223 3.178 21.403-7.573 39.254c-11.322 18.797-30.541 39.547-54.06 57.923c-23.52 18.375-48.304 32.004-69.281 38.441c-19.922 6.114-34.277 5.076-39.92-2.147Z"></path><path fill="#FFF" d="M220.239 220.478c17.017-21.78-12.169-70.237-63.224-110.126C105.96 70.464 51.88 53.868 34.865 75.648c-17.017 21.78 12.169 70.238 63.224 110.126c51.055 39.889 105.133 56.485 122.15 34.704Zm-7.27-5.68c-5.643 7.224-19.998 8.262-39.92 2.148c-20.978-6.437-45.761-20.066-69.28-38.441c-23.52-18.376-42.74-39.126-54.06-57.923c-10.752-17.851-13.218-32.03-7.575-39.254c5.644-7.223 19.999-8.261 39.92-2.148c20.978 6.438 45.762 20.067 69.281 38.442c23.52 18.375 42.739 39.126 54.06 57.923c10.752 17.85 13.218 32.03 7.574 39.254Z"></path><path fill="#FFF" d="M127.552 167.667c10.827 0 19.603-8.777 19.603-19.604c0-10.826-8.776-19.603-19.603-19.603c-10.827 0-19.604 8.777-19.604 19.603c0 10.827 8.777 19.604 19.604 19.604Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

5
frontend/src/main.jsx Normal file
View file

@ -0,0 +1,5 @@
import { render } from 'preact'
import './styles/index.css'
import { App } from './app.jsx'
render(<App />, document.getElementById('app'))

View file

@ -0,0 +1,25 @@
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.preact:hover {
filter: drop-shadow(0 0 2em #673ab8aa);
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View file

@ -0,0 +1,68 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

7
frontend/vite.config.js Normal file
View file

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import preact from '@preact/preset-vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [preact()],
})

View file

@ -1,35 +0,0 @@
{
"name": "wf-radio",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"start-auto": "pnpx nodemon server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.5.2",
"dependencies": {
"ajv": "^8.17.1",
"bcrypt": "^5.1.1",
"better-sqlite3": "^11.9.1",
"dompurify": "^3.2.4",
"express": "^5.1.0",
"jsdom": "^26.0.0",
"jsonwebtoken": "^9.0.2",
"marked": "^15.0.7",
"mysql": "^2.18.1"
},
"pnpm": {
"onlyBuiltDependencies": [
"bcrypt",
"better-sqlite3"
]
},
"devDependencies": {
"nodemon": "^3.1.9"
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,38 +0,0 @@
// --- Imports ---
const express = require("express");
const app = express();
const configManager = require("./src/utils/configManager");
const { connectDatabase, initDatabase } = require('./src/database/index');
// --- Load configuration ---
const config = configManager.loadConfig();
// --- Body parsing ---
app.use(express.json()); // Necessary to parse JSON bodies
// Database connection
(async () => {
// --- Database connection ---
await connectDatabase();
await initDatabase();
// --- Routing ---
app.use("/", require("./src/routes/index"));
app.use("/mods", require("./src/routes/mods"));
app.use("/users", require("./src/routes/users"));
app.use("/list", require("./src/routes/list"));
app.use("/login", require("./src/routes/login"));
})();
// --- Launch ---
const port = config.port;
app.listen(port, () => {
console.log("Server listening on port " + port + "...");
})

View file

@ -1,17 +0,0 @@
const handleError = require("../middleware/errors");
const authService = require("../services/authService");
async function login(req, res) {
try {
const username = req.body.username;
const email = req.body.email;
const password = req.body.password
const token = await authService.login(username, email, password);
res.json({ token });
} catch (err) {
handleError(err, res);
}
}
module.exports = { login };

View file

@ -1,14 +0,0 @@
const index_service = require("../services/indexService");
const handleError = require("../middleware/errors");
async function getVersion(req, res) {
try {
const query_result = await index_service.getVersion();
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
module.exports = { getVersion };

View file

@ -1,68 +0,0 @@
const handleError = require("../middleware/errors");
const mod_service = require("../services/modService");
const { authorizeModModification, authenticateToken } = require("../middleware/auth");
async function listMods(req, res) {
try {
// Query
const query_result = await mod_service.getAllMods();
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function createMod(req, res) {
try {
// Authenticate
await authenticateToken(req);
// Query
const mod_data = req.body;
const user = req.token_infos.username;
const query_result = await mod_service.createMod(mod_data, user);
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function modifyMod(req, res) {
try {
// Authorize
authorizeModModification(req);
// Query
const mod_data = req.body;
const query_result = await mod_service.modifyMod(mod_data);
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function getModByName(req, res) {
try {
// Query
const name = req.params.name
const query_result = await mod_service.getModByName(name);
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function deleteMod(req, res) {
try {
// Authorize
authorizeModModification(req);
// Query
const name = req.params.name
const query_result = await mod_service.deleteMod(name);
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
module.exports = { listMods, getModByName, createMod, modifyMod, deleteMod };

View file

@ -1,63 +0,0 @@
const handleError = require("../middleware/errors");
const user_service = require("../services/userService")
const { authorizeUserModification } = require("../middleware/auth");
async function listUsers(req, res) {
try {
// Query
const query_result = await user_service.getAllUsers();
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function getUserByName(req, res) {
try {
// Query
const query_result = await user_service.getUserByName(req.params.name);
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function createUser(req, res) {
try {
// Query
const query_result = await user_service.createUser(req.body);
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function modifyUser(req, res) {
try {
// Query
const diff_data = req.body;
const query_result = await user_service.modifyUser(diff_data);
res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
async function deleteUser(req, res) {
try {
// Authenticate
await authorizeUserModification(req);
// Query
const user = req.params.name;
const token_user = req.token_infos
const query_result = await user_service.deleteUser(user, token_user);
return res.json(query_result);
} catch (error) {
handleError(error, res);
}
}
module.exports = { listUsers, getUserByName, createUser, modifyUser, deleteUser };

View file

@ -1,112 +0,0 @@
const MySQLDatabase = require("./mysql");
const SQLiteDatabase = require("./sqlite");
const { getConfig } = require("../utils/configManager");
let db;
async function connectDatabase() {
// Get config
const config = await getConfig();
// Choose database type
if (config.database.type === "mysql") {
db = new MySQLDatabase(config.database);
} else if (config.database.type === "sqlite") {
db = new SQLiteDatabase(config.database);
} else {
throw new Error("Invalid database type: ", config.database.type);
}
// Connect
await db.connect();
return db;
}
// Setups the database by creating the tables and the default objects
async function initDatabase() {
if (!db) {
throw new Error("Database is not connected");
}
// --- Users ---
// Uers table
db.exec(`CREATE TABLE IF NOT EXISTS Users (
username TINYTEXT PRIMARY KEY,
display_name TINYTEXT NOT NULL,
email TINYTEXT NOT NULL,
password TINYTEXT NOT NULL,
profile_picture LONGTEXT,
role TINYTEXT NOT NULL,
settings LONGTEXT
);`);
// --- Mods ---
// Mods table
db.exec(`CREATE TABLE IF NOT EXISTS Mods (
name TINYTEXT PRIMARY KEY,
display_name TINYTEXT NOT NULL,
author TINYTEXT NOT NULL,
description TINYTEXT NOT NULL,
FOREIGN KEY (author) REFERENCES Users(username)
);`);
// Mods complementary infos
db.exec(`CREATE TABLE IF NOT EXISTS ModInfos (
mod TINYTEXT PRIMARY KEY,
full_description TEXT NOT NULL,
license TINYTEXT,
custom_license TEXT,
links TEXT,
creation_date TINYTEXT NOT NULL,
downloads_count INT NOT NULL,
FOREIGN KEY (mod) REFERENCES Mods(name)
);`);
// Mods tags
db.exec(`CREATE TABLE IF NOT EXISTS ModTags (
mod TINYTEXT NOT NULL,
tag TINYTEXT NOT NULL,
FOREIGN KEY (mod) REFERENCES Mods(name)
);`);
// Mods versions
db.exec(`CREATE TABLE IF NOT EXISTS ModVersions (
mod TINYTEXT NOT NULL,
version_number TINYTEXT NOT NULL,
channel TINYTEXT NOT NULL,
changelog TEXT NOT NULL,
release_date TINYTEXT NOT NULL,
game_version TINYTEXT NOT NULL,
platform TINYTEXT NOT NULL,
environment TINYTEXT NOT NULL,
url TINYTEXT NOT NULL,
FOREIGN KEY (mod) REFERENCES Mods(name)
);`);
// User favorites (mods)
db.exec(`CREATE TABLE IF NOT EXISTS UserFavoriteMods (
username TINYTEXT NOT NULL,
mod TINYTEXT NOT NULL,
FOREIGN KEY (username) REFERENCES Users(username),
FOREIGN KEY (mod) REFERENCES Mods(name)
);`);
}
function getDatabase() {
return db;
}
module.exports = { getDatabase, connectDatabase, initDatabase };

View file

@ -1,30 +0,0 @@
class MySQLDatabase {
constructor(config) {
const mysql = require("mysql2/promise");
this.config = config;
}
async connect() {
this.db = await mysql.createConnection({
host: this.config.host,
user: this.config.user,
password: this.config.password,
database: this.config.database,
});
console.log("Connected to MySQL");
}
async close() {
await this.db.end();
}
async query(sql, params) {
throw new Error("Not implemented"); //TODO
const [results] = await this.db.execute(sql, params);
return results;
}
}
module.exports = MySQLDatabase;

View file

@ -1,70 +0,0 @@
const sqlite = require("better-sqlite3");
class SQLiteDatabase {
constructor(config) {
this.config = config;
this.db = null;
}
async connect() {
try {
this.db = new sqlite("./data/sqlite.db");
// this.db.pragma("journal_mode = WAL");
console.log("Connected to SQLite");
} catch (err) {
console.error("Error connecting to SQLite database: ", err);
process.exit(1);
}
}
async close() {
if (this.db && this.db.open) {
this.db.close();
console.debug("Closed database connection");
}
}
async query(sql, params = []) {
try {
if (params.length > 0) {
return this.db.prepare(sql).all(params);
} else {
return this.db.prepare(sql).all();
}
} catch (err) {
console.error("Error executing prepared query:", err);
throw err;
}
}
async exec(sql) {
try {
return this.db.exec(sql);
} catch (err) {
console.error("Error executing statement:", err)}
}
async prepare(sql, params = []) {
try {
if (params.length > 0) {
return this.db.prepare(sql).run(params);
} else {
return this.db.prepare(sql);
}
} catch (err) {
console.error("Error executing prepared statement:", err)}
}
async exists(table, attribute, value) {
try {
return this.db.prepare(`SELECT COUNT(*) FROM ${table} WHERE ${attribute} = ?`).get(value)['COUNT(*)'] > 0;
} catch (err) {
console.error("Error checking item existence");
}
}
}
module.exports = SQLiteDatabase;

View file

@ -1,85 +0,0 @@
const { getModByName } = require("../services/modService");
const { getModpackByName } = require("../services/modpackService");
const { getUserByName } = require("../services/userService");
const { verifyToken } = require("../utils/crypto");
const AppError = require("../utils/appError");
async function authenticateToken(req) {
const token = req.header("Authorization");
if (!token) {
throw new AppError(401, "Missing authorization header", "Unauthorized");
}
try {
req.token_infos = await verifyToken(token);
console.debug("Authorizing token from", req.token_infos);
} catch (err) {
throw new AppError(403, "Forbidden: Error verifying the authorization token");
}
return req.token_infos;
}
async function authorizeModModification(req) {
// Auth token
await authenticateToken(req);
// Get mod infos
if (!req.params || !req.params.name) {
throw new AppError(400, "No mod name was scpecified", "Bad request");
}
const mod_name = req.params.name;
const mod = await getModByName(mod_name);
if (!mod) {
throw new AppError(404, "No mod was found with this name", "Not found");
}
// Authorize
if ( mod.author != req.token_infos.username) {
throw new AppError(401, "Mod author differs from current user", "Unauthorized");
}
}
async function authorizeModpackModification(req) {
// Auth token
await authenticateToken(req);
// Get mod infos
if (!req.params || !req.params.name) {
throw new AppError(400, "No mod name was scpecified", "Bad request");
}
const modpack_name = req.params.name;
const modpack = await getModpackByName(modpack_name);
if (!modpack) {
throw new AppError(404, "No mod was found with this name", "Not found");
}
// Authorize
if ( modpack.author != req.token_infos.username) {
throw new AppError(401, "Mod author differs from current user", "Unauthorized");
}
}
async function authorizeUserModification(req) {
// Auth token
await authenticateToken(req);
// Get mod infos
if (!req.params || !req.params.name) {
throw new AppError(400, "No mod name was scpecified", "Bad request");
}
const user_name = req.params.name;
const user = await getUserByName(user_name);
if (!user) {
throw new AppError(404, "No user was found with this name", "Not found");
}
// Authorize
if ( user.username != req.token_infos.username) {
throw new AppError(401, "User to modify differs from current user", "Unauthorized");
}
}
module.exports = { authenticateToken, authorizeModModification, authorizeModpackModification, authorizeUserModification };

View file

@ -1,31 +0,0 @@
const AppError = require("../utils/appError");
const handleError = (err, res) => {
// Send error infos
if (err instanceof AppError) {
// Log
if (err.statusCode == 500) {
console.error("Error:", err.message);
if (err.debugMsg) {
console.debug(" >", err.debugMsg);
}
}
// Response
return res.status(err.statusCode).json({
status: err.status,
message: err.message
});
}
// Default error
console.error("Error:", err.message);
res.status(500).json({
message: 'Internal server error',
status: 500
});
}
module.exports = handleError;

View file

@ -1,12 +0,0 @@
const configManager = require("../utils/configManager");
async function getVersion() {
const version = await configManager.getVersion();
const res = {
version: version
};
return res;
}
module.exports = { getVersion }

View file

@ -1,192 +0,0 @@
const { getDatabase } = require('../database/index');
const AppError = require('../utils/appError');
const db = getDatabase();
// --- Get ---
async function getAllMods() {
return await db.query("SELECT name, display_name, author, description FROM Mods");
}
async function getModByName(name) {
return await db.query("SELECT name, display_name, author FROM Mods WHERE name = ?;", [name]);
}
async function getModFullInfos(name) {
// Query
const base_infos = db.query(`SELECT * FROM Mods WHERE name = ?`, [name]);
const other_infos = db.query(`SELECT full_description, license, links, creation_date, downloads_count
FROM ModInfos WHERE name = ?`, [name]);
const tags = getModTags(name);
// Merge
const res = {...await base_infos, ...await other_infos, ...tags};
return res;
}
async function listVersions(mod_name) {
return await db.query("SELECT * FROM ModVersions WHERE mod = ?", [mod_name]);
}
async function getVersionByNumber(mod_name, version_number) {
return await db.query(`SELECT * FROM ModVersions
WHERE mod = ?
AND version_number = ?;`,
[mod_name, version_number]);
}
async function getVersion(mod_name, version_number, game_version, platform, environment) {
return await db.query(`SELECT * FROM ModVersions
WHERE mod = ?
AND version_number = ?
AND game_version = ?
AND platform = ?
AND environment = ?;`,
[mod_name, version_number, game_version, platform, environment]);
}
// --- Create ---
async function createMod(name, display_name, author, description, mod_infos) {
// Extract infos
const { full_description, license, links, creation_date, tags } = mod_infos;
// Mods table
await db.prepare("INSERT INTO Mods (name, display_name, author, description) \
VALUES (?, ?, ?, ?)",
[name, display_name, author, description]);
// ModInfos table
await db.prepare(`INSERT INTO ModInfos (mod, full_description, license, links, creation_date, downloads_count)
VALUES (?, ?, ?, ?, ?, ?)`,
[name, full_description, license.type, links.toString(), creation_date, 0]);
// Tags
if (tags) {
const tags_proc = addTags(name, tags, []);
}
// License
if (license.type == "custom") {
await db.prepare(`UPDATE ModInfos SET custom_license = ?
WHERE mod = ?`,
[license.content, name]);
}
// Await
if (tags) {
await tags_proc;
}
return;
}
async function addVersion(mod, version_number, channel, changelog, release_date, game_version, platform, environment, url) {
await db.prepare(`INSERT INTO ModVersions (mod, version_number, channel, changelog, release_date, game_version, environment, platform, url)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);`,
[mod, version_number, channel, changelog, release_date, game_version, environment, platform, url]);
return;
}
async function addTags(mod, tags) {
// Add asynchronously
const promises = tags.map(async (tag) => {
db.query(`INSERT INTO ModTags (mod, tag)
VALUES (?, ?);`,
[mod, tag]);
});
await Promise.all(promises);
return;
}
// --- Update ---
async function updateMod(name, display_name, author, description) {
if (display_name) {
await updateModAttributes(name, "display_name", display_name);
}
if (author) {
await updateModAttribute(nale, "author", author);
}
if (description) {
await updateModAttribute(name, "description", description)
}
}
// --- Delete ---
async function deleteMod(name) {
await db.prepare("DELETE FROM Mods WHERE name = ?", [name]);
return;
}
async function deleteVersion(name, version_number, game_version, platform, environment) {
await db.prepare(`DELETE FROM ModVersions WHERE mod = ?
AND version_number = ?
AND game_version = ?
AND platform = ?
AND environment = ?;`,
[name, version_number, game_version, platform, environment]);
return;
}
async function deleteTags(mod, tags) {
// Remove asynchronously
const promises = tags.map(async (tag) => {
db.query(`DELETE FROM ModTags
WHERE mod = ? AND tag = ?;`, [mod, tag]);
});
await Promise.all(promises);
return;
}
// --- Utils ---
async function updateModAttribute(name, attribute, value) {
await db.prepare(`UPDATE Mods SET ${attribute} = ? WHERE name = ?`, [value, name]);
return;
}
async function updateModInfosAttribute(name, attribute, value) {
await db.prepare(`UPDATE ModInfos SET ${attribute} = ? WHERE name = ?`, [value, name]);
return;
}
async function exists(name) {
return db.exists("Mods", "name", name);
}
async function containsVersion(name, version_number, game_version, platform, environment) {
throw new AppError(501, "Not implemented");
// return db.exists("Mods", "name", name);
}
async function containsTag(name, tag) {
throw new AppError(501, "Not implemented");
// return db.exists("Mods", "name", name);
}
// --- Exports ---
module.exports = { getAllMods, getModByName, getModFullInfos,
listVersions, getVersionByNumber, getVersion,
createMod, addVersion, addTags,
updateMod,
deleteMod, deleteVersion, deleteTags,
exists };

View file

View file

@ -1,125 +0,0 @@
const { getDatabase } = require('../database/index');
const AppError = require('../utils/appError');
const db = getDatabase();
// --- Get ---
async function getAllUsers() {
return db.query("SELECT username, display_name, email, profile_picture FROM Users");
}
async function getUserByName(name) {
return await db.query("SELECT username, display_name, profile_picture, role FROM Users WHERE username = ?;", [name]);
}
async function getUserByEmail(email) {
return await db.query("SELECT email, username FROM Users WHERE email = ?;", [email]);
}
async function getFullUserInfos(name) {
return await db.query("SELECT username, display_name, email, profile_picture, role, settings FROM Users WHERE username = ?;", [name]);
}
async function getUserPassword(name) {
return await db.query("SELECT username, password FROM Users WHERE username = ?;", [name]);
}
async function exists(name) {
return await db.exists("Users", "username", name);
}
// --- Create ---
async function createUser( username, email, password, displayName, profilePicture, settings ) {
// Create user
await db.prepare(`INSERT INTO Users (username, email, password, display_name, role )
VALUES (?, ?, ?, ?, ? )`,
[username, email, password, displayName, "user"]);
// Handle nullable fields
if (profilePicture) {
await updateUserAttribute(username, "profile_picture", profilePicture);
}
if (settings) {
await updateUserAttribute(username, "settings", settings);
}
return;
}
async function addFavoriteMods(username, favs) {
const promises = favs.map(async (mod) => {
db.query(`INSERT INTO UserFavoriteMods
(username, mod) VALUES (?, ?);`,
[username, mod]);
});
await Promise.all(promises);
return;
}
// --- Update ---
async function updateUser(username, display_name, email, profile_picture, settings) {
if (display_name) {
await updateUserAttribute(username, "display_name", display_name);
}
if (email) {
await updateUserAttribute(username, "email", email);
}
if (profile_picture) {
await updateUserAttribute(username, "profile_picture", profile_picture)
}
if (settings) {
await updateUserAttribute(username, "settings", settings);
}
}
async function updateUserPassword(username, password) {
await db.prepare(`UPDATE Users SET password = ? WHERE username = ?`, [password, username]);
}
async function updateUserAttribute(username, attribute, value) {
await db.prepare(`UPDATE Users SET ${attribute} = ? WHERE username = ?`, [value, username]);
return;
}
// --- Delete ---
async function deleteUser(username) {
await db.prepare("DELETE FROM Users WHERE username = ?", [username]);
return;
}
async function deleteFavoriteMods(username, favs) {
const promises = favs.map(async (mod) => {
db.query(`DELETE FROM UserFavoriteMods
WHERE username = ? AND mod = ?;`, [username, mod]);
});
// Await
await Promise.all(promises);
return;
}
// --- Exports ---
module.exports = { getAllUsers, getUserByName, getUserByEmail, getFullUserInfos, getUserPassword,
createUser, addFavoriteMods,
updateUser,
deleteUser, deleteFavoriteMods,
exists }

View file

@ -1,11 +0,0 @@
const express = require("express");
const controller = require("../controllers/index");
const router = express.Router();
router.get('/version', async (res, req) => {
controller.getVersion(res, req);
});
module.exports = router;

View file

@ -1,24 +0,0 @@
const express = require("express");
const { listMods } = require("../controllers/mods");
// const { listModpacks } = require("../controllers/modpacks");
const { listUsers } = require("../controllers/users");
const router = express.Router();
// List mods
router.get("/mods", async (req,res) => {
listMods(req, res);
});
// List modpacks
// router.get("/modpacks", async (req,res) => {
// listModpacks(req, res);
// });
// List users
router.get("/users", async (req,res) => {
listUsers(req, res);
});
module.exports = router;

View file

@ -1,11 +0,0 @@
const express = require("express");
const controller = require("../controllers/auth");
const router = express.Router();
// Login
router.post("/", async (req, res) => {
controller.login(req, res);
});
module.exports = router;

View file

View file

@ -1,27 +0,0 @@
const express = require("express");
const controller = require("../controllers/mods");
const router = express.Router();
// Create a mod
router.post("/", async (req, res) => {
controller.createMod(req, res);
});
// Modify mod
router.put("/:name", async (req,res) => {
controller.modifyMod(req,res);
});
// Get mod infos
router.get("/:name", async (req,res) => {
controller.getModByName(req, res);
});
// Delete mod
router.delete("/:name", async (req,res) => {
controller.deleteMod(req, res);
});
module.exports = router;

View file

@ -1,27 +0,0 @@
const express = require("express");
const controller = require("../controllers/users");
const router = express.Router();
// List users
router.get("/", async (req,res) => {
controller.listUsers(req,res);
});
// Create a user
router.post("/", async (req, res) => {
controller.createUser(req, res);
})
// Get user infos
router.get("/:name", async (req,res) => {
controller.getUserByName(req, res);
});
// Delete user
router.delete("/:name", async (req,res) => {
controller.deleteUser(req, res);
});
module.exports = router;

View file

@ -1,21 +0,0 @@
const Ajv = require("ajv");
const ajv = new Ajv();
// --- Schemas ---
const AuthUserSchema = {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
password: { type: 'string', minLength: 3, maxLength: 30 },
},
required: ['email', 'password'],
additionalProperties: false
};
const validateAuthUserData = ajv.compile(AuthUserSchema);
// --- Exports ---
module.exports = { validateAuthUserData, validateAuthNodeData };

View file

@ -1,21 +0,0 @@
const Ajv = require("ajv");
const ajv = new Ajv();
// --- Schemas ---
//TODO
const newModSchema = {
type: 'object',
properties: {
name: { type: 'string'},
},
required: ['name'],
additionalProperties: false
};
const validateNewModData = ajv.compile(newModSchema);
// --- Exports ---
module.exports = { validateNewModData };

View file

@ -1,21 +0,0 @@
const Ajv = require("ajv");
const ajv = new Ajv();
// --- Schemas ---
//TODO
const newModpackSchema = {
type: 'object',
properties: {
name: { type: 'string'},
},
required: ['name'],
additionalProperties: false
};
const validateNewModpackData = ajv.compile(newModpackSchema);
// --- Exports ---
module.exports = { validateNewModpackData };

View file

@ -1,23 +0,0 @@
const Ajv = require("ajv");
const ajv = new Ajv();
// --- Schemas ---
//TODO
const newUserSchema = {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
name: { type: 'string' },
password: { type: 'string', minLength: 3, maxLength: 30 },
},
required: ['name', 'email', 'password'],
additionalProperties: false
};
const validateNewUserData = ajv.compile(newUserSchema);
// --- Exports ---
module.exports = { validateNewUserData };

View file

@ -1,85 +0,0 @@
const userModel = require("../models/user");
const AppError = require("../utils/appError");
const cryptoUtils = require("../utils/crypto");
const configManager = require("../utils/configManager");
const validate = require("../utils/validate_legacy");
const JWT_Secret = configManager.getJWTSecret();
async function login(username, email, password) {
// Check for null
if (!(username || email) || !password) {
throw new AppError(400, "Bad request", "missing credentials");
}
// Get user data
let user_get;
if (email) { // If matches email
user_get = await userModel.getUserByEmail(email);
} else
if (username) { // if matches username
user_get = await userModel.getUserByName(username);
} else {
console.debug("Failed finding user, weird...")
throw new AppError(401, "Unauthorized", "Invalid credentials");
}
// Check if user exists
if (!user_get || user_get.length == 0) {
// throw new AppError(401, "Unauthorized: No user with this name");
throw new AppError(401, "Unauthorized", "Invalid credentials");
}
// Just in case
if (user_get.length > 1) {
throw new AppError(500, "Internal server error", "Found multiple users with this name or email, please contact administration");
}
const user = user_get[0];
// Get user password
const saved_password_get = await userModel.getUserPassword(user.username);
// Check if retrieved password sucessfully
if (!saved_password_get || saved_password_get.length == 0) {
throw new AppError(500, "Unable to retrieve user password");
}
saved_password = saved_password_get[0].password;
// Check if retrieved password sucessfully again
if (!saved_password) {
throw new AppError(500, "Unable to retrieve user password");
}
// Check if passwords match
const passwords_match = await cryptoUtils.passwordsMatch(password, saved_password)
if (!passwords_match) {
// throw new AppError(401, "Unauthorized: Invalid password");
console.debug(password, "differs from", saved_password);
throw new AppError(401, "Unauthorized", "Invalid credentials");
}
const payload = { type: "user",
username: user.username,
email: user.email,
role: user.role };
const token = await cryptoUtils.signToken(payload);
return token;
// // Check if passwords match
// const passwords_match = await bcrypt.compare(password, user[0].password);
// if (!passwords_match) {
// // throw new AppError(401, "Unauthorized: Invalid password");
// console.debug("Password doesn't match")
// throw new AppError(401, "Unauthorized", "Invalid credentials");
// }
// return jwt.sign({ username: user[0].username, role: user[0].role }, await JWT_Secret);
}
// function authorizeRole(user, roles) {
// if (!user || !roles.includes(user.role)) {
// throw new AppError(401, "Unauthorized: You don't have the necessary permissions to access this resource");
// }
// }
module.exports = { login };

View file

@ -1,7 +0,0 @@
const model = require("../models/index");
async function getVersion() {
return model.getVersion();
}
module.exports = { getVersion }

View file

@ -1,164 +0,0 @@
const model = require("../models/mod");
const AppError = require("../utils/appError");
const { validateModData } = require("../utils/validate_legacy");
const { mdToHtml } = require("../utils/convert");
const { sanitizeModData } = require("../utils/sanitize");
// --- Get ---
async function getAllMods() {
return model.getAllMods();
}
async function getModByName(name) {
const res = model.getModByName(name);
if (res.length == 0) {
throw new AppError(404, "Cannot find mod with this name", "Not found");
}
return res[0];
}
async function getFullModInfos(name) {
const res = model.getFullModInfos(name);
if (res.length == 0) {
throw new AppError(404, "Cannot find mod with this name", "Not found");
}
return res[0];
}
async function getModVersion(infos) {
const { mod, version_number, game_version, platform, environment} = infos;
const res = model.getModVersion(mod, version_number, game_version, platform, environment);
if (res.length == 0) {
throw new AppError(404, "Cannot find mod with this name", "Not found");
}
return res[0];
}
// --- Create ---
async function createMod(mod_data, author) {
// Check body validity
//TODO
console.warn("Skipping validity checks for createMod");
// await validateModData(mod_data);
// Generate data
const { name, display_name, description, mod_infos } = mod_data;
mod_infos.full_description = await mdToHtml(mod_infos.full_description); // Convert
await sanitizeModData(mod_data); // Sanitize
//TODO
mod_infos.creation_date = 0
// Write changes to database
await model.createMod(name, display_name, author, description, mod_infos);
// Return
return getModByName(name);
}
async function addVersion(version_data) {
// Validate
//TODO
console.warn("Skipping validity checks for addVersion");
// Generate data
const { mod_name, version_number, channel, changelog, game_version,
platform, environment, url } = version_data; // Split
changelog = await mdToHtml(changelog); // Convert
await sanitizeModData(mod_data); // Sanitize
const release_date = (new Date()).toLocaleDateString();
// Write changes
await model.addVersion(mod_name, version_number, channel, changelog,
release_date, game_version, platform, environment, url); // Database
// Return
return await model.getModVersion(mod_name, version_number, game_version, platform, environment );
}
async function addTags(mod, tags) {
// Validate
//TODO
console.warn("Skipping validity checks for addTags");
// Write changes
await model.addTags(mod, tags);
// Return
const { tags:res } = await model.getFullModInfos(mod);
return { "mod": mod, "tags": res};
}
// --- Update ---
async function updateMod(diff_data) {
//TODO
throw new AppError(501, "Not implemented");
}
// Delete
async function deleteMod(name) {
// Check existence
const mod = await model.getModByName(name);
if (!mod) {
throw new AppError(404, "No was found with this name", "Not found")
}
// Authorize
// TODO move outside of this function
if (mod.author != mod.user) {
throw new AppError(403, "You don't have the necessary permissions to execute this action", "Forbidden");
}
// Write changes to database
await model.deleteMod(name);
// Return
return mod;
}
async function deleteVersion(version_infos) {
// Validate
// TODO
// Generate data
const res = await getModVersion(version_infos);
const { mod, version_number, game_version, platform, environment} = version_infos;
// Write changes to db
await model.deleteVersion(mod, version_number, game_version, platform, environment);
// Return
return res;
}
async function deleteTags(mod, tags) {
// Validate (check existence)
//TODO
console.warn("Skipping validity checks for deleteTags");
// Wites changes to db
await model.deleteTags(mod, tags);
// Return
const { tags:res } = await model.getFullModInfos(mod);
return { "mod": mod, "tags": res};
}
module.exports = { getAllMods, getModByName, getFullModInfos,
createMod, addTags, addVersion,
updateMod,
deleteMod, deleteTags, deleteVersion };

View file

@ -1,46 +0,0 @@
const model = require("../models/user");
const AppError = require("../utils/appError");
const cryptoUtils = require("../utils/crypto");
const { validateUserData } = require("../utils/validate_legacy");
const { sanitizeUserData } = require("../utils/sanitize");
async function getAllUsers() {
return await model.getAllUsers();
}
async function getUserByName(name) {
const res = await model.getUserByName(name);
return res[0];
}
async function createUser(user_data) {
// Check body validity
// TODO
// Sanitize
// TODO
// Gather data
const { username, email, password, display_name, profile_picture, settings } = user_data
const password_hash = await cryptoUtils.hashPassword(password);
await model.createUser(username, email, password_hash, display_name, null, null);
return model.getUserByName(username);
}
async function deleteUser(name, token_user) {
// Check existence
const exists = await model.exists(name);
if (!exists) {
throw new AppError(404, "Cannot find user with this name", "Not found");
}
const res = await model.getUserByName(name);
await model.deleteUser(name);
return res;
}
module.exports = { getAllUsers, getUserByName, createUser, deleteUser };

View file

@ -1,27 +0,0 @@
class AppError extends Error {
constructor(statusCode, message, status = "", debugMsg = "") {
super(message);
this.statusCode = statusCode;
this.debugMsg = debugMsg;
// Get status
if (status === "") {
if (statusCode.toString().startsWith("4")) {
this.status = "Fail";
} else {
this.status = "Error";
}
} else {
this.status = status;
}
}
}
exports.tryCatch = (controller) => async (req, res, next) => {
try {
await controller(req, res, next);
} catch(err) {
next(err);
}
}
module.exports = AppError;

View file

@ -1,89 +0,0 @@
// --- Define constants ---
// Imports
const fs = require("fs");
const path = require("path");
const { version } = require("../../package.json");
// Var decalaration
const config_folder = "config";
const config_file_name = "config.json"
// Global variables
let config = {};
// --- Default config ---
const default_config = {
"port": 8000,
"users": {
"admin": {
"username": "admin",
"password": "admin"
}
},
"database": {
"type": "sqlite"
},
"auth" : {
"JWT_secret": "HGF7654EGBNKJNBJH6754356788GJHGY",
"tokenExpiry": "1h"
}
}
// --- Functions ---
function loadConfig() {
let user_config;
// Parse
try {
// Get user config
user_config = JSON.parse(fs.readFileSync(path.resolve(path.join(config_folder, config_file_name))));
// Warns
if (!user_config.auth || !user_config.auth.JWT_secret) {
console.warn("WARNING: No JWT secret provided, using the default one. Please note that using the default secret is a major security risk.")
}
// Merge default and user configs (default values)
config = { ...default_config, ...user_config };
}
catch (err) {
// Error messages
console.debug("Error:", err)
console.error("Error loading configuration, using the default settings");
console.debug("Search path:", path.resolve("./"));
console.debug("Config file:", path.resolve(path.join(config_folder, config_file_name)))
config = default_config;
}
return config;
}
async function getConfig() {
return config;
}
async function getJWTSecret() {
return config.auth.JWT_secret || process.env.JWT_secret;
}
async function getVersion() {
// Could be done with process.env.npm_package_version
// but may not work without without npm
return version;
}
// Exports
module.exports = { loadConfig, getConfig, getVersion, getJWTSecret };

View file

@ -1,11 +0,0 @@
const marked = require("marked");
async function mdToHtml(md_content) {
if (md_content) {
return marked.parse(md_content);
} else {
return "";
}
}
module.exports = { mdToHtml };

View file

@ -1,61 +0,0 @@
// --- Imports ---
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
const { getConfig, getJWTSecret } = require("./configManager");
// --- Config ---
// Declarations
let JWT_Secret;
let token_expiry;
// Constant values
const saltRounds = 12;
// Load
(async () => {
const config = await getConfig();
JWT_Secret = await getJWTSecret();
token_expiry = config.auth.tokenExpiry;
signature_algorithm = config.auth.signatureAlgorithm;
})();
// --- Functions ---
async function hashPassword(passwd) {
const hash = bcrypt.hashSync(passwd, saltRounds);
return hash;
}
async function passwordsMatch(password, hashed_password) {
return await bcrypt.compare(password, hashed_password);
}
async function signToken(payload, options = null) {
if (options == null) {
return jwt.sign(payload, JWT_Secret, { expiresIn: token_expiry, });
}
else {
return jwt.sign(payload, JWT_Secret, options);
}
}
function verifyToken(token) {
return new Promise( async (resolve, reject) => {
await jwt.verify( token, JWT_Secret, (err, user) => {
if (err) {
reject(err);
} else {
resolve(user);
}
});
});
}
// --- Exports ---
module.exports = { passwordsMatch, hashPassword, verifyToken, signToken };

View file

@ -1,19 +0,0 @@
const createDOMPurify = require("dompurify");
const { JSDOM } = require("jsdom");
// Initialize
const window = new JSDOM("").window;
const DOMPurify = createDOMPurify(window);
async function sanitizeText(text) {
return DOMPurify.sanitize(text);
}
async function sanitizeModData(mod_data) {
console.warn("Skipping sanitanization (not implemented)");
// mod_data.displayName = await sanitizeText(mod_data.displayName);
// mod_data.otherInfos.description = await sanitizeText(mod_data.otherInfos.description);
// mod_data.otherInfos.changelogs = await sanitizeText(mod_data.otherInfos.changelogs);
}
module.exports = { sanitizeText, sanitizeModData };

View file

@ -1,51 +0,0 @@
// --- Imports ---
const AppError = require("./appError");
// --- Functions ---
async function validateNewModData(mod_data) {
throw new AppError(501, "Not implemented");
//TODO
// try {
// node_schemas.validateNewModData(node_data);
// } catch (err) {
// throw new AppError(400, "Missing or invalid fields", "Bad request", err);
// }
}
async function validateNewUserData(user_data) {
throw new AppError(501, "Not implemented");
//TODO
// try {
// node_schemas.validateNewUserData(node_data);
// } catch (err) {
// throw new AppError(400, "Missing or invalid fields", "Bad request", err);
// }
}
async function validateCretendials(identifier, password) {
throw new AppError(501, "Not implemented");
}
// --- Utils ---
async function isEmail(text) {
const email_regex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
return email_regex.test(text);
}
async function isID(text) {
const id_regex = /[a-zA-Z0-9_]+/;
return id_regex.test(text);
}
module.exports = { validateNewModData, validateNewUserData, isEmail, isID };

View file

@ -1,98 +0,0 @@
const mod_model = require("../models/mod");
const user_model = require("../models/user");
const AppError = require("./appError");
async function validateModData(mod_data) {
//TODO WIP
// Check fields existence
const not_null = mod_data &&
Object.keys(mod_data).length == 5 &&
mod_data.name &&
mod_data.displayName &&
mod_data.author &&
mod_data.versions != null;
// mod_data.otherInfos != null &&
// Object.keys(mod_data.otherInfos).length == 0 &&
// mod_data.otherInfos.description != null &&
// mod_data.otherInfos.links != null &&
// mod_data.otherInfos.tags != null &&
// mod_data.otherInfos.screenshots != null &&
// mod_data.otherInfos.license != null &&
// mod_data.otherInfos.changelogs != null;
if (!not_null) {
console.debug("Item is missing expected fields:", mod_data);
throw new AppError(400, "Bad request", "Missing expected fields");
}
// Check fields format (check if sanitized)
const is_valid_name = /^[a-zA-Z0-9_]+$/.test(mod_data.name);
const is_valid_displayName = true;
// const is_valid_displayName = /^[a-zA-Z0-9_]+$/.test(mod_data.name); // Temporary
// const
const is_valid = is_valid_name && is_valid_displayName;
if (!is_valid) {
console.debug("Fields are not following the expected formats");
throw new AppError(400, "Bad request", "The provided fields don't match the expected format");
}
// Check if mod already exists
const exists = await mod_model.exists(mod_data.name);
if (exists) {
console.debug("Error: Item already exists");
throw new AppError(403, "Forbidden", "Content with this name already exists");
}
}
async function validateUserData(user_data) {
throw new AppError(501, "Not implemented");
//TODO
// Check fields existence
// ...
if (!not_null) {
console.debug("Missing expected fields:", mod_data);
throw new AppError(400, "Bad request: Missing expected fields");
}
// Check fields format (check if sanitized)
const is_valid_username = /^[a-zA-Z0-9_]+$/.test(user_data.username);
// const is_valid_email = ...
// ...
const is_valid = is_valid_username && is_valid_email;
if (!is_valid) {
console.debug("Fields are not following the expected formats");
throw new AppError(400, "Bad request: The provided fields don't match the expected format");
}
// Check if user already exists
const exists = await user_model.exists(user_data.username);
if (exists) {
console.debug("Error: User already exists");
throw new AppError(403, "Forbidden: User with this name already exists");
}
}
async function validateCretendials(identifier, password) {
throw new AppError(501, "Not implemented");
}
async function isEmail(text) {
const email_regex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
return email_regex.test(text);
}
async function isID(text) {
const id_regex = /[a-zA-Z0-9_]+/;
return id_regex.test(text);
}
module.exports = { validateModData, validateUserData, isEmail, isID };