banatie-service/docs/url-fix-vps-site.md

3.8 KiB

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:

# 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:

CDN_BASE_URL=https://cdn.banatie.app

3. Reset Database and MinIO Storage

Since this is a breaking change to the storage path format:

# 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:

docker exec banatie-minio mc anonymous get local/banatie
# Should show: Access permission for `local/banatie` is `download`

Verification

Test Direct UUID Access

# 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

# 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

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)