feat: admin-endpunkt zum direkten download des release-ZIP (fuer n8n/CI)

GET /api/v1/releases/:product/download (X-Admin-Token) streamt das aktuelle
release-ZIP; optional ?version=1.2.3 fuer eine bestimmte version. Kein lizenz-
key/token noetig - gedacht fuer automatisierung (n8n, CI). Antwort:
application/zip + header X-Release-Version. Integrationstest ergaenzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
s4luorth
2026-06-10 10:31:19 +02:00
parent fed29ab175
commit 54cebab1f5
3 changed files with 31 additions and 1 deletions

View File

@@ -214,6 +214,17 @@ try {
const relList = await admin('GET', '/api/v1/releases/gdpr-content-blocker');
ok('release list shows 1.1.0', relList.json.ok && relList.json.releases.some((r) => r.version === '1.1.0'), JSON.stringify(relList.json));
// Admin direct download (for n8n/CI) — latest version, no license needed
const dlA = await fetch(BASE + '/api/v1/releases/gdpr-content-blocker/download', { headers: { 'X-Admin-Token': TOKEN } });
const dlABuf = Buffer.from(await dlA.arrayBuffer());
ok('admin download latest 200 zip', dlA.status === 200 && dlA.headers.get('content-type') === 'application/zip', String(dlA.status));
ok('admin download version header = 1.1.0', dlA.headers.get('x-release-version') === '1.1.0');
ok('admin download is a ZIP', dlABuf[0] === 0x50 && dlABuf[1] === 0x4b);
const dlNoAuth = await fetch(BASE + '/api/v1/releases/gdpr-content-blocker/download');
ok('admin download without token → 401', dlNoAuth.status === 401);
const dlBadVer = await admin('GET', '/api/v1/releases/gdpr-content-blocker/download?version=9.9.9');
ok('admin download unknown version → 404', dlBadVer.status === 404);
// ── Release from URL: guards (real fetch not exercised offline) ──
const fuProd = await admin('POST', '/api/v1/releases/from-url', { product: 'nope', version: '1.2.0', zip_url: 'https://example.com/a.zip' });
ok('from-url unknown product → 404', fuProd.status === 404, JSON.stringify(fuProd.json));