"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.start = start;
exports.customFunctionsServer = customFunctionsServer;
exports.ready = ready;
const path_1 = require("path");
const fs_1 = require("fs");
const fastify_1 = __importDefault(require("fastify"));
const cors_1 = __importDefault(require("@fastify/cors"));
const requestTimePlugin_js_1 = __importDefault(require("./serverHelpers/requestTimePlugin.js"));
const autoload_1 = __importDefault(require("@fastify/autoload"));
const env = __importStar(require("../utility/environment/environmentManager.js"));
const hdbTerms_ts_1 = require("../utility/hdbTerms.js");
const harperLogger = __importStar(require("../utility/logging/harper_logger.js"));
const hdbCore = __importStar(require("./fastifyRoutes/plugins/hdbCore.js"));
const userSchema = __importStar(require("../security/user.js"));
const getServerOptions_js_1 = __importDefault(require("./fastifyRoutes/helpers/getServerOptions.js"));
const getCORSOptions_js_1 = __importDefault(require("./fastifyRoutes/helpers/getCORSOptions.js"));
const getHeaderTimeoutConfig_js_1 = __importDefault(require("./fastifyRoutes/helpers/getHeaderTimeoutConfig.js"));
const serverHandlers_js_1 = require("../server/serverHelpers/serverHandlers.js");
const contentTypes_ts_1 = require("../server/serverHelpers/contentTypes.js");
const Server_ts_1 = require("./Server.js");
let fastifyServer;
const routeFolders = new Set();
/**
 * This is the entry point for the fastify route autoloader plugin. This plugin loads JS modules from provided path
 * (configurable) and gives them access to the fastify server, so they can register route handlers. This builds a
 * fastify server instance on-demand, and registers it with the main http access point. Prior to 4.2 this (and static)
 * were basically the only loaders for Harper applications, and this supports all legacy custom functions that rely
 * on fastify routes. Fastify's performance is not as good as our native HTTP handling, so generally this isn't the
 * first choice for new applications where performance is a priority, but certainly is a good option for anyone who
 * likes and/or is familiar with fastify and wants to use its plugins.
 * @param jsContent
 * @param relativePath
 * @param filePath
 * @param projectName
 */
function start(options) {
    // if we have a secure port, need to use the secure HTTP server for fastify (it can be used for HTTP as well)
    const isHttps = options.securePort > 0;
    return {
        // eslint-disable-next-line no-unused-vars
        async handleFile(jsContent, relativePath, filePath, projectName) {
            if (!fastifyServer) {
                fastifyServer = buildServer(isHttps);
                Server_ts_1.server.http((await fastifyServer).server);
            }
            const resolvedServer = await fastifyServer;
            const routeFolder = (0, path_1.dirname)(filePath);
            let prefix = (0, path_1.dirname)(relativePath);
            if (prefix.startsWith('/'))
                prefix = prefix.slice(1);
            if (!routeFolders.has(routeFolder)) {
                routeFolders.add(routeFolder);
                try {
                    resolvedServer.register(buildRouteFolder(routeFolder, prefix));
                }
                catch (error) {
                    if (error.message === 'Root plugin has already booted')
                        harperLogger.warn(`Could not load root fastify route for ${filePath}, this may require a restart to install properly`);
                    else
                        throw error;
                }
            }
        },
        ready,
    };
}
/**
 * Function called to start up server instance on a forked process - this method is called from customFunctionServer after process is
 * forked in the serverParent module
 *
 * @returns {Promise<void>}
 */
