Every T5 script lands in the flat sp/ directory, so the target path is always just the uploaded filename. Removed the field from the Scripts upload form and the server derives targetRelPath from file.name. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
import { json } from '@sveltejs/kit';
|
|
import {
|
|
createScript,
|
|
listScripts,
|
|
listVersions,
|
|
serversMatchingScope,
|
|
ScriptError,
|
|
type ScopeType
|
|
} from '$lib/server/fleet/scripts.js';
|
|
import { findRunningBetaForScript } from '$lib/server/fleet/beta.js';
|
|
|
|
function scopeTypeOrNull(v: FormDataEntryValue | null): ScopeType | null {
|
|
if (v === null) return null;
|
|
const s = String(v);
|
|
if (s === '' || s === 'none') return null;
|
|
if (s === 'game' || s === 'map') return s;
|
|
throw new ScriptError(`invalid scope_type: ${s}`, 'invalid-scope');
|
|
}
|
|
|
|
export async function GET() {
|
|
const scripts = listScripts().map((s) => {
|
|
const versions = listVersions(s.id);
|
|
const matched = s.scope_type && s.scope_value
|
|
? serversMatchingScope(s.scope_type as ScopeType, s.scope_value)
|
|
: [];
|
|
const runningBeta = findRunningBetaForScript(s.id);
|
|
return {
|
|
...s,
|
|
latestVersion: versions[0]
|
|
? {
|
|
id: versions[0].id,
|
|
version: versions[0].version,
|
|
sha256: versions[0].sha256,
|
|
size: versions[0].size,
|
|
uploaded_at: versions[0].uploaded_at
|
|
}
|
|
: null,
|
|
versionCount: versions.length,
|
|
servers: matched,
|
|
runningBetaId: runningBeta?.id ?? null
|
|
};
|
|
});
|
|
return json({ scripts });
|
|
}
|
|
|
|
// Multipart POST — creates a script + version 1 from a single file upload.
|
|
// Fields: name (text), scopeType (optional: 'game'|'map'), scopeValue
|
|
// (optional string), notes (optional), content (file). The target rel path
|
|
// defaults to the uploaded filename — T5 servers keep all scripts in a flat
|
|
// sp/ directory, so there's no sub-path to specify.
|
|
export async function POST({ request }: { request: Request }) {
|
|
const form = await request.formData();
|
|
const name = String(form.get('name') ?? '').trim();
|
|
const notes = form.get('notes') ? String(form.get('notes')) : undefined;
|
|
let scopeType: ScopeType | null;
|
|
try {
|
|
scopeType = scopeTypeOrNull(form.get('scopeType'));
|
|
} catch (err) {
|
|
return json({ ok: false, error: (err as Error).message }, { status: 400 });
|
|
}
|
|
const scopeValueRaw = form.get('scopeValue');
|
|
const scopeValue = scopeValueRaw ? String(scopeValueRaw).trim() : null;
|
|
const file = form.get('content');
|
|
if (!(file instanceof File)) return json({ ok: false, error: 'content file is required' }, { status: 400 });
|
|
|
|
const targetRelPath = file.name;
|
|
const content = Buffer.from(await file.arrayBuffer());
|
|
|
|
try {
|
|
const { script, version } = createScript({
|
|
name,
|
|
targetRelPath,
|
|
scopeType,
|
|
scopeValue,
|
|
content,
|
|
notes
|
|
});
|
|
return json({ ok: true, script, versionId: version.id });
|
|
} catch (err) {
|
|
if (err instanceof ScriptError) {
|
|
return json({ ok: false, error: err.message, kind: err.kind }, { status: 409 });
|
|
}
|
|
throw err;
|
|
}
|
|
}
|