Files
BanGUI/frontend/src/components/blocklist/BlocklistImportLogSection.tsx
Lukas 1285bc8571 feat: comprehensive health check with DB, scheduler, cache
- Add /api/v1/health endpoint with component-level checks
- Verify DB connectivity, fail2ban socket, scheduler, session cache
- Add SQLite WAL cleanup on startup (orphan crash files)
- Migration 8: import_log.timestamp → INTEGER UNIX epoch
- Align import_log timestamps with history_archive (already UNIX int)
- Add unit tests for DB cleanup and health router

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 23:03:57 +02:00

111 lines
4.2 KiB
TypeScript

import { Button, Badge, Table, TableBody, TableCell, TableCellLayout, TableHeader, TableHeaderCell, TableRow, Text, MessageBar, MessageBarBody, Spinner } from "@fluentui/react-components";
import { ArrowClockwiseRegular } from "@fluentui/react-icons";
import { useCommonSectionStyles } from "../../components/commonStyles";
import { useImportLog } from "../../hooks/useImportLog";
import { useBlocklistStyles } from "./blocklistStyles";
import { formatDate } from "../../utils/formatDate";
function formatUnixTimestamp(unixTs: number, timezone: string | null | undefined): string {
return formatDate(new Date(unixTs * 1000).toISOString(), timezone);
}
export function BlocklistImportLogSection(): React.JSX.Element {
const styles = useBlocklistStyles();
const sectionStyles = useCommonSectionStyles();
const { data, loading, error, page, setPage, refresh } = useImportLog(undefined, 20);
return (
<div className={sectionStyles.section}>
<div className={sectionStyles.sectionHeader}>
<Text size={500} weight="semibold">
Import Log
</Text>
<Button icon={<ArrowClockwiseRegular />} appearance="secondary" onClick={refresh}>
Refresh
</Button>
</div>
{error && (
<MessageBar intent="error">
<MessageBarBody>{error}</MessageBarBody>
</MessageBar>
)}
{loading ? (
<div className={styles.centred}>
<Spinner label="Loading log…" />
</div>
) : !data || data.items.length === 0 ? (
<div className={styles.centred}>
<Text>No import runs recorded yet.</Text>
</div>
) : (
<>
<div className={styles.tableWrapper}>
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>Timestamp</TableHeaderCell>
<TableHeaderCell>Source URL</TableHeaderCell>
<TableHeaderCell>Imported</TableHeaderCell>
<TableHeaderCell>Skipped</TableHeaderCell>
<TableHeaderCell>Status</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{data.items.map((entry) => (
<TableRow key={entry.id} className={entry.errors ? styles.errorRow : undefined}>
<TableCell>
<TableCellLayout>
<span className={styles.mono}>{formatUnixTimestamp(entry.timestamp, undefined)}</span>
</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout>
<span className={styles.mono}>{entry.source_url}</span>
</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout>{entry.ips_imported}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout>{entry.ips_skipped}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout>
{entry.errors ? (
<Badge appearance="filled" color="danger">
Error
</Badge>
) : (
<Badge appearance="filled" color="success">
OK
</Badge>
)}
</TableCellLayout>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
{data.total_pages > 1 && (
<div className={styles.pagination}>
<Button size="small" appearance="secondary" disabled={page <= 1} onClick={() => { setPage(page - 1); }}>
Previous
</Button>
<Text size={200}>
Page {page} of {data.total_pages}
</Text>
<Button size="small" appearance="secondary" disabled={page >= data.total_pages} onClick={() => { setPage(page + 1); }}>
Next
</Button>
</div>
)}
</>
)}
</div>
);
}