/***************************************************************************** * Import package * *****************************************************************************/ import express, {Express, NextFunction, Request, Response} from 'express'; import {Connection, createConnection, ResultSetHeader, RowDataPacket} from "mysql2/promise"; import session from "express-session"; import crypto from "crypto" import * as path from "node:path"; /***************************************************************************** * Database Connection * *****************************************************************************/ let database: Connection; async function connectDatabase() { try { database = await createConnection({ host: "localhost", user: "root", password: "toortoor", database: "horizon_changers" }); await database.connect(); console.log("Database is connected"); } catch (error) { console.log(`Database connection failed: ${error}`); } } connectDatabase(); /***************************************************************************** * Define and start web-app server, define json-Parser * *****************************************************************************/ const app: Express = express(); app.listen(8080, () => { console.log('Server started: http://localhost:8080'); }); app.use(express.json()); /***************************************************************************** * session management configuration * *****************************************************************************/ app.use(session({ // save session even if not modified resave: true, // save session even if not used saveUninitialized: true, // forces cookie set on every response needed to set expiration (maxAge) rolling: true, // encrypt session-id in cookie using "secret" as modifier secret: "f47ac10b-58cc-4372-a567-0e02b2c3d479", // set some cookie-attributes. Here expiration-date (offset in ms) cookie: { maxAge: 1000 * 60 * 60 } // 1h })); declare module 'express-session' { interface SessionData { user?: User } } /***************************************************************************** * Datastructure * *****************************************************************************/ export interface User { user_id: number; firstName: string; lastName: string; adress: string; role: string; eMail: string; } /** * @apiDefine SessionExpired * * @apiError (Client Error) {401} SessionNotFound The session of the user is expired or was not set * * @apiErrorExample SessionNotFound: * HTTP/1.1 401 Unauthorized * { * "message":"Session expired, please log in again." * } */ function isLoggedIn(req: Request, res: Response, next: NextFunction) { // Abstract middleware route for checking login state of the user if (req.session.user != null) { // User has an active session and is logged in, continue with route next(); } else { // User is not logged in res.status(401).send({ message: 'Session expired, please log in again', }); } } /***************************************************************************** * HTTP ROUTES: LOGIN * *****************************************************************************/ /** * @api {get} /login Request login state * @apiName GetLogin * @apiGroup Login * * @apiSuccess {User} user The user object * @apiSuccess {string} message Message stating that the user is still logged in * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "user":{ * "id":1, * "username":"admin", * "givenName":"Peter", * "familyName":"Kneisel", * "creationTime":"2017-11-12T09:33:25.000Z" * }, * "message":"User still logged in" * } * * @apiError (Client Error) {401} SessionNotFound The session of the user is expired or was not set * * @apiErrorExample SessionNotFound: * HTTP/1.1 401 Unauthorized * { * "message":"Session expired, please log in again." * } */ app.get('/login', isLoggedIn, (req: Request, res: Response): void => { res.status(200).send({ message: 'User still logged in', user: req.session.user, // Send user object to client for greeting message }); }); /** * @api {post} /login Send login request * @apiName PostLogin * @apiGroup Login * * @apiBody {string} username Username of the user to log in * @apiBody {string} password Password of the user to log in * * @apiSuccess {User} user The user object * @apiSuccess {string} message Message stating the user logged in successfully * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "user":{ * "id":1, * "username":"admin", * "firstName":"Peter", * "lastName":"Kneisel", * "creationTime":"2017-11-12T09:33:25.000Z" * }, * "message":"Successfully logged in" * } * * @apiError (Client Error) {401} LoginIncorrect The login data provided is not correct. * @apiError (Server Error) {500} DatabaseRequestFailed The request to the database failed. * * @apiErrorExample LoginIncorrect: * HTTP/1.1 401 Unauthorized * { * "message":"Username or password is incorrect." * } * * * @apiErrorExample DatabaseRequestFailed: * HTTP/1.1 500 Internal Server Errror * { * "message":"Database request failed: ..." * } */ app.post('/login', async (req: Request, res: Response): Promise<void> => { // Read data from request const eMail: string = req.body.eMail; const password: string = req.body.password; // Create database query and data const data: [string, string] = [ eMail, password //crypto.createHash("sha512").update(password).digest('hex') ]; console.log(data) const query: string = 'SELECT * FROM user WHERE eMail = ? AND password = ?;'; try { const [rows] = await database.query<RowDataPacket[]>(query, data); // Check if database response contains exactly one entry if (rows.length === 1) { // Login data is correct, user is logged in const user: User = { user_id: rows[0].user_id, firstName: rows[0].firstName, lastName: rows[0].lastName, eMail: rows[0].eMail, adress: rows[0].adress, role: rows[0].role, }; req.session.user = user; // Store user object in session for authentication res.status(200).send({ message: 'Erfolgreich eingeloggt :)!', user: user, // Send user object to client for greeting message }); } else { // Login data is incorrect res.status(401).send({ message: 'Passwort und/oder Email stimmt/stimmen nicht.', }); } } catch (error: unknown) { // Unknown error res.status(500).send({ message: 'Database request failed: ' + error, }); } }); /** * @api {post} /logout Logout user * @apiName PostLogout * @apiGroup Logout * * @apiSuccess {string} message Message stating that the user is logged out * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * message: "Successfully logged out" * } */ app.post('/logout', isLoggedIn, (req: Request, res: Response): void => { // Log out user req.session.user = undefined; // Delete user from session res.status(200).send({ message: 'Erfolgreich ausgeloggt! Bis zum nächsten mal :)', }); }); /***************************************************************************** * HTTP ROUTES: USER, USERS * *****************************************************************************/ /** * @api {post} /user Create a new user * @apiName postUser * @apiGroup User * * @apiBody {string} givenName First name of the user * @apiBody {string} familyName Last name of the user * * @apiSuccess {string} message Message stating the new user has been created successfully * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "message":"Successfully created new user" * } * * @apiError (Client Error) {400} NotAllMandatoryFields The request did not contain all mandatory fields * * @apiErrorExample NotAllMandatoryFields: * HTTP/1.1 400 Bad Request * { * "message":"Not all mandatory fields are filled in" * } */ app.post('/user', async (req: Request, res: Response): Promise<void> => { // Read data from request body const firstName: string = req.body.firstName; const lastName: string = req.body.lastName; const eMail: string = req.body.eMail; const password: string = req.body.password; const repeatPassword: string = req.body.repeatPassword; // add a new user if first- and familyName exist if (eMail && password && firstName && lastName) { console.log(password); console.log(repeatPassword); // Check if password and repeatPassword match if (password !== repeatPassword) { res.status(400).send({ message: 'Passwörter stimmen nicht überein!', }); } const data: [string, string, string, string, string] = [ eMail, crypto.createHash("sha512").update(password).digest('hex'), firstName, lastName, new Date().toLocaleString() ]; const query: string = 'INSERT INTO user (firstName, lastName, eMail, password) VALUES (?, ?, ?, ?);'; // Execute database query try { const [result] = await database.query<ResultSetHeader>(query, data); res.status(201).send({ message: `Erfolgreiches Erstellen einer User-ID: ${result.insertId}`, }); } catch (error) { // Send response res.status(500).send({ message: 'Database request failed: ' + error, }); } } else { res.status(400).send({ message: 'Da fehlt doch noch was ;)!', }); } }); /** * @api {get} /user/:userId Get user with given id * @apiName getUser * @apiGroup User * * @apiParam {number} userId The id of the requested user * * @apiSuccess {User} user The requested user object * @apiSuccess {string} message Message stating the user has been found * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "user":{ * "id":1, * "givenName":"Peter", * "familyName":"Kneisel", * "creationTime":"2018-10-21 14:19:12" * }, * "message":"Successfully got user" * } * * @apiError (Client Error) {404} NotFound The requested user can not be found * * @apiErrorExample NotFound: * HTTP/1.1 404 Not Found * { * "message":"The requested user can not be found." * } */ app.get('/user/:userId', isLoggedIn, async (req: Request, res: Response): Promise<void> => { // Read data from request parameters const data: [number] = [ parseInt(req.params.userId) ]; // Search user in database const query: string = 'SELECT * FROM user WHERE user_id = ?;'; try { const [rows] = await database.query<RowDataPacket[]>(query, data); if (rows.length === 1) { const user: User = { user_id: rows[0].user_id, firstName: rows[0].firstName, lastName: rows[0].lastName, eMail: rows[0].eMail, adress: rows[0].adress, role: rows[0].role }; // Send user list to client res.status(200).send({ user: user, message: 'Nutzer gefunden.', }); } else { res.status(404).send({ message: 'Gesuchter Nutzer wurde nicht gefunden.', }); } } catch (error) { // Database operation has failed res.status(500).send({ message: 'Database request failed: ' + error }); } }); /** * @api {put} /user/:userId Update user with given id * @apiName putUser * @apiGroup User * * @apiParam {number} userId The id of the requested user * @apiBody {string} givenName The (new) first name of the user * @apiBody {string} familyName The (new) last name of the user * * @apiSuccess {string} message Message stating the user has been updated * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "message":"Successfully updated user ..." * } * * @apiError (Client Error) {400} NotAllMandatoryFields The request did not contain all mandatory fields * @apiError (Client Error) {404} NotFound The requested user can not be found * * @apiErrorExample NotAllMandatoryFields: * HTTP/1.1 400 Bad Request * { * "message":"Not all mandatory fields are filled in" * } * * @apiErrorExample NotFound: * HTTP/1.1 404 Not Found * { * "message":"The user to update could not be found" * } */ app.put('/user/:userId', isLoggedIn, async (req: Request, res: Response): Promise<void> => { // Read data from request const userId: number = parseInt(req.params.userId); const firstName: string = req.body.firstName; const lastName: string = req.body.lastName; // Check that all arguments are given if (firstName && lastName) { // Create database query and data const data: [string, string, number] = [ firstName, lastName, userId ]; const query: string = 'UPDATE user SET firstName = ?, lastName = ? WHERE user_id = ?;'; // Execute database query try { const [result] = await database.query<ResultSetHeader>(query, data); if (result.affectedRows != 1) { res.status(404).send({ message: 'The user to update could not be found', }); } else { res.status(200).send({ message: `Successfully updated user ${firstName} ${lastName}`, }); } } catch (error) { res.status(500).send({ message: 'Database request failed: ' + error }); } } else { res.status(400).send({ message: 'Not all mandatory fields are filled in', }); } }); /** * @api {delete} /user/:userId Delete user with given id * @apiName deleteUser * @apiGroup User * * @apiParam {number} userId The id of the requested user * * @apiSuccess {string} message Message stating the user has been updated * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "message":"Successfully deleted user ..." * } */ app.delete('/user/:userId', isLoggedIn, async (req: Request, res: Response): Promise<void> => { // Read data from request const userId: number = parseInt(req.params.userId); // Delete user const query: string = 'DELETE FROM user WHERE user_id = ?;'; try { const [result] = await database.query<ResultSetHeader>(query, userId); if (result.affectedRows === 1) { res.status(200).send({ message: `Successfully deleted user `, }); } else { res.status(404).send({ message: 'The user to be deleted could not be found', }); } } catch (error) { // Database operation has failed res.status(500).send({ message: 'Database request failed: ' + error }); } }); /** * @api {get} /users Get all users * @apiName getUsers * @apiGroup Users * * @apiSuccess {User[]} userList The list of all users * @apiSuccess {string} message Message stating the users have been found * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "userList": [ * { * "givenName": "Hans", * "familyName": "Mustermann", * "creationTime": "2018-11-04T13:02:44.791Z", * "id": 1 * }, * { * "givenName": "Bruce", * "familyName": "Wayne", * "creationTime": "2018-11-04T13:03:18.477Z", * "id": 2 * } * ] * "message":"Successfully requested user list" * } */ app.get('/users', isLoggedIn, async (req: Request, res: Response): Promise<void> => { // Send user list to client const query: string = 'SELECT * FROM user;'; try { const [rows] = await database.query<RowDataPacket[]>(query); // Create local user list to parse users from database const userList: User[] = []; // Parse every entry for (const row of rows) { const user: User = { user_id: row.user_id, firstName: row.firstName, lastName: row.lastName, eMail: row.eMail, adress: row.adress, role: row.role }; userList.push(user); } // Send user list to client res.status(200).send({ userList: userList, message: 'Successfully requested user list' }); } catch (error) { // Database operation has failed res.status(500).send({ message: 'Database request failed: ' + error }); } }); /***************************************************************************** * STATIC ROUTES * *****************************************************************************/ app.use(express.static(path.join(__dirname, "..", "..", "client")));