Files
MathApp/docker-compose.yml
Zareian a973c41db2 Security review fixes: admin permissions, guest sessions, payment edge cases, assistant leaks
- Admin: refund/cancel/mark-resolved/Bazaar-link actions need change permission; question and
  curriculum import need add/delete; content browser needs view_question; guest device ids are
  shown to superusers only.
- Accounts: registering a guest's phone revokes the guest's earlier sessions; self-deletion keeps
  Redemption, UserOffer and the phone's OTP rows; the public demo guest cannot be deleted
  (DEMO_GUEST_DEVICE_IDS); display-name contact filter covers any separator and social handles.
- Payment: callback without Authority is 400; a second Bazaar purchase on a paid order is 409;
  orders snapshot credits/hearts/days at creation (payment.0011).
- Assistant: a broken diagram no longer makes a streamed answer free; a late off-topic marker
  refuses the answer (not stored, not cached); a disconnect saves the partial answer; only
  KNOWN_COLLECTIONS are searched.
- Infra: secret-key file is created atomically (workers no longer split keys); no default
  INTERNAL_SERVICE_KEY; Kavenegar key and Bazaar token kept out of error logs; Swagger/ReDoc
  pinned; schema throttled; docker compose bound to 127.0.0.1; non-ASCII digit ids are 400;
  ticket text capped at 5000.
- docs/security-before-production.md: remaining open items 24-31 and the guard tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 04:52:25 +03:30

110 lines
5.9 KiB
YAML

# ---------------------------------------------------------------------
# Docker Compose برای اجرای محلیِ Django به‌همراه استکِ لاگِ متمرکز
# (Grafana Alloy → Loki → Grafana). فقط برای dev/local — تنظیمِ پروداکشن نیست.
#
# جریانِ لاگ:
# Django app --(همون JSON log خط‌به‌خطِ موجود، هم روی stdout هم در
# فایلِ /var/log/django/app.log — بدونِ تغییرِ پیام‌ها)-->
# Alloy --(این فایل رو از طریقِ ولومِ مشترکِ django_logs می‌خونه)-->
# Loki --(ذخیره‌سازیِ لاگ روی فایل‌سیستمِ محلی)-->
# Grafana --(دیتاسورسِ Loki از قبل provision شده؛ داشبوردِ آماده)
#
# ⚠️ قبلاً این نقش با Promtail انجام می‌شد؛ چون Promtail مارچ ۲۰۲۶ به
# EOL رسید، با Grafana Alloy (جانشینِ رسمیِ خودِ Grafana Labs) جایگزین
# شد. جریانِ لاگ (Django → فایلِ مشترک → Loki → Grafana) و لیبل‌های
# level/logger دقیقاً همون قبلی‌ان.
#
# اجرا: docker compose up --build
# - اپ: http://localhost:8000
# - Grafana: http://localhost:3000 (بدونِ لاگین، anonymous admin فعاله)
# ---------------------------------------------------------------------
services:
django:
build: .
container_name: tadrisino_django
# migrate هر بار قبل از اجرا صدا زده می‌شه؛ روی SQLite بی‌خطر و
# idempotent است (مطابقِ همون workflow ای که در README برای اجرای
# محلی/غیرِ Docker هم توصیه شده).
command: sh -c "python manage.py migrate --noinput && python manage.py runserver 0.0.0.0:8000"
# فقط روی خودِ لپ‌تاپ: این پشته DEBUG=1 دارد (کدِ 0000 برای هر شماره) و Grafana بی‌رمز است؛
# روی 0.0.0.0 هر همسایه‌ی وای‌فای به آن‌ها می‌رسید.
ports:
- "127.0.0.1:8000:8000"
environment:
# این مقادیر فقط برای اجرای محلیِ Docker/دِمو هستن؛ برای پروداکشن
# واقعی جایگزین بشن (به .env.example نگاه کنید).
- DJANGO_DEBUG=${DJANGO_DEBUG:-1}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY:-}
- DJANGO_ALLOWED_HOSTS=${DJANGO_ALLOWED_HOSTS:-localhost,127.0.0.1}
- CACHE_URL=${CACHE_URL:-}
- KAVENEGAR_API_KEY=${KAVENEGAR_API_KEY:-}
# مسیرِ اضافه‌ای که Django (علاوه بر stdout) همون خطوطِ JSON رو توش
# هم می‌نویسه؛ فقط وقتی این env ست باشه فعال می‌شه (config/settings.py)
# — یعنی اجرای عادیِ خارج از Docker دقیقاً مثلِ قبل می‌مونه.
- LOG_DIR=/var/log/django
volumes:
- .:/app # کد برای دیباگ/توسعه‌ی زنده mount می‌شه
- django_logs:/var/log/django # ولومِ مشترک با alloy
# ⚠️ عمداً depends_on نداره: Django برای بالا اومدن یا جواب‌دادن به
# درخواست‌ها هیچ نیازی به Loki نداره (فقط توی فایل می‌نویسه؛ Promtail
# و Loki مستقل از این می‌خونن/push می‌کنن). وابستگیِ استارت‌آپیِ اضافه
# فقط باعثِ کندشدنِ بی‌دلیلِ `docker compose up` می‌شد.
loki:
image: grafana/loki:2.9.6
container_name: tadrisino_loki
ports:
- "127.0.0.1:3100:3100"
command: -config.file=/etc/loki/loki-config.yml
volumes:
- ./docker/loki/loki-config.yml:/etc/loki/loki-config.yml:ro
- loki_data:/loki
alloy:
image: grafana/alloy:v1.18.1
container_name: tadrisino_alloy
# همون کاری که promtail قبلاً می‌کرد: config.alloy رو اجرا می‌کنه که
# فایلِ لاگِ Django رو می‌خونه و به Loki push می‌کنه. پرچمِ
# server.http.listen-addr فقط برای UI دیباگِ خودِ Alloy روی 12345 است.
command:
- run
- --server.http.listen-addr=0.0.0.0:12345
- --storage.path=/var/lib/alloy/data
- /etc/alloy/config.alloy
ports:
- "127.0.0.1:12345:12345" # اختیاری: UI دیباگِ Alloy (وضعیتِ کامپوننت‌ها)
volumes:
- ./docker/alloy/config.alloy:/etc/alloy/config.alloy:ro
- django_logs:/var/log/django:ro # همون لاگ‌های Django رو می‌خونه (read-only)
- alloy_data:/var/lib/alloy/data # ذخیره‌سازیِ داخلیِ Alloy (شاملِ positions)
# ⚠️ عمداً فقط به loki وابسته‌ست، نه django: Alloy صرفاً فایلِ
# لاگِ رویِ ولومِ مشترک رو می‌خونه (چه Django بالا باشه چه نه) و به
# Loki push می‌کنه؛ نیازی نداره منتظرِ استارت‌شدنِ Django بمونه.
depends_on:
- loki
grafana:
image: grafana/grafana:10.4.3
container_name: tadrisino_grafana
ports:
- "127.0.0.1:3000:3000"
environment:
# فقط برای دسترسیِ محلی/دِمو ساده شده (بدونِ لاگین)؛ برای هر چیزِ
# عمومی/پروداکشن حتماً anonymous access رو خاموش و پسورد ست کنید.
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
volumes:
- ./docker/grafana/provisioning:/etc/grafana/provisioning:ro
- ./docker/grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana_data:/var/lib/grafana
depends_on:
- loki
volumes:
django_logs:
loki_data:
alloy_data:
grafana_data: