Files
core2026/app/game-api/src/server.ts
T
Hide_D 1688ca0d23 feat: implement game API with TRPC and Redis transport
- Added GameApiConfig interface and configuration resolver from environment variables.
- Created GameApiContext for managing database and transport dependencies.
- Implemented InMemoryTurnDaemonTransport for testing purposes.
- Developed RedisTurnDaemonTransport for command and status handling via Redis streams.
- Defined TurnDaemonStreamKeys for namespacing Redis streams by profile.
- Established TRPC router with endpoints for health check, world state retrieval, and turn daemon commands (run, pause, resume, status).
- Integrated Fastify server with TRPC and Redis transport.
- Added unit tests for router functionality and stream key generation.
- Configured Vitest for testing environment.
2025-12-30 01:04:52 +00:00

79 lines
2.2 KiB
TypeScript

import fastify from 'fastify';
import cors from '@fastify/cors';
import { fastifyTRPCPlugin } from '@trpc/server/adapters/fastify';
import {
createPostgresConnector,
createRedisConnector,
resolvePostgresConfigFromEnv,
resolveRedisConfigFromEnv,
} from '@sammo-ts/infra';
import { resolveGameApiConfigFromEnv } from './config.js';
import { createGameApiContext } from './context.js';
import { buildTurnDaemonStreamKeys } from './daemon/streamKeys.js';
import { RedisTurnDaemonTransport } from './daemon/redisTransport.js';
import { appRouter } from './router.js';
export const createGameApiServer = async () => {
const config = resolveGameApiConfigFromEnv();
const postgres = createPostgresConnector(resolvePostgresConfigFromEnv());
const redis = createRedisConnector(resolveRedisConfigFromEnv());
await postgres.connect();
await redis.connect();
const turnDaemon = new RedisTurnDaemonTransport(redis.client, {
keys: buildTurnDaemonStreamKeys(config.profileName),
requestTimeoutMs: config.daemonRequestTimeoutMs,
});
const app = fastify({
logger: true,
});
await app.register(cors, {
origin: true,
credentials: true,
});
await app.register(fastifyTRPCPlugin, {
prefix: config.trpcPath,
trpcOptions: {
router: appRouter,
createContext: () =>
createGameApiContext({
db: postgres.prisma,
turnDaemon,
profile: {
id: config.profile,
scenario: config.scenario,
name: config.profileName,
},
}),
},
});
app.get('/healthz', async () => ({
ok: true,
profile: config.profileName,
}));
app.addHook('onClose', async () => {
await redis.disconnect();
await postgres.disconnect();
});
return {
app,
config,
};
};
export const runGameApiServer = async (): Promise<void> => {
const { app, config } = await createGameApiServer();
await app.listen({
host: config.host,
port: config.port,
});
};