Merge pull request #3230 from DaydreamCoding/feat/openai-cyber-policy-passthrough

feat(openai): cyber_policy 硬阻断全链路透传、审计与计费
This commit is contained in:
Wesley Liddick
2026-06-16 16:55:51 +08:00
committed by GitHub
56 changed files with 3036 additions and 184 deletions
+13 -1
View File
@@ -881,6 +881,13 @@
</div>
<Toggle v-model="configForm.auto_ban_enabled" />
</div>
<div class="flex items-center justify-between rounded-lg border border-gray-100 p-4 dark:border-dark-700 lg:col-span-2">
<div>
<p class="text-sm font-medium text-gray-900 dark:text-white">{{ t('admin.riskControl.cyberPolicyExcludeBan') }}</p>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.riskControl.cyberPolicyExcludeBanHint') }}</p>
</div>
<Toggle v-model="configForm.cyber_policy_exclude_from_ban_count" />
</div>
<div>
<label class="input-label">{{ t('admin.riskControl.banThreshold') }}</label>
<input v-model.number="configForm.ban_threshold" type="number" min="1" max="1000" class="input" />
@@ -1226,6 +1233,7 @@ const configForm = reactive({
block_message: '内容审计命中风险规则,请调整输入后重试',
email_on_hit: true,
auto_ban_enabled: true,
cyber_policy_exclude_from_ban_count: false,
ban_threshold: 10,
violation_window_hours: 720,
hit_retention_days: 180,
@@ -1702,6 +1710,7 @@ function applyConfig(config: ContentModerationConfig) {
configForm.block_message = config.block_message || '内容审计命中风险规则,请调整输入后重试'
configForm.email_on_hit = config.email_on_hit ?? true
configForm.auto_ban_enabled = config.auto_ban_enabled ?? true
configForm.cyber_policy_exclude_from_ban_count = config.cyber_policy_exclude_from_ban_count ?? false
configForm.ban_threshold = config.ban_threshold || 10
configForm.violation_window_hours = config.violation_window_hours || 720
configForm.hit_retention_days = config.hit_retention_days || 180
@@ -1782,6 +1791,7 @@ async function saveConfig() {
block_message: configForm.block_message || '内容审计命中风险规则,请调整输入后重试',
email_on_hit: configForm.email_on_hit,
auto_ban_enabled: configForm.auto_ban_enabled,
cyber_policy_exclude_from_ban_count: configForm.cyber_policy_exclude_from_ban_count,
ban_threshold: Number(configForm.ban_threshold) || 10,
violation_window_hours: Number(configForm.violation_window_hours) || 720,
hit_retention_days: Number(configForm.hit_retention_days) || 180,
@@ -2097,6 +2107,7 @@ function modeDescription(mode: ModerationMode): string {
}
function resultLabel(row: ContentModerationLog): string {
if (row.action === 'cyber_policy') return t('admin.riskControl.action.cyberPolicy')
if (row.action === 'keyword_block') return t('admin.riskControl.action.keywordBlock')
if (row.action === 'block') return t('admin.riskControl.action.block')
if (row.action === 'error' || row.error) return t('admin.riskControl.action.error')
@@ -2105,7 +2116,7 @@ function resultLabel(row: ContentModerationLog): string {
}
function resultBadgeClass(row: ContentModerationLog): string {
if (row.action === 'block' || row.action === 'keyword_block') return 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
if (row.action === 'block' || row.action === 'keyword_block' || row.action === 'cyber_policy') return 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
if (row.action === 'error' || row.error) return 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300'
if (row.flagged) return 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300'
return 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300'
@@ -2303,6 +2314,7 @@ function parseBlockedKeywords(value: string): string[] {
function violationCountText(row: ContentModerationLog): string {
if (!row.flagged) return '-'
if (row.violation_count === 0) return t('admin.riskControl.violationNotCounted')
return t('admin.riskControl.violationCount', { count: row.violation_count || 1 })
}
+30
View File
@@ -5528,6 +5528,31 @@
</div>
<Toggle v-model="form.risk_control_enabled" />
</div>
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ t('admin.settings.features.riskControl.cyberSessionBlock') }}
</label>
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
{{ t('admin.settings.features.riskControl.cyberSessionBlockHint') }}
</p>
</div>
<Toggle v-model="form.cyber_session_block_enabled" />
</div>
<div v-if="form.cyber_session_block_enabled">
<label class="input-label">
{{ t('admin.settings.features.riskControl.cyberSessionBlockTTL') }}
<span class="text-red-500">*</span>
</label>
<input
v-model.number="form.cyber_session_block_ttl_seconds"
type="number"
min="1"
class="input"
/>
</div>
</div>
</div>
@@ -7648,6 +7673,8 @@ const form = reactive<SettingsForm>({
hide_ccs_import_button: false,
payment_enabled: false,
risk_control_enabled: false,
cyber_session_block_enabled: false,
cyber_session_block_ttl_seconds: 3600,
payment_min_amount: 1,
payment_max_amount: 10000,
payment_daily_limit: 50000,
@@ -8943,6 +8970,9 @@ async function saveSettings() {
// Payment configuration
payment_enabled: form.payment_enabled,
risk_control_enabled: form.risk_control_enabled,
cyber_session_block_enabled: form.cyber_session_block_enabled,
cyber_session_block_ttl_seconds:
Number(form.cyber_session_block_ttl_seconds) || 3600,
payment_min_amount: Number(form.payment_min_amount) || 0,
payment_max_amount: Number(form.payment_max_amount) || 0,
payment_daily_limit: Number(form.payment_daily_limit) || 0,
+1
View File
@@ -490,6 +490,7 @@ const cancelExport = () => exportAbortController?.abort()
const openCleanupDialog = () => { cleanupDialogVisible.value = true }
const getRequestTypeLabel = (log: AdminUsageLog): string => {
const requestType = resolveUsageRequestType(log)
if (requestType === 'cyber') return t('usage.cyber')
if (requestType === 'ws_v2') return t('usage.ws')
if (requestType === 'stream') return t('usage.stream')
if (requestType === 'sync') return t('usage.sync')