mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
Merge remote-tracking branch 'origin/pr/462'
This commit is contained in:
@@ -61,6 +61,11 @@
|
||||
:rate-limit-per-minute="systemConfig.rate_limit_per_minute"
|
||||
:enable-registration="systemConfig.enable_registration"
|
||||
:password-policy-level="systemConfig.password_policy_level"
|
||||
:turnstile-enabled="systemConfig.turnstile_enabled"
|
||||
:turnstile-site-key="systemConfig.turnstile_site_key"
|
||||
:turnstile-secret-key="systemConfig.turnstile_secret_key"
|
||||
:turnstile-secret-configured="systemConfig.turnstile_secret_key_is_set"
|
||||
:turnstile-allowed-hostnames-str="turnstileAllowedHostnamesStr"
|
||||
:auto-delete-expired-keys="systemConfig.auto_delete_expired_keys"
|
||||
:enable-format-conversion="systemConfig.enable_format_conversion"
|
||||
:enable-openai-image-sync-heartbeat="systemConfig.enable_openai_image_sync_heartbeat"
|
||||
@@ -71,6 +76,11 @@
|
||||
@update:rate-limit-per-minute="systemConfig.rate_limit_per_minute = $event"
|
||||
@update:enable-registration="systemConfig.enable_registration = $event"
|
||||
@update:password-policy-level="systemConfig.password_policy_level = $event"
|
||||
@update:turnstile-enabled="systemConfig.turnstile_enabled = $event"
|
||||
@update:turnstile-site-key="systemConfig.turnstile_site_key = $event"
|
||||
@update:turnstile-secret-key="systemConfig.turnstile_secret_key = $event"
|
||||
@update:turnstile-allowed-hostnames-str="turnstileAllowedHostnamesStr = $event"
|
||||
@clear-turnstile-secret="clearTurnstileSecret"
|
||||
@update:auto-delete-expired-keys="systemConfig.auto_delete_expired_keys = $event"
|
||||
@update:enable-format-conversion="systemConfig.enable_format_conversion = $event"
|
||||
@update:enable-openai-image-sync-heartbeat="systemConfig.enable_openai_image_sync_heartbeat = $event"
|
||||
@@ -309,11 +319,13 @@ const {
|
||||
maxRequestBodySizeKB,
|
||||
maxResponseBodySizeKB,
|
||||
sensitiveHeadersStr,
|
||||
turnstileAllowedHostnamesStr,
|
||||
loadSystemConfig,
|
||||
loadSystemVersion,
|
||||
saveSiteInfo,
|
||||
saveProxyConfig,
|
||||
saveBasicConfig,
|
||||
clearTurnstileSecret,
|
||||
saveLogConfig,
|
||||
saveCleanupConfig,
|
||||
handleAutoCleanupToggle,
|
||||
|
||||
@@ -171,6 +171,97 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-2 grid grid-cols-1 md:grid-cols-2 gap-4 border-t pt-5">
|
||||
<div class="flex items-center h-full">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="turnstile-enabled"
|
||||
:checked="turnstileEnabled"
|
||||
@update:checked="$emit('update:turnstileEnabled', $event)"
|
||||
/>
|
||||
<div>
|
||||
<Label
|
||||
for="turnstile-enabled"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
注册人机验证
|
||||
</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
开启后注册与发送邮箱验证码前需要通过 Cloudflare Turnstile
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label
|
||||
for="turnstile-site-key"
|
||||
class="block text-sm font-medium"
|
||||
>
|
||||
Turnstile Site Key
|
||||
</Label>
|
||||
<Input
|
||||
id="turnstile-site-key"
|
||||
:model-value="turnstileSiteKey || ''"
|
||||
type="text"
|
||||
placeholder="0x4AAAA..."
|
||||
class="mt-1"
|
||||
@update:model-value="$emit('update:turnstileSiteKey', String($event || '').trim() || null)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<Label
|
||||
for="turnstile-secret-key"
|
||||
class="block text-sm font-medium"
|
||||
>
|
||||
Turnstile Secret Key
|
||||
</Label>
|
||||
<Button
|
||||
v-if="turnstileSecretConfigured"
|
||||
type="button"
|
||||
variant="link"
|
||||
size="sm"
|
||||
class="h-auto p-0 text-xs"
|
||||
:disabled="loading"
|
||||
@click="$emit('clearTurnstileSecret')"
|
||||
>
|
||||
清空
|
||||
</Button>
|
||||
</div>
|
||||
<Input
|
||||
id="turnstile-secret-key"
|
||||
:model-value="turnstileSecretKey"
|
||||
type="password"
|
||||
:placeholder="turnstileSecretConfigured ? '已配置,留空不修改' : '输入 Secret Key'"
|
||||
class="mt-1"
|
||||
autocomplete="new-password"
|
||||
@update:model-value="$emit('update:turnstileSecretKey', String($event || ''))"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label
|
||||
for="turnstile-hostnames"
|
||||
class="block text-sm font-medium"
|
||||
>
|
||||
允许的 Hostname
|
||||
</Label>
|
||||
<Input
|
||||
id="turnstile-hostnames"
|
||||
:model-value="turnstileAllowedHostnamesStr"
|
||||
type="text"
|
||||
placeholder="example.com, app.example.com"
|
||||
class="mt-1"
|
||||
@update:model-value="$emit('update:turnstileAllowedHostnamesStr', String($event || ''))"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-muted-foreground">
|
||||
留空则不额外校验 Cloudflare 返回的 hostname
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardSection>
|
||||
</template>
|
||||
@@ -192,6 +283,11 @@ defineProps<{
|
||||
rateLimitPerMinute: number
|
||||
enableRegistration: boolean
|
||||
passwordPolicyLevel: string
|
||||
turnstileEnabled: boolean
|
||||
turnstileSiteKey: string | null
|
||||
turnstileSecretKey: string
|
||||
turnstileSecretConfigured: boolean
|
||||
turnstileAllowedHostnamesStr: string
|
||||
autoDeleteExpiredKeys: boolean
|
||||
enableFormatConversion: boolean
|
||||
enableOpenaiImageSyncHeartbeat: boolean
|
||||
@@ -205,6 +301,11 @@ defineEmits<{
|
||||
'update:rateLimitPerMinute': [value: number]
|
||||
'update:enableRegistration': [value: boolean]
|
||||
'update:passwordPolicyLevel': [value: string]
|
||||
'update:turnstileEnabled': [value: boolean]
|
||||
'update:turnstileSiteKey': [value: string | null]
|
||||
'update:turnstileSecretKey': [value: string]
|
||||
'update:turnstileAllowedHostnamesStr': [value: string]
|
||||
clearTurnstileSecret: []
|
||||
'update:autoDeleteExpiredKeys': [value: boolean]
|
||||
'update:enableFormatConversion': [value: boolean]
|
||||
'update:enableOpenaiImageSyncHeartbeat': [value: boolean]
|
||||
|
||||
@@ -15,6 +15,11 @@ export interface SystemConfig {
|
||||
rate_limit_per_minute: number
|
||||
enable_registration: boolean
|
||||
password_policy_level: string
|
||||
turnstile_enabled: boolean
|
||||
turnstile_site_key: string | null
|
||||
turnstile_secret_key: string
|
||||
turnstile_secret_key_is_set: boolean
|
||||
turnstile_allowed_hostnames: string[]
|
||||
// 独立余额 Key 过期管理
|
||||
auto_delete_expired_keys: boolean
|
||||
// 格式转换
|
||||
@@ -56,6 +61,10 @@ const CONFIG_KEYS = [
|
||||
'rate_limit_per_minute',
|
||||
'enable_registration',
|
||||
'password_policy_level',
|
||||
'turnstile_enabled',
|
||||
'turnstile_site_key',
|
||||
'turnstile_secret_key',
|
||||
'turnstile_allowed_hostnames',
|
||||
// 独立余额 Key 过期管理
|
||||
'auto_delete_expired_keys',
|
||||
// 格式转换
|
||||
@@ -98,6 +107,11 @@ function createDefaultConfig(): SystemConfig {
|
||||
rate_limit_per_minute: 0,
|
||||
enable_registration: false,
|
||||
password_policy_level: 'weak',
|
||||
turnstile_enabled: false,
|
||||
turnstile_site_key: null,
|
||||
turnstile_secret_key: '',
|
||||
turnstile_secret_key_is_set: false,
|
||||
turnstile_allowed_hostnames: [],
|
||||
// 独立余额 Key 过期管理
|
||||
auto_delete_expired_keys: false,
|
||||
// 格式转换
|
||||
@@ -165,6 +179,11 @@ export function useSystemConfig() {
|
||||
systemConfig.value.rate_limit_per_minute !== originalConfig.value.rate_limit_per_minute ||
|
||||
systemConfig.value.enable_registration !== originalConfig.value.enable_registration ||
|
||||
systemConfig.value.password_policy_level !== originalConfig.value.password_policy_level ||
|
||||
systemConfig.value.turnstile_enabled !== originalConfig.value.turnstile_enabled ||
|
||||
systemConfig.value.turnstile_site_key !== originalConfig.value.turnstile_site_key ||
|
||||
systemConfig.value.turnstile_secret_key.trim() !== '' ||
|
||||
JSON.stringify(systemConfig.value.turnstile_allowed_hostnames) !==
|
||||
JSON.stringify(originalConfig.value.turnstile_allowed_hostnames) ||
|
||||
systemConfig.value.auto_delete_expired_keys !== originalConfig.value.auto_delete_expired_keys ||
|
||||
systemConfig.value.enable_format_conversion !== originalConfig.value.enable_format_conversion ||
|
||||
systemConfig.value.enable_openai_image_sync_heartbeat !== originalConfig.value.enable_openai_image_sync_heartbeat
|
||||
@@ -233,12 +252,27 @@ export function useSystemConfig() {
|
||||
},
|
||||
})
|
||||
|
||||
const turnstileAllowedHostnamesStr = computed({
|
||||
get: () => systemConfig.value.turnstile_allowed_hostnames.join(', '),
|
||||
set: (val: string) => {
|
||||
systemConfig.value.turnstile_allowed_hostnames = val
|
||||
.split(',')
|
||||
.map((s) => s.trim().toLowerCase())
|
||||
.filter((s) => s.length > 0)
|
||||
},
|
||||
})
|
||||
|
||||
// 加载配置
|
||||
async function loadSystemConfig() {
|
||||
try {
|
||||
for (const key of CONFIG_KEYS) {
|
||||
try {
|
||||
const response = await adminApi.getSystemConfig(key)
|
||||
if (key === 'turnstile_secret_key') {
|
||||
systemConfig.value.turnstile_secret_key = ''
|
||||
systemConfig.value.turnstile_secret_key_is_set = !!response.is_set
|
||||
continue
|
||||
}
|
||||
if (response.value !== null && response.value !== undefined) {
|
||||
; (systemConfig.value as Record<string, unknown>)[key] = response.value
|
||||
}
|
||||
@@ -337,6 +371,21 @@ export function useSystemConfig() {
|
||||
value: systemConfig.value.password_policy_level,
|
||||
description: '密码策略等级',
|
||||
},
|
||||
{
|
||||
key: 'turnstile_enabled',
|
||||
value: systemConfig.value.turnstile_enabled,
|
||||
description: 'Cloudflare Turnstile 注册人机验证开关',
|
||||
},
|
||||
{
|
||||
key: 'turnstile_site_key',
|
||||
value: systemConfig.value.turnstile_site_key?.trim() || null,
|
||||
description: 'Cloudflare Turnstile 站点 Key',
|
||||
},
|
||||
{
|
||||
key: 'turnstile_allowed_hostnames',
|
||||
value: systemConfig.value.turnstile_allowed_hostnames,
|
||||
description: 'Cloudflare Turnstile 允许的 hostname 列表',
|
||||
},
|
||||
{
|
||||
key: 'auto_delete_expired_keys',
|
||||
value: systemConfig.value.auto_delete_expired_keys,
|
||||
@@ -353,6 +402,14 @@ export function useSystemConfig() {
|
||||
description: '同步生图心跳开关:开启后外层 HTTP 状态固定为 200,上游失败写入响应体',
|
||||
},
|
||||
]
|
||||
const turnstileSecret = systemConfig.value.turnstile_secret_key.trim()
|
||||
if (turnstileSecret) {
|
||||
configItems.push({
|
||||
key: 'turnstile_secret_key',
|
||||
value: turnstileSecret,
|
||||
description: 'Cloudflare Turnstile Secret Key',
|
||||
})
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
configItems.map((item) =>
|
||||
@@ -364,6 +421,17 @@ export function useSystemConfig() {
|
||||
originalConfig.value.rate_limit_per_minute = systemConfig.value.rate_limit_per_minute
|
||||
originalConfig.value.enable_registration = systemConfig.value.enable_registration
|
||||
originalConfig.value.password_policy_level = systemConfig.value.password_policy_level
|
||||
originalConfig.value.turnstile_enabled = systemConfig.value.turnstile_enabled
|
||||
originalConfig.value.turnstile_site_key = systemConfig.value.turnstile_site_key?.trim() || null
|
||||
originalConfig.value.turnstile_allowed_hostnames = [
|
||||
...systemConfig.value.turnstile_allowed_hostnames,
|
||||
]
|
||||
if (turnstileSecret) {
|
||||
systemConfig.value.turnstile_secret_key = ''
|
||||
systemConfig.value.turnstile_secret_key_is_set = true
|
||||
originalConfig.value.turnstile_secret_key = ''
|
||||
originalConfig.value.turnstile_secret_key_is_set = true
|
||||
}
|
||||
originalConfig.value.auto_delete_expired_keys =
|
||||
systemConfig.value.auto_delete_expired_keys
|
||||
originalConfig.value.enable_format_conversion =
|
||||
@@ -380,6 +448,29 @@ export function useSystemConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
async function clearTurnstileSecret() {
|
||||
basicConfigLoading.value = true
|
||||
try {
|
||||
await adminApi.updateSystemConfig(
|
||||
'turnstile_secret_key',
|
||||
'',
|
||||
'Cloudflare Turnstile Secret Key'
|
||||
)
|
||||
systemConfig.value.turnstile_secret_key = ''
|
||||
systemConfig.value.turnstile_secret_key_is_set = false
|
||||
if (originalConfig.value) {
|
||||
originalConfig.value.turnstile_secret_key = ''
|
||||
originalConfig.value.turnstile_secret_key_is_set = false
|
||||
}
|
||||
success('Turnstile 密钥已清空')
|
||||
} catch (err) {
|
||||
error('清空 Turnstile 密钥失败')
|
||||
log.error('清空 Turnstile 密钥失败:', err)
|
||||
} finally {
|
||||
basicConfigLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function saveLogConfig() {
|
||||
logConfigLoading.value = true
|
||||
try {
|
||||
@@ -559,6 +650,7 @@ export function useSystemConfig() {
|
||||
maxRequestBodySizeKB,
|
||||
maxResponseBodySizeKB,
|
||||
sensitiveHeadersStr,
|
||||
turnstileAllowedHostnamesStr,
|
||||
// 加载函数
|
||||
loadSystemConfig,
|
||||
loadSystemVersion,
|
||||
@@ -566,6 +658,7 @@ export function useSystemConfig() {
|
||||
saveSiteInfo,
|
||||
saveProxyConfig,
|
||||
saveBasicConfig,
|
||||
clearTurnstileSecret,
|
||||
saveLogConfig,
|
||||
saveCleanupConfig,
|
||||
handleAutoCleanupToggle,
|
||||
|
||||
Reference in New Issue
Block a user