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 Login
* @apiName Login
* @apiGroup Login
* @apiDescription Authentifiziert einen Benutzer/ Admin anhand von E-Mail und Passwort. Speichert den Benutzer/ Admin bei erfolgreichem Login in der Session.
* @apiBody {String} eMail E-Mail-Adresse des Benutzers/ Admins. Muss angegeben werden.
* @apiBody {String} password Passwort des Benutzers/ Admins. Muss angegeben werden.
* @apiSuccess {String} message Erfolgsnachricht.
* @apiSuccess {Object} user Details des authentifizierten Benutzers.
* @apiSuccess {Number} user.admin_id ID des Admins.
* @apiSuccess {String} user.firstName Vorname des Benutzers/ Admins.
* @apiSuccess {String} user.lastName Nachname des Benutzers/ Admins.
* @apiSuccess {String} user.eMail E-Mail-Adresse des Benutzers/ Admins.
* @apiSuccess {String} user.adress Adresse des Benutzers/ Admins (bei Login nicht verpflichtete Angabe).
* @apiSuccess {String} user.role Rolle des Benutzers/ Admins (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; //Variable firstName wird erstellt, mit req.body kann man auf Nutzdaten zugreifen, in dem Falle auf first name
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;
}
// 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] = [
firstName,
lastName,
eMail,
crypto.createHash("sha512").update(password).digest('hex')
];
//soll in user Tabelle eingepflegt werden
const query: string = 'INSERT INTO user (firstName, lastName, eMail, password) VALUES (?, ?, ?, ?);';
const [result] = await database.query<ResultSetHeader>(query, data); //daten werden von dem neu registrierten Nutzer gespeichert
// Hol den neu erstellten Benutzer aus der Datenbank
const [userRows] = await database.query<RowDataPacket[]>(
'SELECT * FROM user WHERE user_id = ?;',
);
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,
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/*****************************************************************************
* HTTP ROUTES: USER, USERS *
*****************************************************************************/
/**
*
* @api {get} /user/:user_id Benutzerinformationen abrufen
* @apiName BenutzerInformationenAbrufen
* @apiGroup User
*
* @apiDescription Ruft die Informationen eines Benutzers anhand der Benutzer-ID ab.
* Die Benutzerdaten werden in der aktuellen Session gespeichert, wenn der Benutzer gefunden wird.
*
* @apiSuccess {String} message Bestätigungsnachricht.
* @apiSuccess {Object} user Details des gefundenen 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.
*
* @apiSuccessExample Erfolgreiche Antwort:
* HTTP/1.1 200 OK
* {
* "user": {
* "user_id": 2,
* "firstName": "Bob",
* "lastName": "Hase",
* "eMail": "bob.hase@outlook.com",
* "adress": "Musterstraße 9, 12345 Musterstadt",
* "role": "user"
* }
* }
*
* @apiError {String} message Fehlermeldung.
*
* @apiErrorExample {json} 401 Fehler (Benutzer nicht gefunden):
* HTTP/1.1 401 Unauthorized
* {
* "message": "Benutzer nicht gefunden"
* }
*
* @apiErrorExample {json} 500 Fehler (Datenbankproblem):
* HTTP/1.1 500 Internal Server Error
* {
* "message": "Database request failed: [Fehlerdetails]"
* }
*
* @apiPermission Authentifiziert
*/
app.get('/user/:user_id', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
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
req.session.user = user; // Store user object in session for authentication
// Login data is incorrect
res.status(401).send({
message: 'Passwort ist falsch',
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
/**
* @api {put} /user Aktualisiere Benutzerdaten
* @apiName UpdateUser
* @apiGroup User
* @apiPermission loggedIn
*
* @apiDescription Diese Route ermöglicht es eingeloggten Benutzern, ihre persönlichen Daten wie Vorname, Nachname, E-Mail-Adresse, Passwort und Adresse zu aktualisieren.
* Der Benutzer muss eingeloggt sein, um diese Route nutzen zu können.
*
*
* @apiBody {String} firstName Vorname des Benutzers. (Pflichtfeld)
* @apiBody {String} lastName Nachname des Benutzers. (Pflichtfeld)
* @apiBody {String} eMail E-Mail-Adresse des Benutzers. (Pflichtfeld)
* @apiBody {String} password Passwort des Benutzers. Wird als SHA-512-Hash gespeichert. (Pflichtfeld)
* @apiBody {String} adress Adresse des Benutzers. (Optional)
*
* @apiSuccess {String} message Erfolgsmeldung, dass die Daten erfolgreich aktualisiert wurden.
*
* @apiError {Number} 400 Fehlende Pflichtfelder: Es wurden nicht alle erforderlichen Felder ausgefüllt.
* @apiError {Number} 404 Benutzer nicht gefunden: Der Benutzer konnte in der Datenbank nicht gefunden werden.
* @apiError {Number} 500 Datenbankfehler: Es gab ein Problem mit der Datenbankanfrage.
*
* @apiSuccessExample {json} Erfolgreiche Antwort:
* HTTP/1.1 200 OK
* {
* "message": "Deine Daten wurden erfolgreich abgeändert"
* }
*
* @apiErrorExample {json} Fehlende Pflichtfelder:
* HTTP/1.1 400 Bad Request
* {
* "message": "Not all mandatory fields are filled in"
* }
*
* @apiErrorExample {json} Benutzer nicht gefunden:
* HTTP/1.1 404 Not Found
* {
* "message": "The user to update could not be found"
* }
*
* @apiErrorExample {json} Datenbankfehler:
* HTTP/1.1 500 Internal Server Error
* {
* "message": "Database request failed: [Fehlermeldung]"
* }
*/
app.put('/user', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
console.log(req.body);
const user_id: number = Number(req.session.user?.user_id); //user_id wird zu Number gemacht weil könnte "theoretisch" undefined sein,. aber eig wegen isloggedin nicht
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 adress: string = req.body.adress;
// Create database query and data
const data: [string, string] = [
eMail,
crypto.createHash("sha512").update(password).digest('hex'),
];
const query: string = 'SELECT * FROM user WHERE eMail = ? AND password = ?;';
try {
const [result] = await database.query<RowDataPacket[]>(query, data);
if (result.length != 1) {
res.status(404).send({
});
}
} catch (error) {
res.status(500).send({
message: 'Database request failed: ' + error
});
}
// Check that all arguments are given
const query: string = 'UPDATE user SET firstName = ?, lastName = ?, eMail = ?, password = ?, adress = ? WHERE user_id = ?;';
// Execute database query
try {
const [result] = await database.query<ResultSetHeader>(query, data);
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
res.status(404).send({
message: 'The user to update could not be found',
});
} else {
res.status(200).send({
message: `Deine Daten wurden erfolgreich abgeändert`,
});
}
} catch (error) {
res.status(500).send({
message: 'Database request failed: ' + error
});
}
} else {
res.status(400).send({
message: 'Not all mandatory fields are filled in',
});
}
});
// update route admin
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;
const eMail: string = req.body.eMail;
const adress: string = req.body.adress;
const role: string = req.body.role;
// Check that all arguments are given
if (firstName && lastName && eMail && role) {
// Create database query and data
const data: [string, string, string, string, string, number] = [
firstName,
lastName,
eMail,
adress,
role,
userId
];
const query: string = 'UPDATE userlist SET firstName = ?, lastName = ?, eMail = ?, adress = ?, role = ?, 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 Benutzerprofil löschen
* @apiName BenutzerprofilLoeschen
* @apiDescription Löscht das Benutzerprofil des aktuell eingeloggten Benutzers aus der Datenbank und beendet die Session.
* "message": "Profil erfolgreich gelöscht."
* }
*
* @apiError {String} message Fehlermeldung.
*
* @apiErrorExample 404 Fehler (Benutzer nicht gefunden):
* HTTP/1.1 404 Not Found
* {
* "message": "Profil konnte nicht gelöscht werden. Versuche es später erneut."
* }
*
* @apiErrorExample 500 Fehler (Datenbankproblem):
* HTTP/1.1 500 Internal Server Error
* {
* "message": "Datenbankanfrage fehlgeschlagen: [Fehlerdetails]"
// Route zum Löschen des eigenen Benutzerprofils
app.delete('/user', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
console.log("delete user called")
const user_id: number = Number(req.session.user?.user_id);
// Führt die Löschoperation in der Datenbank aus
const [result] = await database.query<ResultSetHeader>(query, [user_id]);
// Löscht die Sitzung und sendet Erfolgsmeldung
req.session.destroy((err) => {
if (err) {
console.error('Session destruction error:', err);
}
res.status(200).send({
message: 'Profil erfolgreich gelöscht.'
});
// Sendet Fehlermeldung, wenn kein Benutzer gefunden wurde
message: 'Profil konnte nicht gelöscht werden. Versuche es später erneut.',
} catch (error: unknown) {
// Sendet Fehlermeldung bei Datenbankfehler
message: 'Datenbankanfrage fehlgeschlagen: ' + 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,
});
} catch (error) {
// Database operation has failed
res.status(500).send({
message: 'Database request failed: ' + error
});
}
});