Refresh Configs with browser editing and source inspection
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { getScript, getVersion, getLatestVersion } from '$lib/server/fleet/scripts.js';
|
||||
|
||||
export async function GET({ params, url }: { params: { id: string }; url: URL }) {
|
||||
const id = Number(params.id);
|
||||
if (!Number.isSafeInteger(id) || id <= 0) return json({ error: 'Invalid config ID.' }, { status: 400 });
|
||||
const script = getScript(id);
|
||||
if (!script || !['config', 'baseline'].includes(script.kind)) return json({ error: 'Config not found.' }, { status: 404 });
|
||||
const requested = url.searchParams.get('version');
|
||||
const version = requested === null ? getLatestVersion(id) : getVersion(Number(requested));
|
||||
if (!version || version.script_id !== id) return json({ error: 'Config version not found.' }, { status: 404 });
|
||||
return json({ text: version.content.toString('utf8'), versionId: version.id, version: version.version }, { headers: { 'Cache-Control': 'no-store' } });
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { getServer, listServers } from '$lib/server/fleet/servers.js';
|
||||
import { getBaselineForScope, getScript, getVersion, listPromotedOverlaysForScope, type ScriptRow } from '$lib/server/fleet/scripts.js';
|
||||
import { inspectConfig, type ConfigLayer } from '$lib/server/fleet/config-inspect.js';
|
||||
import type { ConfigInspection } from '$lib/config-workbench.js';
|
||||
|
||||
function inspect(serverName: string, draft?: { artifactId: number; text: string }): ConfigInspection {
|
||||
const server = getServer(serverName);
|
||||
if (!server || server.destroyed_at) throw new Error('Server not found.');
|
||||
const artifact = draft ? getScript(draft.artifactId) : undefined;
|
||||
if (draft && (!artifact || !['baseline', 'config'].includes(artifact.kind))) throw new Error('Config not found.');
|
||||
if (artifact && (artifact.scope_type === null || artifact.scope_value !== server[artifact.scope_type])) {
|
||||
throw new Error('This config does not apply to the selected server.');
|
||||
}
|
||||
if (artifact?.kind === 'baseline' && artifact.scope_type !== 'game') throw new Error('Server composition requires a game baseline.');
|
||||
const baseline = artifact?.kind === 'baseline' ? artifact : server.game ? getBaselineForScope('game', server.game) : undefined;
|
||||
function layer(row: ScriptRow): ConfigLayer | null {
|
||||
const isDraft = row.id === draft?.artifactId;
|
||||
const version = row.current_version_id ? getVersion(row.current_version_id) : undefined;
|
||||
if (!isDraft && !version) return null;
|
||||
return {
|
||||
source: { id: row.id, name: row.name, version: isDraft ? null : version!.version, kind: row.kind as 'baseline' | 'config', draft: isDraft },
|
||||
text: isDraft ? draft!.text : version!.content.toString('utf8')
|
||||
};
|
||||
}
|
||||
const base = baseline ? layer(baseline) : null;
|
||||
if (!base) return { text: '', sources: [], settings: [], warnings: [], reason: 'No promoted baseline for this game. Promote a baseline through a beta to inspect the managed configuration.' };
|
||||
const overlays = [
|
||||
...(server.game ? listPromotedOverlaysForScope('game', server.game) : []),
|
||||
...(server.map ? listPromotedOverlaysForScope('map', server.map) : [])
|
||||
].filter(row => row.id !== artifact?.id);
|
||||
if (artifact?.kind === 'config') overlays.push(artifact);
|
||||
return inspectConfig(base, overlays.map(layer).filter((l): l is ConfigLayer => l !== null), server.game, server.map, server.map_slug);
|
||||
}
|
||||
|
||||
export async function GET({ url }: { url: URL }) {
|
||||
const servers = listServers().filter(s => !s.destroyed_at).map(({ name, game, map }) => ({ name, game, map }));
|
||||
try {
|
||||
const server = url.searchParams.get('server');
|
||||
return json({ servers, inspection: server ? inspect(server) : null }, { headers: { 'Cache-Control': 'no-store' } });
|
||||
} catch (err) {
|
||||
return json({ error: (err as Error).message }, { status: 400 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST({ request }: { request: Request }) {
|
||||
let body;
|
||||
try { body = await request.json(); } catch { return json({ error: 'Invalid preview request.' }, { status: 400 }); }
|
||||
if (!body || typeof body.server !== 'string' || !Number.isSafeInteger(body.artifactId) || typeof body.text !== 'string') {
|
||||
return json({ error: 'Server, config ID and text are required.' }, { status: 400 });
|
||||
}
|
||||
try {
|
||||
return json({ inspection: inspect(body.server, body) }, { headers: { 'Cache-Control': 'no-store' } });
|
||||
} catch (err) {
|
||||
return json({ error: (err as Error).message }, { status: 400 });
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,10 @@ export async function POST({ params, request }: { params: { id: string }; reques
|
||||
const script = getScript(scriptId);
|
||||
if (!script) return json({ ok: false, error: 'not found' }, { status: 404 });
|
||||
|
||||
if ((script.kind === 'config' || script.kind === 'baseline') && findRunningBetaForScript(scriptId)) {
|
||||
return json({ ok: false, error: 'Finish the active beta before saving a config version.' }, { status: 409 });
|
||||
}
|
||||
|
||||
const form = await request.formData();
|
||||
const notes = form.get('notes') ? String(form.get('notes')) : undefined;
|
||||
const file = form.get('content');
|
||||
|
||||
Reference in New Issue
Block a user