# AGY Web — پلتفرم نیروی کار هوش مصنوعی نسخهٔ وب مستقل و چندسازمانی محصول. این پروژه **کاملاً جدا از ربات تلگرام** است و هیچ فایلی از ریشهٔ مخزن را در runtime خود import نمی‌کند. - پلن محصول و معماری: [`PLAN.md`](./PLAN.md) - چک‌لیست اجرایی کامل: [`TODO.md`](./TODO.md) - یادداشت زیرساخت فعلی (فقط مرجع): [`docs/current-infra-notes.md`](./docs/current-infra-notes.md) --- ## ۱. محصول چیست یک وب اپلیکیشن چندسازمانی برای «نیروی کار هوش مصنوعی سازمانی». **کاربر نهایی این‌ها را می‌بیند:** سازمان، مدیر هوش مصنوعی، کارمندهای هوش مصنوعی، پروژه، وظیفه، فایل/خروجی، مصرف و پرداخت. **کاربر نهایی این‌ها را نمی‌بیند:** نام مدل، provider، `base_url`، API key، قیمت خام provider. این‌ها فقط در **Admin Console** و فقط برای مالک سامانه قابل مدیریت‌اند. پلتفرم سه لایهٔ نقش دارد: 1. **مالک سامانه (platform owner)** — providerها، مدل‌ها، routing policy، تعرفه‌ها، sandbox templateها، مشاهدهٔ کل مصرف. 2. **مدیر سازمان مشتری** — ساخت سازمان، دعوت عضو، پروژه، فعال‌سازی کارمند، مصرف. 3. **عضو سازمان** — گفتگو با مدیر/کارمند، ساخت task، آپلود فایل، دیدن خروجی. --- ## ۲. پیش‌نیازها | ابزار | نسخه | |---|---| | Node.js | >= 20 | | pnpm | 9.x | | Python | 3.13 | | Docker + Compose | برای postgres/redis | فعال‌سازی pnpm در صورت نبود: ```bash corepack enable pnpm corepack prepare pnpm@9.12.0 --activate ``` --- ## ۳. راه‌اندازی سریع ```bash # ۱) نصب وابستگی‌های JS pnpm install # ۲) ساخت venv پایتون برای backend pnpm api:venv # ۳) کپی فایل env و پر کردن مقادیر (پیش‌فرض‌ها همه mock هستند) cp .env.example .env # ۴) بالا آوردن postgres و redis docker compose -f infra/docker/docker-compose.yml up -d postgres redis # ۵) اجرای migration و seed pnpm db:migrate pnpm db:seed # ۶) اجرای هم‌زمان frontend و backend pnpm dev ``` آدرس‌ها پس از اجرا: - Frontend: - API: - API docs (Swagger): - Health: ### حساب‌های نمونه (فقط محیط توسعه) `pnpm db:seed` دو حساب می‌سازد و **هر دو عضو سازمان نمونه هستند**، پس بعد از ورود بلافاصله یک داشبورد پر می‌بینید: | نقش | ایمیل | رمز | |---|---|---| | کاربر نمونه | `demo@example.com` | `demo12345` | | مالک سامانه (admin console) | `owner@example.com` | `owner12345` | > این‌ها رمزهای **توسعه** هستند و در `app/seed.py` سخت‌کد شده‌اند. هیچ‌وقت در > production از این‌ها استفاده نکنید و `JWT_SECRET` / `ENCRYPTION_KEY` واقعی > بگذارید (نگهبان config در حالت production مقادیر placeholder را رد می‌کند). > > نکته: دامنهٔ ایمیل عمداً `example.com` است. اگر `.local` یا `.test` بگذارید، > `email-validator` آن را رد می‌کند و حساب ساخته می‌شود ولی **ورود به آن ناممکن > است**. `tests/test_seed.py` همین را تضمین می‌کند. ### اجرای کل استک با Docker ```bash docker compose -f infra/docker/docker-compose.yml up --build ``` --- ## ۴. اسکریپت‌ها | دستور | کار | |---|---| | `pnpm dev` | اجرای هم‌زمان Next.js و FastAPI | | `pnpm build` | build فرانت‌اند | | `pnpm lint` | ESLint (web) + ruff (api) | | `pnpm typecheck` | tsc (web) + mypy (api) | | `pnpm test` | vitest (web) + pytest (api) | | `pnpm format` | Prettier (web) + ruff format (api) | | `pnpm db:migrate` | `alembic upgrade head` | | `pnpm db:seed` | داده‌های نمونه (platform owner، کاربر نمونه، سازمان، templateها) | | `pnpm api:venv` | ساخت venv پایتون | | `pnpm worker:billing` | یک پاس worker صورتحساب | | `pnpm worker:ai-runtime` | یک پاس worker اجرای taskها | | `pnpm worker:sandbox-gateway` | یک پاس پاک‌سازی sessionهای بی‌استفاده | --- ## ۵. معماری ### ۵.۱ سبک معماری - **Clean Architecture** + **Hexagonal / Ports & Adapters** - **DDD** برای مرزبندی bounded contextها - **Event-Driven** برای billing، audit و observability (الگوی Outbox) - **Modular Monolith** در فاز اول — microservice زودهنگام ممنوع قاعدهٔ سخت: لایهٔ `domain` هیچ dependency به FastAPI، SQLAlchemy، Redis، HTTP یا provider خارجی ندارد. فقط `application` و `infrastructure` به آن وابسته‌اند، نه برعکس. ### ۵.۲ Bounded Contextها `identity` · `billing` · `ai_control` · `workforce` · `sandbox` · `knowledge` · `audit` · `projects` · `platform` ### ۵.۳ Ports الزامی `ModelProviderPort` · `PaymentProviderPort` · `SandboxProviderPort` · `GitProviderPort` · `DomainRouterPort` · `StoragePort` · `VectorStorePort` · `NotificationPort` · `AuditPort` · `UsageLedgerPort` هر port حداقل یک adapter **mock** دارد تا کل استک بدون هیچ credential واقعی اجرا شود. ### ۵.۴ جریان درخواست ```text Browser → Web (Next.js/BFF) → API (FastAPI) ├── Domain Modules ├── Event Bus / Outbox → Workers ├── AI Runtime → Model Providers ├── Sandbox Gateway → E2B / Daytona └── Billing Processor → Stripe ``` --- ## ۶. ساختار پوشه ```text web/ PLAN.md پلن محصول و معماری TODO.md چک‌لیست اجرایی README.md همین فایل package.json اسکریپت‌های ریشه pnpm-workspace.yaml تعریف workspace .env.example نمونهٔ env (هیچ secret واقعی ندارد) apps/ web/ Next.js + TypeScript (UI) api/ FastAPI + SQLAlchemy (backend) services/ ai-runtime/ worker اجرای Agent sandbox-gateway/ wrapper ایزوله‌سازی اجرا billing-worker/ پردازش usage و invoice packages/ contracts/ تایپ‌ها و قراردادهای مشترک ui/ componentهای پایه (design system) infra/ docker/ docker-compose migrations/ محل migrationها docs/ architecture/ مستندات معماری product/ مستندات محصول current-infra-notes.md ``` --- ## ۷. کجا کد بنویسیم | می‌خواهم... | محل | |---|---| | صفحه یا component UI بسازم | `apps/web/src/app` یا `apps/web/src/features` | | component پایه/design system | `packages/ui` | | endpoint جدید بسازم | `apps/api/app/interfaces/http` | | use case بنویسم | `apps/api/app/application` | | منطق دامنه بنویسم | `apps/api/app/domain/` | | adapter یا اتصال به سرویس بیرونی | `apps/api/app/infrastructure` | | worker پس‌زمینه بنویسم | `apps/api/app/workers` (پوستهٔ اجرا در `services/`) | | خطا ثبت/پاک‌سازی کنم | `apps/api/app/core/redact.py` + `apps/api/app/infrastructure/error_log.py` | | قرارداد مشترک front/back | `packages/contracts` | **ممنوع:** نوشتن هر کد جدید در فایل‌های ریشهٔ مخزن (`bot.py`, `web_panel.py`, `bot_menus.py`, `bot_messages.py`) و هر وابستگی به `telegram`, `chat_id`, `callback_data`, `InlineKeyboardMarkup`. --- ## ۸. سرویس‌های خارجی و حالت mock همهٔ سرویس‌های بیرونی پیش‌فرض روی `mock` هستند. برای فعال‌سازی واقعی، این envها را پر کنید: | قابلیت | env | مقادیر | |---|---|---| | مدل | `AI_DEFAULT_PROVIDER` | `mock` \| `openai_compatible` | | Git | `GIT_PROVIDER` | `mock` \| `gitea` | | دامنهٔ تست | `DOMAIN_ROUTER` | `mock` \| `caddy` | | Sandbox | `SANDBOX_PROVIDER` | `mock` \| `daytona` \| `e2b` | | پرداخت | `PAYMENT_PROVIDER` | `mock` \| `stripe` | اگر credential واقعی نداریم، **قابلیت حذف نمی‌شود**: مدل دیتابیس، interface، UI و adapter mock کامل ساخته می‌شود و adapter واقعی پشت همین flag آماده می‌ماند. --- ## ۹. Workerهای پس‌زمینه سه worker وجود دارد. همه در `apps/api/app/workers/` پیاده شده‌اند و `services/` پوستهٔ اجرای آن‌هاست: | worker | کار | دستور | |---|---|---| | `billing-worker` | جمع‌بندی usage ledger به فاکتور draft (هر دوره یک‌بار، idempotent) | `pnpm worker:billing` | | `ai-runtime` | برداشتن taskهای در صف و اجرای آن‌ها از مسیر run engine | `pnpm worker:ai-runtime` | | `sandbox-gateway` | متوقف‌کردن sessionهای بی‌استفاده و ثبت audit | `pnpm worker:sandbox-gateway` | هر دستور **یک پاس** اجرا می‌کند و خارج می‌شود (شکل مناسب cron). برای اجرای مستمر `--loop` بدهید: ```bash cd apps/api && .venv/bin/python -m app.workers billing --loop --interval 3600 ``` **اصل مهم:** workerها هیچ منطق تجاری ندارند. هر سه از `app/application/container.py` همان سرویس‌هایی را می‌سازند که HTTP API می‌سازد، پس worker و API هرگز نمی‌توانند دربارهٔ اینکه کدام adapter پشت یک port است اختلاف داشته باشند. جزئیات هر کدام در `services/*/README.md`. --- ## ۱۰. اصول تغییرناپذیر 1. اجرای کد کاربر یا agent روی host اصلی **ممنوع** — فقط از مسیر Sandbox Gateway. 2. هر endpoint چندسازمانی باید `organization_id` را enforce کند. 3. هیچ secret، token یا API key در frontend یا لاگ چاپ نمی‌شود. 4. هر عملیات destructive باید confirmation و audit log داشته باشد. 5. مدل و provider از دید کاربر عادی پنهان است. 6. هر مصرفی در usage ledger داخلی ثبت می‌شود (Stripe فقط adapter است). --- ## ۱۱. کنسول خطا هر خطایی که سامانه بگیرد — در درخواست HTTP، در worker پس‌زمینه، یا در زمان راه‌اندازی — با جزئیات کامل در جدول `error_events` ثبت می‌شود و در `/admin/errors` (فقط مالک سامانه) قابل مشاهده است. **چه چیزی ثبت می‌شود** | منبع | چه زمانی | شدت | | --- | --- | --- | | استثنای مدیریت‌نشده و خطاهای ۵xx | همیشه | `critical` / `error` | | `ValueError` بدون نوع مشخص | همیشه | `warning` | | سایر پاسخ‌های ۴xx | فقط با `ERROR_LOG_CLIENT_ERRORS=1` | `warning` | ۴xx به‌صورت پیش‌فرض ثبت نمی‌شود چون یک API عمومی دائماً ۴۰۴ از اسکنرها می‌گیرد و ثبت همهٔ آن‌ها خطاهای واقعی را زیر خود دفن می‌کند. خطاهای ۵xx همیشه ثبت می‌شوند، مستقل از این تنظیم. **هر رکورد چه چیزی دارد** - `kind`، `error_type`، `message` و `stack` (ردِّ کامل خطا) - `method`، `path`، `status_code` و `request_id` - `organization_id` و `user_id` (پس از احراز هویت) - `fingerprint` — هش پایدار از هویت خطا، برای گروه‌بندی - `metadata_json` — جزئیات اضافی (مثلاً `task_id`) **گروه‌بندی و رسیدگی** خطاهای یکسان بر اساس `fingerprint` در یک ردیف جمع می‌شوند، پس یک باگ که ۴۰۰۰ بار رخ داده یک خط با شمارنده است، نه ۴۰۰۰ خط. `fingerprint` عمداً سازمان و کاربر را در نظر نمی‌گیرد: یک باگ که دو مستأجر را می‌زند هنوز یک باگ است. بستن یک گروه همهٔ رخدادهای باز آن را می‌بندد و در audit log ثبت می‌شود. **پاک‌سازی خودکار secretها** هیچ خطایی بدون عبور از `app/core/redact.py` ذخیره نمی‌شود. سه لایه: کلیدهایی که معنایشان secret است (`password`، `api_key`، …)، الگوهای شناختنی (JWT، `Bearer`، `scheme://user:pass@host`، `sk-…`، `ghp_…`)، و خودِ مقادیر secret تنظیم‌شده در `settings`. یک لاگ خطا که رمز عبور ذخیره کند از نداشتن لاگ خطا بدتر است. **استفاده از `request_id`** هر پاسخ `X-Request-ID` دارد و هر بدنهٔ خطا هم همان شناسه را در `error.request_id` برمی‌گرداند. کاربر می‌تواند فقط همین یک رشته را گزارش کند تا دقیقاً یک رکورد پیدا شود. ```bash # ۵۰۰ بگیر، سپس با همان شناسه رکورد را پیدا کن curl -i http://localhost:8000/api/whatever -H "X-Request-ID: my-trace" curl "http://localhost:8000/api/admin/errors?search=my-trace" -H "Authorization: Bearer $OWNER_TOKEN" ``` **اجرای دستی workerها و ثبت خطا** خطای یک pass کامل با `source=worker` ثبت می‌شود و exit code غیرصفر می‌دهد تا cron متوجه شود. خطای یک task مشخص نیز با `task_id` و سازمان مربوطه ثبت می‌شود. --- ## ۱۲. مستندات - [`docs/architecture/overview.md`](./docs/architecture/overview.md) — نمای کلی معماری - [`docs/security-checklist.md`](./docs/security-checklist.md) — چک‌لیست امنیتی - [`docs/current-infra-notes.md`](./docs/current-infra-notes.md) — زیرساخت فعلی (مرجع)