This commit is contained in:
ryan
2026-07-13 15:08:40 +08:00
parent 08e8eea932
commit 1eff7878a1
441 changed files with 26491 additions and 18405 deletions
@@ -1,37 +1,74 @@
import {Handle, Position, type NodeProps} from '@xyflow/react';
import {Ban, Fingerprint, Flag, Globe2, Play, ShieldCheck} from 'lucide-react';
import { Handle, Position, type NodeProps } from '@xyflow/react';
import {
Ban,
Fingerprint,
Flag,
Globe2,
Play,
ShieldCheck,
} from 'lucide-react';
import {Badge} from '@/components/ui/badge';
import {cn} from '@/lib/utils';
import type {WAFRuleNode} from '@/lib/services/openflare';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
import type { WAFRuleNode } from '@/lib/services/openflare';
export interface RuleFlowNodeData extends Record<string, unknown> { rule: WAFRuleNode; issues: number }
export interface RuleFlowNodeData extends Record<string, unknown> {
rule: WAFRuleNode;
issues: number;
}
const meta = {
start: {label: '开始', icon: Play}, ip_match: {label: 'IP 匹配', icon: Fingerprint}, geo_match: {label: '地域匹配', icon: Globe2},
pow: {label: 'PoW 挑战', icon: ShieldCheck}, allow: {label: '通过', icon: Flag}, block: {label: '阻止', icon: Ban},
start: { label: '开始', icon: Play },
ip_match: { label: 'IP 匹配', icon: Fingerprint },
geo_match: { label: '地域匹配', icon: Globe2 },
pow: { label: 'PoW 挑战', icon: ShieldCheck },
allow: { label: '通过', icon: Flag },
block: { label: '阻止', icon: Ban },
} as const;
const outputHandles: Partial<Record<WAFRuleNode['type'], string[]>> = {start: ['next'], ip_match: ['true', 'false'], geo_match: ['true', 'false'], pow: ['next']};
const outputHandles: Partial<Record<WAFRuleNode['type'], string[]>> = {
start: ['next'],
ip_match: ['true', 'false'],
geo_match: ['true', 'false'],
pow: ['next'],
};
export function RuleNode({data, selected}: NodeProps) {
export function RuleNode({ data, selected }: NodeProps) {
const value = data as RuleFlowNodeData;
const {rule, issues} = value;
const {label, icon: Icon} = meta[rule.type];
const { rule, issues } = value;
const { label, icon: Icon } = meta[rule.type];
return (
<div className={cn('min-w-44 rounded-lg border bg-card shadow-sm transition-shadow', selected && 'ring-2 ring-ring', issues > 0 && 'border-destructive')}>
{rule.type !== 'start' && <Handle type="target" position={Position.Left} />}
<div className="flex items-center gap-3 px-4 py-3">
<Icon className="size-5 text-primary" />
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
<span className="text-sm font-medium">{label}</span>
<span className="font-mono text-[10px] text-muted-foreground">{rule.id}</span>
<div
className={cn(
'min-w-44 rounded-lg border bg-card shadow-sm transition-shadow',
selected && 'ring-2 ring-ring',
issues > 0 && 'border-destructive',
)}
>
{rule.type !== 'start' && (
<Handle type='target' position={Position.Left} />
)}
<div className='flex items-center gap-3 px-4 py-3'>
<Icon className='size-5 text-primary' />
<div className='flex min-w-0 flex-1 flex-col gap-0.5'>
<span className='text-sm font-medium'>{label}</span>
<span className='font-mono text-[10px] text-muted-foreground'>
{rule.id}
</span>
</div>
{issues > 0 && <Badge variant="destructive">{issues}</Badge>}
{issues > 0 && <Badge variant='destructive'>{issues}</Badge>}
</div>
{(outputHandles[rule.type] ?? []).map((handle, index, all) => (
<Handle key={handle} id={handle} type="source" position={Position.Right} style={{top: `${((index + 1) / (all.length + 1)) * 100}%`}}>
<span className="absolute right-3 -translate-y-1/2 text-[9px] text-muted-foreground">{handle}</span>
<Handle
key={handle}
id={handle}
type='source'
position={Position.Right}
style={{ top: `${((index + 1) / (all.length + 1)) * 100}%` }}
>
<span className='absolute right-3 -translate-y-1/2 text-[9px] text-muted-foreground'>
{handle}
</span>
</Handle>
))}
</div>