Skip to content
Snippets Groups Projects
server.ts 16.6 KiB
Newer Older
„Sophia's avatar
„Sophia committed
/*****************************************************************************
 * Import package                                                            *
 *****************************************************************************/
import express, {Express, NextFunction, Request, Response} from 'express';
import {Connection, createConnection, ResultSetHeader, RowDataPacket} from "mysql2/promise";
import session from "express-session";
import crypto from "crypto"
import * as path from "node:path";

/*****************************************************************************
 * Database Connection                                                       *
 *****************************************************************************/
let database: Connection;
async function connectDatabase() {
  try {
    database = await createConnection({
      host: "localhost",
      user: "root",
      password: "toortoor",
„Sophia's avatar
„Sophia committed
      database: "horizon_changers"
„Sophia's avatar
„Sophia committed
    });
    await database.connect();
    console.log("Database is connected");
  } catch (error) {
    console.log(`Database connection failed: ${error}`);
  }
}
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                                          *
 *****************************************************************************/
app.use(session({
  // save session even if not modified
  resave: true,
  // save session even if not used
  saveUninitialized: true,
  // forces cookie set on every response needed to set expiration (maxAge)
  rolling: true,
  // encrypt session-id in cookie using "secret" as modifier
  secret: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  // set some cookie-attributes. Here expiration-date (offset in ms)
  cookie: { maxAge: 1000 * 60 * 60 } // 1h
}));

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

/*****************************************************************************
 * Datastructure                                                             *
 *****************************************************************************/
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
}

/**
 * @apiDefine SessionExpired
 *
 * @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."
 * }
 */
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

/*****************************************************************************
 * HTTP ROUTES: LOGIN                                                        *
 *****************************************************************************/
/**
 * @api {get} /login Request login state
 * @apiName GetLogin
 * @apiGroup Login
 *
 * @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":{
 *         "id":1,
 *         "username":"admin",
 *         "givenName":"Peter",
 *         "familyName":"Kneisel",
 *         "creationTime":"2017-11-12T09:33:25.000Z"
 *      },
 *      "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."
 * }
 */
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
  });
});


/**
 * @api {post} /login Send login request
 * @apiName PostLogin
 * @apiGroup Login
 *
 * @apiBody {string} username Username of the user to log in
 * @apiBody {string} password Password of the user to log in
 *
 * @apiSuccess {User} user The user object
 * @apiSuccess {string} message Message stating the user logged in successfully
 *
 * @apiSuccessExample Success-Response:
 * HTTP/1.1 200 OK
 * {
 *     "user":{
 *         "id":1,
 *         "username":"admin",
Sarah Gloger's avatar
Sarah Gloger committed
 *         "firstName":"Peter",
 *         "lastName":"Kneisel",
„Sophia's avatar
„Sophia committed
 *         "creationTime":"2017-11-12T09:33:25.000Z"
 *     },
 *     "message":"Successfully logged in"
 * }
 *
 * @apiError (Client Error) {401} LoginIncorrect The login data provided is not correct.
 * @apiError (Server Error) {500} DatabaseRequestFailed The request to the database failed.
 *
 * @apiErrorExample LoginIncorrect:
 * HTTP/1.1 401 Unauthorized
 * {
 *     "message":"Username or password is incorrect."
 * }
 *
 *
 * @apiErrorExample DatabaseRequestFailed:
 * HTTP/1.1 500 Internal Server Errror
 * {
 *     "message":"Database request failed: ..."
 * }
 */
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,
Victoria Badeke's avatar
Victoria Badeke committed
    password
    //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,
        role: rows[0].role,

„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,
    });
  }
});

/**
 * @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
 * {
 *     message: "Successfully logged out"
 * }
 */
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                                                  *
 *****************************************************************************/
