mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-05 23:26:38 +08:00
a1a997bcda
Add the React Flow rule editor, ordered graph APIs and runtime DAG execution.\n\nPublish rules only on OpenResty reload and reconcile checksum-driven IP group snapshots in bounded shared memory.
40 lines
2.0 KiB
TypeScript
40 lines
2.0 KiB
TypeScript
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';
|
|
|
|
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},
|
|
} as const;
|
|
|
|
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) {
|
|
const value = data as RuleFlowNodeData;
|
|
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>
|
|
{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>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|