Uecko_ERP/apps/server/src/index.ts

165 lines
5.2 KiB
TypeScript
Raw Normal View History

2025-02-03 13:12:36 +00:00
import http from "http";
import { DateTime } from "luxon";
2025-05-02 21:43:51 +00:00
import os from "os";
2025-04-22 15:09:57 +00:00
import { createApp } from "./app";
2025-02-03 13:12:36 +00:00
import { ENV } from "./config";
2025-05-02 21:43:51 +00:00
import { tryConnectToDatabase } from "./config/database";
2025-04-22 15:09:57 +00:00
import { logger } from "./core/common/infrastructure/logger";
2025-01-28 14:01:02 +00:00
2025-02-03 13:12:36 +00:00
// Guardamos información del estado del servidor
export const currentState = {
launchedAt: DateTime.now(),
appPath: process.cwd(),
host: ENV.HOST,
port: ENV.PORT,
environment: ENV.NODE_ENV,
connections: {} as Record<string, any>,
};
// Manejo de cierre forzado del servidor (graceful shutdown)
const serverStop = (server: http.Server) => {
const forceTimeout = 30000;
return new Promise<void>((resolve, reject) => {
logger.warn("⚡️ Shutting down server");
setTimeout(() => {
logger.error("Could not close connections in time, forcefully shutting down");
resolve();
}, forceTimeout).unref();
server.close((err) => {
if (err) {
return reject(err);
}
logger.info("Closed out remaining connections.");
2025-02-03 19:59:40 +00:00
logger.info("❎ Bye!");
2025-02-03 13:12:36 +00:00
resolve();
});
});
};
// Manejo de errores al iniciar el servidor
const serverError = (error: NodeJS.ErrnoException) => {
2025-05-02 21:43:51 +00:00
logger.error({
message: `⛔️ Server wasn't able to start properly.`,
label: "serverError0",
error,
});
2025-02-03 13:12:36 +00:00
if (error.code === "EADDRINUSE") {
2025-05-02 21:43:51 +00:00
logger.error({ message: error.message, error, label: "serverError1" });
2025-02-15 21:30:12 +00:00
//logger.error(`The port ${error.port} is already used by another application.`);
2025-02-03 13:12:36 +00:00
} else {
2025-05-02 21:43:51 +00:00
logger.error({ message: error.message, error, label: "serverError2" });
2025-02-03 13:12:36 +00:00
}
// Dependiendo de la criticidad, podrías forzar el proceso a salir
2025-04-22 08:11:50 +00:00
process.exit(1);
2025-02-03 13:12:36 +00:00
};
2025-01-28 14:01:02 +00:00
2025-02-03 13:12:36 +00:00
// Almacena en "connections" cada nueva conexión (descomentar si se quiere seguimiento)
const serverConnection = (conn: any) => {
const key = `${conn.remoteAddress}:${conn.remotePort}`;
currentState.connections[key] = conn;
2025-01-29 16:01:17 +00:00
2025-02-03 13:12:36 +00:00
conn.on("close", () => {
delete currentState.connections[key];
2025-01-29 16:01:17 +00:00
});
2025-02-03 13:12:36 +00:00
};
//const sequelizeConn = createSequelizeAdapter();
//const firebirdConn = createFirebirdAdapter();
2025-04-22 17:14:34 +00:00
// Cargar paquetes de la aplicación
2025-05-02 21:43:51 +00:00
// registerPackages();
2025-04-22 17:14:34 +00:00
2025-02-03 13:12:36 +00:00
// Crea el servidor HTTP
const server = http
.createServer(createApp())
.once("listening", () =>
process.on("SIGINT", async () => {
// Por ejemplo, podrías desconectar la base de datos aquí:
// firebirdConn.disconnect();
// O forzar desconexión en adapters
// sequelizeConn.close();
await serverStop(server);
})
)
.on("close", () =>
logger.info(`Shut down at: ${DateTime.now().toLocaleString(DateTime.DATETIME_FULL)}`)
)
.on("connection", serverConnection)
.on("error", serverError);
// Ejemplo de adapters de base de datos (descoméntalos si los necesitas)
// const sequelizeConn = createSequelizeAdapter();
// const firebirdConn = createFirebirdAdapter();
// Manejo de promesas no capturadas
process.on("unhandledRejection", (reason: any, promise: Promise<any>) => {
2025-02-03 19:59:40 +00:00
logger.error("❌ Unhandled rejection at:", promise, "reason:", reason);
2025-02-03 13:12:36 +00:00
// Dependiendo de la aplicación, podrías desear una salida total o un cierre controlado
2025-04-01 14:26:15 +00:00
process.exit(1);
2025-02-03 13:12:36 +00:00
});
// Manejo de excepciones no controladas
process.on("uncaughtException", (error: Error) => {
// firebirdConn.disconnect();
2025-02-03 19:59:40 +00:00
logger.error(`❌ Uncaught exception:`, error.message);
2025-02-03 13:12:36 +00:00
logger.error(error.stack);
// process.exit(1);
});
// Arranca el servidor si la conexión a la base de datos va bien
(async () => {
try {
2025-02-03 19:50:16 +00:00
const now = DateTime.now();
logger.info(`Time: ${now.toLocaleString(DateTime.DATETIME_FULL)} ${now.zoneName}`);
logger.info(`Launched in: ${now.diff(currentState.launchedAt).toMillis()} ms`);
logger.info(`Environment: ${currentState.environment}`);
logger.info(`Process PID: ${process.pid}`);
2025-05-02 21:43:51 +00:00
const database = await tryConnectToDatabase();
2025-02-03 13:12:36 +00:00
// Lógica de inicialización de DB, si procede:
// initStructure(sequelizeConn.connection);
// insertUsers();
2025-05-02 21:43:51 +00:00
//await initModules({ app, database, baseRoutePath: "/api/v1" });
2025-02-03 13:12:36 +00:00
server.listen(currentState.port, () => {
2025-05-02 21:43:51 +00:00
server.emit("listening");
const networkInterfaces = os.networkInterfaces();
const addresses: string[] = [];
// Obtiene todas las direcciones IPv4
for (const interfaceName in networkInterfaces) {
const networkInterface = networkInterfaces[interfaceName];
if (networkInterface) {
for (const iface of networkInterface) {
if (iface.family === "IPv4" && !iface.internal) {
addresses.push(iface.address);
}
}
}
}
addresses.forEach((address) => {
logger.info(`⚡️ Server accessible at: http://${address}:${currentState.port}`);
});
logger.info(`Server started at: ${DateTime.now().toLocaleString(DateTime.DATETIME_FULL)}`);
logger.info(`Server PID: ${process.pid}`);
logger.info(
`Server launched in: ${DateTime.now().diff(currentState.launchedAt).toMillis()} ms`
);
logger.info(`Server path: ${currentState.appPath}`);
logger.info(`Server environment: ${currentState.environment}`);
2025-02-03 13:12:36 +00:00
logger.info("To shut down your server, press <CTRL> + C at any time");
});
} catch (error) {
serverError(error as NodeJS.ErrnoException);
}
2025-01-29 16:01:17 +00:00
})();