name: ${PROJECT_NAME:-d2e}
networks:
  alp:
  data:
  enterprise-gateway:
  enterprise-gateway-viewer:

volumes:
  caddy:
  cdw-config-cachedb-data-1:
  minio-data-1:
  pg-minerva-data-1:
  r-libs:
  trex:
  demodb-data:
  dicom-sqlite-storage:
  hana-data:
  supabase-storage-data:

x-envs:
  env_converter: &x-converter
    URL__DATABASE_LIST__GET: http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/gateway/api/db/database-creds/
    DB_CREDENTIALS__PRIVATE_KEY: ${DB_CREDENTIALS__INTERNAL__DECRYPT_PRIVATE_KEY}
    IDP__ALP_SVC__CLIENT_ID: ${LOGTO__D2E_SVC__CLIENT_ID:-${LOGTO__ALP_SVC__CLIENT_ID}}
    IDP__ALP_SVC__CLIENT_SECRET: ${LOGTO__D2E_SVC__CLIENT_SECRET:-${LOGTO__ALP_SVC__CLIENT_SECRET}}
    IDP__SCOPE: https://alp-default
    IDP_SUBJECT_PROP: sub
  pg: &x-pg
    PG__DB_NAME: alp
    ## Flag to display executed Knex queries in log
    PG__DEBUG: ${PG_DEBUG:-1}
    PG__HOST: ${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}
    PG__MAX_POOL: 10
    PG__MIN_POOL: 0
    PG__PORT: ${PG_PORT:-5432}
    PG__IDLE_TIMEOUT_IN_MS: 1000
    PG__DIALECT: postgresql
    PG__SSL: ${PG__SSL:-false}
    PG__FHIR_DB_NAME: alp
  fhir: &x-fhir
    FHIR__CLIENT_ID: ${FHIR__CLIENT_ID:-db6b2304-f236-45ec-b10c-a852681e7129}
    FHIR__CLIENT_SECRET: ${FHIR__CLIENT_SECRET}
  redis: &x-redis
    REDIS_HOST: ${REDIS_HOST:-${PROJECT_NAME:-d2e}-minerva-redis-1}
    REDIS_PORT: ${REDIS_PORT:-6379}
  trex_sql: &x-trex-sql
    TREX__SQL__HOST: ${PROJECT_NAME:-d2e}-trex
    TREX__SQL__PORT: 5433
    TREX__SQL__USER: postgres
    TREX__SQL__PASSWORD: ${TREX__SQL__PASSWORD:-pencil} # Password for the trex sql interface
    TREX__SQL__DBNAME: postgres
  # "https://${PROJECT_NAME:-d2e}-minerva-portal-server-1.${TLS__INTERNAL__DOMAIN:-alp.local}:41105",
  tls: &x-tls
    TLS__INTERNAL__CA_CRT: ${TLS__INTERNAL__CA_CRT}
    TLS__INTERNAL__CRT: ${TLS__INTERNAL__CRT}
    TLS__INTERNAL__DOMAIN: ${TLS__INTERNAL__DOMAIN:-alp.local}
    TLS__INTERNAL__KEY: ${TLS__INTERNAL__KEY}
  enterprise-gateway: &x-enterprise-gateway
    EG_KERNEL_LAUNCH_TIMEOUT: ${EG_KERNEL_LAUNCH_TIMEOUT:-60}
    EG_KERNEL_INFO_TIMEOUT: ${EG_KERNEL_INFO_TIMEOUT:-60}
    EG_CULL_IDLE_TIMEOUT: ${EG_CULL_IDLE_TIMEOUT:-3600}
    # Use double-defaulting for B/C.  Support for EG_KERNEL_WHITELIST will be removed in a future release
    EG_ALLOWED_KERNELS: ${EG_ALLOWED_KERNELS:-${EG_KERNEL_WHITELIST:-'r_ohdsi_docker'}} # /jupyter/api/kernelspecs
    EG_MIRROR_WORKING_DIRS: ${EG_MIRROR_WORKING_DIRS:-False}
    EG_RESPONSE_PORT: ${EG_RESPONSE_PORT:-8877}
    KG_PORT: ${KG_PORT:-8888}
    EG_BASE_URL: /jupyter
    EG_DEFAULT_KERNEL_NAME: "r_ohdsi_docker"
    EG_DOCKER_MODE: docker
    EG_ALLOW_ORIGIN: "https://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}"
    TREX__ENDPOINT_URL: "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001"
    EG_ENV_WHITELIST: "KERNEL_USERNAME"
    EG_MAX_KERNELS_PER_USER: 1

