From 8b49d490482ad89e8beee248a8588c9aa90b57dc Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Sat, 28 Mar 2026 15:57:02 +0800 Subject: [PATCH] feat: add edit sub-account profile + verify all password features - Add edit profile (display name, phone, email) with Volcengine sync - Add IAMService.update_user for Volcengine UpdateUser API - Add edit-profile API endpoint and URL - Add Edit Profile dialog in IAMUserList frontend - Verify admin change password, sub-account change password, set login password all working Co-Authored-By: Claude Opus 4.6 (1M context) --- API方案.md | 33 ++++++++++++++ backend/apps/monitor/urls.py | 1 + backend/apps/monitor/views.py | 44 +++++++++++++++++++ backend/utils/iam_service.py | 11 +++++ docker-compose.yml | 6 +-- frontend/src/views/iam/IAMUserList.vue | 60 ++++++++++++++++++++++++++ 6 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 API方案.md diff --git a/API方案.md b/API方案.md new file mode 100644 index 0000000..25b528e --- /dev/null +++ b/API方案.md @@ -0,0 +1,33 @@ +方案一:火山 IAM 子账号(推荐) +思路:给队友创建火山子用户,让他直连火山 API。 + +给他的东西 +用途 给什么 +Assets API 子账号的 AK/SK +Seedance 调用 子账号的 Ark API Key + 专用接入点(只绑 Seedance 2.0) +对账 控制台登录密码 +项目标识 ProjectName: "int_dev_Airlabs" +权限范围 +✅ Ark 模型调用、Assets 素材管理、费用中心(只读) +❌ IAM 管理、其他云服务、充值/支付 +优缺点 +✅ 零开发量,控制台几分钟搞定 +✅ 他能自己对账 +✅ 权限可控,随时可禁用/删除 +✅ 互不影响,你的服务挂了不影响他 +❌ 他能直接接触火山资源(但权限受限) +方案二:后端转发 +思路:你的后端包一层,队友只调你的接口,AK/SK 不出服务器。 + +给他的东西 +用途 给什么 +所有调用 后端地址 + 账号密码(JWT 认证) +优缺点 +✅ 队友什么密钥都不需要,最安全 +✅ 你能完全掌控调用行为 +❌ 要写新接口把火山 API 都包一遍 +❌ 对账要你自己做用量统计页面 +❌ 多一跳,依赖你的服务稳定性 +❌ 火山 API 变更你得跟着维护 +一句话结论 +队友是自己人 → 方案一,省事;对外卖服务/不信任对方 → 方案二,可控。 \ No newline at end of file diff --git a/backend/apps/monitor/urls.py b/backend/apps/monitor/urls.py index 7ff3cb5..61ebba9 100644 --- a/backend/apps/monitor/urls.py +++ b/backend/apps/monitor/urls.py @@ -17,6 +17,7 @@ urlpatterns = [ path('iam-users/import/', views.iam_user_import_view), path('iam-users//', views.iam_user_detail_view), path('iam-users//update/', views.iam_user_update_view), + path('iam-users//edit-profile/', views.iam_user_edit_profile_view), path('iam-users//set-login/', views.iam_user_set_login_view), path('iam-users//disable/', views.iam_user_disable_view), path('iam-users//enable/', views.iam_user_enable_view), diff --git a/backend/apps/monitor/views.py b/backend/apps/monitor/views.py index e4a21d3..58080f7 100644 --- a/backend/apps/monitor/views.py +++ b/backend/apps/monitor/views.py @@ -369,6 +369,50 @@ def iam_user_update_view(request, pk): return Response(IAMUserSerializer(user).data) +@api_view(['POST']) +def iam_user_edit_profile_view(request, pk): + """编辑子账号信息(显示名、手机号、邮箱),同步到火山""" + try: + user = IAMUser.objects.get(pk=pk) + except IAMUser.DoesNotExist: + return Response({'error': 'not_found'}, status=status.HTTP_404_NOT_FOUND) + + display_name = request.data.get('display_name') + email = request.data.get('email') + phone = request.data.get('phone') + + # Update on Volcengine + account = user.volc_account + ak = decrypt(account.access_key_enc) + sk = decrypt(account.secret_key_enc) + iam = IAMService(ak, sk) + + try: + iam.update_user(user.username, + display_name=display_name, + email=email, + phone=phone) + except VolcengineAPIError as e: + return Response({'message': f'火山 API 更新失败: {e}'}, + status=status.HTTP_400_BAD_REQUEST) + + # Update locally + if display_name is not None: + user.display_name = display_name + if email is not None: + user.email = email + if phone is not None: + user.phone = phone + user.save() + + AlertRecord.objects.create( + iam_user=user, alert_type='manual', + title=f'编辑子账号信息 {user.username}', + content=f'操作人: {request.user.username}', + ) + return Response({'message': '已更新', 'user': IAMUserSerializer(user).data}) + + @api_view(['POST']) def iam_user_set_login_view(request, pk): """设置子账号的 AirGate 登录密码""" diff --git a/backend/utils/iam_service.py b/backend/utils/iam_service.py index 7eca1c4..cd644a7 100644 --- a/backend/utils/iam_service.py +++ b/backend/utils/iam_service.py @@ -76,6 +76,17 @@ class IAMService: "PolicyType": policy_type, }) + def update_user(self, username: str, display_name: str = None, + email: str = None, phone: str = None) -> dict: + params = {"UserName": username} + if display_name is not None: + params["NewDisplayName"] = display_name + if email is not None: + params["NewEmail"] = email + if phone is not None: + params["NewMobilePhone"] = phone + return self.client.call("UpdateUser", params) + def list_attached_user_policies(self, username: str) -> dict: return self.client.call("ListAttachedUserPolicies", {"UserName": username}) diff --git a/docker-compose.yml b/docker-compose.yml index 62ef58c..ce01bf3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.8' services: - airgate-backend: + backend: build: ./backend ports: - "8101:8100" @@ -15,12 +15,12 @@ services: - backend-data:/app/data restart: unless-stopped - airgate-web: + frontend: build: ./frontend ports: - "5174:80" depends_on: - - airgate-backend + - backend restart: unless-stopped volumes: diff --git a/frontend/src/views/iam/IAMUserList.vue b/frontend/src/views/iam/IAMUserList.vue index faff8d0..a444be2 100644 --- a/frontend/src/views/iam/IAMUserList.vue +++ b/frontend/src/views/iam/IAMUserList.vue @@ -78,6 +78,7 @@ 项目管理 监控配置 + 编辑信息 权限策略 划拨记录 登录密码 @@ -343,6 +344,32 @@ + + + + + + + + + + + + + + + + +
+ 修改会同步到火山引擎 IAM +
+ +
+