浏览代码

Seperate servers

Owen Schwartz 7 月之前
父节点
当前提交
37f51bec9b

+ 56 - 0
server/apiServer.ts

@@ -0,0 +1,56 @@
+import express, { Request, Response } from "express";
+import cors from "cors";
+import cookieParser from "cookie-parser";
+import config from "@server/config";
+import logger from "@server/logger";
+import { errorHandlerMiddleware, notFoundMiddleware, rateLimitMiddleware } from "@server/middlewares";
+import { authenticated, unauthenticated } from "@server/routers/external";
+import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws";
+import { logIncomingMiddleware } from "./middlewares/logIncoming";
+
+const dev = process.env.ENVIRONMENT !== "prod";
+const externalPort = config.server.external_port;
+
+export function createApiServer() {
+  const apiServer = express();
+  
+  // Middleware setup
+  apiServer.set("trust proxy", 1);
+  apiServer.use(cors());
+  apiServer.use(cookieParser());
+  apiServer.use(express.json());
+  
+  if (!dev) {
+    apiServer.use(
+      rateLimitMiddleware({
+        windowMin: config.rate_limit.window_minutes,
+        max: config.rate_limit.max_requests,
+        type: "IP_ONLY",
+      })
+    );
+  }
+
+  // API routes
+  const prefix = `/api/v1`;
+  apiServer.use(logIncomingMiddleware);
+  apiServer.use(prefix, unauthenticated);
+  apiServer.use(prefix, authenticated);
+  
+  // WebSocket routes
+  apiServer.use(`/ws`, wsRouter);
+  
+  // Error handling
+  apiServer.use(notFoundMiddleware);
+  apiServer.use(errorHandlerMiddleware);
+
+  // Create HTTP server
+  const httpServer = apiServer.listen(externalPort, (err?: any) => {
+    if (err) throw err;
+    logger.info(`API server is running on http://localhost:${externalPort}`);
+  });
+
+  // Handle WebSocket upgrades
+  handleWSUpgrade(httpServer);
+
+  return httpServer;
+}

+ 1 - 0
server/config.ts

