feat(代理): 代理配置按登录用户隔离——桌面端 current_uid 登录同步 + brand 页 proxy_users[uid] 读写,各用户各自代理池各自计费复用(未登录回退全局 proxy_url 兼容旧配置)

This commit is contained in:
2026-09-11 16:09:56 +08:00
parent bb52574767
commit 30d31fb318
3 changed files with 61 additions and 9 deletions
@@ -163,7 +163,7 @@ import {
type ApiSecretModuleKey,
type ApiSecretRetention,
} from '@/shared/utils/api-secret-store'
import { getPywebviewApi, type ProxyMode } from '@/shared/bridges/pywebview'
import { getPywebviewApi, type ProxyMode, type DesktopConfigUpdate } from '@/shared/bridges/pywebview'
type SecretState = {
value: string
@@ -275,6 +275,39 @@ function clearSecret(moduleKey: ApiSecretModuleKey) {
ElMessage.success('已清空密钥')
}
function proxyUserId() {
if (typeof window === 'undefined') return '0'
return window.localStorage.getItem('uid') || '0'
}
/** 代理地址按登录用户隔离:proxy_users[uid],各自计费各自复用;
* 未登录(uid=0)回退全局 proxy_url(兼容旧版本已保存的配置)。 */
function readUserProxy(config: Record<string, unknown> | null | undefined) {
const uid = proxyUserId()
if (uid !== '0') {
const users = (config?.proxy_users ?? {}) as Record<string, unknown>
const own = (users[uid] ?? {}) as Record<string, unknown>
return {
url: typeof own.proxy_url === 'string' ? own.proxy_url : '',
mode: Number(own.proxy_mode) === 2 ? 2 : 1,
}
}
return {
url: typeof config?.proxy_url === 'string' ? config.proxy_url : '',
mode: Number(config?.proxy_mode) === 2 ? 2 : 1,
}
}
function userProxyPatch(nextProxyUrl: string, nextProxyMode: ProxyMode) {
const uid = proxyUserId()
if (uid === '0') {
return { proxy_url: nextProxyUrl, proxy_mode: nextProxyMode }
}
const users: Record<string, unknown> = {}
users[uid] = { proxy_url: nextProxyUrl, proxy_mode: nextProxyMode }
return { proxy_users: users }
}
async function loadProxyConfig() {
const requestId = ++proxyLoadRequestId
proxyLoading.value = true
@@ -294,10 +327,9 @@ async function loadProxyConfig() {
try {
const config = await api.read_config()
if (requestId !== proxyLoadRequestId || !dialogVisible.value) return
const nextUrl = typeof config?.proxy_url === 'string' ? config.proxy_url : ''
const nextMode: ProxyMode = Number(config?.proxy_mode) === 2 ? 2 : 1
proxyUrl.value = nextUrl
proxyMode.value = nextMode
const own = readUserProxy(config as Record<string, unknown>)
proxyUrl.value = own.url
proxyMode.value = (own.mode === 2 ? 2 : 1) as ProxyMode
proxyReady.value = true
} catch (error) {
if (requestId !== proxyLoadRequestId || !dialogVisible.value) return
@@ -331,10 +363,8 @@ async function saveAll() {
if (shouldSaveProxy) {
const api = getPywebviewApi()
if (!api?.save_config) throw new Error('当前客户端未提供代理配置保存能力')
await api.save_config({
proxy_url: nextProxyUrl,
proxy_mode: nextProxyMode,
})
// 按登录用户保存:proxy_users[uid](未登录回退全局 proxy_url 字段)
await api.save_config(userProxyPatch(nextProxyUrl, nextProxyMode) as DesktopConfigUpdate)
proxyUrl.value = nextProxyUrl
proxyDirty.value = false
}
@@ -333,6 +333,17 @@ async function submitLogin() {
} catch {
/* 忽略 */
}
// 同步登录用户到桌面客户端配置:Python 端按 uid 选用该用户自己的
// 代理密钥/代理池(各自计费、各自复用省钱)。网页形态无 pywebview
// 桥,静默跳过。
try {
const bridge = (window as unknown as { pywebview?: { api?: { save_config?: (data: Record<string, unknown>) => Promise<unknown> } } }).pywebview
if (bridge?.api && typeof bridge.api.save_config === 'function') {
void bridge.api.save_config({ current_uid: String(data.userId) })
}
} catch {
/* 忽略 */
}
}
if (data.username) {
try {
@@ -365,6 +376,15 @@ onMounted(() => {
} catch {
/* 忽略 */
}
// 同步清掉客户端的登录用户标记:Python 端回退到全局/默认代理池
try {
const bridge = (window as unknown as { pywebview?: { api?: { save_config?: (data: Record<string, unknown>) => Promise<unknown> } } }).pywebview
if (bridge?.api && typeof bridge.api.save_config === 'function') {
void bridge.api.save_config({ current_uid: '' })
}
} catch {
/* 忽略 */
}
// 显式登出/切号后不再自动登录(保留记住的账号,仅关闭自动登录)
autoLogin.value = false
lsSet(AUTO_LOGIN_KEY, '0')
@@ -56,6 +56,8 @@ export interface DesktopConfig {
export interface DesktopConfigUpdate {
proxy_url?: string;
proxy_mode?: ProxyMode;
current_uid?: string;
proxy_users?: Record<string, { proxy_url?: string; proxy_mode?: number }>;
aliprice_usename?: string;
aliprice_pwd?: string;
}