Skip to content
Snippets Groups Projects
server.ts 31.3 KiB
Newer Older
„Sophia's avatar
„Sophia committed
/*****************************************************************************
 * Import package                                                            *
 *****************************************************************************/
Victoria Badeke's avatar
Victoria Badeke committed
//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
„Sophia's avatar
„Sophia committed
/*****************************************************************************
 * Database Connection                                                       *
 *****************************************************************************/
let database: Connection;
Victoria Badeke's avatar
Victoria Badeke committed

// Funktion wird definiert, um die Verbindung zu einer Datenbank asynchron herzustellen
„Sophia's avatar
„Sophia committed
async function connectDatabase() {
Victoria Badeke's avatar
Victoria Badeke committed
  //Versuch, die Verbindung herzustellen
„Sophia's avatar
„Sophia committed
  try {
Victoria Badeke's avatar
Victoria Badeke committed
    //Verbindung erstellen
„Sophia's avatar
„Sophia committed
    database = await createConnection({
      host: "localhost",
      user: "root",
      password: "toortoor",
„Sophia's avatar
„Sophia committed
      database: "horizon_changers"
„Sophia's avatar
„Sophia committed
    });
Victoria Badeke's avatar
Victoria Badeke committed
    //Verbindung herstellen
„Sophia's avatar
„Sophia committed
    await database.connect();
Victoria Badeke's avatar
Victoria Badeke committed
    console.log("Database is connected"); //Erfolg
„Sophia's avatar
„Sophia committed
  } catch (error) {
Victoria Badeke's avatar
Victoria Badeke committed
    console.log(`Database connection failed: ${error}`);//Fehlerbehandlung
„Sophia's avatar
„Sophia committed
  }
}
connectDatabase();

/*****************************************************************************
 * Define and start web-app server, define json-Parser                       *
 *****************************************************************************/
Victoria Badeke's avatar
Victoria Badeke committed
//Start des Servers
Victoria Badeke's avatar
Victoria Badeke committed
const app: Express = express(); //Instanz des Express-Frameworks wird erstellt
„Sophia's avatar
„Sophia committed
app.listen(8080, () => {
Victoria Badeke's avatar
Victoria Badeke committed
  console.log('Server started: http://localhost:8080'); //Anwendung anweisen, auf Port 8080 neue TCP-Verbindung anzunehmen
„Sophia's avatar
„Sophia committed
});
Victoria Badeke's avatar
Victoria Badeke committed
app.use(express.json()); //body Parser - interpretiert den Request-Body als JSON-String - ohne diese Zeile kann nicht auf req.body zugegriffen werden
„Sophia's avatar
„Sophia committed

/*****************************************************************************
 * session management configuration                                          *
 *****************************************************************************/
Victoria Badeke's avatar
Victoria Badeke committed
//Sitzungsmanagement
„Sophia's avatar
„Sophia committed
app.use(session({
Victoria Badeke's avatar
Victoria Badeke committed
  // Sitzungen werden gespeichert, auch wenn sie nicht verändert wurden.
„Sophia's avatar
„Sophia committed
  resave: true,
Victoria Badeke's avatar
Victoria Badeke committed
  //Sitzungen werden gespeichert, auch wenn sie nicht genutzt werden.
„Sophia's avatar
„Sophia committed
  saveUninitialized: true,
Victoria Badeke's avatar
Victoria Badeke committed
  // Die Gültigkeit des Cookies wird bei jeder Anfrage erneuert.
„Sophia's avatar
„Sophia committed
  rolling: true,
Victoria Badeke's avatar
Victoria Badeke committed
  //  Ein geheimer Schlüssel zur Verschlüsselung der Sitzungsdaten.
„Sophia's avatar
„Sophia committed
  secret: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
Victoria Badeke's avatar
Victoria Badeke committed
  //Ablaufzeit des Cookies (1 Stunde).
„Sophia's avatar
„Sophia committed
  cookie: { maxAge: 1000 * 60 * 60 } // 1h
}));

declare module 'express-session' {
  interface SessionData {
    user?: User
  }
}

/*****************************************************************************
 * Datastructure                                                             *
 *****************************************************************************/
