From 3d2b33265790b14b66ab3dd9025b51da202d6cf4 Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Sat, 21 Mar 2026 01:33:02 +0800 Subject: [PATCH] feat: add sub-account portal (login + my keys view) - Login page: toggle between admin/sub-account login - Auth store: isAdmin/isIamUser computed properties - MainLayout: role-based sidebar (admin sees all, sub-account sees only my keys) - HomeRedirect: auto-redirect based on role - MyKeysView: sub-account can view/reveal their own API Keys - Portal is completely isolated from admin functions Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/nginx.conf | 2 +- frontend/src/layouts/MainLayout.vue | 73 +++++++++++++--------- frontend/src/router/index.js | 11 +++- frontend/src/stores/auth.js | 8 ++- frontend/src/views/HomeRedirect.vue | 20 ++++++ frontend/src/views/LoginView.vue | 22 +++++-- frontend/src/views/portal/MyKeysView.vue | 77 ++++++++++++++++++++++++ 7 files changed, 175 insertions(+), 38 deletions(-) create mode 100644 frontend/src/views/HomeRedirect.vue create mode 100644 frontend/src/views/portal/MyKeysView.vue diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 64b00e1..e56a400 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -5,7 +5,7 @@ server { index index.html; location /api/ { - proxy_pass http://airgate-backend:8100; + proxy_pass http://backend:8100; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue index 8d2406a..137d154 100644 --- a/frontend/src/layouts/MainLayout.vue +++ b/frontend/src/layouts/MainLayout.vue @@ -4,41 +4,56 @@ - - - 仪表盘 - - - - 子账号管理 - - - - API Key 管理 - - - - 消费监控 - - - - 告警记录 - - - - 系统设置 - - - - 系统管理 - + + + + + + - {{ auth.user?.username }} + 子账号 + + {{ auth.user?.display_name || auth.user?.username }} + 退出登录 diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 29c7ff2..5f7cb7e 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -12,13 +12,22 @@ const routes = [ path: '/', component: () => import('../layouts/MainLayout.vue'), children: [ - { path: '', name: 'Dashboard', component: () => import('../views/dashboard/DashboardView.vue') }, + // Dynamic home: admin sees Dashboard, iam_user sees MyKeys + { + path: '', + name: 'Home', + component: () => import('../views/HomeRedirect.vue'), + }, + // Admin routes + { path: 'dashboard', name: 'Dashboard', component: () => import('../views/dashboard/DashboardView.vue') }, { path: 'iam-users', name: 'IAMUsers', component: () => import('../views/iam/IAMUserList.vue') }, { path: 'billing', name: 'Billing', component: () => import('../views/billing/BillingView.vue') }, { path: 'alerts', name: 'Alerts', component: () => import('../views/alerts/AlertList.vue') }, { path: 'ark-keys', name: 'ArkKeys', component: () => import('../views/ark/ArkKeysView.vue') }, { path: 'settings', name: 'Settings', component: () => import('../views/settings/SettingsView.vue') }, { path: 'admin', name: 'Admin', component: () => import('../views/admin/AdminView.vue') }, + // IAM user (sub-account) routes + { path: 'my-keys', name: 'MyKeys', component: () => import('../views/portal/MyKeysView.vue') }, ], }, ] diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js index fff08d1..3c8f32d 100644 --- a/frontend/src/stores/auth.js +++ b/frontend/src/stores/auth.js @@ -7,13 +7,15 @@ export const useAuthStore = defineStore('auth', () => { const user = ref(JSON.parse(localStorage.getItem('airgate_user') || 'null')) const isLoggedIn = computed(() => !!token.value) + const isAdmin = computed(() => user.value?.role !== 'iam_user') + const isIamUser = computed(() => user.value?.role === 'iam_user') function setAuth(data) { token.value = data.access - refreshToken.value = data.refresh + refreshToken.value = data.refresh || '' user.value = data.user localStorage.setItem('airgate_token', data.access) - localStorage.setItem('airgate_refresh', data.refresh) + localStorage.setItem('airgate_refresh', data.refresh || '') localStorage.setItem('airgate_user', JSON.stringify(data.user)) } @@ -26,5 +28,5 @@ export const useAuthStore = defineStore('auth', () => { localStorage.removeItem('airgate_user') } - return { token, refreshToken, user, isLoggedIn, setAuth, logout } + return { token, refreshToken, user, isLoggedIn, isAdmin, isIamUser, setAuth, logout } }) diff --git a/frontend/src/views/HomeRedirect.vue b/frontend/src/views/HomeRedirect.vue new file mode 100644 index 0000000..01acfd9 --- /dev/null +++ b/frontend/src/views/HomeRedirect.vue @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue index 7a04860..88d1b79 100644 --- a/frontend/src/views/LoginView.vue +++ b/frontend/src/views/LoginView.vue @@ -5,9 +5,14 @@

AirGate

火山引擎 IAM 子账号管控平台

+ + + - + +
+

我的 API Key

+ + + + + + + + + + + + +
+ + +