feat: channel test (#6917)

* feat: channel test

* fix: code smell
This commit is contained in:
Seefs
2026-08-18 18:03:59 +08:00
committed by GitHub
parent 2b0efd8484
commit 4add708ebe
17 changed files with 454 additions and 118 deletions
@@ -335,6 +335,7 @@ export function ModelMutateDrawer({
'100-199,300-399,401-407,409-499,500-503,505-523,525-599',
'monitor_setting.auto_test_channel_enabled': false,
'monitor_setting.auto_test_channel_minutes': 10,
'monitor_setting.channel_test_concurrency': 1,
'monitor_setting.channel_test_mode': 'scheduled_all',
'channel_affinity_setting.enabled': false,
'channel_affinity_setting.switch_on_success': true,
@@ -73,6 +73,7 @@ const defaultModelSettings: ModelSettings = {
'100-199,300-399,401-407,409-499,500-503,505-523,525-599',
'monitor_setting.auto_test_channel_enabled': false,
'monitor_setting.auto_test_channel_minutes': 10,
'monitor_setting.channel_test_concurrency': 1,
'monitor_setting.channel_test_mode': 'scheduled_all',
'channel_affinity_setting.enabled': false,
'channel_affinity_setting.switch_on_success': true,
@@ -69,55 +69,70 @@ const channelTestModes = [
'passive_recovery',
] as const
type ChannelTestMode = (typeof channelTestModes)[number]
const MAX_CHANNEL_TEST_CONCURRENCY = 32
const routingReliabilitySchema = z
.object({
RetryTimes: z.coerce.number().min(0).max(10),
ChannelDisableThreshold: numericString,
AutomaticDisableChannelEnabled: z.boolean(),
AutomaticEnableChannelEnabled: z.boolean(),
AutomaticDisableKeywords: z.string(),
AutomaticDisableStatusCodes: z.string(),
AutomaticRetryStatusCodes: z.string(),
monitor_setting: z.object({
auto_test_channel_enabled: z.boolean(),
auto_test_channel_minutes: z.coerce
.number()
.int()
.min(1, 'Interval must be at least 1 minute'),
channel_test_mode: z.enum(channelTestModes),
}),
})
.superRefine((values, ctx) => {
const disableParsed = parseHttpStatusCodeRules(
values.AutomaticDisableStatusCodes
)
if (!disableParsed.ok) {
ctx.addIssue({
code: 'custom',
path: ['AutomaticDisableStatusCodes'],
message: `Invalid status code rules: ${disableParsed.invalidTokens.join(
', '
)}`,
})
}
const createRoutingReliabilitySchema = (
t: (key: string, options?: Record<string, unknown>) => string
) =>
z
.object({
RetryTimes: z.coerce.number().min(0).max(10),
ChannelDisableThreshold: numericString,
AutomaticDisableChannelEnabled: z.boolean(),
AutomaticEnableChannelEnabled: z.boolean(),
AutomaticDisableKeywords: z.string(),
AutomaticDisableStatusCodes: z.string(),
AutomaticRetryStatusCodes: z.string(),
monitor_setting: z.object({
auto_test_channel_enabled: z.boolean(),
auto_test_channel_minutes: z.coerce
.number()
.int()
.min(1, t('Interval must be at least 1 minute')),
channel_test_concurrency: z.coerce
.number()
.int(t('Enter a positive integer'))
.min(1, t('Channel test concurrency must be between 1 and 32'))
.max(
MAX_CHANNEL_TEST_CONCURRENCY,
t('Channel test concurrency must be between 1 and 32')
),
channel_test_mode: z.enum(channelTestModes),
}),
})
.superRefine((values, ctx) => {
const disableParsed = parseHttpStatusCodeRules(
values.AutomaticDisableStatusCodes
)
if (!disableParsed.ok) {
ctx.addIssue({
code: 'custom',
path: ['AutomaticDisableStatusCodes'],
message: t('Invalid status code rules: {{tokens}}', {
tokens: disableParsed.invalidTokens.join(', '),
}),
})
}
const retryParsed = parseHttpStatusCodeRules(
values.AutomaticRetryStatusCodes
)
if (!retryParsed.ok) {
ctx.addIssue({
code: 'custom',
path: ['AutomaticRetryStatusCodes'],
message: `Invalid status code rules: ${retryParsed.invalidTokens.join(
', '
)}`,
})
}
})
const retryParsed = parseHttpStatusCodeRules(
values.AutomaticRetryStatusCodes
)
if (!retryParsed.ok) {
ctx.addIssue({
code: 'custom',
path: ['AutomaticRetryStatusCodes'],
message: t('Invalid status code rules: {{tokens}}', {
tokens: retryParsed.invalidTokens.join(', '),
}),
})
}
})
type RoutingReliabilityFormValues = z.output<typeof routingReliabilitySchema>
type RoutingReliabilityFormInput = z.input<typeof routingReliabilitySchema>
type RoutingReliabilitySchema = ReturnType<
typeof createRoutingReliabilitySchema
>
type RoutingReliabilityFormValues = z.output<RoutingReliabilitySchema>
type RoutingReliabilityFormInput = z.input<RoutingReliabilitySchema>
type RoutingReliabilitySectionProps = {
defaultValues: {
@@ -130,6 +145,7 @@ type RoutingReliabilitySectionProps = {
AutomaticRetryStatusCodes: string
'monitor_setting.auto_test_channel_enabled': boolean
'monitor_setting.auto_test_channel_minutes': number
'monitor_setting.channel_test_concurrency': number
'monitor_setting.channel_test_mode': ChannelTestMode
}
}
@@ -148,6 +164,7 @@ type NormalizedRoutingReliabilityValues = {
AutomaticRetryStatusCodes: string
'monitor_setting.auto_test_channel_enabled': boolean
'monitor_setting.auto_test_channel_minutes': number
'monitor_setting.channel_test_concurrency': number
'monitor_setting.channel_test_mode': ChannelTestMode
}
@@ -175,6 +192,8 @@ const buildFormDefaults = (
defaults['monitor_setting.auto_test_channel_enabled'],
auto_test_channel_minutes:
defaults['monitor_setting.auto_test_channel_minutes'],
channel_test_concurrency:
defaults['monitor_setting.channel_test_concurrency'],
channel_test_mode: normalizeChannelTestMode(
defaults['monitor_setting.channel_test_mode']
),
@@ -201,6 +220,8 @@ const normalizeDefaults = (
defaults['monitor_setting.auto_test_channel_enabled'],
'monitor_setting.auto_test_channel_minutes':
defaults['monitor_setting.auto_test_channel_minutes'],
'monitor_setting.channel_test_concurrency':
defaults['monitor_setting.channel_test_concurrency'],
'monitor_setting.channel_test_mode': normalizeChannelTestMode(
defaults['monitor_setting.channel_test_mode']
),
@@ -226,6 +247,8 @@ const normalizeFormValues = (
values.monitor_setting.auto_test_channel_enabled,
'monitor_setting.auto_test_channel_minutes':
values.monitor_setting.auto_test_channel_minutes,
'monitor_setting.channel_test_concurrency':
values.monitor_setting.channel_test_concurrency,
'monitor_setting.channel_test_mode': values.monitor_setting.channel_test_mode,
})
@@ -234,6 +257,7 @@ export function RoutingReliabilitySection({
}: RoutingReliabilitySectionProps) {
const { t } = useTranslation()
const updateOption = useUpdateOption()
const routingReliabilitySchema = createRoutingReliabilitySchema(t)
const baselineRef = useRef<NormalizedRoutingReliabilityValues>(
normalizeDefaults(defaultValues)
)
@@ -484,6 +508,31 @@ export function RoutingReliabilitySection({
)}
/>
<FormField
control={form.control}
name='monitor_setting.channel_test_concurrency'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Channel test concurrency')}</FormLabel>
<FormControl>
<Input
type='number'
min={1}
max={MAX_CHANNEL_TEST_CONCURRENCY}
step={1}
{...safeNumberFieldProps(field)}
/>
</FormControl>
<FormDescription>
{t(
'Maximum number of channels tested at the same time (1-32)'
)}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='AutomaticEnableChannelEnabled'
@@ -83,6 +83,8 @@ const MODELS_SECTIONS = [
settings['monitor_setting.auto_test_channel_enabled'],
'monitor_setting.auto_test_channel_minutes':
settings['monitor_setting.auto_test_channel_minutes'],
'monitor_setting.channel_test_concurrency':
settings['monitor_setting.channel_test_concurrency'],
'monitor_setting.channel_test_mode':
settings['monitor_setting.channel_test_mode'],
}}
@@ -235,6 +235,7 @@ export type ModelSettings = {
AutomaticRetryStatusCodes: string
'monitor_setting.auto_test_channel_enabled': boolean
'monitor_setting.auto_test_channel_minutes': number
'monitor_setting.channel_test_concurrency': number
'monitor_setting.channel_test_mode':
| 'scheduled_all'
| 'auto_ban_only'