Expose ban-time escalation settings in jail detail and config UI

- Backend: Add BantimeEscalation + BantimeEscalationUpdate Pydantic models
  to app/models/config.py; add bantime_escalation field to Jail in jail.py
- Backend: jail_service.get_jail_detail() fetches 7 bantime.* socket commands
  (increment, factor, formula, multipliers, maxtime, rndtime, overalljails)
  and populates bantime_escalation on the returned Jail object
- Backend: config_service.get_jail_config() fetches same 7 commands;
  update_jail_config() writes escalation fields when provided
- Frontend: Add BantimeEscalation + BantimeEscalationUpdate interfaces to
  types/config.ts; extend JailConfig + JailConfigUpdate; extend Jail in
  types/jail.ts
- Frontend: JailDetailPage.tsx adds BantimeEscalationSection component that
  renders only when increment is enabled (shows factor, formula, multipliers,
  max_time, rnd_time, overall_jails)
- Frontend: ConfigPage.tsx JailAccordionPanel adds full escalation edit form
  (Switch for enable/disable, number inputs for factor/max_time/rnd_time,
  text inputs for formula/multipliers, Switch for overall_jails);
  handleSave includes bantime_escalation in the JailConfigUpdate payload
- Tests: Update ConfigPageLogPath.test.tsx mock to include bantime_escalation:null
- Docs: Mark Task 6 as DONE in Tasks.md
This commit is contained in:
2026-03-12 20:30:21 +01:00
parent ea35695221
commit e3375fd187
10 changed files with 386 additions and 0 deletions

View File

@@ -377,6 +377,61 @@ function PatternsSection({ jail }: { jail: Jail }): React.JSX.Element {
);
}
// ---------------------------------------------------------------------------
// Sub-component: Ban-time escalation section
// ---------------------------------------------------------------------------
function BantimeEscalationSection({ jail }: { jail: Jail }): React.JSX.Element | null {
const styles = useStyles();
const esc = jail.bantime_escalation;
if (!esc?.increment) return null;
return (
<div className={styles.section}>
<div className={styles.sectionHeader}>
<Text as="h2" size={500} weight="semibold">
Ban-time Escalation
</Text>
<Badge appearance="filled" color="informative">enabled</Badge>
</div>
<div className={styles.grid}>
{esc.factor !== null && (
<>
<Text className={styles.label}>Factor:</Text>
<Text className={styles.mono}>{String(esc.factor)}</Text>
</>
)}
{esc.formula && (
<>
<Text className={styles.label}>Formula:</Text>
<Text className={styles.mono}>{esc.formula}</Text>
</>
)}
{esc.multipliers && (
<>
<Text className={styles.label}>Multipliers:</Text>
<Text className={styles.mono}>{esc.multipliers}</Text>
</>
)}
{esc.max_time !== null && (
<>
<Text className={styles.label}>Max time:</Text>
<Text>{fmtSeconds(esc.max_time)}</Text>
</>
)}
{esc.rnd_time !== null && (
<>
<Text className={styles.label}>Random jitter:</Text>
<Text>{fmtSeconds(esc.rnd_time)}</Text>
</>
)}
<Text className={styles.label}>Count across all jails:</Text>
<Text>{esc.overall_jails ? "yes" : "no"}</Text>
</div>
</div>
);
}
// ---------------------------------------------------------------------------
// Sub-component: Ignore list section
// ---------------------------------------------------------------------------
@@ -570,6 +625,7 @@ export function JailDetailPage(): React.JSX.Element {
<JailInfoSection jail={jail} onRefresh={refresh} />
<PatternsSection jail={jail} />
<BantimeEscalationSection jail={jail} />
<IgnoreListSection
jailName={name}
ignoreList={ignoreList}