Victoria Badeke's avatar
Victoria Badeke committed
//Datenstruktur User: Interface zur Definition der Benutzerstruktur.
„Sophia's avatar
„Sophia committed
export interface User {
„Sophia's avatar
„Sophia committed
  user_id: number;
  firstName: string;
  lastName: string;
  adress: string;
  role: string;
  eMail: string;
„Sophia's avatar
„Sophia committed
}
„Sophia's avatar
„Sophia committed

export interface Review {
  reviews_id: number;
  title: string;
  description: string;
  rating: number;
  country: string;
  user_id: number;
}
Victoria Badeke's avatar
Victoria Badeke committed
//Middleware: Login-Status prüfen --Prüft, ob ein Benutzer eingeloggt ist. Falls nicht, wird ein HTTP-Status 401 (Unauthorized) zurückgegeben.
Sarah Gloger's avatar
Sarah Gloger committed
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',
    });
  }
}
„Sophia's avatar
„Sophia committed

Victoria Badeke's avatar
Victoria Badeke committed
//HTTP-Routen
„Sophia's avatar
„Sophia committed
/*****************************************************************************
 * HTTP ROUTES: LOGIN                                                        *
 *****************************************************************************/
/**
„Sophia's avatar
„Sophia committed
 * @api {get} /login Login-Status überprüfen
 * @apiName LoginStatus
„Sophia's avatar
„Sophia committed
 * @apiGroup Login
„Sophia's avatar
„Sophia committed
 * @apiDescription Überprüft, ob ein Benutzer noch eingeloggt ist. Gibt bei Erfolg die Benutzerdaten zurück.
„Sophia's avatar
„Sophia committed
 *
 * @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":{
„Sophia's avatar
„Sophia committed
 *          "user_id":42,
 *          "firstName":"Peter",
 *          "lastName":"Kneisel",
 *           "eMail":"admin",
„Sophia's avatar
„Sophia committed
 *      },
 *      "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."
 * }
 */
„Sophia's avatar
„Sophia committed

Victoria Badeke's avatar
Victoria Badeke committed
// Login-Status prüfen (GET /login)
Sarah Gloger's avatar
Sarah Gloger committed
app.get('/login', isLoggedIn, (req: Request, res: Response): void => {
„Sophia's avatar
„Sophia committed
  res.status(200).send({
    message: 'User still logged in',
    user: req.session.user, // Send user object to client for greeting message
  });
});

/**
Luisa Wenz's avatar
Luisa Wenz committed
 * @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.
„Sophia's avatar
„Sophia committed
 *
Luisa Wenz's avatar
Luisa Wenz committed
 * @apiBody {String} eMail E-Mail-Adresse des Benutzers/ Admins. Muss angegeben werden.
 * @apiBody {String} password Passwort des Benutzers/ Admins. Muss angegeben werden.
„Sophia's avatar
„Sophia committed
 *
 * @apiSuccessExample Success-Response:
„Sophia's avatar
„Sophia committed
 * HTTP/1.1 201 OK
„Sophia's avatar
„Sophia committed
 * {
„Sophia's avatar
„Sophia committed
 *     "message":"'Erfolgreich eingeloggt :)!'"
„Sophia's avatar
„Sophia committed
 * }
„Sophia's avatar
„Sophia committed
 * @apiSuccess {String} message Erfolgsnachricht.
 * @apiSuccess {Object} user Details des authentifizierten Benutzers.
Luisa Wenz's avatar
Luisa Wenz committed
 * @apiSuccess {Obejct} admin Details des authentifizierten Admins.
„Sophia's avatar
„Sophia committed
 * @apiSuccess {Number} user.user_id ID des Benutzers.
Luisa Wenz's avatar
Luisa Wenz committed
 * @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).
„Sophia's avatar
„Sophia committed
 *
 * @apiError {String} message Fehlermeldung.
 *
 * @apiErrorExample {json} 401 Fehler (Ungültige Login-Daten):
„Sophia's avatar
„Sophia committed
 * HTTP/1.1 401 Unauthorized
 * {
„Sophia's avatar
„Sophia committed
 *   "message": "Passwort und/oder Email stimmt/stimmen nicht."
„Sophia's avatar
„Sophia committed
 * }
 *
„Sophia's avatar
„Sophia committed
 * @apiErrorExample {json} 500 Fehler (Datenbankproblem):
 * HTTP/1.1 500 Internal Server Error
„Sophia's avatar
„Sophia committed
 * {
„Sophia's avatar
„Sophia committed
 *   "message": "Database request failed: [Fehlerdetails]"
„Sophia's avatar
„Sophia committed
 * }
 */
