Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions live/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
{
"root": true,
"extends": ["@plane/eslint-config/server.js"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": true
}
"parser": "@typescript-eslint/parser"
}
18 changes: 10 additions & 8 deletions live/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@
"type": "module",
"scripts": {
"dev": "tsup --watch --onSuccess 'node --env-file=.env dist/server.js'",
"build": "tsup",
"start": "node dist/server.js",
"lint": "eslint src --ext .ts,.tsx",
"lint:errors": "eslint src --ext .ts,.tsx --quiet"
"build": "tsc --noEmit && tsup",
"start": "node --env-file=.env dist/server.js",
"check:lint": "eslint . --max-warnings 0",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
"fix:format": "prettier --write \"**/*.{ts,tsx,md,json,css,scss}\"",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
},
"keywords": [],
"author": "",
Expand All @@ -20,10 +24,7 @@
"@hocuspocus/extension-logger": "^2.15.0",
"@hocuspocus/extension-redis": "^2.15.0",
"@hocuspocus/server": "^2.15.0",
"@plane/constants": "*",
"@plane/decorators": "*",
"@plane/editor": "*",
"@plane/logger": "*",
"@plane/types": "*",
"@tiptap/core": "2.10.4",
"@tiptap/html": "2.11.0",
Expand All @@ -45,14 +46,15 @@
"yjs": "^13.6.20"
},
"devDependencies": {
"@plane/eslint-config": "*",
"@plane/typescript-config": "*",
"@types/compression": "^1.7.5",
"@types/cors": "^2.8.17",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.21",
"@types/express-ws": "^3.0.4",
"@types/node": "^20.14.9",
"@types/pino-http": "^5.8.4",
"@plane/typescript-config": "*",
"concurrently": "^9.0.1",
"nodemon": "^3.1.7",
"ts-node": "^10.9.2",
Expand Down
1 change: 0 additions & 1 deletion live/src/ee/lib/authentication.ts

This file was deleted.

2 changes: 1 addition & 1 deletion live/src/ee/lib/update-document.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "../../ce/lib/update-document.js"
export * from "../../ce/lib/update-document.js";
182 changes: 88 additions & 94 deletions live/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,135 +1,129 @@
import compression from "compression";
import cors from "cors";
import expressWs from "express-ws";
import express from "express";
import express, { Request, Response } from "express";
import helmet from "helmet";
// hocuspocus server
import { getHocusPocusServer } from "@/core/hocuspocus-server.js";
// helpers
import { convertHTMLDocumentToAllFormats } from "@/core/helpers/convert-document.js";
import { logger, manualLogger } from "@/core/helpers/logger.js";
import { errorHandler } from "@/core/helpers/error-handler.js";
// types
import { TConvertDocumentRequestBody } from "@/core/types/common.js";

const app: any = express();
expressWs(app);

app.set("port", process.env.PORT || 3000);

// Security middleware
app.use(helmet());

// Middleware for response compression
app.use(
compression({
level: 6,
threshold: 5 * 1000,
})
);

// Logging middleware
app.use(logger);

// Body parsing middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// cors middleware
app.use(cors());

const router = express.Router();

const HocusPocusServer = await getHocusPocusServer().catch((err) => {
manualLogger.error("Failed to initialize HocusPocusServer:", err);
process.exit(1);
});

router.get("/health", (_req, res) => {
res.status(200).json({ status: "OK" });
});
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();
}

router.ws("/collaboration", (ws, req) => {
try {
HocusPocusServer.handleConnection(ws, req);
} catch (err) {
manualLogger.error("WebSocket connection error:", err);
ws.close();
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);
}
});

router.post("/convert-document", (req, res) => {
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).send({
message: `Internal server error. ${error}`,
private async setupHocusPocus() {
this.hocuspocusServer = await getHocusPocusServer().catch((err) => {
manualLogger.error("Failed to initialize HocusPocusServer:", err);
process.exit(1);
});
}
});

app.use(process.env.LIVE_BASE_PATH || "/live", router);
private setupRoutes() {
this.router.get("/health", (_req: Request, res: Response) => {
res.status(200).json({ status: "OK" });
});

app.use((_req, res) => {
res.status(404).send("Not Found");
});
this.router.ws("/collaboration", (ws: any, req: Request) => {
try {
this.hocuspocusServer.handleConnection(ws, req);
} catch (err) {
manualLogger.error("WebSocket connection error:", err);
ws.close();
}
});

app.use(errorHandler);
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).send({
message: `Internal server error. ${error}`,
});
}
});

