This commit is contained in:
@@ -39,7 +39,14 @@ export async function POST({ params, request }: { params: { id: string }; reques
|
||||
if (isHttpError(error)) throw error;
|
||||
return json(
|
||||
{ ok: false, error: (error as Error).message },
|
||||
{ status: error instanceof BetaError || error instanceof ScriptError ? 409 : 400 }
|
||||
{
|
||||
status:
|
||||
error instanceof ScriptError && error.kind === 'invalid-release-version'
|
||||
? 400
|
||||
: error instanceof BetaError || error instanceof ScriptError
|
||||
? 409
|
||||
: 400
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,10 @@ export async function POST({ request, url }: { request: Request; url: URL }) {
|
||||
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 });
|
||||
return json(
|
||||
{ ok: false, error: err.message, kind: err.kind },
|
||||
{ status: err.kind === 'invalid-release-version' ? 400 : 409 }
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function GET({ params }: { params: { id: string } }) {
|
||||
script.kind === 'script' || script.kind === 'plugin'
|
||||
? db()
|
||||
.prepare(
|
||||
`SELECT i.server, v.version, v.id AS versionId, i.updated_at AS recordedAt FROM server_scripts i JOIN script_versions v ON v.id = i.script_version_id WHERE v.script_id = ? AND i.rel_path = ? ORDER BY i.server`
|
||||
`SELECT i.server, v.version, v.release_version, v.id AS versionId, i.updated_at AS recordedAt FROM server_scripts i JOIN script_versions v ON v.id = i.script_version_id WHERE v.script_id = ? AND i.rel_path = ? ORDER BY i.server`
|
||||
)
|
||||
.all(id, script.target_rel_path)
|
||||
: [],
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { artifactFormBody } from '$lib/server/request-body.js';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { uploadNewVersion, getScript, ScriptError } from '$lib/server/fleet/scripts.js';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { db } from '$lib/server/db/index.js';
|
||||
import {
|
||||
uploadNewVersion,
|
||||
getScript,
|
||||
getLatestVersion,
|
||||
ScriptError
|
||||
} from '$lib/server/fleet/scripts.js';
|
||||
import { findRunningBetaForScript } from '$lib/server/fleet/beta.js';
|
||||
|
||||
function parseId(idRaw: string): number | null {
|
||||
@@ -11,7 +14,7 @@ function parseId(idRaw: string): number | null {
|
||||
}
|
||||
|
||||
// Multipart POST — creates a new version from an uploaded file. Fields:
|
||||
// content (file), notes and releaseVersion (optional). Response includes a "betaRequired" hint
|
||||
// content (file), notes (optional) and releaseVersion (required for scripts). Response includes a "betaRequired" hint
|
||||
// when the script has a scope so the UI can nudge the operator to open a beta.
|
||||
export async function POST({ params, request }: { params: { id: string }; request: Request }) {
|
||||
const scriptId = parseId(params.id);
|
||||
@@ -45,21 +48,16 @@ export async function POST({ params, request }: { params: { id: string }; reques
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
const sha = createHash('sha256').update(content).digest('hex');
|
||||
const existing = db()
|
||||
.prepare(
|
||||
'SELECT id, version, sha256, size FROM script_versions WHERE script_id = ? AND sha256 = ? ORDER BY version DESC LIMIT 1'
|
||||
)
|
||||
.get(scriptId, sha);
|
||||
if (existing) return json({ ok: true, version: existing, reused: true });
|
||||
}
|
||||
try {
|
||||
const releaseVersion = form.has('releaseVersion')
|
||||
? String(form.get('releaseVersion'))
|
||||
: undefined;
|
||||
const previous = getLatestVersion(scriptId);
|
||||
const version = uploadNewVersion(scriptId, content, notes, releaseVersion);
|
||||
return json({
|
||||
ok: true,
|
||||
reused: script.kind === 'script' && version.version <= (previous?.version ?? 0),
|
||||
version: {
|
||||
id: version.id,
|
||||
version: version.version,
|
||||
@@ -73,7 +71,10 @@ export async function POST({ params, request }: { params: { id: string }; reques
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof ScriptError) {
|
||||
return json({ ok: false, error: err.message, kind: err.kind }, { status: 409 });
|
||||
return json(
|
||||
{ ok: false, error: err.message, kind: err.kind },
|
||||
{ status: err.kind === 'invalid-release-version' ? 400 : 409 }
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function POST({ request }: { request: Request }) {
|
||||
.digest('hex');
|
||||
const duplicates = db()
|
||||
.prepare(
|
||||
`SELECT v.script_id AS scriptId, v.id AS versionId, v.version
|
||||
`SELECT v.script_id AS scriptId, v.id AS versionId, v.version, v.release_version
|
||||
FROM script_versions v JOIN scripts s ON s.id = v.script_id
|
||||
WHERE s.kind = 'script' AND v.sha256 = ? ORDER BY v.version DESC`
|
||||
)
|
||||
|
||||
@@ -261,7 +261,11 @@
|
||||
>
|
||||
<td class="files"
|
||||
>{#if beta.scripts.length === 1}{beta.scripts[0].name}
|
||||
<span>{artifactVersionLabel(beta.scripts[0].version)}</span
|
||||
<span
|
||||
>{artifactVersionLabel(
|
||||
beta.scripts[0].version,
|
||||
beta.scripts[0].kind
|
||||
)}</span
|
||||
>{:else}{beta.scripts.length} files{/if}</td
|
||||
>
|
||||
<td
|
||||
|
||||
@@ -620,7 +620,7 @@
|
||||
$page.url
|
||||
)}>{file.name}</a
|
||||
>
|
||||
· {artifactVersionLabel(file.version)}</summary
|
||||
· {artifactVersionLabel(file.version, file.kind)}</summary
|
||||
>
|
||||
<p><code>{file.target_rel_path}</code></p>
|
||||
{#if file.version}<p class="muted">
|
||||
@@ -661,7 +661,10 @@
|
||||
<a
|
||||
href={`/api/betas/${id}/revisions/${item.revision}/files/${file.script_id}`}
|
||||
download={file.path.split('/').pop()}
|
||||
>{file.name} · {artifactVersionLabel(file)}</a
|
||||
>{file.name} · {artifactVersionLabel(
|
||||
file,
|
||||
file.kind
|
||||
)}</a
|
||||
><code>{file.path}</code><span
|
||||
>{file.sha256.slice(0, 12)}</span
|
||||
>
|
||||
@@ -744,7 +747,8 @@
|
||||
<div class="file-list">
|
||||
{#each preview.files as file}<details open>
|
||||
<summary
|
||||
>{file.name} · {artifactVersionLabel(file)} · {file.servers.length} servers</summary
|
||||
>{file.name} · {artifactVersionLabel(file, file.kind)} · {file.servers
|
||||
.length} servers</summary
|
||||
>
|
||||
<p><code>{file.path}</code> · {file.scope} · SHA {file.sha256.slice(0, 12)}</p>
|
||||
<ul>
|
||||
|
||||
@@ -712,10 +712,14 @@
|
||||
? 'Loading versions…'
|
||||
: 'Choose a version'}</option
|
||||
>{#each versionsFor(pick) as version}<option value={version.id}
|
||||
>{artifactVersionLabel(version)} · {version.sha256.slice(
|
||||
0,
|
||||
8
|
||||
)} · {version.state.replaceAll('-', ' ')}</option
|
||||
>{artifactVersionLabel(
|
||||
version,
|
||||
scripts.find((file) => file.id === pick.scriptId)
|
||||
?.kind
|
||||
)} · {version.sha256.slice(0, 8)} · {version.state.replaceAll(
|
||||
'-',
|
||||
' '
|
||||
)}</option
|
||||
>{/each}</Select
|
||||
></Field
|
||||
>
|
||||
@@ -926,7 +930,8 @@
|
||||
?.name}</strong
|
||||
>
|
||||
· {artifactVersionLabel(
|
||||
versionsFor(pick).find((v) => v.id === pick.versionId)
|
||||
versionsFor(pick).find((v) => v.id === pick.versionId),
|
||||
scripts.find((file) => file.id === pick.scriptId)?.kind
|
||||
)} · {versionsFor(pick)
|
||||
.find((v) => v.id === pick.versionId)
|
||||
?.sha256.slice(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { artifactVersionLabel } from '$lib/artifact-version.js';
|
||||
import Select from '$ui/components/primitives/Select.svelte';
|
||||
import Input from '$ui/components/primitives/Input.svelte';
|
||||
import Field from '$ui/components/primitives/Field.svelte';
|
||||
@@ -336,6 +337,7 @@
|
||||
<td>{scopeLabel(s)}</td>
|
||||
<td
|
||||
><LibraryVersions
|
||||
kind="script"
|
||||
current={s.currentVersion}
|
||||
latest={s.latestVersion}
|
||||
betaId={s.runningBetaId}
|
||||
@@ -389,7 +391,10 @@
|
||||
>
|
||||
{#if reviewSelection}<ul class="selection-review">
|
||||
{#each selectedScripts as script}<li>
|
||||
{script.name} · v{script.latestVersion?.version}<Button
|
||||
{script.name} · {artifactVersionLabel(
|
||||
script.latestVersion,
|
||||
'script'
|
||||
)}<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onclick={() => toggle(script.id)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { artifactVersionLabel } from '$lib/artifact-version.js';
|
||||
import RelativeTime from '$ui/components/RelativeTime.svelte';
|
||||
import { tick } from 'svelte';
|
||||
import ActivityLog from '$ui/components/ActivityLog.svelte';
|
||||
@@ -564,8 +565,15 @@
|
||||
)}>{f.relPath}</a
|
||||
>{:else}{f.relPath}{/if}</td
|
||||
><td>{f.kind ?? 'file'}</td><td
|
||||
>{f.releaseVersion ??
|
||||
(f.version ? `v${f.version}` : '—')}</td
|
||||
>{artifactVersionLabel(
|
||||
f.version
|
||||
? {
|
||||
version: f.version,
|
||||
release_version: f.releaseVersion
|
||||
}
|
||||
: null,
|
||||
f.kind ?? undefined
|
||||
)}</td
|
||||
><td
|
||||
><details>
|
||||
<summary
|
||||
|
||||
Reference in New Issue
Block a user