Files
bodyshop-desktop/src/main/http-server/http-server.ts
2025-08-28 12:51:09 -07:00

211 lines
6.2 KiB
TypeScript

import cors from "cors";
import { app } from "electron";
import log from "electron-log/main";
import express from "express";
import http from "http";
import errorTypeCheck from "../../util/errorTypeCheck";
import ImportJob from "../decoder/decoder";
import folderScan from "../decoder/folder-scan";
import { handleEMSPartsOrder } from "../ems-parts-order/ems-parts-order-handler";
import { handleShopMetaDataFetch } from "../ipc/ipcMainHandler.user";
import { handlePartsPriceChangeRequest } from "../ppc/ppc-handler";
import { handleQuickBookRequest } from "../quickbooks-desktop/quickbooks-desktop";
export default class LocalServer {
private readonly app: express.Application;
private server: http.Server | null;
private PORT = 1337;
constructor() {
this.server = null;
this.app = express();
this.configureMiddleware();
this.configureRoutes();
}
private configureMiddleware(): void {
const allowedOrigins = [
"http://localhost",
"https://localhost",
"http://localhost:3000",
"https://localhost:3000",
"https://test.imex.online",
"https://imex.online",
"https://test.romeonline.io",
"https://romeonline.io",
"https://www.test.imex.online",
"https://www.imex.online",
"https://www.test.romeonline.io",
"https://www.romeonline.io",
];
this.app.use(
cors({
origin: (origin, callback) => {
// Allow requests with no origin (like mobile apps, curl requests)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
return callback(null, true);
} else {
return callback(null, false);
}
},
credentials: true,
}),
);
// Parse JSON bodies
this.app.use(express.json());
this.app.use(express.urlencoded());
//Add logger Middleware
this.app.use((req, res, next) => {
const startTime = Date.now();
const requestId = Math.random().toString(36).substring(2, 15);
// Log request details
log.info(
`[HTTP Server] [${requestId}] Request: ${req.method} ${req.url}`,
);
log.info(
`[HTTP Server] [${requestId}] Headers: ${JSON.stringify(req.headers)}`,
);
// Log request body if it exists
if (req.body && Object.keys(req.body).length > 0) {
log.info(
`[HTTP Server] [${requestId}] Body: ${JSON.stringify(req.body)}`,
);
}
// Capture the original methods
const originalSend = res.send;
const originalJson = res.json;
// Override send method to log response
res.send = function (body): express.Response {
log.info(`[HTTP Server] [${requestId}] Response body: ${body}`);
log.info(
`[HTTP Server] [${requestId}] Response time: ${Date.now() - startTime}ms`,
);
return originalSend.call(this, body);
};
// Override json method to log response
res.json = function (body): express.Response {
log.info(
`[HTTP Server] [${requestId}] Response body: ${JSON.stringify(body)}`,
);
log.info(
`[HTTP Server] [${requestId}] Response time: ${Date.now() - startTime}ms`,
);
return originalJson.call(this, body);
};
next();
});
}
private configureRoutes(): void {
// Basic health check endpoint
this.app.get("/health", (_req: express.Request, res: express.Response) => {
res.status(200).json({ status: "ok" });
});
this.app.post("/ping", (_req, res) => {
res.status(200).json({
appVer: app.getVersion(),
qbPath: app.getPath("userData"), //TODO: Resolve to actual QB file path.
});
});
this.app.post("/qb", handleQuickBookRequest);
this.app.post("/scan", async (_req, res): Promise<void> => {
log.debug("[HTTP Server] Scan request received");
const files = await folderScan();
res.status(200).json(files);
return;
});
this.app.post("/ppc", handlePartsPriceChangeRequest);
this.app.post("/oec", handleEMSPartsOrder);
this.app.post(
"/import",
async (req: express.Request, res: express.Response) => {
log.debug("[HTTP Server] Import request received");
const { filepath } = req.body;
if (!filepath) {
res.status(400).json({ error: "filepath is required" });
return;
}
try {
await ImportJob(filepath);
res.status(200).json({ success: true });
} catch (error) {
log.error(
"[HTTP Server] Error importing file",
errorTypeCheck(error),
);
res.status(500).json({
success: false,
error: "Error importing file",
...errorTypeCheck(error),
});
}
},
);
this.app.post(
"/refresh",
async (_req: express.Request, res: express.Response) => {
log.debug("[HTTP Server] Refresh request received");
try {
await handleShopMetaDataFetch(true);
res.status(200).json({ success: true });
} catch (error) {
log.error(
"[HTTP Server] Error refreshing shop metadata",
errorTypeCheck(error),
);
res.status(500).json({
success: false,
error: "Error importing file",
...errorTypeCheck(error),
});
}
},
);
// Add more routes as needed
}
public start(): void {
try {
this.server = http.createServer(this.app);
this.server.on("error", (error: NodeJS.ErrnoException) => {
if (error.code === "EADDRINUSE") {
log.error(
`[HTTP Server] Port ${this.PORT} is already in use. Please use a different port.`,
);
} else {
log.error(`[HTTP Server] Server error: ${error.message}`);
}
});
this.server.listen(this.PORT, () => {
log.info(
`[HTTP Server] Local HTTP server running on port ${this.PORT}`,
);
});
} catch (error: unknown) {
log.error("[HTTP Server] Error starting server", errorTypeCheck(error));
}
}
public stop(): void {
if (this.server) {
this.server.close();
log.info("[HTTP Server] Local HTTP server stopped");
}
}
}