feat(web-next): custom Node server hosting Next.js + Socket.IO on port 3000

Replaces `next dev` / `next start` with a custom Node http server that
co-hosts Next.js and Socket.IO on the same port, wiring in the
RealtimeHandlerRegistry and session-cookie-based IRealtimeAuthenticator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-08 22:51:27 +02:00
parent 9b7878a623
commit 5659a0f61b
3 changed files with 77 additions and 2 deletions

View File

@@ -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"
}
}

62
apps/web-next/server.ts Normal file
View File

@@ -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}`);
});

11
pnpm-lock.yaml generated
View File

@@ -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