version: "3.8"

# Dokploy Blueprint notes:
# - This stack is intentionally self-contained for one-click deployments.
# - The first deployment is slower because bootstrap downloads Shopware into a persistent volume.
# - Shopware source code and generated JWT keys live in named volumes so redeploys remain stable.
# - Failed messenger jobs remain in the `failed` queue for manual retry via Shopware console commands.

x-shopware-env: &shopware-env
  APP_ENV: "${APP_ENV:-prod}"
  APP_SECRET: "${APP_SECRET}"
  INSTANCE_ID: "${INSTANCE_ID}"
  APP_URL: "${APP_URL}"
  ENABLE_DEMO_DATA: "${ENABLE_DEMO_DATA:-1}"
  TRUSTED_PROXIES: "${TRUSTED_PROXIES:-private_ranges}"
  SYMFONY_TRUSTED_PROXIES: "${SYMFONY_TRUSTED_PROXIES:-private_ranges}"
  SYMFONY_TRUSTED_HEADERS: "${SYMFONY_TRUSTED_HEADERS:-x-forwarded-for,x-forwarded-host,x-forwarded-proto,x-forwarded-port,x-forwarded-prefix}"
  DATABASE_HOST: "database"
  DATABASE_PORT: "3306"
  DATABASE_NAME: "${DATABASE_NAME:-shopware}"
  DATABASE_USER: "${DATABASE_USER:-shopware}"
  DATABASE_PASSWORD: "${DATABASE_PASSWORD}"
  DATABASE_URL: "${DATABASE_URL}"
  REDIS_HOST: "redis"
  REDIS_PORT: "6379"
  REDIS_PASSWORD: "${REDIS_PASSWORD}"
  MAILER_DSN: "${MAILER_DSN:-null://localhost}"
  LOCK_DSN: "${LOCK_DSN}"
  MESSENGER_TRANSPORT_DSN: "${MESSENGER_TRANSPORT_DSN:-doctrine://default?queue_name=async}"
  MESSENGER_TRANSPORT_LOW_PRIORITY_DSN: "${MESSENGER_TRANSPORT_LOW_PRIORITY_DSN:-doctrine://default?queue_name=low_priority}"
  MESSENGER_TRANSPORT_FAILURE_DSN: "${MESSENGER_TRANSPORT_FAILURE_DSN:-doctrine://default?queue_name=failed}"
  COMPOSER_PLUGIN_LOADER: "${COMPOSER_PLUGIN_LOADER:-1}"
  SHOPWARE_HTTP_CACHE_ENABLED: "${SHOPWARE_HTTP_CACHE_ENABLED:-1}"
  SHOPWARE_HTTP_DEFAULT_TTL: "${SHOPWARE_HTTP_DEFAULT_TTL:-7200}"
  INSTALL_LOCALE: "${INSTALL_LOCALE:-en-GB}"
  INSTALL_CURRENCY: "${INSTALL_CURRENCY:-EUR}"
  INSTALL_ADMIN_USERNAME: "${INSTALL_ADMIN_USERNAME:-admin}"
  INSTALL_ADMIN_PASSWORD: "${INSTALL_ADMIN_PASSWORD}"
  PHP_SESSION_HANDLER: "${PHP_SESSION_HANDLER:-redis}"
  PHP_SESSION_SAVE_PATH: "${PHP_SESSION_SAVE_PATH}"
  PHP_SESSION_COOKIE_LIFETIME: "${PHP_SESSION_COOKIE_LIFETIME:-0}"
  PHP_SESSION_GC_MAXLIFETIME: "${PHP_SESSION_GC_MAXLIFETIME:-1440}"
  PHP_MAX_UPLOAD_SIZE: "${PHP_MAX_UPLOAD_SIZE:-128M}"
  PHP_MAX_EXECUTION_TIME: "${PHP_MAX_EXECUTION_TIME:-300}"
  PHP_MEMORY_LIMIT: "${PHP_MEMORY_LIMIT:-512M}"
  FPM_PM: "${FPM_PM:-dynamic}"
  FPM_PM_MAX_CHILDREN: "${FPM_PM_MAX_CHILDREN:-20}"
  FPM_PM_START_SERVERS: "${FPM_PM_START_SERVERS:-4}"
  FPM_PM_MIN_SPARE_SERVERS: "${FPM_PM_MIN_SPARE_SERVERS:-2}"
  FPM_PM_MAX_SPARE_SERVERS: "${FPM_PM_MAX_SPARE_SERVERS:-6}"
  BLUE_GREEN_DEPLOYMENT: "${BLUE_GREEN_DEPLOYMENT:-0}"

x-shopware-runtime-volumes: &shopware-runtime-volumes
  - shopware-code:/var/www/html
  - shopware-secrets:/run/shopware-secrets
  - files:/var/www/html/files
  - theme:/var/www/html/public/theme
  - media:/var/www/html/public/media
  - thumbnail:/var/www/html/public/thumbnail
  - sitemap:/var/www/html/public/sitemap

services:
  database:
    image: mysql:8.4
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: "${DATABASE_NAME:-shopware}"
      MYSQL_USER: "${DATABASE_USER:-shopware}"
      MYSQL_PASSWORD: "${DATABASE_PASSWORD}"
      MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    healthcheck:
      test:
        - CMD-SHELL
        - mysqladmin ping -h 127.0.0.1 -uroot -p$${MYSQL_ROOT_PASSWORD} --silent
      interval: 10s
      timeout: 5s
      retries: 20
      start_period: 30s
    volumes:
      - database-data:/var/lib/mysql

  redis:
    image: redis:7.4-alpine
    restart: unless-stopped
    environment:
      REDIS_PASSWORD: "${REDIS_PASSWORD}"
    command:
      - redis-server
      - --appendonly
      - "yes"
      - --requirepass
      - "${REDIS_PASSWORD}"
    healthcheck:
      test:
        - CMD-SHELL
        - redis-cli -a "$${REDIS_PASSWORD}" ping | grep -q PONG
      interval: 10s
      timeout: 5s
      retries: 20
    volumes:
      - redis-data:/data

  bootstrap:
    image: ${SHOPWARE_CLI_IMAGE:-shopware/shopware-cli:0.14.3-php-8.4}
    restart: "no"
    environment:
      <<: *shopware-env
      COMPOSER_ALLOW_SUPERUSER: "1"
      COMPOSER_HOME: "/tmp/composer"
      SHOPWARE_VERSION: "${SHOPWARE_VERSION:-6.7.12.1}"
      SHOPWARE_DOCKER_VERSION: "${SHOPWARE_DOCKER_VERSION:-0.3.0}"
      ENABLE_DEMO_DATA: "${ENABLE_DEMO_DATA:-1}"
      DEMO_DATA_VERSION: "${DEMO_DATA_VERSION:-2.1.0}"
    depends_on:
      database:
        condition: service_healthy
      redis:
        condition: service_healthy
    entrypoint:
      - /bin/sh
      - -ec
      - |
        mkdir -p /run/shopware-secrets /var/www/html

        if [ ! -s /run/shopware-secrets/jwt-private.pem ] || [ ! -s /run/shopware-secrets/jwt-public.pem ]; then
          echo "Generating JWT keypair."
          openssl genpkey -algorithm RSA -out /run/shopware-secrets/jwt-private.pem -pkeyopt rsa_keygen_bits:2048
          openssl rsa -pubout -in /run/shopware-secrets/jwt-private.pem -out /run/shopware-secrets/jwt-public.pem
          chmod 600 /run/shopware-secrets/jwt-private.pem
          chmod 644 /run/shopware-secrets/jwt-public.pem
        fi

        export JWT_PRIVATE_KEY="$$(cat /run/shopware-secrets/jwt-private.pem)"
        export JWT_PUBLIC_KEY="$$(cat /run/shopware-secrets/jwt-public.pem)"

        if [ -f /var/www/html/composer.json ] && [ -f /var/www/html/bin/console ] && [ -f /var/www/html/vendor/autoload.php ]; then
          echo "Shopware source already exists. Skipping bootstrap."
          exit 0
        fi

        if find /var/www/html -mindepth 1 -maxdepth 1 -print -quit | grep -q .; then
          echo "Incomplete Shopware source detected. Cleaning bootstrap volume."
          find /var/www/html -mindepth 1 -maxdepth 1 -exec rm -rf {} +
        fi

        tmp_dir="$$(mktemp -d)"

        # Composer >=2.9 refuses to install pinned versions that are affected by
        # security advisories published later. Keep the pinned bootstrap deterministic.
        composer config -g audit.block-insecure false 2>/dev/null || true

        composer create-project --no-interaction --prefer-dist "shopware/production:${SHOPWARE_VERSION}" "$$tmp_dir"
        cd "$$tmp_dir"
        composer require --no-interaction "shopware/docker:${SHOPWARE_DOCKER_VERSION}"

        if [ "${ENABLE_DEMO_DATA}" = "1" ]; then
          composer require --no-interaction --prefer-dist "swag/demo-data:${DEMO_DATA_VERSION}"
        fi

        shopware-cli project ci "$$tmp_dir"
        cp -a "$$tmp_dir"/. /var/www/html/
    volumes:
      - shopware-code:/var/www/html
      - shopware-secrets:/run/shopware-secrets

  init-permissions:
    image: alpine:3.21
    restart: "no"
    command:
      - sh
      - -ec
      - |
        mkdir -p \
          /var/www/html/files \
          /var/www/html/public/theme \
          /var/www/html/public/media \
          /var/www/html/public/thumbnail \
          /var/www/html/public/sitemap \
          /run/shopware-secrets
        chown -R 82:82 /var/www/html /run/shopware-secrets
    depends_on:
      bootstrap:
        condition: service_completed_successfully
    volumes: *shopware-runtime-volumes

  init:
    image: ${SHOPWARE_BASE_IMAGE:-shopware/docker-base:8.4.19-nginx}
    restart: "no"
    entrypoint:
      - /bin/sh
      - -ec
      - |
        export JWT_PRIVATE_KEY="$(cat /run/shopware-secrets/jwt-private.pem)"
        export JWT_PUBLIC_KEY="$(cat /run/shopware-secrets/jwt-public.pem)"

        /setup

        if [ "${ENABLE_DEMO_DATA:-1}" != "1" ]; then
          exit 0
        fi

        cd /var/www/html

        echo "Demo data enabled. Refreshing plugin list."
        php bin/console -n plugin:refresh

        if [ ! -d /var/www/html/custom/plugins/SwagPlatformDemoData ]; then
          echo "SwagPlatformDemoData is not present in the volume."
          echo "Continuing without demo data activation."
          exit 0
        fi

        if ! plugin_state="$$(
          php bin/console -n plugin:list --json | php -r '
            $$plugins = json_decode(stream_get_contents(STDIN), true);
            if (!is_array($$plugins)) {
              fwrite(STDERR, "Unable to parse plugin list\n");
              exit(2);
            }

            foreach ($$plugins as $$plugin) {
              if (($$plugin["name"] ?? null) !== "SwagPlatformDemoData") {
                continue;
              }

              $$installed = ($$plugin["installed"] ?? false) ? "1" : "0";
              $$active = ($$plugin["active"] ?? false) ? "1" : "0";

              echo $$installed . ":" . $$active;
              exit(0);
            }

            fwrite(STDERR, "SwagPlatformDemoData not found in plugin list\n");
            exit(1);
          '
        )"; then
          echo "Unable to resolve SwagPlatformDemoData state."
          echo "Continuing without demo data activation."
          exit 0
        fi

        case "$${plugin_state}" in
          "0:0")
            echo "Installing and activating SwagPlatformDemoData."
            php bin/console -n plugin:install --activate SwagPlatformDemoData
            ;;
          "1:0")
            echo "Activating existing SwagPlatformDemoData plugin."
            php bin/console -n plugin:activate SwagPlatformDemoData
            ;;
          "1:1")
            echo "SwagPlatformDemoData is already active."
            ;;
          *)
            echo "Unexpected SwagPlatformDemoData state: $${plugin_state}"
            echo "Continuing without demo data activation."
            exit 0
            ;;
        esac
    environment: *shopware-env
    depends_on:
      init-permissions:
        condition: service_completed_successfully
      database:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes: *shopware-runtime-volumes

  shopware:
    image: ${SHOPWARE_BASE_IMAGE:-shopware/docker-base:8.4.19-nginx}
    restart: unless-stopped
    expose:
      - "8000"
    entrypoint:
      - /bin/sh
      - -ec
      - |
        export JWT_PRIVATE_KEY="$(cat /run/shopware-secrets/jwt-private.pem)"
        export JWT_PUBLIC_KEY="$(cat /run/shopware-secrets/jwt-public.pem)"
        exec /usr/bin/supervisord -c /etc/supervisord.conf
    environment: *shopware-env
    depends_on:
      init:
        condition: service_completed_successfully
    volumes: *shopware-runtime-volumes

  worker:
    image: ${SHOPWARE_BASE_IMAGE:-shopware/docker-base:8.4.19-nginx}
    restart: unless-stopped
    entrypoint:
      - /bin/sh
      - -ec
      - |
        export JWT_PRIVATE_KEY="$(cat /run/shopware-secrets/jwt-private.pem)"
        export JWT_PUBLIC_KEY="$(cat /run/shopware-secrets/jwt-public.pem)"
        exec php bin/console messenger:consume async low_priority --time-limit=300 --memory-limit=512M
    environment: *shopware-env
    depends_on:
      init:
        condition: service_completed_successfully
    volumes: *shopware-runtime-volumes

  scheduler:
    image: ${SHOPWARE_BASE_IMAGE:-shopware/docker-base:8.4.19-nginx}
    restart: unless-stopped
    entrypoint:
      - /bin/sh
      - -ec
      - |
        export JWT_PRIVATE_KEY="$(cat /run/shopware-secrets/jwt-private.pem)"
        export JWT_PUBLIC_KEY="$(cat /run/shopware-secrets/jwt-public.pem)"
        exec php bin/console scheduled-task:run
    environment: *shopware-env
    depends_on:
      init:
        condition: service_completed_successfully
    volumes: *shopware-runtime-volumes

volumes:
  database-data:
  redis-data:
  shopware-code:
  shopware-secrets:
  files:
  theme:
  media:
  thumbnail:
  sitemap:
