* fix: moved typescript parser to the base eslint config * fix: eslint warning * fix: type config setting * fix: convert live eslint to cjs
131 lines
4 KiB
TypeScript
131 lines
4 KiB
TypeScript
import compression from "compression";
|
|
import cors from "cors";
|
|
import express, { Request, Response } from "express";
|
|
import expressWs from "express-ws";
|
|
import helmet from "helmet";
|
|
// hocuspocus server
|
|
// helpers
|
|
import { convertHTMLDocumentToAllFormats } from "@/core/helpers/convert-document.js";
|
|
import { logger, manualLogger } from "@/core/helpers/logger.js";
|
|
import { getHocusPocusServer } from "@/core/hocuspocus-server.js";
|
|
// types
|
|
import { TConvertDocumentRequestBody } from "@/core/types/common.js";
|
|
|
|
export class Server {
|
|
private app: any;
|
|
private router: any;
|
|
private hocuspocusServer: any;
|
|
private serverInstance: any;
|
|
|
|
constructor() {
|
|
this.app = express();
|
|
this.router = express.Router();
|
|
expressWs(this.app);
|
|
this.app.set("port", process.env.PORT || 3000);
|
|
this.setupMiddleware();
|
|
this.setupHocusPocus();
|
|
this.setupRoutes();
|
|
}
|
|
|
|
private setupMiddleware() {
|
|
// Security middleware
|
|
this.app.use(helmet());
|
|
// Middleware for response compression
|
|
this.app.use(compression({ level: 6, threshold: 5 * 1000 }));
|
|
// Logging middleware
|
|
this.app.use(logger);
|
|
// Body parsing middleware
|
|
this.app.use(express.json());
|
|
this.app.use(express.urlencoded({ extended: true }));
|
|
// cors middleware
|
|
this.app.use(cors());
|
|
this.app.use(process.env.LIVE_BASE_PATH || "/live", this.router);
|
|
}
|
|
|
|
private async setupHocusPocus() {
|
|
this.hocuspocusServer = await getHocusPocusServer().catch((err) => {
|
|
manualLogger.error("Failed to initialize HocusPocusServer:", err);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
private setupRoutes() {
|
|
this.router.get("/health", (_req: Request, res: Response) => {
|
|
res.status(200).json({ status: "OK" });
|
|
});
|
|
|
|
this.router.ws("/collaboration", (ws: any, req: Request) => {
|
|
try {
|
|
this.hocuspocusServer.handleConnection(ws, req);
|
|
} catch (err) {
|
|
manualLogger.error("WebSocket connection error:", err);
|
|
ws.close();
|
|
}
|
|
});
|
|
|
|
this.router.post("/convert-document", (req: Request, res: Response) => {
|
|
const { description_html, variant } = req.body as TConvertDocumentRequestBody;
|
|
try {
|
|
if (description_html === undefined || variant === undefined) {
|
|
res.status(400).send({
|
|
message: "Missing required fields",
|
|
});
|
|
return;
|
|
}
|
|
const { description, description_binary } = convertHTMLDocumentToAllFormats({
|
|
document_html: description_html,
|
|
variant,
|
|
});
|
|
res.status(200).json({
|
|
description,
|
|
description_binary,
|
|
});
|
|
} catch (error) {
|
|
manualLogger.error("Error in /convert-document endpoint:", error);
|
|
res.status(500).json({
|
|
message: `Internal server error.`,
|
|
});
|
|
}
|
|
});
|
|
|
|
this.app.use((_req: Request, res: Response) => {
|
|
res.status(404).json({
|
|
message: "Not Found",
|
|
});
|
|
});
|
|
}
|
|
|
|
public listen() {
|
|
this.serverInstance = this.app.listen(this.app.get("port"), () => {
|
|
manualLogger.info(`Plane Live server has started at port ${this.app.get("port")}`);
|
|
});
|
|
}
|
|
|
|
public async destroy() {
|
|
// Close the HocusPocus server WebSocket connections
|
|
await this.hocuspocusServer.destroy();
|
|
manualLogger.info("HocusPocus server WebSocket connections closed gracefully.");
|
|
// Close the Express server
|
|
this.serverInstance.close(() => {
|
|
manualLogger.info("Express server closed gracefully.");
|
|
process.exit(1);
|
|
});
|
|
}
|
|
}
|
|
|
|
const server = new Server();
|
|
server.listen();
|
|
|
|
// Graceful shutdown on unhandled rejection
|
|
process.on("unhandledRejection", async (err: any) => {
|
|
manualLogger.info("Unhandled Rejection: ", err);
|
|
manualLogger.info(`UNHANDLED REJECTION! 💥 Shutting down...`);
|
|
await server.destroy();
|
|
});
|
|
|
|
// Graceful shutdown on uncaught exception
|
|
process.on("uncaughtException", async (err: any) => {
|
|
manualLogger.info("Uncaught Exception: ", err);
|
|
manualLogger.info(`UNCAUGHT EXCEPTION! 💥 Shutting down...`);
|
|
await server.destroy();
|
|
});
|