diff --git a/backendV2/routes/admin/database/apiDaraMgmt.database.js b/backendV2/routes/admin/database/apiDataMgmt.database.js similarity index 100% rename from backendV2/routes/admin/database/apiDaraMgmt.database.js rename to backendV2/routes/admin/database/apiDataMgmt.database.js diff --git a/backendV2/routes/admin/database/userDataMgmt.database.js b/backendV2/routes/admin/database/userDataMgmt.database.js index e69de29..3f12718 100644 --- a/backendV2/routes/admin/database/userDataMgmt.database.js +++ b/backendV2/routes/admin/database/userDataMgmt.database.js @@ -0,0 +1,79 @@ +import mysql from "mysql2"; +import dotenv from "dotenv"; +dotenv.config(); + +const pool = mysql + .createPool({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + }) + .promise(); + +export const createUser = async ( + username, + role, + password, + isAdmin, + email, + first_name, + last_name +) => { + const [result] = await pool.query( + "INSERT INTO users (username, role, password, is_admin, email, first_name, last_name) VALUES (?, ?, ?, ?, ?, ?, ?)", + [username, role, password, isAdmin, email, first_name, last_name] + ); + if (result.affectedRows > 0) return { success: true }; + return { success: false }; +}; + +export const deleteUserById = async (userId) => { + const [result] = await pool.query("DELETE FROM users WHERE id = ?", [userId]); + if (result.affectedRows > 0) return { success: true }; + return { success: false }; +}; + +export const changePassword = async (userId, newPassword) => { + const [result] = await pool.query( + "UPDATE users SET password = ? WHERE id = ?", + [newPassword, userId] + ); + if (result.affectedRows > 0) return { success: true }; + return { success: false }; +}; + +export const editUserById = async ( + userId, + first_name, + last_name, + role, + email, + is_admin +) => { + const [result] = await pool.query( + "UPDATE users SET first_name = ?, last_name = ?, role = ?, email = ?, is_admin = ? WHERE id = ?", + [first_name, last_name, role, email, is_admin, userId] + ); + if (result.affectedRows > 0) return { success: true }; + return { success: false }; +}; + +export const getAllUsers = async () => { + const [result] = await pool.query( + "SELECT id, username, first_name, last_name, role, email, is_admin FROM users" + ); + if (result.length > 0) return { success: true, data: result }; + return { success: false }; +}; + +export const getUserById = async (userId) => { + const [rows] = await pool.query( + "SELECT id, username, first_name, last_name, role, email, is_admin FROM users WHERE id = ?", + [userId] + ); + if (rows.length === 0) { + return { success: false }; + } + return { success: true, data: rows[0] }; +}; diff --git a/backendV2/routes/admin/database/userMgmt.database.js b/backendV2/routes/admin/database/userMgmt.database.js index 7a055fd..4eff8e3 100644 --- a/backendV2/routes/admin/database/userMgmt.database.js +++ b/backendV2/routes/admin/database/userMgmt.database.js @@ -11,7 +11,7 @@ const pool = mysql }) .promise(); -export const loginFunc = async (username, password) => { +export const loginAdmin = async (username, password) => { const [rows] = await pool.query( "SELECT id, username, first_name, last_name, role, is_admin FROM users WHERE username = ? AND password = ?", [username, password] diff --git a/backendV2/routes/admin/userDataMgmt.route.js b/backendV2/routes/admin/userDataMgmt.route.js index e69de29..3c01df1 100644 --- a/backendV2/routes/admin/userDataMgmt.route.js +++ b/backendV2/routes/admin/userDataMgmt.route.js @@ -0,0 +1,126 @@ +import express from "express"; +import { authenticateAdmin } from "../../services/authentication.js"; +const router = express.Router(); +import nodemailer from "nodemailer"; +import dotenv from "dotenv"; +dotenv.config(); + +// database funcs import +import { + createUser, + deleteUserById, + editUserById, + changePassword, + getAllUsers, + getUserById, +} from "./database/userDataMgmt.database.js"; + +router.post("/create-user", authenticateAdmin, async (req, res) => { + const username = req.body.username; + const role = req.body.role; + const password = req.body.password; + const isAdmin = req.body.isAdmin; + const email = req.body.email; + const first_name = req.body.first_name; + const last_name = req.body.last_name; + const result = await createUser( + username, + role, + password, + isAdmin, + email, + first_name, + last_name + ); + if (result.success) { + return res.status(201).json({ message: "User created successfully" }); + } + return res.status(500).json({ message: "Failed to create user" }); +}); + +router.delete("/delete-user/:id", authenticateAdmin, async (req, res) => { + const userId = req.params.id; + const result = await deleteUserById(userId); + if (result.success) { + return res.status(200).json({ message: "User deleted successfully" }); + } + return res.status(500).json({ message: "Failed to delete user" }); +}); + +router.post("/edit-user/:id", authenticateAdmin, async (req, res) => { + const password = req.body.password; + const first_name = req.body.first_name; + const last_name = req.body.last_name; + const role = req.body.role; + const email = req.body.email; + const userId = req.params.id; + const is_admin = req.body.is_admin; + + const result = await editUserById( + userId, + password, + first_name, + last_name, + role, + email, + is_admin + ); + + if (result.success) { + return res.status(200).json({ message: "User edited successfully" }); + } + return res.status(500).json({ message: "Failed to edit user" }); +}); + +router.post("/change-password", authenticateAdmin, async (req, res) => { + const username = req.body.username; + const password = req.body.password; + + const result = await changePassword(username, password); + + if (result.success) { + return res.status(200).json({ message: "Password reset successfully" }); + } + return res.status(500).json({ message: "Failed to reset password" }); +}); + +router.post("/edit-user/:id", authenticateAdmin, async (req, res) => { + const userId = req.params.id; + const first_name = req.body.first_name; + const last_name = req.body.last_name; + const role = req.body.role; + const email = req.body.email; + const is_admin = req.body.is_admin; + + const result = await editUserById( + userId, + first_name, + last_name, + role, + email, + is_admin + ); + + if (result.success) { + return res.status(200).json({ message: "User edited successfully" }); + } + return res.status(500).json({ message: "Failed to edit user" }); +}); + +router.get("/users", authenticateAdmin, async (req, res) => { + const result = await getAllUsers(); + if (result.success) { + return res.status(200).json({ users: result.data }); + } + return res.status(500).json({ message: "Failed to retrieve users" }); +}); + +router.get("/user/:id", authenticateAdmin, async (req, res) => { + const result = await getUserById(req.params.id); + if (result.success) { + return res.status(200).json({ user: result.data }); + } + return res.status(500).json({ message: "Failed to retrieve user" }); +}); + +export default router; diff --git a/backendV2/routes/admin/userMgmt.route.js b/backendV2/routes/admin/userMgmt.route.js index a2dd56c..e58ac75 100644 --- a/backendV2/routes/admin/userMgmt.route.js +++ b/backendV2/routes/admin/userMgmt.route.js @@ -1,22 +1,22 @@ import express from "express"; -import { authenticate, generateToken } from "../services/tokenService.js"; +import { authenticate, generateToken } from "../../services/authentication.js"; const router = express.Router(); import nodemailer from "nodemailer"; import dotenv from "dotenv"; dotenv.config(); // database funcs import -import { loginFunc } from "./database/userMgmt.database.js"; +import { loginAdmin } from "./database/userMgmt.database.js"; router.post("/login", async (req, res) => { - const result = await loginFunc(req.body.username, req.body.password); + const result = await loginAdmin(req.body.username, req.body.password); if (result.success) { const token = await generateToken({ username: result.data.username, first_name: result.data.first_name, last_name: result.data.last_name, - role: result.data.role, + admin: result.data.is_admin, }); return res.status(200).json({ message: "Login erfolgreich", token }); } diff --git a/backendV2/server.js b/backendV2/server.js index 7603dae..2a1249a 100644 --- a/backendV2/server.js +++ b/backendV2/server.js @@ -3,13 +3,20 @@ import cors from "cors"; import env from "dotenv"; import loansMgmtRouter from "./routes/app/loanMgmt.route.js"; import userMgmtRouter from "./routes/app/userMgmt.route.js"; +import userDataMgmtRouter from "./routes/admin/userDataMgmt.route.js" env.config(); const app = express(); const port = 8002; app.use(cors()); +// frontend routes app.use("/api/loans", loansMgmtRouter); app.use("/api/users", userMgmtRouter); + +// admin routes + +app.use("/api/admin/user-data", userDataMgmtRouter); + // Increase body size limits to support large CSV JSON payloads app.use(express.urlencoded({ extended: true, limit: "10mb" })); app.set("view engine", "ejs"); diff --git a/backendV2/services/authentication.js b/backendV2/services/authentication.js index 41d4301..7b7b891 100644 --- a/backendV2/services/authentication.js +++ b/backendV2/services/authentication.js @@ -17,6 +17,29 @@ export async function generateToken(payload) { .sign(secret); } +export async function authenticateAdmin(req, res, next) { + const authHeader = req.headers["authorization"]; + if (!authHeader) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const [scheme, token] = authHeader.split(" "); + if (!/^Bearer$/i.test(scheme) || !token) { + return res.status(401).json({ message: "Unauthorized" }); + } + + try { + const payload = await verifyToken(token); + if (!payload?.admin) { + return res.status(403).json({ message: "Forbidden: admin only" }); + } + req.user = payload; + return next(); + } catch { + return res.status(403).json({ message: "Forbidden" }); + } +} + export async function authenticate(req, res, next) { const authHeader = req.headers["authorization"]; const apiKey = req.params.apiKey;