Victoria Badeke's avatar
Victoria Badeke committed

//Benutzer-Login (POST /login)
„Sophia's avatar
„Sophia committed
app.post('/login', async (req: Request, res: Response): Promise<void> => {
  // Read data from request
„Sophia's avatar
„Sophia committed
  const eMail: string = req.body.eMail;
„Sophia's avatar
„Sophia committed
  const password: string = req.body.password;

  // Create database query and data
  const data: [string, string] = [
„Sophia's avatar
„Sophia committed
    eMail,
„Sophia's avatar
„Sophia committed
    crypto.createHash("sha512").update(password).digest('hex'),
„Sophia's avatar
„Sophia committed
  ];
Victoria Badeke's avatar
Victoria Badeke committed
  console.log(data)
„Sophia's avatar
„Sophia committed
  const query: string = 'SELECT * FROM user WHERE eMail = ? AND password = ?;';
„Sophia's avatar
„Sophia committed

  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 = {
„Sophia's avatar
„Sophia committed
        user_id: rows[0].user_id,
        firstName: rows[0].firstName,
        lastName: rows[0].lastName,
        eMail: rows[0].eMail,
        adress: rows[0].adress,
„Sophia's avatar
„Sophia committed
        role: rows[0].role || 'user',
„Sophia's avatar
„Sophia committed
      };
„Sophia's avatar
„Sophia committed

„Sophia's avatar
„Sophia committed
      req.session.user = user; // Store user object in session for authentication
      res.status(200).send({
Sarah Gloger's avatar
Sarah Gloger committed
        message: 'Erfolgreich eingeloggt :)!',
„Sophia's avatar
„Sophia committed
        user: user, // Send user object to client for greeting message
Sarah Gloger's avatar
Sarah Gloger committed

„Sophia's avatar
„Sophia committed
      });
    } else {
Victoria Badeke's avatar
Victoria Badeke committed
      // Login data is incorrect
„Sophia's avatar
„Sophia committed
      res.status(401).send({
Sarah Gloger's avatar
Sarah Gloger committed
        message: 'Passwort und/oder Email stimmt/stimmen nicht.',
„Sophia's avatar
„Sophia committed
      });
    }
  } catch (error: unknown) {
    // Unknown error
    res.status(500).send({
      message: 'Database request failed: ' + error,
    });
  }
});

„Sophia's avatar
„Sophia committed

„Sophia's avatar
„Sophia committed
/**
 * @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
 * {
„Sophia's avatar
„Sophia committed
 *     message: "Erfolgreich ausgeloggt! Bis zum nächsten mal :)"
„Sophia's avatar
„Sophia committed
 * }
 */
Victoria Badeke's avatar
Victoria Badeke committed

//Logout
Sarah Gloger's avatar
Sarah Gloger committed
app.post('/logout', isLoggedIn, (req: Request, res: Response): void => {
„Sophia's avatar
„Sophia committed
  // Log out user
  req.session.user = undefined; // Delete user from session
  res.status(200).send({
Sarah Gloger's avatar
Sarah Gloger committed
    message: 'Erfolgreich ausgeloggt! Bis zum nächsten mal :)',
„Sophia's avatar
„Sophia committed
  });
});
/*****************************************************************************
 * HTTP ROUTES: USER, USERS                                                  *
 *****************************************************************************/
