"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const express_basic_auth_1 = __importDefault(require("express-basic-auth"));
const log4js_1 = __importDefault(require("log4js"));
const helpers_1 = require("./helpers");
const routes_1 = __importDefault(require("./controllers/routes"));
const init_1 = __importDefault(require("./init"));
const models_1 = require("./models");
async function start() {
    // Spawn the Express app.
    const app = (0, express_1.default)();
    // Middlewares.
    // Middleware for removing the url prefix, if it's set.
    if (process.kresus.urlPrefix !== '/') {
        const rootRegexp = (0, helpers_1.makeUrlPrefixRegExp)(process.kresus.urlPrefix);
        // We use a trick here, by having something that rewrites the URL by
        // removing the URL prefix if it was set.
        //
        // This means that if the browser tries to access any resource without
        // the URL prefix, this would work; so make sure to return 404 on these
        // accesses, to avoid hard-to-debug situations.
        app.use((req, res, next) => {
            // Safeguard against misuses.
            if (!req.url.startsWith(process.kresus.urlPrefix)) {
                res.status(404);
                return false;
            }
            req.url = req.url.replace(rootRegexp, '/');
            return next();
        });
    }
    // Generic express middlewares.
    app.use(log4js_1.default.connectLogger(log4js_1.default.getLogger('HTTP'), {
        level: 'auto',
        format: ':method :url - :status (:response-time ms)',
        // By default all 3xx status codes, whereas not harmful, will emit a warning message.
        // Only keep the warning for 300 (multiple choices) & 310 (too many redirections).
        statusRules: [
            {
                from: 301,
                to: 309,
                level: 'info',
            },
        ],
    }));
    if (process.kresus.basicAuth) {
        app.use((0, express_basic_auth_1.default)({
            users: process.kresus.basicAuth,
            challenge: true,
            realm: 'Kresus Basic Auth',
        }));
    }
    app.use(express_1.default.json({
        limit: '100mb',
    }));
    app.use(express_1.default.urlencoded({
        extended: true,
        limit: '10mb',
    }));
    app.use(express_1.default.text({
        limit: '100mb',
    }));
    app.use(express_1.default.static(`${__dirname}/../client`, {}));
    if (process.env.NODE_ENV === 'development') {
        // In development mode, allow any cross-origin resource sharing.
        // Note that having both Allow-Origin set to "*" and credentials in a
        // request are disallowed, so we just reflect the origin header back in
        // the allow-origin CORS header.
        app.use((req, res, next) => {
            res.header('Access-Control-Allow-Origin', req.headers.origin);
            res.header('Access-Control-Allow-Headers', 'content-type');
            res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
            res.header('Access-Control-Allow-Credentials', 'true');
            next();
        });
    }
    // Use a passportjs compatible middleware for logging the only current
    // user.
    app.use(async (req, res, next) => {
        const { userLoginHttpHeader, defaultUser } = process.kresus;
        if (userLoginHttpHeader) {
            // Try to retrieve the user login from this header name.
            const userLogin = req.get(userLoginHttpHeader);
            if (!userLogin) {
                res.status(401).json({
                    code: 401,
                    message: `missing "${userLoginHttpHeader}" header`,
                });
                return;
            }
            // Then check if this user actually exists.
            const user = await models_1.User.findByLogin(userLogin);
            if (!user) {
                res.status(401).json({
                    code: 401,
                    message: `user "${userLogin}" could not be found`,
                });
                return;
            }
            req.user = user;
        }
        else {
            req.user = defaultUser;
        }
        next();
    });
    // Routes.
    for (const reqpath of Object.keys(routes_1.default)) {
        const descriptor = routes_1.default[reqpath];
        for (const [verb, controller] of Object.entries(descriptor)) {
            switch (verb) {
                case 'param': {
                    const paramName = reqpath.split('/').pop();
                    // paramName can never be undefined due to reqpath.split() returning always
                    // an array with at least one item, but as Array.pop can be undefined,
                    // TypeScript wants the check.
                    if (typeof paramName !== 'undefined') {
                        app.param(paramName, controller);
                    }
                    break;
                }
                case 'put':
                    app.put(`/${reqpath}`, controller);
                    break;
                case 'post':
                    app.post(`/${reqpath}`, controller);
                    break;
                case 'delete':
                    app.delete(`/${reqpath}`, controller);
                    break;
                case 'get':
                    app.get(`/${reqpath}`, controller);
                    break;
                default:
                    throw new Error(`unknown API verb used in index.ts: ${verb}`);
            }
        }
    }
    const server = app.listen(process.kresus.port, process.kresus.host);
    // Raise the timeout limit, since some banking modules can be quite
    // long at fetching new transactions. Time is in milliseconds.
    server.timeout = 5 * 60 * 1000;
    await (0, init_1.default)();
}
module.exports = {
    start,
};
