feat: add LocationRulesFields UI component and form wiring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ import { GeoBlockFields } from "./GeoBlockFields";
|
||||
import { WafFields } from "./WafFields";
|
||||
import { MtlsFields } from "./MtlsConfig";
|
||||
import { RedirectsFields } from "./RedirectsFields";
|
||||
import { LocationRulesFields } from "./LocationRulesFields";
|
||||
import { RewriteFields } from "./RewriteFields";
|
||||
import type { CaCertificate } from "@/lib/models/ca-certificates";
|
||||
|
||||
@@ -133,6 +134,7 @@ export function CreateHostDialog({
|
||||
</Select>
|
||||
</div>
|
||||
<RedirectsFields initialData={initialData?.redirects} />
|
||||
<LocationRulesFields initialData={initialData?.location_rules} />
|
||||
<RewriteFields initialData={initialData?.rewrite} />
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-1 block">Custom Pre-Handlers (JSON)</label>
|
||||
@@ -263,6 +265,7 @@ export function EditHostDialog({
|
||||
</Select>
|
||||
</div>
|
||||
<RedirectsFields initialData={host.redirects} />
|
||||
<LocationRulesFields initialData={host.location_rules} />
|
||||
<RewriteFields initialData={host.rewrite} />
|
||||
<div>
|
||||
<label className="text-sm font-medium mb-1 block">Custom Pre-Handlers (JSON)</label>
|
||||
|
||||
91
src/components/proxy-hosts/LocationRulesFields.tsx
Normal file
91
src/components/proxy-hosts/LocationRulesFields.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Trash2, Plus } from "lucide-react";
|
||||
import type { LocationRule } from "@/lib/models/proxy-hosts";
|
||||
|
||||
type Props = { initialData?: LocationRule[] };
|
||||
|
||||
export function LocationRulesFields({ initialData = [] }: Props) {
|
||||
const [rules, setRules] = useState<LocationRule[]>(initialData);
|
||||
|
||||
const addRule = () =>
|
||||
setRules((r) => [...r, { path: "", upstreams: [] }]);
|
||||
|
||||
const removeRule = (i: number) =>
|
||||
setRules((r) => r.filter((_, idx) => idx !== i));
|
||||
|
||||
const updatePath = (i: number, value: string) =>
|
||||
setRules((r) => r.map((rule, idx) => (idx === i ? { ...rule, path: value } : rule)));
|
||||
|
||||
const updateUpstreams = (i: number, value: string) =>
|
||||
setRules((r) =>
|
||||
r.map((rule, idx) =>
|
||||
idx === i
|
||||
? {
|
||||
...rule,
|
||||
upstreams: value
|
||||
.split("\n")
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean),
|
||||
}
|
||||
: rule
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p className="text-sm font-semibold mb-2">Location Rules</p>
|
||||
<input type="hidden" name="location_rules_json" value={JSON.stringify(rules)} />
|
||||
{rules.length > 0 && (
|
||||
<div className="mb-2 flex flex-col gap-3">
|
||||
{rules.map((rule, i) => (
|
||||
<div key={i} className="grid grid-cols-[1fr_1fr_40px] gap-2 items-start">
|
||||
<div>
|
||||
{i === 0 && (
|
||||
<span className="text-xs font-medium text-muted-foreground px-1 mb-1 block">Path Pattern</span>
|
||||
)}
|
||||
<Input
|
||||
size={1}
|
||||
placeholder="/ws/*"
|
||||
value={rule.path}
|
||||
onChange={(e) => updatePath(i, e.target.value)}
|
||||
className="h-8 text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{i === 0 && (
|
||||
<span className="text-xs font-medium text-muted-foreground px-1 mb-1 block">Upstreams</span>
|
||||
)}
|
||||
<Textarea
|
||||
placeholder={"ws-backend:8080\nws-backend2:8080"}
|
||||
value={rule.upstreams.join("\n")}
|
||||
onChange={(e) => updateUpstreams(i, e.target.value)}
|
||||
className="text-sm min-h-[32px]"
|
||||
rows={Math.max(1, rule.upstreams.length)}
|
||||
/>
|
||||
</div>
|
||||
<div className={i === 0 ? "mt-5" : ""}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => removeRule(i)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<Button type="button" variant="ghost" size="sm" onClick={addRule}>
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
Add Location Rule
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user