/**
„Sophia's avatar
„Sophia committed
 *
 * @api {post} /user Benutzerregistrierung
 * @apiName BenutzerRegistrierung
„Sophia's avatar
„Sophia committed
 * @apiGroup User
 *
„Sophia's avatar
„Sophia committed
 * @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.
„Sophia's avatar
„Sophia committed
 *
„Sophia's avatar
„Sophia committed
 * @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").
„Sophia's avatar
„Sophia committed
 *
 * @apiSuccessExample Success-Response:
„Sophia's avatar
„Sophia committed
 * HTTP/1.1 201 OK
„Sophia's avatar
„Sophia committed
 * {
„Sophia's avatar
„Sophia committed
 *     "message":"'Erfolgreich registriert und eingeloggt!'"
„Sophia's avatar
„Sophia committed
 * }
„Sophia's avatar
„Sophia committed

„Sophia's avatar
„Sophia committed
 *
„Sophia's avatar
„Sophia committed
 * @apiError {String} message Fehlermeldung.
„Sophia's avatar
„Sophia committed
 *
„Sophia's avatar
„Sophia committed
 * @apiErrorExample {json} 400 Fehler (Felder fehlen):
„Sophia's avatar
„Sophia committed
 * HTTP/1.1 400 Bad Request
 * {
„Sophia's avatar
„Sophia committed
 *   "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."
„Sophia's avatar
„Sophia committed
 * }
 */
Victoria Badeke's avatar
Victoria Badeke committed

//Benutzer registrieren (POST /user)
Sarah Gloger's avatar
Sarah Gloger committed
app.post('/user', async (req: Request, res: Response): Promise<void> => {
Victoria Badeke's avatar
Victoria Badeke committed
  const firstName: string = req.body.firstName; //Variable firstName wird erstellt, mit req.body kann man auf Nutzdaten zugreifen, in dem Falle auf first name
„Sophia's avatar
„Sophia committed
  const lastName: string = req.body.lastName;
Sarah Gloger's avatar
Sarah Gloger committed
  const eMail: string = req.body.eMail;
  const password: string = req.body.password;
  const repeatPassword: string = req.body.repeatPassword;

„Sophia's avatar
„Sophia committed
  // Check, if any given value is empty
  if (!firstName || !lastName || !eMail || !password || !repeatPassword) {
    res.status(400).send({
    message: "Bitte alle Felder ausfüllen!",
    });
    return;
  }
Sarah Gloger's avatar
Sarah Gloger committed

„Sophia's avatar
„Sophia committed
  // Check if password and repeatPassword match
  if (password !== repeatPassword) {
    res.status(400).send({
      message: 'Passwörter stimmen nicht überein!',
    });
    return;
  }
Victoria Badeke's avatar
Victoria Badeke committed
//aufrufen der namen, email, passwort
„Sophia's avatar
„Sophia committed
  const data: [string, string, string, string] = [
    firstName,
    lastName,
Victoria Badeke's avatar
Victoria Badeke committed
    eMail,
    crypto.createHash("sha512").update(password).digest('hex')
„Sophia's avatar
„Sophia committed

Victoria Badeke's avatar
Victoria Badeke committed
  ];
//soll in user Tabelle eingepflegt werden
  const query: string = 'INSERT INTO user (firstName, lastName, eMail, password) VALUES (?, ?, ?, ?);';
„Sophia's avatar
„Sophia committed

  try {
Victoria Badeke's avatar
Victoria Badeke committed
    const [result] = await database.query<ResultSetHeader>(query, data); //daten werden von dem neu registrierten Nutzer gespeichert
„Sophia's avatar
„Sophia committed

    // Hol den neu erstellten Benutzer aus der Datenbank
    const [userRows] = await database.query<RowDataPacket[]>(
        'SELECT * FROM user WHERE user_id = ?;',
Victoria Badeke's avatar
Victoria Badeke committed
        [result.insertId] //=vom registrierten User id nehmen
„Sophia's avatar
„Sophia committed
    );

    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',
      };
Sarah Gloger's avatar
Sarah Gloger committed
// Überprüfen, ob die E-Mail bereits existiert
„Sophia's avatar
„Sophia committed

Victoria Badeke's avatar
Victoria Badeke committed

„Sophia's avatar
„Sophia committed
      // Speichere den Benutzer in der Session
      req.session.user = user;
Sarah Gloger's avatar
Sarah Gloger committed

„Sophia's avatar
„Sophia committed
      res.status(201).send({
„Sophia's avatar
„Sophia committed
        message: 'Erfolgreich registriert und eingeloggt!',
        user,
„Sophia's avatar
„Sophia committed
      });
„Sophia's avatar
„Sophia committed
    } else {
„Sophia's avatar
„Sophia committed
      res.status(500).send({
„Sophia's avatar
„Sophia committed
        message: 'Registrierung erfolgreich, aber der Benutzer konnte nicht abgerufen werden.',
„Sophia's avatar
„Sophia committed
      });
    }
„Sophia's avatar
„Sophia committed
  } catch (error) {
    res.status(500).send({
      message: 'Datenbankanfrage fehlgeschlagen: ' + error,
„Sophia's avatar
„Sophia committed
    });
  }
});

