Fix BanUnbanForm floating promises and add submit guards

This commit is contained in:
2026-04-19 19:42:39 +02:00
parent 76c9f388a8
commit 082dcc7ee1
3 changed files with 132 additions and 37 deletions

View File

@@ -29,54 +29,59 @@ export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps):
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 handleBan = (): void => {
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;
}
onBan(banJail, banIpVal.trim())
.then(() => {
setFormSuccess(`${banIpVal.trim()} banned in ${banJail}.`);
setBanIpVal("");
})
.catch((err: unknown) => {
const msg =
err instanceof ApiError
? `${String(err.status)}: ${err.body}`
: err instanceof Error
? err.message
: String(err);
setFormError(msg);
});
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 = (fromAllJails: boolean): void => {
const handleUnban = async (fromAllJails: boolean): Promise<void> => {
setFormError(null);
setFormSuccess(null);
if (!unbanIpVal.trim()) {
setFormError("IP address is required.");
return;
}
const jail = fromAllJails ? undefined : unbanJail || undefined;
onUnban(unbanIpVal.trim(), jail)
.then(() => {
const scope = jail ?? "all jails";
setFormSuccess(`${unbanIpVal.trim()} unbanned from ${scope}.`);
setUnbanIpVal("");
setUnbanJail("");
})
.catch((err: unknown) => {
const msg =
err instanceof ApiError
? `${String(err.status)}: ${err.body}`
: err instanceof Error
? err.message
: String(err);
setFormError(msg);
});
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 (
@@ -130,7 +135,9 @@ export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps):
</Select>
</Field>
</div>
<Button appearance="primary" icon={<LockClosedRegular />} onClick={handleBan}>
<Button appearance="primary" icon={<LockClosedRegular />} onClick={() => {
void handleBan();
}} disabled={isBanning}>
Ban
</Button>
</div>
@@ -167,10 +174,14 @@ export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps):
</Select>
</Field>
</div>
<Button appearance="secondary" icon={<LockOpenRegular />} onClick={() => { handleUnban(false); }}>
<Button appearance="secondary" icon={<LockOpenRegular />} onClick={() => {
void handleUnban(false);
}} disabled={isUnbanning}>
Unban
</Button>
<Button appearance="outline" icon={<LockOpenRegular />} onClick={() => { handleUnban(true); }}>
<Button appearance="outline" icon={<LockOpenRegular />} onClick={() => {
void handleUnban(true);
}} disabled={isUnbanning}>
Unban from All Jails
</Button>
</div>