First Commit
Build and Push Auth Portal Docker Image / build-and-push (push) Successful in 5m26s
Build and Push Auth Portal Docker Image / create-release (push) Skipped

This commit is contained in:
Denozordec
2026-07-18 13:27:37 +07:00
commit bac95bdb2e
154 changed files with 26610 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
# Интеграция auth-portal ↔ VPS Tracker
Единый вход: пользователь логинится на auth-portal, получает JWT, переходит в VPS Tracker с токеном в URL fragment. VPS API проверяет JWT и права `vps:*`.
## Архитектура
```
Browser → VPS UI (нет token)
→ redirect AUTH_PORTAL_URL/?return_to=http://localhost:5173/auth/callback
→ login
→ redirect return_to#access_token=…
→ VPS /auth/callback сохраняет token
→ API Authorization: Bearer …
```
Общий секрет: `JWT_SECRET` / `AUTH_JWT_SECRET` (HS256). Issuer: `ISSUER` / `AUTH_ISSUER`.
## Локальный запуск
### 1. auth-portal
```bash
cd auth-portal
pnpm install
cp .env.example .env
# JWT_SECRET=dev-secret-change-me
# RETURN_TO_ALLOWLIST=.shnt.top,localhost,http://localhost:5173
pnpm --filter @authportal/api dev # :8080
pnpm --filter web dev # :5175
```
В `apps/web/.env.local`:
```env
VITE_VPS_APP_URL=http://localhost:5173
```
Bootstrap: `[email protected]` / `admin`. В админке выдайте app **vps** и нужные permissions.
### 2. vps-tracker
```bash
cd vps-tracker
pnpm install
```
Корень / API env (или shell):
```env
AUTH_REQUIRED=true
AUTH_JWT_SECRET=dev-secret-change-me
AUTH_ISSUER=https://auth.shnt.top
AUTH_PORTAL_URL=http://localhost:5175
```
`apps/web/.env.local`:
```env
VITE_AUTH_ENABLED=true
VITE_AUTH_PORTAL_URL=http://localhost:5175
```
```bash
pnpm --filter @cfdm/api dev
pnpm --filter web dev # :5173
```
Откройте http://localhost:5173 → редирект на portal → после логина обратно в VPS.
## Permissions ↔ API / UI
Иерархия: `admin``write``read` в рамках одной секции.
| Permission | API | UI |
|------------|-----|-----|
| `vps:dashboard:read` | `GET /api/dashboard/*` | `/dashboard` |
| `vps:vps:read` | `GET /api/vps`, projects, reports-related reads | `/vps`, `/tariffs`, `/projects`, аналитика |
| `vps:vps:write` | POST/PUT/PATCH/DELETE `/api/vps`, projects | create/edit/delete VPS |
| `vps:accounts:read` | GET providers, provider-accounts | `/providers`, `/accounts` |
| `vps:accounts:write` | мутации providers/accounts | формы аккаунтов |
| `vps:payments:read` | GET payments, balance-ledger | `/payments`, `/balance` |
| `vps:payments:write` | мутации payments / ledger | формы платежей |
| `vps:sync:write` | POST `/api/sync/*`, GET sync status | `/sync-journal`, кнопка sync |
| `vps:settings:admin` | `/api/settings`, backup, audit, migrate | `/settings`, `/audit` |
Без app `vps` в JWT `apps`**403** на весь `/api/*` (кроме health и CFDM integration).
`AUTH_REQUIRED=false` — auth выключен (удобно для локальной разработки без portal).
## Troubleshooting
| Симптом | Причина |
|---------|---------|
| 401 на API | Нет/битый Bearer; разные `JWT_SECRET` |
| 403 «нет доступа к приложению» | В portal не выдан app `vps` |
| 403 на write | Только `*:read` в permissions |
| Loop на login | `return_to` не в `RETURN_TO_ALLOWLIST` |
| CORS | Portal и VPS на разных origin — fragment handoff не требует CORS для token |
## Production
- Один `JWT_SECRET` в secret store обоих сервисов
- `ISSUER=https://auth.shnt.top`
- `RETURN_TO_ALLOWLIST=.shnt.top,https://vps.shnt.top`
- `AUTH_REQUIRED=true`, `VITE_AUTH_ENABLED=true`
- `VITE_VPS_APP_URL=https://vps.shnt.top` (portal)
+41
View File
@@ -0,0 +1,41 @@
# UI Design Contract — Auth Portal
Surface lock: **`frame`** (ReUI Frame). Не смешивать shadcn Card и Frame на ops-экранах.
Эталоны: [app-shell-12](https://reui.io/preview/base/app-shell-12) · [solution-users-1](https://reui.io/preview/base/solution-users-1) · [solution-users-2](https://reui.io/preview/base/solution-users-2) · [data-grid-filtering-2](https://reui.io/preview/base/data-grid-filtering-2) · [empty-state-12](https://reui.io/preview/base/empty-state-12)
## Shared App Shell chrome
Эталон: CFDM / EvoBGP + ReUI [app-shell-12](https://reui.io/preview/base/app-shell-12).
При переключении между portal / CFDM / EvoBGP / vps-tracker меняются **только** sidebar nav labels/hrefs и `main` content. Разметка, ширина, фон и hover chrome идентичны.
| Токен / зона | Значение |
|--------------|----------|
| `--sidebar-width` | `240px` |
| Sidebar / hover colors | theme `--sidebar` / `--sidebar-accent`**без** AppShell `color-mix` override |
| Header | `h-12`, `sticky`, `border-b`, `px-4 md:px-6` |
| Header left | `SidebarTrigger` + `Separator` + Breadcrumb |
| Header right | **AppsMenu****ModeToggle** (+ account menu; SystemMonitor — stub/нет) |
| Sidebar | AppSwitcher → groups → icons `size-4`**пустой** `SidebarFooter` |
| `main` | `gap-4 md:gap-6`, `px-4 py-4 md:px-6 md:py-5` |
Запрещено в chrome: `SidebarRail`, `NavUser` footer, Search pill в header.
Nav groups Auth Portal:
- **Портал:** Приложения (`/apps`)
- **Админ** (только `is_admin`): Пользователи (`/admin`)
## Spacing
- AppShell main / PageShell: `gap-4 md:gap-6`
- No `space-y-*` / `space-x-*` — use `flex` + `gap-*`
- Max 1 primary CTA per screen
- Semantic tokens only (`Badge` / `Alert` variants) — no raw `bg-emerald-*`
## Forbidden
- Card as ops list/dashboard shell
- Hand-rolled data tables when ReUI DataGrid exists
- Mixing Card and Frame on the same ops screen