„Sophia's avatar
„Sophia committed

Luisa Wenz's avatar
Luisa Wenz committed
/*****************************************************************************
 * 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.
 *
Victoria Badeke's avatar
Victoria Badeke committed
 * @apiParam {String} user_id in der URL.
 *
Luisa Wenz's avatar
Luisa Wenz committed
 * @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
 */

Victoria Badeke's avatar
Victoria Badeke committed
//Gibt Benutzerinformationen zurück.
„Sophia's avatar
„Sophia committed
app.get('/user/:user_id', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
„Sophia's avatar
„Sophia committed
  // Read data from request parameters
  const data: [number] = [
„Sophia's avatar
„Sophia committed
    parseInt(req.params.user_id)
„Sophia's avatar
„Sophia committed
  ];
  // Search user in database
„Sophia's avatar
„Sophia committed
  const query: string = 'SELECT * FROM user WHERE user_id = ?;';
„Sophia's avatar
„Sophia committed

  try {
    const [rows] = await database.query<RowDataPacket[]>(query, data);
    if (rows.length === 1) {
      const user: User = {
„Sophia's avatar
„Sophia committed
        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
„Sophia's avatar
„Sophia committed
      };

„Sophia's avatar
„Sophia committed
      req.session.user = user; // Store user object in session for authentication
„Sophia's avatar
„Sophia committed
      res.status(200).send({
„Sophia's avatar
„Sophia committed
        user: user, // Send user object to client for greeting message

„Sophia's avatar
„Sophia committed
      });
    } else {
„Sophia's avatar
„Sophia committed
      // Login data is incorrect
      res.status(401).send({
        message: 'Passwort ist falsch',
„Sophia's avatar
„Sophia committed
      });
    }
„Sophia's avatar
„Sophia committed
  } catch (error: unknown) {
    // Unknown error
„Sophia's avatar
„Sophia committed
    res.status(500).send({
„Sophia's avatar
„Sophia committed
      message: 'Database request failed: ' + error,
„Sophia's avatar
„Sophia committed
    });
  }
});

„Sophia's avatar
„Sophia committed

Luisa Wenz's avatar
Luisa Wenz committed

/**
 * @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]"
 *     }
 */