const liveServer = app.listen(app.get("port"), () => {
manualLogger.info(`Plane Live server has started at port ${app.get("port")}`);
});
this.app.use((_req: Request, res: Response) => {
res.status(404).send("Not Found");
});
}

const gracefulShutdown = async () => {
manualLogger.info("Starting graceful shutdown...");
public listen() {
this.serverInstance = this.app.listen(this.app.get("port"), () => {
manualLogger.info(`Plane Live server has started at port ${this.app.get("port")}`);
});
}

try {
public async destroy() {
// Close the HocusPocus server WebSocket connections
await HocusPocusServer.destroy();
await this.hocuspocusServer.destroy();
manualLogger.info("HocusPocus server WebSocket connections closed gracefully.");

// Close the Express server
liveServer.close(() => {
this.serverInstance.close(() => {
manualLogger.info("Express server closed gracefully.");
process.exit(1);
});
} catch (err) {
manualLogger.error("Error during shutdown:", err);
process.exit(1);
}
}

// Forcefully shut down after 10 seconds if not closed
setTimeout(() => {
manualLogger.error("Forcing shutdown...");
process.exit(1);
}, 10000);
};
const server = new Server();
server.listen();

// Graceful shutdown on unhandled rejection
process.on("unhandledRejection", (err: any) => {
process.on("unhandledRejection", async (err: any) => {
manualLogger.info("Unhandled Rejection: ", err);
manualLogger.info(`UNHANDLED REJECTION! 💥 Shutting down...`);
gracefulShutdown();
await server.destroy();
});

// Graceful shutdown on uncaught exception
process.on("uncaughtException", (err: any) => {
process.on("uncaughtException", async (err: any) => {
manualLogger.info("Uncaught Exception: ", err);
manualLogger.info(`UNCAUGHT EXCEPTION! 💥 Shutting down...`);
gracefulShutdown();
await server.destroy();
});
7 changes: 5 additions & 2 deletions live/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"extends": "@plane/typescript-config/base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"module": "ES2015",
"moduleResolution": "Bundler",
"lib": ["ES2015"],
"target": "ES2015",
"outDir": "./dist",
"rootDir": ".",
"baseUrl": ".",
Expand All @@ -16,6 +17,8 @@
"skipLibCheck": true,
"sourceMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"sourceRoot": "/"
},
"include": ["src/**/*.ts", "tsup.config.ts"],
Expand Down
25 changes: 10 additions & 15 deletions live/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { defineConfig, Options } from "tsup";
import { defineConfig } from "tsup";

export default defineConfig((options: Options) => ({
export default defineConfig({
entry: ["src/server.ts"],
format: ["esm"],
dts: false,
clean: true,
target: "node18",
sourcemap: true,
format: ["esm", "cjs"],
dts: true,
splitting: false,
bundle: true,
sourcemap: true,
minify: false,
target: "node18",
outDir: "dist",
esbuildOptions(options) {
options.alias = {
"@/core": "./src/core",
"@/plane-live": "./src/ce"
};
env: {
NODE_ENV: process.env.NODE_ENV || "development",
},
...options,
}));
});
5 changes: 5 additions & 0 deletions packages/constants/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
root: true,
extends: ["@plane/eslint-config/library.js"],
parser: "@typescript-eslint/parser",
};
35 changes: 33 additions & 2 deletions packages/constants/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@
"name": "@plane/constants",
"version": "0.27.0",
"private": true,
"main": "./src/index.ts",
"license": "AGPL-3.0"
"license": "AGPL-3.0",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist/**/*"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.js"
}
},
"scripts": {
"dev": "tsup --watch",
"build": "tsc --noEmit && tsup",
"check:lint": "eslint . --max-warnings 0",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
"fix:format": "prettier --write \"**/*.{ts,tsx,md,json,css,scss}\"",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
},
"dependencies": {
"@plane/types": "*"
},
"devDependencies": {
"@plane/eslint-config": "*",
"@plane/typescript-config": "*",
"tsup": "8.4.0"
}
}
Loading