feat(rate-limit): 站点级请求频率限制支持继承与自定义

在站点详情流量限制中配置 limit_req_per_ip;渲染按 effective rate 生成多 limit_req_zone,并以站点+IP 隔离计数。
This commit is contained in:
ryan
2026-07-20 15:02:38 +08:00
parent a261c01a9c
commit 80c47f6ff3
8 changed files with 330 additions and 15 deletions
@@ -278,6 +278,25 @@ export function normalizeLimitRate(value: string) {
return normalized;
}
const limitReqPattern = /^\d+r\/[sm]$/i;
export function validateLimitReqPerIP(value: string) {
const normalized = value.trim();
if (!normalized || normalized === '0' || normalized === '-1') {
return null;
}
if (!limitReqPattern.test(normalized)) {
return '请求频率格式不合法,请使用 10r/s、100r/m,或 -1 关闭';
}
return null;
}
export function normalizeLimitReqPerIP(value: string) {
const normalized = value.trim().toLowerCase();
if (!normalized || normalized === '0') return '';
return normalized;
}
export function validateCacheRules(
policy: 'static' | 'all' | 'url' | 'suffix' | 'path_prefix' | 'path_exact',
rules: string[],
@@ -335,6 +354,7 @@ export function buildPayloadFromRoute(
limit_conn_per_server: route.limit_conn_per_server,
limit_conn_per_ip: route.limit_conn_per_ip,
limit_rate: route.limit_rate,
limit_req_per_ip: route.limit_req_per_ip,
cache_enabled: route.cache_enabled,
cache_policy: (() => {
if (!route.cache_enabled) {
@@ -228,6 +228,7 @@ export function ProxyRouteCreateSheet({
limit_conn_per_server: 0,
limit_conn_per_ip: 0,
limit_rate: '',
limit_req_per_ip: '',
cache_enabled: true,
cache_policy: 'static',
cache_rules: [],
@@ -19,7 +19,9 @@ import type { ProxyRouteItem } from '@/lib/services/openflare';
import {
normalizeLimitRate,
normalizeLimitReqPerIP,
validateLimitRate,
validateLimitReqPerIP,
} from '../../components/helpers';
import { proxyRouteFormIds } from '../helpers';
import { useRouteSectionSave } from '../hooks/use-route-section-save';
@@ -30,6 +32,7 @@ const rateLimitSchema = z
limit_conn_per_server: z.string(),
limit_conn_per_ip: z.string(),
limit_rate: z.string(),
limit_req_per_ip: z.string(),
})
.superRefine((value, context) => {
for (const field of [
@@ -57,6 +60,15 @@ const rateLimitSchema = z
message: limitRateError,
});
}
const limitReqError = validateLimitReqPerIP(value.limit_req_per_ip);
if (limitReqError) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ['limit_req_per_ip'],
message: limitReqError,
});
}
});
type RateLimitValues = z.infer<typeof rateLimitSchema>;
@@ -99,6 +111,7 @@ export function LimitsSection({
limit_conn_per_server: formatConnValue(route.limit_conn_per_server),
limit_conn_per_ip: formatConnValue(route.limit_conn_per_ip),
limit_rate: route.limit_rate || '',
limit_req_per_ip: route.limit_req_per_ip || '',
},
});
@@ -107,6 +120,7 @@ export function LimitsSection({
limit_conn_per_server: formatConnValue(route.limit_conn_per_server),
limit_conn_per_ip: formatConnValue(route.limit_conn_per_ip),
limit_rate: route.limit_rate || '',
limit_req_per_ip: route.limit_req_per_ip || '',
});
}, [form, route]);
@@ -129,6 +143,9 @@ export function LimitsSection({
),
limit_conn_per_ip: parseConnValue(values.limit_conn_per_ip),
limit_rate: normalizeLimitRate(values.limit_rate),
limit_req_per_ip: normalizeLimitReqPerIP(
values.limit_req_per_ip,
),
},
'流量限制已保存',
);
@@ -172,7 +189,7 @@ export function LimitsSection({
control={form.control}
name='limit_rate'
render={({ field }) => (
<FormItem className='md:col-span-2'>
<FormItem>
<FormLabel>限速</FormLabel>
<FormControl>
<Input placeholder='512k/1m 或 -1' {...field} />
@@ -184,6 +201,24 @@ export function LimitsSection({
</FormItem>
)}
/>
<FormField
control={form.control}
name='limit_req_per_ip'
render={({ field }) => (
<FormItem>
<FormLabel>单 IP 请求频率</FormLabel>
<FormControl>
<Input placeholder='10r/s / 100r/m 或 -1' {...field} />
</FormControl>
<FormDescription>
空或 0 继承全局默认;-1 关闭;例如 10r/s、100r/m
为自定义频率。
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
</SectionShell>
+2
View File
@@ -233,6 +233,7 @@ export interface ProxyRouteItem {
limit_conn_per_server: number;
limit_conn_per_ip: number;
limit_rate: string;
limit_req_per_ip: string;
cache_enabled: boolean;
cache_policy: string;
cache_rules: string;
@@ -269,6 +270,7 @@ export interface ProxyRouteMutationPayload {
limit_conn_per_server?: number;
limit_conn_per_ip?: number;
limit_rate?: string;
limit_req_per_ip?: string;
cache_enabled: boolean;
cache_policy: string;
cache_rules: string[];