services:
  trex:
    container_name: &c84 ${PROJECT_NAME:-d2e}-trex
    init: true
    command: npm run start
    depends_on:
      alp-minerva-pg-mgmt-init:
        condition: service_completed_successfully
      alp-logto-post-init:
        condition: service_completed_successfully
    hostname: ${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-trex:${DOCKER_TAG_NAME:-develop}
    platform: linux/amd64
    logging:
      options:
        tag: *c84
    networks:
      alp:
        priority: 20
      data:
        priority: 10
      enterprise-gateway:
        priority: 20
      enterprise-gateway-viewer:
        priority: 20
    volumes:
      - trex:/usr/src/data
      - cdw-config-cachedb-data-1:/usr/src/cdw_data/dynamically_generated # Volume for dynamically generated cdw-config duckdb file
    healthcheck:
      interval: 10s
      retries: 8
      start_period: 300s
      test:
        - CMD
        - npm
        - run
        - health
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    environment:
      <<:
        - *x-converter
        - *x-tls
        - *x-fhir
        - *x-pg
        - *x-trex-sql
      REP_PG: postgres://postgres:${DEMO__DB_PASSWORD:-mypass}@demodb:5432/postgres
      TPM_USE_NODE_MODULES: "false"
      SERVICE_ROUTES: |-
        {
        "analytics": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001",
        "bookmark": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/analytics-svc/api/services/bookmark",
        "cdw": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001",
        "d2e-webapi": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/d2e-webapi",
        "paConfig": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/pa-config-svc",
        "trex": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001",
        "queryGen": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001",
        "jobplugins": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001",
        "portalServer": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/system-portal",
        "dataset": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/gateway/api/dataset",
        "usermgmt": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/usermgmt/api",
        "strategus-analysis": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001",
        "terminology": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/terminology",
        "prefect": "http://${PROJECT_NAME:-d2e}-dataflow-gen-1:41120/d2e/api",
        "idIssuerUrl": "http://${PROJECT_NAME:-d2e}-logto-1:3001/oidc",
        "minio": "",
        "fhirServer": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/fhir-server/fhir/R4",
        "fhirTokenUrl": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/fhir-server/oauth2/token",
        "fhirGateway": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/fhir-gateway",
        "filesManager": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/files-manager/api",
        "whiteRabbit": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/white-rabbit/api",
        "alpdb": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/gateway/api/db",
        "dicomServer": "http://${PROJECT_NAME:-d2e}-dicom-server-1:8042",
        "logto": "http://${PROJECT_NAME:-d2e}-logto-1:3001",
        "jupyter-gateway": "http://${PROJECT_NAME:-d2e}-enterprise-gateway:8888/jupyter",
        "jupyter-gateway-viewer": "http://${PROJECT_NAME:-d2e}-enterprise-gateway-viewer:8888/jupyter",
        "kpi-dashboard": "${EXTERNAL__KPI_DASHBOARD_URL:-http://docker-dash:8050}",
        "docker-logs": "http://${PROJECT_NAME:-d2e}-logging-1:8080",
        "supabaseStorage": "http://${PROJECT_NAME:-d2e}-supabase-storage-1:9000",
        "dataflowStorage": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/system-portal/supabase-storage",
        "mlflow": "http://${PROJECT_NAME:-d2e}-mlflow:5000",
        "mcp-server": "http://${PROJECT_NAME:-d2e}-trex.${TLS__INTERNAL__DOMAIN:-alp.local}:33001/mcp/chat",
        "materializeCohorts": "https://${PROJECT_NAME:-d2e}-materialize-cohorts:3333"
        }
      PG_SUPER_PASSWORD: ${PG_SUPER_PASSWORD}
      PG_SUPER_USER: ${PG_SUPER_USER:-postgres}
      PG_MANAGE_PASSWORD: ${PG_ADMIN_PASSWORD}
      PG_MANAGE_USER: ${PG_ADMIN_USER:-alp_pg_admin_user}
      PG_PASSWORD: ${PG_WRITE_PASSWORD}
      PG_USER: ${PG_WRITE_USER:-alp_pg_write_user}
      PORTAL__LOG_DISCLAIMER: ${PORTAL__LOG_DISCLAIMER:-false}
      PREFECT_API_URL: ${PREFECT_API_URL:-http://${PROJECT_NAME:-d2e}-dataflow-gen-1:41120/d2e/api}
      DB_CREDENTIALS__PUBLIC_KEYS: |-
        {
         "DataPlatform": "",
         "Internal": "${DB_CREDENTIALS__INTERNAL__PUBLIC_KEY}"
        }
      DB_CREDENTIALS__PRIVATE_KEY: ${DB_CREDENTIALS__INTERNAL__DECRYPT_PRIVATE_KEY}
      GATEWAY__IDP_AUTH_TYPE: logto
      GATEWAY__IDP_SUBJECT_PROP: sub
      GATEWAY__WO_PROTOCOL_FQDN: ${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}
      HANA__DRIVER_CLASS_PATH: ${HANA__DRIVER_CLASS_PATH:-/app/inst/drivers/ngdbc-latest.jar}
      HANA_HINT: ${HANA_HINT:-}
      CADDY__D2E__PUBLIC_FQDN: ${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}
      IDP__ALP_DATA_CLIENT_ID: ${LOGTO__D2E_DATA__CLIENT_ID:-${LOGTO__ALP_DATA__CLIENT_ID}}
      IDP__ALP_SVC_CLIENT_ID: ${LOGTO__D2E_SVC__CLIENT_ID:-${LOGTO__ALP_SVC__CLIENT_ID}}
      IDP__RELYING_PARTY: ${IDP__RELYING_PARTY}
      IDP__REQUIRED_CLAIM: ${IDP__REQUIRED_CLAIM-thirdPartyToken}
      LOGTO__AUDIENCES: https://alp-default
      LOGTO__CLIENT_ID: ${LOGTO__D2E_APP__CLIENT_ID:-${LOGTO__ALP_APP__CLIENT_ID}}
      LOGTO__D2E_SVC__CLIENT_ID: ${LOGTO__D2E_SVC__CLIENT_ID:-${LOGTO__ALP_SVC__CLIENT_ID}}
      LOGTO__D2E_SVC__CLIENT_SECRET: ${LOGTO__D2E_SVC__CLIENT_SECRET:-${LOGTO__ALP_SVC__CLIENT_SECRET}}
      LOGTO__D2E_DATA__CLIENT_ID: ${LOGTO__D2E_DATA__CLIENT_ID:-${LOGTO__ALP_DATA__CLIENT_ID}}
      LOGTO__D2E_DATA__CLIENT_SECRET: ${LOGTO__D2E_DATA__CLIENT_SECRET:-${LOGTO__ALP_DATA__CLIENT_SECRET}}
      LOGTO__CLIENT_SECRET: ${LOGTO__D2E_APP__CLIENT_SECRET:-${LOGTO__ALP_APP__CLIENT_SECRET}}
      LOGTO__ISSUER: http://${PROJECT_NAME:-d2e}-logto-1:3001/oidc
      LOGTO__RESOURCE_API: https://alp-default
      LOGTO__SCOPE: ${LOGTO__SCOPE:-openid offline_access profile email}
      LOGTO__SVC_CLIENT_ID: ${LOGTO__D2E_SVC__CLIENT_ID:-${LOGTO__ALP_SVC__CLIENT_ID}}
      LOGTO__SVC_CLIENT_SECRET: ${LOGTO__D2E_SVC__CLIENT_SECRET:-${LOGTO__ALP_SVC__CLIENT_SECRET}}
      LOGTO__TOKEN_URL: http://${PROJECT_NAME:-d2e}-logto-1:3001/oidc/token
      LOGTO__ADMIN_SERVER__FQDN_URL: http://${PROJECT_NAME:-d2e}-logto-1:3002
      LOGTO__DEFAULT_TENANT__FQDN_URL: ${LOGTO__DEFAULT_TENANT__FQDN_URL:-https://default.logto.app/api}
      LOGTO__CLIENTID_PASSWORD__BASIC_AUTH: ${LOGTO__CLIENTID_PASSWORD__BASIC_AUTH}
      LOGTO_API_M2M_CLIENT_ID: ${LOGTO_API_M2M_CLIENT_ID}
      LOGTO_API_M2M_CLIENT_SECRET: ${LOGTO_API_M2M_CLIENT_SECRET}
      NODE_ENV: development
      POSTGRES__DRIVER_CLASS_PATH: ${POSTGRES__DRIVER_CLASS_PATH:-/app/inst/drivers/postgresql-42.3.1.jar}
      PREFECT_DOCKER_NETWORK: "${PROJECT_NAME:-d2e}_data"
      PREFECT_DOCKER_VOLUMES: '${PREFECT_DOCKER_VOLUMES_CUSTOM:-["${PROJECT_NAME:-d2e}_trex:/app/duckdb_data"]}'
      GH_TOKEN: ${GH_TOKEN}
      PLUGINS_SEED: '${PLUGINS_SEED:-["d2e-functions", "d2e-ui", "d2e-flows", "d2e-atlas", "data-transformation-flow", "hades-flow", "d2e-fhir-server", "fhir"]}'
      PLUGINS_API_VERSION: ${PLUGINS_API_VERSION:-latest}
      PLUGINS_SEED_UPDATE: ${PLUGINS_SEED_UPDATE:-false}
      PLUGINS_REGISTRY: ${PLUGINS_REGISTRY:-https://pkgs.dev.azure.com/data2evidence/d2e/_packaging/d2e/npm/registry/}
      TPM_REGISTRY_URL: ${PLUGINS_REGISTRY:-https://pkgs.dev.azure.com/data2evidence/d2e/_packaging/d2e/npm/registry/}
      PLUGINS_IMAGE_TAG: ${PLUGINS_IMAGE_TAG:-develop}
      PLUGINS_FLOW_CUSTOM_REPO_IMAGE_CONFIG: "${PLUGINS_FLOW_CUSTOM_REPO_IMAGE_CONFIG:-{}}" #{"current": "ghcr.io/ohdsi/d2e", "new": ""}
      PLUGINS_INFORMATION_URL: ${PLUGINS_INFORMATION_URL:-https://feeds.dev.azure.com/data2evidence/d2e/_apis/packaging/Feeds/d2e/packages?api-version=7.1&includeDescription=true}
      PROJECT_NAME: ${PROJECT_NAME:-d2e}
      DATAFLOW_TEMPLATE_REPO_URL: ${DATAFLOW_TEMPLATE_REPO_URL:-https://github.com/data2evidence/templates.git}
      DATAFLOW_TEMPLATE_BRANCH: ${DATAFLOW_TEMPLATE_BRANCH:-main}
      NOTEBOOK_TEMPLATE_REPO_URL: ${NOTEBOOK_TEMPLATE_REPO_URL:-https://github.com/data2evidence/templates.git}
      NOTEBOOK_TEMPLATE_REPO_BRANCH: ${NOTEBOOK_TEMPLATE_REPO_BRANCH:-main}
      FHIR_STRUCTURE_MAP_TEMPLATE_REPO_URL: ${FHIR_STRUCTURE_MAP_TEMPLATE_REPO_URL:-https://github.com/data2evidence/templates.git}
      FHIR_STRUCTURE_MAP_TEMPLATE_REPO_BRANCH: ${FHIR_STRUCTURE_MAP_TEMPLATE_REPO_BRANCH:-main}
      TREX_SQL_PASSWORD: ${TREX__SQL__PASSWORD}
      GOOGLE_APPLICATION_CREDENTIALS: /usr/src/data/google-credentials.json
      PG_ADMIN_PASSWORD: ${PG_ADMIN_PASSWORD}
      PG_WRITE_PASSWORD: ${PG_WRITE_PASSWORD}
      PG_STUDY_RESULTS_READ_PASSWORD: ${PG_STUDY_RESULTS_READ_PASSWORD}
      PG_STUDY_RESULTS_ADMIN_PASSWORD: ${PG_STUDY_RESULTS_ADMIN_PASSWORD}
      SUPABASE_STORAGE_JWT_TOKEN: ${SUPABASE_STORAGE_JWT_TOKEN}
      SUPABASE_STORAGE_JWT_SECRET: ${SUPABASE_STORAGE_JWT_SECRET}
      PERSEUS__FILES_MANAGER_HOST: trex
      isHttpTestRun: ${isHttpTestRun:-false}
      isTestEnv: ${isTestEnv:-false}
      HTTPTEST_DB_DIALECT: ${HTTPTEST_DB_DIALECT:-duckdb}
      local: ${local}
      TESTSCHEMA: ${TESTSCHEMA}
      REFRESH_TOKEN__ENDPOINT: ${REFRESH_TOKEN__ENDPOINT}
      REFRESH_TOKEN__CLIENT_ID: ${REFRESH_TOKEN__CLIENT_ID}
      REFRESH_TOKEN__CLIENT_SECRET: ${REFRESH_TOKEN__CLIENT_SECRET}
      EXCLUDE_ANALYSIS_IDS: ${EXCLUDE_ANALYSIS_IDS}
      DC_HANA_READ_ROLE: ${DC_HANA_READ_ROLE}
      MINIO__SECRET_KEY: ${MINIO__SECRET_KEY}
      DEMO__DB_PASSWORD: ${DEMO__DB_PASSWORD:-mypass}
      USE_PUBLIC_WEBAPI_PROXY: ${USE_PUBLIC_WEBAPI_PROXY:-false}
      PUBLIC_WEBAPI_PROXY_URL: ${PUBLIC_WEBAPI_PROXY_URL:-http://localhost:3001}
      PUBLIC_WEBAPI_DATASOURCE: ${PUBLIC_WEBAPI_DATASOURCE:-SYNPUF1K}
      REDIS_PASSWORD: ${REDIS_PASSWORD}
      SQL_RETURN_ON: ${SQL_RETURN_ON:-false}
      AZURE_OPENAI_API_KEY: ${AZURE_OPENAI_API_KEY:-}
      AZURE_OPENAI_API_ENDPOINT: ${AZURE_OPENAI_API_ENDPOINT:-}
      AZURE_OPENAI_API_DEPLOYMENT_NAME: ${AZURE_OPENAI_API_DEPLOYMENT_NAME:-}
      AZURE_OPENAI_API_VERSION: ${AZURE_OPENAI_API_VERSION:-}
      AZURE_OPENAI_API_INSTANCE_NAME: ${AZURE_OPENAI_API_INSTANCE_NAME:-}
      AI_MODEL: ${AI_MODEL:-azure:gpt-4o}
      D2E_MEMORY_LIMIT: ${D2E_MEMORY_LIMIT:-}
      D2E_SWAP_LIMIT: ${D2E_SWAP_LIMIT:-}
      D2E_DUCKDB_MEMORY_LIMIT: ${D2E_DUCKDB_MEMORY_LIMIT:-}
      D2E_DUCKDB_THREADS: ${D2E_DUCKDB_THREADS:-}
      INSTALL_SQLALCHEMY: ${INSTALL_SQLALCHEMY:-}
      IS_SELF_MANAGED_ROLES: ${IS_SELF_MANAGED_ROLES:-false}
      AZ_AUTO_GRANT_RESEARCHER_BY_DATASET_CODES: ${AZ_AUTO_GRANT_RESEARCHER_BY_DATASET_CODES:-}
      USER_MGMT__ROLE_SOURCE: ${USER_MGMT__ROLE_SOURCE:-usermgmt}
      CACHE_FLOW_LEVEL_CONCURRENCY: ${CACHE_FLOW_LEVEL_CONCURRENCY:-1}
      CACHE_TABLE_LEVEL_CONCURRENCY: ${CACHE_TABLE_LEVEL_CONCURRENCY:-1}
      CACHE_TASK_TIMEOUT: ${CACHE_TASK_TIMEOUT:-10800}
      ANALYTICS_STREAMING_CHUNK_SIZE_BY_DIALECT: '${ANALYTICS_STREAMING_CHUNK_SIZE_BY_DIALECT:-{"hana": 10000}}'
      ANALYTICS_HANA_STREAMING_ENABLED: ${ANALYTICS_HANA_STREAMING_ENABLED:-false}
      # TREX_DEBUG_GC: ${TREX_DEBUG_GC:-0}
      # RUST_LOG: ${RUST_LOG:-info}

  alp-minerva-pg-mgmt-init:
    container_name: &c15 ${PROJECT_NAME:-d2e}-minerva-pg-mgmt-init-1
    depends_on:
      alp-minerva-postgres:
        condition: service_healthy
    hostname: ${PROJECT_NAME:-d2e}-minerva-pg-mgmt-init-1.${TLS__INTERNAL__DOMAIN:-alp.local}
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-pg-mgmt-init:${DOCKER_TAG_NAME:-develop}
    logging:
      options:
        tag: *c15
    networks:
      alp:
        priority: 20
    platform: linux/amd64
    environment:
      <<:
        - *x-tls
      NODE_ENV: development
      ALP_DB_LOGLEVEL: ${ALP_DB_LOGLEVEL:-info}
      POSTGRES_CONNECTION_CONFIG: |-
        {
         "connectionTimeoutMillis": 60000,
         "database": "${PG_DB_NAME:-alp}",
         "host": "${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}",
         "idle_in_transaction_session_timeout": 60000,
         "password": "${PG_SUPER_PASSWORD}",
         "port": "${PG_PORT:-5432}",
         "query_timeout": 60000,
         "statement_timeout": 60000,
         "user": "${PG_SUPER_USER:-postgres}",
         "ssl": false
        }
      POSTGRES_MANAGE_CONFIG: |-
        {
         "databases": {
          "+${PG_DB_NAME:-alp}": {
           "schemas": {
            "+cdw_config": {},
            "+jobplugins": {},
            "+logto": {},
            "+portal": {},
            "+public": {},
            "+qe_config": {},
            "+terminology": {},
            "+usermgmt": {},
            "+files_manager": {},
            "+perseus": {},
            "+white_rabbit": {},
            "+trex": {},
            "+storage": {},
            "+strategus": {},
            "+fhir": {}
           }
          }
         }
        }
      POSTGRES_ALTER_EXTENSION_CONFIG: |-
        {
         "databases": {
          "${PG__FHIR_DB_NAME:-alp}": {
             "schema": "pg_catalog",
             "extension": "pg_trgm"
          }
         }
        }
      POSTGRES_MANAGE_USERS: |-
        {
         "${PG_DB_NAME:-alp}": {
          "manager": "${PG_ADMIN_USER:-alp_pg_admin_user}",
          "managerPassword": "${PG_ADMIN_PASSWORD}",
          "reader": "${PG_WRITE_USER:-alp_pg_write_user}",
          "readerPassword": "${PG_WRITE_PASSWORD}",
          "writer": "${PG_WRITE_USER:-alp_pg_write_user}",
          "writerPassword": "${PG_WRITE_PASSWORD}",
          "logtoManager": "${PG_LOGTO_MANAGER:-${PG__LOGTO_MANAGER_USER:-logto_postgres}}",
          "logtoManagerPassword": "${PG__LOGTO_MANAGER_PASSWORD}"
         }
        }
      POSTGRES_MANAGE_ROLES_USERS: ${POSTGRES_MANAGE_ROLES_USERS:-{}}
      POSTGRES_SUPERUSER: ${PG_SUPER_USER:-postgres}
      POSTGRES_SUPERUSER_PASSWORD: ${PG_SUPER_PASSWORD}
      POSTGRES_CONFIG_DB_NAME: ${PG_DB_NAME:-alp}
      PORT: 41110
      sqlOnly: false

  alp-caddy:
    container_name: &c1 ${PROJECT_NAME:-d2e}-caddy
    hostname: ${PROJECT_NAME:-d2e}-caddy.${TLS__INTERNAL__DOMAIN:-alp.local}
    platform: linux/amd64
    image: caddy:2.8-alpine
    logging:
      options:
        tag: *c1
    networks:
      alp:
        priority: 20
      enterprise-gateway:
        priority: 20
      enterprise-gateway-viewer:
        priority: 20
    command: >
      sh -c "
        mkdir -p /srv/caddy-config &&
        cat <<EOT > /srv/caddy-config/Caddyfile
        {
          debug
          pki {
            ca local {
              name \"D2E Local CA\"
              root_cn \"D2E Local CA - 2024 ECC Root\"
              intermediate_cn \"D2E Local CA - ECC Intermediate\"
            }
          }
        }

        (publicurlredirect) {
          ${CADDY__D2E__ALIAS_PUBLIC_FQDN:-${CADDY__ALP__ALIAS_PUBLIC_FQDN:-}} {
            $TLS__CADDY_DIRECTIVE
            redir https://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}{uri} permanent
          }
        }

        (proxy_headers_codespaces) {
          header_up Host {upstream_hostport}
          header_up X-Forwarded-Host ${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}
          header_up X-Forwarded-Proto {scheme}
          header_up X-Forwarded-For {remote}
        }

        (proxy_headers_default) {
          header_up Host {upstream_hostport}
        }

        (portal) {
          log {
            format console {
              level_format color
            }
          }

          # Logto api server
          @logto {
            path /api/*
            path /oidc/*
            path /sign-in
            path /consent
            path /callback/* # For azure connector

            path /d2e/logto/index.*
            path /d2e/logto/assets/* # logto assets are moved to this path
          }

          handle @logto {
            uri strip_prefix /d2e/logto
            reverse_proxy http://${PROJECT_NAME:-d2e}-logto-1:3001 {
              import ${PROXY_HEADERS_MODE:-proxy_headers_default}
              transport http {
                read_buffer 8192
              }
            }
          }

          @jupyter_websocket {
            path /jupyter/*
            header Connection *Upgrade*
            header Upgrade websocket
          }

          @strategus_websocket {
            path /strategus-results/*
            header Connection *Upgrade*
            header Upgrade websocket
          }
          
          handle @jupyter_websocket {
            reverse_proxy https://${PROJECT_NAME:-d2e}-trex:33000 {
              transport http {
                tls
                tls_insecure_skip_verify
                read_buffer 8192
              }
              method GET
              rewrite /usermgmt/api/me/is_token_valid_internal
              header_up X-Forwarded-Method {method}
              header_up X-Forwarded-Uri {uri}
              header_up authorization {http.request.uri.query.token}
              @good status 2xx
              handle_response @good {
                request_header Remote-User {rp.header.Remote-User}
                request_header Remote-Email {rp.header.Remote-Email}
              }
            }
            reverse_proxy @jupyter_websocket http://${PROJECT_NAME:-d2e}-enterprise-gateway:8888
          }

          handle @strategus_websocket {
            @study_id path_regexp ^/strategus-results/([^/]+)/(.*)$
            handle @study_id {
              uri strip_prefix /strategus-results/{re.1}
              reverse_proxy {re.1}:3838
            }
          }

          ## call gateway if there are no matched files
          handle {
            reverse_proxy https://${PROJECT_NAME:-d2e}-trex:33000 {
              transport http {
                tls
                tls_insecure_skip_verify
                read_buffer 8192
              }
            }
          }
        }

        $INSTANCE_SPECIFIC_CONFIG

        # Enable if need to access admin console
        # http://localhost:3002 {
        #   uri strip_prefix /d2e/logto
        #   reverse_proxy http://${PROJECT_NAME:-d2e}-logto-1:3002 {
        #     import ${PROXY_HEADERS_MODE:-proxy_headers_default}
        #     transport http {
        #       read_buffer 8192
        #     }
        #   }
        # }

        https://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${CADDY__ADDITIONAL_HOSTS:+, ${CADDY__ADDITIONAL_HOSTS}} {
          $TLS__CADDY_DIRECTIVE

          @aliasbasepaths {
              path_regexp portal_basepath ^/(d2e|portal)(/)?$
          }
          redir @aliasbasepaths /d2e/portal
          import portal
        }
      EOT
        caddy run --environ --config /srv/caddy-config/Caddyfile --adapter caddyfile
      "
    ports:
      - ${CADDY_PORT:-${PORT:-443}}:443
      # - 3002:3002 # Enable if need to access admin console
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    volumes:
      - caddy:/data
    environment:
      CADDY__D2E__PUBLIC_FQDN: ${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}
      TLS__CADDY_DIRECTIVE: ${TLS__CADDY_DIRECTIVE:-tls internal}
      INSTANCE_SPECIFIC_CONFIG: ${INSTANCE_SPECIFIC_CONFIG:-}
      CADDY__D2E__ALIAS_PUBLIC_FQDN: ${CADDY__D2E__ALIAS_PUBLIC_FQDN:-${CADDY__ALP__ALIAS_PUBLIC_FQDN:-}}
      PROJECT_NAME: ${PROJECT_NAME:-d2e}

  alp-dataflow-gen:
    command:
      - sh
      - -c
      - "prefect server start"
    container_name: &c2 ${PROJECT_NAME:-d2e}-dataflow-gen-1
    depends_on:
      alp-logto-post-init:
        condition: service_completed_successfully
      alp-minerva-pg-mgmt-init:
        condition: service_completed_successfully
    healthcheck:
      interval: 30s
      retries: 30
      start_period: 30s
      test:
        [
          "CMD",
          "python",
          "-c",
          "import urllib.request, sys; sys.exit(0 if 200 <= urllib.request.urlopen('http://localhost:41120/d2e/api/health', timeout=5).getcode() < 300 else 1)",
        ]
      timeout: 30s
    hostname: dataflow-gen-1
    image: prefecthq/prefect:3.6.10-python3.12
    platform: linux/amd64
    logging:
      options:
        tag: *c2
    networks:
      alp:
        priority: 20
      data:
        priority: 10
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    environment:
      <<:
        - *x-converter
        - *x-pg
        - *x-tls
      LOCAL_DEBUG: "false"
      PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://${PG_SUPER_USER:-postgres}:${PG_SUPER_PASSWORD}@${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}:${PG_PORT:-5432}/${PG_DB_NAME:-alp}
      PREFECT_API_DEFAULT_LIMIT: 5000
      PREFECT_SERVER_API_BASE_PATH: /d2e/api
      PREFECT_UI_SERVE_BASE: /d2e
      PREFECT_UI_API_URL: http://0.0.0.0:41120/d2e/api
      PREFECT_UI_URL: http://0.0.0.0:41120/d2e
      PREFECT_LOGGING_LEVEL: DEBUG
      PREFECT_LOGGING_SERVER_LEVEL: WARNING
      PREFECT_SERVER_ANALYTICS_ENABLED: "false"
      PREFECT_SERVER_API_HOST: 0.0.0.0
      PREFECT_UI_STATIC_DIRECTORY: /app/ui-files
      PREFECT_SERVER_API_PORT: 41120
      PREFECT_API_ENABLE_HTTP2: False

  alp-dataflow-gen-worker:
    command:
      - sh
      - -c
      - "rm -rf /root/.prefect/storage/run_cache.db /root/.prefect/storage/events.db; prefect work-pool create 'docker-pool' --type docker; prefect worker start --pool 'docker-pool' --type docker --name prefect-docker-worker --with-healthcheck"
    container_name: &c83 ${PROJECT_NAME:-d2e}-dataflow-gen-worker
    platform: linux/amd64
    depends_on:
      alp-logto-post-init:
        condition: service_completed_successfully
      alp-dataflow-gen:
        condition: service_healthy
    healthcheck:
      interval: 30s
      retries: 30
      start_period: 30s
      test:
        [
          "CMD",
          "python",
          "-c",
          "import urllib.request, sys; sys.exit(0 if 200 <= urllib.request.urlopen('http://localhost:8080/health', timeout=5).getcode() < 300 else 1)",
        ]
      timeout: 30s
    hostname: ${PROJECT_NAME:-d2e}-dataflow-gen-worker.${TLS__INTERNAL__DOMAIN:-alp.local}
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-dataflow-gen-worker:${DOCKER_TAG_NAME:-develop}
    logging:
      options:
        tag: *c83
    networks:
      data:
        priority: 20
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      PREFECT_API_URL: http://${PROJECT_NAME:-d2e}-dataflow-gen-1:41120/d2e/api
      PREFECT_API_ENABLE_HTTP2: False
      D2E_MEMORY_LIMIT: ${D2E_MEMORY_LIMIT:-16G}
      D2E_SWAP_LIMIT: ${D2E_SWAP_LIMIT:-64G}
      INSTALL_SQLALCHEMY: ${INSTALL_SQLALCHEMY:-}
      DOCKER_REGISTRY_URL: ${DOCKER_REGISTRY_URL:-ghcr.io}
      PREFECT_WORKER_WEBSERVER_HOST: 0.0.0.0
      PREFECT_TASKS_CACHE_ENABLED: false
      PREFECT_FLOW_RUN_CACHE_ENABLED: false
      PREFECT_RESULTS_PERSIST_BY_DEFAULT: false
      PREFECT_EVENTS_ENABLED: false

  # Needed only for future alterations of schema when version upgrades
  # alp-logto-db-post-alterations-init:
  #   depends_on:
  #       alp-minerva-postgres:
  #         condition: service_healthy
  #   image: ghcr.io/logto-io/logto:1.12
  #   entrypoint: ["sh", "-c", "npm run alteration deploy latest"]
  #   environment:
  #     CI: true
  #     DB_URL: postgres://${PG_SUPER_USER:-postgres}:${PG_SUPER_PASSWORD}@alp-minerva-postgres-1:${PG_PORT:-5432}/${PG_DB_NAME:-postgres}
  #   networks:
  #     - alp

  alp-logto-post-init:
    container_name: ${PROJECT_NAME:-d2e}-logto-post-init-1
    depends_on:
      alp-logto:
        condition: service_healthy
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-logto-post-init:${DOCKER_TAG_NAME:-develop}
    networks:
      alp:
        priority: 20
    platform: linux/amd64
    environment:
      ENV_TYPE: ${ENV_TYPE:-local}
      PG__DB_NAME: alp
      PG__HOST: ${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}
      PG__PASSWORD: ${PG__LOGTO_MANAGER_PASSWORD}
      PG__PORT: ${PG_PORT:-5432}
      PG__USER: ${PG__LOGTO_MANAGER_USER:-logto_postgres}
      PG__SSL: ${PG__SSL:-false}
      PG__SCHEMA: logto
      # LOGTO__CONNECTOR_CONFIG: '{ "id": "y9nq6f91yeg19kwt1x6xz","connectorId": "azuread-alp", "config": { "clientId": "${ENTRA__CLIENT_ID}", "tenantId": "${ENTRA__TENANT_ID}", "clientSecret": "${ENTRA__CLIENT_SECRET}", "cloudInstance": "https://login.microsoftonline.com", "scopes": "openid,profile,email,offline_access"}, "syncProfile": true}'
      # LOGTO__DISABLE_BASIC_AUTH: ${LOGTO__DISABLE_BASIC_AUTH:-true}
      # LOGTO__CUSTOM_JWT: '{"environmentVariables": {"clientId": "${ENTRA__CLIENT_ID}", "tenantId": "${ENTRA__TENANT_ID}", "clientSecret": "${ENTRA__CLIENT_SECRET}"}, "script": "/**\n* This function is called during the access token generation process to get custom claims for the JWT token.\n* Limit custom claims to under 50KB.\n*\n* @param {Object} payload - The input payload of the function.\n* @param {AccessTokenPayload} payload.token -The JWT token.\n* @param {Context} payload.context - Logto internal data that can be used to pass additional information\n* @param {EnvironmentVariables} [payload.environmentVariables] - The environment variables.\n*\n* @returns The custom claims.\n*/\n\n// @ts-ignore\nconst getCustomJwtClaims = async ({ token, context, environmentVariables, extra }) => {\n  const clientId = environmentVariables?.clientId || \"\";\n  const clientSecret = environmentVariables?.clientSecret || \"\";\n  const tenantId = environmentVariables?.tenantId || \"\";\n  const refreshToken = extra.thirdPartyRefreshToken || \"\";\n\n  const tokenEndpoint = `https://login.microsoftonline.com/$${tenantId}/oauth2/v2.0/token`;\n\n  const params =\n    `client_id=$${encodeURIComponent(clientId)}` +\n    `&client_secret=$${encodeURIComponent(clientSecret)}` +\n    `&refresh_token=$${encodeURIComponent(refreshToken)}` +\n    `&grant_type=refresh_token` +\n    `&scope=openid`;\n\n  let thirdPartyToken = extra.thirdPartyToken;\n  let thirdPartyRefreshToken = refreshToken;\n  try {\n    const response = await fetch(tokenEndpoint, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n      body: params,\n    });\n\n    if (!response.ok) {\n      const responseBody = await response.text();\n      throw new Error(`Error refreshing token: $${response.statusText}: $${responseBody}: $${params}`);\n    }\n\n    const data = await response.json();\n    thirdPartyToken = data.id_token;\n    thirdPartyRefreshToken = data.refresh_token;\n  } catch (error) {}\n\n  const email = context.user?.primaryEmail || \"\";\n\n  const scopes = [];\n\n  for (const role of context.user.roles || []) {\n    for (const scope of role.scopes || []) {\n      scopes.push(scope.name);\n    }\n  }\n\n  return { thirdPartyToken, thirdPartyRefreshToken, email, roles: scopes };\n};\n", "tokenSample": {"aud": "http://localhost:3000/api/test", "gty": "authorization_code", "jti": "f1d3d2d1-1f2d-3d4e-5d6f-7d8a9d0e1d2", "kind": "AccessToken", "scope": "read write", "grantId": "grant_123", "clientId": "my_app", "accountId": "uid_123"}, "contextSample": {"user": {"id": "123", "name": "Foo Bar", "roles": [], "avatar": "https://example.com/avatar.png", "profile": {}, "username": "foo", "customData": {}, "identities": {}, "hasPassword": false, "primaryEmail": "foo@logto.io", "primaryPhone": "+1234567890", "applicationId": "my-app", "organizations": [], "ssoIdentities": [], "organizationRoles": [], "mfaVerificationFactors": []}}}'
      LOGTO__CUSTOM_JWT: '{"script": "/**\n* This function is called during the access token generation process to get custom claims for the JWT token.\n* Limit custom claims to under 50KB.\n*\n* @param {Object} payload - The input payload of the function.\n* @param {AccessTokenPayload} payload.token -The JWT token.\n* @param {Context} payload.context - Logto internal data that can be used to pass additional information\n* @param {EnvironmentVariables} [payload.environmentVariables] - The environment variables.\n*\n* @returns The custom claims.\n*/\n\n// @ts-ignore\nconst getCustomJwtClaims = async ({ token, context, environmentVariables, extra }) => {\n  const scopes = [];\n\n  for (const role of context.user.roles || []) {\n    for (const scope of role.scopes || []) {\n      scopes.push(scope.name);\n    }\n  }\n\n  return { roles: scopes };\n};\n", "tokenSample": {"aud": "http://localhost:3000/api/test", "gty": "authorization_code", "jti": "f1d3d2d1-1f2d-3d4e-5d6f-7d8a9d0e1d2", "kind": "AccessToken", "scope": "read write", "grantId": "grant_123", "clientId": "my_app", "accountId": "uid_123"}, "contextSample": {"user": {"id": "123", "name": "Foo Bar", "roles": [], "avatar": "https://example.com/avatar.png", "profile": {}, "username": "foo", "customData": {}, "identities": {}, "hasPassword": false, "primaryEmail": "foo@logto.io", "primaryPhone": "+1234567890", "applicationId": "my-app", "organizations": [], "ssoIdentities": [], "organizationRoles": [], "mfaVerificationFactors": []}}, "environmentVariables": {}}'
      LOGTO__CLIENT_APPS: '[{"name":"alp-svc","description":"alp-svc","type":"MachineToMachine", "id": "${LOGTO__D2E_SVC__CLIENT_ID:-${LOGTO__ALP_SVC__CLIENT_ID}}", "secret": "${LOGTO__D2E_SVC__CLIENT_SECRET:-${LOGTO__ALP_SVC__CLIENT_SECRET}}"},{"name":"alp-data","description":"alp-data","type":"MachineToMachine", "id": "${LOGTO__D2E_DATA__CLIENT_ID:-${LOGTO__ALP_DATA__CLIENT_ID}}", "secret": "${LOGTO__D2E_DATA__CLIENT_SECRET:-${LOGTO__ALP_DATA__CLIENT_SECRET}}"},{"name":"alp-app","description":"alp-app","type":"Traditional", "id": "${LOGTO__D2E_APP__CLIENT_ID:-${LOGTO__ALP_APP__CLIENT_ID}}", "secret": "${LOGTO__D2E_APP__CLIENT_SECRET:-${LOGTO__ALP_APP__CLIENT_SECRET}}", "oidcClientMetadata":{"redirectUris":["https://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}/d2e/portal/login-callback","https://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}/d2e/ui/gateway/dashboard","https://localhost:4000/d2e/portal/login-callback","https://localhost:8081"],"postLogoutRedirectUris":["https://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}/d2e/portal","https://localhost:4000/d2e/portal","https://localhost:8081"]},"customClientMetadata":{"corsAllowedOrigins":[],"refreshTokenTtlInDays":14,"alwaysIssueRefreshToken":true,"rotateRefreshToken":true}}]'
      LOGTO__RESOURCE: '{"name":"alp-default","indicator":"https://alp-default","accessTokenTtl":3600}'
      LOGTO__USER: '{"username":"admin","initialPassword":"Updatepassword12345"}'
      LOGTO__ROLES: '[{"name":"role.systemadmin","description":"System admin","type":"User"},{"name":"role.useradmin","description":"User admin","type":"User"},{"name":"role.dashboardviewer","description":"Dashboard viewer","type":"User"},{"name":"role.viewer","description":"Viewer","type":"User"},{"name":"role.jobrunner","description":"Job runner","type":"User"},{"name":"role.studyresultsreader","description":"Study results reader","type":"User"},{"name":"role.etlmappingcontributor","description":"ETL mapping contributor","type":"User"}]'
      LOGTO__SCOPES: '[{"name":"role.systemadmin","description":"System admin"},{"name":"role.useradmin","description":"User admin"},{"name":"role.dashboardviewer","description":"Dashboard viewer"},{"name":"role.viewer","description":"Viewer"},{"name":"role.jobrunner","description":"Job runner"},{"name":"role.studyresultsreader","description":"Study results reader"},{"name":"role.etlmappingcontributor","description":"ETL mapping contributor"}]'
      LOGTO__ADMIN_SERVER__FQDN_URL: "http://${PROJECT_NAME:-d2e}-logto-1:3002"
      LOGTO__CLIENTID_PASSWORD__BASIC_AUTH: ${LOGTO__CLIENTID_PASSWORD__BASIC_AUTH}
      LOGTO__ALP_ADMIN_APP: '{"application": {"id": "${LOGTO_API_M2M_CLIENT_ID}", "name":"alp-admin","description":"alp-admin", "secret": "${LOGTO_API_M2M_CLIENT_SECRET}"}, "role": { "name":"api-access","description":"Logto API access"}}'
      LOGTO__TERM_OF_USE_URL: ${LOGTO__TERM_OF_USE_URL:-}
      LOGTO__PRIVACY_POLICY_URL: ${LOGTO__PRIVACY_POLICY_URL:-}
      LOGTO__CUSTOM_CSS: ${LOGTO__CUSTOM_CSS:-}
      CADDY__D2E__PUBLIC_FQDN: ${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}

  alp-logto:
    container_name: &c8 ${PROJECT_NAME:-d2e}-logto-1
    depends_on:
      alp-minerva-pg-mgmt-init:
        condition: service_completed_successfully
    healthcheck:
      interval: 20s
      retries: 30
      start_period: 120s
      test:
        - CMD
        - wget
        - --proxy
        - off
        - -O
        - logto.status
        - http://localhost:3001/api/status
    hostname: ${PROJECT_NAME:-d2e}-logto-1.${TLS__INTERNAL__DOMAIN:-alp.local}
    platform: linux/amd64
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-logto:${DOCKER_TAG_NAME:-develop}
    logging:
      options:
        tag: *c8
    networks:
      alp:
        priority: 20
      data:
        priority: 10
    entrypoint:
      - sh
      - -c
      - npm run cli db seed -- --swe && npx @logto/cli db alteration rollback 1.23.0 && npx @logto/cli db alteration deploy 1.23.1 && npm start
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    environment:
      <<:
        - *x-tls
      ADMIN_DISABLE_LOCALHOST: ${LOGTO__DISABLE_ADMIN_CONSOLE:-false}
      # ADMIN_ENDPOINT: http://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}:3002 # Enable if need to access admin console
      # ENDPOINT: http://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}:3001 # Enable if need to access admin console
      DB_URL: postgres://${PG__LOGTO_MANAGER_USER:-logto_postgres}:${PG__LOGTO_MANAGER_PASSWORD}@${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}:${PG_PORT:-5432}/${PG_DB_NAME:-alp}?options=-c%20search_path%3Dlogto
      ENDPOINT: https://${CADDY__D2E__PUBLIC_FQDN:-${CADDY__ALP__PUBLIC_FQDN:-localhost}}${PORT:+:${PORT:-443}}
      LOGTO_API_M2M_CLIENT_ID: ${LOGTO_API_M2M_CLIENT_ID}
      LOGTO_API_M2M_CLIENT_SECRET: ${LOGTO_API_M2M_CLIENT_SECRET}
      LOGTO_ROLES_AZ_GROUPS_MAPPING: ${LOGTO_ROLES_AZ_GROUPS_MAPPING:-{"role.systemadmin":"37b5364a-348c-494f-b67f-a59a40e52ab7","role.useradmin":"90597adc-92af-4649-8151-68c3b7feb66c","role.dashboardviewer":"50a3b140-21eb-46dd-9d81-61a77cbc81f5"}}
      NODE_ENV: test
      PROJECT_NAME: ${PROJECT_NAME:-d2e}
      PORT: 3001
      TRUST_PROXY_HEADER: true
      DATABASE_CONNECTION_TIMEOUT: 30000

  alp-minerva-postgres:
    container_name: &c17 ${PROJECT_NAME:-d2e}-minerva-postgres-1
    healthcheck:
      interval: 20s
      retries: 30
      start_period: 60s
      test:
        - CMD-SHELL
        - pg_isready
        - -d
        - ${POSTGRES_DB}
      timeout: 30s
    hostname: ${PROJECT_NAME:-d2e}-minerva-postgres-1.${TLS__INTERNAL__DOMAIN:-alp.local}
    image: postgres:15-alpine
    logging:
      options:
        tag: *c17
    networks:
      alp:
        priority: 20
      data:
        priority: 10
      enterprise-gateway:
        priority: 10
      enterprise-gateway-viewer:
        priority: 10
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    command:
      - "postgres"
      - "-c"
      - "wal_level=logical"
      - "-c"
      - "max_connections=1000"
    shm_size: 1gb
    volumes:
      - pg-minerva-data-1:/var/lib/postgresql/data
      - ./cache/pgbackup:/backup
    environment:
      <<:
        - *x-tls
      POSTGRES_DB: ${PG_DB_NAME:-alp}
      POSTGRES_PASSWORD: ${PG_SUPER_PASSWORD}

  demodb:
    image: ohdsi/broadsea-atlasdb:2.2.0
    container_name: ${PROJECT_NAME:-d2e}-demodb
    mem_limit: "2G"
    command: >
      sh -c '
      set -e;
      echo "Starting PostgreSQL in background via original docker-entrypoint.sh...";
      /usr/local/bin/docker-entrypoint.sh postgres -c wal_level=logical &
      PG_PID=$!;
      echo "Waiting for PostgreSQL to become ready...";
        until pg_isready -q -U postgres -h localhost -p 5432; do
          echo "PostgreSQL is unavailable - sleeping for 1 second...";
          sleep 1;
        done;
      echo "PostgreSQL is ready on localhost:5432.";
      echo "Attempting to change password for user postgres...";
      psql -v ON_ERROR_STOP=1 -h localhost -U postgres -d postgres -c "ALTER USER postgres WITH PASSWORD '\''${DEMO__DB_PASSWORD:-mypass}'\'';";
      echo "Adding table cohort_definition to demo_cdm_results schema...";
      psql -v ON_ERROR_STOP=1 -h localhost -U postgres -d postgres -c "CREATE TABLE IF NOT EXISTS demo_cdm_results.cohort_definition ( cohort_definition_id integer NOT NULL, cohort_definition_name varchar(255) NOT NULL, cohort_definition_description text NULL, definition_type_concept_id integer NOT NULL, cohort_definition_syntax text NULL, subject_concept_id integer NOT NULL, cohort_initiation_date date NULL );";
      echo "PostgreSQL setup complete. Bringing PostgreSQL process (PID $PG_PID) to foreground...";
      wait $PG_PID;
      echo "PostgreSQL process (PID $PG_PID) has exited.";
      exit $?;'
    environment:
      POSTGRES_PASSWORD: ${DEMO__DB_PASSWORD:-mypass}
    networks:
      alp:
        priority: 20
      data:
        priority: 10
    platform: linux/amd64
    volumes:
      - demodb-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 10
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    profiles: [demodb]

  redis:
    command: redis-server --requirepass ${REDIS_PASSWORD}
    container_name: &c26 ${PROJECT_NAME:-d2e}-minerva-redis-1
    healthcheck:
      interval: 20s
      retries: 10
      start_period: 60s
      timeout: 30s
      test: redis-cli --no-auth-warning -a ${REDIS_PASSWORD} ping | grep PONG
    hostname: ${PROJECT_NAME:-d2e}-minerva-redis-1.${TLS__INTERNAL__DOMAIN:-alp.local}
    image: redis:7
    logging:
      options:
        tag: *c26
    networks:
      alp:
        priority: 20
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}

  supabase-storage:
    container_name: &c19 ${PROJECT_NAME:-d2e}-supabase-storage-${BASE_PORT:-1}
    depends_on:
      trex:
        condition: service_healthy
      alp-minerva-postgres:
        condition: service_healthy
      alp-minerva-pg-mgmt-init:
        condition: service_completed_successfully
    platform: linux/amd64
    healthcheck:
      test:
        [
          "CMD",
          "wget",
          "--proxy=off",
          "--no-verbose",
          "--tries=1",
          "--spider",
          "http://${PROJECT_NAME:-d2e}-supabase-storage-1:9000/status",
        ]
      timeout: 30s
      interval: 10s
      retries: 20
      start_period: 40s
    # Use custom image with postgresql-client pre-installed for offline capability
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-supabase-storage:${DOCKER_TAG_NAME:-develop}
    logging:
      options:
        tag: *c19
    networks:
      alp:
        priority: 20
      data:
        priority: 10
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    volumes:
      - supabase-storage-data:/var/lib/storage
    entrypoint: ["/bin/sh", "-c"]
    command: >
      "set -e;
      echo 'Waiting 30 seconds for trex migrations...';
      sleep 30;
      echo 'Starting storage server in background (will run migrations)...';
      node dist/start/server.js &
      SERVER_PID=\$!;
      echo 'Waiting 20 seconds for storage migrations to complete...';
      sleep 20;
      echo 'Updating public.objects view to include user_metadata...';
      export PGPASSWORD='${PG_SUPER_PASSWORD}';
      psql -h '${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}' -p '${PG_PORT:-5432}' -U '${PG_SUPER_USER:-postgres}' -d '${PG_DB_NAME:-alp}' -c 'ALTER TABLE IF EXISTS storage.objects DISABLE ROW LEVEL SECURITY;';
      psql -h '${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}' -p '${PG_PORT:-5432}' -U '${PG_SUPER_USER:-postgres}' -d '${PG_DB_NAME:-alp}' -c 'DROP VIEW IF EXISTS public.objects CASCADE;';
      psql -h '${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}' -p '${PG_PORT:-5432}' -U '${PG_SUPER_USER:-postgres}' -d '${PG_DB_NAME:-alp}' -c 'CREATE VIEW public.objects AS SELECT * FROM storage.objects;';
      psql -h '${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}' -p '${PG_PORT:-5432}' -U '${PG_SUPER_USER:-postgres}' -d '${PG_DB_NAME:-alp}' -c 'GRANT SELECT, INSERT, UPDATE, DELETE ON public.objects TO service_role;';
      psql -h '${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}' -p '${PG_PORT:-5432}' -U '${PG_SUPER_USER:-postgres}' -d '${PG_DB_NAME:-alp}' -c 'GRANT SELECT, INSERT, UPDATE, DELETE ON public.objects TO authenticated;';
      psql -h '${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}' -p '${PG_PORT:-5432}' -U '${PG_SUPER_USER:-postgres}' -d '${PG_DB_NAME:-alp}' -c 'GRANT SELECT ON public.objects TO anon;';
      echo 'View setup complete. Storage server continuing...';
      wait \$SERVER_PID;"
    environment:
      <<:
        - *x-tls
      PROJECT_REF: ${PROJECT_REF:-default}
      PORT: 9000
      FILE_SIZE_LIMIT: 1048576000
      DATABASE_URL: postgres://${PG_SUPER_USER:-postgres}:${PG_SUPER_PASSWORD}@${PG_HOST:-${PROJECT_NAME:-d2e}-minerva-postgres-1}:${PG_PORT:-5432}/${PG_DB_NAME:-alp}
      PGRST_JWT_SECRET: ${SUPABASE_STORAGE_JWT_SECRET:-mysecret}
      SERVICE_KEY: ${SUPABASE_STORAGE_JWT_TOKEN:-mytoken}
      MIGRATIONS_SCHEMA: storage
      MIGRATIONS_TABLE: migrations
      SKIP_MIGRATIONS: "false"
      STORAGE_BACKEND: file
      GLOBAL_S3_BUCKET: stub
      GLOBAL_S3_ENDPOINT: http://supabase-storage:9000/s3
      GLOBAL_S3_PROTOCOL: "http"
      GLOBAL_S3_FORCE_PATH_STYLE: true
      FILE_STORAGE_BACKEND_PATH: /var/lib/storage
      TENANT_ID: stub
      REGION: stub

  dicom-server:
    # uses 8042 port on the container
    image: orthancteam/orthanc:24.6.1
    container_name: &c27 ${PROJECT_NAME:-d2e}-dicom-server-${BASE_PORT:-1}
    logging:
      options:
        tag: *c27
    restart: ${DOCKER__RESTART_POLICY:-unless-stopped}
    networks:
      alp:
      data:
    hostname: ${PROJECT_NAME:-d2e}-dicom-server-1.${TLS__INTERNAL__DOMAIN:-alp.local}
    environment:
      VERBOSE_ENABLED: true
      VERBOSE_STARTUP: true
      ORTHANC__NAME: D2E Dicom Service
      ORTHANC__AWS_S3_STORAGE__BUCKET_NAME: dicom-images
      ORTHANC__AWS_S3_STORAGE__ENDPOINT: http://${MINIO__ENDPOINT:-alp-minerva-s3}:${MINIO__PORT:-9000}
      ORTHANC__AWS_S3_STORAGE__ACCESS_KEY: ${MINIO__ACCESS_KEY:-root}
      ORTHANC__AWS_S3_STORAGE__SECRET_KEY: ${MINIO__SECRET_KEY}
      ORTHANC__AWS_S3_STORAGE__VIRTUAL_ADDRESSING: false
      ORTHANC__AWS_S3_STORAGE__REGION: ${MINIO__REGION:-ap-southeast-1}
      ORTHANC__AUTHENTICATION_ENABLED: false
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "/probes/test-aliveness.py --user=healthcheck --pwd=${DICOM__HEALTH_CHECK_PASSWORD}",
        ]
      start_period: 10s
      interval: 20s
      retries: 20
      timeout: 60s
    volumes:
      - dicom-sqlite-storage:/var/lib/orthanc/db
    profiles: [dicom]

  enterprise-gateway:
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-enterprise-gateway:${DOCKER_TAG_NAME:-develop}
    container_name: &c28 ${PROJECT_NAME:-d2e}-enterprise-gateway
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8888/jupyter/api || exit 1"]
      interval: 30s
      timeout: 30s
      retries: 5
      start_period: 30s
    platform: linux/amd64
    logging:
      options:
        tag: *c28
    networks:
      enterprise-gateway:
    user: root # Run as root
    environment:
      <<:
        - *x-enterprise-gateway
      EG_DOCKER_NETWORK: ${EG_DOCKER_NETWORK:-${PROJECT_NAME:-d2e}_enterprise-gateway}
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    labels:
      app: "enterprise-gateway"
      component: "enterprise-gateway"
    deploy:
      replicas: 1
      endpoint_mode: dnsrr
      labels:
        app: "enterprise-gateway"
        component: "enterprise-gateway"

  enterprise-gateway-viewer:
    image: ${DOCKER_IMAGE_PREFIX:-ghcr.io/ohdsi/}d2e-enterprise-gateway:${DOCKER_TAG_NAME:-develop}
    container_name: &c29 ${PROJECT_NAME:-d2e}-enterprise-gateway-viewer
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8888/jupyter/api || exit 1"]
      interval: 30s
      timeout: 30s
      retries: 5
      start_period: 30s
    platform: linux/amd64
    logging:
      options:
        tag: *c29
    networks:
      enterprise-gateway-viewer:
    user: root # Run as root
    environment:
      <<:
        - *x-enterprise-gateway
      EG_DOCKER_NETWORK: ${EG_DOCKER_NETWORK:-${PROJECT_NAME:-d2e}_enterprise-gateway-viewer}
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    labels:
      app: "enterprise-gateway-viewer"
      component: "enterprise-gateway-viewer"
    deploy:
      replicas: 1
      endpoint_mode: dnsrr
      labels:
        app: "enterprise-gateway-viewer"
        component: "enterprise-gateway-viewer"

  mlflow:
    image: ghcr.io/mlflow/mlflow:v3.0.0
    container_name: &c31 ${PROJECT_NAME:-d2e}-mlflow
    platform: linux/amd64
    logging:
      options:
        tag: *c31
    networks:
      alp:
      data:
      enterprise-gateway:
    command: mlflow server --host 0.0.0.0 --port 5000
    profiles: [mlflow]

  hana:
    container_name: hana
    hostname: hana
    image: saplabs/hanaexpress:2.00.082.00.20250528.1
    platform: linux/amd64
    ports:
      - 1128-1129:1128-1129
      - 39013:39013
      - 39017:39017
      - 39041-39045:39041-39045
      - 59013-59014:59013-59014
    volumes:
      - hana-data:/hana/mounts
    networks:
      alp:
      data:
    profiles: [hana]

  logging:
    image: amir20/dozzle:v8.13.9
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    read_only: true
    networks:
      alp:
    environment:
      - DOZZLE_FILTER=name=${PROJECT_NAME:-d2e}
      - DOZZLE_BASE=/dockerlogs
      - DOZZLE_NO_ANALYTICS=true
