Merge branch 'main' into feature/auction-menu-parity
# Conflicts: # app/game-frontend/e2e/playwright.config.mjs # app/game-frontend/package.json # app/gateway-api/src/adminRouter.ts # app/gateway-api/src/lobby/profileStatusService.ts # app/gateway-api/src/orchestrator/gatewayOrchestrator.ts # app/gateway-api/test/orchestratorOperations.test.ts # app/gateway-api/test/orchestratorPlan.test.ts # app/gateway-frontend/e2e/server-operations.spec.ts # app/gateway-frontend/src/views/AdminView.vue # app/gateway-frontend/src/views/ServerOperationsView.vue
This commit is contained in:
@@ -818,6 +818,7 @@ export const adminRouter = router({
|
||||
apiRunning: false,
|
||||
daemonRunning: false,
|
||||
auctionRunning: false,
|
||||
battleSimRunning: false,
|
||||
tournamentRunning: false,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -27,6 +27,7 @@ export type LobbyProfileStatus = {
|
||||
apiRunning: boolean;
|
||||
daemonRunning: boolean;
|
||||
auctionRunning: boolean;
|
||||
battleSimRunning: boolean;
|
||||
tournamentRunning: boolean;
|
||||
};
|
||||
korName: string;
|
||||
@@ -72,7 +73,13 @@ export class RepositoryProfileStatusService implements GatewayProfileStatusServi
|
||||
row: GatewayProfileRecord,
|
||||
runtimeMap: Map<
|
||||
string,
|
||||
{ apiRunning: boolean; daemonRunning: boolean; auctionRunning: boolean; tournamentRunning: boolean }
|
||||
{
|
||||
apiRunning: boolean;
|
||||
daemonRunning: boolean;
|
||||
auctionRunning: boolean;
|
||||
battleSimRunning: boolean;
|
||||
tournamentRunning: boolean;
|
||||
}
|
||||
>
|
||||
): LobbyProfileStatus {
|
||||
const meta = row.meta;
|
||||
@@ -86,6 +93,7 @@ export class RepositoryProfileStatusService implements GatewayProfileStatusServi
|
||||
apiRunning: false,
|
||||
daemonRunning: false,
|
||||
auctionRunning: false,
|
||||
battleSimRunning: false,
|
||||
tournamentRunning: false,
|
||||
},
|
||||
korName: (meta.korName as string | undefined) ?? row.profile,
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface ProfileRuntimeState {
|
||||
apiRunning: boolean;
|
||||
daemonRunning: boolean;
|
||||
auctionRunning: boolean;
|
||||
battleSimRunning: boolean;
|
||||
tournamentRunning: boolean;
|
||||
}
|
||||
|
||||
@@ -71,6 +72,7 @@ export const planProfileReconcile = (
|
||||
runtime.apiRunning &&
|
||||
runtime.daemonRunning &&
|
||||
runtime.auctionRunning &&
|
||||
runtime.battleSimRunning &&
|
||||
runtime.tournamentRunning
|
||||
),
|
||||
shouldStop: false,
|
||||
@@ -78,7 +80,12 @@ export const planProfileReconcile = (
|
||||
}
|
||||
return {
|
||||
shouldStart: false,
|
||||
shouldStop: runtime.apiRunning || runtime.daemonRunning || runtime.auctionRunning || runtime.tournamentRunning,
|
||||
shouldStop:
|
||||
runtime.apiRunning ||
|
||||
runtime.daemonRunning ||
|
||||
runtime.auctionRunning ||
|
||||
runtime.battleSimRunning ||
|
||||
runtime.tournamentRunning,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -279,7 +286,10 @@ const parseInstallOptions = (
|
||||
};
|
||||
};
|
||||
|
||||
const buildProcessName = (profileName: string, role: 'api' | 'daemon' | 'auction' | 'tournament'): string =>
|
||||
const buildProcessName = (
|
||||
profileName: string,
|
||||
role: 'api' | 'daemon' | 'auction' | 'battle-sim' | 'tournament'
|
||||
): string =>
|
||||
`sammo:${profileName}:${
|
||||
role === 'api'
|
||||
? 'game-api'
|
||||
@@ -287,7 +297,9 @@ const buildProcessName = (profileName: string, role: 'api' | 'daemon' | 'auction
|
||||
? 'turn-daemon'
|
||||
: role === 'auction'
|
||||
? 'auction-worker'
|
||||
: 'tournament-worker'
|
||||
: role === 'battle-sim'
|
||||
? 'battle-sim-worker'
|
||||
: 'tournament-worker'
|
||||
}`;
|
||||
|
||||
const isMissingProcessError = (error: unknown): boolean =>
|
||||
@@ -300,12 +312,14 @@ export const buildProcessDefinitions = (
|
||||
api: { name: string; script: string; cwd: string; env: Record<string, string> };
|
||||
daemon: { name: string; script: string; cwd: string; env: Record<string, string> };
|
||||
auction: { name: string; script: string; cwd: string; env: Record<string, string> };
|
||||
battleSim: { name: string; script: string; cwd: string; env: Record<string, string> };
|
||||
tournament: { name: string; script: string; cwd: string; env: Record<string, string> };
|
||||
} => {
|
||||
const baseEnv = { ...(config.baseEnv ?? {}) };
|
||||
const apiName = buildProcessName(profile.profileName, 'api');
|
||||
const daemonName = buildProcessName(profile.profileName, 'daemon');
|
||||
const auctionName = buildProcessName(profile.profileName, 'auction');
|
||||
const battleSimName = buildProcessName(profile.profileName, 'battle-sim');
|
||||
const tournamentName = buildProcessName(profile.profileName, 'tournament');
|
||||
const runtimeWorkspace = profile.buildWorkspace ?? config.workspaceRoot;
|
||||
const apiCwd = path.join(runtimeWorkspace, 'app', 'game-api');
|
||||
@@ -352,6 +366,15 @@ export const buildProcessDefinitions = (
|
||||
GAME_API_ROLE: 'auction-worker',
|
||||
},
|
||||
},
|
||||
battleSim: {
|
||||
name: battleSimName,
|
||||
script: apiScript,
|
||||
cwd: apiCwd,
|
||||
env: {
|
||||
...apiEnv,
|
||||
GAME_API_ROLE: 'battle-sim-worker',
|
||||
},
|
||||
},
|
||||
tournament: {
|
||||
name: tournamentName,
|
||||
script: apiScript,
|
||||
@@ -402,12 +425,14 @@ const mapRuntimeStates = (profileNames: string[], processNames: Map<string, bool
|
||||
const apiName = buildProcessName(profileName, 'api');
|
||||
const daemonName = buildProcessName(profileName, 'daemon');
|
||||
const auctionName = buildProcessName(profileName, 'auction');
|
||||
const battleSimName = buildProcessName(profileName, 'battle-sim');
|
||||
const tournamentName = buildProcessName(profileName, 'tournament');
|
||||
return {
|
||||
profileName,
|
||||
apiRunning: processNames.get(apiName) ?? false,
|
||||
daemonRunning: processNames.get(daemonName) ?? false,
|
||||
auctionRunning: processNames.get(auctionName) ?? false,
|
||||
battleSimRunning: processNames.get(battleSimName) ?? false,
|
||||
tournamentRunning: processNames.get(tournamentName) ?? false,
|
||||
};
|
||||
});
|
||||
@@ -1009,6 +1034,7 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
|
||||
await this.processManager.start(definitions.api);
|
||||
await this.processManager.start(definitions.daemon);
|
||||
await this.processManager.start(definitions.auction);
|
||||
await this.processManager.start(definitions.battleSim);
|
||||
await this.processManager.start(definitions.tournament);
|
||||
await this.repository.updateLastError(profile.profileName, null);
|
||||
return true;
|
||||
@@ -1025,10 +1051,11 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
|
||||
const apiName = buildProcessName(profile.profileName, 'api');
|
||||
const daemonName = buildProcessName(profile.profileName, 'daemon');
|
||||
const auctionName = buildProcessName(profile.profileName, 'auction');
|
||||
const battleSimName = buildProcessName(profile.profileName, 'battle-sim');
|
||||
const tournamentName = buildProcessName(profile.profileName, 'tournament');
|
||||
const existingNames = new Set((await this.processManager.list()).map((process) => process.name));
|
||||
const failures: string[] = [];
|
||||
for (const name of [apiName, daemonName, auctionName, tournamentName]) {
|
||||
for (const name of [apiName, daemonName, auctionName, battleSimName, tournamentName]) {
|
||||
if (!existingNames.has(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ const createHarness = (
|
||||
{ name: 'sammo:che:2:game-api', status: 'online' },
|
||||
{ name: 'sammo:che:2:turn-daemon', status: 'online' },
|
||||
{ name: 'sammo:che:2:auction-worker', status: 'online' },
|
||||
{ name: 'sammo:che:2:battle-sim-worker', status: 'online' },
|
||||
{ name: 'sammo:che:2:tournament-worker', status: 'online' },
|
||||
]
|
||||
: [],
|
||||
@@ -139,7 +140,7 @@ const createHarness = (
|
||||
};
|
||||
|
||||
describe('GatewayOrchestrator first-class operations', () => {
|
||||
it('starts all profile processes and records success', async () => {
|
||||
it('starts every profile process and records success', async () => {
|
||||
const harness = createHarness(buildOperation('START'));
|
||||
|
||||
await harness.orchestrator.runOperationsNow();
|
||||
@@ -149,12 +150,13 @@ describe('GatewayOrchestrator first-class operations', () => {
|
||||
'sammo:che:2:game-api',
|
||||
'sammo:che:2:turn-daemon',
|
||||
'sammo:che:2:auction-worker',
|
||||
'sammo:che:2:battle-sim-worker',
|
||||
'sammo:che:2:tournament-worker',
|
||||
]);
|
||||
expect(harness.completions).toEqual(['SUCCEEDED']);
|
||||
});
|
||||
|
||||
it('stops all profile processes and records success', async () => {
|
||||
it('stops every profile process and records success', async () => {
|
||||
const harness = createHarness(buildOperation('STOP'));
|
||||
|
||||
await harness.orchestrator.runOperationsNow();
|
||||
@@ -164,12 +166,14 @@ describe('GatewayOrchestrator first-class operations', () => {
|
||||
'sammo:che:2:game-api',
|
||||
'sammo:che:2:turn-daemon',
|
||||
'sammo:che:2:auction-worker',
|
||||
'sammo:che:2:battle-sim-worker',
|
||||
'sammo:che:2:tournament-worker',
|
||||
]);
|
||||
expect(harness.deleted).toEqual([
|
||||
'sammo:che:2:game-api',
|
||||
'sammo:che:2:turn-daemon',
|
||||
'sammo:che:2:auction-worker',
|
||||
'sammo:che:2:battle-sim-worker',
|
||||
'sammo:che:2:tournament-worker',
|
||||
]);
|
||||
expect(harness.completions).toEqual(['SUCCEEDED']);
|
||||
@@ -195,6 +199,7 @@ describe('GatewayOrchestrator first-class operations', () => {
|
||||
'sammo:che:2:game-api',
|
||||
'sammo:che:2:turn-daemon',
|
||||
'sammo:che:2:auction-worker',
|
||||
'sammo:che:2:battle-sim-worker',
|
||||
'sammo:che:2:tournament-worker',
|
||||
]);
|
||||
expect(harness.completions).toEqual(['SUCCEEDED']);
|
||||
@@ -217,12 +222,14 @@ describe('GatewayOrchestrator first-class operations', () => {
|
||||
'sammo:che:2:game-api',
|
||||
'sammo:che:2:turn-daemon',
|
||||
'sammo:che:2:auction-worker',
|
||||
'sammo:che:2:battle-sim-worker',
|
||||
'sammo:che:2:tournament-worker',
|
||||
]);
|
||||
expect(harness.deleted).toEqual([
|
||||
'sammo:che:2:game-api',
|
||||
'sammo:che:2:turn-daemon',
|
||||
'sammo:che:2:auction-worker',
|
||||
'sammo:che:2:battle-sim-worker',
|
||||
'sammo:che:2:tournament-worker',
|
||||
]);
|
||||
expect(harness.completions).toEqual(['FAILED']);
|
||||
|
||||
@@ -30,6 +30,7 @@ describe('planProfileReconcile', () => {
|
||||
apiRunning: true,
|
||||
daemonRunning: false,
|
||||
auctionRunning: true,
|
||||
battleSimRunning: true,
|
||||
tournamentRunning: true,
|
||||
})
|
||||
).toEqual({ shouldStart: true, shouldStop: false });
|
||||
@@ -41,6 +42,7 @@ describe('planProfileReconcile', () => {
|
||||
apiRunning: false,
|
||||
daemonRunning: false,
|
||||
auctionRunning: false,
|
||||
battleSimRunning: false,
|
||||
tournamentRunning: false,
|
||||
})
|
||||
).toEqual({ shouldStart: true, shouldStop: false });
|
||||
@@ -52,6 +54,7 @@ describe('planProfileReconcile', () => {
|
||||
apiRunning: true,
|
||||
daemonRunning: true,
|
||||
auctionRunning: true,
|
||||
battleSimRunning: true,
|
||||
tournamentRunning: true,
|
||||
})
|
||||
).toEqual({ shouldStart: false, shouldStop: false });
|
||||
@@ -63,6 +66,7 @@ describe('planProfileReconcile', () => {
|
||||
apiRunning: true,
|
||||
daemonRunning: true,
|
||||
auctionRunning: false,
|
||||
battleSimRunning: true,
|
||||
tournamentRunning: true,
|
||||
})
|
||||
).toEqual({ shouldStart: true, shouldStop: false });
|
||||
@@ -74,6 +78,7 @@ describe('planProfileReconcile', () => {
|
||||
apiRunning: false,
|
||||
daemonRunning: true,
|
||||
auctionRunning: false,
|
||||
battleSimRunning: false,
|
||||
tournamentRunning: false,
|
||||
})
|
||||
).toEqual({ shouldStart: false, shouldStop: true });
|
||||
@@ -85,6 +90,7 @@ describe('planProfileReconcile', () => {
|
||||
apiRunning: false,
|
||||
daemonRunning: false,
|
||||
auctionRunning: false,
|
||||
battleSimRunning: false,
|
||||
tournamentRunning: false,
|
||||
})
|
||||
).toEqual({ shouldStart: false, shouldStop: false });
|
||||
@@ -116,6 +122,11 @@ describe('buildProcessDefinitions', () => {
|
||||
script: path.join(buildWorkspace, 'app', 'game-api', 'dist', 'index.js'),
|
||||
env: { GAME_API_ROLE: 'auction-worker' },
|
||||
});
|
||||
expect(definitions.battleSim).toMatchObject({
|
||||
cwd: path.join(buildWorkspace, 'app', 'game-api'),
|
||||
script: path.join(buildWorkspace, 'app', 'game-api', 'dist', 'index.js'),
|
||||
env: { GAME_API_ROLE: 'battle-sim-worker' },
|
||||
});
|
||||
expect(definitions.tournament).toMatchObject({
|
||||
cwd: path.join(buildWorkspace, 'app', 'game-api'),
|
||||
script: path.join(buildWorkspace, 'app', 'game-api', 'dist', 'index.js'),
|
||||
@@ -129,6 +140,7 @@ describe('buildProcessDefinitions', () => {
|
||||
expect(definitions.api.cwd).toBe(path.join(processConfig.workspaceRoot, 'app', 'game-api'));
|
||||
expect(definitions.daemon.cwd).toBe(path.join(processConfig.workspaceRoot, 'app', 'game-engine'));
|
||||
expect(definitions.auction.cwd).toBe(path.join(processConfig.workspaceRoot, 'app', 'game-api'));
|
||||
expect(definitions.battleSim.cwd).toBe(path.join(processConfig.workspaceRoot, 'app', 'game-api'));
|
||||
expect(definitions.tournament.cwd).toBe(path.join(processConfig.workspaceRoot, 'app', 'game-api'));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user