Victoria Badeke's avatar
Victoria Badeke committed
//Aktualisiert den Benutzer
„Sophia's avatar
„Sophia committed
app.put('/user', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
„Sophia's avatar
„Sophia committed

„Sophia's avatar
„Sophia committed
  // Read data from request
„Sophia's avatar
„Sophia committed
  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
Victoria Badeke's avatar
Victoria Badeke committed
  const firstName: string = req.body.firstName;
  const lastName: string = req.body.lastName;
„Sophia's avatar
„Sophia committed
  const eMail: string = req.body.eMail;
  const password: string = req.body.password;
  const adress: string = req.body.adress;

„Sophia's avatar
„Sophia committed
  // Create database query and data
  const data: [string, string] = [
    eMail,
    crypto.createHash("sha512").update(password).digest('hex'),
  ];
„Sophia's avatar
„Sophia committed
  console.log("login " + data)
„Sophia's avatar
„Sophia committed
  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
„Sophia's avatar
„Sophia committed
  if (firstName && lastName && eMail && password) {
„Sophia's avatar
„Sophia committed
    // Create database query and data
„Sophia's avatar
„Sophia committed
    const data: [string, string, string, string, string, number] = [
Victoria Badeke's avatar
Victoria Badeke committed
      firstName,
      lastName,
„Sophia's avatar
„Sophia committed
      eMail,
„Sophia's avatar
„Sophia committed
      crypto.createHash("sha512").update(password).digest('hex'),
„Sophia's avatar
„Sophia committed
      adress,
      user_id
„Sophia's avatar
„Sophia committed
    ];
„Sophia's avatar
„Sophia committed
    const query: string = 'UPDATE user SET firstName = ?, lastName = ?, eMail = ?, password = ?, adress = ? WHERE user_id = ?;';
„Sophia's avatar
„Sophia committed
    console.log(query, data)
„Sophia's avatar
„Sophia committed

    // Execute database query
    try {
      const [result] = await database.query<ResultSetHeader>(query, data);

„Sophia's avatar
„Sophia committed
      console.log(result)
„Sophia's avatar
„Sophia committed
      if (result.affectedRows != 1) {
„Sophia's avatar
„Sophia committed
        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> => {
„Sophia's avatar
„Sophia committed
  console.log(req.body);
„Sophia's avatar
„Sophia committed
  // 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
    ];
„Sophia's avatar
„Sophia committed
    const query: string = 'UPDATE user SET firstName = ?, lastName = ?, eMail = ?, adress = ?, role = ? WHERE user_id = ?;';
„Sophia's avatar
„Sophia committed

    // Execute database query
    try {
      const [result] = await database.query<ResultSetHeader>(query, data);

      if (result.affectedRows != 1) {
„Sophia's avatar
„Sophia committed
        res.status(404).send({
          message: 'The user to update could not be found',
        });
      } else {
        res.status(200).send({
Victoria Badeke's avatar
Victoria Badeke committed
          message: `Successfully updated user ${firstName} ${lastName}`,
„Sophia's avatar
„Sophia committed
        });
      }
    } catch (error) {
      res.status(500).send({
        message: 'Database request failed: ' + error
      });
    }
  } else {
    res.status(400).send({
      message: 'Not all mandatory fields are filled in',
    });
  }
});

„Sophia's avatar
„Sophia committed
// User löschen vom Admin aus
Lilith Faust's avatar
Lilith Faust committed

/**
 * @api {delete} /user/:userId Lösche Benutzer
 * @apiName DeleteUser
 * @apiGroup Admin
 * @apiPermission loggedIn
 *
 * @apiDescription Diese Route ermöglicht es eingeloggten Admins, einen Benutzer anhand seiner ID zu löschen.
 * Der Admin muss eingeloggt sein, um diese Route nutzen zu können.
 *
 * @apiParam {Number} userId Die ID des Benutzers, der gelöscht werden soll. (Pflichtfeld)
 *
 * @apiSuccess {String} message Erfolgsmeldung, dass der Benutzer erfolgreich gelöscht wurde.
 *
 * @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": "Löschen des Nutzers war erfolgreich!"
 *     }
 *
 * @apiErrorExample {json} Benutzer nicht gefunden:
 *     HTTP/1.1 404 Not Found
 *     {
 *       "message": "Der Nutzer, welcher gelöscht werden soll, konnte nicht gefunden werden."
 *     }
 *
 * @apiErrorExample {json} Datenbankfehler:
 *     HTTP/1.1 500 Internal Server Error
 *     {
 *       "message": "Database request failed: [Fehlermeldung]"
 *     }
 */

„Sophia's avatar
„Sophia committed
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
    });
  }
});

