feat: 계정 Web Push 전달 기반과 사건 판정을 추가한다

게임 상태 변경과 같은 트랜잭션에 내구성 outbox를 기록하고 Gateway가 계정 설정과 기기 구독으로 fan-out하도록 한다. 전역 기본값은 비활성으로 유지하며 migration과 회귀 테스트를 함께 추가한다.
This commit is contained in:
2026-08-23 13:16:07 +00:00
parent f858806295
commit 7bc3213b03
34 changed files with 1964 additions and 9 deletions
+18 -1
View File
@@ -33,6 +33,8 @@ import { RemoteUserIconStore } from './account/remoteUserIconStore.js';
import { gatewayFastifyRouterOptions } from './fastifyOptions.js';
import { RuntimeNavigationConfigStore } from './navigation/runtimeNavigationConfig.js';
import { registerRuntimeNavigationRoute } from './navigation/runtimeNavigationRoute.js';
import { WebPushCoordinator } from './webPush/coordinator.js';
import { registerWebPushInternalRoute } from './webPush/internalRoute.js';
export const createGatewayApiServer = async () => {
const config = resolveGatewayApiConfigFromEnv();
@@ -86,11 +88,21 @@ export const createGatewayApiServer = async () => {
config.navigationConfigFile,
config.defaultNavigationConfigFile
);
const app = fastify({
logger: true,
routerOptions: gatewayFastifyRouterOptions,
});
const webPush = new WebPushCoordinator(
postgres.prisma as GatewayPrismaClient,
{
enabled: config.webPushEnabled,
vapidSubject: config.webPushVapidSubject,
vapidPublicKey: config.webPushVapidPublicKey,
vapidPrivateKey: config.webPushVapidPrivateKey,
pollIntervalMs: config.webPushPollIntervalMs,
},
(error) => app.log.error({ err: error }, 'web push delivery failed')
);
await app.register(cors, {
origin: true,
@@ -110,6 +122,7 @@ export const createGatewayApiServer = async () => {
profiles,
secret: config.gameTokenSecret,
});
registerWebPushInternalRoute(app, { secret: config.gameTokenSecret, webPush });
registerRuntimeNavigationRoute(app, navigationConfig);
await app.register(fastifyTRPCPlugin, {
@@ -142,6 +155,7 @@ export const createGatewayApiServer = async () => {
requestHeaders: req.headers,
prisma: postgres.prisma as GatewayPrismaClient,
navigationConfig,
webPush,
}),
},
});
@@ -152,11 +166,14 @@ export const createGatewayApiServer = async () => {
}));
app.addHook('onClose', async () => {
await webPush.stop();
await orchestrator.stop();
await redis.disconnect();
await postgres.disconnect();
});
webPush.start();
return {
app,
config,