نام نمایشی و هویت اپلیکیشن در همهٔ سطوح کاربر-پسند از «AGY Web» به «Smart Organization» تغییر کرد: - UI: عنوان تب مرورگر، صفحهٔ ورود، برند و لوگوی پوستهٔ اپ (AG → SO) - بکاند: مقدار پیشفرض app_name، نام و توضیح توزیع پایتون - مانیفستها: package.json ریشه و چهار پکیج، pyproject.toml - مستندات: README، PLAN، TODO و دو سند docs شناسههای داخلی عمداً دستنخورده ماندهاند: scope پکیجهای @agy/*، کلیدهای localStorage، نامهای logger، نام فایل پایگاهٔ داده، و اطلاعات اتصال پایگاه داده در .env.example و infra/docker/docker-compose.yml.
333 lines
15 KiB
Markdown
333 lines
15 KiB
Markdown
# Smart Organization — پلتفرم نیروی کار هوش مصنوعی
|
||
|
||
نسخهٔ وب مستقل و چندسازمانی محصول. این پروژه **کاملاً جدا از ربات تلگرام** است و
|
||
هیچ فایلی از ریشهٔ مخزن را در 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) — زیرساخت فعلی (مرجع)
|