„Sophia's avatar
„Sophia committed
/**
Victoria Badeke's avatar
Victoria Badeke committed
 * @api {delete} /user Benutzerprofil löschen
 * @apiName BenutzerprofilLoeschen
„Sophia's avatar
„Sophia committed
 * @apiGroup User
Victoria Badeke's avatar
Victoria Badeke committed
 * @apiDescription Löscht das Benutzerprofil des aktuell eingeloggten Benutzers aus der Datenbank und beendet die Session.
„Sophia's avatar
„Sophia committed
 *
Victoria Badeke's avatar
Victoria Badeke committed
 * @apiSuccess {String} message Erfolgsnachricht
„Sophia's avatar
„Sophia committed
 *
Victoria Badeke's avatar
Victoria Badeke committed
 * @apiSuccessExample Sucsess-Response:
„Sophia's avatar
„Sophia committed
 * HTTP/1.1 200 OK
 * {
Victoria Badeke's avatar
Victoria Badeke committed
 *     "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]"
„Sophia's avatar
„Sophia committed
 * }
 */
Sarah Gloger's avatar
Sarah Gloger committed
// Route zum Löschen des eigenen Benutzerprofils
app.delete('/user', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
  console.log("delete user called")
„Sophia's avatar
„Sophia committed
  // Read data from request
Sarah Gloger's avatar
Sarah Gloger committed
  const user_id: number  = Number(req.session.user?.user_id);
„Sophia's avatar
„Sophia committed
  // Delete user
„Sophia's avatar
„Sophia committed
  const query: string = 'DELETE FROM user WHERE user_id = ?;';
„Sophia's avatar
„Sophia committed
  try {
Sarah Gloger's avatar
Sarah Gloger committed
    // Führt die Löschoperation in der Datenbank aus
    const [result] = await database.query<ResultSetHeader>(query, [user_id]);
„Sophia's avatar
„Sophia committed
    if (result.affectedRows === 1) {
Sarah Gloger's avatar
Sarah Gloger committed
      // 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.'
        });
„Sophia's avatar
„Sophia committed
      });
    } else {
Sarah Gloger's avatar
Sarah Gloger committed
      // Sendet Fehlermeldung, wenn kein Benutzer gefunden wurde
„Sophia's avatar
„Sophia committed
      res.status(404).send({
Sarah Gloger's avatar
Sarah Gloger committed
        message: 'Profil konnte nicht gelöscht werden. Versuche es später erneut.',
„Sophia's avatar
„Sophia committed
      });
    }
Sarah Gloger's avatar
Sarah Gloger committed
  } catch (error: unknown) {
    // Sendet Fehlermeldung bei Datenbankfehler
„Sophia's avatar
„Sophia committed
    res.status(500).send({
Sarah Gloger's avatar
Sarah Gloger committed
      message: 'Datenbankanfrage fehlgeschlagen: ' + error,
„Sophia's avatar
„Sophia committed
    });
  }
});

Victoria Badeke's avatar
Victoria Badeke committed
/**
 *
 * @api {get} /users Nutzerliste abrufen
 * @apiName NutzerListeAbrufen
 * @apiGroup Admin
 *
 * @apiDescription Ruft die Nutzerdaten aller in der Datenbank registrierten Nutzer auf.
 *
 *
 *
 * @apiSuccess {String} message Bestätigungsnachricht.
 * @apiSuccess {Object[]} userList Liste der gefundenen Nutzer.
 * @apiSuccess {Number} userList.user_id ID des Nutzers.
 * @apiSuccess {String} userList.firstName Vorname des Nutzers.
 * @apiSuccess {String} userList.lastName Nachname des Nutzers.
 * @apiSuccess {String} userList.eMail E-Mail-Adresse des Nutzers.
 * @apiSuccess {String} userList.adress Adresse des Nutzers (falls vorhanden, optional).
 * @apiSuccess {String} userList.role Rolle des Nutzers.
 *
 * @apiSuccessExample Success-Response:
 * HTTP/1.1 200 OK
 * {
 *   "userList": [
 *     {
 *       "user_id": 1,
 *       "firstName": "Alice",
 *       "lastName": "Beispiel",
 *       "eMail": "alice.beispiel@mail.com",
 *       "adress": "Beispielstraße 10, 12345 Beispielstadt",
 *       "role": "admin"
 *     },
 *     {
 *       "user_id": 2,
 *       "firstName": "Bob",
 *       "lastName": "Hase",
 *       "eMail": "bob.hase@mail.com",
 *       "adress": "Musterstraße 9, 12345 Musterstadt",
 *       "role": "user"
 *     }
Victoria Badeke's avatar
Victoria Badeke committed
 *   ]
Victoria Badeke's avatar
Victoria Badeke committed
 * }
 *
 * @apiError {String} message Fehlermeldung.
 *
 * @apiErrorExample {json} 500 Fehler (Datenbankproblem):
 * HTTP/1.1 500 Internal Server Error
 * {
 *   "message": "Database request failed: [Fehlerdetails]"
 * }
 *
 *
 *
 */
