services: # --- Base de datos MariaDB --- db: image: mariadb:lts-noble container_name: factuges_acana_db restart: unless-stopped environment: MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASS} MARIADB_USER: ${DB_USER} MARIADB_PASSWORD: ${DB_PASS} MARIADB_DATABASE: ${DB_NAME} volumes: - ./volumes/db_data:/var/lib/mysql networks: - internal healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 20s timeout: 5s retries: 10 # --- phpMyAdmin (solo accesible por Traefik + whitelist IP) --- phpmyadmin: image: phpmyadmin/phpmyadmin container_name: factuges_acana_phpmyadmin restart: unless-stopped environment: PMA_HOST: db PMA_USER: ${DB_USER} PMA_PASSWORD: ${DB_PASS} PMA_VERBOSES: "FactuGES Acana" MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS} UPLOAD_LIMIT: 64M depends_on: - db networks: - internal - edge labels: traefik.enable: "true" # /phpmyadmin → phpMyAdmin traefik.http.routers.factuges_acana_phpmyadmin.rule: Host(`${DOMAIN}`) && PathPrefix(`/phpmyadmin`) traefik.http.routers.factuges_acana_phpmyadmin.entrypoints: websecure traefik.http.routers.factuges_acana_phpmyadmin.tls.certresolver: cloudflare-dns traefik.http.routers.factuges_acana_phpmyadmin.priority: "110" traefik.http.routers.factuges_acana_phpmyadmin.service: factuges_acana_phpmyadmin traefik.http.services.factuges_acana_phpmyadmin.loadbalancer.server.port: "80" # strip /phpmyadmin del path antes de llegar al contenedor traefik.http.middlewares.factuges_acana_phpmyadmin_strip.stripprefix.prefixes: "/phpmyadmin" # whitelist a tu IP traefik.http.middlewares.factuges_acana_phpmyadmin_ipwhitelist.ipwhitelist.sourcerange: "79.116.183.41/32" # encadenar middlewares traefik.http.routers.factuges_acana_phpmyadmin.middlewares: "factuges_acana_phpmyadmin_strip,factuges_acana_phpmyadmin_ipwhitelist" # --- API Node.js --- api: image: factuges-server:acana-latest #${API_IMAGE} container_name: factuges_acana_api restart: unless-stopped depends_on: db: condition: service_healthy environment: NODE_ENV: production COMPANY: acana PORT: ${API_PORT:-3002} DB_DIALECT: "mysql" DB_HOST: "db" DB_PORT: ${DB_PORT} DB_NAME: ${DB_NAME} DB_USER: ${DB_USER} DB_PASS: ${DB_PASS} FRONTEND_URL: ${FRONTEND_URL} networks: - internal - edge labels: traefik.enable: "true" # /api → backend traefik.http.routers.factuges_acana_api.rule: Host(`${DOMAIN}`) && PathPrefix(`/api`) traefik.http.routers.factuges_acana_api.entrypoints: websecure traefik.http.routers.factuges_acana_api.tls.certresolver: cloudflare-dns traefik.http.routers.factuges_acana_api.priority: "100" traefik.http.routers.factuges_acana_api.service: factuges_acana_api # Servicio traefik.http.services.factuges_acana_api.loadbalancer.server.port: "${API_PORT:-3002}" # --- Web estática Vite (nginx) --- web: image: nginx:alpine container_name: factuges_acana_web restart: unless-stopped depends_on: - api environment: VITE_API_SERVER_URL: ${DOMAIN}/api volumes: - ./web/public:/usr/share/nginx/html:ro - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro networks: - edge labels: traefik.enable: "true" # / → frontend traefik.http.routers.factuges_acana_web.rule: Host(`${DOMAIN}`) && PathPrefix(`/`) traefik.http.routers.factuges_acana_web.entrypoints: websecure traefik.http.routers.factuges_acana_web.tls.certresolver: cloudflare-dns # prioridad más baja para que /api y /phpmyadmin ganen traefik.http.routers.factuges_acana_web.priority: "1" traefik.http.routers.factuges_acana_web.service: factuges_acana_web traefik.http.services.factuges_acana_web.loadbalancer.server.port: "80" networks: edge: name: edge external: true internal: name: factuges_acana_internal external: false volumes: # Solo declarativo; usamos ruta ./volumes/db_data arriba db_data: