admin/logs/ips: delete button
Massimo Melina committed
Nov 12, 2024 at 01:01 UTC
8d7b0467a63164732e144d3bfffbe10780b1df26
2 files changed
+44
-6
admin/src/LogsPage.ts
+21
-5
@@ -6,8 +6,7 @@ import { API_URL, apiCall, useApi, useApiList } from './api'
6
import { DataTable, DataTableProps } from './DataTable'
7
import {
8
CFG, Dict, formatBytes, HTTP_UNAUTHORIZED, newDialog, prefix, shortenAgent, splitAt, tryJson, md,
9
- typedKeys, NBSP, _dbg, mapFilter, safeDecodeURIComponent
10
-, stringAfter
9
+ typedKeys, NBSP, _dbg, mapFilter, safeDecodeURIComponent, stringAfter, onlyTruthy, formatTimestamp
10
} from '@hfs/shared'
11
import {
12
NetmaskField, Flex, IconBtn, useBreakpoint, usePauseButton, useToggleButton, WildcardsSupported, Country,
@@ -15,10 +14,10 @@ import {
14
} from './mui';
15
import { GridColDef } from '@mui/x-data-grid'
16
import _ from 'lodash'
18
-import { ClearAll, Download, Settings, SmartToy } from '@mui/icons-material'
17
+import { AutoDelete, ClearAll, Delete, Download, Settings, SmartToy } from '@mui/icons-material'
18
import { ConfigForm } from './ConfigForm'
19
import { BoolField, SelectField } from '@hfs/mui-grid-form'
21
-import { toast, useDialogBarColors } from './dialog'
20
+import { alertDialog, toast, useDialogBarColors } from './dialog'
21
import { useBlockIp } from './useBlockIp'
22
import { ALL as COUNTRIES } from './countries'
23
@@ -151,13 +150,30 @@ export function LogFile({ file, footerSide, hidden, limit, ...rest }: { limit?:
150
const rows = useMemo(() => showApi || list?.[0]?.uri === undefined ? list : list.filter(x => !x.uri.startsWith(API_URL)), [list, showApi]) //TODO TypeError: l.uri is undefined
151
const blockIp = useBlockIp()
152
const isConsole = file === 'console'
153
+ const isIps = file === 'ips'
154
return hidden ? null : h(DataTable, {
155
error,
156
loading: connecting,
157
rows,
158
compact: true,
159
actionsProps: { hideUnder: 'md' },
160
- actions: ({ row }) => [ !isConsole && blockIp.iconBtn(row.ip, "From log") ],
160
+ actions: isConsole ? undefined : (({ row }) => onlyTruthy([
161
+ blockIp.iconBtn(row.ip, "From log"),
162
+ isIps && h(Btn, {
163
+ icon: Delete,
164
+ confirm: true,
165
+ title: `Delete ${row.ip}`,
166
+ doneMessage: true,
167
+ onClick: () => apiCall('delete_ips', { ip: row.ip }).then(() => setList(was => was.filter(x => x.ip !== row.ip)))
168
+ }),
169
+ isIps && h(Btn, {
170
+ icon: AutoDelete,
171
+ confirm: true,
172
+ title: `Delete all records up to ${formatTimestamp(row.ts)}`,
173
+ onClick: () => apiCall('delete_ips', { ts: row.ts }).then(res => toast(`${res.n} deleted`)).then(reload)
174
+ }),
175
+ ])),
176
+ initialState: isIps ? { sorting: { sortModel: [{ field: 'ts', sort: 'desc' }] } } : undefined,
177
...rest,
178
footerSide: width => h(Box, {}, // 4 icons don't fit the tabs row on mobile
179
pauseButton,
src/api.log.ts
+23
-1
@@ -1,7 +1,8 @@
1
import { ApiHandlers } from './apiMiddleware'
2
import _ from 'lodash'
3
import { consoleLog } from './consoleLog'
4
-import { HTTP_NOT_ACCEPTABLE, HTTP_NOT_FOUND, wait } from './cross'
4
+import { HTTP_BAD_REQUEST, HTTP_NOT_ACCEPTABLE, HTTP_NOT_FOUND, wait } from './cross'
5
+import { apiAssertTypes } from './misc'
6
import events from './events'
7
import { loggers } from './log'
8
import { SendListReadable } from './SendList'
@@ -56,6 +57,27 @@ export default {
57
58
},
59
60
+ async delete_ips({ ip, ts }) {
61
+ apiAssertTypes({ string_undefined: { ip, ts } })
62
+ if (ip) {
63
+ if (!ips.has(ip))
64
+ throw HTTP_NOT_FOUND
65
+ ips.del(ip)
66
+ return {}
67
+ }
68
+ if (ts) {
69
+ ts = new Date(ts)
70
+ let n = 0
71
+ for await (const [k, rec] of ips.iterator())
72
+ if (rec.ts <= ts) {
73
+ ips.del(k)
74
+ ++n
75
+ }
76
+ return { n }
77
+ }
78
+ throw HTTP_BAD_REQUEST
79
+ },
80
+
81
reset_ips() {
82
return ips.clear()
83
},