مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت. تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
7.8 KiB
زیرساخت فعلی — یادداشت مرجع
این سند خلاصهٔ بررسی کد فعلی ربات تلگرام است، فقط برای درک زیرساخت موجود. نسخهٔ وب جدید هیچکدام از این فایلها را 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 flagDOMAIN_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 تزریقپذیر است (
requestcallable) تا تست بدون شبکه اجرا شود. - 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 نمیشوند. فقط قراردادها، الگوهای اعتبارسنجی و قواعد امنیتی بالا به عنوان مرجع استفاده شدهاند.