services:
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      - VIVD_CADDY_PRIMARY_HOST=${VIVD_CADDY_PRIMARY_HOST}
      - VIVD_CADDY_ACME_EMAIL=${VIVD_CADDY_ACME_EMAIL:-}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
      - caddy_sites:/etc/caddy/sites.d
      - caddy_runtime_routes:/etc/caddy/runtime.d:ro
      - published_sites:/srv/published:ro
    depends_on:
      - backend
      - minio
    networks:
      - vivd-network

  backend:
    image: ghcr.io/vivd-studio/vivd-server:${VIVD_SELFHOST_IMAGE_TAG:-latest}
    pull_policy: always
    restart: unless-stopped
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-vivd}
      - PORT=3000
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
      - OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
      - GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
      - GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT:-}
      - VERTEX_LOCATION=${VERTEX_LOCATION:-}
      - GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS:-}
      - GOOGLE_APPLICATION_CREDENTIALS_JSON=${GOOGLE_APPLICATION_CREDENTIALS_JSON:-}
      - VIVD_GOOGLE_APPLICATION_CREDENTIALS_PATH=${VIVD_GOOGLE_APPLICATION_CREDENTIALS_PATH:-}
      - OPENCODE_MODEL_STANDARD=${OPENCODE_MODEL_STANDARD:-}
      - OPENCODE_MODEL_ADVANCED=${OPENCODE_MODEL_ADVANCED:-}
      - OPENCODE_MODEL_PRO=${OPENCODE_MODEL_PRO:-}
      - DOMAIN=${DOMAIN}
      - BETTER_AUTH_URL=${BETTER_AUTH_URL:-}
      - VIVD_SCRATCH_CREATION_MODE=${VIVD_SCRATCH_CREATION_MODE:-studio_astro}
      - VIVD_CADDY_TLS_MODE=${VIVD_CADDY_TLS_MODE:-off}
      - VIVD_PUBLISH_INCLUDE_WWW_ALIAS=${VIVD_PUBLISH_INCLUDE_WWW_ALIAS:-false}
      - VIVD_PUBLIC_DOCS_BASE_URL=${VIVD_PUBLIC_DOCS_BASE_URL:-https://docs.vivd.studio}
      - TRUSTED_DOMAINS=${TRUSTED_DOMAINS:-}
      - TENANT_BASE_DOMAIN=${TENANT_BASE_DOMAIN:-}
      - TENANT_DOMAIN_ROUTING_ENABLED=${TENANT_DOMAIN_ROUTING_ENABLED:-false}
      - CONTROL_PLANE_HOST=${CONTROL_PLANE_HOST:-}
      - SUPERADMIN_HOSTS=${SUPERADMIN_HOSTS:-}
      - BACKEND_URL=${BACKEND_URL:-}
      - VIVD_PUBLIC_PLUGIN_API_BASE_URL=${VIVD_PUBLIC_PLUGIN_API_BASE_URL:-}
      - PUBLISHED_DIR=/srv/published
      - CADDY_SITES_DIR=/etc/caddy/sites.d
      - CADDY_RUNTIME_ROUTES_DIR=/etc/caddy/runtime.d
      - CADDY_ADMIN_URL=http://caddy:2019
      - SCRAPER_API_KEY=${SCRAPER_API_KEY}
      - CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID:-}
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN:-}
      - VIVD_BUCKET_MODE=${VIVD_BUCKET_MODE:-local}
      - VIVD_LOCAL_S3_BUCKET=${VIVD_LOCAL_S3_BUCKET:-}
      - VIVD_LOCAL_S3_ENDPOINT_URL=${VIVD_LOCAL_S3_ENDPOINT_URL:-}
      - VIVD_LOCAL_S3_DOWNLOAD_ENDPOINT_URL=${VIVD_LOCAL_S3_DOWNLOAD_ENDPOINT_URL:-}
      - VIVD_LOCAL_S3_ACCESS_KEY=${VIVD_LOCAL_S3_ACCESS_KEY}
      - VIVD_LOCAL_S3_SECRET_KEY=${VIVD_LOCAL_S3_SECRET_KEY}
      - VIVD_LOCAL_S3_REGION=${VIVD_LOCAL_S3_REGION:-}
      - R2_ENDPOINT=${R2_ENDPOINT:-}
      - R2_BUCKET=${R2_BUCKET:-}
      - R2_ACCESS_KEY=${R2_ACCESS_KEY:-}
      - R2_SECRET_KEY=${R2_SECRET_KEY:-}
      - VIVD_S3_BUCKET=${VIVD_S3_BUCKET:-}
      - VIVD_S3_ENDPOINT_URL=${VIVD_S3_ENDPOINT_URL:-}
      - VIVD_S3_ACCESS_KEY_ID=${VIVD_S3_ACCESS_KEY_ID:-}
      - VIVD_S3_SECRET_ACCESS_KEY=${VIVD_S3_SECRET_ACCESS_KEY:-}
      - VIVD_S3_SESSION_TOKEN=${VIVD_S3_SESSION_TOKEN:-}
      - VIVD_S3_REGION=${VIVD_S3_REGION:-}
      - VIVD_S3_DOWNLOAD_ENDPOINT_URL=${VIVD_S3_DOWNLOAD_ENDPOINT_URL:-}
      - VIVD_S3_PUBLIC_BASE_URL=${VIVD_S3_PUBLIC_BASE_URL:-}
      - VIVD_EMAIL_PROVIDER=${VIVD_EMAIL_PROVIDER:-}
      - EMAIL_PROVIDER=${EMAIL_PROVIDER:-}
      - RESEND_API_KEY=${RESEND_API_KEY:-}
      - RESEND_WEBHOOK_SECRET=${RESEND_WEBHOOK_SECRET:-}
      - VIVD_EMAIL_FROM=${VIVD_EMAIL_FROM:-}
      - VIVD_FROM_EMAIL=${VIVD_FROM_EMAIL:-}
      - VIVD_SMTP_URL=${VIVD_SMTP_URL:-}
      - VIVD_SMTP_HOST=${VIVD_SMTP_HOST:-}
      - VIVD_SMTP_PORT=${VIVD_SMTP_PORT:-}
      - VIVD_SMTP_SECURE=${VIVD_SMTP_SECURE:-}
      - VIVD_SMTP_USER=${VIVD_SMTP_USER:-}
      - VIVD_SMTP_PASSWORD=${VIVD_SMTP_PASSWORD:-}
      - VIVD_SMTP_REQUIRE_TLS=${VIVD_SMTP_REQUIRE_TLS:-}
      - VIVD_SMTP_IGNORE_TLS=${VIVD_SMTP_IGNORE_TLS:-}
      - VIVD_SMTP_FROM_EMAIL=${VIVD_SMTP_FROM_EMAIL:-}
      - VIVD_SES_ACCESS_KEY_ID=${VIVD_SES_ACCESS_KEY_ID:-}
      - VIVD_SES_SECRET_ACCESS_KEY=${VIVD_SES_SECRET_ACCESS_KEY:-}
      - VIVD_SES_FROM_EMAIL=${VIVD_SES_FROM_EMAIL:-}
      - VIVD_SES_REGION=${VIVD_SES_REGION:-}
      - STUDIO_MACHINE_PROVIDER=${STUDIO_MACHINE_PROVIDER:-docker}
      - SINGLE_PROJECT_MODE=${SINGLE_PROJECT_MODE:-false}
      - VIVD_SELFHOST_UPDATE_WORKDIR=/srv/selfhost
      - VIVD_SELFHOST_UPDATE_SERVICES=backend frontend scraper
      - VIVD_SELFHOST_UPDATE_HELPER_IMAGE=${VIVD_SELFHOST_UPDATE_HELPER_IMAGE:-docker:28-cli}
      - DOCKER_STUDIO_SOCKET_PATH=${DOCKER_STUDIO_SOCKET_PATH:-/var/run/docker.sock}
      - DOCKER_STUDIO_API_BASE_URL=${DOCKER_STUDIO_API_BASE_URL:-}
      - DOCKER_STUDIO_API_VERSION=${DOCKER_STUDIO_API_VERSION:-}
      - DOCKER_STUDIO_NETWORK=${DOCKER_STUDIO_NETWORK:-vivd-network}
      - DOCKER_STUDIO_ROUTE_PREFIX=${DOCKER_STUDIO_ROUTE_PREFIX:-/_studio}
      - DOCKER_STUDIO_PUBLIC_BASE_URL=${DOCKER_STUDIO_PUBLIC_BASE_URL:-}
      - DOCKER_STUDIO_INTERNAL_PROXY_BASE_URL=${DOCKER_STUDIO_INTERNAL_PROXY_BASE_URL:-http://caddy}
      - DOCKER_STUDIO_IMAGE_REPO=${DOCKER_STUDIO_IMAGE_REPO:-}
      - DOCKER_STUDIO_IMAGE=${DOCKER_STUDIO_IMAGE:-}
      - DOCKER_STUDIO_CPUS=${DOCKER_STUDIO_CPUS:-1}
      - DOCKER_STUDIO_MEMORY_MB=${DOCKER_STUDIO_MEMORY_MB:-2048}
    volumes:
      - backend_data:/app/projects
      - opencode_data:/root/.local/share/opencode/storage
      - published_sites:/srv/published
      - caddy_sites:/etc/caddy/sites.d
      - caddy_runtime_routes:/etc/caddy/runtime.d
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./:/srv/selfhost:ro
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      postgres:
        condition: service_healthy
      scraper:
        condition: service_healthy
      minio:
        condition: service_started
    networks:
      - vivd-network

  frontend:
    image: ghcr.io/vivd-studio/vivd-ui:${VIVD_SELFHOST_IMAGE_TAG:-latest}
    pull_policy: always
    restart: unless-stopped
    depends_on:
      - backend
    networks:
      - vivd-network

  scraper:
    image: ghcr.io/vivd-studio/vivd-scraper:${VIVD_SELFHOST_IMAGE_TAG:-latest}
    pull_policy: always
    restart: unless-stopped
    environment:
      - PORT=3001
      - MAX_BROWSERS=${MAX_BROWSERS:-2}
      - MAX_CONCURRENT_SCRAPES=${MAX_CONCURRENT_SCRAPES:-2}
      - SCRAPER_API_KEY=${SCRAPER_API_KEY}
      - OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
      - PROXY_HOST=${PROXY_HOST:-}
      - PROXY_PORT=${PROXY_PORT:-823}
      - PROXY_USERNAME=${PROXY_USERNAME:-}
      - PROXY_PASSWORD=${PROXY_PASSWORD:-}
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3001/health').then((res) => { if (!res.ok) process.exit(1); }).catch(() => process.exit(1))"]
      interval: 15s
      timeout: 10s
      retries: 5
      start_period: 30s
    networks:
      - vivd-network

  postgres:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB:-vivd}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-vivd}"]
      interval: 5s
      timeout: 5s
      retries: 5
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - vivd-network

  minio:
    image: ${VIVD_LOCAL_S3_IMAGE:-minio/minio:latest}
    restart: unless-stopped
    command: server /data --console-address :9001
    environment:
      MINIO_ROOT_USER: ${VIVD_LOCAL_S3_ACCESS_KEY}
      MINIO_ROOT_PASSWORD: ${VIVD_LOCAL_S3_SECRET_KEY}
    volumes:
      - minio_data:/data
    networks:
      - vivd-network

  minio-init:
    image: ${VIVD_LOCAL_S3_MC_IMAGE:-minio/mc:latest}
    restart: "no"
    profiles:
      - setup
    depends_on:
      - minio
    environment:
      - VIVD_LOCAL_S3_BUCKET=${VIVD_LOCAL_S3_BUCKET:-}
      - VIVD_LOCAL_S3_ENDPOINT_URL=${VIVD_LOCAL_S3_ENDPOINT_URL:-}
      - VIVD_LOCAL_S3_ACCESS_KEY=${VIVD_LOCAL_S3_ACCESS_KEY}
      - VIVD_LOCAL_S3_SECRET_KEY=${VIVD_LOCAL_S3_SECRET_KEY}
    entrypoint: >
      /bin/sh -c '
      until mc alias set vivd "$${VIVD_LOCAL_S3_ENDPOINT_URL:-http://minio:9000}" "$$VIVD_LOCAL_S3_ACCESS_KEY" "$$VIVD_LOCAL_S3_SECRET_KEY" >/dev/null 2>&1; do
        sleep 1
      done &&
      mc mb --ignore-existing "vivd/$${VIVD_LOCAL_S3_BUCKET:-vivd}"
      '
    networks:
      - vivd-network

volumes:
  backend_data:
  opencode_data:
  published_sites:
  caddy_data:
  caddy_config:
  caddy_sites:
  caddy_runtime_routes:
  postgres_data:
  minio_data:

networks:
  vivd-network:
    driver: bridge
