Global tab provided the same four editable fields as Server tab:
log_level, log_target, db_purge_age, db_max_matches. Server tab already
has these fields plus additional read-only info (db_path, syslog_socket)
and a Flush Logs button.
- Add hint text to DB Purge Age and DB Max Matches fields in ServerTab
- Remove GlobalTab component import from ConfigPage
- Remove 'global' from TabValue type
- Remove Global tab element from TabList
- Remove conditional render for GlobalTab
- Remove GlobalTab from barrel export (index.ts)
- Delete GlobalTab.tsx file
- Update ConfigPage test to remove Global tab test case
All 123 frontend tests pass.
Task 2: adds a new Log tab to the Configuration page.
Backend:
- New Pydantic models: Fail2BanLogResponse, ServiceStatusResponse
(backend/app/models/config.py)
- New service methods in config_service.py:
read_fail2ban_log() — queries socket for log target/level, validates the
resolved path against a safe-prefix allowlist (/var/log) to prevent
path traversal, then reads the tail of the file via the existing
_read_tail_lines() helper; optional substring filter applied server-side.
get_service_status() — delegates to health_service.probe() and appends
log level/target from the socket.
- New endpoints in routers/config.py:
GET /api/config/fail2ban-log?lines=200&filter=...
GET /api/config/service-status
Both require authentication; log endpoint returns 400 for non-file log
targets or path-traversal attempts, 502 when fail2ban is unreachable.
Frontend:
- New LogTab.tsx component:
Service Health panel (Running/Offline badge, version, jail count, bans,
failures, log level/target, offline warning banner).
Log viewer with color-coded lines (error=red, warning=yellow,
debug=grey), toolbar (filter input + debounce, lines selector, manual
refresh, auto-refresh with interval selector), truncation notice, and
auto-scroll to bottom on data updates.
fetchData uses Promise.allSettled so a log-read failure never hides the
service-health panel.
- Types: Fail2BanLogResponse, ServiceStatusResponse (types/config.ts)
- API functions: fetchFail2BanLog, fetchServiceStatus (api/config.ts)
- Endpoint constants (api/endpoints.ts)
- ConfigPage.tsx: Log tab added after existing tabs
Tests:
- Backend service tests: TestReadFail2BanLog (6), TestGetServiceStatus (2)
- Backend router tests: TestGetFail2BanLog (8), TestGetServiceStatus (3)
- Frontend: LogTab.test.tsx (8 tests)
Docs:
- Features.md: Log section added under Configuration View
- Architekture.md: config.py router and config_service.py descriptions updated
- Tasks.md: Task 2 marked done
- Remove Export tab and all its imports from ConfigPage.tsx
- Remove Refresh and Reload fail2ban buttons from JailsTab; clean up
associated state (reloading, reloadMsg, deactivating) and handlers
- Add Create Config button to Jails tab list pane (listHeader pattern);
create CreateJailDialog component that calls createJailConfigFile API
- Remove Active/Inactive and 'Has local override' badges from FilterDetail
and ActionDetail; remove now-unused Badge imports
- Replace read-only log path spans with editable Input fields in JailConfigDetail
- Export CreateJailDialog from components/config/index.ts
- Mark all 5 tasks done in Docs/Tasks.md
- Add use_dns and prefregex fields to JailConfig model (backend + frontend types)
- Add prefregex to JailConfigUpdate; validate as regex before writing
- Fetch usedns and prefregex in get_jail_config via asyncio.gather
- Write usedns and prefregex in update_jail_config
- ConfigPage JailAccordionPanel: editable date_pattern input, dns_mode
Select dropdown (yes/warn/no/raw), and prefregex input
- 8 new service unit tests + 3 new router integration tests
- 628 tests pass; 85% line coverage; ruff/mypy/tsc/eslint clean
- 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
The JailAccordionPanel previously allowed deleting log paths but
had no UI to add new ones. The backend endpoint, API helper, and
hook all existed; only the UI was missing.
Changes:
- ConfigPage.tsx: import addLogPath/AddLogPathRequest; add state
(newLogPath, newLogPathTail, addingLogPath) and handleAddLogPath
callback to JailAccordionPanel; render inline form below the
log-path list with Input, Switch (tail/head), and labeled Add
button that appends on success and surfaces errors inline.
- ConfigPageLogPath.test.tsx: 6 tests covering render, disabled
state, enabled state, successful add, success feedback, and API
error handling. All 33 frontend tests pass.
- Task 1: Mark imported blocklist IP addresses
- Add BanOrigin type and _derive_origin() to ban.py model
- Populate origin field in ban_service list_bans() and bans_by_country()
- BanTable and MapPage companion table show origin badge column
- Tests: origin derivation in test_ban_service.py and test_dashboard.py
- Task 2: Add origin filter to dashboard and world map
- ban_service: _origin_sql_filter() helper; origin param on list_bans()
and bans_by_country()
- dashboard router: optional origin query param forwarded to service
- Frontend: BanOriginFilter type + BAN_ORIGIN_FILTER_LABELS in ban.ts
- fetchBans / fetchBansByCountry forward origin to API
- useBans / useMapData accept and pass origin; page resets on change
- BanTable accepts origin prop; DashboardPage adds segmented filter
- MapPage adds origin Select next to time-range picker
- Tests: origin filter assertions in test_ban_service and test_dashboard