async function customFunctionsServer() {
    try {
        // Instantiate new instance of HDB IPC client and assign it to global.
        harperLogger.info('In Custom Functions Fastify server' + process.cwd());
        harperLogger.info(`Custom Functions Running with NODE_ENV set as: ${process.env.NODE_ENV}`);
        harperLogger.debug(`Custom Functions server process ${process.pid} starting up.`);
        await setUp();
        const isHttps = env.get(hdbTerms_ts_1.CONFIG_PARAMS.HTTP_SECUREPORT) > 0;
        let server;
        try {
            //generate a Fastify server instance
            server = fastifyServer = await buildServer(isHttps);
        }
        catch (err) {
            harperLogger.error(`Custom Functions buildServer error: ${err}`);
            throw err;
        }
        try {
            //make sure the process waits for the server to be fully instantiated before moving forward
            await server.ready();
        }
        catch (err) {
            harperLogger.error(`Custom Functions server.ready() error: ${err}`);
            throw err;
        }
        // fastify can't clean up properly
        server.server.cantCleanupProperly = true;
    }
    catch (err) {
        harperLogger.error(`Custom Functions ${process.pid} Error: ${err}`);
        harperLogger.error(err);
        process.exit(1);
    }
}
/**
 * Makes sure global values are set before server starts.
 * @returns {Promise<void>}
 */
async function setUp() {
    try {
        harperLogger.info('Custom Functions starting configuration.');
        await userSchema.setUsersWithRolesCache();
        harperLogger.info('Custom Functions completed configuration.');
    }
    catch (e) {
        harperLogger.error(e);
    }
}
// eslint-disable-next-line require-await
function buildRouteFolder(routesFolder, projectName) {
    return async function (cfServer) {
        try {
            harperLogger.info('Custom Functions starting buildRoutes');
            harperLogger.trace('Loading fastify routes folder ' + routesFolder);
            const setUpRoutes = (0, fs_1.existsSync)(routesFolder);
            // check for a routes folder and, if present, ingest each of the route files in the project's routes folder
            if (setUpRoutes) {
                cfServer
                    .register(autoload_1.default, (parent) => ({
                    dir: routesFolder,
                    dirNameRoutePrefix: false,
                    options: {
                        hdbCore: parent.hdbCore,
                        logger: harperLogger.loggerWithTag('custom-function'),
                        prefix: `/${projectName}`,
                    },
                }))
                    .after((err, instance, next) => {
                    if (err?.message) {
                        harperLogger.error(err.message);
                    }
                    else if (err) {
                        harperLogger.error(err);
                    }
                    next();
                });
            }
        }
        catch (e) {
            harperLogger.error(`Custom Functions errored buildRoutes: ${e}`);
        }
    };
}
/**
 * This method configures and returns a Fastify server - for either HTTP or HTTPS  - based on the provided config settings
 *
 * @param isHttps - <boolean> - type of communication protocol to build server for
 * @returns {FastifyInstance}
 */
async function buildServer(isHttps) {
    harperLogger.info(`Custom Functions starting buildServer.`);
    const serverOpts = (0, getServerOptions_js_1.default)(isHttps);
    const app = (0, fastify_1.default)(serverOpts);
    //Fastify does not set this property in the initial app construction
    app.server.headersTimeout = (0, getHeaderTimeoutConfig_js_1.default)();
    //set top-level error handler for server - all errors caught/thrown within the API will bubble up to this handler so they
    // can be handled in a coordinated way
    app.setErrorHandler(serverHandlers_js_1.serverErrorHandler);
    const corsOptions = (0, getCORSOptions_js_1.default)();
    if (corsOptions) {
        app.register(cors_1.default, corsOptions);
    }
    app.register(function (instance, options, done) {
        instance.setNotFoundHandler(function (request, reply) {
            if (reply.sent || reply.raw.headersSent || reply.raw.writableEnded)
                return;
            app.server.emit('unhandled', request.raw, reply.raw);
        });
        done();
    });
    app.register(requestTimePlugin_js_1.default);
    await app.register(hdbCore);
    await app.after();
    (0, contentTypes_ts_1.registerContentHandlers)(app);
    harperLogger.info(`Custom Functions completed buildServer.`);
    return app;
}
function ready() {
    if (fastifyServer) {
        if (fastifyServer.then)
            return fastifyServer.then((server) => {
                return server.ready();
            });
        return fastifyServer.ready();
    }
}
//# sourceMappingURL=fastifyRoutes.js.map