feat: add database schema support for gateway and game profiles

- Introduced `dbSchema` configuration option in GatewayApiConfig and GatewayOrchestratorConfig.
- Implemented schema resolution logic in environment configuration functions.
- Updated context and orchestrator factory to use GatewayPrismaClient instead of PrismaClient.
- Refactored orchestrator server and profile repository to accommodate new database schema handling.
- Created separate Prisma schemas for game and gateway in the infra package.
- Enhanced Postgres connector to support schema overrides in database URLs.
- Updated documentation to reflect changes in database schema handling.
- Added new Prisma generation and database push scripts for game and gateway schemas.
This commit is contained in:
2026-01-03 14:47:28 +00:00
parent 67d995c65f
commit a9609dcc19
33 changed files with 388 additions and 158 deletions
+35 -3
View File
@@ -11,6 +11,7 @@ type EnvMap = Record<string, string | undefined>;
export interface DatabaseUrlOptions {
envFile?: string;
env?: NodeJS.ProcessEnv;
schema?: string;
}
const parseEnvFile = (rawText: string): EnvMap => {
@@ -47,18 +48,42 @@ const loadEnvFile = async (envFile: string): Promise<EnvMap> => {
}
};
const applySchemaToDatabaseUrl = (
url: string,
schema: string | undefined
): string => {
if (!schema) {
return url;
}
try {
const parsed = new URL(url);
parsed.searchParams.set('schema', schema);
return parsed.toString();
} catch {
return url;
}
};
export const resolveDatabaseUrl = async (
options?: DatabaseUrlOptions
): Promise<string> => {
const env = options?.env ?? process.env;
if (env.DATABASE_URL) {
return env.DATABASE_URL;
const schema =
options?.schema ??
env.POSTGRES_SCHEMA ??
env.DATABASE_SCHEMA;
return applySchemaToDatabaseUrl(env.DATABASE_URL, schema);
}
const envFile = options?.envFile ?? DEFAULT_ENV_FILE;
const fileEnv = await loadEnvFile(envFile);
if (fileEnv.DATABASE_URL) {
return fileEnv.DATABASE_URL;
const schema =
options?.schema ??
fileEnv.POSTGRES_SCHEMA ??
fileEnv.DATABASE_SCHEMA;
return applySchemaToDatabaseUrl(fileEnv.DATABASE_URL, schema);
}
const host = env.POSTGRES_HOST ?? fileEnv.POSTGRES_HOST ?? '127.0.0.1';
@@ -66,5 +91,12 @@ export const resolveDatabaseUrl = async (
const user = env.POSTGRES_USER ?? fileEnv.POSTGRES_USER ?? 'sammo';
const password = env.POSTGRES_PASSWORD ?? fileEnv.POSTGRES_PASSWORD ?? '';
const dbName = env.POSTGRES_DB ?? fileEnv.POSTGRES_DB ?? 'sammo';
return `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
const schema =
options?.schema ??
env.POSTGRES_SCHEMA ??
fileEnv.POSTGRES_SCHEMA ??
env.DATABASE_SCHEMA ??
fileEnv.DATABASE_SCHEMA ??
'public';
return `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=${schema}`;
};
@@ -1,5 +1,5 @@
import {
createPostgresConnector,
createGamePostgresConnector,
type InputJsonValue,
type TurnEngineDatabaseClient,
type TurnEngineEventCreateManyInput,
@@ -117,7 +117,7 @@ export const seedScenarioToDatabase = async (
},
});
const connector = createPostgresConnector({ url: options.databaseUrl });
const connector = createGamePostgresConnector({ url: options.databaseUrl });
const now = options.now ?? new Date();
const tickSeconds = options.tickSeconds ?? DEFAULT_TICK_SECONDS;
const generalGold = options.defaultGeneralGold ?? DEFAULT_GENERAL_GOLD;
@@ -125,7 +125,7 @@ export const seedScenarioToDatabase = async (
await connector.connect();
try {
const prisma = connector.prisma as TurnEngineDatabaseClient;
const prisma = connector.prisma as unknown as TurnEngineDatabaseClient;
if (options.resetTables ?? true) {
await prisma.event.deleteMany();