admin/options: block-ip rules can now disabled

Massimo Melina committed Jan 3, 2024 at 16:46 UTC 8a4a6aa0fbf3e64b21150d578338011529b4005e
3 files changed +20 -5
admin/src/ArrayField.ts
+9 -2
@@ -2,10 +2,10 @@
2
3 import { createElement as h, Fragment, useMemo, useState } from 'react'
4 import { Dict, isOrderedEqual, setHidden, swap } from './misc'
5 -import { Add, Edit, Delete, ArrowUpward, ArrowDownward, Undo } from '@mui/icons-material'
5 +import { Add, Edit, Delete, ArrowUpward, ArrowDownward, Undo, Check } from '@mui/icons-material'
6 import { formDialog } from './dialog'
7 import { DataGrid, GridActionsCellItem, GridAlignment, GridColDef } from '@mui/x-data-grid'
8 -import { FieldDescriptor, FieldProps, labelFromKey } from '@hfs/mui-grid-form'
8 +import { BoolField, FieldDescriptor, FieldProps, labelFromKey } from '@hfs/mui-grid-form'
9 import { Box, FormHelperText, FormLabel } from '@mui/material'
10 import { DateTimeField } from './DateTimeField'
11 import _ from 'lodash'
@@ -43,6 +43,9 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
43 field: f.k,
44 headerName: f.headerName ?? (typeof f.label === 'string' ? f.label : labelFromKey(f.k)),
45 disableColumnMenu: true,
46 + valueGetter({ value }: any) {
47 + return (f.toField || _.identity)(value)
48 + },
49 ...def,
50 ...f.$width ? { [f.$width >= 8 ? 'width' : 'flex']: f.$width } : (!def?.width && !def?.flex && { flex: 1 }),
51 ...f.$column,
@@ -126,6 +129,10 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
129 }
130
131 const byType: Dict<{ field?: Partial<FieldDescriptor>, column?: Partial<GridColDef> }> = {
132 + boolean: {
133 + field: { comp: BoolField },
134 + column: { renderCell: ({ value }) => value && h(Check) },
135 + },
136 dateTime: {
137 field: { comp: DateTimeField },
138 column: {
admin/src/OptionsPage.ts
+9 -1
@@ -5,7 +5,7 @@ import { createElement as h, Fragment, useEffect, useRef } from 'react';
5 import { apiCall, useApiEx } from './api'
6 import { state, useSnapState } from './state'
7 import { Link as RouterLink } from 'react-router-dom'
8 -import { CardMembership, EditNote, Refresh, Warning } from '@mui/icons-material'
8 +import { CardMembership, Check, EditNote, Refresh, Warning } from '@mui/icons-material'
9 import { Dict, MAX_TILE_SIZE, REPO_URL, isIpLocalHost, wait, with_, try_, ipForUrl, SORT_BY_OPTIONS, THEME_OPTIONS,
10 CFG } from './misc'
11 import { iconTooltip, InLink, LinkBtn, modifiedSx, wikiLink, useBreakpoint, NetmaskField, WildcardsSupported } from './mui'
@@ -179,6 +179,14 @@ export default function OptionsPage() {
179 fields: [
180 { k: 'ip', label: "Blocked IP", sm: 6, required: true, helperText: h(WildcardsSupported) },
181 { k: 'expire', $type: 'dateTime', minDate: new Date(), sm: 6, helperText: "Leave empty for no expiration" },
182 + {
183 + k: 'disabled',
184 + $type: 'boolean',
185 + label: "Enabled",
186 + toField: (x: any) => !x,
187 + fromField: (x: any) => x ? undefined : true,
188 + sm: 6,
189 + },
190 { k: 'comment' },
191 ],
192 },
src/block.ts
+2 -2
@@ -5,14 +5,14 @@ import { disconnect, getConnections, normalizeIp } from './connections'
5 import { makeNetMatcher, MINUTE, onlyTruthy } from './misc'
6 import { Socket } from 'net'
7
8 -interface BlockingRule { ip: string, comment?: string, expire?: Date }
8 +interface BlockingRule { ip: string, comment?: string, expire?: Date, disabled?: boolean }
9
10 const block = defineConfig('block', [] as BlockingRule[], rules => {
11 const now = new Date()
12 const ret = !Array.isArray(rules) ? []
13 : onlyTruthy(rules.map(rule => {
14 rule.expire &&= new Date(rule.expire)
15 - return !(rule.expire! > now) && makeNetMatcher(rule.ip)
15 + return !rule.disabled && (rule.expire || now) <= now && makeNetMatcher(rule.ip)
16 }))
17 // reapply new block to existing connections
18 for (const { socket, ip } of getConnections())