Bernd-L/exDateMan

View on GitHub
backend/src/server.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import * as express from "express";
import { join } from "path";
import pEvent from "p-event";
import { readFileSync } from "fs";
import { log } from "console";
import { createServer } from "https";
import { ExdatemanApplication } from "./app/application";
import { json, urlencoded } from "body-parser";
import * as cookieParser from "cookie-parser";
import { Client } from "pg";
import db from "./app/db";

/**
 * Handles SSL setup and starts the Express server, mounts the API application
 * and serves static files
 */
export class ExpressServer {
  /**
   * The Express server
   */
  private app: express.Application;

  constructor() {
    // Initiate the Express server
    this.app = express();

    // Parse cookies
    // cookie-parser is for cookies; and cookies are for JWT
    this.app.use(cookieParser());

    // Parse application/json
    this.app.use(json());

    // Parse application/x-www-form-urlencoded
    this.app.use(urlencoded({ extended: true }));

    // Instantiate the API and mount it
    this.app.use("/api/v2", new ExdatemanApplication().routes);

    // Serve all frontend files
    this.app.use(
      express.static(join(__dirname, "../../frontend/dist/exdateman")),
    );

    // Serve main page
    this.app.use((req: express.Request, res: express.Response) => {
      // Don't redirect to preserve the Angular routes
      res.sendFile(join(__dirname, "../../frontend/dist/exdateman/index.html"));
    });
  }

  /**
   * Start the server either with or without SSL
   *
   * Checks if SSL is desired using the `EDM_SSL` environment variable.
   *
   * The SSL parameters specified in the environment are either taken out of
   * their direct value representation (e.g. `EDM_SSL_PK_VAL`), otherwise the
   * files of the paths specified in them (e.g. `EDM_SSL_PK`) will be read and
   * used.
   */
  async start() {
    // Connect to the db
    await db();

    // Set the ports
    const PORT: string = process.env.PORT || 443 + "";
    const INSECURE_PORT: string = process.env.INSECURE_PORT || 80 + "";

    // Check if SSL is desired
    if (process.env.EDM_SSL === "true") {
      // Use SSL

      /**
       * The private key for SSL
       */
      const privateKey: string =
        process.env.EDM_SSL_PK_VAL ||
        readFileSync(process.env.EDM_SSL_PK + "", "utf8");

      /**
       * The certificate for SSL
       */
      const certificate: string =
        process.env.EDM_SSL_CERT_VAL ||
        readFileSync(process.env.EDM_SSL_CERT + "", "utf8");

      /**
       * The certificate authority chain for SSL
       */
      const ca: string =
        process.env.EDM_SSL_CA_VAL ||
        readFileSync(process.env.EDM_SSL_CA + "", "utf8");

      /**
       * The credentials as one object for SSL
       */
      const credentials = {
        key: privateKey,
        cert: certificate,
        ca,
      };

      // Create the https app server
      await pEvent(
        createServer(credentials, this.app).listen(PORT),
        "listening",
      );
      log("HTTPS app server listening on port " + PORT);

      // Create the http redirect server
      express()
        .use("*", (req, res) => {
          res.redirect("https://" + req.headers.host + req.url);
        })
        .listen(INSECURE_PORT);
      log("HTTP redirect server listening on port " + INSECURE_PORT);
    } else {
      // Don't use SSL
      // Start the http app server
      await pEvent(this.app.listen(process.env.PORT || 80 + ""), "listening");
      log("HTTP app server listening on port " + (process.env.PORT || 80 + ""));
    }
  }
}