#!/usr/bin/env node require("dotenv").config() require("sucrase/register") const path = require("node:path") const Module = require("node:module") const { Buffer } = require("node:buffer") const { webcrypto: crypto } = require("node:crypto") const { InfisicalClient } = require("@infisical/sdk") const moduleAlias = require("module-alias") const { onExit } = require("signal-exit") const opentelemetry = require("@opentelemetry/sdk-node") const { getNodeAutoInstrumentations, } = require("@opentelemetry/auto-instrumentations-node") const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-http") const { OTLPLogExporter } = require("@opentelemetry/exporter-logs-otlp-http") const { Resource } = require("@opentelemetry/resources") const { SemanticResourceAttributes, } = require("@opentelemetry/semantic-conventions") // Override file execution arg process.argv.splice(1, 1) process.argv[1] = path.resolve(process.argv[1]) // Expose boot function to global global.Boot = Boot global.isProduction = process.env.NODE_ENV === "production" global["__root"] = path.resolve(process.cwd()) global["__src"] = path.resolve( globalThis["__root"], path.dirname(process.argv[1]), ) global["aliases"] = { root: global["__root"], src: global["__src"], // expose shared resources "@db_models": path.resolve(__dirname, "db_models"), "@shared-utils": path.resolve(__dirname, "utils"), "@shared-classes": path.resolve(__dirname, "classes"), "@shared-lib": path.resolve(__dirname, "lib"), "@shared-middlewares": path.resolve(__dirname, "middlewares"), // expose internal resources "@lib": path.resolve(global["__src"], "lib"), "@middlewares": path.resolve(global["__src"], "middlewares"), "@controllers": path.resolve(global["__src"], "controllers"), "@config": path.resolve(global["__src"], "config"), "@shared": path.resolve(global["__src"], "shared"), "@classes": path.resolve(global["__src"], "classes"), "@models": path.resolve(global["__src"], "models"), "@services": path.resolve(global["__src"], "services"), "@utils": path.resolve(global["__src"], "utils"), } function registerBaseAliases(fromPath, customAliases = {}) { if (typeof fromPath === "undefined") { if (module.parent.filename.includes("dist")) { fromPath = path.resolve(process.cwd(), "dist") } else { fromPath = path.resolve(process.cwd(), "src") } } moduleAlias.addAliases({ ...customAliases, "@controllers": path.resolve(fromPath, "controllers"), "@middlewares": path.resolve(fromPath, "middlewares"), "@models": path.resolve(fromPath, "models"), "@classes": path.resolve(fromPath, "classes"), "@lib": path.resolve(fromPath, "lib"), "@utils": path.resolve(fromPath, "utils"), }) } function registerPatches() { global.b64Decode = (data) => { return Buffer.from(data, "base64").toString("utf-8") } global.b64Encode = (data) => { return Buffer.from(data, "utf-8").toString("base64") } global.nanoid = (t = 21) => crypto .getRandomValues(new Uint8Array(t)) .reduce( (t, e) => (t += (e &= 63) < 36 ? e.toString(36) : e < 62 ? (e - 26).toString(36).toUpperCase() : e > 62 ? "-" : "_"), "", ) Array.prototype.updateFromObjectKeys = function (obj) { this.forEach((value, index) => { if (obj[value] !== undefined) { this[index] = obj[value] } }) return this } global.ToBoolean = (value) => { if (typeof value === "boolean") { return value } if (typeof value === "string") { return value.toLowerCase() === "true" } return false } } function registerAliases() { registerBaseAliases(global["__src"], global["aliases"]) } async function injectEnvFromInfisical() { const envMode = (global.FORCE_ENV ?? global.isProduction) ? "prod" : "dev" console.log( `[BOOT] 🔑 Injecting env variables from INFISICAL in [${envMode}] mode...`, ) const client = new InfisicalClient({ auth: { universalAuth: { clientId: process.env.INFISICAL_CLIENT_ID, clientSecret: process.env.INFISICAL_CLIENT_SECRET, }, }, }) const secrets = await client.listSecrets({ environment: envMode, path: process.env.INFISICAL_PATH ?? "/", projectId: process.env.INFISICAL_PROJECT_ID ?? null, includeImports: false, }) //inject to process.env secrets.forEach((secret) => { if (!process.env[secret.secretKey]) { process.env[secret.secretKey] = secret.secretValue } }) } async function Boot(main) { if (!main) { throw new Error("main class is not defined") } const service_id = process.env.lb_service.id console.log( `[BOOT] Booting (${service_id}) in [${global.isProduction ? "production" : "development"}] mode...`, ) const traceExporter = new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ?? "http://fr02.ragestudio.net:4318/v1/traces", }) const logExporter = new OTLPLogExporter({ url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ?? "http://fr02.ragestudio.net:4318/v1/logs", }) const sdk = new opentelemetry.NodeSDK({ traceExporter, logExporter, instrumentations: [getNodeAutoInstrumentations()], resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: service_id ?? "node_app", }), }) sdk.start() if ( process.env.INFISICAL_CLIENT_ID && process.env.INFISICAL_CLIENT_SECRET ) { console.log( `[BOOT] INFISICAL Credentials found, injecting env variables from INFISICAL...`, ) await injectEnvFromInfisical() } const instance = new main() onExit( (code, signal) => { console.log(`[BOOT] Cleaning up...`) sdk.shutdown() .then(() => console.log("Tracing terminated")) .catch((error) => console.log("Error terminating tracing", error), ) if (typeof instance.onClose === "function") { instance.onClose() } instance.engine.close() }, { alwaysLast: true, }, ) await instance.initialize() if (process.env.lb_service && process.send) { process.send({ status: "ready", }) } return instance } try { // Apply patches registerPatches() // Apply aliases registerAliases() // execute main Module.runMain() } catch (error) { console.error("[BOOT] ❌ Boot error: ", error) }