admin/log: country is logged (if geo is enabled)

Massimo Melina committed Dec 14, 2023 at 11:25 UTC f937dfb8483f2ee8ff927c2e7f9728496de7b483
4 files changed +29 -6
admin/src/LogsPage.ts
+19 -1
@@ -11,6 +11,7 @@ import { GridColDef } from '@mui/x-data-grid'
11 import _ from 'lodash'
12 import { SmartToy } from '@mui/icons-material'
13 import md from './md'
14 +import { Country } from './MonitorPage'
15
16 export default function LogsPage() {
17 const [tab, setTab] = useState(0)
@@ -36,11 +37,14 @@ export default function LogsPage() {
37 }
38
39 function LogFile({ file, pause, showApi }: { file: string, pause?: boolean, showApi: boolean }) {
40 + const [showCountry, setShowCountry] = useState(false)
41 const { list, error, connecting } = useApiList('get_log', { file }, {
42 invert: true,
43 pause,
44 map(x) {
45 const { extra } = x
46 + if (extra?.country && !showCountry)
47 + setShowCountry(true)
48 x.notes = extra?.dl ? "fully downloaded"
49 : extra?.ul ? "uploaded " + formatBytes(extra.size)
50 : x.status === HTTP_UNAUTHORIZED && x.uri?.startsWith(API_URL + 'loginSrp') ? "login failed" + prefix(':\n', extra?.u)
@@ -87,7 +91,21 @@ function LogFile({ file, pause, showApi }: { file: string, pause?: boolean, show
91 flex: .6,
92 minWidth: 100,
93 maxWidth: 230,
90 - mergeRender: { other: 'user' },
94 + mergeRender: {
95 + other: 'user',
96 + display: 'flex',
97 + justifyContent: 'space-between',
98 + gap: '.5em',
99 + override: { renderCell: ({ value, row }) => h(Fragment, {}, h('span', {}, value), h(Country, { code: row.extra?.country })) }
100 + },
101 + },
102 + {
103 + headerName: "Country",
104 + field: 'country',
105 + valueGetter: ({ row }) => row.extra?.country,
106 + hidden: !showCountry,
107 + hideUnder: 'lg',
108 + renderCell: ({ value }) => h(Country, { code: value, def: '-' }),
109 },
110 {
111 field: 'user',
admin/src/MonitorPage.ts
+1 -1
@@ -83,7 +83,7 @@ function MoreInfo() {
83
84 }
85
86 -function Country({ code, ip, def }: { code: string, ip?: string, def: ReactNode }) {
86 +export function Country({ code, ip, def }: { code: string, ip?: string, def?: ReactNode }) {
87 const { data } = useBatch(code === undefined && ip && ip2countryBatch, ip, { delay: 100 }) // query if necessary
88 code ||= data || ''
89 const country = code && _.find(COUNTRIES, { code })
src/geo.ts
+4 -3
@@ -17,12 +17,13 @@ setInterval(checkFiles, DAY) // keep updated at run-time
17 export const ip2country = _.memoize((ip: string) => ip2location.getCountryShortAsync(ip).then(v => v === '-' ? '' : v, () => ''))
18
19 export const geoFilter: Middleware = async (ctx, next) => {
20 - if (allow.get() !== null && !isLocalHost(ctx)) {
20 + if (enabled.get() && !isLocalHost(ctx)) {
21 const { connection } = ctx.state
22 const country = connection.country ??= await ip2country(ctx.ip)
23 updateConnection(connection, { country })
24 - if (country ? list.get().includes(country) !== allow.get() : !allowUnknown.get())
25 - return disconnect(ctx)
24 + if (allow.get() !== null)
25 + if (country ? list.get().includes(country) !== allow.get() : !allowUnknown.get())
26 + return disconnect(ctx)
27 }
28 return next()
29 }
src/log.ts
+5 -1
@@ -11,6 +11,7 @@ import { createFileWithPath, prepareFolder } from './util-files'
11 import { getCurrentUsername } from './auth'
12 import { DAY, makeNetMatcher, tryJson } from './misc'
13 import events from './events'
14 +import { getConnection } from './connections'
15
16 class Logger {
17 stream?: Writable
@@ -106,7 +107,10 @@ export const logMw: Koa.Middleware = async (ctx, next) => {
107 const uri = ctx.originalUrl
108 let extra = ctx.state.includesLastByte && ctx.vfsNode && ctx.res.finished && { dl: 1 }
109 || ctx.state.uploadPath && { ul: ctx.state.uploadPath, size: ctx.state.uploadSize }
109 - || ctx.state.logExtra
110 + const conn = getConnection(ctx)
111 + if (conn?.country)
112 + Object.assign(extra ||= {}, { country: conn.country })
113 + extra = extra ? Object.assign(extra, ctx.state.logExtra) : ctx.state.logExtra
114 if (logUA.get())
115 extra = Object.assign({ ua: ctx.get('user-agent') }, extra)
116 events.emit(logger.name, Object.assign(_.pick(ctx, ['ip', 'method','status']), { length, user, ts: now, uri, extra }))