/**
 * @api {post} /user Create a new user
 * @apiName postUser
 * @apiGroup User
 *
 * @apiBody {string} givenName First name of the user
 * @apiBody {string} familyName Last name of the user
 *
 * @apiSuccess {string} message Message stating the new user has been created successfully
 *
 * @apiSuccessExample Success-Response:
 * HTTP/1.1 200 OK
 * {
 *     "message":"Successfully created new user"
 * }
 *
 * @apiError (Client Error) {400} NotAllMandatoryFields The request did not contain all mandatory fields
 *
 * @apiErrorExample NotAllMandatoryFields:
 * HTTP/1.1 400 Bad Request
 * {
 *     "message":"Not all mandatory fields are filled in"
 * }
 */
Sarah Gloger's avatar
Sarah Gloger committed
app.post('/user', async (req: Request, res: Response): Promise<void> => {
„Sophia's avatar
„Sophia committed
  // Read data from request body
„Sophia's avatar
„Sophia committed
  const firstName: string = req.body.firstName;
  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
  // add a new user if first- and familyName exist
„Sophia's avatar
„Sophia committed
  if (eMail && password && firstName && lastName) {
Sarah Gloger's avatar
Sarah Gloger committed
    console.log(password);
    console.log(repeatPassword);

    // Check if password and repeatPassword match
    if (password !== repeatPassword) {
     res.status(400).send({
        message: 'Passwörter stimmen nicht überein!',
     });
    }

„Sophia's avatar
„Sophia committed
    const data: [string, string, string, 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
      firstName,
      lastName,
„Sophia's avatar
„Sophia committed
      new Date().toLocaleString()
    ];
Sarah Gloger's avatar
Sarah Gloger committed
    const query: string = 'INSERT INTO user (firstName, lastName, eMail, password) VALUES (?, ?, ?, ?);';
„Sophia's avatar
„Sophia committed
    // Execute database query
    try {
      const [result] = await database.query<ResultSetHeader>(query, data);
      res.status(201).send({
Sarah Gloger's avatar
Sarah Gloger committed
        message: `Erfolgreiches Erstellen einer User-ID: ${result.insertId}`,
„Sophia's avatar
„Sophia committed
      });
    } catch (error) {
      // Send response
      res.status(500).send({
        message: 'Database request failed: ' + error,
      });
    }
Sarah Gloger's avatar
Sarah Gloger committed
  }  else {
„Sophia's avatar
„Sophia committed
    res.status(400).send({
Sarah Gloger's avatar
Sarah Gloger committed
      message: 'Da fehlt doch noch was ;)!',
„Sophia's avatar
„Sophia committed
    });
  }
});

/**
 * @api {get} /user/:userId Get user with given id
 * @apiName getUser
 * @apiGroup User
 *
 * @apiParam {number} userId The id of the requested user
 *
 * @apiSuccess {User} user The requested user object
 * @apiSuccess {string} message Message stating the user has been found
 *
 * @apiSuccessExample Success-Response:
 * HTTP/1.1 200 OK
 * {
 *     "user":{
 *         "id":1,
 *         "givenName":"Peter",
 *         "familyName":"Kneisel",
 *         "creationTime":"2018-10-21 14:19:12"
 *     },
 *     "message":"Successfully got user"
 * }
 *
 *  @apiError (Client Error) {404} NotFound The requested user can not be found
 *
 * @apiErrorExample NotFound:
 * HTTP/1.1 404 Not Found
 * {
 *     "message":"The requested user can not be found."
 * }
 */
Sarah Gloger's avatar
Sarah Gloger committed
app.get('/user/:userId', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
„Sophia's avatar
„Sophia committed
  // Read data from request parameters
  const data: [number] = [
    parseInt(req.params.userId)
  ];
  // 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
      };

      // Send user list to client
      res.status(200).send({
        user: user,
Sarah Gloger's avatar
Sarah Gloger committed
        message: 'Nutzer gefunden.',
„Sophia's avatar
„Sophia committed
      });
    } else {
      res.status(404).send({
Sarah Gloger's avatar
Sarah Gloger committed
        message: 'Gesuchter Nutzer wurde nicht gefunden.',
„Sophia's avatar
„Sophia committed
      });
    }
  } catch (error) {
    // Database operation has failed
    res.status(500).send({
      message: 'Database request failed: ' + error
    });
  }
});

