"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebServer = void 0;
var path = require("path");
var http = require("http");
var https = require("https");
var express = require("express");
var compression = require("compression");
var cookieParser = require("cookie-parser");
var cookieSession = require("cookie-session");
var flash = require("flash");
var morgan = require("morgan");
var SamlProvider_1 = require("./SamlProvider");
var LoginProvider_1 = require("./LoginProvider");
var Config_1 = require("./Config");
var openflow_api_1 = require("@openiap/openflow-api");
var RateLimiterMemory = require('rate-limiter-flexible').RateLimiterMemory;
var url = require("url");
var WebSocketServer_1 = require("./WebSocketServer");
var Logger_1 = require("./Logger");
var BaseRateLimiter = new RateLimiterMemory({
    points: Config_1.Config.api_rate_limit_points,
    duration: Config_1.Config.api_rate_limit_duration,
});
var rateLimiter = function (req, res, next) {
    BaseRateLimiter
        .consume(WebServer.remoteip(req))
        .then(function (e) {
        // console.info("API_O_RATE_LIMIT consumedPoints: " + e.consumedPoints + " remainingPoints: " + e.remainingPoints);
        next();
    })
        .catch(function (e) {
        var route = url.parse(req.url).pathname;
        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(websocket_rate_limit))
            websocket_rate_limit.bind(__assign(__assign({}, Logger_1.Logger.otel.defaultlabels), { route: route })).add(1);
        console.warn("API_RATE_LIMIT consumedPoints: " + e.consumedPoints + " remainingPoints: " + e.remainingPoints + " msBeforeNext: " + e.msBeforeNext);
        res.status(429).json({ response: 'RATE_LIMIT' });
    });
};
var websocket_rate_limit = null;
var WebServer = /** @class */ (function () {
    function WebServer() {
    }
    WebServer.remoteip = function (req) {
        var 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;
    };
    WebServer.configure = function (baseurl) {
        return __awaiter(this, void 0, void 0, function () {
            var loggerstream, server, options, ca, port;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(Logger_1.Logger.otel)) {
                            websocket_rate_limit = Logger_1.Logger.otel.meter.createCounter("openflow_webserver_rate_limit_count", {
                                description: 'Total number of rate limited web request'
                            }); // "route"
                        }
                        this.app = express();
                        this.app.get("/metrics", function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
                            var result, _loop_1, i;
                            return __generator(this, function (_a) {
                                result = "";
                                _loop_1 = function (i) {
                                    var cli = WebSocketServer_1.WebSocketServer._clients[i];
                                    try {
                                        if (!openflow_api_1.NoderedUtil.IsNullEmpty(cli.metrics) && cli.user != null) {
                                            var arr = cli.metrics.split('\n');
                                            var replacer = function (match, offset, string) {
                                                return '{' + offset + ',username="' + cli.user.username + '"}';
                                            };
                                            for (var y = 0; y < arr.length; y++) {
                                                var line = arr[y];
                                                if (!line.startsWith("#")) {
                                                    if (line.indexOf("}") > -1) {
                                                        line = line.replace(/{(.*)}/gi, replacer);
                                                        arr[y] = line;
                                                    }
                                                    else if (!openflow_api_1.NoderedUtil.IsNullEmpty(line) && line.indexOf(' ') > -1) {
                                                        var _arr = line.split(' ');
                                                        _arr[0] += '{username="' + cli.user.username + '"}';
                                                        line = _arr.join(' ');
                                                        arr[y] = line;
                                                    }
                                                }
                                            }
                                            result += arr.join('\n') + '\n';
                                        }
                                    }
                                    catch (error) {
                                        console.error(error);
                                    }
                                };
                                for (i = WebSocketServer_1.WebSocketServer._clients.length - 1; i >= 0; i--) {
                                    _loop_1(i);
                                }
                                res.set({ 'Content-Type': 'text/plain' });
                                res.send(result);
                                return [2 /*return*/];
                            });
                        }); });
                        loggerstream = {
                            write: function (message, encoding) {
                                Logger_1.Logger.instanse.silly(message);
                            }
                        };
                        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);
                        // Add headers
                        this.app.use(function (req, res, next) {
                            // 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', 'X-Requested-With,content-type');
                            // 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");
                            // Pass to next layer of middleware
                            next();
                        });
                        return [4 /*yield*/, LoginProvider_1.LoginProvider.configure(this.app, baseurl)];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, SamlProvider_1.SamlProvider.configure(this.app, baseurl)];
                    case 2:
                        _a.sent();
                        server = null;
                        if (Config_1.Config.tls_crt != '' && Config_1.Config.tls_key != '') {
                            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')
                                };
                            }
                            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;
                                // options.cert += "\n" + ca;
                            }
                            if (Config_1.Config.tls_passphrase !== "") {
                                options.passphrase = Config_1.Config.tls_passphrase;
                            }
                            server = https.createServer(options, this.app);
                        }
                        else {
                            server = http.createServer(this.app);
                        }
                        port = Config_1.Config.port;
                        server.listen(port).on('error', function (error) {
                            Logger_1.Logger.instanse.error(error);
                            process.exit(404);
                        });
                        return [2 /*return*/, server];
                }
            });
        });
    };
    return WebServer;
}());
exports.WebServer = WebServer;
//# sourceMappingURL=WebServer.js.map