Files
BanGUI/frontend/src/pages/jails/BanUnbanForm.tsx

190 lines
5.5 KiB
TypeScript

import { useState } from "react";
import {
Button,
Field,
Input,
MessageBar,
MessageBarBody,
Select,
Text,
} from "@fluentui/react-components";
import { LockClosedRegular, LockOpenRegular } from "@fluentui/react-icons";
import { useCommonSectionStyles } from "../../components/commonStyles";
import { useJailsPageStyles } from "./jailsPageStyles";
import { ApiError } from "../../api/client";
interface BanUnbanFormProps {
jailNames: string[];
onBan: (jail: string, ip: string) => Promise<void>;
onUnban: (ip: string, jail?: string) => Promise<void>;
}
export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.JSX.Element {
const styles = useJailsPageStyles();
const sectionStyles = useCommonSectionStyles();
const [banIpVal, setBanIpVal] = useState("");
const [banJail, setBanJail] = useState("");
const [unbanIpVal, setUnbanIpVal] = useState("");
const [unbanJail, setUnbanJail] = useState("");
const [formError, setFormError] = useState<string | null>(null);
const [formSuccess, setFormSuccess] = useState<string | null>(null);
const [isBanning, setIsBanning] = useState(false);
const [isUnbanning, setIsUnbanning] = useState(false);
const formatErrorMessage = (err: unknown): string =>
err instanceof ApiError
? `${String(err.status)}: ${err.body}`
: err instanceof Error
? err.message
: String(err);
const handleBan = async (): Promise<void> => {
setFormError(null);
setFormSuccess(null);
if (!banIpVal.trim() || !banJail) {
setFormError("Both IP address and jail are required.");
return;
}
setIsBanning(true);
try {
const ip = banIpVal.trim();
await onBan(banJail, ip);
setFormSuccess(`${ip} banned in ${banJail}.`);
setBanIpVal("");
} catch (err: unknown) {
setFormError(formatErrorMessage(err));
} finally {
setIsBanning(false);
}
};
const handleUnban = async (fromAllJails: boolean): Promise<void> => {
setFormError(null);
setFormSuccess(null);
if (!unbanIpVal.trim()) {
setFormError("IP address is required.");
return;
}
setIsUnbanning(true);
try {
const ip = unbanIpVal.trim();
const jail = fromAllJails ? undefined : unbanJail || undefined;
await onUnban(ip, jail);
const scope = jail ?? "all jails";
setFormSuccess(`${ip} unbanned from ${scope}.`);
setUnbanIpVal("");
setUnbanJail("");
} catch (err: unknown) {
setFormError(formatErrorMessage(err));
} finally {
setIsUnbanning(false);
}
};
return (
<div className={sectionStyles.section}>
<div className={sectionStyles.sectionHeader}>
<Text as="h2" size={500} weight="semibold">
Ban / Unban IP
</Text>
</div>
{formError && (
<MessageBar intent="error">
<MessageBarBody>{formError}</MessageBarBody>
</MessageBar>
)}
{formSuccess && (
<MessageBar intent="success">
<MessageBarBody>{formSuccess}</MessageBarBody>
</MessageBar>
)}
<Text size={300} weight="semibold">
Ban an IP
</Text>
<div className={styles.formRow}>
<div className={styles.formField}>
<Field label="IP Address">
<Input
placeholder="e.g. 192.168.1.100"
value={banIpVal}
onChange={(_, d) => {
setBanIpVal(d.value);
}}
/>
</Field>
</div>
<div className={styles.formField}>
<Field label="Jail">
<Select
value={banJail}
onChange={(_, d) => {
setBanJail(d.value);
}}
>
<option value="">Select jail</option>
{jailNames.map((n) => (
<option key={n} value={n}>
{n}
</option>
))}
</Select>
</Field>
</div>
<Button appearance="primary" icon={<LockClosedRegular />} onClick={() => {
void handleBan();
}} disabled={isBanning}>
Ban
</Button>
</div>
<Text size={300} weight="semibold" className={styles.sectionHeadingSpacing}>
Unban an IP
</Text>
<div className={styles.formRow}>
<div className={styles.formField}>
<Field label="IP Address">
<Input
placeholder="e.g. 192.168.1.100"
value={unbanIpVal}
onChange={(_, d) => {
setUnbanIpVal(d.value);
}}
/>
</Field>
</div>
<div className={styles.formField}>
<Field label="Jail (optional — leave blank for all)">
<Select
value={unbanJail}
onChange={(_, d) => {
setUnbanJail(d.value);
}}
>
<option value="">All jails</option>
{jailNames.map((n) => (
<option key={n} value={n}>
{n}
</option>
))}
</Select>
</Field>
</div>
<Button appearance="secondary" icon={<LockOpenRegular />} onClick={() => {
void handleUnban(false);
}} disabled={isUnbanning}>
Unban
</Button>
<Button appearance="outline" icon={<LockOpenRegular />} onClick={() => {
void handleUnban(true);
}} disabled={isUnbanning}>
Unban from All Jails
</Button>
</div>
</div>
);
}