Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
0772d925
Commit
0772d925
authored
Mar 17, 2026
by
Wang Lvyuan
Browse files
fix(admin/accounts): reset edit modal state on reopen
parent
045cba78
Changes
2
Hide whitespace changes
Inline
Side-by-side
frontend/src/components/account/EditAccountModal.vue
View file @
0772d925
...
...
@@ -1980,271 +1980,281 @@ const normalizePoolModeRetryCount = (value: number) => {
return
normalized
}
watch
(
()
=>
props
.
a
ccount
,
(
newAccount
)
=>
{
if
(
newAccount
)
{
antigravityMixedChannelConfirmed
.
value
=
false
showMixedChannelWarning
.
value
=
false
mixedChannelWarningDetails
.
value
=
null
mixedChannelWarningRawMessage
.
value
=
''
mixedChannelWarningAction
.
value
=
null
form
.
name
=
newAccount
.
name
form
.
notes
=
newAccount
.
notes
||
''
form
.
proxy_id
=
newAccount
.
proxy_id
form
.
concurrency
=
newAccount
.
concurrency
form
.
load_factor
=
newAccount
.
load_factor
??
null
form
.
priority
=
newAccount
.
priority
form
.
rate_multiplier
=
newAccount
.
rate_multiplier
??
1
form
.
status
=
(
newAccount
.
status
===
'
active
'
||
newAccount
.
status
===
'
inactive
'
||
newAccount
.
status
===
'
error
'
)
?
newAccount
.
status
:
'
active
'
form
.
group_ids
=
newAccount
.
group_ids
||
[]
form
.
expires_at
=
newAccount
.
expires_at
??
null
// Load intercept warmup requests setting (applies to all account types)
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
|
undefined
interceptWarmupRequests
.
value
=
credentials
?.
intercept_warmup_requests
===
true
autoPauseOnExpired
.
value
=
newAccount
.
auto_pause_on_expired
===
true
// Load mixed scheduling setting (only for antigravity accounts)
mixedScheduling
.
value
=
false
allowOverages
.
value
=
false
const
extra
=
newAccount
.
extra
as
Record
<
string
,
unknown
>
|
undefined
mixedScheduling
.
value
=
extra
?.
mixed_scheduling
===
true
allowOverages
.
value
=
extra
?.
allow_overages
===
true
// Load OpenAI passthrough toggle (OpenAI OAuth/API Key)
openaiPassthroughEnabled
.
value
=
false
openaiOAuthResponsesWebSocketV2Mode
.
value
=
OPENAI_WS_MODE_OFF
openaiAPIKeyResponsesWebSocketV2Mode
.
value
=
OPENAI_WS_MODE_OFF
codexCLIOnlyEnabled
.
value
=
false
anthropicPassthroughEnabled
.
value
=
false
if
(
newAccount
.
platform
===
'
openai
'
&&
(
newAccount
.
type
===
'
oauth
'
||
newAccount
.
type
===
'
apikey
'
))
{
openaiPassthroughEnabled
.
value
=
extra
?.
openai_passthrough
===
true
||
extra
?.
openai_oauth_passthrough
===
true
openaiOAuthResponsesWebSocketV2Mode
.
value
=
resolveOpenAIWSModeFromExtra
(
extra
,
{
modeKey
:
'
openai_oauth_responses_websockets_v2_mode
'
,
enabledKey
:
'
openai_oauth_responses_websockets_v2_enabled
'
,
fallbackEnabledKeys
:
[
'
responses_websockets_v2_enabled
'
,
'
openai_ws_enabled
'
],
defaultMode
:
OPENAI_WS_MODE_OFF
}
)
openaiAPIKeyResponsesWebSocketV2Mode
.
value
=
resolveOpenAIWSModeFromExtra
(
extra
,
{
modeKey
:
'
openai_apikey_responses_websockets_v2_mode
'
,
enabledKey
:
'
openai_apikey_responses_websockets_v2_enabled
'
,
fallbackEnabledKeys
:
[
'
responses_websockets_v2_enabled
'
,
'
openai_ws_enabled
'
],
defaultMode
:
OPENAI_WS_MODE_OFF
}
)
if
(
newAccount
.
type
===
'
oauth
'
)
{
codexCLIOnlyEnabled
.
value
=
extra
?.
codex_cli_only
===
true
}
}
if
(
newAccount
.
platform
===
'
anthropic
'
&&
newAccount
.
type
===
'
apikey
'
)
{
anthropicPassthroughEnabled
.
value
=
extra
?.
anthropic_passthrough
===
true
}
const
syncFormFromAccount
=
(
newAccount
:
Account
|
null
)
=>
{
if
(
!
newA
ccount
)
{
return
}
antigravityMixedChannelConfirmed
.
value
=
false
showMixedChannelWarning
.
value
=
false
mixedChannelWarningDetails
.
value
=
null
mixedChannelWarningRawMessage
.
value
=
''
mixedChannelWarningAction
.
value
=
null
form
.
name
=
newAccount
.
name
form
.
notes
=
newAccount
.
notes
||
''
form
.
proxy_id
=
newAccount
.
proxy_id
form
.
concurrency
=
newAccount
.
concurrency
form
.
load_factor
=
newAccount
.
load_factor
??
null
form
.
priority
=
newAccount
.
priority
form
.
rate_multiplier
=
newAccount
.
rate_multiplier
??
1
form
.
status
=
(
newAccount
.
status
===
'
active
'
||
newAccount
.
status
===
'
inactive
'
||
newAccount
.
status
===
'
error
'
)
?
newAccount
.
status
:
'
active
'
form
.
group_ids
=
newAccount
.
group_ids
||
[]
form
.
expires_at
=
newAccount
.
expires_at
??
null
// Load intercept warmup requests setting (applies to all account types)
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
|
undefined
interceptWarmupRequests
.
value
=
credentials
?.
intercept_warmup_requests
===
true
autoPauseOnExpired
.
value
=
newAccount
.
auto_pause_on_expired
===
true
// Load mixed scheduling setting (only for antigravity accounts)
mixedScheduling
.
value
=
false
allowOverages
.
value
=
false
const
extra
=
newAccount
.
extra
as
Record
<
string
,
unknown
>
|
undefined
mixedScheduling
.
value
=
extra
?.
mixed_scheduling
===
true
allowOverages
.
value
=
extra
?.
allow_overages
===
true
// Load OpenAI passthrough toggle (OpenAI OAuth/API Key)
openaiPassthroughEnabled
.
value
=
false
openaiOAuthResponsesWebSocketV2Mode
.
value
=
OPENAI_WS_MODE_OFF
openaiAPIKeyResponsesWebSocketV2Mode
.
value
=
OPENAI_WS_MODE_OFF
codexCLIOnlyEnabled
.
value
=
false
anthropicPassthroughEnabled
.
value
=
false
if
(
newAccount
.
platform
===
'
openai
'
&&
(
newAccount
.
type
===
'
oauth
'
||
newAccount
.
type
===
'
apikey
'
))
{
openaiPassthroughEnabled
.
value
=
extra
?.
openai_passthrough
===
true
||
extra
?.
openai_oauth_passthrough
===
true
openaiOAuthResponsesWebSocketV2Mode
.
value
=
resolveOpenAIWSModeFromExtra
(
extra
,
{
modeKey
:
'
openai_oauth_responses_websockets_v2_mode
'
,
enabledKey
:
'
openai_oauth_responses_websockets_v2_enabled
'
,
fallbackEnabledKeys
:
[
'
responses_websockets_v2_enabled
'
,
'
openai_ws_enabled
'
],
defaultMode
:
OPENAI_WS_MODE_OFF
}
)
openaiAPIKeyResponsesWebSocketV2Mode
.
value
=
resolveOpenAIWSModeFromExtra
(
extra
,
{
modeKey
:
'
openai_apikey_responses_websockets_v2_mode
'
,
enabledKey
:
'
openai_apikey_responses_websockets_v2_enabled
'
,
fallbackEnabledKeys
:
[
'
responses_websockets_v2_enabled
'
,
'
openai_ws_enabled
'
],
defaultMode
:
OPENAI_WS_MODE_OFF
}
)
if
(
newAccount
.
type
===
'
oauth
'
)
{
codexCLIOnlyEnabled
.
value
=
extra
?.
codex_cli_only
===
true
}
}
if
(
newAccount
.
platform
===
'
anthropic
'
&&
newAccount
.
type
===
'
apikey
'
)
{
anthropicPassthroughEnabled
.
value
=
extra
?.
anthropic_passthrough
===
true
}
// Load quota limit for apikey/bedrock accounts (bedrock quota is also loaded in its own branch above)
if
(
newAccount
.
type
===
'
apikey
'
||
newAccount
.
type
===
'
bedrock
'
)
{
const
quotaVal
=
extra
?.
quota_limit
as
number
|
undefined
editQuotaLimit
.
value
=
(
quotaVal
&&
quotaVal
>
0
)
?
quotaVal
:
null
const
dailyVal
=
extra
?.
quota_daily_limit
as
number
|
undefined
editQuotaDailyLimit
.
value
=
(
dailyVal
&&
dailyVal
>
0
)
?
dailyVal
:
null
const
weeklyVal
=
extra
?.
quota_weekly_limit
as
number
|
undefined
editQuotaWeeklyLimit
.
value
=
(
weeklyVal
&&
weeklyVal
>
0
)
?
weeklyVal
:
null
// Load quota reset mode config
editDailyResetMode
.
value
=
(
extra
?.
quota_daily_reset_mode
as
'
rolling
'
|
'
fixed
'
)
||
null
editDailyResetHour
.
value
=
(
extra
?.
quota_daily_reset_hour
as
number
)
??
null
editWeeklyResetMode
.
value
=
(
extra
?.
quota_weekly_reset_mode
as
'
rolling
'
|
'
fixed
'
)
||
null
editWeeklyResetDay
.
value
=
(
extra
?.
quota_weekly_reset_day
as
number
)
??
null
editWeeklyResetHour
.
value
=
(
extra
?.
quota_weekly_reset_hour
as
number
)
??
null
editResetTimezone
.
value
=
(
extra
?.
quota_reset_timezone
as
string
)
||
null
}
else
{
editQuotaLimit
.
value
=
null
editQuotaDailyLimit
.
value
=
null
editQuotaWeeklyLimit
.
value
=
null
editDailyResetMode
.
value
=
null
editDailyResetHour
.
value
=
null
editWeeklyResetMode
.
value
=
null
editWeeklyResetDay
.
value
=
null
editWeeklyResetHour
.
value
=
null
editResetTimezone
.
value
=
null
}
// Load quota limit for apikey/bedrock accounts (bedrock quota is also loaded in its own branch above)
if
(
newAccount
.
type
===
'
apikey
'
||
newAccount
.
type
===
'
bedrock
'
)
{
const
quotaVal
=
extra
?.
quota_limit
as
number
|
undefined
editQuotaLimit
.
value
=
(
quotaVal
&&
quotaVal
>
0
)
?
quotaVal
:
null
const
dailyVal
=
extra
?.
quota_daily_limit
as
number
|
undefined
editQuotaDailyLimit
.
value
=
(
dailyVal
&&
dailyVal
>
0
)
?
dailyVal
:
null
const
weeklyVal
=
extra
?.
quota_weekly_limit
as
number
|
undefined
editQuotaWeeklyLimit
.
value
=
(
weeklyVal
&&
weeklyVal
>
0
)
?
weeklyVal
:
null
// Load quota reset mode config
editDailyResetMode
.
value
=
(
extra
?.
quota_daily_reset_mode
as
'
rolling
'
|
'
fixed
'
)
||
null
editDailyResetHour
.
value
=
(
extra
?.
quota_daily_reset_hour
as
number
)
??
null
editWeeklyResetMode
.
value
=
(
extra
?.
quota_weekly_reset_mode
as
'
rolling
'
|
'
fixed
'
)
||
null
editWeeklyResetDay
.
value
=
(
extra
?.
quota_weekly_reset_day
as
number
)
??
null
editWeeklyResetHour
.
value
=
(
extra
?.
quota_weekly_reset_hour
as
number
)
??
null
editResetTimezone
.
value
=
(
extra
?.
quota_reset_timezone
as
string
)
||
null
}
else
{
editQuotaLimit
.
value
=
null
editQuotaDailyLimit
.
value
=
null
editQuotaWeeklyLimit
.
value
=
null
editDailyResetMode
.
value
=
null
editDailyResetHour
.
value
=
null
editWeeklyResetMode
.
value
=
null
editWeeklyResetDay
.
value
=
null
editWeeklyResetHour
.
value
=
null
editResetTimezone
.
value
=
null
}
// Load antigravity model mapping (Antigravity 只支持映射模式)
if
(
newAccount
.
platform
===
'
antigravity
'
)
{
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
|
undefined
// Load antigravity model mapping (Antigravity 只支持映射模式)
if
(
newAccount
.
platform
===
'
antigravity
'
)
{
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
|
undefined
// Antigravity 始终使用映射模式
antigravityModelRestrictionMode
.
value
=
'
mapping
'
antigravityWhitelistModels
.
value
=
[]
// Antigravity 始终使用映射模式
antigravityModelRestrictionMode
.
value
=
'
mapping
'
antigravityWhitelistModels
.
value
=
[]
// 从 model_mapping 读取映射配置
const
rawAgMapping
=
credentials
?.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
rawAgMapping
&&
typeof
rawAgMapping
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
rawAgMapping
)
// 无论是白名单样式(key===value)还是真正的映射,都统一转换为映射列表
antigravityModelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
}
else
{
// 兼容旧数据:从 model_whitelist 读取,转换为映射格式
const
rawWhitelist
=
credentials
?.
model_whitelist
if
(
Array
.
isArray
(
rawWhitelist
)
&&
rawWhitelist
.
length
>
0
)
{
antigravityModelMappings
.
value
=
rawWhitelist
.
map
((
v
)
=>
String
(
v
).
trim
())
.
filter
((
v
)
=>
v
.
length
>
0
)
.
map
((
m
)
=>
({
from
:
m
,
to
:
m
}
))
}
else
{
antigravityModelMappings
.
value
=
[]
}
}
// 从 model_mapping 读取映射配置
const
rawAgMapping
=
credentials
?.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
rawAgMapping
&&
typeof
rawAgMapping
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
rawAgMapping
)
// 无论是白名单样式(key===value)还是真正的映射,都统一转换为映射列表
antigravityModelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
}
else
{
// 兼容旧数据:从 model_whitelist 读取,转换为映射格式
const
rawWhitelist
=
credentials
?.
model_whitelist
if
(
Array
.
isArray
(
rawWhitelist
)
&&
rawWhitelist
.
length
>
0
)
{
antigravityModelMappings
.
value
=
rawWhitelist
.
map
((
v
)
=>
String
(
v
).
trim
())
.
filter
((
v
)
=>
v
.
length
>
0
)
.
map
((
m
)
=>
({
from
:
m
,
to
:
m
}
))
}
else
{
antigravityModelRestrictionMode
.
value
=
'
mapping
'
antigravityWhitelistModels
.
value
=
[]
antigravityModelMappings
.
value
=
[]
}
}
}
else
{
antigravityModelRestrictionMode
.
value
=
'
mapping
'
antigravityWhitelistModels
.
value
=
[]
antigravityModelMappings
.
value
=
[]
}
// Load quota control settings (Anthropic OAuth/SetupToken only)
loadQuotaControlSettings
(
newAccount
)
loadTempUnschedRules
(
credentials
)
// Initialize API Key fields for apikey type
if
(
newAccount
.
type
===
'
apikey
'
&&
newAccount
.
credentials
)
{
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
const
platformDefaultUrl
=
newAccount
.
platform
===
'
openai
'
||
newAccount
.
platform
===
'
sora
'
?
'
https://api.openai.com
'
:
newAccount
.
platform
===
'
gemini
'
?
'
https://generativelanguage.googleapis.com
'
:
'
https://api.anthropic.com
'
editBaseUrl
.
value
=
(
credentials
.
base_url
as
string
)
||
platformDefaultUrl
// Load model mappings and detect mode
const
existingMappings
=
credentials
.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
existingMappings
&&
typeof
existingMappings
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
existingMappings
)
// Detect if this is whitelist mode (all from === to) or mapping mode
const
isWhitelistMode
=
entries
.
length
>
0
&&
entries
.
every
(([
from
,
to
])
=>
from
===
to
)
if
(
isWhitelistMode
)
{
// Whitelist mode: populate allowedModels
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
entries
.
map
(([
from
])
=>
from
)
modelMappings
.
value
=
[]
}
else
{
// Mapping mode: populate modelMappings
modelRestrictionMode
.
value
=
'
mapping
'
modelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
allowedModels
.
value
=
[]
}
}
else
{
// No mappings: default to whitelist mode with empty selection (allow all)
modelRestrictionMode
.
value
=
'
whitelist
'
modelMappings
.
value
=
[]
allowedModels
.
value
=
[]
}
// Load quota control settings (Anthropic OAuth/SetupToken only)
loadQuotaControlSettings
(
newAccount
)
loadTempUnschedRules
(
credentials
)
// Initialize API Key fields for apikey type
if
(
newAccount
.
type
===
'
apikey
'
&&
newAccount
.
credentials
)
{
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
const
platformDefaultUrl
=
newAccount
.
platform
===
'
openai
'
||
newAccount
.
platform
===
'
sora
'
?
'
https://api.openai.com
'
:
newAccount
.
platform
===
'
gemini
'
?
'
https://generativelanguage.googleapis.com
'
:
'
https://api.anthropic.com
'
editBaseUrl
.
value
=
(
credentials
.
base_url
as
string
)
||
platformDefaultUrl
// Load model mappings and detect mode
const
existingMappings
=
credentials
.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
existingMappings
&&
typeof
existingMappings
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
existingMappings
)
// Detect if this is whitelist mode (all from === to) or mapping mode
const
isWhitelistMode
=
entries
.
length
>
0
&&
entries
.
every
(([
from
,
to
])
=>
from
===
to
)
if
(
isWhitelistMode
)
{
// Whitelist mode: populate allowedModels
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
entries
.
map
(([
from
])
=>
from
)
modelMappings
.
value
=
[]
}
else
{
// Mapping mode: populate modelMappings
modelRestrictionMode
.
value
=
'
mapping
'
modelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
allowedModels
.
value
=
[]
}
}
else
{
// No mappings: default to whitelist mode with empty selection (allow all)
modelRestrictionMode
.
value
=
'
whitelist
'
modelMappings
.
value
=
[]
allowedModels
.
value
=
[]
}
// Load pool mode
poolModeEnabled
.
value
=
credentials
.
pool_mode
===
true
poolModeRetryCount
.
value
=
normalizePoolModeRetryCount
(
Number
(
credentials
.
pool_mode_retry_count
??
DEFAULT_POOL_MODE_RETRY_COUNT
)
)
// Load custom error codes
customErrorCodesEnabled
.
value
=
credentials
.
custom_error_codes_enabled
===
true
const
existingErrorCodes
=
credentials
.
custom_error_codes
as
number
[]
|
undefined
if
(
existingErrorCodes
&&
Array
.
isArray
(
existingErrorCodes
))
{
selectedErrorCodes
.
value
=
[...
existingErrorCodes
]
}
else
{
selectedErrorCodes
.
value
=
[]
}
}
else
if
(
newAccount
.
type
===
'
bedrock
'
&&
newAccount
.
credentials
)
{
const
bedrockCreds
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
const
authMode
=
(
bedrockCreds
.
auth_mode
as
string
)
||
'
sigv4
'
editBedrockRegion
.
value
=
(
bedrockCreds
.
aws_region
as
string
)
||
''
editBedrockForceGlobal
.
value
=
(
bedrockCreds
.
aws_force_global
as
string
)
===
'
true
'
if
(
authMode
===
'
apikey
'
)
{
editBedrockApiKeyValue
.
value
=
''
}
else
{
editBedrockAccessKeyId
.
value
=
(
bedrockCreds
.
aws_access_key_id
as
string
)
||
''
editBedrockSecretAccessKey
.
value
=
''
editBedrockSessionToken
.
value
=
''
}
// Load pool mode
poolModeEnabled
.
value
=
credentials
.
pool_mode
===
true
poolModeRetryCount
.
value
=
normalizePoolModeRetryCount
(
Number
(
credentials
.
pool_mode_retry_count
??
DEFAULT_POOL_MODE_RETRY_COUNT
)
)
// Load custom error codes
customErrorCodesEnabled
.
value
=
credentials
.
custom_error_codes_enabled
===
true
const
existingErrorCodes
=
credentials
.
custom_error_codes
as
number
[]
|
undefined
if
(
existingErrorCodes
&&
Array
.
isArray
(
existingErrorCodes
))
{
selectedErrorCodes
.
value
=
[...
existingErrorCodes
]
}
else
{
selectedErrorCodes
.
value
=
[]
}
}
else
if
(
newAccount
.
type
===
'
bedrock
'
&&
newAccount
.
credentials
)
{
const
bedrockCreds
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
const
authMode
=
(
bedrockCreds
.
auth_mode
as
string
)
||
'
sigv4
'
editBedrockRegion
.
value
=
(
bedrockCreds
.
aws_region
as
string
)
||
''
editBedrockForceGlobal
.
value
=
(
bedrockCreds
.
aws_force_global
as
string
)
===
'
true
'
if
(
authMode
===
'
apikey
'
)
{
editBedrockApiKeyValue
.
value
=
''
}
else
{
editBedrockAccessKeyId
.
value
=
(
bedrockCreds
.
aws_access_key_id
as
string
)
||
''
editBedrockSecretAccessKey
.
value
=
''
editBedrockSessionToken
.
value
=
''
}
// Load pool mode for bedrock
poolModeEnabled
.
value
=
bedrockCreds
.
pool_mode
===
true
const
retryCount
=
bedrockCreds
.
pool_mode_retry_count
poolModeRetryCount
.
value
=
(
typeof
retryCount
===
'
number
'
&&
retryCount
>=
0
)
?
retryCount
:
DEFAULT_POOL_MODE_RETRY_COUNT
// Load quota limits for bedrock
const
bedrockExtra
=
(
newAccount
.
extra
as
Record
<
string
,
unknown
>
)
||
{
}
editQuotaLimit
.
value
=
typeof
bedrockExtra
.
quota_limit
===
'
number
'
?
bedrockExtra
.
quota_limit
:
null
editQuotaDailyLimit
.
value
=
typeof
bedrockExtra
.
quota_daily_limit
===
'
number
'
?
bedrockExtra
.
quota_daily_limit
:
null
editQuotaWeeklyLimit
.
value
=
typeof
bedrockExtra
.
quota_weekly_limit
===
'
number
'
?
bedrockExtra
.
quota_weekly_limit
:
null
// Load model mappings for bedrock
const
existingMappings
=
bedrockCreds
.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
existingMappings
&&
typeof
existingMappings
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
existingMappings
)
const
isWhitelistMode
=
entries
.
length
>
0
&&
entries
.
every
(([
from
,
to
])
=>
from
===
to
)
if
(
isWhitelistMode
)
{
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
entries
.
map
(([
from
])
=>
from
)
modelMappings
.
value
=
[]
}
else
{
modelRestrictionMode
.
value
=
'
mapping
'
modelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
allowedModels
.
value
=
[]
}
}
else
{
modelRestrictionMode
.
value
=
'
whitelist
'
modelMappings
.
value
=
[]
allowedModels
.
value
=
[]
}
}
else
if
(
newAccount
.
type
===
'
upstream
'
&&
newAccount
.
credentials
)
{
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
editBaseUrl
.
value
=
(
credentials
.
base_url
as
string
)
||
''
// Load pool mode for bedrock
poolModeEnabled
.
value
=
bedrockCreds
.
pool_mode
===
true
const
retryCount
=
bedrockCreds
.
pool_mode_retry_count
poolModeRetryCount
.
value
=
(
typeof
retryCount
===
'
number
'
&&
retryCount
>=
0
)
?
retryCount
:
DEFAULT_POOL_MODE_RETRY_COUNT
// Load quota limits for bedrock
const
bedrockExtra
=
(
newAccount
.
extra
as
Record
<
string
,
unknown
>
)
||
{
}
editQuotaLimit
.
value
=
typeof
bedrockExtra
.
quota_limit
===
'
number
'
?
bedrockExtra
.
quota_limit
:
null
editQuotaDailyLimit
.
value
=
typeof
bedrockExtra
.
quota_daily_limit
===
'
number
'
?
bedrockExtra
.
quota_daily_limit
:
null
editQuotaWeeklyLimit
.
value
=
typeof
bedrockExtra
.
quota_weekly_limit
===
'
number
'
?
bedrockExtra
.
quota_weekly_limit
:
null
// Load model mappings for bedrock
const
existingMappings
=
bedrockCreds
.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
existingMappings
&&
typeof
existingMappings
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
existingMappings
)
const
isWhitelistMode
=
entries
.
length
>
0
&&
entries
.
every
(([
from
,
to
])
=>
from
===
to
)
if
(
isWhitelistMode
)
{
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
entries
.
map
(([
from
])
=>
from
)
modelMappings
.
value
=
[]
}
else
{
const
platformDefaultUrl
=
newAccount
.
platform
===
'
openai
'
||
newAccount
.
platform
===
'
sora
'
?
'
https://api.openai.com
'
:
newAccount
.
platform
===
'
gemini
'
?
'
https://generativelanguage.googleapis.com
'
:
'
https://api.anthropic.com
'
editBaseUrl
.
value
=
platformDefaultUrl
// Load model mappings for OpenAI OAuth accounts
if
(
newAccount
.
platform
===
'
openai
'
&&
newAccount
.
credentials
)
{
const
oauthCredentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
const
existingMappings
=
oauthCredentials
.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
existingMappings
&&
typeof
existingMappings
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
existingMappings
)
const
isWhitelistMode
=
entries
.
length
>
0
&&
entries
.
every
(([
from
,
to
])
=>
from
===
to
)
if
(
isWhitelistMode
)
{
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
entries
.
map
(([
from
])
=>
from
)
modelMappings
.
value
=
[]
}
else
{
modelRestrictionMode
.
value
=
'
mapping
'
modelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
allowedModels
.
value
=
[]
}
}
else
{
modelRestrictionMode
.
value
=
'
whitelist
'
modelMappings
.
value
=
[]
allowedModels
.
value
=
[]
}
}
else
{
modelRestrictionMode
.
value
=
'
mapping
'
modelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
allowedModels
.
value
=
[]
}
}
else
{
modelRestrictionMode
.
value
=
'
whitelist
'
modelMappings
.
value
=
[]
allowedModels
.
value
=
[]
}
}
else
if
(
newAccount
.
type
===
'
upstream
'
&&
newAccount
.
credentials
)
{
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
editBaseUrl
.
value
=
(
credentials
.
base_url
as
string
)
||
''
}
else
{
const
platformDefaultUrl
=
newAccount
.
platform
===
'
openai
'
||
newAccount
.
platform
===
'
sora
'
?
'
https://api.openai.com
'
:
newAccount
.
platform
===
'
gemini
'
?
'
https://generativelanguage.googleapis.com
'
:
'
https://api.anthropic.com
'
editBaseUrl
.
value
=
platformDefaultUrl
// Load model mappings for OpenAI OAuth accounts
if
(
newAccount
.
platform
===
'
openai
'
&&
newAccount
.
credentials
)
{
const
oauthCredentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
const
existingMappings
=
oauthCredentials
.
model_mapping
as
Record
<
string
,
string
>
|
undefined
if
(
existingMappings
&&
typeof
existingMappings
===
'
object
'
)
{
const
entries
=
Object
.
entries
(
existingMappings
)
const
isWhitelistMode
=
entries
.
length
>
0
&&
entries
.
every
(([
from
,
to
])
=>
from
===
to
)
if
(
isWhitelistMode
)
{
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
entries
.
map
(([
from
])
=>
from
)
modelMappings
.
value
=
[]
}
else
{
modelRestrictionMode
.
value
=
'
mapping
'
modelMappings
.
value
=
entries
.
map
(([
from
,
to
])
=>
({
from
,
to
}
))
allowedModels
.
value
=
[]
}
poolModeEnabled
.
value
=
false
poolM
odeRetr
yCount
.
value
=
DEFAULT_POOL_MODE_RETRY_COUNT
customErrorCodesEnabled
.
value
=
false
selectedErrorC
odes
.
value
=
[]
}
else
{
m
ode
l
Re
s
tr
ictionMode
.
value
=
'
whitelist
'
modelMappings
.
value
=
[]
allowedM
ode
l
s
.
value
=
[]
}
editApiKey
.
value
=
''
}
else
{
modelRestrictionMode
.
value
=
'
whitelist
'
modelMappings
.
value
=
[]
allowedModels
.
value
=
[]
}
poolModeEnabled
.
value
=
false
poolModeRetryCount
.
value
=
DEFAULT_POOL_MODE_RETRY_COUNT
customErrorCodesEnabled
.
value
=
false
selectedErrorCodes
.
value
=
[]
}
editApiKey
.
value
=
''
}
watch
(
[()
=>
props
.
show
,
()
=>
props
.
account
],
([
show
,
newAccount
],
[
wasShow
,
previousAccount
])
=>
{
if
(
!
show
||
!
newAccount
)
{
return
}
if
(
!
wasShow
||
newAccount
!==
previousAccount
)
{
syncFormFromAccount
(
newAccount
)
}
}
,
{
immediate
:
true
}
...
...
frontend/src/components/account/__tests__/EditAccountModal.spec.ts
0 → 100644
View file @
0772d925
import
{
describe
,
expect
,
it
,
vi
}
from
'
vitest
'
import
{
defineComponent
}
from
'
vue
'
import
{
mount
}
from
'
@vue/test-utils
'
const
{
updateAccountMock
,
checkMixedChannelRiskMock
}
=
vi
.
hoisted
(()
=>
({
updateAccountMock
:
vi
.
fn
(),
checkMixedChannelRiskMock
:
vi
.
fn
()
}))
vi
.
mock
(
'
@/stores/app
'
,
()
=>
({
useAppStore
:
()
=>
({
showError
:
vi
.
fn
(),
showSuccess
:
vi
.
fn
(),
showInfo
:
vi
.
fn
()
})
}))
vi
.
mock
(
'
@/stores/auth
'
,
()
=>
({
useAuthStore
:
()
=>
({
isSimpleMode
:
true
})
}))
vi
.
mock
(
'
@/api/admin
'
,
()
=>
({
adminAPI
:
{
accounts
:
{
update
:
updateAccountMock
,
checkMixedChannelRisk
:
checkMixedChannelRiskMock
}
}
}))
vi
.
mock
(
'
@/api/admin/accounts
'
,
()
=>
({
getAntigravityDefaultModelMapping
:
vi
.
fn
()
}))
vi
.
mock
(
'
vue-i18n
'
,
async
()
=>
{
const
actual
=
await
vi
.
importActual
<
typeof
import
(
'
vue-i18n
'
)
>
(
'
vue-i18n
'
)
return
{
...
actual
,
useI18n
:
()
=>
({
t
:
(
key
:
string
)
=>
key
})
}
})
import
EditAccountModal
from
'
../EditAccountModal.vue
'
const
BaseDialogStub
=
defineComponent
({
name
:
'
BaseDialog
'
,
props
:
{
show
:
{
type
:
Boolean
,
default
:
false
}
},
template
:
'
<div v-if="show"><slot /><slot name="footer" /></div>
'
})
const
ModelWhitelistSelectorStub
=
defineComponent
({
name
:
'
ModelWhitelistSelector
'
,
props
:
{
modelValue
:
{
type
:
Array
,
default
:
()
=>
[]
}
},
emits
:
[
'
update:modelValue
'
],
template
:
`
<div>
<button
type="button"
data-testid="rewrite-to-snapshot"
@click="$emit('update:modelValue', ['gpt-5.2-2025-12-11'])"
>
rewrite
</button>
<span data-testid="model-whitelist-value">
{{ Array.isArray(modelValue) ? modelValue.join(',') : '' }}
</span>
</div>
`
})
function
buildAccount
()
{
return
{
id
:
1
,
name
:
'
OpenAI Key
'
,
notes
:
''
,
platform
:
'
openai
'
,
type
:
'
apikey
'
,
credentials
:
{
api_key
:
'
sk-test
'
,
base_url
:
'
https://api.openai.com
'
,
model_mapping
:
{
'
gpt-5.2
'
:
'
gpt-5.2
'
}
},
extra
:
{},
proxy_id
:
null
,
concurrency
:
1
,
priority
:
1
,
rate_multiplier
:
1
,
status
:
'
active
'
,
group_ids
:
[],
expires_at
:
null
,
auto_pause_on_expired
:
false
}
as
any
}
function
mountModal
(
account
=
buildAccount
())
{
return
mount
(
EditAccountModal
,
{
props
:
{
show
:
true
,
account
,
proxies
:
[],
groups
:
[]
},
global
:
{
stubs
:
{
BaseDialog
:
BaseDialogStub
,
Select
:
true
,
Icon
:
true
,
ProxySelector
:
true
,
GroupSelector
:
true
,
ModelWhitelistSelector
:
ModelWhitelistSelectorStub
}
}
})
}
describe
(
'
EditAccountModal
'
,
()
=>
{
it
(
'
reopening the same account rehydrates the OpenAI whitelist from props
'
,
async
()
=>
{
const
account
=
buildAccount
()
updateAccountMock
.
mockReset
()
checkMixedChannelRiskMock
.
mockReset
()
checkMixedChannelRiskMock
.
mockResolvedValue
({
has_risk
:
false
})
updateAccountMock
.
mockResolvedValue
(
account
)
const
wrapper
=
mountModal
(
account
)
expect
(
wrapper
.
get
(
'
[data-testid="model-whitelist-value"]
'
).
text
()).
toBe
(
'
gpt-5.2
'
)
await
wrapper
.
get
(
'
[data-testid="rewrite-to-snapshot"]
'
).
trigger
(
'
click
'
)
expect
(
wrapper
.
get
(
'
[data-testid="model-whitelist-value"]
'
).
text
()).
toBe
(
'
gpt-5.2-2025-12-11
'
)
await
wrapper
.
setProps
({
show
:
false
})
await
wrapper
.
setProps
({
show
:
true
})
expect
(
wrapper
.
get
(
'
[data-testid="model-whitelist-value"]
'
).
text
()).
toBe
(
'
gpt-5.2
'
)
await
wrapper
.
get
(
'
form#edit-account-form
'
).
trigger
(
'
submit.prevent
'
)
expect
(
updateAccountMock
).
toHaveBeenCalledTimes
(
1
)
expect
(
updateAccountMock
.
mock
.
calls
[
0
]?.[
1
]?.
credentials
?.
model_mapping
).
toEqual
({
'
gpt-5.2
'
:
'
gpt-5.2
'
})
})
})
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment