Preview: Schema-Skript und Runner-Konfiguration

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-09-30 21:53:41 +00:00
1 parent def490ceca
commit 40a8aad3f9
3 files changed
+160

No files matched your search

+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env node
/**
* Preview 2.0: legt die Preview-Runner-Attribute an der Collection websiteProjects an.
* Idempotent: vorhandene Attribute werden uebersprungen.
* Usage:
* node scripts/preview-runner-setup.mjs --check (nur melden, nichts anlegen)
* node scripts/preview-runner-setup.mjs (fehlende Attribute anlegen)
*/
import { pathToFileURL } from 'node:url'
import { config } from '../server/config.js'
export const PREVIEW_RUNNER_ATTRIBUTES = [
{ key: 'previewMode', type: 'string', size: 10, required: false },
{ key: 'previewEnv', type: 'string', size: 20000, required: false },
{ key: 'previewState', type: 'string', size: 20, required: false },
{ key: 'previewDetected', type: 'string', size: 40, required: false },
{ key: 'previewBuiltSha', type: 'string', size: 40, required: false },
{ key: 'previewBuiltAt', type: 'string', size: 40, required: false },
{ key: 'previewError', type: 'string', size: 500, required: false },
]
/** Echter Appwrite-Client (REST) fuer die Collection websiteProjects. */
export function createAppwriteClient() {
const { endpoint, projectId, databaseId, apiKey } = config.appwrite
const collectionId = config.collections.websiteProjects
const base = `${endpoint.replace(/\/$/, '')}/databases/${databaseId}/collections/${collectionId}`
async function aw(path, { method = 'GET', body } = {}) {
const response = await fetch(`${base}${path}`, {
method,
headers: {
'Content-Type': 'application/json',
'X-Appwrite-Project': projectId,
'X-Appwrite-Key': apiKey,
},
body: body ? JSON.stringify(body) : undefined,
})
const data = await response.json().catch(() => ({}))
if (!response.ok) throw new Error(data.message || `Appwrite ${response.status}`)
return data
}
return {
async listAttributes() {
// Ohne limit liefert Appwrite nur 25 Attribute - die Sammlung hat mehr
const q = encodeURIComponent(JSON.stringify({ method: 'limit', values: [200] }))
const data = await aw(`/attributes?queries[]=${q}`)
return data.attributes || []
},
async createAttribute({ type, ...body }) {
await aw(`/attributes/${type}`, { method: 'POST', body })
},
async waitForAttribute(key, maxSec = 30) {
for (let i = 0; i < maxSec; i += 1) {
const attr = await aw(`/attributes/${key}`).catch(() => null)
if (attr?.status === 'available') return
await new Promise((r) => setTimeout(r, 1000))
}
throw new Error(`Attribut ${key} wurde nicht verfuegbar`)
},
}
}
export async function main({ check = false, client, log = console.log } = {}) {
const api = client || createAppwriteClient()
const existing = new Set((await api.listAttributes()).map((a) => a.key))
const missing = PREVIEW_RUNNER_ATTRIBUTES.filter((a) => !existing.has(a.key))
for (const attr of PREVIEW_RUNNER_ATTRIBUTES) {
if (existing.has(attr.key)) log(`= Attribut ${attr.key} (existiert)`)
else if (check) log(`- Attribut ${attr.key} FEHLT`)
}
if (check) {
log(missing.length ? `${missing.length} Attribut(e) fehlen.` : 'Alle Attribute vorhanden.')
return { missing }
}
for (const attr of missing) {
await api.createAttribute(attr)
log(`+ Attribut ${attr.key}`)
}
for (const attr of missing) await api.waitForAttribute(attr.key)
log('Fertig.')
return { missing }
}
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
if (invokedDirectly) {
const check = process.argv.includes('--check')
main({ check })
.then(({ missing }) => process.exit(check && missing.length ? 1 : 0))
.catch((e) => {
console.error(e.message)
process.exit(1)
})
}