Refactor frontend pages and config components into single-component files for Task 13
This commit is contained in:
157
frontend/src/components/config/JailSectionPanel.tsx
Normal file
157
frontend/src/components/config/JailSectionPanel.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import { useCallback } from "react";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionHeader,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Field,
|
||||
Input,
|
||||
Select,
|
||||
Switch,
|
||||
} from "@fluentui/react-components";
|
||||
import { KVEditor } from "./KVEditor";
|
||||
import { StringListEditor } from "./StringListEditor";
|
||||
import { useConfigStyles } from "./configStyles";
|
||||
import type { JailSectionConfig } from "../../types/config";
|
||||
|
||||
const BACKENDS = ["", "auto", "polling", "gamin", "pyinotify", "systemd"] as const;
|
||||
|
||||
interface JailSectionPanelProps {
|
||||
jailName: string;
|
||||
section: JailSectionConfig;
|
||||
onChange: (next: JailSectionConfig) => void;
|
||||
}
|
||||
|
||||
export function JailSectionPanel({ jailName, section, onChange }: JailSectionPanelProps): React.JSX.Element {
|
||||
const styles = useConfigStyles();
|
||||
|
||||
const update = useCallback(
|
||||
(patch: Partial<JailSectionConfig>): void => {
|
||||
onChange({ ...section, ...patch });
|
||||
},
|
||||
[onChange, section],
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.sectionCard}>
|
||||
<div className={styles.fieldRow}>
|
||||
<Field label="Enabled">
|
||||
<Switch
|
||||
checked={section.enabled ?? false}
|
||||
onChange={(_e, d) => { update({ enabled: d.checked }); }}
|
||||
aria-label={`Enable jail ${jailName}`}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Port">
|
||||
<Input
|
||||
value={section.port ?? ""}
|
||||
size="small"
|
||||
placeholder="e.g. ssh or 22"
|
||||
onChange={(_e, d) => { update({ port: d.value || null }); }}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Filter">
|
||||
<Input
|
||||
value={section.filter ?? ""}
|
||||
size="small"
|
||||
placeholder="e.g. sshd"
|
||||
className={styles.codeInput}
|
||||
onChange={(_e, d) => { update({ filter: d.value || null }); }}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Backend">
|
||||
<Select
|
||||
size="small"
|
||||
value={section.backend ?? ""}
|
||||
onChange={(_e, d) => { update({ backend: d.value || null }); }}
|
||||
>
|
||||
{BACKENDS.map((b) => (
|
||||
<option key={b} value={b}>
|
||||
{b === "" ? "(default)" : b}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</Field>
|
||||
<Field label="Max Retry">
|
||||
<Input
|
||||
value={section.maxretry !== null ? String(section.maxretry) : ""}
|
||||
size="small"
|
||||
type="number"
|
||||
placeholder="e.g. 5"
|
||||
onChange={(_e, d) => {
|
||||
const n = parseInt(d.value, 10);
|
||||
update({ maxretry: d.value === "" ? null : isNaN(n) ? null : n });
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Find Time (s)">
|
||||
<Input
|
||||
value={section.findtime !== null ? String(section.findtime) : ""}
|
||||
size="small"
|
||||
type="number"
|
||||
placeholder="e.g. 600"
|
||||
onChange={(_e, d) => {
|
||||
const n = parseInt(d.value, 10);
|
||||
update({ findtime: d.value === "" ? null : isNaN(n) ? null : n });
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Ban Time (s)">
|
||||
<Input
|
||||
value={section.bantime !== null ? String(section.bantime) : ""}
|
||||
size="small"
|
||||
type="number"
|
||||
placeholder="e.g. 3600"
|
||||
onChange={(_e, d) => {
|
||||
const n = parseInt(d.value, 10);
|
||||
update({ bantime: d.value === "" ? null : isNaN(n) ? null : n });
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Accordion multiple collapsible defaultOpenItems={["logpath"]}>
|
||||
<AccordionItem value="logpath" className={styles.accordionItem}>
|
||||
<AccordionHeader>Log Paths ({section.logpath.length})</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<div className={styles.sectionCard}>
|
||||
<StringListEditor
|
||||
items={section.logpath}
|
||||
onChange={(next) => { update({ logpath: next }); }}
|
||||
placeholder="e.g. /var/log/auth.log"
|
||||
addLabel="Add log path"
|
||||
/>
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="actions" className={styles.accordionItem}>
|
||||
<AccordionHeader>Actions ({section.action.length})</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<div className={styles.sectionCard}>
|
||||
<StringListEditor
|
||||
items={section.action}
|
||||
onChange={(next) => { update({ action: next }); }}
|
||||
placeholder="e.g. iptables-multiport[name=SSH, port=ssh]"
|
||||
addLabel="Add action"
|
||||
/>
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
||||
{Object.keys(section.extra).length > 0 && (
|
||||
<AccordionItem value="extra" className={styles.accordionItem}>
|
||||
<AccordionHeader>Extra Settings ({Object.keys(section.extra).length})</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<div className={styles.sectionCard}>
|
||||
<KVEditor entries={section.extra} onChange={(next) => { update({ extra: next }); }} />
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user