blocked ips: added comment and expiration
Massimo Melina committed
Oct 4, 2023 at 00:00 UTC
3dc5dec477823f23b3ef466a388c443602723225
2 files changed
+23
-7
admin/src/OptionsPage.ts
+7
-4
@@ -154,7 +154,7 @@ export default function OptionsPage() {
154
{ k: 'admin_net', comp: NetmaskField, label: "Admin-panel accessible from", placeholder: "any address",
155
helperText: h(Fragment, {}, "IP address of browser machine. ", h(WildcardsSupported))
156
},
157
- { k: 'mime', comp: ArrayField, label: false, reorder: true, prepend: true,
157
+ { k: 'mime', comp: ArrayField, label: false, reorder: true, prepend: true, sm: 12,
158
fields: [
159
{ k: 'k', label: "File mask", $width: 1, $column: {
160
renderCell: ({ value, id }: any) => h('code', {},
@@ -169,9 +169,12 @@ export default function OptionsPage() {
169
toField: x => Object.entries(x || {}).map(([k,v]) => ({ k, v })),
170
fromField: x => Object.fromEntries(x.map((row: any) => [row.k, row.v])),
171
},
172
- { k: 'block', label: "Blocked IPs", multiline: true, minRows:3, helperText: h(Fragment, {}, "Enter an IP address for each line. ", h(WildcardsSupported)),
173
- fromField: (all:string) => all.split('\n').map(s => s.trim()).filter(Boolean).map(ip => ({ ip })),
174
- toField: (all: any) => !Array.isArray(all) ? '' : all.map(x => x?.ip).filter(Boolean).join('\n')
172
+ { k: 'block', label: false, comp: ArrayField, prepend: true, sm: 12,
173
+ fields: [
174
+ { k: 'ip', label: "Blocked IP", sm: 6, helperText: h(WildcardsSupported) },
175
+ { k: 'expire', $type: 'dateTime', minDate: new Date(), sm: 6, helperText: "Leave empty for no expiration" },
176
+ { k: 'comment' },
177
+ ],
178
},
179
{ k: 'server_code', comp: TextEditorField, sm: 12, getError: v => try_(() => new Function(v) && null, e => e.message),
180
helperText: md(`This code works similarly to [a plugin](${REPO_URL}blob/main/dev-plugins.md) (with some limitations)`)
src/block.ts
+16
-3
@@ -2,14 +2,18 @@
2
3
import { defineConfig } from './config'
4
import { getConnections, normalizeIp } from './connections'
5
-import { makeNetMatcher, onlyTruthy } from './misc'
5
+import { makeNetMatcher, MINUTE, onlyTruthy } from './misc'
6
import { Socket } from 'net'
7
8
-interface BlockingRule { ip: string }
8
+interface BlockingRule { ip: string, comment?: string, expire?: Date }
9
10
const block = defineConfig('block', [] as BlockingRule[], rules => {
11
+ const now = new Date()
12
const ret = !Array.isArray(rules) ? []
12
- : onlyTruthy(rules.map(rule => makeNetMatcher(rule.ip, true)))
13
+ : onlyTruthy(rules.map(rule => {
14
+ rule.expire &&= new Date(rule.expire)
15
+ return !(rule.expire! > now) && makeNetMatcher(rule.ip, true)
16
+ }))
17
// reapply new block to existing connections
18
for (const { socket, ip } of getConnections())
19
applyBlock(socket, ip)
@@ -20,3 +24,12 @@ export function applyBlock(socket: Socket, ip=normalizeIp(socket.remoteAddress||
24
if (ip && block.compiled().find(rule => rule(ip)))
25
return socket.destroy()
26
}
27
+
28
+setInterval(() => { // twice a minute, check if any block has expired
29
+ const now = new Date()
30
+ const next = block.get().filter(x => !x.expire || x.expire > now)
31
+ const n = block.get().length - next.length
32
+ if (!n) return
33
+ console.log("blocking rules:", n, "expired")
34
+ block.set(next)
35
+}, MINUTE/2)