Newer
Older
/*****************************************************************************
* Import package *
*****************************************************************************/
//Importieren der Dattenbanken
import express, {Express, NextFunction, Request, Response} from 'express'; //Web-Framework
import {Connection, createConnection, ResultSetHeader, RowDataPacket} from "mysql2/promise"; // Datenbankinteraktion
import session from "express-session"; //Sitzungsverwaltung
import crypto from "crypto"; //Passwort-Hashing-Funktion
import * as path from "node:path"; //Arbeiten mit Dateipfaden
//Datenbankverbindung
/*****************************************************************************
* Database Connection *
*****************************************************************************/
let database: Connection;
// Funktion wird definiert, um die Verbindung zu einer Datenbank asynchron herzustellen
database = await createConnection({
host: "localhost",
user: "root",
password: "toortoor",
console.log(`Database connection failed: ${error}`);//Fehlerbehandlung
}
}
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 *
*****************************************************************************/
// Sitzungen werden gespeichert, auch wenn sie nicht verändert wurden.
//Sitzungen werden gespeichert, auch wenn sie nicht genutzt werden.
// Die Gültigkeit des Cookies wird bei jeder Anfrage erneuert.
// Ein geheimer Schlüssel zur Verschlüsselung der Sitzungsdaten.
cookie: { maxAge: 1000 * 60 * 60 } // 1h
}));
declare module 'express-session' {
interface SessionData {
user?: User
}
}
/*****************************************************************************
* Datastructure *
*****************************************************************************/
//Datenstruktur User: Interface zur Definition der Benutzerstruktur.
user_id: number;
firstName: string;
lastName: string;
adress: string;
role: string;
eMail: string;
//Middleware: Login-Status prüfen --Prüft, ob ein Benutzer eingeloggt ist. Falls nicht, wird ein HTTP-Status 401 (Unauthorized) zurückgegeben.
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 *
*****************************************************************************/
/**
* @apiDescription Überprüft, ob ein Benutzer noch eingeloggt ist. Gibt bei Erfolg die Benutzerdaten zurück.
*
* @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":{
* "user_id":42,
* "firstName":"Peter",
* "lastName":"Kneisel",
* "eMail":"admin",
* },
* "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 Benutzer-Login
* @apiName BenutzerLogin
* @apiGroup User
* @apiDescription Authentifiziert einen Benutzer anhand von E-Mail und Passwort. Speichert den Benutzer bei erfolgreichem Login in der Session.
* @apiBody {String} eMail E-Mail-Adresse des Benutzers. Muss angegeben werden.
* @apiBody {String} password Passwort des Benutzers. Muss angegeben werden.
* @apiSuccess {String} message Erfolgsnachricht.
* @apiSuccess {Object} user Details des authentifizierten Benutzers.
* @apiSuccess {Number} user.user_id ID des Benutzers.
* @apiSuccess {String} user.firstName Vorname des Benutzers.
* @apiSuccess {String} user.lastName Nachname des Benutzers.
* @apiSuccess {String} user.eMail E-Mail-Adresse des Benutzers.
* @apiSuccess {String} user.adress Adresse des Benutzers (bei Login nicht verpflichtete Angabe).
* @apiSuccess {String} user.role Rolle des Benutzers (Standardeinstellung user).
*
* @apiError {String} message Fehlermeldung.
*
* @apiErrorExample {json} 401 Fehler (Ungültige Login-Daten):
* @apiErrorExample {json} 500 Fehler (Datenbankproblem):
* HTTP/1.1 500 Internal Server Error
app.post('/login', async (req: Request, res: Response): Promise<void> => {
// Read data from request
const password: string = req.body.password;
// Create database query and data
const data: [string, string] = [
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,
};
req.session.user = user; // Store user object in session for authentication
res.status(200).send({
});
}
} 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
* {
app.post('/logout', isLoggedIn, (req: Request, res: Response): void => {
// Log out user
req.session.user = undefined; // Delete user from session
res.status(200).send({
});
});
/*****************************************************************************
* HTTP ROUTES: USER, USERS *
*****************************************************************************/
/**
*
* @api {post} /user Benutzerregistrierung
* @apiName BenutzerRegistrierung
* @apiDescription Registriert einen neuen Benutzer und loggt ihn anschließend automatisch ein.
*
* @apiBody {String} firstName Vorname des Benutzers. Muss angegeben werden.
* @apiBody {String} lastName Nachname des Benutzers. Muss angegeben werden.
* @apiBody {String} eMail E-Mail-Adresse des Benutzers. Muss angegeben werden.
* @apiBody {String} password Passwort des Benutzers. Muss angegeben werden.
* @apiBody {String} repeatPassword Wiederholung des Passworts. Muss mit `password` übereinstimmen.
* @apiSuccess {string} message Bestätigungsnachricht.
* @apiSuccess {Object} user Details des neu erstellten Benutzers.
* @apiSuccess {Number} user.user_id ID des Benutzers.
* @apiSuccess {String} user.firstName Vorname des Benutzers.
* @apiSuccess {String} user.lastName Nachname des Benutzers.
* @apiSuccess {String} user.eMail E-Mail-Adresse des Benutzers.
* @apiSuccess {String} user.adress Adresse des Benutzers (falls vorhanden, optional).
* @apiSuccess {String} user.role Rolle des Benutzers (Standardwert: "user").
* "message": "Bitte alle Felder ausfüllen!"
* }
*
* @apiErrorExample {json} 400 Fehler (Passwörter stimmen nicht überein):
* HTTP/1.1 400 Bad Request
* {
* "message": "Passwörter stimmen nicht überein!"
* }
*
* @apiErrorExample {json} 500 Fehler (Datenbankproblem):
* HTTP/1.1 500 Internal Server Error
* {
* "message": "Datenbankanfrage fehlgeschlagen: [Fehlerdetails]"
* }
*
* @apiErrorExample {json} 500 Fehler (Benutzer nicht abrufbar):
* HTTP/1.1 500 Internal Server Error
* {
* "message": "Registrierung erfolgreich, aber der Benutzer konnte nicht abgerufen werden."
app.post('/user', async (req: Request, res: Response): Promise<void> => {
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;
// Check, if any given value is empty
if (!firstName || !lastName || !eMail || !password || !repeatPassword) {
res.status(400).send({
message: "Bitte alle Felder ausfüllen!",
});
return;
}
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
// Check if password and repeatPassword match
if (password !== repeatPassword) {
res.status(400).send({
message: 'Passwörter stimmen nicht überein!',
});
return;
}
const data: [string, string, string, string] = [
eMail,
crypto.createHash("sha512").update(password).digest('hex'),
firstName,
lastName,
];
const query: string = 'INSERT INTO user (eMail, password, firstName, lastName) VALUES (?, ?, ?, ?);';
try {
const [result] = await database.query<ResultSetHeader>(query, data);
// Hol den neu erstellten Benutzer aus der Datenbank
const [userRows] = await database.query<RowDataPacket[]>(
'SELECT * FROM user WHERE user_id = ?;',
[result.insertId]
);
if (userRows.length === 1) {
const user: User = {
user_id: userRows[0].user_id,
firstName: userRows[0].firstName,
lastName: userRows[0].lastName,
eMail: userRows[0].eMail,
adress: userRows[0].adress || '',
role: userRows[0].role || 'user',
};
// Speichere den Benutzer in der Session
req.session.user = user;
message: 'Registrierung erfolgreich, aber der Benutzer konnte nicht abgerufen werden.',
} catch (error) {
res.status(500).send({
message: 'Datenbankanfrage fehlgeschlagen: ' + error,
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
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,
});
}
} catch (error) {
// Database operation has failed
res.status(500).send({
message: 'Database request failed: ' + error
});
}
});
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;
// Create database query and data
const data: [string, string, number] = [
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}`,
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
});
}
} 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
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
});
}
});
//stellt sicher, dass nur eingeloggte Benutzer Zugriff auf die Benutzerliste haben.
// Die Route ruft alle Benutzer aus der Tabelle user in der Datenbank ab.
// Sie wandelt die Ergebnisse der Datenbank in ein standardisiertes Format (User-Objekte) um.
app.get('/users', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
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")));