"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebServer = void 0;
const os = require("os");
const path = require("path");
const http = require("http");
const https = require("https");
const express = require("express");
const compression = require("compression");
const cookieParser = require("cookie-parser");
const cookieSession = require("cookie-session");
const flash = require("flash");
const morgan = require("morgan");
const SamlProvider_1 = require("./SamlProvider");
const LoginProvider_1 = require("./LoginProvider");
const Config_1 = require("./Config");
const openflow_api_1 = require("@openiap/openflow-api");
const { RateLimiterMemory } = require('rate-limiter-flexible');
const url = require("url");
const Logger_1 = require("./Logger");
const WebSocketServerClient_1 = require("./WebSocketServerClient");
var _hostname = "";
const rateLimiter = (req, res, next) => {
    if (req.originalUrl.indexOf('/oidc') > -1)
        return next();
    Logger_1.Logger.instanse.verbose('WebServer', 'rateLimiter', "Validate for " + req.originalUrl);
    WebServer.BaseRateLimiter
        .consume(WebServer.remoteip(req))
        .then((e) => {
        Logger_1.Logger.instanse.verbose("WebServer", "rateLimiter", "consumedPoints: " + e.consumedPoints + " remainingPoints: " + e.remainingPoints);
        next();
    })
        .catch((e) => {
        const route = url.parse(req.url).pathname;
        // if (!NoderedUtil.IsNullUndefinded(websocket_rate_limit)) websocket_rate_limit.bind({ ...Logger.otel.defaultlabels, route: route }).update(e.consumedPoints);
        Logger_1.Logger.instanse.warn("WebServer", "rateLimiter", "API_RATE_LIMIT consumedPoints: " + e.consumedPoints + " remainingPoints: " + e.remainingPoints + " msBeforeNext: " + e.msBeforeNext);
        res.status(429).json({ response: 'RATE_LIMIT' });
    });
};
class WebServer {
    static remoteip(req) {
        let remoteip = req.socket.remoteAddress;
        if (req.headers["X-Forwarded-For"] != null)
            remoteip = req.headers["X-Forwarded-For"];
        if (req.headers["X-real-IP"] != null)
            remoteip = req.headers["X-real-IP"];
        if (req.headers["x-forwarded-for"] != null)
            remoteip = req.headers["x-forwarded-for"];
        if (req.headers["x-real-ip"] != null)
            remoteip = req.headers["x-real-ip"];
        return remoteip;
    }
    static async isBlocked(req) {
        try {
            var remoteip = LoginProvider_1.LoginProvider.remoteip(req);
            if (!openflow_api_1.NoderedUtil.IsNullEmpty(remoteip)) {
                remoteip = remoteip.toLowerCase();
                var blocks = await Logger_1.Logger.DBHelper.GetIPBlockList(null);
                if (blocks && blocks.length > 0) {
                    for (var i = 0; i < blocks.length; i++) {
                        var block = blocks[i];
                        var blocklist = block.ips;
                        if (blocklist && Array.isArray(blocklist)) {
                            for (var x = 0; x < blocklist.length; x++) {
                                var ip = blocklist[x];
                                if (!openflow_api_1.NoderedUtil.IsNullEmpty(ip)) {
                                    ip = ip.toLowerCase();
                                    if (ip == remoteip) {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        catch (error) {
            Logger_1.Logger.instanse.error("WebServer", "/ipblock", error);
        }
        return false;
    }
    static async configure(baseurl, parent) {
        const span = Logger_1.Logger.otel.startSubSpan("WebServer.configure", parent);
        WebServer.BaseRateLimiter = new RateLimiterMemory({
            points: Config_1.Config.api_rate_limit_points,
            duration: Config_1.Config.api_rate_limit_duration,
        });
        try {
            if (!openflow_api_1.NoderedUtil.IsNullUndefinded(Logger_1.Logger.otel)) {
                // websocket_rate_limit = Logger.otel.meter.createObservableUpDownCounter("openflow_webserver_rate_limit_count", {
                //     description: 'Total number of rate limited web request'
                // }) // "route"
            }
            this.app = express();
            this.app.disable("x-powered-by");
            const loggerstream = {
                write: function (message, encoding) {
                    Logger_1.Logger.instanse.silly("WebServer", "configure", message);
                }
            };
            this.app.use(async (req, res, next) => {
                if (await WebServer.isBlocked(req)) {
                    var remoteip = WebSocketServerClient_1.WebSocketServerClient.remoteip(req);
                    Logger_1.Logger.instanse.error("WebServer", "setCORSHeaders", remoteip + " is blocked");
                    try {
                        res.status(429).json({ "message": "ip blocked" });
                    }
                    catch (error) {
                    }
                    return;
                }
                next();
            });
            this.app.use("/", express.static(path.join(__dirname, "/public")));
            this.app.use(morgan('combined', { stream: loggerstream }));
            this.app.use(compression());
            this.app.use(express.urlencoded({ extended: true }));
            this.app.use(express.json());
            this.app.use(cookieParser());
            this.app.set('trust proxy', 1);
            this.app.use(cookieSession({
                name: "session", secret: Config_1.Config.cookie_secret, httpOnly: true
            }));
            this.app.use(flash());
            if (Config_1.Config.api_rate_limit)
                this.app.use(rateLimiter);
            this.app.get("/livenessprobe", (req, res, next) => {
                if (openflow_api_1.NoderedUtil.IsNullEmpty(_hostname))
                    _hostname = (Config_1.Config.getEnv("HOSTNAME", undefined) || os.hostname()) || "unknown";
                res.end(JSON.stringify({ "success": "true", "hostname": _hostname }));
                res.end();
            });
            // https://scaleup.us/2020/06/21/how-to-block-ips-in-your-traefik-proxy-server/
            this.app.get("/ipblock", async (req, res, next) => {
                if (await WebServer.isBlocked(req)) {
                    var remoteip = LoginProvider_1.LoginProvider.remoteip(req);
                    Logger_1.Logger.instanse.error("WebServer", "/ipblock", remoteip + " is blocked");
                    res.statusCode = 401;
                    res.setHeader('WWW-Authenticate', 'Basic realm="OpenFlow"');
                    res.end('Unauthorized');
                    return;
                }
                return res.status(200).send({ message: 'ok.' });
            });
            // Add headers
            this.app.use(function (req, res, next) {
                Logger_1.Logger.instanse.verbose('WebServer', 'setCORSHeaders', "add for " + req.originalUrl);
                // const origin: string = (req.headers.origin as any);
                // if (NoderedUtil.IsNullEmpty(origin)) {
                //     res.header('Access-Control-Allow-Origin', '*');
                // } else {
                //     res.header('Access-Control-Allow-Origin', origin);
                // }
                // Website you wish to allow to connect
                res.setHeader('Access-Control-Allow-Origin', '*');
                // Request methods you wish to allow
                res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
                // Request headers you wish to allow
                res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Access-Control-Allow-Headers, Authorization");
                // Set to true if you need the website to include cookies in the requests sent
                // to the API (e.g. in case you use sessions)
                res.setHeader('Access-Control-Allow-Credentials', "true");
                // Disable Caching
                res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
                res.header('Expires', '-1');
                res.header('Pragma', 'no-cache');
                if (req.originalUrl == "/me") {
                    res.redirect('/oidc/me');
                    return next();
                }
                // Grafana hack
                if (req.originalUrl == "/oidc/me" && req.method == "OPTIONS") {
                    return res.send("ok");
                }
                if (req.originalUrl.indexOf('/oidc') > -1)
                    return next();
                // Pass to next layer of middleware
                next();
            });
            span === null || span === void 0 ? void 0 : span.addEvent("Configure LoginProvider");
            await LoginProvider_1.LoginProvider.configure(this.app, baseurl);
            try {
                span === null || span === void 0 ? void 0 : span.addEvent("Configure FormioEP");
                let FormioEPProxy = null;
                try {
                    FormioEPProxy = require("./ee/FormioEP");
                }
                catch (error) {
                }
                if (!openflow_api_1.NoderedUtil.IsNullUndefinded(FormioEPProxy)) {
                    await FormioEPProxy.FormioEP.configure(this.app, baseurl);
                }
            }
            catch (error) {
            }
            span === null || span === void 0 ? void 0 : span.addEvent("Configure SamlProvider");
            await SamlProvider_1.SamlProvider.configure(this.app, baseurl);
            WebServer.server = null;
            if (Config_1.Config.tls_crt != '' && Config_1.Config.tls_key != '') {
                let options = {
                    cert: Config_1.Config.tls_crt,
                    key: Config_1.Config.tls_key
                };
                if (Config_1.Config.tls_crt.indexOf("---") == -1) {
                    options = {
                        cert: Buffer.from(Config_1.Config.tls_crt, 'base64').toString('ascii'),
                        key: Buffer.from(Config_1.Config.tls_key, 'base64').toString('ascii')
                    };
                }
                let ca = Config_1.Config.tls_ca;
                if (ca !== "") {
                    if (ca.indexOf("---") === -1) {
                        ca = Buffer.from(Config_1.Config.tls_ca, 'base64').toString('ascii');
                    }
                    options.ca = ca;
                }
                if (Config_1.Config.tls_passphrase !== "") {
                    options.passphrase = Config_1.Config.tls_passphrase;
                }
                WebServer.server = https.createServer(options, this.app);
            }
            else {
                WebServer.server = http.createServer(this.app);
            }
            await Config_1.Config.db.connect(span);
            return WebServer.server;
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            Logger_1.Logger.instanse.error("WebServer", "configure", error);
            return null;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    static Listen() {
        WebServer.server.listen(Config_1.Config.port).on('error', function (error) {
            Logger_1.Logger.instanse.error("WebServer", "Listen", error);
            if (Config_1.Config.NODE_ENV == "production") {
                WebServer.server.close();
                process.exit(404);
            }
        });
        Logger_1.Logger.instanse.info("WebServer", "Listen", "Listening on " + Config_1.Config.baseurl());
    }
}
exports.WebServer = WebServer;
WebServer.server = null;
//# sourceMappingURL=WebServer.js.map