Files
core2026/app/gateway-api/src/lobby/profileStatusService.ts
T

118 lines
3.9 KiB
TypeScript

import { gatewayProfileCapabilities, type GatewayProfileCapabilities } from '@sammo-ts/common';
import type { GatewayOrchestratorHandle } from '../orchestrator/gatewayOrchestrator.js';
import type {
GatewayProfileRecord,
GatewayProfileRepository,
GatewayProfileStatus,
} from '../orchestrator/profileRepository.js';
import { orderGatewayProfiles, resolveGatewayProfileKoreanName } from '../profileOrder.js';
export type LobbyMapSnapshot = {
updatedAt: string | null;
summary?: string | null;
};
export type LobbyGeneralStatus = {
exists: boolean;
cityId: number | null;
cityName: string | null;
updatedAt: string | null;
};
export type LobbyProfileStatus = {
profileName: string;
profile: string;
instanceKey: string;
currentScenario: string | null;
/** @deprecated Rollback-compatible mirror of currentScenario. */
scenario: string;
status: GatewayProfileStatus;
lifecycle: GatewayProfileCapabilities & {
dataInitialized: boolean;
};
apiPort: number;
runtime: {
apiRunning: boolean;
daemonRunning: boolean;
auctionRunning: boolean;
battleSimRunning: boolean;
tournamentRunning: boolean;
};
korName: string;
color: string;
};
export interface GatewayProfileStatusService {
listLobbyProfiles(input: { userId?: string } | undefined): Promise<LobbyProfileStatus[]>;
}
// 로비에서 사용할 프로필 상태를 메모리에서 반환하는 구현체.
export class InMemoryProfileStatusService implements GatewayProfileStatusService {
private profiles: LobbyProfileStatus[];
constructor(initial: LobbyProfileStatus[] = []) {
this.profiles = [...initial];
}
async listLobbyProfiles(): Promise<LobbyProfileStatus[]> {
return orderGatewayProfiles(this.profiles);
}
setProfiles(profiles: LobbyProfileStatus[]): void {
this.profiles = [...profiles];
}
}
// 프로필 저장소/오케스트레이터를 이용해 로비 상태를 구성하는 기본 구현체.
export class RepositoryProfileStatusService implements GatewayProfileStatusService {
constructor(
private readonly profiles: GatewayProfileRepository,
private readonly orchestrator: GatewayOrchestratorHandle
) {}
async listLobbyProfiles(): Promise<LobbyProfileStatus[]> {
const rows = orderGatewayProfiles(await this.profiles.listProfiles());
const runtimeStates = await this.orchestrator.listRuntimeStates(rows.map((profile) => profile.profileName));
const runtimeMap = new Map(runtimeStates.map((state) => [state.profileName, state]));
return rows.map((row) => this.mapProfile(row, runtimeMap));
}
private mapProfile(
row: GatewayProfileRecord,
runtimeMap: Map<
string,
{
apiRunning: boolean;
daemonRunning: boolean;
auctionRunning: boolean;
battleSimRunning: boolean;
tournamentRunning: boolean;
}
>
): LobbyProfileStatus {
const meta = row.meta;
return {
profileName: row.profileName,
profile: row.profile,
instanceKey: row.instanceKey,
currentScenario: row.currentScenario,
scenario: row.scenario,
status: row.status,
lifecycle: {
...gatewayProfileCapabilities(row.status),
dataInitialized: row.currentScenario !== null,
},
apiPort: row.apiPort,
runtime: runtimeMap.get(row.profileName) ?? {
apiRunning: false,
daemonRunning: false,
auctionRunning: false,
battleSimRunning: false,
tournamentRunning: false,
},
korName: resolveGatewayProfileKoreanName(row.profile, meta.korName),
color: (meta.color as string | undefined) ?? '#ffffff',
};
}
}