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; //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,
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
message: 'Änderung gespeichert',
user: user, // Send user object to client for greeting message
// Login data is incorrect
res.status(401).send({
message: 'Passwort ist falsch',
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);
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
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}`,
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
});
}
} 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 ..."
* }
*/
// 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
});
}
});
/**
* @api {delete} /user Benutzer löschen
* @apiName DeleteUser
* @apiGroup User
* @apiDescription Löscht das eigene Benutzerprofil
*
* @apiSuccess {String} message Erfolgsmeldung
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "message": "Nutzer erfolgreich gelöscht."
* }
*
* @apiError 404 Benutzer nicht gefunden
* @apiError 500 Datenbankfehler
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 404 Not Found
* {
* "message": "Nutzer nicht gefunden."
* }
*/