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:
@@ -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
62
apps/web-next/server.ts
Normal 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
11
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user