feat(cdn): integrate CDN Manager into the application
quality / commitlint (push) Skipped
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 57s
quality / api (push) Failing after 30s
CD / quality (push) Failing after 1m36s
CD / publish (push) Skipped

- Updated environment configurations to include CDN Manager in the RETURN_TO_ALLOWLIST.
- Enhanced target app resolution to recognize CDN-related hosts.
- Added CDN Manager to the application switcher and updated relevant documentation.
- Included tests to verify the correct mapping of CDN hosts.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-09-04 14:11:34 +07:00
co-authored by Cursor
parent a591d12c69
commit 0f1f89776b
15 changed files with 191 additions and 9 deletions
+1 -1
View File
@@ -179,7 +179,7 @@ VITE_AUTH_ENABLED=true
VITE_AUTH_PORTAL_URL=https://auth.shnt.top
```
См. [integrate-vps-tracker.md](integrate-vps-tracker.md), [integrate-cfdm.md](integrate-cfdm.md), [integrate-evobgp.md](integrate-evobgp.md), [integrate-evofirewall.md](integrate-evofirewall.md).
См. [integrate-vps-tracker.md](integrate-vps-tracker.md), [integrate-cfdm.md](integrate-cfdm.md), [integrate-evobgp.md](integrate-evobgp.md), [integrate-evofirewall.md](integrate-evofirewall.md), [integrate-cdnmanager.md](integrate-cdnmanager.md).
Logout SSO: `https://auth.shnt.top/logout`.
### Technitium DNS (OIDC)
+2 -2
View File
@@ -36,7 +36,7 @@ Content-Type: application/json
```
- `events`: 150 за запрос
- `source_app`: `vps` | `cfdm` | `bgp` | `fw` (не `portal`)
- `source_app`: `vps` | `cfdm` | `bgp` | `fw` | `dns` | `cdn` (не `portal`)
- `event_id`: идемпотентность (дубликаты → `duplicates++`)
- Ответ: `{ "accepted": N, "duplicates": M }`
@@ -45,7 +45,7 @@ Content-Type: application/json
| Где | Переменная |
|-----|------------|
| auth-portal | `AUDIT_INGEST_SECRET` |
| apps (vps / cfdm / bgp / fw) | `AUTH_PORTAL_URL` + `AUTH_AUDIT_INGEST_SECRET` (тот же секрет) |
| apps (vps / cfdm / bgp / fw / cdn) | `AUTH_PORTAL_URL` + `AUTH_AUDIT_INGEST_SECRET` (тот же секрет) |
Dev default secret: `dev-audit-ingest-secret`.
+141
View File
@@ -0,0 +1,141 @@
# Интеграция auth-portal ↔ CDN Manager
Единый вход: пользователь логинится на auth-portal, получает JWT, переходит в CDNManager с токеном в URL fragment. CDNManager API проверяет JWT и права `cdn:*`.
## Архитектура
```
Browser → CDNManager UI (нет token)
→ redirect AUTH_PORTAL_URL/?return_to=…/auth/callback
→ login
→ redirect return_to#access_token=…
→ CDNManager /auth/callback сохраняет token (cdnmanager_token)
→ API Authorization: Bearer …
```
Общий секрет: `JWT_SECRET` / `AUTH_JWT_SECRET` (HS256). Issuer: `ISSUER` / `AUTH_ISSUER`.
App id в портале: **`cdn`** (каталог permissions).
## Локальный запуск
### 1. auth-portal
```bash
cd auth-portal
pnpm install
# JWT_SECRET=dev-secret-change-me
# RETURN_TO_ALLOWLIST=.shnt.top,localhost,http://localhost:5173,…,http://localhost:5176
pnpm --filter @authportal/api dev # :8080
pnpm --filter web dev # :5175
```
В `apps/web/.env.local` (опционально для App Switcher / SSO open):
```env
VITE_CDN_APP_URL=http://localhost:5176
```
Bootstrap: `[email protected]` / `admin`. В админке выдайте app **cdn** и permissions `cdn:*`.
### 2. CDNManager
```bash
cd CDNManager
pnpm install
```
Корень / API:
```env
AUTH_REQUIRED=true
AUTH_JWT_SECRET=dev-secret-change-me
AUTH_ISSUER=https://auth.shnt.top
AUTH_PORTAL_URL=http://localhost:5175
CLOUDFLARE_API_TOKEN=
```
`apps/web/.env.local`:
```env
VITE_AUTH_ENABLED=true
VITE_AUTH_PORTAL_URL=http://localhost:5175
```
Порт Vite web — `5176` (`apps/web/vite.config.ts`). Добавьте origin в `RETURN_TO_ALLOWLIST` портала.
```bash
pnpm --filter @cdnmanager/api dev
pnpm --filter web dev
```
Откройте CDNManager → редирект на portal → после логина NavUser показывает имя/email.
## Permissions ↔ API / UI
Иерархия: `admin``write``read` в рамках одной секции.
| Permission | API | UI |
|------------|-----|-----|
| `cdn:dashboard:read` | GET `/api/v1/dashboard/*`, `/topology` | `/` |
| `cdn:nodes:read` | GET `/api/v1/nodes*`, `/locations` | `/nodes` |
| `cdn:nodes:write` | POST/PATCH/DELETE nodes | create/edit нод |
| `cdn:aliases:read` | GET `/api/v1/aliases*` | `/aliases` |
| `cdn:aliases:write` | POST/PATCH aliases, retarget | create / Retarget |
| `cdn:zones:read` | GET `/api/v1/zones*` | `/zones` |
| `cdn:zones:write` | POST/PATCH zones, BIND export | создать зону |
| `cdn:sync:write` | POST `…/sync`, `…/apply` | Sync / Apply |
| `cdn:topology:read` | GET `/api/v1/topology` | `/topology` |
| `cdn:settings:admin` | GET/PATCH `/api/v1/settings` | `/settings/*` |
Без app `cdn` в JWT `apps`**403** на защищённые `/api/v1/*`.
`AUTH_REQUIRED=false` — локальный login (`ADMIN_*`) для тестов/dev без portal; UI `/login`.
## App Switcher
Публичный конфиг: `GET {AUTH_PORTAL_URL}/api/v1/app-switcher` (CORS open). CDNManager chrome (`AppSwitcher` / `AppsMenu`) читает его через `ensureAuthConfig().portalUrl`.
Редактор только на портале: **Админка → Ссылки приложений** (`/admin/apps`). В CDNManager Settings → Интеграции — read-only ссылка на портал.
`CURRENT_APP_ID = cdn`. Если в JWT есть `apps[]` — в меню только пересечение с каталогом.
## Audit ingest
Dual-write локального журнала в portal: [`integrate-audit-ingest.md`](./integrate-audit-ingest.md) (`source_app: cdn`).
## UI аккаунта
SidebarFooter → **NavUser**: Настройки, Тема, Выйти → `AUTH_PORTAL_URL/logout`.
## Logout (SSO)
Очистить `cdnmanager_token` → редирект на **`/logout`** портала (не на `/?return_to=…` — иначе portal сразу выдаст новый SSO-токен).
## Production (Docker)
Рекомендуется Traefik-стек в репозитории CDNManager: `docs/deploy-traefik.md`
(`deploy/docker-compose.traefik.yml` + `deploy/env.traefik.example`).
Ключевые env контейнера:
```env
AUTH_REQUIRED=true
AUTH_JWT_SECRET=<тот же JWT_SECRET портала>
AUTH_ISSUER=https://auth.shnt.top
AUTH_PORTAL_URL=https://auth.shnt.top
AUTH_AUDIT_INGEST_SECRET=<AUDIT_INGEST_SECRET портала>
CLOUDFLARE_API_TOKEN=<Zone DNS Edit + Zone Read>
```
В portal: `RETURN_TO_ALLOWLIST` включает `https://cdn.shnt.top` (или ваш origin).
## Troubleshooting
| Симптом | Причина |
|---------|---------|
| SSO loop / «Сессия не принята» | разный `JWT_SECRET` или `ISSUER` у portal и CDNManager |
| 403 «Нет доступа к приложению» | у пользователя нет app `cdn` в portal |
| 403 «Недостаточно прав» | нет нужного `cdn:…` permission |
| return_to rejected | origin CDNManager не в `RETURN_TO_ALLOWLIST` |
| «Выйти» сразу возвращает в CDNManager | клиент должен открывать `/logout`, не login с `return_to` |
+1 -1
View File
@@ -43,7 +43,7 @@ Nav groups Auth Portal:
- **Портал:** Приложения (`/apps`)
- **Админ** (только `is_admin`): Пользователи (`/admin`), Журнал (`/admin/audit`), Ссылки приложений (`/admin/apps`)
App Switcher: `portal_settings.app_switcher_json` → public `GET /api/v1/app-switcher`, admin `GET/PUT /api/v1/admin/app-switcher`. Ids: `cfdm` · `vps` · `bgp` · `fw` · `dns`.
App Switcher: `portal_settings.app_switcher_json` → public `GET /api/v1/app-switcher`, admin `GET/PUT /api/v1/admin/app-switcher`. Ids: `cfdm` · `vps` · `bgp` · `fw` · `dns` · `cdn`.
## MCP workflow