name: ristu-fresh

services:
  ristu-db:
    image: docker.io/library/postgres:16@sha256:71e27bf60b70bded003791b5573f8b808365613f341df20ffcf0c1ed7bc13ddf
    container_name: ristu-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: orthanc_ris
      POSTGRES_USER: orthanc_ris
      POSTGRES_PASSWORD: ${RISTU_DB_PASSWORD:?Ustaw RISTU_DB_PASSWORD w .env}
    volumes:
      - ristu_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -q -U orthanc_ris -d orthanc_ris"]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 10s
    networks:
      - ristu

  orthanc-db:
    image: docker.io/library/postgres:16@sha256:71e27bf60b70bded003791b5573f8b808365613f341df20ffcf0c1ed7bc13ddf
    container_name: orthanc-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: orthanc
      POSTGRES_USER: orthanc
      POSTGRES_PASSWORD: ${ORTHANC_DB_PASSWORD:?Ustaw ORTHANC_DB_PASSWORD w .env}
    volumes:
      - orthanc_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -q -U orthanc -d orthanc"]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 10s
    networks:
      - ristu

  orthanc-auth-service:
    image: docker.io/orthancteam/orthanc-auth-service@sha256:2054b28ed33b0b05c9611cb13b538f7f55af83f30885a172639d6527e85c0d52
    container_name: orthanc-auth-service
    restart: unless-stopped
    command: ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8001"]
    environment:
      ENABLE_KEYCLOAK: "false"
      SECRET_KEY: ${ORTHANC_AUTH_SECRET:?Ustaw ORTHANC_AUTH_SECRET w .env}
      USERS: "{\"share-user\":\"${ORTHANC_AUTH_SERVICE_PASSWORD:?Ustaw ORTHANC_AUTH_SERVICE_PASSWORD w .env}\"}"
      # Ristu wykorzystuje z odpowiedzi Auth Service wyłącznie token i buduje
      # link viewer'a dla hosta bieżącego żądania. Te techniczne adresy nie są
      # zwracane użytkownikowi.
      PUBLIC_ORTHANC_ROOT: https://localhost:${RISTU_HTTPS_PORT:?Ustaw RISTU_HTTPS_PORT w .env}/
      PUBLIC_LANDING_ROOT: https://localhost:${RISTU_HTTPS_PORT}/
      PUBLIC_OHIF_ROOT: https://localhost:${RISTU_HTTPS_PORT}/ohif-next/
      OHIF_DATA_SOURCE: dicom-web
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS --max-time 5 -o /dev/null http://127.0.0.1:8001/docs"]
      interval: 10s
      timeout: 5s
      retries: 20
      start_period: 20s
    networks:
      - ristu

  orthanc:
    image: docker.io/orthancteam/orthanc-pre-release@sha256:847627fcd450c2bd0c1c837e96f36f50ecee5d30342484946a6c47e0812eaafa
    container_name: orthanc
    restart: unless-stopped
    depends_on:
      orthanc-db:
        condition: service_healthy
    environment:
      DICOM_WEB_PLUGIN_ENABLED: "true"
      OHIF_PLUGIN_ENABLED: "false"
      HOUSEKEEPER_PLUGIN_ENABLED: "false"
      VERBOSE_ENABLED: "false"
      VERBOSE_STARTUP: "false"
      # Pełna konfiguracja jest osadzona w Compose, dzięki czemu dystrybucja
      # nie wymaga pobierania ani montowania plików config/orthanc/*.json.
      ORTHANC_JSON: >-
        {
          "Name": "Ristu Fresh Orthanc",
          "StorageDirectory": "/var/lib/orthanc/db",
          "SaveJobs": true,
          "SynchronousZipStream": true,
          "DicomAet": "RISTU",
          "DicomPort": 4242,
          "RemoteAccessAllowed": true,
          "SslEnabled": false,
          "AuthenticationEnabled": false,
          "DicomModalities": {},
          "DicomModalitiesInDatabase": true,
          "DatabaseServerIdentifier": "ristu-orthanc",
          "DicomDefaultRetrieveMethod": "C-GET",
          "DicomScuTimeout": 120,
          "DicomWeb": {
            "Enable": true,
            "Root": "/dicom-web/",
            "PublicRoot": "/dicom-web/",
            "SeriesMetadata": "Full",
            "EnableMetadataCache": true
          },
          "PostgreSQL": {
            "EnableIndex": true,
            "EnableStorage": false,
            "Host": "orthanc-db",
            "Port": 5432,
            "Database": "orthanc",
            "Username": "orthanc",
            "Password": "${ORTHANC_DB_PASSWORD:?Ustaw ORTHANC_DB_PASSWORD w .env}",
            "Lock": false,
            "EnableSsl": false,
            "MaximumConnectionRetries": 20,
            "ConnectionRetryInterval": 5,
            "TransactionMode": "ReadCommitted",
            "IndexConnectionsCount": 20,
            "HousekeepingInterval": 1,
            "AllowInconsistentChildCounts": false,
            "UseDynamicConnectionPool": true,
            "ApplicationName": "Ristu-Fresh-Orthanc"
          }
        }
    volumes:
      - orthanc_dicom:/var/lib/orthanc/db
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS --max-time 5 -o /dev/null http://127.0.0.1:8042/system"]
      interval: 10s
      timeout: 5s
      retries: 30
      start_period: 30s
    networks:
      - ristu

  ohif:
    image: ghcr.io/${RISTU_GHCR_OWNER:?Ustaw RISTU_GHCR_OWNER w .env}/ristu-viewer:${RISTU_IMAGE_TAG:?Ustaw RISTU_IMAGE_TAG w .env}
    container_name: ohif
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS --max-time 5 -o /dev/null http://127.0.0.1:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 20
      start_period: 20s
    networks:
      - ristu

  ristu-api:
    image: ghcr.io/${RISTU_GHCR_OWNER:?Ustaw RISTU_GHCR_OWNER w .env}/ristu-api:${RISTU_IMAGE_TAG:?Ustaw RISTU_IMAGE_TAG w .env}
    container_name: ristu-api
    restart: unless-stopped
    depends_on:
      ristu-db:
        condition: service_healthy
      orthanc:
        condition: service_healthy
      orthanc-auth-service:
        condition: service_healthy
    command:
      - sh
      - -c
      - >-
        python -m alembic upgrade heads &&
        exec uvicorn app.main:app --host 0.0.0.0 --port 8000
        --proxy-headers --forwarded-allow-ips=*
    environment:
      APP_NAME: Ristu
      ENVIRONMENT: production
      EXPOSE_API_DOCS: "false"
      # Frontend i API są wystawiane przez ten sam origin, więc instalator nie
      # musi znać publicznego adresu hosta ani otwierać CORS.
      CORS_ORIGINS: ""
      CORS_ORIGIN_REGEX: ""
      AUTH_ENABLED: "true"
      DATABASE_URL: postgresql+psycopg://orthanc_ris:${RISTU_DB_PASSWORD:?Ustaw RISTU_DB_PASSWORD w .env}@ristu-db:5432/orthanc_ris
      PROXY_SESSION_SECRET: ${RISTU_PROXY_SESSION_SECRET:?Ustaw RISTU_PROXY_SESSION_SECRET w .env}
      OHIF_PROXY_ORIGIN: ""
      OHIF_PROXY_PORT: ${RISTU_HTTPS_PORT}
      ORTHANC_BASE_URL: http://orthanc:8042
      ORTHANC_VERIFY_SSL: "false"
      ORTHANC_AUTH_SERVICE_URL: http://orthanc-auth-service:8001
      ORTHANC_AUTH_SERVICE_ALLOW_HTTP: "true"
      ORTHANC_AUTH_SERVICE_VERIFY_SSL: "false"
      ORTHANC_AUTH_SERVICE_USER: share-user
      ORTHANC_AUTH_SERVICE_PASSWORD: ${ORTHANC_AUTH_SERVICE_PASSWORD:?Ustaw ORTHANC_AUTH_SERVICE_PASSWORD w .env}
      LOCAL_ADMIN_ENABLED: "true"
      LOCAL_ADMIN_OHIF_ENABLED: "true"
      LOCAL_ADMIN_ALLOWED_IP: ${RISTU_LOCAL_ADMIN_ALLOWED_IP:?Ustaw RISTU_LOCAL_ADMIN_ALLOWED_IP w .env}
      AI_SETTINGS_SECRET: ${RISTU_SETTINGS_SECRET:?Ustaw RISTU_SETTINGS_SECRET w .env}
      IDENTITY_SETTINGS_SECRET: ${RISTU_SETTINGS_SECRET:?Ustaw RISTU_SETTINGS_SECRET w .env}
      UPDATE_MANIFEST_URL: ${RISTU_UPDATE_MANIFEST_URL:-}
      DICTATION_ENABLED: "false"
      UPLOAD_V2_SPOOL_HIGH_WATER_BYTES: ${RISTU_UPLOAD_V2_SPOOL_HIGH_WATER_BYTES:?Ustaw RISTU_UPLOAD_V2_SPOOL_HIGH_WATER_BYTES w .env}
      UPLOAD_V2_SPOOL_OWNER_HIGH_WATER_BYTES: ${RISTU_UPLOAD_V2_SPOOL_OWNER_HIGH_WATER_BYTES:?Ustaw RISTU_UPLOAD_V2_SPOOL_OWNER_HIGH_WATER_BYTES w .env}
      UPLOAD_V2_SPOOL_SAFETY_MARGIN_BYTES: ${RISTU_UPLOAD_V2_SPOOL_SAFETY_MARGIN_BYTES:?Ustaw RISTU_UPLOAD_V2_SPOOL_SAFETY_MARGIN_BYTES w .env}
      UPLOAD_V2_SPOOL_HIGH_WATER_ITEMS: ${RISTU_UPLOAD_V2_SPOOL_HIGH_WATER_ITEMS:?Ustaw RISTU_UPLOAD_V2_SPOOL_HIGH_WATER_ITEMS w .env}
      UPLOAD_V2_SPOOL_OWNER_HIGH_WATER_ITEMS: ${RISTU_UPLOAD_V2_SPOOL_OWNER_HIGH_WATER_ITEMS:?Ustaw RISTU_UPLOAD_V2_SPOOL_OWNER_HIGH_WATER_ITEMS w .env}
      UPLOAD_V2_SPOOL_MIN_FREE_INODES: ${RISTU_UPLOAD_V2_SPOOL_MIN_FREE_INODES:?Ustaw RISTU_UPLOAD_V2_SPOOL_MIN_FREE_INODES w .env}
      UPLOAD_V2_MAX_CHUNK_BYTES: ${RISTU_UPLOAD_V2_MAX_CHUNK_BYTES:?Ustaw RISTU_UPLOAD_V2_MAX_CHUNK_BYTES w .env}
      UPLOAD_V2_MAX_CONCURRENT_WRITES: ${RISTU_UPLOAD_V2_MAX_CONCURRENT_WRITES:?Ustaw RISTU_UPLOAD_V2_MAX_CONCURRENT_WRITES w .env}
      UPLOAD_V2_MAX_ARCHIVE_MEMBERS: ${RISTU_UPLOAD_V2_MAX_ARCHIVE_MEMBERS:?Ustaw RISTU_UPLOAD_V2_MAX_ARCHIVE_MEMBERS w .env}
      UPLOAD_V2_OPERATIONS_INTERVAL_SECONDS: ${RISTU_UPLOAD_V2_OPERATIONS_INTERVAL_SECONDS:?Ustaw RISTU_UPLOAD_V2_OPERATIONS_INTERVAL_SECONDS w .env}
      UPLOAD_V2_RECONCILE_PAGE_SIZE: ${RISTU_UPLOAD_V2_RECONCILE_PAGE_SIZE:?Ustaw RISTU_UPLOAD_V2_RECONCILE_PAGE_SIZE w .env}
      UPLOAD_V2_SOURCE_STALE_SECONDS: ${RISTU_UPLOAD_V2_SOURCE_STALE_SECONDS:?Ustaw RISTU_UPLOAD_V2_SOURCE_STALE_SECONDS w .env}
      UPLOAD_V2_STUCK_LEASE_SECONDS: ${RISTU_UPLOAD_V2_STUCK_LEASE_SECONDS:?Ustaw RISTU_UPLOAD_V2_STUCK_LEASE_SECONDS w .env}
      UPLOAD_V2_ORPHAN_GRACE_SECONDS: ${RISTU_UPLOAD_V2_ORPHAN_GRACE_SECONDS:?Ustaw RISTU_UPLOAD_V2_ORPHAN_GRACE_SECONDS w .env}
      UPLOAD_V2_RETRY_PAYLOAD_RETENTION_SECONDS: ${RISTU_UPLOAD_V2_RETRY_PAYLOAD_RETENTION_SECONDS:?Ustaw RISTU_UPLOAD_V2_RETRY_PAYLOAD_RETENTION_SECONDS w .env}
      UPLOAD_V2_REPORT_RETENTION_SECONDS: ${RISTU_UPLOAD_V2_REPORT_RETENTION_SECONDS:?Ustaw RISTU_UPLOAD_V2_REPORT_RETENTION_SECONDS w .env}
      UPLOAD_V2_LEDGER_RETENTION_SECONDS: ${RISTU_UPLOAD_V2_LEDGER_RETENTION_SECONDS:?Ustaw RISTU_UPLOAD_V2_LEDGER_RETENTION_SECONDS w .env}
      # Neutralny seam build-time modułów. W czystej instalacji pozostaje pusty;
      # konkretna lista należy wyłącznie do lokalnego .env/override instalacji.
      ENABLED_MODULES: ${ENABLED_MODULES:-}
    volumes:
      - ristu_api_runtime:/runtime
    healthcheck:
      test:
        - CMD
        - python
        - -c
        - import httpx; httpx.get('http://127.0.0.1:8000/health', timeout=5).raise_for_status()
      interval: 10s
      timeout: 5s
      retries: 30
      start_period: 30s
    networks:
      - ristu

  ristu-web:
    image: ghcr.io/${RISTU_GHCR_OWNER:?Ustaw RISTU_GHCR_OWNER w .env}/ristu-web:${RISTU_IMAGE_TAG:?Ustaw RISTU_IMAGE_TAG w .env}
    container_name: ristu-web
    restart: unless-stopped
    environment:
      RISTU_LOCAL_ADMIN_ALLOWED_IP: ${RISTU_LOCAL_ADMIN_ALLOWED_IP:?Ustaw RISTU_LOCAL_ADMIN_ALLOWED_IP w .env}
      RISTU_LOCAL_ADMIN_BIND_IP: ${RISTU_LOCAL_ADMIN_BIND_IP:-127.0.0.1}
      RISTU_TLS_SAN_DNS: ${RISTU_TLS_SAN_DNS:-localhost}
      RISTU_TLS_SAN_IPS: ${RISTU_TLS_SAN_IPS:-127.0.0.1}
    depends_on:
      ristu-api:
        condition: service_healthy
      orthanc:
        condition: service_healthy
      ohif:
        condition: service_healthy
    ports:
      - ${RISTU_BIND_IP:-0.0.0.0}:${RISTU_HTTPS_PORT:?Ustaw RISTU_HTTPS_PORT w .env}:8443
      - ${RISTU_LOCAL_ADMIN_BIND_IP:-127.0.0.1}:${RISTU_LOCAL_ADMIN_HTTPS_PORT:?Ustaw RISTU_LOCAL_ADMIN_HTTPS_PORT w .env}:8445
    volumes:
      - ristu_web_tls:/etc/nginx/tls
    healthcheck:
      test: ["CMD-SHELL", "curl -fksS --max-time 5 -o /dev/null https://127.0.0.1:8443/health"]
      interval: 10s
      timeout: 5s
      retries: 20
      start_period: 20s
    networks:
      - ristu

networks:
  ristu:
    driver: bridge

volumes:
  ristu_db:
  ristu_api_runtime:
  ristu_web_tls:
  orthanc_db:
  orthanc_dicom:
