admin/monitor: download progress #273
Massimo Melina committed
Jun 20, 2023 at 12:45 UTC
2305fd5fdf95331a654f1551634e8c089e82eb6b
4 files changed
+17
-10
admin/src/MonitorPage.ts
+5
-4
@@ -3,7 +3,7 @@
3
import _ from "lodash"
4
import { createElement as h, useMemo, Fragment, useState } from "react"
5
import { apiCall, useApiEvents, useApiEx, useApiList } from "./api"
6
-import { PauseCircle, PlayCircle, Delete, Lock, Block, FolderZip, Upload } from '@mui/icons-material'
6
+import { PauseCircle, PlayCircle, Delete, Lock, Block, FolderZip, Upload, Download } from '@mui/icons-material'
7
import { Alert, Box, Chip, ChipProps } from '@mui/material'
8
import { DataGrid } from "@mui/x-data-grid"
9
import { formatBytes, IconBtn, IconProgress, iconTooltip, manipulateConfig, useBreakpoint } from "./misc"
@@ -118,9 +118,10 @@ function Connections() {
118
h(Box, { ml: 2, color: 'text.secondary' }, value)
119
)
120
const i = value?.lastIndexOf('/')
121
+ const progress = row.uploadProgress ?? row.downloadProgress
122
return h(Fragment, {},
122
- row.uploadProgress !== undefined
123
- && h(IconProgress, { icon: Upload, progress: row.uploadProgress, sx: { mr: 1 } }),
123
+ progress !== undefined
124
+ && h(IconProgress, { icon: row.uploadProgress ? Upload : Download, progress, sx: { mr: 1 } }),
125
value.slice(i + 1),
126
i > 0 && h(Box, { ml: 2, color: 'text.secondary' }, value.slice(0, i))
127
)
@@ -144,7 +145,7 @@ function Connections() {
145
},
146
{
147
field: 'sent',
147
- headerName: "Total",
148
+ headerName: "Sent",
149
type: 'number',
150
renderCell: ({ value, row}) => formatBytes(Math.max(value||0, row.got||0))
151
},
src/api.monitor.ts
+1
@@ -75,6 +75,7 @@ const apis: ApiHandlers = {
75
v: (socket.remoteFamily?.endsWith('6') ? 6 : 4),
76
got: socket.bytesRead,
77
sent: socket.bytesWritten,
78
+ downloadProgress: conn.downloadProgress,
79
started,
80
secure: (secure || undefined) as boolean|undefined, // undefined will save some space once json-ed
81
...fromCtx(conn.ctx),
src/connections.ts
+2
-1
@@ -7,10 +7,11 @@ import _ from 'lodash'
7
8
export class Connection {
9
readonly started = new Date()
10
- sent = 0
10
+ sent = 0 // socket-scoped, not request-scoped
11
got = 0
12
outSpeed?: number
13
inSpeed?: number
14
+ downloadProgress?: number
15
uploadProgress?: number
16
uploadPath?: string
17
ctx?: Koa.Context
src/throttler.ts
+9
-5
@@ -49,13 +49,17 @@ export const throttler: Koa.Middleware = async (ctx, next) => {
49
const update = _.debounce(() => {
50
const ts = conn[SymThrStr] as ThrottledStream
51
const outSpeed = roundSpeed(ts.getSpeed())
52
- updateConnection(conn, { outSpeed, sent: ts.getBytesSent() })
52
+ updateConnection(conn, {
53
+ outSpeed,
54
+ sent: conn.socket.bytesWritten,
55
+ downloadProgress: ts.getBytesSent() / downloadTotal,
56
+ })
57
/* in case this stream stands still for a while (before the end), we'll have neither 'sent' or 'close' events,
58
* so who will take care to updateConnection? This artificial next-call will ensure just that */
59
clearTimeout(conn[SymTimeout])
60
if (outSpeed || !closed)
61
conn[SymTimeout] = setTimeout(update, DELAY)
58
- }, DELAY, { maxWait:DELAY })
62
+ }, DELAY, { leading: true, maxWait:DELAY })
63
ts.on('sent', (n: number) => {
64
totalSent += n
65
update()
@@ -70,11 +74,11 @@ export const throttler: Koa.Middleware = async (ctx, next) => {
74
delete ip2group[ctx.ip]
75
})
76
73
- const bak = ctx.response.length // preserve
77
+ const downloadTotal: number = ctx.response.length
78
ctx.body = ctx.body.pipe(ts)
79
76
- if (bak)
77
- ctx.response.length = bak
80
+ if (downloadTotal) // preserve this info
81
+ ctx.response.length = downloadTotal
82
ts.once('end', () => // in case of compressed response, we offer calculation of real size
83
ctx.state.length = ts.getBytesSent())
84
}