diff --git a/apps/web-next/package.json b/apps/web-next/package.json index 5a005c7..002d542 100644 --- a/apps/web-next/package.json +++ b/apps/web-next/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "build": "echo 'Next.js build requires full environment — use pnpm dev or docker'", - "dev": "next dev --port 3000", + "dev": "tsx server.ts", + "start": "node --import tsx server.ts", "lint": "eslint .", "test": "vitest run --passWithNoTests", "test:e2e": "playwright test", @@ -18,6 +19,7 @@ "@repo/core-api": "workspace:*", "@repo/core-cms": "workspace:*", "@repo/core-events": "workspace:*", + "@repo/core-realtime": "workspace:*", "@repo/core-shared": "workspace:*", "@repo/core-trpc": "workspace:*", "@repo/core-ui": "workspace:*", @@ -33,6 +35,7 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "reflect-metadata": "^0.2.2", + "socket.io": "^4.7.0", "superjson": "^2.2.1" }, "devDependencies": { @@ -47,6 +50,7 @@ "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "jsdom": "^25.0.0", + "tsx": "^4.0.0", "vitest": "^3.0.0" } } diff --git a/apps/web-next/server.ts b/apps/web-next/server.ts new file mode 100644 index 0000000..d271648 --- /dev/null +++ b/apps/web-next/server.ts @@ -0,0 +1,62 @@ +// apps/web-next/server.ts +// SERVER-ONLY entry. Boots Next.js + Socket.IO on the same Node http server. +import "reflect-metadata"; +import { createServer } from "node:http"; +import next from "next"; +import { Server as IOServer } from "socket.io"; +import { + RealtimeHandlerRegistry, + SocketIORealtimeBroadcaster, + SocketIORealtimeServer, + type IRealtimeAuthenticator, +} from "@repo/core-realtime"; +import { SESSION_COOKIE } from "@repo/auth"; +import { authContainer } from "@repo/auth/di/container"; +import { AUTH_SYMBOLS } from "@repo/auth/di/symbols"; +import { bindAll } from "./src/server/bind-production.js"; + +const dev = process.env.NODE_ENV !== "production"; +const port = Number(process.env.PORT ?? 3000); + +const app = next({ dev }); +const handle = app.getRequestHandler(); + +await app.prepare(); + +const httpServer = createServer((req, res) => handle(req, res)); +const io = new IOServer(httpServer); + +const broadcaster = new SocketIORealtimeBroadcaster(io); +const registry = new RealtimeHandlerRegistry(); + +await bindAll({ realtime: broadcaster, realtimeRegistry: registry }); + +const authenticator: IRealtimeAuthenticator = { + authenticate: async ({ cookies }) => { + const sessionId = cookies[SESSION_COOKIE]; + if (!sessionId) return null; + const authService = authContainer.get<{ + validateSession: (id: string) => Promise<{ user: { id: string }; session: unknown } | null>; + }>(AUTH_SYMBOLS.IAuthenticationService); + const result = await authService.validateSession(sessionId); + return result + ? { + userId: result.user.id, + // Roles are not yet in the session shape; extend here when DB-backed roles ship. + roles: (result as unknown as { roles?: string[] }).roles ?? [], + } + : null; + }, +}; + +const realtimeServer = new SocketIORealtimeServer({ + httpServer, + io, + authenticator, + registry, +}); +await realtimeServer.start(); + +httpServer.listen(port, () => { + console.log(`> Ready on http://localhost:${port}`); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ed3c22..b512d6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,6 +157,9 @@ importers: '@repo/core-events': specifier: workspace:* version: link:../../packages/core-events + '@repo/core-realtime': + specifier: workspace:* + version: link:../../packages/core-realtime '@repo/core-shared': specifier: workspace:* version: link:../../packages/core-shared @@ -202,6 +205,9 @@ importers: reflect-metadata: specifier: ^0.2.2 version: 0.2.2 + socket.io: + specifier: ^4.7.0 + version: 4.8.3 superjson: specifier: ^2.2.1 version: 2.2.6 @@ -239,6 +245,9 @@ importers: jsdom: specifier: ^25.0.0 version: 25.0.1 + tsx: + specifier: ^4.0.0 + version: 4.21.0 vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0) @@ -14856,7 +14865,7 @@ snapshots: tsx@4.21.0: dependencies: esbuild: 0.27.7 - get-tsconfig: 4.8.1 + get-tsconfig: 4.13.7 optionalDependencies: fsevents: 2.3.3