networks:
  proxy:
    name: proxy
  internal:
    name: internal

volumes:
  traefik_letsencrypt:
  n8n_data:
  postgres_data:
  redis_data:
  portainer_data:
  gitea_data:

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --providers.docker.network=proxy
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.le.acme.email=${ACME_EMAIL}
      - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.le.acme.httpchallenge=true
      - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
      - --log.level=INFO
      - --accesslog=true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik_letsencrypt:/letsencrypt
    networks:
      - proxy

  postgres:
    image: postgres:16-alpine
    container_name: n8n-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./postgres/init:/docker-entrypoint-initdb.d:ro
    networks:
      - internal
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    container_name: n8n-redis
    restart: unless-stopped
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--appendonly", "yes"]
    volumes:
      - redis_data:/data
    networks:
      - internal
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 10

  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_LISTEN_ADDRESS=0.0.0.0
      - N8N_PROTOCOL=https
      - N8N_EDITOR_BASE_URL=https://${N8N_HOST}/
      - WEBHOOK_URL=https://${N8N_HOST}/
      - GENERIC_TIMEZONE=${TIMEZONE}
      - TZ=${TIMEZONE}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_RUNNERS_ENABLED=true
      - N8N_PROXY_HOPS=1
      - N8N_CACHE_BACKEND=redis
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
      - QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}
      - HTTP_PROXY=http://xray:8080
      - HTTPS_PROXY=http://xray:8080
      - NO_PROXY=localhost,127.0.0.1,postgres,redis
      - N8N_DIAGNOSTICS_ENABLED=false
      - N8N_VERSION_NOTIFICATIONS_ENABLED=false
      - N8N_TEMPLATES_ENABLED=false
      - EXTERNAL_FRONTEND_HOOKS_URLS=
      - N8N_DIAGNOSTICS_CONFIG_FRONTEND=
      - N8N_DIAGNOSTICS_CONFIG_BACKEND=
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - proxy
      - internal
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.n8n.rule=Host(`${N8N_HOST}`)
      - traefik.http.routers.n8n.entrypoints=websecure
      - traefik.http.routers.n8n.tls=true
      - traefik.http.routers.n8n.tls.certresolver=le
      - traefik.http.services.n8n.loadbalancer.server.port=5678

  tunnel:
    image: jnovack/autossh:latest
    container_name: n8n-tunnel
    restart: unless-stopped
    environment:
      SSH_REMOTE_USER: tunnel
      SSH_REMOTE_HOST: 94.247.214.20
      SSH_REMOTE_PORT: 22
      SSH_TUNNEL_PORT: 5678
      SSH_TARGET_HOST: n8n
      SSH_TARGET_PORT: 5678
      SSH_MODE: "-R"
    volumes:
      - ./ssh/tunnel_key:/id_rsa:ro
    networks:
      - internal

  xray:
    image: ghcr.io/xtls/xray-core:latest
    container_name: xray
    restart: unless-stopped
    volumes:
      - ./xray/config.json:/etc/xray/config.json:ro
    networks:
      - internal
    command: ["-config", "/etc/xray/config.json"]

  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__server__DOMAIN=${GITEA_HOST}
      - GITEA__server__ROOT_URL=https://${GITEA_HOST}/
      - GITEA__server__SSH_DOMAIN=${GITEA_HOST}
      - GITEA__server__SSH_PORT=222
      - GITEA__server__SSH_LISTEN_PORT=22
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=postgres:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
      - GITEA__service__DISABLE_REGISTRATION=true
    volumes:
      - gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "222:22"
    networks:
      - proxy
      - internal
    depends_on:
      postgres:
        condition: service_healthy
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.gitea.rule=Host(`${GITEA_HOST}`)
      - traefik.http.routers.gitea.entrypoints=websecure
      - traefik.http.routers.gitea.tls=true
      - traefik.http.routers.gitea.tls.certresolver=le
      - traefik.http.services.gitea.loadbalancer.server.port=3000

  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    command: -H unix:///var/run/docker.sock
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.portainer.rule=Host(`${PORTAINER_HOST}`)
      - traefik.http.routers.portainer.entrypoints=websecure
      - traefik.http.routers.portainer.tls=true
      - traefik.http.routers.portainer.tls.certresolver=le
      - traefik.http.services.portainer.loadbalancer.server.port=9000

  # --- Grabcevo (статический лендинг «База Грабцево») -----------------------
  # Продающий сайт производственно-складского комплекса. Чистая статика
  # (index.html + assets/), отдаётся nginx. Домен — кириллический (IDN),
  # traefik/Let's Encrypt работают по punycode-форме из ${GRABCEVO_HOST}.
  grabcevo:
    image: nginx:alpine
    container_name: grabcevo
    restart: unless-stopped
    volumes:
      - ./grabcevo/site:/usr/share/nginx/html:ro
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.grabcevo.rule=Host(`${GRABCEVO_HOST}`) || Host(`www.${GRABCEVO_HOST}`)
      - traefik.http.routers.grabcevo.entrypoints=websecure
      - traefik.http.routers.grabcevo.tls=true
      - traefik.http.routers.grabcevo.tls.certresolver=le
      # www → apex (301)
      - traefik.http.routers.grabcevo.middlewares=grabcevo-wwwredirect
      - traefik.http.middlewares.grabcevo-wwwredirect.redirectregex.regex=^https?://www\.(.*)
      - traefik.http.middlewares.grabcevo-wwwredirect.redirectregex.replacement=https://$${1}
      - traefik.http.middlewares.grabcevo-wwwredirect.redirectregex.permanent=true
      - traefik.http.services.grabcevo.loadbalancer.server.port=80
