admin: connections
Massimo Melina committed
Feb 4, 2022 at 09:46 UTC
65eb65d4cf07693950fbaf6450847345b96a6574
4 files changed
+87
-8
admin/src/MonitorPage.ts
+44
-8
@@ -1,19 +1,26 @@
1
import _ from "lodash"
2
import { isValidElement, createElement as h } from "react"
3
import { useApiComp } from "./api"
4
-import { Refresh } from '@mui/icons-material'
5
-import { Button } from '@mui/material'
4
+import { Lock, Refresh } from '@mui/icons-material'
5
+import { Button, Grid } from '@mui/material'
6
+import { DataGrid } from "@mui/x-data-grid"
7
8
export default function MonitorPage() {
9
const [res, reload] = useApiComp('get_status')
10
if (isValidElement(res))
11
return res
11
- return h('div', {},
12
- h(Button, { onClick: reload, startIcon:h(Refresh) }, 'Reload'),
13
- h('ul', {},
14
- pair('started'),
15
- pair('http', 'HTTP', v => v.active ? 'port '+v.port : 'off'),
16
- pair('https', 'HTTPS', v => v.active ? 'port '+v.port : 'off'),
12
+ return h('div', { flex: 1, display: 'flex', flexDirection: 'column' },
13
+ h('div', {},
14
+ h(Button, { onClick: reload, startIcon:h(Refresh) }, 'Reload') ),
15
+ h(Grid, { container: true, flex: 1 },
16
+ h(Grid, { item: true, md: 6 },
17
+ h('ul', {},
18
+ pair('started'),
19
+ pair('http', 'HTTP', v => v.active ? 'port '+v.port : 'off'),
20
+ pair('https', 'HTTPS', v => v.active ? 'port '+v.port : 'off'),
21
+ ) ),
22
+ h(Grid, { item: true, md: 6 },
23
+ h(Connections))
24
)
25
)
26
@@ -32,3 +39,32 @@ export default function MonitorPage() {
39
}
40
41
const isoDateRe = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
42
+
43
+function Connections() {
44
+ const [res] = useApiComp('get_connections')
45
+ if (isValidElement(res))
46
+ return res
47
+ const { list } = res
48
+ const rows = list.map((x:any,id:number) => ({ id, ...x }))
49
+ return h(DataGrid, {
50
+ pageSize: 25,
51
+ columns: [
52
+ {
53
+ field: 'ip',
54
+ headerName: 'IP',
55
+ flex: 1,
56
+ valueGetter: ({ row, value }) => (row.v === 6 ? `[${value}]` : value) + ' :' + row.port
57
+ },
58
+ {
59
+ field: 'v',
60
+ headerName: 'Protocol',
61
+ renderCell: ({ value, row }) => h('span', {}, 'IPv' + value, row.secure && h(Lock))
62
+ },
63
+ {
64
+ field: 'started',
65
+ valueGetter: ({ value }) => new Date(value).toLocaleTimeString()
66
+ },
67
+ ],
68
+ rows,
69
+ })
70
+}
src/adminApis.ts
+15
@@ -5,6 +5,7 @@ import { HFS_STARTED } from './index'
5
import { Server } from 'http'
6
import vfsApis from './api.vfs'
7
import accountsApis from './api.accounts'
8
+import { getConnections } from './connections'
9
10
export const adminApis: ApiHandlers = {
11
@@ -41,4 +42,18 @@ export const adminApis: ApiHandlers = {
42
}
43
},
44
45
+ get_connections() {
46
+ return {
47
+ list: getConnections().map(({ socket, started, secure }) => {
48
+ return {
49
+ v: socket.remoteFamily?.endsWith('6') ? 6 : 4,
50
+ port: socket.remotePort,
51
+ ip: socket.remoteAddress,
52
+ started,
53
+ secure: secure || undefined, // undefined will save some space once json-ed
54
+ }
55
+ })
56
+ }
57
+ }
58
+
59
}
src/connections.ts
new
+24
@@ -0,0 +1,24 @@
1
+import { Socket } from 'net'
2
+import { app } from './index'
3
+
4
+interface Connection {
5
+ socket: Socket
6
+ secure: boolean
7
+ started: Date
8
+}
9
+
10
+const all: Connection[] = []
11
+
12
+export function newConnection(socket: Socket, secure:boolean) {
13
+ const conn: Connection = { socket, secure, started: new Date() }
14
+ all.push(conn)
15
+ app.emit('connection', socket) // we'll use these events for SSE
16
+ socket.on('close', () => {
17
+ all.splice(all.indexOf(conn), 1)
18
+ app.emit('connectionClosed', socket)
19
+ })
20
+}
21
+
22
+export function getConnections(): Readonly<typeof all> {
23
+ return all
24
+}
src/listen.ts
+4
@@ -4,6 +4,7 @@ import { app } from './index'
4
import * as https from 'https'
5
import { watchLoad } from './watchLoad'
6
import { networkInterfaces } from 'os';
7
+import { newConnection } from './connections'
8
9
let httpSrv: http.Server
10
let httpsSrv: http.Server
@@ -93,6 +94,9 @@ function startServer(srv: http.Server, port: number, secure:string='') {
94
console.error(code === 'EADDRINUSE' ? `couldn't listen on busy port ${port}` : e)
95
reject(e)
96
})
97
+
98
+ srv.on('connection', socket =>
99
+ newConnection(socket, Boolean(secure)))
100
}
101
catch(e) {
102
console.error("couldn't listen on port", port, String(e))