/** * Custom Next.js server so we can attach a plain WebSocket server for WebRTC * signaling (offer/answer/ICE relay) alongside the normal Next.js request * handler. Standalone output mode can't be combined with a custom server * (see next.config.ts), so this project deploys by running this file * directly (`npm run start` / `npm run dev`) instead of `next start`. * * The WS server never touches audio/video/screen-share media — it only * relays small JSON signaling messages between participants of the same * meeting room (see lib/meeting/signalingServer.ts). */ import { createServer } from "node:http"; import next from "next"; import { WebSocketServer } from "ws"; import { attachSignalingServer } from "./lib/meeting/signalingServer"; const port = parseInt(process.env.PORT || "3000", 10); const hostname = process.env.HOSTNAME || "0.0.0.0"; const dev = process.env.NODE_ENV !== "production"; const app = next({ dev, hostname, port }); const SIGNALING_PATH = "/ws/signaling"; app.prepare().then(() => { // Must be requested after prepare() resolves. const handle = app.getRequestHandler(); const upgradeHandle = app.getUpgradeHandler(); const server = createServer((req, res) => { handle(req, res); }); const wss = new WebSocketServer({ noServer: true }); attachSignalingServer(wss); server.on("upgrade", (req, socket, head) => { const url = req.url ?? ""; if (url.startsWith(SIGNALING_PATH)) { wss.handleUpgrade(req, socket, head, (ws) => { wss.emit("connection", ws, req); }); } else { // Let Next.js handle its own upgrade requests (e.g. dev-mode HMR). upgradeHandle(req, socket, head); } }); server.listen(port, () => { console.log( `> ANOUMA server listening at http://${hostname}:${port} (${dev ? "development" : "production"}), signaling on ${SIGNALING_PATH}`, ); }); });