From f25e1bcc8ad28da3856569d94ae89a53912f83a0 Mon Sep 17 00:00:00 2001 From: Towsty Date: Tue, 1 Sep 2026 19:14:34 -0500 Subject: [PATCH] Spell out which Coolify backup vars belong on AIGen vs xAIGen. A URL in BACKUP_INGEST_TOKEN was treated as a missing token. Errors now say to put the URL on AIGen and the same secret on both apps. Co-authored-by: Cursor --- .env.example | 10 ++-- server/api/library/backup-ingest.post.ts | 10 ++-- server/api/library/stills/[id]/backup.post.ts | 12 ++--- server/utils/backupStudio.ts | 46 +++++++++++++++++++ 4 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 server/utils/backupStudio.ts diff --git a/.env.example b/.env.example index ed3a11e..0c17913 100644 --- a/.env.example +++ b/.env.example @@ -24,10 +24,12 @@ NUXT_OIDC_AUTHORIZE_URL=https://auth.carrgarage.com/application/o/authorize/ NUXT_OIDC_TOKEN_URL=http://192.168.77.2:9000/application/o/token/ LIBRARY_DIR=/data/library -# AIGen → xAIGen still backup. Set the URL + token on AIGen. Set the same token on xAIGen as BACKUP_INGEST_TOKEN. -# BACKUP_STUDIO_URL=https://xaigen.carrgarage.com -# BACKUP_STUDIO_TOKEN= -# BACKUP_INGEST_TOKEN= +# AIGen → xAIGen still backup. Same secret on both apps. URL only on AIGen. +# AIGen: +# BACKUP_STUDIO_URL=https://xaigen.carrgarage.com +# BACKUP_STUDIO_TOKEN=pick-a-long-random-secret +# xAIGen: +# BACKUP_INGEST_TOKEN=pick-a-long-random-secret # Private clone (xaigen): password login, isolated library, Comfy prefix # AUTH_MODE=password diff --git a/server/api/library/backup-ingest.post.ts b/server/api/library/backup-ingest.post.ts index 5d87c70..b5d2295 100644 --- a/server/api/library/backup-ingest.post.ts +++ b/server/api/library/backup-ingest.post.ts @@ -1,3 +1,4 @@ +import { backupIngestSecret } from '~/server/utils/backupStudio' import { passwordAuthEnabled, privateInstanceId } from '~/server/utils/localAuth' import { saveStill } from '~/server/utils/library' @@ -7,9 +8,12 @@ function ingestOwner() { } function assertIngestToken(event: Parameters[0]) { - const expected = String(useRuntimeConfig().backupIngestToken || process.env.BACKUP_INGEST_TOKEN || '').trim() - if (!expected) { - throw createError({ statusCode: 503, statusMessage: 'Backup ingest is not configured' }) + const expected = backupIngestSecret() + if (!expected || /^https?:\/\//i.test(expected)) { + throw createError({ + statusCode: 503, + statusMessage: 'Set BACKUP_INGEST_TOKEN on xAIGen to the same secret as BACKUP_STUDIO_TOKEN on AIGen, not a URL.' + }) } const header = String(getHeader(event, 'authorization') || '') if (header !== `Bearer ${expected}`) { diff --git a/server/api/library/stills/[id]/backup.post.ts b/server/api/library/stills/[id]/backup.post.ts index 46d3f03..01c241a 100644 --- a/server/api/library/stills/[id]/backup.post.ts +++ b/server/api/library/stills/[id]/backup.post.ts @@ -1,17 +1,17 @@ import { readFileSync } from 'node:fs' import { isXaigenStudio } from '~/utils/videoModels' +import { backupStudioConfigError, backupStudioTarget } from '~/server/utils/backupStudio' import { assertLibraryOwner, getStill, stillPath } from '~/server/utils/library' export default defineEventHandler(async (event) => { if (isXaigenStudio()) { throw createError({ statusCode: 404, statusMessage: 'Not found' }) } - const config = useRuntimeConfig() - const url = String(config.backupStudioUrl || process.env.BACKUP_STUDIO_URL || '').replace(/\/$/, '') - const token = String(config.backupStudioToken || process.env.BACKUP_STUDIO_TOKEN || '').trim() - if (!url || !token) { - throw createError({ statusCode: 503, statusMessage: 'Backup studio is not configured' }) + const missing = backupStudioConfigError() + if (missing) { + throw createError({ statusCode: 503, statusMessage: missing }) } + const { url, token } = backupStudioTarget() const { owner } = assertLibraryOwner(event) const id = String(getRouterParam(event, 'id') || '') const still = getStill(owner, id) @@ -31,7 +31,7 @@ export default defineEventHandler(async (event) => { const message = await res.text().catch(() => '') throw createError({ statusCode: res.status === 401 ? 502 : res.status, - statusMessage: message || 'Backup studio refused the still' + statusMessage: message || 'xAIGen refused the still. Check BACKUP_INGEST_TOKEN matches AIGen BACKUP_STUDIO_TOKEN.' }) } return { ok: true } diff --git a/server/utils/backupStudio.ts b/server/utils/backupStudio.ts new file mode 100644 index 0000000..506dc46 --- /dev/null +++ b/server/utils/backupStudio.ts @@ -0,0 +1,46 @@ +function env(...keys: string[]) { + for (const key of keys) { + const value = String(process.env[key] || '').trim() + if (value) return value + } + return '' +} + +function looksLikeUrl(value: string) { + return /^https?:\/\//i.test(value) +} + +export function backupStudioTarget() { + const config = useRuntimeConfig() + const url = String( + config.backupStudioUrl + || env('BACKUP_STUDIO_URL', 'NUXT_BACKUP_STUDIO_URL') + ).replace(/\/$/, '') + const token = String( + config.backupStudioToken + || env('BACKUP_STUDIO_TOKEN', 'NUXT_BACKUP_STUDIO_TOKEN') + ).trim() + const misplaced = env('BACKUP_INGEST_TOKEN', 'NUXT_BACKUP_INGEST_TOKEN') + return { url, token, misplaced } +} + +export function backupIngestSecret() { + const config = useRuntimeConfig() + return String( + config.backupIngestToken + || env('BACKUP_INGEST_TOKEN', 'NUXT_BACKUP_INGEST_TOKEN', 'BACKUP_STUDIO_TOKEN', 'NUXT_BACKUP_STUDIO_TOKEN') + ).trim() +} + +export function backupStudioConfigError() { + const { url, token, misplaced } = backupStudioTarget() + if (url && token) return '' + if (!url && looksLikeUrl(misplaced)) { + return 'BACKUP_INGEST_TOKEN on AIGen is a URL. Put https://xaigen.carrgarage.com in BACKUP_STUDIO_URL, and put the same secret in BACKUP_STUDIO_TOKEN here and BACKUP_INGEST_TOKEN on xAIGen.' + } + if (!url && !token) { + return 'Set BACKUP_STUDIO_URL and BACKUP_STUDIO_TOKEN on AIGen. The token must match BACKUP_INGEST_TOKEN on xAIGen.' + } + if (!url) return 'Set BACKUP_STUDIO_URL on AIGen to https://xaigen.carrgarage.com' + return 'Set BACKUP_STUDIO_TOKEN on AIGen to the same secret as BACKUP_INGEST_TOKEN on xAIGen' +}