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
9618cb56
Commit
9618cb56
authored
Jan 12, 2026
by
yangjianbo
Browse files
Merge branch 'main' into test
parents
9c02ab78
8c1958c9
Changes
22
Hide whitespace changes
Inline
Side-by-side
frontend/src/i18n/locales/zh.ts
View file @
9618cb56
...
...
@@ -2696,6 +2696,27 @@ export default {
securityWarning
:
'
警告:此密钥拥有完整的管理员权限,请妥善保管。
'
,
usage
:
'
使用方法:在请求头中添加 x-api-key: <your-admin-api-key>
'
},
streamTimeout
:
{
title
:
'
流超时处理
'
,
description
:
'
配置上游响应超时时的账户处理策略,避免问题账户持续被选中
'
,
enabled
:
'
启用流超时处理
'
,
enabledHint
:
'
当上游响应超时时,自动处理问题账户
'
,
timeoutSeconds
:
'
超时阈值(秒)
'
,
timeoutSecondsHint
:
'
流数据间隔超过此时间视为超时(30-300秒)
'
,
action
:
'
处理方式
'
,
actionTempUnsched
:
'
临时不可调度
'
,
actionError
:
'
标记为错误状态
'
,
actionNone
:
'
不处理
'
,
actionHint
:
'
超时后对账户执行的操作
'
,
tempUnschedMinutes
:
'
暂停时长(分钟)
'
,
tempUnschedMinutesHint
:
'
临时不可调度的持续时间(1-60分钟)
'
,
thresholdCount
:
'
触发阈值(次数)
'
,
thresholdCountHint
:
'
累计超时多少次后触发处理(1-10次)
'
,
thresholdWindowMinutes
:
'
阈值窗口(分钟)
'
,
thresholdWindowMinutesHint
:
'
超时计数的时间窗口(1-60分钟)
'
,
saved
:
'
流超时设置保存成功
'
,
saveFailed
:
'
保存流超时设置失败
'
},
saveSettings
:
'
保存设置
'
,
saving
:
'
保存中...
'
,
settingsSaved
:
'
设置保存成功
'
,
...
...
frontend/src/views/admin/SettingsView.vue
View file @
9618cb56
...
...
@@ -147,6 +147,144 @@
</div>
</div>
<!-- Stream Timeout Settings -->
<div
class=
"card"
>
<div
class=
"border-b border-gray-100 px-6 py-4 dark:border-dark-700"
>
<h2
class=
"text-lg font-semibold text-gray-900 dark:text-white"
>
{{
t
(
'
admin.settings.streamTimeout.title
'
)
}}
</h2>
<p
class=
"mt-1 text-sm text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.settings.streamTimeout.description
'
)
}}
</p>
</div>
<div
class=
"space-y-5 p-6"
>
<!-- Loading State -->
<div
v-if=
"streamTimeoutLoading"
class=
"flex items-center gap-2 text-gray-500"
>
<div
class=
"h-4 w-4 animate-spin rounded-full border-b-2 border-primary-600"
></div>
{{
t
(
'
common.loading
'
)
}}
</div>
<template
v-else
>
<!-- Enable Stream Timeout -->
<div
class=
"flex items-center justify-between"
>
<div>
<label
class=
"font-medium text-gray-900 dark:text-white"
>
{{
t
(
'
admin.settings.streamTimeout.enabled
'
)
}}
</label>
<p
class=
"text-sm text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.settings.streamTimeout.enabledHint
'
)
}}
</p>
</div>
<Toggle
v-model=
"streamTimeoutForm.enabled"
/>
</div>
<!-- Settings - Only show when enabled -->
<div
v-if=
"streamTimeoutForm.enabled"
class=
"space-y-4 border-t border-gray-100 pt-4 dark:border-dark-700"
>
<!-- Action -->
<div>
<label
class=
"mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t
(
'
admin.settings.streamTimeout.action
'
)
}}
</label>
<select
v-model=
"streamTimeoutForm.action"
class=
"input w-64"
>
<option
value=
"temp_unsched"
>
{{
t
(
'
admin.settings.streamTimeout.actionTempUnsched
'
)
}}
</option>
<option
value=
"error"
>
{{
t
(
'
admin.settings.streamTimeout.actionError
'
)
}}
</option>
<option
value=
"none"
>
{{
t
(
'
admin.settings.streamTimeout.actionNone
'
)
}}
</option>
</select>
<p
class=
"mt-1.5 text-xs text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.settings.streamTimeout.actionHint
'
)
}}
</p>
</div>
<!-- Temp Unsched Minutes (only show when action is temp_unsched) -->
<div
v-if=
"streamTimeoutForm.action === 'temp_unsched'"
>
<label
class=
"mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t
(
'
admin.settings.streamTimeout.tempUnschedMinutes
'
)
}}
</label>
<input
v-model.number=
"streamTimeoutForm.temp_unsched_minutes"
type=
"number"
min=
"1"
max=
"60"
class=
"input w-32"
/>
<p
class=
"mt-1.5 text-xs text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.settings.streamTimeout.tempUnschedMinutesHint
'
)
}}
</p>
</div>
<!-- Threshold Count -->
<div>
<label
class=
"mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t
(
'
admin.settings.streamTimeout.thresholdCount
'
)
}}
</label>
<input
v-model.number=
"streamTimeoutForm.threshold_count"
type=
"number"
min=
"1"
max=
"10"
class=
"input w-32"
/>
<p
class=
"mt-1.5 text-xs text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.settings.streamTimeout.thresholdCountHint
'
)
}}
</p>
</div>
<!-- Threshold Window Minutes -->
<div>
<label
class=
"mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{
t
(
'
admin.settings.streamTimeout.thresholdWindowMinutes
'
)
}}
</label>
<input
v-model.number=
"streamTimeoutForm.threshold_window_minutes"
type=
"number"
min=
"1"
max=
"60"
class=
"input w-32"
/>
<p
class=
"mt-1.5 text-xs text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.settings.streamTimeout.thresholdWindowMinutesHint
'
)
}}
</p>
</div>
</div>
<!-- Save Button -->
<div
class=
"flex justify-end border-t border-gray-100 pt-4 dark:border-dark-700"
>
<button
type=
"button"
@
click=
"saveStreamTimeoutSettings"
:disabled=
"streamTimeoutSaving"
class=
"btn btn-primary btn-sm"
>
<svg
v-if=
"streamTimeoutSaving"
class=
"mr-1 h-4 w-4 animate-spin"
fill=
"none"
viewBox=
"0 0 24 24"
>
<circle
class=
"opacity-25"
cx=
"12"
cy=
"12"
r=
"10"
stroke=
"currentColor"
stroke-width=
"4"
></circle>
<path
class=
"opacity-75"
fill=
"currentColor"
d=
"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
{{
streamTimeoutSaving
?
t
(
'
common.saving
'
)
:
t
(
'
common.save
'
)
}}
</button>
</div>
</
template
>
</div>
</div>
<!-- Registration Settings -->
<div
class=
"card"
>
<div
class=
"border-b border-gray-100 px-6 py-4 dark:border-dark-700"
>
...
...
@@ -840,6 +978,17 @@ const adminApiKeyMasked = ref('')
const
adminApiKeyOperating
=
ref
(
false
)
const
newAdminApiKey
=
ref
(
''
)
// Stream Timeout 状态
const
streamTimeoutLoading
=
ref
(
true
)
const
streamTimeoutSaving
=
ref
(
false
)
const
streamTimeoutForm
=
reactive
({
enabled
:
true
,
action
:
'
temp_unsched
'
as
'
temp_unsched
'
|
'
error
'
|
'
none
'
,
temp_unsched_minutes
:
5
,
threshold_count
:
3
,
threshold_window_minutes
:
10
})
type
SettingsForm
=
SystemSettings
&
{
smtp_password
:
string
turnstile_secret_key
:
string
...
...
@@ -1129,8 +1278,43 @@ function copyNewKey() {
})
}
// Stream Timeout 方法
async
function
loadStreamTimeoutSettings
()
{
streamTimeoutLoading
.
value
=
true
try
{
const
settings
=
await
adminAPI
.
settings
.
getStreamTimeoutSettings
()
Object
.
assign
(
streamTimeoutForm
,
settings
)
}
catch
(
error
:
any
)
{
console
.
error
(
'
Failed to load stream timeout settings:
'
,
error
)
}
finally
{
streamTimeoutLoading
.
value
=
false
}
}
async
function
saveStreamTimeoutSettings
()
{
streamTimeoutSaving
.
value
=
true
try
{
const
updated
=
await
adminAPI
.
settings
.
updateStreamTimeoutSettings
({
enabled
:
streamTimeoutForm
.
enabled
,
action
:
streamTimeoutForm
.
action
,
temp_unsched_minutes
:
streamTimeoutForm
.
temp_unsched_minutes
,
threshold_count
:
streamTimeoutForm
.
threshold_count
,
threshold_window_minutes
:
streamTimeoutForm
.
threshold_window_minutes
})
Object
.
assign
(
streamTimeoutForm
,
updated
)
appStore
.
showSuccess
(
t
(
'
admin.settings.streamTimeout.saved
'
))
}
catch
(
error
:
any
)
{
appStore
.
showError
(
t
(
'
admin.settings.streamTimeout.saveFailed
'
)
+
'
:
'
+
(
error
.
message
||
t
(
'
common.unknownError
'
))
)
}
finally
{
streamTimeoutSaving
.
value
=
false
}
}
onMounted
(()
=>
{
loadSettings
()
loadAdminApiKey
()
loadStreamTimeoutSettings
()
})
</
script
>
Prev
1
2
Next
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