# ADR-UI-01: Operator Console Architecture
**狀態**:Accepted
**日期**:2026-05-03(台北)
**決策者**:統帥
**範圍**:AwoooP Operator Console 前端整體架構、與現有 apps/web/ 的整合方式
**關聯**:ADR-106(六合約)、ADR-112(contract governance)、ADR-115(principal mapping)
---
## 背景
AwoooP 需要一個 Operator Console(管理員後台),讓平台管理員可以:
1. 管理 tenant(project)和 principal 角色
2. 查看和管理 contract revisions(六合約的 draft/publish/activate)
3. 監控 runs(run state, saga steps, budget usage)
4. 執行 approval decisions(human approval for WAITING_APPROVAL runs)
現有 `apps/web/` 是 Next.js 14 前端,已有 AI 監控 dashboard。Operator Console 需要在不破壞現有功能的情況下整合。
---
## 決策
### D1 — 整合方式:子路由擴展(不建新應用)
Operator Console 作為 `apps/web/` 的新路由段:
```
apps/web/src/app/
├── (existing)/ ← 現有頁面(不動)
│ ├── dashboard/
│ ├── incidents/
│ └── ...
└── awooop/ ← 新增 Operator Console
├── layout.tsx ← Console layout(sidebar + auth gate)
├── tenants/ ← Tenant 管理
│ ├── page.tsx
│ └── [project_id]/
│ ├── page.tsx
│ └── principals/
├── contracts/ ← Contract governance
│ ├── page.tsx
│ └── [contract_id]/
│ ├── page.tsx
│ └── revisions/
├── runs/ ← Run 監控
│ ├── page.tsx
│ └── [run_id]/
│ └── page.tsx
└── approvals/ ← Approval decisions
├── page.tsx
└── [run_id]/
└── page.tsx
```
### D2 — Auth Gate(Operator Console 專屬)
Operator Console 需要 `platform_subject.roles` 中包含 `admin` 或 `approver`:
```typescript
// apps/web/src/app/awooop/layout.tsx
import { redirect } from "next/navigation"
import { getServerSession } from "@/lib/auth"
import { hasOperatorRole } from "@/lib/awooop-auth"
export default async function AwoooPLayout({ children }) {
const session = await getServerSession()
if (!session || !hasOperatorRole(session.user)) {
redirect("/unauthorized")
}
return (
)
}
```
### D3 — API Client 分層
```
apps/web/src/lib/
├── awooop/
│ ├── client.ts ← AwoooP Platform API client(/v1/platform/...)
│ ├── types.ts ← TypeScript types(從 API spec 生成)
│ ├── runs.ts ← Run 相關 API calls
│ ├── contracts.ts ← Contract 相關 API calls
│ ├── tenants.ts ← Tenant 相關 API calls
│ └── approvals.ts ← Approval 相關 API calls
```
**API base URL**:
```typescript
// 從環境變數讀取(禁止 hardcode 內網 IP,feedback_frontend_internal_ip_ban)
const AWOOOP_API_BASE = process.env.NEXT_PUBLIC_AWOOOP_API_URL
// K8s ConfigMap 設置:NEXT_PUBLIC_AWOOOP_API_URL=https://api.awoooi.com
```
### D4 — 8 個 UI 模組(Phase 5 實作)
| 模組 | 路由 | 功能 | ADR 關聯 |
|------|------|------|---------|
| M1 Tenant List | `/awooop/tenants` | 列出所有 project,建立/停用 | ADR-115 |
| M2 Principal Manager | `/awooop/tenants/[id]/principals` | 角色管理,auto-provision 確認 | ADR-115 |
| M3 Contract Dashboard | `/awooop/contracts` | 六合約總覽,active revision 狀態 | ADR-106 |
| M4 Contract Editor | `/awooop/contracts/[id]/revisions` | draft/publish/activate 操作 | ADR-112 |
| M5 Run Monitor | `/awooop/runs` | 所有 run 的即時狀態 | ADR-114 |
| M6 Run Detail | `/awooop/runs/[run_id]` | saga_steps, budget, trace_id | ADR-119 |
| M7 Approval Queue | `/awooop/approvals` | WAITING_APPROVAL run 列表 | ADR-114 |
| M8 Approval Decision | `/awooop/approvals/[run_id]` | approve/reject + token 生成 | ADR-116 |
**各模組的詳細設計在 ADR-UI-02 / ADR-UI-03 / ADR-UI-04。**
### D5 — 設計原則
1. **禁止 emoji,使用 Lucide icon**(feedback_no_emoji_use_icons)
2. **全站統一字體/顏色**(feedback_design_system_consistency)
3. **100% next-intl i18n**(feedback_i18n_zero_hardcode)— 所有文字走翻譯 key
4. **禁止內網 IP**(feedback_frontend_internal_ip_ban)— NEXT_PUBLIC_* 只用公網域名
5. **Logo SVG 與正式環境一致**(feedback_brand_logo_consistency)
### D6 — 即時更新(WebSocket / SSE)
Run Monitor(M5)和 Approval Queue(M7)需要即時更新:
```typescript
// Server-Sent Events(比 WebSocket 更簡單,適合單向推送)
// GET /v1/platform/runs/stream?project_id=awoooi&status=RUNNING,WAITING_APPROVAL
const eventSource = new EventSource(
`/v1/platform/runs/stream?project_id=${projectId}`
)
eventSource.onmessage = (event) => {
const update = JSON.parse(event.data) as RunStateUpdate
updateRunInCache(update)
}
```
---
## 後果
### Benefits
- 複用現有 apps/web/ 的 auth、i18n、設計系統(不重造輪子)
- 子路由設計:現有功能不受影響,可獨立部署 Operator Console 路由
- Phase 5 前,`/awooop/*` 路由可以用 feature flag 隱藏(9 個 feature flag 中的 `ENABLE_OPERATOR_CONSOLE`)
### Costs
- 需要在 apps/web/ 建立 `awooop/` 目錄結構(~15 個新檔案)
- API client 需要 TypeScript type generation(從 OpenAPI spec)
### Risks
- Operator Console 的 auth gate 若有漏洞 → 非管理員可存取敏感操作
- 緩解:auth gate 在 layout.tsx 實作(server-side),不依賴 client-side 隱藏
---
## 驗收標準
- [ ] `/awooop` 路由在 `apps/web/src/app/` 建立(Phase 5)
- [ ] auth gate:非 admin/approver → redirect `/unauthorized`(整合測試)
- [ ] `NEXT_PUBLIC_AWOOOP_API_URL` 不含內網 IP(CI lint check)
- [ ] 8 個模組全部有對應路由(Phase 5 完成)
## 關聯
- ADR-106(六合約,Contract Dashboard M3/M4 的資料來源)
- ADR-112(contract governance API,M4 的操作路徑)
- ADR-114(run state,M5 M6 的資料來源)
- ADR-115(principal mapping,M1 M2)
- ADR-116(approval token,M8 核心功能)