feat(tls-fingerprint): 新增 TLS 指纹 Profile 数据库管理及代码质量优化
新增功能: - 新增 TLS 指纹 Profile CRUD 管理(Ent schema + 迁移 + Admin API + 前端管理界面) - 支持账号绑定数据库中的自定义 TLS Profile,或随机选择(profile_id=-1) - HTTPUpstream.DoWithTLS 接口从 bool 改为 *tlsfingerprint.Profile,支持按账号指定 Profile - AccountUsageService 注入 TLSFingerprintProfileService,统一 usage 场景与网关的 Profile 解析逻辑 代码优化: - 删除已被 TLSFingerprintProfileService 完全取代的 registry.go 死代码(418 行) - 提取 3 个 dialer 的重复 TLS 握手逻辑为 performTLSHandshake() 共用函数 - 修复 GetTLSFingerprintProfileID 缺少 json.Number 处理的 bug - gateway_service.Forward 中 ResolveTLSProfile 从重试循环内重复调用改为预解析局部变量 - 删除冗余的 buildClientHelloSpec() 单行 wrapper 和 int64(e.ID) 无效转换 - tls_fingerprint_profile_cache.go 日志从 log.Printf 改为 slog 结构化日志 - dialer_capture_test.go 添加 //go:build integration 标签,防止 CI 失败 - 去重 TestProfileExpectation 类型至共享 test_types_test.go - 修复 9 个测试文件缺少 tlsfingerprint import 的编译错误 - 修复 error_policy_integration_test.go 中 handleError 回调签名被错误替换的问题
This commit is contained in:
@@ -2169,6 +2169,14 @@
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Profile selector -->
|
||||
<div v-if="tlsFingerprintEnabled" class="mt-3">
|
||||
<select v-model="tlsFingerprintProfileId" class="input">
|
||||
<option :value="null">{{ t('admin.accounts.quotaControl.tlsFingerprint.defaultProfile') }}</option>
|
||||
<option v-if="tlsFingerprintProfiles.length > 0" :value="-1">{{ t('admin.accounts.quotaControl.tlsFingerprint.randomProfile') }}</option>
|
||||
<option v-for="p in tlsFingerprintProfiles" :key="p.id" :value="p.id">{{ p.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Session ID Masking -->
|
||||
@@ -3082,6 +3090,8 @@ const umqModeOptions = computed(() => [
|
||||
{ value: 'serialize', label: t('admin.accounts.quotaControl.rpmLimit.umqModeSerialize') },
|
||||
])
|
||||
const tlsFingerprintEnabled = ref(false)
|
||||
const tlsFingerprintProfileId = ref<number | null>(null)
|
||||
const tlsFingerprintProfiles = ref<{ id: number; name: string }[]>([])
|
||||
const sessionIdMaskingEnabled = ref(false)
|
||||
const cacheTTLOverrideEnabled = ref(false)
|
||||
const cacheTTLOverrideTarget = ref<string>('5m')
|
||||
@@ -3247,6 +3257,10 @@ watch(
|
||||
() => props.show,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
// Load TLS fingerprint profiles
|
||||
adminAPI.tlsFingerprintProfiles.list()
|
||||
.then(profiles => { tlsFingerprintProfiles.value = profiles.map(p => ({ id: p.id, name: p.name })) })
|
||||
.catch(() => { tlsFingerprintProfiles.value = [] })
|
||||
// Modal opened - fill related models
|
||||
allowedModels.value = [...getModelsByPlatform(form.platform)]
|
||||
// Antigravity: 默认使用映射模式并填充默认映射
|
||||
@@ -3747,6 +3761,7 @@ const resetForm = () => {
|
||||
rpmStickyBuffer.value = null
|
||||
userMsgQueueMode.value = ''
|
||||
tlsFingerprintEnabled.value = false
|
||||
tlsFingerprintProfileId.value = null
|
||||
sessionIdMaskingEnabled.value = false
|
||||
cacheTTLOverrideEnabled.value = false
|
||||
cacheTTLOverrideTarget.value = '5m'
|
||||
@@ -4825,6 +4840,9 @@ const handleAnthropicExchange = async (authCode: string) => {
|
||||
// Add TLS fingerprint settings
|
||||
if (tlsFingerprintEnabled.value) {
|
||||
extra.enable_tls_fingerprint = true
|
||||
if (tlsFingerprintProfileId.value) {
|
||||
extra.tls_fingerprint_profile_id = tlsFingerprintProfileId.value
|
||||
}
|
||||
}
|
||||
|
||||
// Add session ID masking settings
|
||||
@@ -4940,6 +4958,9 @@ const handleCookieAuth = async (sessionKey: string) => {
|
||||
// Add TLS fingerprint settings
|
||||
if (tlsFingerprintEnabled.value) {
|
||||
extra.enable_tls_fingerprint = true
|
||||
if (tlsFingerprintProfileId.value) {
|
||||
extra.tls_fingerprint_profile_id = tlsFingerprintProfileId.value
|
||||
}
|
||||
}
|
||||
|
||||
// Add session ID masking settings
|
||||
|
||||
@@ -1504,6 +1504,14 @@
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Profile selector -->
|
||||
<div v-if="tlsFingerprintEnabled" class="mt-3">
|
||||
<select v-model="tlsFingerprintProfileId" class="input">
|
||||
<option :value="null">{{ t('admin.accounts.quotaControl.tlsFingerprint.defaultProfile') }}</option>
|
||||
<option v-if="tlsFingerprintProfiles.length > 0" :value="-1">{{ t('admin.accounts.quotaControl.tlsFingerprint.randomProfile') }}</option>
|
||||
<option v-for="p in tlsFingerprintProfiles" :key="p.id" :value="p.id">{{ p.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Session ID Masking -->
|
||||
@@ -1841,6 +1849,8 @@ const umqModeOptions = computed(() => [
|
||||
{ value: 'serialize', label: t('admin.accounts.quotaControl.rpmLimit.umqModeSerialize') },
|
||||
])
|
||||
const tlsFingerprintEnabled = ref(false)
|
||||
const tlsFingerprintProfileId = ref<number | null>(null)
|
||||
const tlsFingerprintProfiles = ref<{ id: number; name: string }[]>([])
|
||||
const sessionIdMaskingEnabled = ref(false)
|
||||
const cacheTTLOverrideEnabled = ref(false)
|
||||
const cacheTTLOverrideTarget = ref<string>('5m')
|
||||
@@ -2255,11 +2265,21 @@ watch(
|
||||
}
|
||||
if (!wasShow || newAccount !== previousAccount) {
|
||||
syncFormFromAccount(newAccount)
|
||||
loadTLSProfiles()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const loadTLSProfiles = async () => {
|
||||
try {
|
||||
const profiles = await adminAPI.tlsFingerprintProfiles.list()
|
||||
tlsFingerprintProfiles.value = profiles.map(p => ({ id: p.id, name: p.name }))
|
||||
} catch {
|
||||
tlsFingerprintProfiles.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// Model mapping helpers
|
||||
const addModelMapping = () => {
|
||||
modelMappings.value.push({ from: '', to: '' })
|
||||
@@ -2458,6 +2478,7 @@ function loadQuotaControlSettings(account: Account) {
|
||||
rpmStickyBuffer.value = null
|
||||
userMsgQueueMode.value = ''
|
||||
tlsFingerprintEnabled.value = false
|
||||
tlsFingerprintProfileId.value = null
|
||||
sessionIdMaskingEnabled.value = false
|
||||
cacheTTLOverrideEnabled.value = false
|
||||
cacheTTLOverrideTarget.value = '5m'
|
||||
@@ -2495,6 +2516,7 @@ function loadQuotaControlSettings(account: Account) {
|
||||
if (account.enable_tls_fingerprint === true) {
|
||||
tlsFingerprintEnabled.value = true
|
||||
}
|
||||
tlsFingerprintProfileId.value = account.tls_fingerprint_profile_id ?? null
|
||||
|
||||
// Load session ID masking setting
|
||||
if (account.session_id_masking_enabled === true) {
|
||||
@@ -2932,8 +2954,14 @@ const handleSubmit = async () => {
|
||||
// TLS fingerprint setting
|
||||
if (tlsFingerprintEnabled.value) {
|
||||
newExtra.enable_tls_fingerprint = true
|
||||
if (tlsFingerprintProfileId.value) {
|
||||
newExtra.tls_fingerprint_profile_id = tlsFingerprintProfileId.value
|
||||
} else {
|
||||
delete newExtra.tls_fingerprint_profile_id
|
||||
}
|
||||
} else {
|
||||
delete newExtra.enable_tls_fingerprint
|
||||
delete newExtra.tls_fingerprint_profile_id
|
||||
}
|
||||
|
||||
// Session ID masking setting
|
||||
|
||||
Reference in New Issue
Block a user