Files
lst/app/main.ts

305 lines
8.1 KiB
TypeScript

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
import { toNodeHandler } from "better-auth/node";
import cors from "cors";
import express from "express";
import { createServer } from "http";
import { createProxyMiddleware } from "http-proxy-middleware";
import morgan from "morgan";
import os from "os";
import { dirname, join } from "path";
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";
import { fileURLToPath } from "url";
import { schedulerManager } from "./src/internal/logistics/controller/schedulerManager.js";
import { setupMobileRoutes } from "./src/internal/mobile/route.js";
import { printers } from "./src/internal/ocp/printers/printers.js";
import { setupRoutes } from "./src/internal/routerHandler/routeHandler.js";
import { baseModules } from "./src/internal/system/controller/modules/baseModules.js";
import { baseSettings } from "./src/internal/system/controller/settings/baseSettings.js";
import {
addListeners,
manualFixes,
settingsMigrate,
} from "./src/internal/system/utlis/addListeners.js";
import { swaggerOptions } from "./src/pkg/apiDocs/swaggerOptions.js";
import { auth } from "./src/pkg/auth/auth.js";
import { db } from "./src/pkg/db/db.js";
import { settings } from "./src/pkg/db/schema/settings.js";
import { createLogger } from "./src/pkg/logger/logger.js";
import { v1Listener } from "./src/pkg/logger/v1Listener.js";
import { apiHitMiddleware } from "./src/pkg/middleware/apiHits.js";
import { initializeProdPool, pool } from "./src/pkg/prodSql/prodSqlConnect.js";
import { validateEnv } from "./src/pkg/utils/envValidator.js";
import { sendNotify } from "./src/pkg/utils/notify.js";
import { returnFunc } from "./src/pkg/utils/return.js";
import { tryCatch } from "./src/pkg/utils/tryCatch.js";
import { setupIoServer } from "./src/ws/server.js";
const main = async () => {
const env = validateEnv(process.env);
const PORT = Number(process.env.VITE_PORT) || 4200;
//create the logger
const log = createLogger({ module: "system", subModule: "main start" });
// base path
let basePath: string = "";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Db connection stuff
const res = await tryCatch(db.select().from(settings));
if (res.error) {
return returnFunc({
success: false,
module: "system",
level: "fatal",
message: `Database lookup failed`,
notify: false,
data: [],
});
}
if (res.data.length === 0) {
//return
// returnFunc({
// success: false,
// module: "system",
// level: "fatal",
// message: `This seems to be the first time you have started the app please validate the settings have been intiated`,
// notify: false,
// data: [],
// });
}
// connect to the prod sql
console.log("Connecting to the sql server");
await initializeProdPool();
// express app
const app = express();
// global env that run only in dev
if (process.env.NODE_ENV?.trim() !== "production") {
app.use(morgan("tiny"));
basePath = "/lst";
app.use(
basePath + "/test",
express.static(join(__dirname, "../controller")),
);
}
// old app prox temp stuff
app.use(
basePath + "/old",
createProxyMiddleware({
target: `http://localhost:${process.env.V1PORT || "3000"}`, // change this to pull from the correct port
changeOrigin: true,
pathRewrite: (path, req) => {
// Remove the basePath + '/old' prefix from the path dynamically
return path.replace(`${basePath}/old`, "");
},
headers: {
// forward auth headers if needed
"X-Forwarded-By": "express-proxy",
},
}),
);
// global middleware
app.set("trust proxy", true);
app.use(apiHitMiddleware);
app.all(basePath + "/api/auth/*splat", toNodeHandler(auth)); // sign-in sign-out
app.use(express.json());
const allowedOrigins = [
/^https?:\/\/localhost:(5173|5500|4200|3000|4000)$/, // all the allowed backend ports
/^http?:\/\/localhost:(5173|5500|4200|3000|4000)$/,
/^https?:\/\/.*\.alpla\.net$/,
"http://localhost:4173",
"http://localhost:4200",
"http://localhost:3000",
"http://localhost:3001",
"http://localhost:4000",
"http://localhost:4001",
"http://localhost:5500",
env.BETTER_AUTH_URL, // prod
];
app.use(
cors({
origin: (origin, callback) => {
//console.log("CORS request from origin:", origin);
if (!origin) return callback(null, true); // allow same-site or direct calls
try {
const hostname = new URL(origin).hostname; // strips protocol/port
//console.log("Parsed hostname:", hostname);
if (allowedOrigins.includes(origin)) {
return callback(null, true);
}
// Now this works for *.alpla.net
if (hostname.endsWith(".alpla.net") || hostname === "alpla.net") {
return callback(null, true);
}
} catch (err) {
//console.error("Invalid Origin header:", origin);
}
return callback(new Error("Not allowed by CORS: " + origin));
},
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
credentials: true,
exposedHeaders: [
"set-cookie",
"expo-protocol-version",
"expo-sfv-version",
],
allowedHeaders: [
"Content-Type",
"Authorization",
"X-Requested-With",
"XMLHttpRequest",
"expo-runtime-version",
"expo-platform",
"expo-channel-name",
"*",
],
}),
);
// docs and routes
const openapiSpec: any = swaggerJsdoc(swaggerOptions);
app.use(
basePath + "/api/docs",
swaggerUi.serve,
swaggerUi.setup(openapiSpec),
);
app.use(basePath + "/d", express.static(join(__dirname, "../lstDocs/build")));
app.use(
basePath + "/app",
express.static(join(__dirname, "../frontend/dist")),
);
app.get(basePath + "/app/*splat", (req, res) => {
res.sendFile(join(__dirname, "../frontend/dist/index.html"));
});
app.get(basePath + "/d/*splat", (req, res) => {
res.sendFile(join(__dirname, "../lstDocs/build/index.html"));
});
// server setup
const server = createServer(app);
// register app
setupRoutes(app, basePath);
// ws stuff
setupIoServer(server, basePath);
// start all systems after we are intiallally up and running
setTimeout(() => {
baseSettings();
baseModules();
printers();
schedulerManager();
// start up the v1listener
v1Listener();
addListeners();
//userMigrate();
// some temp fixes
// above 230 remove these
manualFixes();
settingsMigrate();
}, 5 * 1000);
// setTimeout(() => {
// startHonoServer();
// }, 8 * 1000);
// start the server up
server.listen(PORT, "0.0.0.0", () =>
log.info(
{ stack: { name: "test" } },
`Server running in ${
process.env.NODE_ENV ? process.env.NODE_ENV : "dev"
}, on http://0.0.0.0:${PORT}${basePath}`,
),
);
process.on("uncaughtException", async (err) => {
//console.log("Uncaught Exception:", err);
// await closePool();
// const emailData = {
// email: "blake.matthes@alpla.com", // should be moved to the db so it can be reused.
// subject: `${os.hostname()} has just encountered a crash.`,
// template: "serverCrash",
// context: {
// error: err,
// plant: `${os.hostname()}`,
// },
// };
if (!process.env.WEBHOOK_URL) {
// await sendEmail(emailData);
} else {
log.fatal({ stack: err.stack }, err.message);
await sendNotify({
module: "system",
subModule: "fatalCrash",
hostname: os.hostname(),
message: err.message,
stack: err?.stack,
});
}
//process.exit(1);
});
process.on("SIGINT", async () => {
console.log("\nGracefully shutting down...");
try {
await pool.close();
console.log("Closed SQL connection.");
} catch (err) {
console.error("Error closing SQL connection:", err);
} finally {
process.exit(0);
}
});
// Also handle other termination signals (optional)
process.on("SIGTERM", async () => {
console.log("SIGTERM received. Closing SQL connection...");
try {
await pool.close();
} catch (err) {
console.error(err);
} finally {
process.exit(0);
}
});
// setInterval(() => {
// const used = process.memoryUsage();
// console.log(
// `Heap: ${(used.heapUsed / 1024 / 1024).toFixed(2)} MB / RSS: ${(
// used.rss / 1024 / 1024
// ).toFixed(2)} MB`,
// );
// }, 10000);
};
main();