member_center/docs/ADMIN_CLIENT_SPLIT_PLAN.md
warrenchen 75e235b8e3 Add admin area controllers and views for managing OAuth clients, security settings, subscriptions, and tenants
- Implemented OAuthClientsController for CRUD operations on OAuth clients.
- Added SecurityController to manage security settings.
- Created SubscriptionsController for handling subscriptions with export functionality.
- Developed TenantsController for tenant management including create, edit, and delete operations.
- Added views for each controller to facilitate user interaction.
- Introduced layout and shared views for consistent admin UI.
- Implemented model classes for handling data in views.
- Added validation and error handling in forms.
2026-04-01 17:40:45 +09:00

261 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Admin / Client UI 拆分工作計劃
## 目標
- 在同一個 `MemberCenter.Web` 專案中,將會員端與管理端 UI 明確分區。
- 保持單一登入入口與單一帳號系統。
- 讓具有 `admin` 權限的帳號同時可使用會員功能與管理功能。
- 將目前偏向後台的共用介面,調整為 client-first 的會員中心體驗。
- 補齊會員註冊、Google 第三方登入與訂閱資料銜接流程。
## 已確認決策
### 帳號與登入
- `Admin``Client` 共用同一個登入入口。
- `Admin` 帳號同時也是會員帳號,可使用會員介面功能。
- 不拆成兩套認證系統,不建立獨立 admin login。
### UI 與導覽
- 會員端為主介面。
- 功能選單採分組方式呈現。
- 一般會員只看到會員功能。
- 具有 `admin` role 的帳號,額外看到 `Admin` 功能分類。
- `Admin` 分類展開後顯示管理功能連結。
- 目前先以 `admin` role 做整包顯示,不先做細權限切分。
### 未授權存取
- 非 admin 使用者存取 `/admin/*` 時,回應 `404`
- 不使用 `403` 頁面暴露後台存在。
## 目標範圍
### 本次要做
- 調整 `MemberCenter.Web` 路由與結構,將管理端移入 `Areas/Admin`
- 將現有共用 layout 改為 client-first 導覽。
- 將 admin 功能從全站共用導覽中抽離,改成 role-based 顯示。
- 建立 admin 路由未授權時回 `404` 的處理方式。
- 補上會員註冊、第三方登入與訂閱綁定的工作規劃。
### 本次不做
- 細粒度權限模型,例如依功能模組拆 `tenant.read``audit.read`
- 獨立的 `AdminWeb` / `ClientWeb` 專案拆分。
- 大幅重做視覺設計。
- API 權限模型重構。
- 註冊確認信寄送實作。
## 實作策略
### 策略原則
- 先切 UI 邊界,再保留既有 Identity 與 role policy。
- 先做低風險結構重整,不同時引入細權限與大幅 UI redesign。
- 保持既有 URL 慣例,避免不必要的 route breakage。
### 預期結構
```text
src/MemberCenter.Web/
├── Areas/
│ └── Admin/
│ ├── Controllers/
│ └── Views/
├── Controllers/ # client only
├── Views/
│ ├── Shared/
│ │ ├── _Layout.cshtml # client-first layout
│ │ └── ...
│ └── ...
```
## 分階段計劃
### Phase 0: 會員註冊與帳號銜接規格補齊
狀態:已完成
目標:先將會員建立、第三方登入與訂閱資料綁定的規則固定,避免後續 UI 與 auth 重構互相衝突。
#### 需求規則
- 會員帳號以 `email` 為主要識別。
- `UserName` 強制等於 `Email`
- 不提供獨立 username。
- 本地註冊完成後,帳號標記為未認證。
- 本階段先不寄送確認信。
- 未認證帳號仍可登入。
- 後續功能完整後,未認證帳號將可被限制部分功能;本階段先保留此狀態與擴充空間。
- 支援 Google 作為第一個第三方登入/註冊 provider。
- Google 第一次登入時,若系統已存在相同 email 的本地帳號,直接 auto-link。
- Google 回傳 email 即使未驗證,仍允許建立帳號或連接既有帳號。
- 使用者若先以本地帳號註冊,之後再以 Google 同 email 登入,應連接到同一個帳號,不建立第二個 user。
- 註冊成功後,若系統中已有相同 email 的訂閱資料,需將相關 `newsletter_subscriptions.user_id` 補上。
- 訂閱綁定時必須保留既有訂閱狀態與偏好,不可覆蓋。
- 訂閱綁定完成後需補一筆 audit log。
- 後續若導入事件,保留發送 `subscription.linked_to_user` 的擴充空間。
#### 子工作
- 已完成:定義本地註冊後的帳號狀態與登入規則。
- 已完成:定義 Google external login / register / auto-link 流程。
- 已完成:定義訂閱資料綁定與 audit log 寫入時機。
- 已完成:將上述規則同步反映到目前的 Web / API 實作階段。
- 已完成:`subscription.linked_to_user` 事件發送。
- 註記:未認證帳號的功能限制屬後續能力擴充,不阻擋本 phase 完成。
- 註記Google 實際整合驗證仍需提供 Google OAuth 設定,屬外部驗證條件,不阻擋本 phase 完成。
完成條件:
- 註冊、Google 登入、同 email 帳號連接、訂閱綁定規則均有明確定義。
- 後續 Phase 1 之後的 UI 與 auth 重構可直接依規則實作。
### Phase 1: Route 與目錄切分
狀態:已完成
目標:先建立清楚的 UI 邊界。
- 將現有 `Controllers/Admin/*` 移入 `Areas/Admin/Controllers/*`
- 將現有 `Views/Admin/*` 移入 `Areas/Admin/Views/*`
- 調整 route 設定,讓 `/admin/*` 由 area route 處理。
- 確認既有 admin URL 可維持不變。
完成條件:
- 所有 admin 頁面由 `Areas/Admin` 提供。
- 會員端 controller 不再與 admin controller 混在同一層。
### Phase 2: Layout 與導覽切分
狀態:已完成
目標:把 UI 改成 client-first不再全站露出後台功能。
- 重構共用 layout移除固定顯示的 admin 連結。
- 建立 client-first 功能選單。
- 若使用者具 `admin` role顯示 `Admin` 功能分類。
- `Admin` 分類底下先列出既有管理功能:
- Tenants
- Newsletter Lists
- Subscriptions
- OAuth Clients
- Audit Logs
- Security
- Blacklist
完成條件:
- 一般會員不會在主選單看到 admin 連結。
- admin 使用者可從同一套主介面展開進入管理功能。
### Phase 3: Admin 畫面容器整理
狀態:已完成
目標:讓進入 admin 區後有明確上下文。
- 規劃 admin area 是否使用獨立 layout。
- 若使用獨立 layout保留回會員區入口。
- 若先共用 layout至少在 admin 頁面標示目前位於管理區。
建議:
- 第一版可先採用共用主殼 + admin 區塊標示。
- 若後續 admin 功能持續增長,再抽 `_AdminLayout`
目前進度:
- 已完成:獨立 `Admin` area layout。
- 已完成:保留回會員區入口。
- 已完成admin shell 基礎結構整理top bar、side nav、active state、區域標示
- 已完成:補上可替換的基礎樣式 hooks避免後續設計重做時需要拆 route 或 view 結構。
- 註記:後續若需進一步整理 admin/client 的整體體驗、內容層級、表格與表單版型,視為 UI/UX refinement不阻擋本 phase 完成。
完成條件:
- 使用者進入 admin 頁面時,有清楚的區域辨識。
### Phase 4: 未授權存取改為 404
狀態:已完成
目標:保留授權檢查,同時隱藏 admin surface。
- 保留 `[Authorize(Policy = "Admin")]`
- 增加 admin 未授權時的統一處理,避免顯示預設 `403`
- 確認未登入與已登入但非 admin 的行為符合預期。
已定案:
- 未登入進 `/admin/*`:直接回 `404`
- 已登入但非 admin 進 `/admin/*`:直接回 `404`
目前進度:
- 已完成:保留 `[Authorize(Policy = "Admin")]`
- 已完成admin 未授權時不顯示預設 `403`,改為 `404`
- 已完成:目前實作上,未登入與非 admin 存取 `/admin/*` 均回 `404`
- 已完成:將「未登入也回 `404`」正式定案並同步到工作計劃。
### Phase 5: 驗證與文件更新
狀態:已完成
目標:確保重構後行為可驗證、文件一致。
- 驗證會員端主要頁面仍可正常使用。
- 驗證 admin 帳號可以:
- 使用會員功能
- 看見 `Admin` 選單
- 進入 admin 各頁
- 驗證非 admin 帳號無法看見 admin 選單,且直接進 admin URL 會得到 `404`
- 更新 README / UI 文件中的 web 結構描述
目前進度:
- 已完成:`dotnet build MemberCenter.sln -m:1`
- 已完成:會員端主要頁面可用性驗證(首頁 / login / register
- 已完成admin 帳號操作驗證(可登入、可進 member profile、可進 admin route
- 已完成:一般會員登入驗證(可登入、可進 profile、首頁不顯示 admin 群組)。
- 已完成:非 admin / 未登入情境驗證(匿名打 `/admin/*``404`)。
- 已完成計劃文件、README、UI / Flow / Use Case / Design / OpenAPI 相關文件更新。
- 註記Google 真實 round-trip 驗證需提供 Google OAuth 設定,屬外部條件,不阻擋本 phase 完成。
## 影響檔案預估
- `src/MemberCenter.Web/Program.cs`
- `src/MemberCenter.Web/Views/Shared/_Layout.cshtml`
- `src/MemberCenter.Web/Controllers/Admin/*`
- `src/MemberCenter.Web/Views/Admin/*`
- 新增 `src/MemberCenter.Web/Areas/Admin/...`
- 視需要更新 `docs/UI.md`
## 風險與注意事項
- Area 導入後view 路徑與 route mapping 容易有小錯誤,需要逐頁驗證。
- 若直接共用同一個 layout需避免 client 與 admin 的語意混亂。
- `404` 偽裝策略要搭配真正的 authorization不能只靠 route 隱藏。
- 若未登入也直接回 `404`,可能會讓合法 admin 使用者失去登入引導;這點需明確決策。
## 建議執行順序
1. 先完成 Phase 0確認註冊、Google 登入與訂閱綁定規則。
2. 再完成 Phase 1做純結構重整。
3. 接著做 Phase 2修正選單與角色顯示。
4. 然後決定 Phase 3 要共用 layout 還是抽 admin layout。
5. 再做 Phase 4補齊 `404` 授權行為。
6. 最後做 Phase 5 的驗證與文件更新。
## 本次文件用途
這份計劃作為後續逐步實作的工作底稿。後續每一步都應以「單一階段可驗證完成」為原則,避免一次改太多導致 routing、授權與 UI 問題混在一起。
## 目前總結
- 已完成Phase 0、Phase 1、Phase 2、Phase 3、Phase 4、Phase 5
- 部分完成Phase 5