plugins: newSocket lets you now specify a log message for disconnection

Massimo Melina committed Feb 21, 2025 at 12:34 UTC d507db8b0861365005a688e3f6c6dcc990b7d599
4 files changed +32 -25
admin/src/LogsPage.ts
+24 -21
@@ -4,7 +4,7 @@ import { createElement as h, Fragment, ReactNode, useEffect, useMemo, useState }
4 import httpCodes from './httpCodes'
5 import { Box, Tab, Tabs } from '@mui/material'
6 import { API_URL, apiCall, useApi, useApiList } from './api'
7 -import { DataTable, DataTableProps } from './DataTable'
7 +import { DataTable, DataTableColumn, DataTableProps } from './DataTable'
8 import {
9 CFG, Dict, formatBytes, HTTP_UNAUTHORIZED, newDialog, prefix, shortenAgent, splitAt, tryJson, md,
10 typedKeys, _dbg, mapFilter, safeDecodeURIComponent, stringAfter, onlyTruthy, formatTimestamp, formatSpeed
@@ -13,7 +13,6 @@ import {
13 NetmaskField, Flex, IconBtn, useBreakpoint, usePauseButton, useToggleButton, WildcardsSupported, Country,
14 hTooltip, Btn, wikiLink
15 } from './mui';
16 -import { GridColDef } from '@mui/x-data-grid'
16 import _ from 'lodash'
17 import { AutoDelete, LinkOff, ClearAll, Delete, Download, Settings, SmartToy, Terminal } from '@mui/icons-material'
18 import { ConfigForm } from './ConfigForm'
@@ -143,7 +142,7 @@ export function LogFile({ file, footerSide, hidden, limit, filter, ...rest }: Lo
142 const isIps = file === 'ips'
143 if (isIps)
144 reloadIps = reload
146 - const tsColumn: GridColDef = {
145 + const tsColumn: DataTableColumn = {
146 field: 'ts',
147 headerName: "Timestamp",
148 type: 'dateTime',
@@ -151,6 +150,18 @@ export function LogFile({ file, footerSide, hidden, limit, filter, ...rest }: Lo
150 valueGetter: ({ value }) => new Date(value as string),
151 renderCell: ({ value }) => h(Fragment, {}, value.toLocaleDateString(), h('br'), value.toLocaleTimeString())
152 }
153 + const ipColumn: DataTableColumn = {
154 + field: 'ip',
155 + headerName: "Address",
156 + flex: .6,
157 + minWidth: 130,
158 + maxWidth: 230,
159 + mergeRender: {
160 + user: { display: 'flex', justifyContent: 'space-between', gap: '.5em', },
161 + country: showCountry && {},
162 + ua: {},
163 + },
164 + }
165 const rows = useMemo(() =>
166 filter ? list.filter(filter)
167 : showApi || list?.[0]?.uri === undefined ? list
@@ -209,34 +220,26 @@ export function LogFile({ file, footerSide, hidden, limit, filter, ...rest }: Lo
220 flex: 1,
221 mergeRender: { k: { override: { valueFormatter: ({ value }) => value !== 'log' && value } } }
222 }
212 - ] : file === 'ips' || file === 'disconnections' ? [
223 + ] : isIps || file === 'disconnections' ? [
224 tsColumn,
214 - {
215 - field: 'ip',
216 - headerName: "Address",
217 - flex: 1,
218 - },
225 + ipColumn,
226 {
227 headerName: "Country",
228 field: 'country',
229 flex: 1,
230 hidden: !showCountry,
231 + hideUnder: 'md',
232 valueGetter: ({ value }) => _.find(COUNTRIES, { code: value })?.name || value,
233 renderCell: ({ row }) => h(Country, { code: row.country, long: true, def: '-' }),
234 + },
235 + {
236 + hidden: isIps,
237 + field: 'msg',
238 + headerName: "Message",
239 + flex: 4,
240 }
241 ] : [
228 - {
229 - field: 'ip',
230 - headerName: "Address",
231 - flex: .6,
232 - minWidth: 130,
233 - maxWidth: 230,
234 - mergeRender: {
235 - user: { display: 'flex', justifyContent: 'space-between', gap: '.5em', },
236 - country: showCountry && {},
237 - ua: {},
238 - },
239 - },
242 + ipColumn,
243 {
244 headerName: "Country",
245 field: 'country',
dev-plugins.md
+1
@@ -590,6 +590,7 @@ This section is still partially documented, and you may need to have a look at t
590 - `newSocket`
591 - parameters: { socket,ip }
592 - preventable
593 + - return: you can return a string with a message that will be logged, and it will also cause disconnection
594 - `getList` called when get=list on legit requests to ?get=list
595 - parameters: { node, ctx }
596 - async supported
src/api.monitor.ts
+2 -2
@@ -1,7 +1,7 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import _ from 'lodash'
4 -import { Connection, getConnections } from './connections'
4 +import { Connection, disconnect, getConnections } from './connections'
5 import { apiAssertTypes, isLocalHost, shortenAgent, try_, wait, wantArray } from './misc'
6 import { ApiHandlers } from './apiMiddleware'
7 import Koa from 'koa'
@@ -17,7 +17,7 @@ export default {
17 : _.matches({ ip, port })
18 const found = getConnections().filter(c => match(getConnAddress(c)))
19 for (const c of found)
20 - c.socket.destroy()
20 + disconnect(c.socket, "manual disconnection")
21 return { result: found.length }
22 },
23
src/connections.ts
+5 -2
@@ -4,6 +4,7 @@ import { Socket } from 'net'
4 import events from './events'
5 import { Context } from 'koa'
6 import { ip2country } from './geo'
7 +import _ from 'lodash'
8
9 export class Connection {
10 readonly started = new Date()
@@ -42,8 +43,10 @@ const all: Connection[] = []
43
44 export function newConnection(socket: Socket) {
45 const ip = normalizeIp(socket.remoteAddress || '')
45 - if (events.emit('newSocket', { socket, ip })?.isDefaultPrevented())
46 - return socket.destroy()
46 + const res = events.emit('newSocket', { socket, ip })
47 + const msg = res?.isDefaultPrevented() ? 'plugin (newSocket)' : res?.find(_.isString)
48 + if (msg)
49 + return disconnect(socket, msg)
50 new Connection(socket)
51 }
52