@@ -21,6 +21,7 @@ const environmentSchema = z.object({
     server: z.object({
         external_port: portSchema,
         internal_port: portSchema,
+        next_port: portSchema,
         internal_hostname: z.string(),
         secure_cookies: z.boolean(),
         signup_secret: z.string().optional(),

+ 30 - 102
server/index.ts

@@ -1,107 +1,35 @@
-import config from "@server/config";
-import express, { Request, Response } from "express";
-import next from "next";
-import { parse } from "url";
-import logger from "@server/logger";
-import helmet from "helmet";
-import cors from "cors";
-import {
-    errorHandlerMiddleware,
-    notFoundMiddleware,
-    rateLimitMiddleware,
-} from "@server/middlewares";
-import internal from "@server/routers/internal";
-import { authenticated, unauthenticated } from "@server/routers/external";
-import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws";
-import cookieParser from "cookie-parser";
-import { User, UserOrg } from "@server/db/schema";
 import { ensureActions } from "./db/ensureActions";
-import { logIncomingMiddleware } from "./middlewares/logIncoming";
-
-const dev = process.env.ENVIRONMENT !== "prod";
-
-const app = next({ dev });
-const handle = app.getRequestHandler();
-
-const externalPort = config.server.external_port;
-const internalPort = config.server.internal_port;
-
-app.prepare().then(() => {
-    ensureActions(); // This loads the actions into the database
-
-    // External server
-    const externalServer = express();
-    externalServer.set("trust proxy", 1);
-
-    // externalServer.use(helmet()); // Disabled because causes issues with Next.js
-    externalServer.use(cors());
-    externalServer.use(cookieParser());
-    externalServer.use(express.json());
-    if (!dev) {
-        externalServer.use(
-            rateLimitMiddleware({
-                windowMin: config.rate_limit.window_minutes,
-                max: config.rate_limit.max_requests,
-                type: "IP_ONLY",
-            })
-        );
-    }
-
-    const prefix = `/api/v1`;
-    externalServer.use(logIncomingMiddleware);
-    externalServer.use(prefix, unauthenticated);
-    externalServer.use(prefix, authenticated);
-    // externalServer.use(`${prefix}/ws`, wsRouter);
-
-    externalServer.use(notFoundMiddleware);
-
-    // We are using NEXT from here on
-    externalServer.all("*", (req: Request, res: Response) => {
-        const parsedUrl = parse(req.url!, true);
-        handle(req, res, parsedUrl);
-    });
-
-    const httpServer = externalServer.listen(externalPort, (err?: any) => {
-        if (err) throw err;
-        logger.info(
-            `Main server is running on http://localhost:${externalPort}`
-        );
-    });
-
-    // handleWSUpgrade(httpServer);
-
-    externalServer.use(errorHandlerMiddleware);
-
-    // Internal server
-    const internalServer = express();
-
-    internalServer.use(helmet());
-    internalServer.use(cors());
-    internalServer.use(cookieParser());
-    internalServer.use(express.json());
-
-    internalServer.use(prefix, internal);
-
-    internalServer.listen(internalPort, (err?: any) => {
-        if (err) throw err;
-        logger.info(
-            `Internal server is running on http://localhost:${internalPort}`
-        );
-    });
-
-    internalServer.use(notFoundMiddleware);
-    internalServer.use(errorHandlerMiddleware);
-});
+import { createApiServer } from "./apiServer";
+import { createNextServer } from "./nextServer";
+import { createInternalServer } from "./internalServer";
+import { User, UserOrg } from "./db/schema";
+
+async function startServers() {
+  await ensureActions();
+  
+  // Start all servers
+  const apiServer = createApiServer();
+  const nextServer = await createNextServer();
+  const internalServer = createInternalServer();
+
+  return {
+    apiServer,
+    nextServer,
+    internalServer
+  };
+}
 
+// Types
 declare global {
-    // TODO: eventually make seperate types that extend express.Request
-    namespace Express {
-        interface Request {
-            user?: User;
-            userOrg?: UserOrg;
-            userOrgRoleId?: number;
-            userOrgId?: string;
-            userOrgIds?: string[];
-        }
+  namespace Express {
+    interface Request {
+      user?: User;
+      userOrg?: UserOrg;
+      userOrgRoleId?: number;
+      userOrgId?: string;
+      userOrgIds?: string[];
     }
+  }
 }
+
+startServers().catch(console.error);

+ 32 - 0
server/internalServer.ts

@@ -0,0 +1,32 @@
+import express from "express";
+import helmet from "helmet";
+import cors from "cors";
+import cookieParser from "cookie-parser";
+import config from "@server/config";
+import logger from "@server/logger";
+import { errorHandlerMiddleware, notFoundMiddleware } from "@server/middlewares";
+import internal from "@server/routers/internal";
+
+const internalPort = config.server.internal_port;
+
+export function createInternalServer() {
+  const internalServer = express();
+
+  internalServer.use(helmet());
+  internalServer.use(cors());
+  internalServer.use(cookieParser());
+  internalServer.use(express.json());
+
+  const prefix = `/api/v1`;
+  internalServer.use(prefix, internal);
+
+  internalServer.use(notFoundMiddleware);
+  internalServer.use(errorHandlerMiddleware);
+
+  internalServer.listen(internalPort, (err?: any) => {
+    if (err) throw err;
+    logger.info(`Internal server is running on http://localhost:${internalPort}`);
+  });
+
+  return internalServer;
+}

+ 29 - 0
server/nextServer.ts

@@ -0,0 +1,29 @@
+import next from "next";
+import express from "express";
+import { parse } from "url";
+import logger from "@server/logger";
+import config from "@server/config";
+
+const nextPort = config.server.next_port;
+
+export async function createNextServer() {
+//   const app = next({ dev });
+  const app = next({ dev: process.env.ENVIRONMENT !== "prod" });
+  const handle = app.getRequestHandler();
+
+  await app.prepare();
+
+  const nextServer = express();
+
+  nextServer.all("*", (req, res) => {
+    const parsedUrl = parse(req.url!, true);
+    return handle(req, res, parsedUrl);
+  });
+
+  nextServer.listen(nextPort, (err?: any) => {
+    if (err) throw err;
+    logger.info(`Next.js server is running on http://localhost:${nextPort}`);
+  });
+
+  return nextServer;
+}

+ 57 - 72
server/routers/ws.ts

@@ -17,7 +17,6 @@ interface WebSocketRequest extends IncomingMessage {
 
 interface AuthenticatedWebSocket extends WebSocket {
     newt?: Newt;
-    isAlive?: boolean;
 }
 
 interface TokenPayload {
@@ -124,77 +123,23 @@ const verifyToken = async (token: string): Promise<TokenPayload | null> => {
 
         return { newt: existingNewt[0], session };
     } catch (error) {
-        console.error("Token verification failed:", error);
+        logger.error("Token verification failed:", error);
         return null;
     }
 };
 
-// Router endpoint (unchanged)
-router.get("/ws", (req: Request, res: Response) => {
-    res.status(200).send("WebSocket endpoint");
-});
+const setupConnection = (ws: AuthenticatedWebSocket, newt: Newt): void => {
+    logger.info("Establishing websocket connection");
 
-// WebSocket upgrade handler
-const handleWSUpgrade = (server: HttpServer): void => {
-    server.on("upgrade", async (request: WebSocketRequest, socket: Socket, head: Buffer) => {
-        try {
-            const token = request.url?.includes("?")
-                ? new URLSearchParams(request.url.split("?")[1]).get("token") || ""
-                : request.headers["sec-websocket-protocol"];
-
-            if (!token) {
-                socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
-                socket.destroy();
-                return;
-            }
-
-            const tokenPayload = await verifyToken(token);
-            if (!tokenPayload) {
-                socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
-                socket.destroy();
-                return;
-            }
-
-            request.token = token;
-
-            wss.handleUpgrade(request, socket, head, (ws: AuthenticatedWebSocket) => {
-                ws.newt = tokenPayload.newt;
-                ws.isAlive = true;
-                wss.emit("connection", ws, request);
-            });
-        } catch (error) {
-            console.error("Upgrade error:", error);
-            socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
-            socket.destroy();
-        }
-    });
-};
-
-// WebSocket connection handler
-wss.on("connection", (ws: AuthenticatedWebSocket, request: WebSocketRequest) => {
-    const newtId = ws.newt?.newtId;
-    if (!newtId) {
-        console.error("Connection attempt without newt ID");
+    if (!newt) {
+        logger.error("Connection attempt without newt");
         return ws.terminate();
     }
 
-    // Add client to tracking
-    addClient(newtId, ws);
-
-    // Set up ping-pong for connection health check
-    const pingInterval = setInterval(() => {
-        if (ws.isAlive === false) {
-            clearInterval(pingInterval);
-            removeClient(newtId, ws);
-            return ws.terminate();
-        }
-        ws.isAlive = false;
-        ws.ping();
-    }, 30000);
+    ws.newt = newt;
 
-    ws.on("pong", () => {
-        ws.isAlive = true;
-    });
+    // Add client to tracking
+    addClient(newt.newtId, ws);
 
     ws.on("message", async (data) => {
         try {
@@ -226,7 +171,7 @@ wss.on("connection", (ws: AuthenticatedWebSocket, request: WebSocketRequest) =>
             if (response) {
                 if (response.broadcast) {
                     // Broadcast to all clients except sender if specified
-                    broadcastToAllExcept(response.message, response.excludeSender ? newtId : undefined);
+                    broadcastToAllExcept(response.message, response.excludeSender ? newt.newtId : undefined);
                 } else if (response.targetNewtId) {
                     // Send to specific client if targetNewtId is provided
                     sendToClient(response.targetNewtId, response.message);
@@ -235,9 +180,9 @@ wss.on("connection", (ws: AuthenticatedWebSocket, request: WebSocketRequest) =>
                     ws.send(JSON.stringify(response.message));
                 }
             }
-    
+
         } catch (error) {
-            console.error("Message handling error:", error);
+            logger.error("Message handling error:", error);
             ws.send(JSON.stringify({
                 type: "error",
                 data: {
@@ -247,18 +192,58 @@ wss.on("connection", (ws: AuthenticatedWebSocket, request: WebSocketRequest) =>
             }));
         }
     });    
-
+    
     ws.on("close", () => {
-        clearInterval(pingInterval);
-        removeClient(newtId, ws);
-        logger.info(`Client disconnected - Newt ID: ${newtId}`);
+        removeClient(newt.newtId, ws);
+        logger.info(`Client disconnected - Newt ID: ${newt.newtId}`);
     });
-
+    
     ws.on("error", (error: Error) => {
-        console.error(`WebSocket error for Newt ID ${newtId}:`, error);
+        logger.error(`WebSocket error for Newt ID ${newt.newtId}:`, error);
     });
+
+    logger.info(`WebSocket connection established - Newt ID: ${newt.newtId}`);
+};
+
+// Router endpoint (unchanged)
+router.get("/ws", (req: Request, res: Response) => {
+    res.status(200).send("WebSocket endpoint");
 });
 
+// WebSocket upgrade handler
+const handleWSUpgrade = (server: HttpServer): void => {
+    server.on("upgrade", async (request: WebSocketRequest, socket: Socket, head: Buffer) => {
+        try {
+            const token = request.url?.includes("?")
+                ? new URLSearchParams(request.url.split("?")[1]).get("token") || ""
+                : request.headers["sec-websocket-protocol"];
+
+            if (!token) {
+                logger.warn("Unauthorized connection attempt: no token...");
+                socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
+                socket.destroy();
+                return;
+            }
+
+            const tokenPayload = await verifyToken(token);
+            if (!tokenPayload) {
+                logger.warn("Unauthorized connection attempt: invalid token...");
+                socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
+                socket.destroy();
+                return;
+            }
+
+            wss.handleUpgrade(request, socket, head, (ws: AuthenticatedWebSocket) => {
+                setupConnection(ws, tokenPayload.newt);
+            });
+        } catch (error) {
+            logger.error("WebSocket upgrade error:", error);
+            socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
+            socket.destroy();
+        }
+    });
+};
+
 export {
     router,
     handleWSUpgrade,

+ 0 - 12
src/app/[orgId]/settings/sites/[niceId]/components/NewtConfig.tsx

@@ -1,12 +0,0 @@
-"use client";
-
-export function NewtConfig() {
-    const config = `curl -fsSL https://get.docker.com -o get-docker.sh
-sh get-docker.sh`;
-
-    return (
-        <pre className="mt-2 w-full rounded-md bg-slate-950 p-4 overflow-x-auto">
-            <code className="text-white whitespace-pre-wrap">{config}</code>
-        </pre>
-    );
-}

+ 7 - 1
src/app/[orgId]/settings/sites/components/CreateSiteForm.tsx

@@ -174,7 +174,13 @@ Endpoint = ${siteDefaults.endpoint}:${siteDefaults.listenPort}
 PersistentKeepalive = 5`
             : "";
 
-    const newtConfig = `newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret}`;
+    // am I at http or https?
+    let proto = "http:";
+    if (typeof window !== "undefined") {
+        proto = window.location.protocol;
+    }
+
+    const newtConfig = `newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${proto}//${siteDefaults?.endpoint}`;
 
     return (
         <>