Preview: Admin-API Nachbesserung (off verdraengt Bau, env-Sperre)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
07bef6971c
commit
b3cf78c82a
2 files changed
+82
-23
No files matched your search
@@ -7,6 +7,7 @@ import { envKey, encryptEnv, decryptEnv, envNames, validateEnvChange } from '../
|
||||
import { listRunLogs, readRunLog, isRunFile } from '../../services/previewLogs.js'
|
||||
import * as runnerClient from '../../services/previewRunnerClient.js'
|
||||
import { runDeploy, fetchPreviewConfig } from '../../services/previewDeploy.js'
|
||||
import { withLocks } from '../../services/keyedLock.js'
|
||||
import { getRepoInfo } from '../../services/giteaAdmin.js'
|
||||
|
||||
const router = Router()
|
||||
@@ -18,6 +19,7 @@ const SHA_RE = /^[0-9a-f]{7,40}$/
|
||||
function fail(status, message) {
|
||||
const err = new Error(message)
|
||||
err.status = status
|
||||
err.expected = true
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -79,9 +81,9 @@ const wrap = (fn) => async (req, res) => {
|
||||
try {
|
||||
await fn(req, res)
|
||||
} catch (err) {
|
||||
const status = err.status || 500
|
||||
if (status >= 500 && status !== 503) console.error('[admin/preview]', err.message)
|
||||
res.status(status).json({ error: status >= 500 ? err.message || 'Preview-Aktion fehlgeschlagen' : err.message })
|
||||
const status = err.expected ? err.status : 500
|
||||
if (!err.expected) console.error('[admin/preview]', err.message)
|
||||
res.status(status).json({ error: !err.expected ? 'Preview-Aktion fehlgeschlagen' : err.message })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +97,13 @@ router.patch('/:id/preview', wrap(async (req, res) => {
|
||||
if (!MODES.includes(mode)) throw fail(400, 'Ungültiger Modus (auto, build, dev oder off)')
|
||||
const doc = await loadProject(req.params.id)
|
||||
if (mode === 'off') {
|
||||
const saved = await updateDocument(col(), doc.$id, { previewMode: mode })
|
||||
// runDeploy (Modus off) belegt den Platz des Repos: laufende Verfolger schreiben danach nichts mehr
|
||||
await triggerRebuild(saved)
|
||||
try { await runnerClient.stopDev(doc.subdomain) } catch (err) {
|
||||
console.error('[admin/preview] Dev stoppen fehlgeschlagen:', err.message)
|
||||
}
|
||||
const updated = await updateDocument(col(), doc.$id, { previewMode: mode, previewState: 'off' })
|
||||
return res.json(await view(updated))
|
||||
return res.json(await view(await getDocument(col(), doc.$id)))
|
||||
}
|
||||
const updated = await updateDocument(col(), doc.$id, { previewMode: mode })
|
||||
await triggerRebuild(updated)
|
||||
@@ -110,18 +114,23 @@ router.put('/:id/preview/env', wrap(async (req, res) => {
|
||||
requireRunner()
|
||||
const key = envKey()
|
||||
if (!key) throw fail(503, 'Preview-Variablen sind nicht eingerichtet')
|
||||
const doc = await loadProject(req.params.id)
|
||||
let current
|
||||
try { current = decryptEnv(doc.previewEnv, key) } catch {
|
||||
throw fail(409, 'Vorhandene Preview-Variablen sind nicht entschlüsselbar')
|
||||
}
|
||||
const { set, remove } = req.body || {}
|
||||
const result = validateEnvChange(current, { set, remove })
|
||||
if (!result.ok) throw fail(400, result.error)
|
||||
const stored = Object.keys(result.env).length ? encryptEnv(result.env, key) : ''
|
||||
const updated = await updateDocument(col(), doc.$id, { previewEnv: stored })
|
||||
const { set = {}, remove = [] } = req.body || {}
|
||||
if (!set || typeof set !== 'object' || Array.isArray(set)) throw fail(400, '„set“ muss ein Objekt sein.')
|
||||
if (!Array.isArray(remove)) throw fail(400, '„remove“ muss eine Liste sein.')
|
||||
const updated = await withLocks(['preview-env:' + req.params.id], async () => {
|
||||
const doc = await loadProject(req.params.id)
|
||||
let current
|
||||
try { current = decryptEnv(doc.previewEnv, key) } catch {
|
||||
throw fail(409, 'Vorhandene Preview-Variablen sind nicht entschlüsselbar')
|
||||
}
|
||||
const result = validateEnvChange(current, { set, remove })
|
||||
if (!result.ok) throw fail(400, result.error)
|
||||
const stored = Object.keys(result.env).length ? encryptEnv(result.env, key) : ''
|
||||
if (stored.length > 20000) throw fail(400, 'Zu viele oder zu lange Variablen')
|
||||
return updateDocument(col(), doc.$id, { previewEnv: stored })
|
||||
})
|
||||
if ((updated.previewMode || 'auto') !== 'off') await triggerRebuild(updated)
|
||||
res.json(await view(await getDocument(col(), doc.$id)))
|
||||
res.json(await view(await getDocument(col(), updated.$id)))
|
||||
}))
|
||||
|
||||
router.post('/:id/preview/rebuild', wrap(async (req, res) => {
|
||||
@@ -159,7 +168,10 @@ router.post('/:id/preview/dev/stop', wrap(async (req, res) => {
|
||||
requireRunner()
|
||||
const doc = await loadProject(req.params.id)
|
||||
await runnerClient.stopDev(doc.subdomain)
|
||||
const updated = await updateDocument(col(), doc.$id, { previewState: 'sleeping' })
|
||||
const fresh = await getDocument(col(), doc.$id)
|
||||
const updated = ['online', 'starting', 'sleeping'].includes(fresh.previewState)
|
||||
? await updateDocument(col(), doc.$id, { previewState: 'sleeping' })
|
||||
: fresh
|
||||
res.json(await view(updated))
|
||||
}))
|
||||
|
||||
|
||||
Reference in new issue
Block a user