ecom-store/scripts/docker-compose.m5.yml
Iris Florencio ee8c3d1fb1 feat(saleor): app token persistente + helper GraphQL
Item 1 da sugestão — vincular API token Saleor para uso programático.

Mudancas:
- docker-compose.m5.yml: env RSA_PRIVATE_KEY agora recebe ${RSA_PRIVATE_KEY_CONTENT}
  (conteudo da chave, nao path) — corrige bug MalformedFraming do jwt_manager
- scripts/.env: adicionado RSA_PRIVATE_KEY_CONTENT (PKCS#1) e SALEOR_APP_TOKEN
- scripts/saleor-media/RSA_PRIVATE_KEY.pem: convertida PKCS#8 → PKCS#1
  (Saleor 3.23 jwt_manager so' aceita PKCS#1)
- scripts/saleor-api.py: helper CLI para queries GraphQL (count/products/me/query)
- App 'M5 API Service' criado via appCreate (id QXBwOjE=)
  - permissoes: MANAGE_ORDERS, PRODUCTS, CHANNELS, SHIPPING, TAXES, CHECKOUTS,
                USERS, STAFF, HANDLE_PAYMENTS, APPS
  - token: NPXnQSAcwTH40BGlqV010baewTSUzR (persistente, sem expiracao)

Bug colateral corrigido: pem MalformedFraming impedia Django setup e login.

Validacao:
- python3 scripts/saleor-api.py count: 4 products, 0 orders, 2 channels
- Token persistente funciona via Authorization: Bearer
2026-07-06 23:12:48 -03:00

307 lines
11 KiB
YAML

# docker-compose.m5.yml — Motor Ecommerce M5 Brasil
# Versão atualizada conforme manual Saleor 3.23
services:
# ── Traefik (Proxy Reverso) ─────────────────────────
traefik:
image: traefik:v3.0
container_name: m5_traefik
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.myresolver.acme.httpchallenge=true
- --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.myresolver.acme.email=irisflorencio@yahoo.com.br
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
ports:
- "80:80"
- "443:443"
- "8090:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-acme:/letsencrypt
- ./traefik-conf/traefik.yml:/etc/traefik/traefik.yml:ro
- ./traefik-conf/routes.yml:/etc/traefik/routes.yml:ro
- /etc/letsencrypt:/certs:ro
- ./traefik-conf/traefik.yml:/etc/traefik/traefik.yml:ro
- ./traefik-conf/routes.yml:/etc/traefik/routes.yml:ro
- /etc/letsencrypt:/certs:ro
networks:
- m5_network
labels:
- "traefik.enable=false"
# ── Banco de dados principal ──────────────────────────
postgres:
image: postgres:16-alpine
container_name: m5_postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-m5ecommerce}
POSTGRES_USER: ${POSTGRES_USER:-m5user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- m5_network
labels:
- "traefik.enable=false"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-m5user}"]
interval: 10s
timeout: 5s
retries: 5
# ── Cache / Session ──────────────────────────────────
redis:
image: redis:7-alpine
container_name: m5_redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
networks:
- m5_network
labels:
- "traefik.enable=false"
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 3s
retries: 5
# ── Saleor API ───────────────────────────────────────
saleor:
image: ghcr.io/saleor/saleor:3.23
container_name: m5_saleor
restart: unless-stopped
entrypoint: ["/app/entrypoint.sh"]
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.saleor-api.rule=Host(`loja.m5digital.net.br`)"
- "traefik.http.routers.saleor-api.entrypoints=websecure"
- "traefik.http.routers.saleor-api.tls=true"
- "traefik.http.services.saleor-api.loadbalancer.server.port=8000"
environment:
- DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-m5ecommerce}
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379
- CELERY_BROKER_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
- SECRET_KEY=${SALEOR_SECRET_KEY}
- ALLOWED_HOSTS=loja.m5digital.net.br,localhost,127.0.0.1,dashboard.m5digital.net.br
- ALLOWED_GRAPHQL_ORIGINS=https://dashboard.m5digital.net.br,https://loja.m5digital.net.br,http://localhost:3000
- DEFAULT_MAIL_FROM=${DEFAULT_EMAIL_FROM}
- EMAIL_URL=${EMAIL_URL}
# Saleor 3.23 espera o CONTEUDO da chave PEM, nao o path
# O conteudo e' carregado via ${RSA_PRIVATE_KEY_CONTENT} definido em .env
- RSA_PRIVATE_KEY=${RSA_PRIVATE_KEY_CONTENT}
- STRIPE_API_KEY=${STRIPE_API_KEY}
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
- MERCADOPAGO_ACCESS_TOKEN=${MERCADOPAGO_ACCESS_TOKEN}
- MERCADOPAGO_WEBHOOK_SECRET=${MERCADOPAGO_WEBHOOK_SECRET}
volumes:
- ./saleor-media:/app/media
- ./entrypoint.sh:/app/entrypoint.sh:ro
networks:
- m5_network
# ── Saleor Worker (Celery) ─────────────────────────
worker:
image: ghcr.io/saleor/saleor:3.23
container_name: m5_saleor_worker
restart: unless-stopped
command: celery -A saleor worker --loglevel=info
depends_on:
- postgres
- redis
environment:
- DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-m5ecommerce}
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379
- CELERY_BROKER_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
- SECRET_KEY=${SALEOR_SECRET_KEY}
volumes:
- saleor-media:/app/media
networks:
- m5_network
labels:
- "traefik.enable=false"
# ── Saleor Dashboard ─────────────────────────────────
saleor-dashboard:
image: ghcr.io/saleor/saleor-dashboard:latest
container_name: m5_saleor_dashboard
restart: unless-stopped
environment:
- API_URL=https://loja.m5digital.net.br/graphql/
- APP_MOUNT_URI=/
- NODE_ENV=production
networks:
- m5_network
labels:
- "traefik.enable=true"
- "traefik.http.routers.saleor-dash.rule=Host(`dashboard.m5digital.net.br`)"
- "traefik.http.routers.saleor-dash.entrypoints=websecure"
- "traefik.http.routers.saleor-dash.tls=true"
- "traefik.http.services.saleor-dash.loadbalancer.server.port=80"
# ── n8n (Automação) ──────────────────────────────────
n8n:
image: n8nio/n8n:${N8N_VERSION:-1.70}
container_name: m5_n8n
restart: unless-stopped
depends_on:
- postgres
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${N8N_DB:-n8n}
- DB_POSTGRESDB_USER=${POSTGRES_USER:-m5user}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
- N8N_HOST=https://n8n.m5digital.net.br
- WEBHOOK_URL=https://n8n.m5digital.net.br/
- N8N_PROTOCOL=https
- N8N_TRUST_PROXY=true
- GENERIC_TIMEZONE=America/Sao_Paulo
- WA_AUTOCARRIER_URL=http://m5_openwa:8080
- SALEOR_API_URL=https://loja.m5digital.net.br/graphql/
volumes:
- n8n_data:/home/node/.n8n
- ./n8n-workflows:/workflows
networks:
- m5_network
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`n8n.m5digital.net.br`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls=true"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
# ── OpenWA (WhatsApp) ────────────────────────────────
openwa:
image: ghcr.io/rmyndharis/openwa:latest
container_name: m5_openwa
restart: unless-stopped
environment:
- PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
- PUPPETEER_EXECUTABLE_PATH=/opt/google/chrome/chrome
- CHROME_PATH=/opt/google/chrome/chrome
- WA_CHROMIUM_FLAGS=--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage --disable-gpu --disable-software-rasterizer --no-zygote --single-process
- WA_USE_CHROME=true
- MULTI_DEVICE=true
- READ_MESSAGES=true
- GROUP_PARTICIPANTS=true
- WEBHOOK_URL=${WA_WEBHOOK_URL:-https://n8n.m5digital.net.br/webhook/wa}
env_file:
- ./openwa.env
ports:
- "2785:2785"
- "8080:8080"
- "8081:8081"
- "8002:8002"
volumes:
- openwa_data:/var/lib/wa-automate
- ./openwa-sessions:/sessions
networks:
- m5_network
security_opt:
- seccomp=unconfined
labels:
- "traefik.enable=true"
- "traefik.http.routers.openwa.rule=Host(`wa.m5digital.net.br`)"
- "traefik.http.routers.openwa.entrypoints=websecure"
- "traefik.http.routers.openwa.tls=true"
- "traefik.http.services.openwa.loadbalancer.server.port=2785"
# ── Gitea (Git) ─────────────────────────────────────
gitea:
image: gitea/gitea:${GITEA_VERSION:-1.22}
container_name: m5_gitea
restart: unless-stopped
ports:
- "2222:22"
depends_on:
- postgres
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=postgres:5432
- GITEA__database__NAME=${GITEA_DB:-gitea}
- GITEA__database__USER=${POSTGRES_USER:-m5user}
- GITEA__database__PASSWD=${POSTGRES_PASSWORD}
- GITEA__server__PROTOCOL=http
- GITEA__server__ROOT_URL=https://git.m5digital.net.br/
- GITEA__server__DOMAIN=git.m5digital.net.br
- GITEA__security__INSTALL_LOCK=true
- GITEA__service__DISABLE_REGISTRATION=false
volumes:
- gitea_data:/data
- ./gitea-repos:/git/repositories
networks:
- m5_network
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`git.m5digital.net.br`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls=true"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
# ── yt-pub-livesx ──────────────────────────────────
yt-pub-livesx-dashboard:
image: yt-pub-livesx-dashboard
container_name: yt-pub-livesx-dashboard-1
restart: unless-stopped
environment:
- TZ=America/Sao_Paulo
ports:
- "8091:8091"
volumes:
- ./yt-pub-livesx/data:/app/data
- ./yt-pub-livesx/credentials.enc:/app/credentials.enc:ro
networks:
- m5_network
labels:
- "traefik.enable=true"
- "traefik.http.routers.video.rule=Host(`video.m5digital.net.br`)"
- "traefik.http.routers.video.entrypoints=websecure"
- "traefik.http.routers.video.tls=true"
- "traefik.http.services.video.loadbalancer.server.port=8091"
yt-pub-livesx-scheduler:
image: yt-pub-livesx-scheduler
container_name: yt-pub-livesx-scheduler-1
restart: unless-stopped
environment:
- TZ=America/Sao_Paulo
volumes:
- ./yt-pub-livesx/data:/app/data
- ./yt-pub-livesx/credentials.enc:/app/credentials.enc:ro
networks:
- m5_network
networks:
m5_network:
name: scripts_m5_network
external: true
volumes:
postgres_data:
redis_data:
saleor-media:
gitea_data:
n8n_data:
openwa_data:
traefik-acme: