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);
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 user 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',
});
}
});
// User löschen vom Admin aus
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: `Löschen des Nutzers war erfolgreich!`,
});
} else {
res.status(404).send({
message: 'Der Nutzer, das gelöscht werden soll, konnte nicht gefunden werden.',
});
}
} catch (error) {
// Database operation has failed
res.status(500).send({
message: 'Database request failed: ' + error
});
}
});
/**
* @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."
* }
*/