Files
smart-org/docs/current-infra-notes.md
mjalmoudy94 717e53a7ce نسخهٔ وب مستقل — پلتفرم نیروی کار هوش مصنوعی
مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت.
تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
2026-10-13 00:39:28 +03:30

7.8 KiB
Raw Permalink Blame History

زیرساخت فعلی — یادداشت مرجع

این سند خلاصهٔ بررسی کد فعلی ربات تلگرام است، فقط برای درک زیرساخت موجود. نسخهٔ وب جدید هیچ‌کدام از این فایل‌ها را import نمی‌کند. هر قابلیت با یک adapter مستقل داخل web/ بازنویسی می‌شود. این سند صرفاً قراردادها و محدودیت‌هایی را ثبت می‌کند که باید در adapterهای جدید حفظ شوند.


۱. Domain Routing و Preview Domain

منابع بررسی‌شده: caddy_manager.py (۲۵۹ خط)، port_registry.py (۵۳۱ خط).

۱.۱ Caddy

  • مسیر فایل: CADDYFILE_PATH = /etc/caddy/Caddyfile (یک فایل مشترک سیستمی).
  • دامنهٔ wildcard: WILDCARD_DOMAIN = msa.artacloud.ir.
  • allowlist سخت‌گیرانهٔ host: فقط hostهای تک‌label زیر دامنهٔ wildcard مجازند. الگوی فعلی: [a-z0-9\-]{1,63}\.msa\.artacloud\.ir. هر کاراکتری که بتواند از بلوک سایت خارج شود (آکولاد، کوتیشن، فاصله، newline) رد می‌شود.
  • target فقط local: الگوی فعلی (https?://)?(localhost|127\.0\.0\.1):\d{1,5}. این برای جلوگیری از SSRF است — نباید هیچ‌وقت به host دیگر proxy شود.
  • چرخهٔ امن نوشتن: خواندن فایل → ساخت/جایگزینی بلوک → نوشتن → caddy validate → در صورت خطا بازگردانی به نسخهٔ قبلی → systemctl reload caddy و در صورت شکست caddy reload --config ....
  • پیدا کردن بلوک سایت با شمارش عمق آکولاد انجام می‌شود (نه regex)، چون بلوک‌ها تودرتو هستند (handle, route, tls, header).

۱.۲ Port Registry

دو جدول در SQLite (data/memory.db):

جدول نقش
port_registry port (PK)، user_id، project_name، service_name، subdomain، description، created_at — یکتا روی (user_id, project_name, service_name)
subdomain_registry host (PK)، user_id، project_name، port، protected، created_at — ledger مالکیت دامنه

قواعد کلیدی که باید در DomainRouterPort جدید حفظ شوند:

  • محدودهٔ تخصیص port: 8080-8999.
  • SYSTEM_RESERVED_PORTS = {22, 53, 80, 443, 2019, 3000, 5432, 6379, 8008}.
  • SYSTEM_PROTECTED_PORTS = {3000, 35555, 35556, 35557} ∪ reserved — یک پروژهٔ کاربر هرگز نباید به این‌ها proxy کند، وگرنه سرویس tenant دیگر عمومی می‌شود.
  • MAX_SUBDOMAINS_PER_PROJECT = 5.
  • SYSTEM_OWNER_PROJECT = "__system__" برای hostهای بدون مالک شناخته‌شده.
  • snapshot پورت‌های listening با ss -tlnH گرفته می‌شود. اگر snapshot ممکن نبود (None)، تخصیص رد می‌شود — تفاوت «چیزی گوش نمی‌دهد» با «نمی‌دانیم» حفظ می‌شود.
  • در تخصیص هم‌زمان، IntegrityError گرفته و تا ۵ بار rescan/retry می‌شود.
  • هر host دقیقاً یک مالک دارد؛ بدون این ledger هر پروژه می‌توانست دامنهٔ پروژهٔ دیگر را حذف یا hijack کند.
  • hostهایی که قابل انتساب به پروژه نیستند protected = 1 می‌شوند (فقط admin).

۱.۳ تصمیم برای نسخهٔ وب

  • DomainRouterPort با متدهای allocate_port / claim_preview_domain / configure_reverse_proxy / remove_preview_domain / list_project_domains.
  • دو adapter: ManagedDomainRouterAdapter (mock، بدون لمس Caddy) و CaddyDomainRouterAdapter (واقعی، پشت env flag DOMAIN_ROUTER=caddy).
  • مالکیت دامنه به organization_id + project_id در PostgreSQL منتقل می‌شود، نه user_id تلگرام.

۲. Git / Gitea

منابع بررسی‌شده: git_manager.py (۱۰۶۴ خط)، gitea_api.py (۲۱۵ خط).

۲.۱ تنظیمات فعلی (از config.py)

متغیر مقدار پیش‌فرض
GITEA_URL https://git.msa.artacloud.ir (عمومی، برای نمایش)
GITEA_INTERNAL_URL http://127.0.0.1:3000 (برای تماس API)
GITEA_ADMIN_USER root
GITEA_ADMIN_TOKEN خالی — بدون آن همهٔ قابلیت‌های Gitea غیرفعال است

۲.۲ رفتار GitManager

  • ensure_gitea_repo(repo_name, owner, description, private=True): اگر repo نبود می‌سازد، اگر بود و private نبود آن را PATCH می‌کند به private. auto_init=False، default_branch=main.
  • generate_project_credentials(project_name, owner_id): نام کاربری به شکل u{owner_id}_{clean_name[:room]}_{sha1(f"{owner_id}:{project_name}")[:8]} و رمز secrets.token_urlsafe(12). هش کوتاه برای جلوگیری از collision دو پروژه با نام سانیتایز یکسان ضروری است (وگرنه PATCH رمز کاربر مشترک، credential پروژهٔ اول را بی‌صدا خراب می‌کند). سقف ۳۶ کاراکتر Gitea رعایت می‌شود.
  • قفل per-repo (_RepoLock) که برای همان asyncio task قابل re-enter است؛ git_publish/git_sync داخل بخش بحرانی خودشان دوباره lock می‌گیرند، پس asyncio.Lock ساده deadlock می‌داد.
  • پاک‌سازی index.lock فقط اگر قدیمی‌تر از STALE_LOCK_AGE_SECONDS = 30 باشد (حذف lock جوان می‌تواند index را خراب کند).

۲.۳ GiteaApiClient

متدها: create_pr، pr_status، merge_pr، list_open_prs، list_labeled_issues(label="agybot")، comment_issue، close_issue. نکات:

  • transport تزریق‌پذیر است (request callable) تا تست بدون شبکه اجرا شود.
  • token هرگز در هیچ مقدار برگشتی یا prompt قرار نمی‌گیرد؛ base_url امن برای نمایش است.
  • صفحه‌بندی با cursor page انجام می‌شود تا نتایج در یک صفحه بریده نشوند.
  • خواندن پیام خطا از message / error / errors (نه فقط error).
  • برچسب agybot روی issue آن را به task سازمانی تبدیل می‌کند.

۲.۴ تصمیم برای نسخهٔ وب

  • GitProviderPort با متدهای ensure_project_repo / create_branch / commit_files / open_pull_request / merge_pull_request / get_repo_status.
  • GiteaGitAdapter (واقعی) و MockGitAdapter (پیش‌فرض dev).
  • نام repo در نسخهٔ وب: orgslug-projectslug-shortid (collision-free).
  • credential هر پروژه encrypted at rest در project_git_credentials ذخیره می‌شود؛ token/password هرگز خام به frontend برنمی‌گردد.
  • admin token فقط سمت backend می‌ماند.

۳. جمع‌بندی

قابلیت فعلی منبع فعلی جایگزین در نسخهٔ وب
Caddy subdomain routing caddy_manager.py DomainRouterPort + CaddyDomainRouterAdapter
Port/subdomain ledger port_registry.py (SQLite) جداول preview_ports / preview_domains (PostgreSQL)
Gitea repo + credentials git_manager.py GitProviderPort + GiteaGitAdapter
PR / issue workflow gitea_api.py همان port، متدهای PR/issue

هیچ‌کدام از این فایل‌ها در runtime نسخهٔ وب import نمی‌شوند. فقط قراردادها، الگوهای اعتبارسنجی و قواعد امنیتی بالا به عنوان مرجع استفاده شده‌اند.