services:
  k3s-server:
    image: ${K3S_IMAGE:-rancher/k3s:v1.36.2-k3s1}
    container_name: k3s-server
    command:
      - server
      - --tls-san=127.0.0.1
      - --tls-san=k3s-server
      - --tls-san=${K3S_API_HOST:-127.0.0.1}   # внешний IP/домен для доступа к apiserver извне
      - --disable=traefik
    privileged: true
    cgroup: host          # kubelet в cgroup v2 (Ubuntu 22.04+/RedOS)
    restart: unless-stopped
    environment:
      K3S_TOKEN: ${K3S_TOKEN:-supersecrettoken}
      K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
      K3S_KUBECONFIG_MODE: "644"
    #ports:
    #  - "6443:6443"   # kube-apiserver — доступен извне; ОБЯЗАТЕЛЬНО ограничь фаерволом до доверенных IP
    #  - "80:80"        # ingress http  — задействуются после установки ingress-контроллера
    #  - "443:443"      # ingress https
    volumes:
      - k3s-server-data:/var/lib/rancher/k3s   # etcd/state, образы, манифесты — только named volume
      - ./k3s:/output:z                 # отсюда забираем контекст k8s (:z — SELinux label для RedOS)
    tmpfs:
      - /run
      - /var/run
    ulimits:
      nproc: 65535
      nofile:
        soft: 65535
        hard: 65535
    healthcheck:
      test: ["CMD", "kubectl", "get", "--raw=/readyz"]
      interval: 10s
      timeout: 5s
      retries: 30
      start_period: 30s

  k3s-worker:
    image: ${K3S_IMAGE:-rancher/k3s:v1.36.2-k3s1}
    container_name: k3s-worker
    command:
      - agent
    privileged: true
    cgroup: host          # kubelet в cgroup v2 (Ubuntu 22.04+/RedOS)
    restart: unless-stopped
    depends_on:
      k3s-server:
        condition: service_healthy
    environment:
      K3S_URL: https://k3s-server:6443
      K3S_TOKEN: ${K3S_TOKEN:-supersecrettoken}
    volumes:
      - k3s-worker-data:/var/lib/rancher/k3s
    tmpfs:
      - /run
      - /var/run
    ulimits:
      nproc: 65535
      nofile:
        soft: 65535
        hard: 65535

  gitea:
    image: ${GITEA_IMAGE:-gitea/gitea:1.22}
    container_name: gitea
    restart: unless-stopped
    environment:
      USER_UID: 1000
      USER_GID: 1000
      GITEA__server__ROOT_URL: ${GITEA_ROOT_URL:-http://localhost:3000/}
      GITEA__server__SSH_PORT: ${GITEA_SSH_PORT:-2222}
    ports:
      - "3000:3000"                       # web UI (80/443 заняты k3s)
      - "${GITEA_SSH_PORT:-2222}:22"      # git over ssh
    volumes:
      - ./gitea-data:/data:z            # :z — SELinux label для RedOS
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:3000/api/healthz"]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 20s

  # ---------------------------------------------------------------------------
  # Инфраструктурные зависимости apps/django (sarex-backend).
  # Django деплоится в k3s и ходит сюда — про связность k3s→compose см. README.
  # Креды/версии взяты из sarex-backend/docker-compose.yaml и apps/django/.env.example.
  # ---------------------------------------------------------------------------
  postgres:
    image: ${POSTGRES_IMAGE:-postgres:12-alpine}
    container_name: postgres
    restart: unless-stopped
    environment:
      # суперпользователь (администрирование)
      POSTGRES_DB: ${SAREX_POSTGRES_DB:-sarex_db}
      POSTGRES_USER: ${SAREX_POSTGRES_USER:-sarex}
      POSTGRES_PASSWORD: ${SAREX_POSTGRES_PASSWORD:-sarex}
      # роли приложений с паролями — читаются init-скриптом 00-init.sh
      DJANGO_DB: ${SAREX_POSTGRES_DB:-sarex_db}
      DJANGO_DB_USER: ${SAREX_DJANGO_DB_USER:-django}
      DJANGO_DB_PASSWORD: ${SAREX_DJANGO_DB_PASSWORD:-django-secret}
      PROC_DB: ${SAREX_PROCESSING_DB:-workflow_db}
      PROC_DB_USER: ${SAREX_PROCESSING_DB_USER:-processing}
      PROC_DB_PASSWORD: ${SAREX_PROCESSING_DB_PASSWORD:-processing-secret}
    volumes:
      - sarex-postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${SAREX_POSTGRES_USER:-sarex} -d ${SAREX_POSTGRES_DB:-sarex_db}"]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 10s

  # Одноразовый init: на каждое приложение заводит роль (LOGIN+пароль) и базу
  # данных с этим владельцем. Идемпотентно (по образцу minio-init).
  postgres-init:
    image: ${POSTGRES_IMAGE:-postgres:12-alpine}
    depends_on:
      postgres:
        condition: service_healthy
    entrypoint:
      - /bin/sh
      - -c
      - |
        set -e
        until psql -h postgres -U "$$SUPER_USER" -d "$$SUPER_DB" -c 'SELECT 1' >/dev/null 2>&1; do
          echo 'waiting for postgres...'; sleep 2;
        done

        q() { psql -v ON_ERROR_STOP=1 -h postgres -U "$$SUPER_USER" -d "$$SUPER_DB" "$$@"; }

        # provision <db> <user> <password>
        provision() {
          q -tc "SELECT 1 FROM pg_roles WHERE rolname='$$2'" | grep -q 1 \
            || q -c "CREATE ROLE \"$$2\" LOGIN PASSWORD '$$3'"
          q -c "ALTER ROLE \"$$2\" WITH LOGIN PASSWORD '$$3'"
          q -tc "SELECT 1 FROM pg_database WHERE datname='$$1'" | grep -q 1 \
            || q -c "CREATE DATABASE \"$$1\" OWNER \"$$2\""
          q -c "ALTER DATABASE \"$$1\" OWNER TO \"$$2\""
          q -c "GRANT ALL PRIVILEGES ON DATABASE \"$$1\" TO \"$$2\""
          echo "db $$1 + user $$2 ready"
        }

        provision "$$DJANGO_DB"    "$$DJANGO_USER"    "$$DJANGO_PASSWORD"
        provision "$$PROC_DB"      "$$PROC_USER"      "$$PROC_PASSWORD"
        provision "$$BIM_DB"       "$$BIM_USER"       "$$BIM_PASSWORD"
        provision "$$WORKSPACE_DB" "$$WORKSPACE_USER" "$$WORKSPACE_PASSWORD"

        # Расширения PostgreSQL в базе django: CREATE EXTENSION может только
        # суперпользователь. Django-миграции (CreateExtension) идут с IF NOT
        # EXISTS → после этого станут no-op и не упадут на правах.
        for ext in $$DJANGO_EXTENSIONS; do
          q -c "CREATE EXTENSION IF NOT EXISTS \"$$ext\"" && echo "extension $$ext ready in $$DJANGO_DB"
        done
    environment:
      PGPASSWORD: ${SAREX_POSTGRES_PASSWORD:-sarex}
      SUPER_USER: ${SAREX_POSTGRES_USER:-sarex}
      SUPER_DB: ${SAREX_POSTGRES_DB:-sarex_db}
      # django (использует основную базу SAREX_POSTGRES_DB)
      DJANGO_DB: ${SAREX_POSTGRES_DB:-sarex_db}
      DJANGO_USER: ${SAREX_DJANGO_DB_USER:-django}
      DJANGO_PASSWORD: ${SAREX_DJANGO_DB_PASSWORD:-django-secret}
      DJANGO_EXTENSIONS: ${SAREX_DJANGO_DB_EXTENSIONS:-ltree}
      # processing
      PROC_DB: ${SAREX_PROCESSING_DB:-workflow_db}
      PROC_USER: ${SAREX_PROCESSING_DB_USER:-processing}
      PROC_PASSWORD: ${SAREX_PROCESSING_DB_PASSWORD:-processing-secret}
      # bim
      BIM_DB: ${SAREX_BIM_DB:-bim_db}
      BIM_USER: ${SAREX_BIM_DB_USER:-bim}
      BIM_PASSWORD: ${SAREX_BIM_DB_PASSWORD:-bim-secret}
      # workspace
      WORKSPACE_DB: ${SAREX_WORKSPACE_DB:-workspace_db}
      WORKSPACE_USER: ${SAREX_WORKSPACE_DB_USER:-workspace}
      WORKSPACE_PASSWORD: ${SAREX_WORKSPACE_DB_PASSWORD:-workspace-secret}
    restart: "no"

  redis:
    image: ${SAREX_REDIS_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/redis:7.0.10}
    container_name: redis
    restart: unless-stopped
    # protected-mode no: redis только во внутренней docker-сети (порт наружу не
    # публикуется), иначе бэкенд/celery из других контейнеров отбиваются "DENIED"
    command: ["redis-server", "--save", "60", "1", "--loglevel", "warning", "--protected-mode", "no", "--stop-writes-on-bgsave-error", "no"]
    volumes:
      - sarex-redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 5s

  rabbitmq:
    image: ${SAREX_RABBITMQ_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/rabbimtq:3.11.23}
    container_name: rabbitmq
    restart: unless-stopped
    environment:
      RABBITMQ_VHOST: ${SAREX_RABBITMQ_VHOST:-api}
      RABBITMQ_USERNAME: ${SAREX_RABBITMQ_USER:-rabbit}
      RABBITMQ_PASSWORD: ${SAREX_RABBITMQ_PASSWORD:-rabbit}
      RABBITMQ_DISK_FREE_ABSOLUTE_LIMIT: 200MB
    volumes:
      - sarex-rabbitmq-data:/bitnami/rabbitmq/mnesia
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
      interval: 15s
      timeout: 10s
      retries: 12
      start_period: 30s

  minio:
    image: ${SAREX_MINIO_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/minio:250724}
    container_name: minio
    restart: unless-stopped
    command: ["server", "/data", "--console-address", ":9001"]
    environment:
      MINIO_ROOT_USER: ${SAREX_MINIO_ROOT_USER:-minioadmin}
      MINIO_ROOT_PASSWORD: ${SAREX_MINIO_ROOT_PASSWORD:-minioadmin}
      # Консоль отдаётся через верхний nginx с корня minio-домена
      MINIO_BROWSER_REDIRECT_URL: ${SAREX_MINIO_CONSOLE_URL:-https://minio.sarex.local}
    volumes:
      - sarex-minio-data:/data

  # Одноразовый init: ждёт MinIO и на каждое приложение заводит бакет +
  # отдельного пользователя со scoped-политикой (доступ только к своему бакету).
  minio-init:
    image: ${SAREX_MC_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/mc:250724}
    depends_on:
      - minio
    entrypoint:
      - /bin/sh
      - -c
      - |
        until mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD"; do
          echo 'waiting for minio...'; sleep 2;
        done

        # provision <bucket> <user> <password> <policy>
        provision() {
          mc mb -p "local/$$1"
          mc admin user add local "$$2" "$$3"
          printf '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::%s","arn:aws:s3:::%s/*"]}]}' "$$1" "$$1" > /tmp/policy.json
          mc admin policy create local "$$4" /tmp/policy.json || true
          mc admin policy attach local "$$4" --user "$$2" || true
          echo "bucket $$1 + user $$2 ready"
        }

        # Единый бакет на все приложения (processing/measurements тоже сюда)
        provision "$$MINIO_BUCKET"      "$$MINIO_APP_USER"  "$$MINIO_APP_PASSWORD"  "$$MINIO_APP_POLICY"
    environment:
      MINIO_ROOT_USER: ${SAREX_MINIO_ROOT_USER:-minioadmin}
      MINIO_ROOT_PASSWORD: ${SAREX_MINIO_ROOT_PASSWORD:-minioadmin}
      # единый бакет/пользователь приложения
      MINIO_BUCKET: ${SAREX_MINIO_BUCKET:-sarex-media-storage}
      MINIO_APP_USER: ${SAREX_MINIO_APP_USER:-sarex-app}
      MINIO_APP_PASSWORD: ${SAREX_MINIO_APP_PASSWORD:-sarex-app-secret}
      MINIO_APP_POLICY: ${SAREX_MINIO_APP_POLICY:-sarex-app-rw}
    restart: "no"

  # ---------------------------------------------------------------------------
  # Приложение sarex (backend + celery + frontend).
  # Образы приватные (cr.yandex) — нужен `docker login cr.yandex`.
  # Для локальной сборки backend раскомментируй build (репозиторий — соседний).
  # ---------------------------------------------------------------------------
  backend:
    image: ${SAREX_BACKEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/backend:production_80d1d847}
    # build:
    #   context: ../../generic/sarex-backend
    #   dockerfile: Dockerfile
    container_name: sarex-backend
    restart: unless-stopped
    environment: &sarex-backend-env
      ALLOWED_HOSTS: "*"
      DJANGO_SETTINGS_MODULE: config.settings.production
      JWT_PRIVATE_KEY: ${SAREX_JWT_PRIVATE_KEY:-JWT}
      JWT_PUBLIC_KEY: ${SAREX_JWT_PUBLIC_KEY:-JPK}
      DJANGO_POSTGRES_HOST: postgres
      DJANGO_POSTGRES_DATABASE: ${SAREX_POSTGRES_DB:-sarex_db}
      DJANGO_POSTGRES_USER: ${SAREX_DJANGO_DB_USER:-django}
      DJANGO_POSTGRES_PASSWORD: ${SAREX_DJANGO_DB_PASSWORD:-django-secret}
      # celery: брокер rabbitmq + result backend redis (pydantic env_prefix CELERY_*)
      CELERY_RABBITMQ_HOST: rabbitmq
      CELERY_RABBITMQ_USER: ${SAREX_RABBITMQ_USER:-rabbit}
      CELERY_RABBITMQ_PASSWORD: ${SAREX_RABBITMQ_PASSWORD:-rabbit}
      CELERY_RABBITMQ_VHOST: ${SAREX_RABBITMQ_VHOST:-api}
      CELERY_REDIS_HOST: redis
      # S3 / объектное хранилище → MinIO (сервис minio)
      S3_HOST: http://minio:9000
      AWS_S3_ENDPOINT_URL: http://minio:9000
      S3_LOGIN: ${SAREX_MINIO_APP_USER:-sarex-app}
      S3_PASSWORD: ${SAREX_MINIO_APP_PASSWORD:-sarex-app-secret}
      S3_BUCKET: ${SAREX_MINIO_BUCKET:-sarex-media-storage}
      S3_REGION: ${SAREX_MINIO_REGION:-us-east-1}
      # Сервис measurements (MeasurementSettings, env_prefix MEASUREMENTS_)
      MEASUREMENTS_HOST: ${SAREX_MEASUREMENTS_HOST:-http://measurements:8000/}
    tty: true
    ports:
      - "8000:8000"   # HTTP API (uWSGI http-socket), migrate выполняется в entrypoint
    volumes:
      # свой uwsgi.ini поверх образного: http-socket + 4 воркера (:z — SELinux/RedOS)
      - ./backend/uwsgi.ini:/opt/sarex/uwsgi.ini:ro,z
      # production.py с включённой классической JWT-аутентификацией
      - ./backend/production.py:/opt/sarex/config/settings/production.py:ro,z
    depends_on:
      postgres:
        condition: service_healthy
      postgres-init:
        condition: service_completed_successfully
      redis:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
      minio-init:
        condition: service_completed_successfully

  celery:
    image: ${SAREX_BACKEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/backend:production_80d1d847}
    container_name: sarex-celery
    restart: unless-stopped
    entrypoint: celery -A config worker -l info -E -Q default -n default_worker.%h --concurrency=1 -B
    environment:
      <<: *sarex-backend-env
    volumes:
      - ./backend/production.py:/opt/sarex/config/settings/production.py:ro,z
    depends_on:
      postgres:
        condition: service_healthy
      postgres-init:
        condition: service_completed_successfully
      redis:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
      minio-init:
        condition: service_completed_successfully

  frontend:
    image: ${SAREX_FRONTEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/frontend:contour_42138486}
    container_name: sarex-frontend
    restart: unless-stopped
    ports:
      - "8080:80"     # web UI
    depends_on:
      - backend

  # ---------------------------------------------------------------------------
  # Приложение processing (workflows-api + workflows-frontend).
  # Образы приватные (cr.yandex). БД — отдельная workflow_db в общем postgres.
  # Версии/env взяты из apps/processing/base/*.yaml.
  # ---------------------------------------------------------------------------
  processing-api:
    image: ${SAREX_PROCESSING_API_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/workflows-api_prod:075fc0}
    container_name: processing-api
    restart: unless-stopped
    entrypoint: ["/httpserver"]
    command: ["migrate"]          # в манифесте: /httpserver migrate (миграции + сервер на :8080)
    environment:
      HTTP_HOST: 0.0.0.0:8080
      POSTGRES_ADDRESS: postgres
      POSTGRES_PORT: "5432"
      POSTGRES_DB: ${SAREX_PROCESSING_DB:-workflow_db}
      POSTGRES_USER: ${SAREX_PROCESSING_DB_USER:-processing}
      POSTGRES_PASSWORD: ${SAREX_PROCESSING_DB_PASSWORD:-processing-secret}
      POSTGRES_POOL_SIZE: "3"
      POSTGRES_SSL_USE: "0"
      ENABLE_SQL_QUERY: "0"
      DJANGO_HOST: http://backend:8000          # django-backend в этом же compose
      PUBLIC_KEY: ${SAREX_JWT_PUBLIC_KEY:-JPK}   # JWT public key (общий с backend)
    ports:
      - "8081:8080"
    depends_on:
      postgres:
        condition: service_healthy
      postgres-init:
        condition: service_completed_successfully

  processing-frontend:
    image: ${SAREX_PROCESSING_FRONTEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/workflows-frontend:ugok2_85f6ce2c}
    container_name: processing-frontend
    restart: unless-stopped
    ports:
      - "8082:8080"     # web UI (контейнер слушает 8080)
    depends_on:
      - processing-api

  # ---------------------------------------------------------------------------
  # measurements — FastAPI, считает измерения по растрам из S3/MinIO (GDAL vsis3).
  # БД/воркера нет. Обязателен S3_JSON_SETTINGS. Источник: apps/measurements/base.
  # ---------------------------------------------------------------------------
  measurements:
    image: ${SAREX_MEASUREMENTS_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/measurements:production_a5bb3e84}
    container_name: measurements
    restart: unless-stopped
    entrypoint: ["/opt/entrypoint.sh"]
    environment:
      # host http:// → GDAL переключается на AWS_HTTPS=NO (см. CONFIGURATION.md)
      S3_JSON_SETTINGS: '{"host":"http://minio:9000","login":"${SAREX_MINIO_APP_USER:-sarex-app}","password":"${SAREX_MINIO_APP_PASSWORD:-sarex-app-secret}","verify":false,"buckets":["${SAREX_MINIO_BUCKET:-sarex-media-storage}"]}'
      LOG_LEVEL: INFO
      TRACING_USE: "false"
      AUTH: "0"
      DJANGO_HOST: http://backend:8000
    # порт наружу не публикуем — доступ только из backend по http://measurements:8000/
    depends_on:
      minio-init:
        condition: service_completed_successfully

  # Верхний reverse-proxy: 3 домена → платформа / rabbitmq (admin) / minio
  nginx:
    image: ${NGINX_IMAGE:-nginx:1.27-alpine}
    container_name: sarex-nginx
    restart: unless-stopped
    ports:
      - "80:80"       # http
      - "443:443"     # https (самоподписанный сертификат)
    environment:
      # подставляются envsubst'ом в шаблон при старте контейнера
      PLATFORM_DOMAIN: ${PLATFORM_DOMAIN:-sarex.local}
      ADMIN_DOMAIN: ${ADMIN_DOMAIN:-admin.sarex.local}
      MINIO_DOMAIN: ${MINIO_DOMAIN:-minio.sarex.local}
    volumes:
      - ./nginx/templates:/etc/nginx/templates:ro,z   # :z — SELinux label для RedOS
      - ./nginx/certs:/etc/nginx/certs:ro,z            # TLS-сертификаты
    depends_on:
      # - backend      # TODO: закомментированы в шаблоне nginx, пока в работе
      # - frontend
      - minio
      - rabbitmq
      # - processing-api        # processing пока не поднимаем (закомментирован в шаблоне nginx)
      # - processing-frontend
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1/healthz"]
      interval: 10s
      timeout: 5s
      retries: 6
      start_period: 10s

volumes:
  k3s-server-data:
  k3s-worker-data:
  gitea-data:
  sarex-postgres-data:
  sarex-redis-data:
  sarex-rabbitmq-data:
  sarex-minio-data: