diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 363dd4f..1ee86f6 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -21,6 +21,7 @@ "Agent Detail": "智能体详情", "Flow Builder": "流程编排", "Profile": "个人资料", + "Update Profile Information": "更新个人资料信息", "Logout": "退出登录", "Logged out": "已退出登录", "AI Agent Workflow Platform": "AI Agent 工作流平台", @@ -355,6 +356,8 @@ "Unsaved": "未保存", "Saved": "已保存", "Copy failed, please copy manually": "复制失败,请手动复制", + "Copied to clipboard": "已复制到剪贴板", + "Copy failed": "复制失败", "All": "全部", "Recent": "最近使用", "Favorites": "收藏", @@ -1237,5 +1240,122 @@ "Online Support": "在线客服", "Feedback & Suggestions": "反馈建议", "Guangzhou Sunflower Network Information Technology Co., Ltd.": "广州向日葵网络信息技术有限公司", - "All Rights Reserved": "版权所有" + "All Rights Reserved": "版权所有", + + "API Access": "API 访问", + "API key and API secret can be used to access": "API 密钥和 API 密钥可用于访问", + "API key and API secret pairs can be used to access the Jingrow API.": "API 密钥和 API 密钥对可用于访问 Jingrow API。", + "Jingrow API": "Jingrow API", + "Create New API Key": "创建新的 API 密钥", + "Regenerate API Key": "重新生成 API 密钥", + "You don't have an API key yet. Click the button above to create one.": "您还没有 API 密钥。请点击上方按钮创建一个。", + "Please copy the API secret now. You won't be able to see it again!": "请立即复制 API 密钥。您将无法再次查看它!", + "API Secret": "API 密钥", + "SSH Keys": "SSH 密钥", + "Add SSH Key": "添加 SSH 密钥", + "Add New SSH Key": "添加新的 SSH 密钥", + "SSH Key": "SSH 密钥", + "SSH Fingerprint": "SSH 指纹", + "Added Time": "添加时间", + "Set as Default": "设为默认", + "No SSH keys configured": "未配置 SSH 密钥", + "Add a new SSH key to your account": "为您的账户添加新的 SSH 密钥", + "SSH key is required": "SSH 密钥是必填项", + "Link Partner Account": "关联合作伙伴账户", + "Enter the partner code provided by your partner": "输入您的合作伙伴提供的合作伙伴代码", + "Partner Code": "合作伙伴代码", + "For example: rGjw3hJ81b": "例如:rGjw3hJ81b", + "Submit": "提交", + "Remove Partner": "移除合作伙伴", + "This will remove the partner associated with your account. Are you sure you want to remove this partner?": "这将移除与您的账户关联的合作伙伴。确定要移除此合作伙伴吗?", + "SSH key added successfully": "SSH 密钥添加成功", + "Failed to add SSH key": "添加 SSH 密钥失败", + "SSH key updated successfully": "SSH 密钥更新成功", + "Failed to set SSH key as default": "设置默认 SSH 密钥失败", + "SSH key deleted successfully": "SSH 密钥删除成功", + "Failed to delete SSH key": "删除 SSH 密钥失败", + "Are you sure you want to delete this SSH key?": "确定要删除此 SSH 密钥吗?", + "Advanced Features": "高级功能", + "Enable private benches": "启用私有工作台", + "Enable security portal": "启用安全门户", + "Save Changes": "保存更改", + "Feature flags updated successfully": "功能标志更新成功", + "Failed to update feature flags": "更新功能标志失败", + "Marketplace Developer": "应用市场开发者", + "Developers can publish their apps on the marketplace for users to subscribe to, either paid or free.": "开发者可以在应用市场发布自己的应用,供用户付费或免费订阅。", + "Become a Developer": "成为开发者", + "Become a Marketplace Developer?": "成为应用市场开发者?", + "After confirmation, you will be able to publish apps to our marketplace.": "确认后,您将能够在我们的应用市场发布应用。", + "Enable Two-Factor Authentication": "启用双因素认证", + "Disable Two-Factor Authentication": "禁用双因素认证", + "Enable two-factor authentication for your account to add an extra layer of security": "为您的账户启用双因素认证以增加额外的安全层", + "Disable two-factor authentication for your account": "为您的账户禁用双因素认证", + "Enable": "启用", + "Disable": "禁用", + "Reset Password": "重置密码", + "Change your account login password": "更改您的账户登录密码", + "Disable Account": "禁用账户", + "Enable Account": "启用账户", + "Disable your account and stop billing": "禁用您的账户并停止计费", + "Enable your account and resume billing": "启用您的账户并恢复计费", + "After confirming this action:": "确认此操作后:", + "Your account will be disabled": "您的账户将被禁用", + "Your activated sites will be suspended immediately and deleted after one week.": "您已激活的站点将立即暂停,并在一周后删除。", + "Your account billing will stop": "您的账户计费将停止", + "You can log in later to re-enable your account. Do you want to continue?": "您可以稍后登录以重新启用您的账户。是否继续?", + "Your account will be enabled": "您的账户将被启用", + "Your suspended sites will be reactivated": "您已暂停的站点将被重新激活", + "Your account billing will resume": "您的账户计费将恢复", + "Do you want to continue?": "是否继续?", + "Referral Program": "推荐有礼", + "Your exclusive referral link": "您的专属推荐链接", + "Invite others to join Jingrow,": "邀请他人加入 Jingrow,", + "when they register and spend at least ¥100, you will get ¥20": "当他们注册并消费至少¥100时,您将获得¥20", + "Jingrow Partner": "Jingrow 合作伙伴", + "Jingrow partner associated with your account": "与您的账户关联的 Jingrow 合作伙伴", + "Add Partner Code": "添加合作伙伴代码", + "Unlink Partner": "取消关联合作伙伴", + "Have a Jingrow partner referral code? Click": "有 Jingrow 合作伙伴推荐代码吗?点击", + "to associate with your partner team.": "以与您的合作伙伴团队关联。", + "Feature coming soon": "功能即将推出", + "is an invalid referral code": "是无效的推荐代码", + "Current Password": "当前密码", + "New Password": "新密码", + "Please enter current password": "请输入当前密码", + "Please enter new password": "请输入新密码", + "Please re-enter new password": "请重新输入新密码", + "New password cannot be the same as current password": "新密码不能与当前密码相同", + "Password strength is good 👍": "密码强度良好 👍", + "Tip: Password should contain symbols, numbers and uppercase letters": "提示:密码应包含符号、数字和大写字母", + "Password updated successfully": "密码更新成功", + "Your password has been updated": "您的密码已更新", + "Current password is incorrect": "当前密码不正确", + "Setting you as a developer...": "正在将您设置为开发者...", + "You can now publish apps to our marketplace": "您现在可以在我们的应用市场发布应用了", + "Failed to mark you as a developer": "将您设置为开发者失败", + "Approval Request has already been sent to Partner": "批准请求已发送给合作伙伴", + "Approval Request has been sent to Partner": "批准请求已发送给合作伙伴", + "If you disable two-factor authentication, your account will become insecure": "如果您禁用双因素认证,您的账户将变得不安全", + "Steps to Disable Two-Factor Authentication": "禁用双因素认证的步骤", + "Open the authenticator app": "打开身份验证器应用", + "Enter the code from the app below": "在下方输入应用中的代码", + "Verify the code in the app to disable two-factor authentication": "输入应用中的验证码以禁用双因素认证", + "Steps to Enable Two-Factor Authentication": "启用双因素认证的步骤", + "Download an authenticator app on your phone, such as Alibaba Cloud APP, etc.": "在手机上下载身份验证器应用,例如阿里云 APP 等", + "Scan the QR code": "扫描二维码", + "Enter the code from the authenticator app": "输入身份验证器应用中的代码", + "Enter the code from the authenticator app below": "在下方输入身份验证器应用中的代码", + "Note": "注意", + "If you cannot access the authenticator app, your account will be locked. Please ensure you back up your vault/key.": "如果您无法访问身份验证器应用,您的账户将被锁定。请确保您已备份您的密钥。", + "Setup Key": "设置密钥", + "Verify the code in the app to enable two-factor authentication": "输入应用中的验证码以启用双因素认证", + "Please enter the code from the authenticator app": "请输入身份验证器应用中的代码", + "Two-factor authentication enabled successfully": "双因素认证已成功启用", + "Two-factor authentication disabled successfully": "双因素认证已成功禁用", + "Invalid TOTP code, please try again": "无效的 TOTP 代码,请重试", + "Failed to enable two-factor authentication": "启用双因素认证失败", + "Failed to disable two-factor authentication": "禁用双因素认证失败", + "Failed to load QR code": "加载二维码失败", + "Enabling two-factor authentication...": "正在启用双因素认证...", + "Disabling two-factor authentication...": "正在禁用双因素认证..." } diff --git a/src/shared/api/account.ts b/src/shared/api/account.ts index 64db653..d9ddc92 100644 --- a/src/shared/api/account.ts +++ b/src/shared/api/account.ts @@ -233,7 +233,7 @@ export const updateFeatureFlags = async (values: Record): Promi // 获取用户信息(包含 API Key) // 使用 jcloud.api.account.get API 获取账户信息 -export const getUserAccountInfo = async (): Promise<{ success: boolean; data?: any; message?: string }> => { +export const getUserAccountInfo = async (): Promise<{ success: boolean; data?: any; team?: any; message?: string }> => { try { const response = await axios.get( `/api/action/jcloud.api.account.get`, @@ -246,7 +246,7 @@ export const getUserAccountInfo = async (): Promise<{ success: boolean; data?: a const result = response.data?.message || response.data if (result?.user) { - return { success: true, data: result.user } + return { success: true, data: result.user, team: result.team } } return { success: false, message: 'API 返回的数据中未找到用户信息' } @@ -257,3 +257,298 @@ export const getUserAccountInfo = async (): Promise<{ success: boolean; data?: a } } } + +// 成为开发者(更新 Team 的 is_developer 字段) +// 使用 jcloud.api.client.set_value API,与 jcloud dashboard 保持一致 +export const becomeDeveloper = async (teamName: string): Promise<{ success: boolean; data?: any; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.client.set_value`, + { + pagetype: 'Team', + name: teamName, + fieldname: { is_developer: 1 } + }, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + // set_value API 返回更新后的 Team 对象 + const result = response.data?.message || response.data + return { success: true, data: result, message: '成为开发者成功' } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '成为开发者失败' + } + } +} + +// 禁用账户 +export const disableAccount = async (totpCode?: string): Promise<{ success: boolean; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.account.disable_account`, + totpCode ? { totp_code: totpCode } : {}, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + return { success: true, message: response.data?.message || '账户已禁用' } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '禁用账户失败' + } + } +} + +// 启用账户 +export const enableAccount = async (): Promise<{ success: boolean; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.account.enable_account`, + {}, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + return { success: true, message: response.data?.message || '账户已启用' } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '启用账户失败' + } + } +} + +// 更新密码 +export const updatePassword = async (params: { + old_password: string + new_password: string + confirm_password: string + logout_all_sessions?: number +}): Promise<{ success: boolean; message?: string; redirectUrl?: string }> => { + try { + const response = await axios.post( + `/api/action/jingrow.core.pagetype.user.user.update_password`, + { + old_password: params.old_password, + new_password: params.new_password, + confirm_password: params.confirm_password, + logout_all_sessions: params.logout_all_sessions || 1 + }, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + const result = response.data?.message || response.data + return { + success: true, + message: '密码更新成功', + redirectUrl: typeof result === 'string' ? result : undefined + } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '更新密码失败' + } + } +} + +// 测试密码强度 +export const testPasswordStrength = async (params: { + old_password: string + new_password: string +}): Promise<{ success: boolean; data?: { score: number; feedback: any }; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jingrow.core.pagetype.user.user.test_password_strength`, + { + old_password: params.old_password, + new_password: params.new_password + }, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + const result = response.data?.message || response.data + return { success: true, data: result } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '测试密码强度失败' + } + } +} + +// 验证合作伙伴代码 +export const validatePartnerCode = async (code: string): Promise<{ success: boolean; isValid: boolean; partnerName?: string; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.partner.validate_partner_code`, + { code }, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + const result = response.data?.message || response.data + if (Array.isArray(result) && result.length >= 2) { + return { + success: true, + isValid: result[0] === true, + partnerName: result[1] || undefined + } + } + return { success: true, isValid: false } + } catch (error: any) { + return { + success: false, + isValid: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '验证合作伙伴代码失败' + } + } +} + +// 添加合作伙伴代码 +export const addPartnerCode = async (referralCode: string): Promise<{ success: boolean; message?: string; isAlreadySent?: boolean }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.partner.add_partner`, + { referral_code: referralCode }, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + const result = response.data?.message || response.data + if (result === 'Request already sent') { + return { success: true, message: '请求已发送', isAlreadySent: true } + } + return { success: true, message: '请求已发送' } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '添加合作伙伴代码失败' + } + } +} + +// 移除合作伙伴 +export const removePartner = async (): Promise<{ success: boolean; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.partner.remove_partner`, + {}, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + return { success: true, message: response.data?.message || '合作伙伴已移除' } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '移除合作伙伴失败' + } + } +} + +// 获取合作伙伴名称 +export const getPartnerName = async (partnerEmail: string): Promise<{ success: boolean; data?: string; message?: string }> => { + try { + const response = await axios.get( + `/api/action/jcloud.api.partner.get_partner_name`, + { + params: { partner_email: partnerEmail }, + headers: get_session_api_headers(), + withCredentials: true + } + ) + + const result = response.data?.message || response.data + return { success: true, data: result } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '获取合作伙伴名称失败' + } + } +} + +// 获取 2FA QR 码 URL +export const get2FAQRCodeUrl = async (): Promise<{ success: boolean; data?: string; message?: string }> => { + try { + const response = await axios.get( + `/api/action/jcloud.api.account.get_2fa_qr_code_url`, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + const result = response.data?.message || response.data + return { success: true, data: result } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '获取 2FA QR 码失败' + } + } +} + +// 启用 2FA +export const enable2FA = async (totpCode: string): Promise<{ success: boolean; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.account.enable_2fa`, + { totp_code: totpCode }, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + return { success: true, message: response.data?.message || '双因素认证已启用' } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '启用双因素认证失败' + } + } +} + +// 禁用 2FA +export const disable2FA = async (totpCode: string): Promise<{ success: boolean; message?: string }> => { + try { + const response = await axios.post( + `/api/action/jcloud.api.account.disable_2fa`, + { totp_code: totpCode }, + { + headers: get_session_api_headers(), + withCredentials: true + } + ) + + return { success: true, message: response.data?.message || '双因素认证已禁用' } + } catch (error: any) { + return { + success: false, + message: error.response?.data?.detail || error.response?.data?.message || error.message || '禁用双因素认证失败' + } + } +} diff --git a/src/shared/components/ClickToCopyField.vue b/src/shared/components/ClickToCopyField.vue index 849f623..c747f21 100644 --- a/src/shared/components/ClickToCopyField.vue +++ b/src/shared/components/ClickToCopyField.vue @@ -29,6 +29,7 @@ import { ref } from 'vue' import { NInput, NButton, NIcon, useMessage } from 'naive-ui' import { Icon } from '@iconify/vue' +import { t } from '../i18n' interface Props { textContent: string @@ -50,12 +51,12 @@ const handleCopy = async () => { try { await navigator.clipboard.writeText(props.textContent) copied.value = true - message.success('已复制到剪贴板') + message.success(t('Copied to clipboard')) setTimeout(() => { copied.value = false }, 2000) } catch (error) { - message.error('复制失败') + message.error(t('Copy failed')) } finally { copying.value = false } diff --git a/src/views/settings/Settings.vue b/src/views/settings/Settings.vue index dede9cf..357315e 100644 --- a/src/views/settings/Settings.vue +++ b/src/views/settings/Settings.vue @@ -31,7 +31,7 @@ {{ userAccountInfo?.username || '-' }}
- {{ t('Phone') }}: + {{ t('Mobile') }}: {{ userAccountInfo?.mobile_no || '-' }}
@@ -51,25 +51,146 @@
- - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + +

