Skip to content
Snippets Groups Projects
key.js 8.15 KiB
Newer Older
  • Learn to ignore specific revisions
  • Dominik Hebeler's avatar
    Dominik Hebeler committed
    var express = require("express");
    var router = express.Router();
    
    var multer = require("multer");
    
    const { param, validationResult } = require("express-validator");
    
    const config = require("config");
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
    var orderRouter = require("./orders/orders");
    
    var checkout_router_paypal = require("./checkout/paypal");
    
    var checkout_router_manual = require("./checkout/manual");
    
    var checkout_router_micropayment = require("./checkout/micropayment");
    
    var checkout_router_cash = require("./checkout/cash");
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
    var Key = require("../app/Key");
    
    router.get("/create", function (req, res, next) {
    
      if (req.cookies.key) {
        return res.redirect(`${res.locals.baseDir}/key/enter`);
      }
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
      Key.GET_NEW_KEY().then((key) => {
    
        res.locals.key = key;
    
        let setting_url = res.baseDir.replace(/\/keys.*/, "");
        setting_url += "/meta/settings/load-settings?";
    
        let params = {
          key: key.get_key(),
        };
    
        for (let cookie in req.cookies) {
          if (
            cookie.match(
              /^(dark_mode$|new_tab$|zitate$|web_|bilder_|produkte_|nachrichten_|science_)/
            )
          ) {
            params[cookie] = req.cookies[cookie];
          }
        }
        setting_url += new URLSearchParams(params).toString();
        res.locals.setting_url = setting_url;
    
        res.render("login/create");
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
    router.get("/remove", (req, res) => {
      if (req.cookies.key) {
        res.clearCookie("key");
      }
    
    
      // Check if a redirection URL is supplied
      let url = req.query.url;
    
      if (!url && typeof req.headers.referer !== "undefined") {
        url = req.headers.referer;
      }
    
      if (url) {
        let parsed_url = new URL(url);
    
        if (
          parsed_url.hostname !== req.hostname ||
          parsed_url.pathname.match(/\/key\/.*/)
        ) {
          url = `${res.baseDir}/key/enter`;
    
        url = `${res.baseDir}/key/enter`;
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
    
    
    router.get("/enter", function (req, res, next) {
    
      if (req.cookies.key) {
    
        Key.GET_KEY(req.cookies.key, false).then((key) => {
    
          res.redirect(
            `${res.locals.baseDir}/key/` + encodeURIComponent(key.get_key())
          );
        });
    
        // If the user is using a URL Parameter on MetaGer to use his key there might be a key in the referer
        let matches = req.header("referer").match(/\?.*key=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/);
        if (matches) {
          let key_from_referer = matches[1];
          Key.GET_KEY(key_from_referer, false).then((key) => {
            res.redirect(
              `${res.locals.baseDir}/key/` + encodeURIComponent(key.get_key())
            );
          });
        } else {
          res.render("login/key");
        }
    
    const upload_storage = multer.memoryStorage();
    const upload = multer({
      storage: upload_storage,
      limits: {
        fileSize: 5 * 1024 * 1024,
        files: 1,
    
    router.post("/enter", upload.single("file"), async (req, res, next) => {
      /** @type {Key} */
      let key = null;
      if (typeof req.body.key === "string" && req.body.key.length > 0) {
        key = await Key.GET_KEY(req.body.key.trim()).then((key) => key.get_key());
      }
    
      if (key !== null) {
        res.redirect(`${res.locals.baseDir}/key/` + key);
      } else if (typeof req.file === "undefined") {
        res.render("login/key", { errors: "File not provided or invalid" });
      } else {
        const jimp = require("jimp");
    
        jimp.read(req.file.buffer, (err, image) => {
          if (err) {
            res.render("login/key", { errors: ["Error reading image data"] });
            return;
          }
          const QrCode = require("qrcode-reader");
          let qr = new QrCode();
          qr.callback = (err, value) => {
    
            if (err) {
    
              res.render("login/key", { errors: ["Error decoding QR"] });
    
            let url;
            try {
              url = new URL(value.result);
            } catch (err) {
              res.render("login/key", { errors: ["Error parsing URL"] });
              return;
            }
            let key = url.searchParams.get("key");
            if (key !== null) {
              res.redirect(`${res.locals.baseDir}/key/` + encodeURIComponent(key));
            } else {
              res.render("login/key", { errors: ["Error parsing URL"] });
            }
          };
          qr.decode(image.bitmap);
        });
    
    router.use("/:key", param("key").isUUID(4), async (req, res, next) => {
    
      // Input Validation
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
      }
    
    
      let metager_url = "https://metager.de";
    
      if (!req.lng.match(/^de/)) {
    
        metager_url = "https://metager.org";
      }
      metager_url += "/meta/settings/load-settings?";
    
        key: req.params.key,
      };
    
    
      for (let cookie in req.cookies) {
    
        if (
          cookie.match(
            /^(dark_mode$|new_tab$|zitate$|web_|bilder_|produkte_|nachrichten_|science_)/
          )
        ) {
    
          params[cookie] = req.cookies[cookie];
        }
      }
    
    
      metager_url += new URLSearchParams(params).toString();
    
      Key.GET_KEY(req.params.key, false).then((key) => {
    
        req.data = {
    
          price: config.get("price"),
    
            settings_url: metager_url,
    
            qr: `${res.locals.baseDir}/key/${req.params.key}/qr.png`,
    
          },
          cookies: req.cookies,
          page: "fill",
          links: {
            fill_url: `${res.locals.baseDir}/key/${req.params.key}`,
            orders_url: `${res.locals.baseDir}/key/${req.params.key}/orders`,
          },
    
          js: [`${res.locals.baseDir}/js/key.js`],
    
          css: [`${res.locals.baseDir}/styles/key/key.css`],
    
    });
    
    // Basic account page
    router.get("/:key", async (req, res) => {
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
      if (req.data.admin) {
        res.redirect(`${res.locals.baseDir}/logout`);
        return;
    
      } else if (
        !req.cookies.key ||
        req.cookies.key !== req.data.key.key.get_key()
      ) {
    
        res.cookie("key", req.data.key.key.get_key(), {
    
          sameSite: "lax",
    
          maxAge: 5 * 365 * 24 * 60 * 60 * 1000, // Store for 5 years
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
        });
      }
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
      res.render("key", req.data);
    
    router.get("/:key/logincode", (req, res) => {
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
      req.data.key.key.get_logincode().then((code) => {
    
        res.set("Cache-Control", "no-store");
        res.json({
          key: req.data.key.key.get_key(),
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
          code: code,
    
    router.get("/:key/qr.png", (req, res) => {
      let metager_url = req.data.key.settings_url;
      let QRCode = require("qrcode");
      res.header({
        "Content-Type": "image/png",
    
        "Content-Disposition": `inline; filename=metager_key.png`,
    
      });
      QRCode.toFileStream(res, metager_url, {
    
        errorCorrectionLevel: "L",
        scale: 16,
    
    router.use("/:key/orders", orderRouter);
    
    
    /**
     * Validate Amount field for checkout process
     */
    router.use(
      "/:key/checkout/:amount?",
      param("amount")
        .optional({ checkFalsy: true })
        .isInt()
        .toInt()
    
        .isIn(config.get("price.purchasable")),
    
      (req, res, next) => {
        // Input Validation
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
          return res.status(400).json({ errors: errors.array() });
        }
    
        let amount = req.params.amount;
        if (typeof amount !== "undefined" && parseInt(req.params.amount) === 0) {
          amount = undefined;
        } else {
          amount = parseInt(amount);
        }
        req.data.checkout = {
          amount: amount,
        };
    
        // Add a URL to change the checkout amount
        req.data.change_url = {
    
          amount:
            `${res.locals.baseDir}/key/` +
            encodeURIComponent(req.data.key.key.get_key()) +
            "#charge",
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
    router.get("/:key/checkout/:amount", (req, res) => {
      if (req.query.error) {
        req.data.checkout.error = req.query.error;
      }
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
      req.data.js.push(`${res.locals.baseDir}/js/funding_sources.js`);
    
      res.render("key", req.data);
    
    // Funding source is selected define some URLs:
    router.use("/:key/checkout/:amount?/:payment_source", (req, res, next) => {
      req.data.change_url.funding_source =
        `${res.locals.baseDir}/key/` +
        encodeURIComponent(req.data.key.key.get_key()) +
        "/checkout/" +
        encodeURIComponent(req.data.checkout.amount) +
        "#payment";
      next();
    });
    
    
    router.use("/:key/checkout/:amount?/paypal", checkout_router_paypal);
    
    router.use("/:key/checkout/:amount?/manual", checkout_router_manual);
    
    router.use(
      "/:key/checkout/:amount?/micropayment",
      checkout_router_micropayment
    );
    
    router.use("/:key/checkout/:amount?/cash", checkout_router_cash);
    
    Dominik Hebeler's avatar
    Dominik Hebeler committed
    module.exports = router;