211 lines
6.2 KiB
TypeScript
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");
|
|
}
|
|
}
|
|
}
|