import { Settings2 } from 'lucide-react'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Field, FieldDescription, FieldGroup, FieldLabel, } from '@/components/ui/field'; import { Input } from '@/components/ui/input'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Separator } from '@/components/ui/separator'; import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import type { WAFIPGroup, WAFRuleNode } from '@/lib/services/openflare'; import { countryOptions, regionOptions, type GeoOption } from './geo-options'; import { NODE_TYPE_LABELS } from './node-factory'; import { UA_BROWSER_OPTIONS, UA_OS_OPTIONS } from './ua-options'; export function NodeProperties({ node, ipGroups, onChange, }: { node: WAFRuleNode; ipGroups: WAFIPGroup[]; onChange: (node: WAFRuleNode) => void; }) { return ( ); } function PropertyFields({ node, ipGroups, onChange, }: { node: WAFRuleNode; ipGroups: WAFIPGroup[]; onChange: (node: WAFRuleNode) => void; }) { if (node.type === 'start' || node.type === 'allow') return

系统节点无需配置。

; if (node.type === 'ip_match') return ( onChange({ ...node, config: { ...node.config, ips } }) } /> onChange({ ...node, config: { ...node.config, cidrs } }) } /> ({ value: String(group.id), label: group.name, searchText: `${group.name} ${group.id}`, }))} value={node.config.ip_group_ids.map(String)} onChange={(values) => onChange({ ...node, config: { ...node.config, ip_group_ids: values.map(Number) }, }) } /> ); if (node.type === 'geo_match') return ( onChange({ ...node, config: { ...node.config, countries } }) } /> onChange({ ...node, config: { ...node.config, regions } }) } /> ); if (node.type === 'ua_check') return (

UA 检查

开启 UA 检查 开启后如果请求头不携带 UA 返回 False
onChange({ ...node, config: { ...node.config, require_ua } }) } />

UA 匹配

匹配模式 浏览器与操作系统两侧都有选择时生效 ({ value: option.value, label: option.label, searchText: `${option.label} ${option.value}`, }))} value={node.config.browsers} onChange={(browsers) => onChange({ ...node, config: { ...node.config, browsers } }) } /> ({ value: option.value, label: option.label, searchText: `${option.label} ${option.value}`, }))} value={node.config.operating_systems} onChange={(operating_systems) => onChange({ ...node, config: { ...node.config, operating_systems }, }) } />

屏蔽

命中返回 false,优先级高于匹配
屏蔽常见爬虫 UA onChange({ ...node, config: { ...node.config, block_common_bots }, }) } /> 屏蔽非正常 UA onChange({ ...node, config: { ...node.config, block_abnormal_ua }, }) } />
); if (node.type === 'pow') return ( 算法 {(['difficulty', 'session_ttl', 'challenge_ttl'] as const).map( (key) => ( onChange({ ...node, config: { ...node.config, [key]: value } }) } /> ), )} ); return ( onChange({ ...node, config: { ...node.config, status_code } }) } /> HTML 响应体