# CDN URL Architecture Fix - VPS Deployment This document describes the changes needed on VPS to support the new CDN URL architecture. ## Problem Previous URL structure used presigned URLs with 24-hour expiry, which doesn't work for permanent image embedding on websites. ## Solution New URL structure with direct CDN access: - `cdn.banatie.app/{org}/{proj}/img/{imageId}` - Direct MinIO access for static images - `cdn.banatie.app/{org}/{proj}/img/@{alias}` - API-mediated alias resolution - `cdn.banatie.app/{org}/{proj}/live/{scope}?prompt=...` - API-mediated live generation ## Storage Path Format ``` Old: {orgSlug}/{projectSlug}/{category}/{timestamp-filename.ext} New: {orgSlug}/{projectSlug}/img/{imageId} ``` Where `imageId` = UUID (same as `images.id` in database). ## VPS Deployment Steps ### 1. Update Caddy Configuration Add the following routing rules to your Caddy config: ```caddyfile # CDN Domain cdn.banatie.app { # UUID pattern - direct to MinIO (no extension in URL) @uuid path_regexp uuid ^/([^/]+)/([^/]+)/img/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$ handle @uuid { reverse_proxy banatie-minio:9000 { # Rewrite to bucket path header_up Host cdn.banatie.app rewrite * /banatie{uri} } } # Alias pattern (@name) - proxy to API @alias path_regexp alias ^/([^/]+)/([^/]+)/img/@(.+)$ handle @alias { reverse_proxy banatie-api:3000 { rewrite * /cdn{uri} } } # Live URL pattern - proxy to API @live path_regexp live ^/([^/]+)/([^/]+)/live/(.+)$ handle @live { reverse_proxy banatie-api:3000 { rewrite * /cdn{uri} } } # Fallback for other patterns handle { reverse_proxy banatie-minio:9000 { header_up Host cdn.banatie.app rewrite * /banatie{uri} } } } ``` ### 2. Update Environment Variables Add to `/opt/banatie/.env`: ```env CDN_BASE_URL=https://cdn.banatie.app ``` ### 3. Reset Database and MinIO Storage Since this is a breaking change to the storage path format: ```bash # Stop services cd /opt/banatie docker compose down # Clean database (WARNING: deletes all data) rm -rf /opt/banatie/data/postgres/* # Clean MinIO storage (WARNING: deletes all files) rm -rf /opt/banatie/data/minio/drive{1,2,3,4}/* # Rebuild and start services docker compose up -d --build ``` ### 4. Run Storage Initialization After rebuild, the `banatie-storage-init` container will: 1. Create the `banatie` bucket 2. Configure service user with readwrite access 3. Enable public anonymous download access for CDN Verify public access is enabled: ```bash docker exec banatie-minio mc anonymous get local/banatie # Should show: Access permission for `local/banatie` is `download` ``` ## Verification ### Test Direct UUID Access ```bash # After generating an image, get its UUID from database or API response # Then test direct CDN access: curl -I "https://cdn.banatie.app/{orgSlug}/{projectSlug}/img/{uuid}" # Expected: HTTP 200 with Content-Type: image/png (or similar) ``` ### Test Alias Resolution ```bash # Assign an alias to an image via API, then test: curl -I "https://cdn.banatie.app/{orgSlug}/{projectSlug}/img/@hero" # Expected: HTTP 200 (API resolves alias and streams image) ``` ### Test Live URL Generation ```bash curl -I "https://cdn.banatie.app/{orgSlug}/{projectSlug}/live/test?prompt=mountain" # Expected: HTTP 200 (generates or returns cached image) ``` ## Rollback If issues occur: 1. Revert code changes 2. Rebuild API container 3. Regenerate any images (old storage paths won't work) ## Notes - `filename = image.id` (UUID) ensures consistent identification across DB, storage, and URLs - Files are stored without extension; Content-Type is served from MinIO metadata - Cloudflare caching can be enabled for UUID patterns (see url-fix-cloudflare-site.md)