Victoria Badeke's avatar
Victoria Badeke committed
//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.
Sarah Gloger's avatar
Sarah Gloger committed
app.get('/users', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
„Sophia's avatar
„Sophia committed
  // Send user list to client
„Sophia's avatar
„Sophia committed
  const query: string = 'SELECT * FROM user;';
„Sophia's avatar
„Sophia committed

  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 = {
„Sophia's avatar
„Sophia committed
        user_id: row.user_id,
        firstName: row.firstName,
        lastName: row.lastName,
        eMail: row.eMail,
        adress: row.adress,
        role: row.role
„Sophia's avatar
„Sophia committed
      };
      userList.push(user);
    }

    // Send user list to client
    res.status(200).send({
Victoria Badeke's avatar
Victoria Badeke committed
      userList: userList
„Sophia's avatar
„Sophia committed
    });
  } catch (error) {
    // Database operation has failed
    res.status(500).send({
      message: 'Database request failed: ' + error
    });
  }
});

„Sophia's avatar
„Sophia committed
//Reisebericht schreiben
app.post('/reviews', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
  const country: string = req.body.country;
  const title: string = req.body.title;
  const description: string = req.body.description;
  const rating: number = req.body.rating;
  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

  // Check, if any given value is empty
  if (!country || !title || !description) {
    res.status(400).send({
      message: "Bitte alle Felder ausfüllen!",
    });
    return;
  }

//aufrufen titel, beschreibung, land, rating, userid
  const data: [string, string, string, number, number] = [
    title,
    description,
    country,
    Number(rating),
    user_id,
  ];
//soll in review tabelle gespeichert werden
  const query: string = 'INSERT INTO reviews (title, description, country, rating, user_id) VALUES (?, ?, ?, ?, ?);';
Victoria Badeke's avatar
Victoria Badeke committed

„Sophia's avatar
„Sophia committed
  try {
    const [rows] = await database.query<RowDataPacket[]>(query, data);
    // Check if database response contains exactly one entry
      res.status(201).send({
        message: 'Deine Bewertung wurde erfolgreich erstellt. Danke für dein Feedback! :).',
      });
  } catch (error: unknown) {
    // Unknown error
    res.status(500).send({
      message: 'Database request failed: ' + error,
    });
  }
});

/// Read Review

app.get('/reviews', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
  // Die `user_id` wird aus der Middleware `isLoggedIn` bereitgestellt
  const user_id: number  = Number(req.session.user?.user_id);

  // SQL-Query für alle Reviews des Users
  const query: string = 'SELECT * FROM reviews WHERE user_id = ?;';

  try {
    // Daten aus der Datenbank abrufen
    const [rows] = await database.query<RowDataPacket[]>(query, [user_id]);

      // Alle Reviews sammeln und formatieren
      const reviewsList: Review[] = [];
      for (const row of rows) {
        const reviews: Review = {
          reviews_id: row.reviews_id,
          user_id: row.user_id,
          country: row.country,
          title: row.title,
          description: row.description,
          rating: row.rating,
        };
        reviewsList.push(reviews);
      }
      // Erfolgsantwort senden
      res.status(200).send({
        reviewsList: reviewsList, // Alle Reviews zurückgeben
        message: 'Erfolgreicher Zugriff auf Review.',
      });
  } catch (error) {
    // Fehlerbehandlung bei Datenbankoperationen
    console.error('Database error: ', error);
    res.status(500).send({
      message: 'Database request failed: ' + error,
    });
  }
});
„Sophia's avatar
„Sophia committed
/*****************************************************************************
 * STATIC ROUTES                                                             *
 *****************************************************************************/
app.use(express.static(path.join(__dirname, "..", "..", "client")));