mancala/apps/backend/src/index.ts

77 lines
3.1 KiB
TypeScript
Raw Normal View History

2021-06-27 01:41:52 +03:00
2022-07-15 21:43:29 +03:00
import { RTMTWS } from "./rtmt/rtmt_websocket";
import { GameManager } from "./game/GameManager";
import { GameStoreImpl } from "./game/gamestore/GameStoreImpl";
import { MatchMakerImpl } from "./matchmaker/MatchMakerImpl";
import { ExpressApp } from "./server/ExpressApp";
import { WebServer } from "./server/WebServer";
import { KeycloakTokenVerifier, readAuthConfig } from "./auth";
import { createPool, runMigrations } from "./db/postgres";
import { PostgresGameHistoryRepository } from "./history/GameHistoryRepository";
import { createValkeyClient } from "./live/valkey";
import { ValkeyLiveGameStore } from "./live/LiveGameStore";
import { GameRecorder } from "./game/GameRecorder";
2021-06-27 19:28:09 +03:00
const DATABASE_URL = process.env.DATABASE_URL || "postgres://mancala:mancala@localhost:5433/mancala";
const VALKEY_URL = process.env.VALKEY_URL || "redis://localhost:6379";
const MIGRATION_RETRY_MS = 30_000;
const VALKEY_STARTUP_WAIT_MS = 5_000;
async function main() {
const authConfig = readAuthConfig();
const tokenVerifier = new KeycloakTokenVerifier({
issuer: authConfig.keycloakIssuer,
jwksUrl: authConfig.keycloakJwksUrl,
allowedClientIds: authConfig.keycloakClientIds,
});
const pool = createPool(DATABASE_URL);
migrateWhenReachable(pool);
const valkey = createValkeyClient(VALKEY_URL);
await waitForReady(valkey, VALKEY_STARTUP_WAIT_MS);
const liveGameStore = new ValkeyLiveGameStore(valkey);
const recorder = new GameRecorder(new PostgresGameHistoryRepository(pool), liveGameStore);
const rtmt = new RTMTWS({ tokenVerifier, legacyUserKeyAuth: authConfig.legacyUserKeyAuth });
const gameStore = new GameStoreImpl();
const matchMaker = new MatchMakerImpl({ rtmt });
const gameManager = new GameManager({ rtmt, gameStore, matchMaker, liveGameStore, recorder });
2021-06-27 19:28:09 +03:00
// Before accepting connections, so reconnecting players find their games.
await gameManager.restoreLiveGames();
recorder.start();
gameManager.startTimers();
2022-05-06 00:07:41 +03:00
const expressApp = new ExpressApp({ gameStore, legacyUserKeyAuth: authConfig.legacyUserKeyAuth });
const server = new WebServer({ expressApp: expressApp.app });
rtmt.initWebSocket(server.server);
}
// The game runs without the database; saved games wait in the recorder until migrations have run.
function migrateWhenReachable(pool: ReturnType<typeof createPool>) {
runMigrations(pool).catch((err) => {
console.error(`(DB) Migrations failed, retrying in ${MIGRATION_RETRY_MS / 1000}s:`, err.message);
setTimeout(() => migrateWhenReachable(pool), MIGRATION_RETRY_MS);
});
}
function waitForReady(client: ReturnType<typeof createValkeyClient>, timeoutMs: number): Promise<void> {
if (client.status === "ready") return Promise.resolve();
return new Promise((resolve) => {
const timer = setTimeout(() => {
console.error("(Valkey) Not reachable yet; starting without restoring live games");
resolve();
}, timeoutMs);
client.once("ready", () => {
clearTimeout(timer);
resolve();
});
});
}
main().catch((err) => {
console.error(err);
process.exit(1);
});