services:
  traefik:
    image: traefik:v2.11
    container_name: fedeo-traefik
    restart: unless-stopped
    command:
      - --api.insecure=false
      - --api.dashboard=false
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
      - --certificatesresolvers.letsencrypt.acme.email=${CONTACT_EMAIL}
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --accesslog=true
      - --accesslog.filepath=/logs/access.log
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./traefik/letsencrypt:/letsencrypt
      - ./traefik/logs:/logs
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - web

  db:
    image: postgres:16
    container_name: fedeo-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - internal

  minio:
    image: minio/minio:latest
    container_name: fedeo-minio
    restart: unless-stopped
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    volumes:
      - ./minio:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - internal

  createbuckets:
    image: minio/mc:latest
    container_name: fedeo-minio-init
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      mc alias set local http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
      mc mb --ignore-existing local/${MINIO_BUCKET};
      mc anonymous set private local/${MINIO_BUCKET};
      exit 0;
      "
    restart: "no"
    networks:
      - internal

  backend:
    image: git.federspiel.tech/flfeders/fedeo/backend:dev
    container_name: fedeo-backend
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      minio:
        condition: service_healthy
      createbuckets:
        condition: service_completed_successfully
    environment:
      NODE_ENV: production
      FEDEO_RUN_MIGRATIONS: ${FEDEO_RUN_MIGRATIONS:-true}
      HOST: ${HOST:-0.0.0.0}
      PORT: ${PORT:-3100}
      COOKIE_SECRET: ${COOKIE_SECRET}
      JWT_SECRET: ${JWT_SECRET}
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}
      DATABASE_URL: ${DATABASE_URL}
      MAILER_SMTP_HOST: ${MAILER_SMTP_HOST}
      MAILER_SMTP_PORT: ${MAILER_SMTP_PORT}
      MAILER_SMTP_SSL: ${MAILER_SMTP_SSL}
      MAILER_SMTP_USER: ${MAILER_SMTP_USER}
      MAILER_SMTP_PASS: ${MAILER_SMTP_PASS}
      MAILER_FROM: ${MAILER_FROM}
      S3_ENDPOINT: ${S3_ENDPOINT}
      S3_REGION: ${S3_REGION}
      S3_ACCESS_KEY: ${S3_ACCESS_KEY}
      S3_SECRET_KEY: ${S3_SECRET_KEY}
      S3_BUCKET: ${S3_BUCKET}
      FEDEO_FILE_BACKEND: ${FEDEO_FILE_BACKEND:-s3}
      SEAFILE_BASE_URL: ${SEAFILE_BASE_URL}
      SEAFILE_INTERNAL_URL: ${SEAFILE_INTERNAL_URL}
      SEAFILE_ADMIN_EMAIL: ${SEAFILE_ADMIN_EMAIL}
      SEAFILE_ADMIN_PASSWORD: ${SEAFILE_ADMIN_PASSWORD}
      M2M_API_KEY: ${M2M_API_KEY}
      API_BASE_URL: ${API_BASE_URL}
      GOCARDLESS_BASE_URL: ${GOCARDLESS_BASE_URL}
      GOCARDLESS_SECRET_ID: ${GOCARDLESS_SECRET_ID}
      GOCARDLESS_SECRET_KEY: ${GOCARDLESS_SECRET_KEY}
      DOKUBOX_IMAP_HOST: ${DOKUBOX_IMAP_HOST}
      DOKUBOX_IMAP_PORT: ${DOKUBOX_IMAP_PORT}
      DOKUBOX_IMAP_SECURE: ${DOKUBOX_IMAP_SECURE}
      DOKUBOX_IMAP_USER: ${DOKUBOX_IMAP_USER}
      DOKUBOX_IMAP_PASSWORD: ${DOKUBOX_IMAP_PASSWORD}
      OPENAI_API_KEY: ${OPENAI_API_KEY}
      STIRLING_API_KEY: ${STIRLING_API_KEY}
      FEDEO_BOOTSTRAP_ADMIN_EMAIL: ${FEDEO_BOOTSTRAP_ADMIN_EMAIL:-}
      FEDEO_BOOTSTRAP_ADMIN_PASSWORD: ${FEDEO_BOOTSTRAP_ADMIN_PASSWORD:-}
      FEDEO_BOOTSTRAP_ADMIN_FIRST_NAME: ${FEDEO_BOOTSTRAP_ADMIN_FIRST_NAME:-Admin}
      FEDEO_BOOTSTRAP_ADMIN_LAST_NAME: ${FEDEO_BOOTSTRAP_ADMIN_LAST_NAME:-Benutzer}
      FEDEO_BOOTSTRAP_TENANT_NAME: ${FEDEO_BOOTSTRAP_TENANT_NAME:-FEDEO}
      FEDEO_BOOTSTRAP_TENANT_SHORT: ${FEDEO_BOOTSTRAP_TENANT_SHORT:-FEDEO}
      MATRIX_HOMESERVER_URL: ${MATRIX_HOMESERVER_URL:-http://matrix-synapse:8008}
      MATRIX_SERVER_NAME: ${MATRIX_SERVER_NAME:-${DOMAIN}}
      MATRIX_RTC_HOST: ${MATRIX_RTC_HOST:-${DOMAIN}}
      MATRIX_RTC_JWT_URL: ${MATRIX_RTC_JWT_URL:-}
      MATRIX_LIVEKIT_URL: ${MATRIX_LIVEKIT_URL:-}
      MATRIX_REGISTRATION_SHARED_SECRET: ${MATRIX_REGISTRATION_SHARED_SECRET:-change-this-matrix-registration-secret}
      MATRIX_SERVICE_USER_LOCALPART: ${MATRIX_SERVICE_USER_LOCALPART:-fedeo_service}
      LIVEKIT_KEY: ${LIVEKIT_KEY:-fedeo-livekit}
      LIVEKIT_SECRET: ${LIVEKIT_SECRET:-change-this-livekit-secret-please-replace}
      NODE_EXPORTER_URL: ${NODE_EXPORTER_URL:-http://node-exporter:9100}
    labels:
      - traefik.enable=true
      - traefik.http.routers.fedeo-backend.rule=Host(`${DOMAIN}`) && PathPrefix(`/backend`)
      - traefik.http.routers.fedeo-backend.entrypoints=websecure
      - traefik.http.routers.fedeo-backend.tls.certresolver=letsencrypt
      - traefik.http.middlewares.fedeo-backend-strip.stripprefix.prefixes=/backend
      - traefik.http.routers.fedeo-backend.middlewares=fedeo-backend-strip
      - traefik.http.services.fedeo-backend.loadbalancer.server.port=3100
      - traefik.docker.network=fedeo_web
    networks:
      - web
      - internal

  node-exporter:
    image: prom/node-exporter:v1.8.2
    container_name: fedeo-node-exporter
    restart: unless-stopped
    command:
      - --path.procfs=/host/proc
      - --path.sysfs=/host/sys
      - --path.rootfs=/rootfs
      - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro,rslave
    networks:
      - internal

  frontend:
    image: git.federspiel.tech/flfeders/fedeo/frontend:dev
    container_name: fedeo-frontend
    restart: unless-stopped
    depends_on:
      - backend
    environment:
      NODE_ENV: production
      NUXT_PUBLIC_API_BASE: https://${DOMAIN}/backend
      NUXT_PUBLIC_PDF_LICENSE: ${NUXT_PUBLIC_PDF_LICENSE}
      NUXT_PUBLIC_MATRIX_ELEMENT_URL: ${NUXT_PUBLIC_MATRIX_ELEMENT_URL:-}
    labels:
      - traefik.enable=true
      - traefik.http.routers.fedeo-frontend.rule=Host(`${DOMAIN}`)
      - traefik.http.routers.fedeo-frontend.entrypoints=websecure
      - traefik.http.routers.fedeo-frontend.tls.certresolver=letsencrypt
      - traefik.http.routers.fedeo-frontend.priority=1
      - traefik.http.services.fedeo-frontend.loadbalancer.server.port=3000
      - traefik.docker.network=fedeo_web
    networks:
      - web

  matrix-db:
    image: postgres:16-alpine
    container_name: fedeo-matrix-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${MATRIX_POSTGRES_DB:-synapse}
      POSTGRES_USER: ${MATRIX_POSTGRES_USER:-synapse}
      POSTGRES_PASSWORD: ${MATRIX_POSTGRES_PASSWORD:-change-this-matrix-db-password}
      POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate=C --lc-ctype=C
    volumes:
      - ./matrix/postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${MATRIX_POSTGRES_USER:-synapse} -d ${MATRIX_POSTGRES_DB:-synapse}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - internal

  matrix-redis:
    image: redis:7-alpine
    container_name: fedeo-matrix-redis
    restart: unless-stopped
    networks:
      - internal

  matrix-synapse:
    image: ghcr.io/element-hq/synapse:latest
    container_name: fedeo-matrix-synapse
    restart: unless-stopped
    depends_on:
      matrix-db:
        condition: service_healthy
      matrix-redis:
        condition: service_started
    environment:
      DOMAIN: ${DOMAIN}
      MATRIX_POSTGRES_DB: ${MATRIX_POSTGRES_DB:-synapse}
      MATRIX_POSTGRES_USER: ${MATRIX_POSTGRES_USER:-synapse}
      MATRIX_POSTGRES_PASSWORD: ${MATRIX_POSTGRES_PASSWORD:-change-this-matrix-db-password}
      MATRIX_REGISTRATION_SHARED_SECRET: ${MATRIX_REGISTRATION_SHARED_SECRET:-change-this-matrix-registration-secret}
      MATRIX_SERVER_NAME: ${MATRIX_SERVER_NAME:-${DOMAIN}}
      MATRIX_TURN_SHARED_SECRET: ${MATRIX_TURN_SHARED_SECRET:-change-this-turn-secret}
      SYNAPSE_CONFIG_PATH: /data/homeserver.yaml
      SYNAPSE_REPORT_STATS: "no"
      SYNAPSE_SERVER_NAME: ${MATRIX_SERVER_NAME:-${DOMAIN}}
    entrypoint: /bin/sh
    command:
      - -ec
      - |
        if [ ! -f /data/homeserver.yaml ]; then
          /start.py generate
        fi
        python - <<'PY'
        import os
        import yaml

        path = "/data/homeserver.yaml"
        with open(path, "r", encoding="utf-8") as handle:
            config = yaml.safe_load(handle) or {}

        domain = os.environ["DOMAIN"]
        server_name = os.environ.get("MATRIX_SERVER_NAME") or domain
        config["server_name"] = server_name
        config["public_baseurl"] = f"https://{domain}/"
        config["database"] = {
            "name": "psycopg2",
            "args": {
                "user": os.environ.get("MATRIX_POSTGRES_USER", "synapse"),
                "password": os.environ["MATRIX_POSTGRES_PASSWORD"],
                "database": os.environ.get("MATRIX_POSTGRES_DB", "synapse"),
                "host": "matrix-db",
                "cp_min": 5,
                "cp_max": 10,
            },
        }
        config["redis"] = {"enabled": True, "host": "matrix-redis"}
        config["registration_shared_secret"] = os.environ["MATRIX_REGISTRATION_SHARED_SECRET"]
        config["turn_uris"] = [
            f"turn:{domain}:3478?transport=udp",
            f"turn:{domain}:3478?transport=tcp",
        ]
        config["turn_shared_secret"] = os.environ["MATRIX_TURN_SHARED_SECRET"]
        config["turn_user_lifetime"] = "1h"
        config["enable_registration"] = False
        config["experimental_features"] = {
            **(config.get("experimental_features") or {}),
            "msc3266_enabled": True,
            "msc4222_enabled": True,
        }
        config["login_via_existing_session"] = {
            "enabled": True,
            "require_ui_auth": False,
            "token_timeout": "5m",
        }
        config["max_event_delay_duration"] = "24h"
        config["rc_message"] = {"per_second": 0.5, "burst_count": 30}
        config["rc_delayed_event_mgmt"] = {"per_second": 1, "burst_count": 20}

        with open(path, "w", encoding="utf-8") as handle:
            yaml.safe_dump(config, handle, sort_keys=False)
        PY
        exec /start.py
    volumes:
      - ./matrix/synapse:/data
    labels:
      - traefik.enable=true
      - traefik.http.routers.fedeo-matrix.rule=Host(`${DOMAIN}`) && PathPrefix(`/_matrix`)
      - traefik.http.routers.fedeo-matrix.entrypoints=websecure
      - traefik.http.routers.fedeo-matrix.tls.certresolver=letsencrypt
      - traefik.http.services.fedeo-matrix.loadbalancer.server.port=8008
      - traefik.docker.network=fedeo_web
    networks:
      - web
      - internal

  matrix-well-known:
    image: nginx:1.27-alpine
    container_name: fedeo-matrix-well-known
    restart: unless-stopped
    command:
      - /bin/sh
      - -ec
      - |
        mkdir -p /usr/share/nginx/html/.well-known/matrix
        cat >/usr/share/nginx/html/.well-known/matrix/client <<EOF
        {
          "m.homeserver": {
            "base_url": "https://${DOMAIN}"
          },
          "org.matrix.msc4143.rtc_foci": [
            {
              "type": "livekit",
              "livekit_service_url": "https://${DOMAIN}/livekit/jwt"
            }
          ]
        }
        EOF
        cat >/usr/share/nginx/html/.well-known/matrix/server <<EOF
        {
          "m.server": "${MATRIX_SERVER_NAME:-${DOMAIN}}:443"
        }
        EOF
        exec nginx -g 'daemon off;'
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.fedeo-matrix-well-known-cors.headers.accesscontrolalloworiginlist=*
      - traefik.http.middlewares.fedeo-matrix-well-known-cors.headers.accesscontrolallowmethods=GET,OPTIONS
      - traefik.http.middlewares.fedeo-matrix-well-known-cors.headers.accesscontrolallowheaders=Content-Type,Authorization
      - traefik.http.routers.fedeo-matrix-well-known.rule=Host(`${DOMAIN}`) && PathPrefix(`/.well-known/matrix`)
      - traefik.http.routers.fedeo-matrix-well-known.entrypoints=websecure
      - traefik.http.routers.fedeo-matrix-well-known.tls.certresolver=letsencrypt
      - traefik.http.routers.fedeo-matrix-well-known.middlewares=fedeo-matrix-well-known-cors
      - traefik.http.services.fedeo-matrix-well-known.loadbalancer.server.port=80
      - traefik.docker.network=fedeo_web
    networks:
      - web

  matrix-turn:
    image: instrumentisto/coturn:4
    container_name: fedeo-matrix-turn
    restart: unless-stopped
    command:
      - --fingerprint
      - --use-auth-secret
      - --static-auth-secret=${MATRIX_TURN_SHARED_SECRET:-change-this-turn-secret}
      - --realm=${MATRIX_SERVER_NAME:-${DOMAIN}}
      - --listening-port=3478
      - --tls-listening-port=5349
      - --min-port=49160
      - --max-port=49200
      - --no-cli
      - --no-tlsv1
      - --no-tlsv1_1
    ports:
      - "3478:3478/tcp"
      - "3478:3478/udp"
      - "5349:5349/tcp"
      - "49160-49200:49160-49200/udp"
    networks:
      - internal

  matrix-livekit:
    image: livekit/livekit-server:v1.9
    container_name: fedeo-matrix-livekit
    restart: unless-stopped
    depends_on:
      - matrix-redis
    entrypoint: /bin/sh
    command:
      - -ec
      - |
        cat >/tmp/livekit.yaml <<EOF
        port: 7880
        redis:
          address: matrix-redis:6379
        rtc:
          tcp_port: 7881
          port_range_start: 50000
          port_range_end: 50100
          use_external_ip: true
        keys:
          ${LIVEKIT_KEY:-fedeo-livekit}: ${LIVEKIT_SECRET:-change-this-livekit-secret-please-replace}
        room:
          auto_create: true
        EOF
        exec /livekit-server --config /tmp/livekit.yaml
    ports:
      - "7881:7881/tcp"
      - "50000-50100:50000-50100/udp"
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.fedeo-matrix-livekit-strip.stripprefix.prefixes=/livekit/sfu
      - traefik.http.routers.fedeo-matrix-livekit.rule=Host(`${DOMAIN}`) && PathPrefix(`/livekit/sfu`)
      - traefik.http.routers.fedeo-matrix-livekit.entrypoints=websecure
      - traefik.http.routers.fedeo-matrix-livekit.tls.certresolver=letsencrypt
      - traefik.http.routers.fedeo-matrix-livekit.middlewares=fedeo-matrix-livekit-strip
      - traefik.http.services.fedeo-matrix-livekit.loadbalancer.server.port=7880
      - traefik.docker.network=fedeo_web
    networks:
      - web
      - internal

  matrix-rtc-jwt:
    image: ghcr.io/element-hq/lk-jwt-service:latest
    container_name: fedeo-matrix-rtc-jwt
    restart: unless-stopped
    depends_on:
      - matrix-livekit
      - matrix-synapse
    environment:
      LIVEKIT_URL: wss://${DOMAIN}/livekit/sfu
      LIVEKIT_KEY: ${LIVEKIT_KEY:-fedeo-livekit}
      LIVEKIT_SECRET: ${LIVEKIT_SECRET:-change-this-livekit-secret-please-replace}
      LIVEKIT_FULL_ACCESS_HOMESERVERS: ${MATRIX_SERVER_NAME:-${DOMAIN}}
      LIVEKIT_JWT_BIND: :8080
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.fedeo-matrix-rtc-jwt-strip.stripprefix.prefixes=/livekit/jwt
      - traefik.http.routers.fedeo-matrix-rtc-jwt.rule=Host(`${DOMAIN}`) && PathPrefix(`/livekit/jwt`)
      - traefik.http.routers.fedeo-matrix-rtc-jwt.entrypoints=websecure
      - traefik.http.routers.fedeo-matrix-rtc-jwt.tls.certresolver=letsencrypt
      - traefik.http.routers.fedeo-matrix-rtc-jwt.middlewares=fedeo-matrix-rtc-jwt-strip
      - traefik.http.services.fedeo-matrix-rtc-jwt.loadbalancer.server.port=8080
      - traefik.docker.network=fedeo_web
    networks:
      - web
      - internal

  matrix-element:
    image: vectorim/element-web:latest
    container_name: fedeo-matrix-element
    user: "0:0"
    restart: unless-stopped
    entrypoint: /bin/sh
    command:
      - -ec
      - |
        cat >/app/config.json <<EOF
        {
          "default_server_config": {
            "m.homeserver": {
              "base_url": "https://${DOMAIN}",
              "server_name": "${MATRIX_SERVER_NAME:-${DOMAIN}}"
            }
          },
          "org.matrix.msc4143.rtc_foci": [
            {
              "type": "livekit",
              "livekit_service_url": "https://${DOMAIN}/livekit/jwt"
            }
          ],
          "disable_custom_urls": false,
          "disable_guests": true,
          "brand": "FEDEO Matrix",
          "default_theme": "light",
          "features": {
            "feature_video_rooms": true
          }
        }
        EOF
        exec nginx -g 'daemon off;'
    labels:
      - traefik.enable=true
      - traefik.http.routers.fedeo-matrix-element.rule=Host(`${DOMAIN}`) && PathPrefix(`/element`)
      - traefik.http.routers.fedeo-matrix-element.entrypoints=websecure
      - traefik.http.routers.fedeo-matrix-element.tls.certresolver=letsencrypt
      - traefik.http.middlewares.fedeo-matrix-element-strip.stripprefix.prefixes=/element
      - traefik.http.routers.fedeo-matrix-element.middlewares=fedeo-matrix-element-strip
      - traefik.http.services.fedeo-matrix-element.loadbalancer.server.port=80
      - traefik.docker.network=fedeo_web
    networks:
      - web

networks:
  web:
    name: fedeo_web
    driver: bridge
  internal:
    name: fedeo_internal
    driver: bridge
