نسخهٔ وب مستقل — پلتفرم نیروی کار هوش مصنوعی

مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت.
تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
This commit is contained in:
mjalmoudy94
2026-10-13 00:39:28 +03:30
commit 717e53a7ce
185 changed files with 39910 additions and 0 deletions
+332
View File
@@ -0,0 +1,332 @@
# 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: <http://localhost:3000>
- API: <http://localhost:8000>
- API docs (Swagger): <http://localhost:8000/docs>
- Health: <http://localhost:8000/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/<context>` |
| adapter یا اتصال به سرویس بیرونی | `apps/api/app/infrastructure` |
| worker پس‌زمینه بنویسم | `apps/api/app/workers` (پوستهٔ اجرا در `services/<name>`) |
| خطا ثبت/پاک‌سازی کنم | `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/<name>`
پوستهٔ اجرای آن‌هاست:
| 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) — زیرساخت فعلی (مرجع)