Skip to content
Snippets Groups Projects
app.js 4.32 KiB
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
const { readdirSync, lstatSync } = require("fs");

var indexRouter = require("./routes/index");
const RedisClient = require("./app/RedisClient");

// Localization
const i18next = require("i18next");
let i18nextfsbackend = require("i18next-fs-backend");
const i18nextmiddleware = require("i18next-http-middleware");
const mglangdetector = require("./app/Langdetector");
const langdetector = new i18nextmiddleware.LanguageDetector();
langdetector.addDetector(mglangdetector);

let ns = [];
readdirSync(path.join(__dirname, "lang", "en")).forEach((fileName) => {
  const joinedPath = path.join(path.join(__dirname, "lang", "en"), fileName);
  if (!lstatSync(joinedPath).isDirectory()) {
    ns.push(fileName.replace(".json", ""));
  }
});

let env = process.env.NODE_ENV;
/** @global */
global.__redis_client = RedisClient.CLIENT();
/** @global */
global.__database_client = require("knex")(require("./knexfile"));

i18next
  .use(langdetector)
  .use(i18nextfsbackend)
  .init({
    debug: false,
    // Lang Detector Options
    detection: {
      order: ["mg_detection"],
    },
    // FS Backend Options
    backend: {
      loadPath: path.join(__dirname, "lang", "{{lng}}", "{{ns}}.json"),
      addPath: path.join(__dirname, "lang", "{{lng}}", "{{ns}}.missing.json"),
    },
    ns: ns,
    defaultNS: "index",
    fallbackLng: "en",
    initImmediate: false,
    preload: readdirSync(path.join(__dirname, "lang")).filter((fileName) => {
      const joinedPath = path.join(path.join(__dirname, "lang"), fileName);
      const isDirectory = lstatSync(joinedPath).isDirectory();
      return isDirectory;
    }),
  });

var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

// Setup logging
if (process.env.NODE_ENV === "development") {
  app.use(logger("dev"));
} else {
  // Do not log successful requests in production
  app.use(
    logger("dev", {
      skip: (req, res) => {
        return res.statusCode < 400;
      },
    })
  );
}
app.use(express.json());
app.use(express.urlencoded({ extended: false, limit: "1mb" }));
app.use(cookieParser());

// Healthcheck URL
app.get("/healthz", (req, res) => {
  res.status(200);
  res.header({ "Content-Type": "application/json" });
  res.send(JSON.stringify({ status: "OK" }));
});

app.get("/healthz/cron", async (req, res) => {
  let live = await __redis_client.get("cron_liveness");

  if (live !== null) {
    res.json({ message: "OK" });
  } else {
    res.status(404).json({ message: "DOWN" });
  }
});

app.use((req, res, next) => {
  // Make req and res accessible in EJS
  res.locals = req;
  res.locals = res;
  res.locals.baseDir = "";
  let subPath = req.url.match(/^((\/.*)?\/keys)/);
  let allowed_hosts = ["localhost", "metager.de", "metager.org", "metager3.de"];

  if (
    allowed_hosts.includes(req.hostname) ||
    (process.env.NODE_ENV === "development" &&
      req.hostname.match(
        /^(localhost|.*\.ngrok-free\.app|.*\.review\.metager\.de)$/
      ))
  ) {
    let proto = req.get("x-forwarded-proto") ?? req.protocol;
    let host = req.get("x-forwarded-host") ?? req.get("host");
    let port = req.get("x-forwarded-port");

    if (
      host.match(
        /^(.*\.ngrok-free\.app|metager\.de|metager\.org|metager3\.de)$/
      )
    ) {
      proto = "https";
      port = undefined;
    }

    res.locals.baseDir =
      `${proto}://${host}` +
      (port ? `:${port}` : "") +
      (subPath ? subPath[1] : "");
  }
  next();
});

app.use(
  i18nextmiddleware.handle(i18next, {
    ignoreRoutes: (req, res, options, i18next) => {
      if (
        req.path.match(/^(\/[a-zA-Z\-]{2,5})?(\/keys)?\/(styles|js|images)\//)
      ) {
        return true;
      }
    },
  })
);

app.use(/^((\/.*)?\/keys)/, indexRouter);
app.use(indexRouter);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = err;

  if (err.status != 404) console.error(err.stack);

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;