Changed file structure to implement the frontend properly and created frontend template

This commit is contained in:
Gu://em_ 2025-05-12 16:10:41 +02:00
parent feceab03b1
commit 32c0ffd715
41 changed files with 3784 additions and 0 deletions

View file

@ -0,0 +1,27 @@
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

@ -0,0 +1,89 @@
// --- 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

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

View file

@ -0,0 +1,61 @@
// --- 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

@ -0,0 +1,19 @@
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

@ -0,0 +1,51 @@
// --- 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

@ -0,0 +1,98 @@
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 };