# ==========================================================================
# LoreMindMJ - Stack complete pour distribution utilisateur
# --------------------------------------------------------------------------
# Lancement  : docker compose up -d
# Acces      : http://localhost:8081
# Mise a jour: docker compose pull && docker compose up -d
# ==========================================================================

services:
  postgres:
    image: postgres:16-alpine
    container_name: loremind-postgres
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-loremind}
      POSTGRES_USER: ${POSTGRES_USER:-loremind}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-loremind}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  minio:
    image: minio/minio:latest
    container_name: loremind-minio
    environment:
      MINIO_ROOT_USER: ${MINIO_USER:-minioadmin}
      MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD:-minioadmin}
    volumes:
      - minio-data:/data
    # Mapping bind sur loopback pour autoriser un core/web lance en local (mode dev)
    # a atteindre MinIO. Invisible sur le LAN donc non-exploitable depuis l'exterieur.
    ports:
      - "127.0.0.1:9000:9000"
      - "127.0.0.1:9001:9001"
    command: server /data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 10s
      timeout: 5s
      retries: 3
    restart: unless-stopped

  # Creation automatique du bucket loremind-images au premier lancement.
  minio-init:
    image: minio/mc:latest
    container_name: loremind-minio-init
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      mc alias set local http://minio:9000 ${MINIO_USER:-minioadmin} ${MINIO_PASSWORD:-minioadmin} &&
      mc mb --ignore-existing local/loremind-images &&
      mc anonymous set download local/loremind-images &&
      echo 'Bucket loremind-images pret.'
      "

  core:
    # Defaut : GHCR (registry public, reputation domaine elevee).
    # Pour les anciennes installs qui pointaient sur Gitea, REGISTRY et
    # IMAGE_NAMESPACE peuvent etre overrides dans .env :
    #   REGISTRY=git.igmlcreation.fr
    #   IMAGE_NAMESPACE=ietm64/  (le slash final est important : voir image: ci-dessous)
    image: ${REGISTRY:-ghcr.io}/${IMAGE_NAMESPACE:-igmlcreation/loremind-}core:${TAG:-latest}
    container_name: loremind-core
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    depends_on:
      postgres:
        condition: service_healthy
      minio:
        condition: service_healthy
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB:-loremind}
      SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-loremind}
      SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
      APP_CORS_ALLOWED_ORIGINS: http://localhost:${WEB_PORT:-8081}
      BRAIN_BASE_URL: http://brain:8000
      BRAIN_INTERNAL_SECRET: ${BRAIN_INTERNAL_SECRET:?set BRAIN_INTERNAL_SECRET in .env}
      ADMIN_USERNAME: ${ADMIN_USERNAME:-admin}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD:?set ADMIN_PASSWORD in .env}
      MINIO_ENDPOINT: http://minio:9000
      MINIO_ACCESS_KEY: ${MINIO_USER:-minioadmin}
      MINIO_SECRET_KEY: ${MINIO_PASSWORD:-minioadmin}
      # Detection des mises a jour : interroge le registry et delegue le pull/restart
      # a Watchtower. Si WATCHTOWER_TOKEN est vide, la feature est desactivee
      # (l'UI masque le badge et le bouton).
      UPDATE_CHECK_REGISTRY: ${REGISTRY:-ghcr.io}
      UPDATE_CHECK_IMAGES: ${IMAGE_NAMESPACE:-igmlcreation/loremind-}core,${IMAGE_NAMESPACE:-igmlcreation/loremind-}brain,${IMAGE_NAMESPACE:-igmlcreation/loremind-}web
      UPDATE_CHECK_TAG: ${TAG:-latest}
      WATCHTOWER_URL: http://watchtower:8080
      WATCHTOWER_TOKEN: ${WATCHTOWER_TOKEN:-}
      # Licensing : la cle publique JWT est embarquee dans le binaire
      # (core/src/main/resources/licensing/jwt-public-key.pem).
      # LICENSING_JWT_PUBLIC_KEY est un override optionnel (rotation de cle
      # sans rebuild) - non defini par defaut.
      LICENSING_JWT_PUBLIC_KEY: ${LICENSING_JWT_PUBLIC_KEY:-}
      LICENSING_RELAY_BASE_URL: ${LICENSING_RELAY_BASE_URL:-https://loremind-auth.igmlcreation.fr}
      # Chemin du docker config.json partage avec Watchtower
      LICENSING_DOCKER_CONFIG_PATH: /shared/docker/config.json
      # Chemin du repertoire partage avec le switcher (commande + resultat).
      # Doit matcher le volume `switcher-data` monte ci-dessous.
      SWITCHER_DATA_PATH: /shared/switcher
    volumes:
      # Volume partage avec Watchtower : Core ecrit les credentials registry
      # GHCR (recus du relais) ici, Watchtower les utilise pour pull les images
      # privees du canal beta. Pas de creds = no-op.
      - docker-config:/shared/docker
      # Volume partage avec le switcher : Core ecrit une commande de switch
      # de canal ici (command.json), le switcher la traite et y depose son
      # resultat (result.json). Cf. service `switcher` ci-dessous.
      - switcher-data:/shared/switcher
    restart: unless-stopped

  # Ollama embarque (option par defaut pour les utilisateurs sans Ollama installe).
  # Active via COMPOSE_PROFILES=local-ollama (gere par l'installeur).
  # Si l'utilisateur a deja Ollama sur l'hote, ce service reste inactif et
  # OLLAMA_BASE_URL pointe vers http://host.docker.internal:11434.
  ollama:
    image: ollama/ollama:latest
    container_name: loremind-ollama
    profiles: ["local-ollama"]
    volumes:
      - ollama-data:/root/.ollama
    # Port expose sur loopback uniquement pour debug / pull manuel de modeles.
    ports:
      - "127.0.0.1:11434:11434"
    # GPU NVIDIA si disponible (silencieusement ignore sinon).
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    restart: unless-stopped

  brain:
    image: ${REGISTRY:-ghcr.io}/${IMAGE_NAMESPACE:-igmlcreation/loremind-}brain:${TAG:-latest}
    container_name: loremind-brain
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    environment:
      LLM_PROVIDER: ${LLM_PROVIDER:-ollama}
      # Defaut = Ollama embarque (service ollama du compose).
      # L'installeur reecrit cette valeur en http://host.docker.internal:11434
      # si l'utilisateur choisit le mode "Ollama deja installe sur l'hote".
      OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
      LLM_MODEL: ${LLM_MODEL:-gemma4:26b}
      ONEMIN_API_KEY: ${ONEMIN_API_KEY:-}
      ONEMIN_MODEL: ${ONEMIN_MODEL:-gpt-4o-mini}
      INTERNAL_SHARED_SECRET: ${BRAIN_INTERNAL_SECRET:?set BRAIN_INTERNAL_SECRET in .env}
    volumes:
      - brain-data:/app/data
    extra_hosts:
      # Linux : permet au conteneur d'atteindre Ollama sur l'hote.
      # Mac/Windows Docker Desktop le fait nativement.
      - "host.docker.internal:host-gateway"
    restart: unless-stopped

  web:
    image: ${REGISTRY:-ghcr.io}/${IMAGE_NAMESPACE:-igmlcreation/loremind-}web:${TAG:-latest}
    container_name: loremind-web
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    depends_on:
      - core
      - brain
    ports:
      - "${WEB_PORT:-8081}:80"
    restart: unless-stopped

  # Sidecar de bascule de canal (stable <-> beta).
  #
  # Pourquoi : la bascule entre canaux change le PREFIXE d'image (loremind- vs
  # loremind-beta-), donc Watchtower seul ne peut pas la faire — il met a jour
  # des images, pas leur reference. Ce sidecar fait le `sed .env` + le
  # `docker compose pull/up -d` quand le Core depose une commande JSON.
  #
  # Securite : pas de port expose. La commande arrive via volume partage
  # (`switcher-data`) que SEUL le Core ecrit. Le switcher valide strictement
  # le contenu (channel ∈ {stable, beta}, rien d'autre) — pas de RCE via
  # compromission du Core.
  #
  # L'image switcher est volontairement HORS de IMAGE_NAMESPACE : elle reste
  # `igmlcreation/loremind-switcher` sur les deux canaux. Sinon le switcher
  # se tuerait lui-meme pendant le `docker compose up -d` (race condition).
  switcher:
    image: ghcr.io/igmlcreation/loremind-switcher:${SWITCHER_TAG:-latest}
    container_name: loremind-switcher
    # PAS de label watchtower : la maj du switcher se fait via le canal
    # stable uniquement, et hors du flow d'auto-update.
    volumes:
      # Socket Docker du host : permet de lancer docker compose pull/up.
      - /var/run/docker.sock:/var/run/docker.sock
      # Repertoire compose du host (docker-compose.yml + .env) — RW pour
      # pouvoir sed la ligne IMAGE_NAMESPACE.
      - ${COMPOSE_PROJECT_DIR:-./}:/compose
      # Volume partage avec le Core pour la commande + le resultat.
      - switcher-data:/data
      # Volume partage avec le Core + Watchtower : contient config.json avec
      # les creds GHCR (ecrits par le Core a partir du token Patreon).
      # Indispensable pour pull les images privees du canal beta.
      - docker-config:/shared/docker
    environment:
      # Repertoire interne ou trouver docker-compose.yml et .env. Bind au
      # volume ci-dessus (COMPOSE_PROJECT_DIR = repertoire d'install du host).
      COMPOSE_DIR: /compose
      # Nom de projet docker compose : fixe ici pour que le switcher cible
      # le MEME stack que celui qui tourne (sinon il creerait un duplicate).
      # Doit matcher le `name:` (en V2.x) ou le nom du dossier du host.
      COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME:-loremind}
      # Indique au CLI Docker du switcher ou trouver config.json (auth GHCR
      # pour les images privees beta). Meme mecanisme que sur Watchtower.
      DOCKER_CONFIG: /shared/docker
    restart: unless-stopped

  # Mises a jour automatiques des images core/brain/web.
  # Active uniquement si COMPOSE_PROFILES=autoupdate (gere par l'installeur).
  # Postgres et MinIO sont volontairement exclus (donnees persistantes,
  # compatibilite de version a verifier manuellement).
  watchtower:
    # Fork maintenu de containrrr/watchtower (l'original est abandonne depuis
    # ~2023 et son client Docker API est trop vieux pour les versions recentes
    # de Docker Desktop -- erreur "client version 1.25 is too old").
    # nickfedor/watchtower est un drop-in : memes variables d'environnement,
    # meme API HTTP, juste l'image change.
    image: nickfedor/watchtower:latest
    container_name: loremind-watchtower
    profiles: ["autoupdate"]
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      # Volume partage avec Core : credentials registry GHCR (canal beta).
      # Watchtower lit le config.json depuis DOCKER_CONFIG.
      - docker-config:/shared/docker
    environment:
      # Indique a Watchtower (et au CLI Docker embarque) ou trouver le
      # config.json. Active automatiquement l'auth GHCR pour les images
      # du canal beta des que Core a ecrit le fichier.
      DOCKER_CONFIG: /shared/docker
      WATCHTOWER_LABEL_ENABLE: "true"
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_INCLUDE_RESTARTING: "true"
      # MONITOR_ONLY=true => detecte sans appliquer (l'UI declenche manuellement).
      # MONITOR_ONLY=false => applique automatiquement selon WATCHTOWER_SCHEDULE.
      WATCHTOWER_MONITOR_ONLY: "${WATCHTOWER_MONITOR_ONLY:-false}"
      WATCHTOWER_SCHEDULE: "${WATCHTOWER_SCHEDULE:-0 0 4 * * *}"
      # API HTTP pour declenchement manuel via le bouton UI (Core -> Watchtower).
      WATCHTOWER_HTTP_API_UPDATE: "true"
      WATCHTOWER_HTTP_API_PERIODIC_POLLS: "true"
      # Pas de ":?" ici : Compose interpole TOUT le fichier avant de filtrer
      # par profile, donc un ":?" planterait le "up" meme quand autoupdate est
      # inactif. L'installeur genere toujours WATCHTOWER_TOKEN ; defaut vide
      # pour ne rien casser sur les deploiements sans autoupdate.
      WATCHTOWER_HTTP_API_TOKEN: "${WATCHTOWER_TOKEN:-}"
      WATCHTOWER_TIMEOUT: 60s
      WATCHTOWER_NOTIFICATIONS_LEVEL: info
      TZ: ${TZ:-Europe/Paris}
    restart: unless-stopped

volumes:
  postgres-data:
  minio-data:
  brain-data:
  ollama-data:
  # Volume partage Core <-> Watchtower : config.json Docker pour
  # l'authentification au registry prive GHCR (canal beta Patreon).
  docker-config:
  # Volume partage Core <-> Switcher : commande de bascule de canal + resultat.
  switcher-data:
