"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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
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 __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const env = __importStar(require("./environment"));
const db_1 = __importDefault(require("./db"));
db_1.default.init();
const Koa = require("koa");
const cors = require("@koa/cors");
const destroyable = require("server-destroy");
const koaBody = require("koa-body");
const pino = require("koa-pino-logger");
const http = require("http");
const api = require("./api");
const eventEmitter = require("./events");
const automations = require("./automations/index");
const Sentry = require("@sentry/node");
const fileSystem = require("./utilities/fileSystem");
const bullboard = require("./automations/bullboard");
const { logAlert } = require("@devlego/backend-core/logging");
const { Thread } = require("./threads");
const redis_1 = __importDefault(require("./utilities/redis"));
const migrations = __importStar(require("./migrations"));
const backend_core_1 = require("@devlego/backend-core");
const workerRequests_1 = require("./utilities/workerRequests");
const app = new Koa();
app.use(cors());
// set up top level koa middleware
app.use(koaBody({
    multipart: true,
    formLimit: "10mb",
    jsonLimit: "10mb",
    textLimit: "10mb",
    enableTypes: ["json", "form", "text"],
    parsedMethods: ["POST", "PUT", "PATCH", "DELETE"],
}));
app.use(pino({
    prettyPrint: {
        levelFirst: true,
    },
    level: env.LOG_LEVEL || "error",
}));
if (!env.isTest()) {
    const plugin = bullboard.init();
    app.use(plugin);
}
app.context.eventEmitter = eventEmitter;
app.context.auth = {};
// api routes
app.use(api.router.routes());
if (env.isProd()) {
    env._set("NODE_ENV", "production");
    Sentry.init();
    app.on("error", (err, ctx) => {
        Sentry.withScope(function (scope) {
            scope.addEventProcessor(function (event) {
                return Sentry.Handlers.parseRequest(event, ctx.request);
            });
            Sentry.captureException(err);
        });
    });
}
const server = http.createServer(app.callback());
destroyable(server);
let shuttingDown = false, errCode = 0;
server.on("close", () => __awaiter(void 0, void 0, void 0, function* () {
    // already in process
    if (shuttingDown) {
        return;
    }
    shuttingDown = true;
    if (!env.isTest()) {
        console.log("Server Closed");
    }
    yield automations.shutdown();
    yield redis_1.default.shutdown();
    yield backend_core_1.events.shutdown();
    yield Thread.shutdown();
    api.shutdown();
    if (!env.isTest()) {
        process.exit(errCode);
    }
}));
module.exports = server.listen(env.PORT || 0, () => __awaiter(void 0, void 0, void 0, function* () {
    var _a;
    console.log(`Budibase running on ${JSON.stringify(server.address())}`);
    env._set("PORT", server.address().port);
    eventEmitter.emitPort(env.PORT);
    fileSystem.init();
    yield redis_1.default.init();
    // run migrations on startup if not done via http
    // not recommended in a clustered environment
    if (!env.HTTP_MIGRATIONS && !env.isTest()) {
        try {
            yield migrations.migrate();
        }
        catch (e) {
            logAlert("Error performing migrations. Exiting.", e);
            shutdown();
        }
    }
    // check and create admin user if required
    if (env.SELF_HOSTED &&
        !env.MULTI_TENANCY &&
        env.BB_ADMIN_USER_EMAIL &&
        env.BB_ADMIN_USER_PASSWORD) {
        const checklist = yield (0, workerRequests_1.getChecklist)();
        if (!((_a = checklist === null || checklist === void 0 ? void 0 : checklist.adminUser) === null || _a === void 0 ? void 0 : _a.checked)) {
            try {
                const tenantId = backend_core_1.tenancy.getTenantId();
                yield (0, workerRequests_1.createAdminUser)(env.BB_ADMIN_USER_EMAIL, env.BB_ADMIN_USER_PASSWORD, tenantId);
                console.log("Admin account automatically created for", env.BB_ADMIN_USER_EMAIL);
            }
            catch (e) {
                logAlert("Error creating initial admin user. Exiting.", e);
                shutdown();
            }
        }
    }
    // check for version updates
    yield backend_core_1.installation.checkInstallVersion();
    // done last - this will never complete
    yield automations.init();
}));
const shutdown = () => {
    server.close();
    server.destroy();
};
process.on("uncaughtException", err => {
    // @ts-ignore
    // don't worry about this error, comes from zlib isn't important
    if (err && err["code"] === "ERR_INVALID_CHAR") {
        return;
    }
    errCode = -1;
    logAlert("Uncaught exception.", err);
    shutdown();
});
process.on("SIGTERM", () => {
    shutdown();
});