+ {{ t('Invite others to join Jingrow,') }} + {{ t('when they register and spend at least ¥100, you will get ¥20') }} +

+
+
+ + + + +
+

+ {{ t('Have a Jingrow partner referral code? Click') }} + {{ t('Add Partner Code') }} + {{ t('to associate with your partner team.') }} +

+
+
+

{{ partnerBillingName }}

+

{{ teamInfo?.partner_email }}

+
+
+
@@ -81,8 +202,7 @@
- {{ t('API key and API secret can be used to access') }} - {{ t('Jingrow API') }} + {{ t('API key and API secret pairs can be used to access the Jingrow API.') }}
@@ -342,16 +462,10 @@ - - - - - - - + @@ -369,34 +483,12 @@ - - - - - - - - - -

- {{ t('API key and API secret pairs can be used to access the') }} - {{ t('Jingrow API') }}. + {{ t('API key and API secret pairs can be used to access the Jingrow API.') }}

@@ -453,11 +545,286 @@ + + + + + + +
+ + +

{{ t('Steps to Disable Two-Factor Authentication') }}

+
    +
  1. {{ t('Open the authenticator app') }}
  2. +
  3. {{ t('Enter the code from the app below') }}
  4. +
+
+ + + +
+ + +
+ +
+ QR Code +
+ + + +

{{ t('Steps to Enable Two-Factor Authentication') }}

+
    +
  1. {{ t('Download an authenticator app on your phone, such as Alibaba Cloud APP, etc.') }}
  2. +
  3. {{ t('Scan the QR code') }}
  4. +
  5. {{ t('Enter the code from the authenticator app below') }}
  6. +
+ + + {{ t('If you cannot access the authenticator app, your account will be locked. Please ensure you back up your vault/key.') }} + +
+ + + +

{{ t('Setup Key') }}

+

+ {{ setupKey }} +

+
+ + + + + +
+ + + + + + + {{ t('Enable Two-Factor Authentication') }} + + + {{ t('Disable Two-Factor Authentication') }} + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +

{{ t('Enter the partner code provided by your partner') }}

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

+ {{ t('This will remove the partner associated with your account. Are you sure you want to remove this partner?') }} +

+
+ + + + + + + + + +