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
248fe092
Commit
248fe092
authored
Apr 07, 2026
by
shaw
Committed by
陈曦
Apr 08, 2026
Browse files
feat: Beta策略支持按模型区分处理(模型白名单)
parent
b85ab201
Changes
8
Show whitespace changes
Inline
Side-by-side
backend/internal/handler/dto/settings.go
View file @
248fe092
...
...
@@ -161,6 +161,9 @@ type BetaPolicyRule struct {
Action
string
`json:"action"`
Scope
string
`json:"scope"`
ErrorMessage
string
`json:"error_message,omitempty"`
ModelWhitelist
[]
string
`json:"model_whitelist,omitempty"`
FallbackAction
string
`json:"fallback_action,omitempty"`
FallbackErrorMessage
string
`json:"fallback_error_message,omitempty"`
}
// BetaPolicySettings Beta 策略配置 DTO
...
...
backend/internal/service/gateway_service.go
View file @
248fe092
...
...
@@ -3946,7 +3946,7 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
// Beta policy: evaluate once; block check + cache filter set for buildUpstreamRequest.
// Always overwrite the cache to prevent stale values from a previous retry with a different account.
if account.Platform == PlatformAnthropic && c != nil {
policy
:=
s
.
evaluateBetaPolicy
(
ctx
,
c
.
GetHeader
(
"anthropic-beta"
),
account
)
policy := s.evaluateBetaPolicy(ctx, c.GetHeader("anthropic-beta"), account
, parsed.Model
)
if policy.blockErr != nil {
return nil, policy.blockErr
}
...
...
@@ -5603,7 +5603,7 @@ func (s *GatewayService) buildUpstreamRequest(ctx context.Context, c *gin.Contex
}
// Build effective drop set: merge static defaults with dynamic beta policy filter rules
policyFilterSet
:=
s
.
getBetaPolicyFilterSet
(
ctx
,
c
,
account
)
policyFilterSet := s.getBetaPolicyFilterSet(ctx, c, account
, modelID
)
effectiveDropSet := mergeDropSets(policyFilterSet)
effectiveDropWithClaudeCodeSet := mergeDropSets(policyFilterSet, claude.BetaClaudeCode)
...
...
@@ -5843,7 +5843,7 @@ type betaPolicyResult struct {
}
// evaluateBetaPolicy loads settings once and evaluates all rules against the given request.
func
(
s
*
GatewayService
)
evaluateBetaPolicy
(
ctx
context
.
Context
,
betaHeader
string
,
account
*
Account
)
betaPolicyResult
{
func (s *GatewayService) evaluateBetaPolicy(ctx context.Context, betaHeader string, account *Account
, model string
) betaPolicyResult {
if s.settingService == nil {
return betaPolicyResult{}
}
...
...
@@ -5858,10 +5858,11 @@ func (s *GatewayService) evaluateBetaPolicy(ctx context.Context, betaHeader stri
if !betaPolicyScopeMatches(rule.Scope, isOAuth, isBedrock) {
continue
}
switch
rule
.
Action
{
effectiveAction, effectiveErrMsg := resolveRuleAction(rule, model)
switch effectiveAction {
case BetaPolicyActionBlock:
if result.blockErr == nil && betaHeader != "" && containsBetaToken(betaHeader, rule.BetaToken) {
msg
:=
rule
.
ErrorMessage
msg :=
effectiveErrMsg
if msg == "" {
msg = "beta feature " + rule.BetaToken + " is not allowed"
}
...
...
@@ -5903,7 +5904,7 @@ const betaPolicyFilterSetKey = "betaPolicyFilterSet"
// In the /v1/messages path, Forward() evaluates the policy first and caches the result;
// buildUpstreamRequest reuses it (zero extra DB calls). In the count_tokens path, this
// evaluates on demand (one DB call).
func
(
s
*
GatewayService
)
getBetaPolicyFilterSet
(
ctx
context
.
Context
,
c
*
gin
.
Context
,
account
*
Account
)
map
[
string
]
struct
{}
{
func (s *GatewayService) getBetaPolicyFilterSet(ctx context.Context, c *gin.Context, account *Account
, model string
) map[string]struct{} {
if c != nil {
if v, ok := c.Get(betaPolicyFilterSetKey); ok {
if fs, ok := v.(map[string]struct{}); ok {
...
...
@@ -5911,7 +5912,7 @@ func (s *GatewayService) getBetaPolicyFilterSet(ctx context.Context, c *gin.Cont
}
}
}
return
s
.
evaluateBetaPolicy
(
ctx
,
""
,
account
)
.
filterSet
return s.evaluateBetaPolicy(ctx, "", account
, model
).filterSet
}
// betaPolicyScopeMatches checks whether a rule's scope matches the current account type.
...
...
@@ -5930,6 +5931,33 @@ func betaPolicyScopeMatches(scope string, isOAuth bool, isBedrock bool) bool {
}
}
// matchModelWhitelist checks if a model matches any pattern in the whitelist.
// Reuses matchModelPattern from group.go which supports exact and wildcard prefix matching.
func matchModelWhitelist(model string, whitelist []string) bool {
for _, pattern := range whitelist {
if matchModelPattern(pattern, model) {
return true
}
}
return false
}
// resolveRuleAction determines the effective action and error message for a rule given the request model.
// When ModelWhitelist is empty, the rule's primary Action/ErrorMessage applies unconditionally.
// When non-empty, Action applies to matching models; FallbackAction/FallbackErrorMessage applies to others.
func resolveRuleAction(rule BetaPolicyRule, model string) (action, errorMessage string) {
if len(rule.ModelWhitelist) == 0 {
return rule.Action, rule.ErrorMessage
}
if matchModelWhitelist(model, rule.ModelWhitelist) {
return rule.Action, rule.ErrorMessage
}
if rule.FallbackAction != "" {
return rule.FallbackAction, rule.FallbackErrorMessage
}
return BetaPolicyActionPass, "" // default fallback: pass (fail-open)
}
// droppedBetaSet returns claude.DroppedBetas as a set, with optional extra tokens.
func droppedBetaSet(extra ...string) map[string]struct{} {
m := make(map[string]struct{}, len(defaultDroppedBetasSet)+len(extra))
...
...
@@ -5976,7 +6004,7 @@ func (s *GatewayService) resolveBedrockBetaTokensForRequest(
modelID string,
) ([]string, error) {
// 1. 对原始 header 中的 beta token 做 block 检查(快速失败)
policy
:=
s
.
evaluateBetaPolicy
(
ctx
,
betaHeader
,
account
)
policy := s.evaluateBetaPolicy(ctx, betaHeader, account
, modelID
)
if policy.blockErr != nil {
return nil, policy.blockErr
}
...
...
@@ -5988,7 +6016,7 @@ func (s *GatewayService) resolveBedrockBetaTokensForRequest(
// 例如:管理员 block 了 interleaved-thinking,客户端不在 header 中带该 token,
// 但请求体中包含 thinking 字段 → autoInjectBedrockBetaTokens 会自动补齐 →
// 如果不做此检查,block 规则会被绕过。
if
blockErr
:=
s
.
checkBetaPolicyBlockForTokens
(
ctx
,
betaTokens
,
account
);
blockErr
!=
nil
{
if blockErr := s.checkBetaPolicyBlockForTokens(ctx, betaTokens, account
, modelID
); blockErr != nil {
return nil, blockErr
}
...
...
@@ -5997,7 +6025,7 @@ func (s *GatewayService) resolveBedrockBetaTokensForRequest(
// checkBetaPolicyBlockForTokens 检查 token 列表中是否有被管理员 block 规则命中的 token。
// 用于补充 evaluateBetaPolicy 对 header 的检查,覆盖 body 自动注入的 token。
func
(
s
*
GatewayService
)
checkBetaPolicyBlockForTokens
(
ctx
context
.
Context
,
tokens
[]
string
,
account
*
Account
)
*
BetaBlockedError
{
func (s *GatewayService) checkBetaPolicyBlockForTokens(ctx context.Context, tokens []string, account *Account
, model string
) *BetaBlockedError {
if s.settingService == nil || len(tokens) == 0 {
return nil
}
...
...
@@ -6009,14 +6037,15 @@ func (s *GatewayService) checkBetaPolicyBlockForTokens(ctx context.Context, toke
isBedrock := account.IsBedrock()
tokenSet := buildBetaTokenSet(tokens)
for _, rule := range settings.Rules {
if
rule
.
Action
!=
BetaPolicyActionBlock
{
effectiveAction, effectiveErrMsg := resolveRuleAction(rule, model)
if effectiveAction != BetaPolicyActionBlock {
continue
}
if !betaPolicyScopeMatches(rule.Scope, isOAuth, isBedrock) {
continue
}
if _, present := tokenSet[rule.BetaToken]; present {
msg
:=
rule
.
ErrorMessage
msg :=
effectiveErrMsg
if msg == "" {
msg = "beta feature " + rule.BetaToken + " is not allowed"
}
...
...
@@ -8474,7 +8503,7 @@ func (s *GatewayService) buildCountTokensRequest(ctx context.Context, c *gin.Con
}
// Build effective drop set for count_tokens: merge static defaults with dynamic beta policy filter rules
ctEffectiveDropSet
:=
mergeDropSets
(
s
.
getBetaPolicyFilterSet
(
ctx
,
c
,
account
))
ctEffectiveDropSet := mergeDropSets(s.getBetaPolicyFilterSet(ctx, c, account
, modelID
))
// OAuth 账号:处理 anthropic-beta header
if tokenType == "oauth" {
...
...
backend/internal/service/setting_service.go
View file @
248fe092
...
...
@@ -1527,6 +1527,18 @@ func (s *SettingService) SetBetaPolicySettings(ctx context.Context, settings *Be
if
!
validScopes
[
rule
.
Scope
]
{
return
fmt
.
Errorf
(
"rule[%d]: invalid scope %q"
,
i
,
rule
.
Scope
)
}
// Validate model_whitelist patterns
for
j
,
pattern
:=
range
rule
.
ModelWhitelist
{
trimmed
:=
strings
.
TrimSpace
(
pattern
)
if
trimmed
==
""
{
return
fmt
.
Errorf
(
"rule[%d]: model_whitelist[%d] cannot be empty"
,
i
,
j
)
}
settings
.
Rules
[
i
]
.
ModelWhitelist
[
j
]
=
trimmed
}
// Validate fallback_action
if
rule
.
FallbackAction
!=
""
&&
!
validActions
[
rule
.
FallbackAction
]
{
return
fmt
.
Errorf
(
"rule[%d]: invalid fallback_action %q"
,
i
,
rule
.
FallbackAction
)
}
}
data
,
err
:=
json
.
Marshal
(
settings
)
...
...
backend/internal/service/settings_view.go
View file @
248fe092
...
...
@@ -182,6 +182,9 @@ type BetaPolicyRule struct {
Action
string
`json:"action"`
// "pass" | "filter" | "block"
Scope
string
`json:"scope"`
// "all" | "oauth" | "apikey" | "bedrock"
ErrorMessage
string
`json:"error_message,omitempty"`
// 自定义错误消息 (action=block 时生效)
ModelWhitelist
[]
string
`json:"model_whitelist,omitempty"`
// 模型匹配模式列表(为空=对所有模型生效)
FallbackAction
string
`json:"fallback_action,omitempty"`
// 未匹配白名单的模型的处理方式
FallbackErrorMessage
string
`json:"fallback_error_message,omitempty"`
// 未匹配白名单时的自定义错误消息 (fallback_action=block 时生效)
}
// BetaPolicySettings Beta 策略配置
...
...
frontend/src/api/admin/settings.ts
View file @
248fe092
...
...
@@ -359,6 +359,9 @@ export interface BetaPolicyRule {
action
:
'
pass
'
|
'
filter
'
|
'
block
'
scope
:
'
all
'
|
'
oauth
'
|
'
apikey
'
|
'
bedrock
'
error_message
?:
string
model_whitelist
?:
string
[]
fallback_action
?:
'
pass
'
|
'
filter
'
|
'
block
'
fallback_error_message
?:
string
}
/**
...
...
frontend/src/i18n/locales/en.ts
View file @
248fe092
...
...
@@ -4623,7 +4623,19 @@ export default {
errorMessagePlaceholder
:
'
Custom error message when blocked
'
,
errorMessageHint
:
'
Leave empty for default message
'
,
saved
:
'
Beta policy settings saved
'
,
saveFailed
:
'
Failed to save beta policy settings
'
saveFailed
:
'
Failed to save beta policy settings
'
,
modelWhitelist
:
'
Model Whitelist
'
,
modelWhitelistHint
:
'
Leave empty to apply to all models. Supports exact match and wildcard prefix (e.g., claude-opus-*)
'
,
modelPatternPlaceholder
:
'
e.g., claude-opus-* or claude-opus-4-6
'
,
addModelPattern
:
'
Add model pattern
'
,
removePattern
:
'
Remove
'
,
fallbackAction
:
'
Fallback Action
'
,
fallbackActionHint
:
'
Action for models not matching the whitelist
'
,
fallbackErrorMessagePlaceholder
:
'
Custom error message when non-whitelisted models are blocked
'
,
quickPresets
:
'
Quick Presets
'
,
presetOpusOnly
:
'
Opus only for 1M
'
,
presetOpusOnlyDesc
:
'
Pass for Opus, filter others
'
,
commonPatterns
:
'
Common patterns
'
},
saveSettings
:
'
Save Settings
'
,
saving
:
'
Saving...
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
248fe092
...
...
@@ -4787,7 +4787,19 @@ export default {
errorMessagePlaceholder
:
'
拦截时返回的自定义错误消息
'
,
errorMessageHint
:
'
留空则使用默认错误消息
'
,
saved
:
'
Beta 策略设置保存成功
'
,
saveFailed
:
'
保存 Beta 策略设置失败
'
saveFailed
:
'
保存 Beta 策略设置失败
'
,
modelWhitelist
:
'
模型白名单
'
,
modelWhitelistHint
:
'
留空则对所有模型生效。支持精确匹配和通配符前缀(如 claude-opus-*)
'
,
modelPatternPlaceholder
:
'
例如: claude-opus-* 或 claude-opus-4-6
'
,
addModelPattern
:
'
添加模型规则
'
,
removePattern
:
'
移除
'
,
fallbackAction
:
'
未匹配模型处理方式
'
,
fallbackActionHint
:
'
当请求模型不在白名单中时的处理方式
'
,
fallbackErrorMessagePlaceholder
:
'
未匹配模型被拦截时返回的自定义错误消息
'
,
quickPresets
:
'
快捷预设
'
,
presetOpusOnly
:
'
仅 Opus 允许 1M
'
,
presetOpusOnlyDesc
:
'
Opus 透传,其他模型过滤
'
,
commonPatterns
:
'
常用模式
'
},
saveSettings
:
'
保存设置
'
,
saving
:
'
保存中...
'
,
...
...
frontend/src/views/admin/SettingsView.vue
View file @
248fe092
...
...
@@ -630,6 +630,108 @@
{{
t
(
'
admin.settings.betaPolicy.errorMessageHint
'
)
}}
<
/p
>
<
/div
>
<!--
Quick
Presets
(
only
for
tokens
with
presets
)
-->
<
div
v
-
if
=
"
betaPresets[rule.beta_token]?.length
"
class
=
"
mt-3
"
>
<
label
class
=
"
mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400
"
>
{{
t
(
'
admin.settings.betaPolicy.quickPresets
'
)
}}
<
/label
>
<
div
class
=
"
flex flex-wrap gap-2
"
>
<
button
v
-
for
=
"
preset in betaPresets[rule.beta_token]
"
:
key
=
"
preset.label
"
type
=
"
button
"
class
=
"
inline-flex items-center gap-1 rounded-md border border-primary-200 bg-primary-50 px-2.5 py-1 text-xs font-medium text-primary-700 transition-colors hover:bg-primary-100 dark:border-primary-800 dark:bg-primary-900/30 dark:text-primary-300 dark:hover:bg-primary-900/50
"
@
click
=
"
applyBetaPreset(rule, preset)
"
:
title
=
"
preset.description
"
>
{{
preset
.
label
}}
<
/button
>
<
/div
>
<
/div
>
<!--
Model
Whitelist
-->
<
div
class
=
"
mt-3
"
>
<
label
class
=
"
mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400
"
>
{{
t
(
'
admin.settings.betaPolicy.modelWhitelist
'
)
}}
<
/label
>
<
p
class
=
"
mb-2 text-xs text-gray-400 dark:text-gray-500
"
>
{{
t
(
'
admin.settings.betaPolicy.modelWhitelistHint
'
)
}}
<
/p
>
<!--
Existing
patterns
-->
<
div
v
-
for
=
"
(_, index) in (rule.model_whitelist || [])
"
:
key
=
"
index
"
class
=
"
mb-1.5 flex items-center gap-2
"
>
<
input
v
-
model
=
"
rule.model_whitelist![index]
"
type
=
"
text
"
class
=
"
input input-sm flex-1
"
:
placeholder
=
"
t('admin.settings.betaPolicy.modelPatternPlaceholder')
"
/>
<
button
type
=
"
button
"
@
click
=
"
rule.model_whitelist!.splice(index, 1)
"
class
=
"
shrink-0 rounded p-1 text-red-400 transition-colors hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20
"
>
<
svg
class
=
"
h-4 w-4
"
fill
=
"
none
"
viewBox
=
"
0 0 24 24
"
stroke
=
"
currentColor
"
stroke
-
width
=
"
2
"
>
<
path
stroke
-
linecap
=
"
round
"
stroke
-
linejoin
=
"
round
"
d
=
"
M6 18L18 6M6 6l12 12
"
/>
<
/svg
>
<
/button
>
<
/div
>
<!--
Add
pattern
button
-->
<
button
type
=
"
button
"
@
click
=
"
if (!rule.model_whitelist) rule.model_whitelist = []; rule.model_whitelist.push('')
"
class
=
"
mb-2 inline-flex items-center gap-1 text-xs text-primary-600 transition-colors hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300
"
>
<
svg
class
=
"
h-3.5 w-3.5
"
fill
=
"
none
"
viewBox
=
"
0 0 24 24
"
stroke
=
"
currentColor
"
stroke
-
width
=
"
2
"
>
<
path
stroke
-
linecap
=
"
round
"
stroke
-
linejoin
=
"
round
"
d
=
"
M12 4v16m8-8H4
"
/>
<
/svg
>
{{
t
(
'
admin.settings.betaPolicy.addModelPattern
'
)
}}
<
/button
>
<!--
Common
pattern
chips
-->
<
div
class
=
"
flex flex-wrap items-center gap-1.5
"
>
<
span
class
=
"
text-xs text-gray-400 dark:text-gray-500
"
>
{{
t
(
'
admin.settings.betaPolicy.commonPatterns
'
)
}}
:
<
/span
>
<
button
v
-
for
=
"
pattern in commonModelPatterns
"
:
key
=
"
pattern
"
type
=
"
button
"
class
=
"
rounded border border-gray-200 px-2 py-0.5 text-xs text-gray-600 transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:border-dark-600 dark:text-gray-400 dark:hover:border-primary-700 dark:hover:bg-primary-900/30 dark:hover:text-primary-300
"
@
click
=
"
addQuickPattern(rule, pattern)
"
>
{{
pattern
}}
<
/button
>
<
/div
>
<
/div
>
<!--
Fallback
Action
(
only
when
model_whitelist
is
non
-
empty
)
-->
<
div
v
-
if
=
"
rule.model_whitelist && rule.model_whitelist.length > 0
"
class
=
"
mt-3
"
>
<
label
class
=
"
mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400
"
>
{{
t
(
'
admin.settings.betaPolicy.fallbackAction
'
)
}}
<
/label
>
<
Select
:
modelValue
=
"
rule.fallback_action || 'pass'
"
@
update
:
modelValue
=
"
rule.fallback_action = $event as any
"
:
options
=
"
betaPolicyActionOptions
"
/>
<
p
class
=
"
mt-1 text-xs text-gray-400 dark:text-gray-500
"
>
{{
t
(
'
admin.settings.betaPolicy.fallbackActionHint
'
)
}}
<
/p
>
<!--
Fallback
Error
Message
(
only
when
fallback_action
=
block
)
-->
<
div
v
-
if
=
"
rule.fallback_action === 'block'
"
class
=
"
mt-2
"
>
<
input
v
-
model
=
"
rule.fallback_error_message
"
type
=
"
text
"
class
=
"
input
"
:
placeholder
=
"
t('admin.settings.betaPolicy.fallbackErrorMessagePlaceholder')
"
/>
<
p
class
=
"
mt-1 text-xs text-gray-400 dark:text-gray-500
"
>
{{
t
(
'
admin.settings.betaPolicy.errorMessageHint
'
)
}}
<
/p
>
<
/div
>
<
/div
>
<
/div
>
<!--
Save
Button
-->
...
...
@@ -2058,6 +2160,9 @@ const betaPolicyForm = reactive({
action
:
'
pass
'
|
'
filter
'
|
'
block
'
scope
:
'
all
'
|
'
oauth
'
|
'
apikey
'
|
'
bedrock
'
error_message
?:
string
model_whitelist
?:
string
[]
fallback_action
?:
'
pass
'
|
'
filter
'
|
'
block
'
fallback_error_message
?:
string
}
>
}
)
...
...
@@ -2716,10 +2821,48 @@ const betaDisplayNames: Record<string, string> = {
'
context-1m-2025-08-07
'
:
'
Context 1M
'
}
// 快捷预设:按 beta_token 定义预设方案
const
betaPresets
:
Record
<
string
,
Array
<
{
label
:
string
description
:
string
action
:
'
pass
'
|
'
filter
'
|
'
block
'
model_whitelist
:
string
[]
fallback_action
:
'
pass
'
|
'
filter
'
|
'
block
'
}
>>
=
{
'
context-1m-2025-08-07
'
:
[
{
label
:
t
(
'
admin.settings.betaPolicy.presetOpusOnly
'
),
description
:
t
(
'
admin.settings.betaPolicy.presetOpusOnlyDesc
'
),
action
:
'
pass
'
,
model_whitelist
:
[
'
claude-opus-4-6
'
],
fallback_action
:
'
filter
'
,
}
,
],
}
// 常用模型模式(具体 ID + 通配符示例)
const
commonModelPatterns
=
[
'
claude-opus-4-6
'
,
'
claude-sonnet-4-6
'
,
'
claude-opus-*
'
,
'
claude-sonnet-*
'
]
function
getBetaDisplayName
(
token
:
string
):
string
{
return
betaDisplayNames
[
token
]
||
token
}
function
applyBetaPreset
(
rule
:
(
typeof
betaPolicyForm
.
rules
)[
number
],
preset
:
{
action
:
'
pass
'
|
'
filter
'
|
'
block
'
;
model_whitelist
:
string
[];
fallback_action
:
'
pass
'
|
'
filter
'
|
'
block
'
}
)
{
rule
.
action
=
preset
.
action
rule
.
model_whitelist
=
[...
preset
.
model_whitelist
]
rule
.
fallback_action
=
preset
.
fallback_action
}
function
addQuickPattern
(
rule
:
(
typeof
betaPolicyForm
.
rules
)[
number
],
pattern
:
string
)
{
if
(
!
rule
.
model_whitelist
)
rule
.
model_whitelist
=
[]
if
(
!
rule
.
model_whitelist
.
includes
(
pattern
))
{
rule
.
model_whitelist
.
push
(
pattern
)
}
}
async
function
loadBetaPolicySettings
()
{
betaPolicyLoading
.
value
=
true
try
{
...
...
@@ -2735,8 +2878,22 @@ async function loadBetaPolicySettings() {
async
function
saveBetaPolicySettings
()
{
betaPolicySaving
.
value
=
true
try
{
// Clean up empty patterns before saving
const
cleanedRules
=
betaPolicyForm
.
rules
.
map
(
rule
=>
{
const
whitelist
=
rule
.
model_whitelist
?.
filter
(
p
=>
p
.
trim
()
!==
''
)
const
hasWhitelist
=
whitelist
&&
whitelist
.
length
>
0
return
{
beta_token
:
rule
.
beta_token
,
action
:
rule
.
action
,
scope
:
rule
.
scope
,
error_message
:
rule
.
error_message
,
model_whitelist
:
hasWhitelist
?
whitelist
:
undefined
,
fallback_action
:
hasWhitelist
?
(
rule
.
fallback_action
||
'
pass
'
)
:
undefined
,
fallback_error_message
:
hasWhitelist
&&
rule
.
fallback_action
===
'
block
'
?
rule
.
fallback_error_message
:
undefined
,
}
}
)
const
updated
=
await
adminAPI
.
settings
.
updateBetaPolicySettings
({
rules
:
betaPolicyForm
.
r
ules
rules
:
cleanedR
ules
}
)
betaPolicyForm
.
rules
=
updated
.
rules
appStore
.
showSuccess
(
t
(
'
admin.settings.betaPolicy.saved
'
))
...
...
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