# لایه تولید — TLS با Let's Encrypt و انتشار روی پورت‌های استاندارد ۸۰/۴۳. # پیش‌نیاز: متغیرهای DOMAIN و ACME_EMAIL در .env؛ به‌علاوه رکورد DNS دامنه به این سرور. # # bash scripts/stack.sh deploy-prod # (معادل: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build) # # همه درخواست‌های HTTP به HTTPS هدایت می‌شوند و مسیریابی‌ها فقط روی دامنه DOMAIN پاسخ می‌دهند. # برای داشبوردها: analytics.DOMAIN (آنالیتیکس) و grafana.DOMAIN (مانیتورینگ، با فایل monitoring). services: traefik: command: !override - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.constraints=Label(`com.docker.compose.project`, `aramesh-backend`) - --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:?ACME_EMAIL را در .env تنظیم کنید} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web - --ping=true - --accesslog=true - --metrics.prometheus=true ports: !override - "80:80" - "443:443" volumes: !override - /var/run/docker.sock:/var/run/docker.sock:ro - letsencrypt_data:/letsencrypt # MinIO کنسول (رمز root) و API مستقیمش فقط از localhost سرور در دسترس باشند — # از راه دور با تونل SSH: ssh -L 9001:127.0.0.1:9001 server minio: ports: !override - "127.0.0.1:9001:9001" - "127.0.0.1:19000:9000" labels: - traefik.enable=true - traefik.http.routers.media.rule=Host(`${DOMAIN}`) && PathPrefix(`/media`) - traefik.http.routers.media.entrypoints=websecure - traefik.http.routers.media.tls.certresolver=le - traefik.http.routers.media.priority=100 - traefik.http.routers.media.middlewares=media-rewrite,sec-headers - traefik.http.middlewares.media-rewrite.replacepathregex.regex=^/media/(.*) - traefik.http.middlewares.media-rewrite.replacepathregex.replacement=/aramesh-media/$$1 - traefik.http.services.media.loadbalancer.server.port=9000 backend: environment: # سایت هم‌مبدأ است — CORS لازم نیست CORS_ORIGINS: '[]' SITE_URL: https://${DOMAIN:?DOMAIN را در .env تنظیم کنید} labels: - traefik.enable=true - traefik.http.routers.api.rule=Host(`${DOMAIN}`) && PathPrefix(`/api`) - traefik.http.routers.api.entrypoints=websecure - traefik.http.routers.api.tls.certresolver=le - traefik.http.routers.api.middlewares=api-flood,sec-headers - traefik.http.routers.seo.rule=Host(`${DOMAIN}`) && (PathPrefix(`/blog/`) || Path(`/blog`) || PathPrefix(`/projects/`) || Path(`/projects`) || Path(`/en`) || PathPrefix(`/en/`) || PathPrefix(`/robots.txt`) || PathPrefix(`/sitemap.xml`)) - traefik.http.routers.seo.entrypoints=websecure - traefik.http.routers.seo.tls.certresolver=le - traefik.http.routers.seo.middlewares=sec-headers frontend: labels: - traefik.enable=true - traefik.http.routers.web.rule=Host(`${DOMAIN}`) && PathPrefix(`/`) - traefik.http.routers.web.entrypoints=websecure - traefik.http.routers.web.tls.certresolver=le - traefik.http.routers.web.middlewares=sec-headers # تعریف یک‌بارهٔ میان‌افزار هدرهای امنیتی — روی همهٔ روترهای websecure استفاده می‌شود - traefik.http.middlewares.sec-headers.headers.stsSeconds=31536000 - traefik.http.middlewares.sec-headers.headers.stsIncludeSubdomains=true - traefik.http.middlewares.sec-headers.headers.stsPreload=true - traefik.http.middlewares.sec-headers.headers.contentTypeNosniff=true chatbot: labels: - traefik.enable=true - traefik.http.routers.chat.rule=Host(`${DOMAIN}`) && PathPrefix(`/chat`) - traefik.http.routers.chat.entrypoints=websecure - traefik.http.routers.chat.tls.certresolver=le - traefik.http.routers.chat.priority=100 - traefik.http.routers.chat.middlewares=api-flood,sec-headers umami: labels: - traefik.enable=true - traefik.http.routers.analytics.rule=Host(`analytics.${DOMAIN}`) - traefik.http.routers.analytics.entrypoints=websecure - traefik.http.routers.analytics.tls.certresolver=le - traefik.http.routers.analytics.middlewares=sec-headers - traefik.http.services.analytics.loadbalancer.server.port=3000 # داشبورد Grafana (docker-compose.monitoring.yml) در تولید با TLS نیاز به روتر # Host(`grafana.DOMAIN`) دارد — چون آن سرویس فقط با فایل monitoring بالا می‌آید، # برچسب‌هایش را در override سرور بگذارید (README، بخش مانیتورینگ): # docker-compose.override.yml روی سرور: # services: # grafana: # labels: # - traefik.http.routers.grafana.rule=Host(`grafana.YOURDOMAIN`) # - traefik.http.routers.grafana.entrypoints=websecure # - traefik.http.routers.grafana.tls.certresolver=le # - traefik.http.services.grafana.loadbalancer.server.port=3000 volumes: letsencrypt_data: