This commit is contained in:
2026-04-26 00:08:27 +02:00
parent 92074e7f60
commit c3e5448597
40 changed files with 1783 additions and 54 deletions

View File

@@ -1,5 +1,7 @@
const express = require("express");
const cors = require('cors');
const cookieParser = require('cookie-parser');
const passport = require('passport');
const dotenv = require('dotenv');
@@ -14,14 +16,39 @@ if(process.env.NODE_ENV) {
const app = express();
const connectDB = require("./db");
// JSON LIMIT EXPRESS
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({
extended: true,
limit: '50mb'
}));
// connect database
connectDB();
// CORS
app.use(cookieParser());
app.use(cors({
origin: 'http://localhost:3000',
credentials: true, // if using cookies/auth
}));
// ROUTES (NO AUTH)
app.use('/user', require('./routes/user'));
// AUTH
checkAuth = passport.authenticate('jwt', { session: false });
app.use(checkAuth);
// ROUTES WITH AUTH
/*
app.use('/campaign', require('./routes/campaign'));
app.use('/maps', require('./routes/map'));
app.use('/datagen', require('./routes/datagen'));
app.use('/admin', require('./routes/admin'));
*/
app.get("/api/test", (req, res) => {
console.log("Hey");
res.json({"message": "Hello from backend!"});

View File

@@ -0,0 +1,16 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {type: String, required: true},
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String },
date: { type: Date, default: Date.now},
admin: {type: Boolean, default: false},
image: { type: String },
setupCode: { type: String },
settings: { type: Object }
});
module.exports = mongoose.model('User', UserSchema);

175
backend/src/routes/user.js Normal file
View File

@@ -0,0 +1,175 @@
const express = require('express')
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
const secret = require('../services/keys').secret;
const rateLimitMiddleware = require("../services/rate-limiter");
const crypto = require("crypto");
const { isAdmin } = require('../services/middleware');
const User = require("../models/User");
const upload = require("../services/storage");
// Admin registers new user
router.post('/register', isAdmin, async (req, res) => {
try {
let setupCode = crypto.randomBytes(64).toString('base64url');
let user = new User({
admin: false,
name: crypto.randomBytes(16).toString('base64url'),
username: crypto.randomBytes(16).toString('base64url'),
email: crypto.randomBytes(16).toString('base64url'),
setupCode
});
await user.save();
res.json({ status: "ok", code: setupCode });
} catch (err) {
res.json({ status: "error", msg: "internal" });
}
});
// User gets if setup account exists given the query code
router.get('/verify-setup', async (req, res) => {
try {
const user = await User.findOne({ setupCode: req.query.code });
if (user) {
res.json({ status: "ok", code: req.query.code });
} else {
res.json({ status: "error", msg: "not-exists" });
}
} catch (err) {
res.json({ status: "error", msg: "internal" });
}
});
// User posts the parameters of his new account given by admin
router.post('/setup', rateLimitMiddleware, async (req, res) => {
const { name, username, email, password } = req.body;
const setupCode = req.query.code;
if (!(name && username && email && password && setupCode)) {
return res.json({ status: "error", msg: "params" });
}
try {
const user = await User.findOne({ setupCode });
if (!user) {
return res.json({ status: "error", msg: "not-found" });
}
const sameUser = await User.findOne({ email });
if (sameUser) {
return res.json({ status: "error", msg: "already-email" });
}
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
user.username = username;
user.email = email;
user.setupCode = undefined;
await user.save();
res.json({ status: "ok" });
} catch (err) {
res.json({ status: "error", msg: "internal" });
}
});
// Login post
router.post('/login', rateLimitMiddleware, async (req, res) => {
const { username, password } = req.body;
if (!(username && password)) {
return res.json({ status: "error", msg: "params" });
}
try {
const user = await User.findOne({ username });
if (!user) {
return res.json({ status: "error", msg: "wrong" });
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.json({ status: "error", msg: "wrong" });
}
const payload = {
_id: user._id,
username: user.username,
name: user.name,
email: user.email,
admin: user.admin,
settings: user.settings
};
const token = await new Promise((resolve, reject) => {
jwt.sign(payload, secret, { expiresIn: 172800 }, (err, token) => {
if (err) reject(err);
else resolve(token);
});
});
res.json({ status: "ok", token, msg: "success" });
} catch (err) {
res.json({ status: "error", msg: "internal" });
}
});
// Upload avatar post
router.post("/upload-avatar", upload.single("image"), passport.authenticate('jwt', {session: false}), async (req, res) => {
try {
const imageName = req.file.filename;
await User.updateOne(req.user, { image: imageName });
res.json({ status: "ok", msg: "uploaded" });
} catch (err) {
res.json({ status: "error", msg: "internal" });
}
});
router.get("/retrieve-avatar", async (req, res) => {
try {
const data = await User.findOne({ username: req.query.username });
res.json({ status: "ok", image: data.image });
} catch (err) {
res.json({ status: "error" });
}
});
router.get("/has-admin", async (req, res) => {
try {
const data = await User.findOne({ admin: true });
if (data) res.json({ status: "ok" });
else res.json({ status: "init" });
} catch (err) {
res.json({ status: "error" });
}
});
router.post("/update-settings", passport.authenticate('jwt', {session: false}), async (req, res) => {
try {
await User.updateOne(req.user, { settings: req.body.settings });
res.json({ status: "ok", settings: req.body.settings });
} catch (err) {
res.json({ status: "error", msg: "internal" });
}
});
router.get('/get-settings', passport.authenticate('jwt', {session: false}), async (req, res) => {
try {
const data = await User.findOne(req.user);
res.json({ status: "ok", settings: data.settings });
} catch (err) {
res.json({ status: "error", msg: "internal" });
}
});
module.exports = router;

View File

@@ -0,0 +1,5 @@
const fs = require('fs');
module.exports = {
secret: "putyoursecrethere"
}

View File

@@ -0,0 +1,17 @@
const User = require("../models/User");
async function isAdmin(req, res, next) {
try {
const user = await User.findOne(req.user).lean();
if (user && user.admin) {
return next();
}
res.json({ status: "error", msg: "unauthorized" });
} catch (err) {
res.json({ status: "error", msg: err.message });
}
}
module.exports = {
isAdmin
}

View File

@@ -0,0 +1,11 @@
const setRateLimit = require("express-rate-limit");
// Rate limit middleware
const rateLimitMiddleware = setRateLimit({
windowMs: 60 * 60 * 1000,
max: 150,
message: "Has fet masses peticions de login en una hora (ets un robot???)",
headers: true,
});
module.exports = rateLimitMiddleware;

View File

@@ -0,0 +1,14 @@
const multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
var upload = multer({storage: storage});
module.exports = upload;