cc-libs/tools/trap-sandbox/src/modules/trapos-cloud-gateway/index.ts

57 lines
1.9 KiB
TypeScript

import type { FastifyPluginAsync } from "fastify";
import type { WebSocket } from "@fastify/websocket";
import { createConnection } from "./connection.js";
import type { GatewayRegistry, GatewaySocket } from "./registry.js";
import type { OpencodeProbe } from "../../opencode.js";
import type { AccountId } from "../../auth.js";
let nextConnectionId = 1;
export type GatewayModuleOptions = {
registry: GatewayRegistry;
probe: OpencodeProbe;
resolveAccount: (secret: string | undefined) => AccountId | null;
};
// Adapter: the only place that touches the real `ws` socket. Bridges socket events
// into the (pure) connection state machine. @fastify/websocket must be registered
// on a parent scope before this plugin.
const gatewayModule: FastifyPluginAsync<GatewayModuleOptions> = (fastify, opts) => {
// Open liveness check for the HTTP service itself — no auth, no opencode probe.
fastify.get("/health", () => ({ ok: true }));
fastify.get("/gateway", { websocket: true }, (socket: WebSocket, req) => {
const connId = `gw-${nextConnectionId++}`;
const log = fastify.log.child({ connId });
const gatewaySocket: GatewaySocket = {
send: (data) => socket.send(data),
close: (code, reason) => socket.close(code, reason),
};
log.info({ remoteAddress: req.ip }, "gateway connection opened");
const connection = createConnection({
socket: gatewaySocket,
registry: opts.registry,
probe: opts.probe,
resolveAccount: opts.resolveAccount,
log,
});
socket.on("message", (raw) => {
void connection.onMessage(raw);
});
socket.on("close", (code, reason) => {
connection.onClose(code, reason.toString());
});
socket.on("error", (err) => {
log.warn({ err }, "gateway socket error");
connection.onClose();
});
});
return Promise.resolve();
};
export default gatewayModule;