Support multi-game betas with automatic server selection
This commit is contained in:
@@ -10,6 +10,7 @@ import type { StoredPlan } from '$lib/server/fleet/beta-tests.js';
|
||||
import { kick } from '$lib/server/pipeline/worker.js';
|
||||
import { parseBetaPlan, type BetaPlan } from '$lib/beta-plan.js';
|
||||
import { betaListPage } from '$lib/server/fleet/beta-pages.js';
|
||||
import { parseBetaInstanceCounts } from '$lib/server/fleet/beta-selection.js';
|
||||
|
||||
export async function GET({ url }: { url: URL }) {
|
||||
const state = url.searchParams.get('state');
|
||||
@@ -47,6 +48,7 @@ export async function POST({ request }: { request: Request }) {
|
||||
artifacts?: Array<{ id?: number; versionId?: number }>;
|
||||
scripts?: Array<{ scriptId?: number; scriptVersionId?: number }>;
|
||||
servers?: string[];
|
||||
instancesPerGame?: Record<string, number>;
|
||||
waitForHealth?: boolean;
|
||||
waitForMatch?: boolean;
|
||||
notes?: string;
|
||||
@@ -57,29 +59,35 @@ export async function POST({ request }: { request: Request }) {
|
||||
// Normalize into the openBeta shape. Both fields feed the same list.
|
||||
const rawEntries: Array<{ scriptId?: number; scriptVersionId?: number }> = [];
|
||||
if (Array.isArray(body.artifacts)) {
|
||||
for (const a of body.artifacts) rawEntries.push({ scriptId: a.id, scriptVersionId: a.versionId });
|
||||
for (const a of body.artifacts) rawEntries.push({ scriptId: a?.id, scriptVersionId: a?.versionId });
|
||||
}
|
||||
if (Array.isArray(body.scripts)) {
|
||||
for (const s of body.scripts) rawEntries.push({ scriptId: s.scriptId, scriptVersionId: s.scriptVersionId });
|
||||
for (const s of body.scripts) rawEntries.push({ scriptId: s?.scriptId, scriptVersionId: s?.scriptVersionId });
|
||||
}
|
||||
if (rawEntries.length === 0 || !Array.isArray(body.servers)) {
|
||||
if (rawEntries.length === 0) {
|
||||
return json(
|
||||
{ ok: false, error: 'artifacts[]/scripts[] (non-empty) and servers[] are required' },
|
||||
{ ok: false, error: 'artifacts[]/scripts[] must contain at least one file' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
if (body.servers.length === 0) {
|
||||
return json({ ok: false, error: 'at least one server is required' }, { status: 400 });
|
||||
if ((body.servers !== undefined) === (body.instancesPerGame !== undefined)) {
|
||||
return json({ ok: false, error: 'Provide either instancesPerGame or servers, exclusively' }, { status: 400 });
|
||||
}
|
||||
if (body.instancesPerGame !== undefined) {
|
||||
try { body.instancesPerGame = parseBetaInstanceCounts(body.instancesPerGame); }
|
||||
catch (error) { return json({ ok: false, error: (error as Error).message }, { status: 400 }); }
|
||||
} else if (!Array.isArray(body.servers) || !body.servers.length || !body.servers.every(name => typeof name === 'string' && name.trim())) {
|
||||
return json({ ok: false, error: 'servers must contain at least one server name' }, { status: 400 });
|
||||
}
|
||||
const normalized: Array<{ scriptId: number; scriptVersionId: number }> = [];
|
||||
for (const s of rawEntries) {
|
||||
if (typeof s.scriptId !== 'number' || typeof s.scriptVersionId !== 'number') {
|
||||
if (!Number.isSafeInteger(s.scriptId) || !Number.isSafeInteger(s.scriptVersionId) || s.scriptId! < 1 || s.scriptVersionId! < 1) {
|
||||
return json(
|
||||
{ ok: false, error: 'each entry needs scriptId/id and scriptVersionId/versionId' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
normalized.push({ scriptId: s.scriptId, scriptVersionId: s.scriptVersionId });
|
||||
normalized.push({ scriptId: s.scriptId!, scriptVersionId: s.scriptVersionId! });
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -90,6 +98,7 @@ export async function POST({ request }: { request: Request }) {
|
||||
testPlan,
|
||||
scripts: normalized,
|
||||
servers: body.servers,
|
||||
instancesPerGame: body.instancesPerGame,
|
||||
waitForHealth: body.waitForHealth ?? true,
|
||||
waitForMatch: body.waitForMatch ?? false,
|
||||
notes: body.notes
|
||||
|
||||
Reference in New Issue
Block a user