/**
 * @api {put} /user/:userId Update user with given id
 * @apiName putUser
 * @apiGroup User
 *
 * @apiParam {number} userId The id of the requested user
 * @apiBody {string} givenName The (new) first name of the user
 * @apiBody {string} familyName The (new) last name of the user
 *
 * @apiSuccess {string} message Message stating the user has been updated
 *
 * @apiSuccessExample Success-Response:
 * HTTP/1.1 200 OK
 * {
 *     "message":"Successfully updated user ..."
 * }
 *
 * @apiError (Client Error) {400} NotAllMandatoryFields The request did not contain all mandatory fields
 * @apiError (Client Error) {404} NotFound The requested user can not be found
 *
 * @apiErrorExample NotAllMandatoryFields:
 * HTTP/1.1 400 Bad Request
 * {
 *     "message":"Not all mandatory fields are filled in"
 * }
 *
 * @apiErrorExample NotFound:
 * HTTP/1.1 404 Not Found
 * {
 *     "message":"The user to update could not be found"
 * }
 */
Sarah Gloger's avatar
Sarah Gloger committed
app.put('/user/:userId', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
„Sophia's avatar
„Sophia committed
  // Read data from request
  const userId: number = parseInt(req.params.userId);
Victoria Badeke's avatar
Victoria Badeke committed
  const firstName: string = req.body.firstName;
  const lastName: string = req.body.lastName;
„Sophia's avatar
„Sophia committed
  // Check that all arguments are given
Victoria Badeke's avatar
Victoria Badeke committed
  if (firstName && lastName) {
„Sophia's avatar
„Sophia committed
    // Create database query and data
    const data: [string, string, number] = [
Victoria Badeke's avatar
Victoria Badeke committed
      firstName,
      lastName,
„Sophia's avatar
„Sophia committed
      userId
    ];
Victoria Badeke's avatar
Victoria Badeke committed
    const query: string = 'UPDATE user SET firstName = ?, lastName = ? WHERE user_id = ?;';
„Sophia's avatar
„Sophia committed

    // 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({
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',
    });
  }
});

/**
 * @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 ..."
 * }
 */
Sarah Gloger's avatar
Sarah Gloger committed
app.delete('/user/:userId', isLoggedIn, async (req: Request, res: Response): Promise<void> => {
„Sophia's avatar
„Sophia committed
  // Read data from request
  const userId: number = parseInt(req.params.userId);
  // Delete user
„Sophia's avatar
„Sophia committed
  const query: string = 'DELETE FROM user WHERE user_id = ?;';
„Sophia's avatar
„Sophia committed
  try {
    const [result] = await database.query<ResultSetHeader>(query, userId);
    if (result.affectedRows === 1) {
      res.status(200).send({
        message: `Successfully deleted user `,
      });
    } else {
      res.status(404).send({
        message: 'The user to be deleted could not be found',
      });
    }
  } catch (error) {
    // Database operation has failed
    res.status(500).send({
      message: 'Database request failed: ' + error
    });
  }
});

/**
 * @api {get} /users Get all users
 * @apiName getUsers
 * @apiGroup Users
 *
 * @apiSuccess {User[]} userList The list of all users
 * @apiSuccess {string} message Message stating the users have been found
 *
 * @apiSuccessExample Success-Response:
 * HTTP/1.1 200 OK
 * {
 *    "userList": [
 *      {
 *        "givenName": "Hans",
 *        "familyName": "Mustermann",
 *        "creationTime": "2018-11-04T13:02:44.791Z",
 *        "id": 1
 *     },
 *      {
 *        "givenName": "Bruce",
 *        "familyName": "Wayne",
 *        "creationTime": "2018-11-04T13:03:18.477Z",
 *        "id": 2
 *      }
 *    ]
 *    "message":"Successfully requested user list"
 * }
 */
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({
      userList: userList,
      message: 'Successfully requested user list'
    });
  } catch (error) {
    // Database operation has failed
    res.status(500).send({
      message: 'Database request failed: ' + error
    });
  }
});

/*****************************************************************************
 * STATIC ROUTES                                                             *
 *****************************************************************************/
app.use(express.static(path.join(__dirname, "..", "..", "client")));