@samitouri / QOSami-HFS / commits / db00cc3e

optimization: avoid geo-querying local IPs

Massimo Melina committed Jan 5, 2024 at 10:57 UTC db00cc3e492fbd163c2823dcc9771013346201d0
6 files changed +16 -10
admin/src/OptionsPage.ts
+2 -2
@@ -6,7 +6,7 @@ import { apiCall, useApiEx } from './api'
6 import { state, useSnapState } from './state'
7 import { Link as RouterLink } from 'react-router-dom'
8 import { CardMembership, EditNote, Refresh, Warning } from '@mui/icons-material'
9 -import { Dict, MAX_TILE_SIZE, REPO_URL, ipLocalHost, wait, with_, try_, ipForUrl, SORT_BY_OPTIONS, THEME_OPTIONS,
9 +import { Dict, MAX_TILE_SIZE, REPO_URL, isIpLocalHost, wait, with_, try_, ipForUrl, SORT_BY_OPTIONS, THEME_OPTIONS,
10 CFG } from './misc'
11 import { iconTooltip, InLink, LinkBtn, modifiedSx, wikiLink, useBreakpoint } from './mui'
12 import { Form, BoolField, NumberField, SelectField, FieldProps, Field, StringField } from '@hfs/mui-grid-form';
@@ -244,7 +244,7 @@ export default function OptionsPage() {
244 if (onHttps && certChange && !await confirmDialog("You may disrupt https service, kicking you out"))
245 return
246 await apiCall('set_config', { values: changes })
247 - if (newPort !== undefined || changes.listen_interface && !(loc.hostname === 'localhost' && ipLocalHost(changes.listen_interface))) {
247 + if (newPort !== undefined || changes.listen_interface && !(loc.hostname === 'localhost' && isIpLocalHost(changes.listen_interface))) {
248 await alertDialog("You are being redirected but in some cases this may fail. Hold on tight!", 'warning')
249 const host = ipForUrl(changes.listen_interface || loc.hostname)
250 // we have to jump protocol also in case of random port, because we want people to know their port while using GUI
admin/src/mui.ts
+3 -2
@@ -6,7 +6,7 @@ import { SxProps } from '@mui/system'
6 import { createElement as h, FC, forwardRef, Fragment, ReactNode, useCallback, useState } from 'react'
7 import { Box, BoxProps, Breakpoint, ButtonProps, CircularProgress, IconButton, IconButtonProps, Link, LinkProps,
8 Tooltip, TooltipProps, useMediaQuery } from '@mui/material'
9 -import { formatPerc, prefix, WIKI_URL } from '../../src/cross'
9 +import { formatPerc, isIpLan, isIpLocalHost, prefix, WIKI_URL } from '../../src/cross'
10 import { dontBotherWithKeys, useBatch, useStateMounted } from '@hfs/shared'
11 import { Promisable } from '@hfs/mui-grid-form'
12 import { alertDialog, confirmDialog, toast } from './dialog'
@@ -229,7 +229,8 @@ export function useToggleButton(iconBtn: (state:boolean) => Omit<IconBtnProps, '
229 }
230
231 export function Country({ code, ip, def, long, short }: { code: string, ip?: string, def?: ReactNode, long?: boolean, short?: boolean }) {
232 - const { data } = useBatch(code === undefined && ip && ip2countryBatch, ip, { delay: 100 }) // query if necessary
232 + const good = ip && !isIpLocalHost(ip) && !isIpLan(ip)
233 + const { data } = useBatch(code === undefined && good && ip2countryBatch, ip, { delay: 100 }) // query if necessary
234 code ||= data || ''
235 const country = code && _.find(COUNTRIES, { code })
236 return !country ? h(Fragment, {}, def) : h(Tooltip, {
plugins/download-counter/plugin.js
+3 -3
@@ -1,7 +1,7 @@
1 // other plugins can use ctx.state.download_counter_ignore to mark downloads that shouldn't be counted
2
3 exports.description = "Counts downloads for each file, and displays the total in the list or file menu"
4 -exports.version = 5.1 // count files in archive
4 +exports.version = 5.11 // change in API
5 exports.apiRequired = 8.3
6
7 exports.config = {
@@ -46,10 +46,10 @@ exports.init = async api => {
46 unload: () => save.flush(), // we may have pending savings
47 middleware: ctx => () => { // callback = execute after other middlewares are done
48 if (ctx.status >= 300 || ctx.state.download_counter_ignore || ctx.state.considerAsGui || ctx.state.includesLastByte === false) return
49 - if (!(ctx.vfsNode || api.getConfig('archives') && ctx.state.archive)) return
49 + if (!(ctx.state.vfsNode || api.getConfig('archives') && ctx.state.archive)) return
50 ctx.state.completed.then(() => {
51 const key = uri2key(ctx.path)
52 - const entries = ctx.vfsNode ? [key]
52 + const entries = ctx.state.vfsNode ? [key]
53 : ctx.state.originalStream?.getArchiveEntries?.().filter(x => x.at(-1) !== '/').map(x => key + uri2key(x))
54 if (!entries) return
55 for (const k of entries)
src/apiMiddleware.ts
+1
@@ -91,6 +91,7 @@ export class SendListReadable<T> extends Readable {
91 if (!bufferTime)
92 bufferTime = 200
93 this.processBuffer = _.debounce(() => {
94 + if (!this.buffer.length) return
95 this.push(this.buffer)
96 this.buffer = []
97 }, bufferTime, { maxWait: bufferTime })
src/cross.ts
+5 -1
@@ -356,10 +356,14 @@ export function xlate(input: any, table: Record<string, any>) {
356 return table[input] ?? input
357 }
358
359 -export function ipLocalHost(ip: string) {
359 +export function isIpLocalHost(ip: string) {
360 return ip === '::1' || ip.endsWith('127.0.0.1')
361 }
362
363 +export function isIpLan(ip: string) {
364 + return /^(?:|:10\..*|172\.(1[6-9]|2\d|3[01])\..*|192\.168\..*)$/.test(ip)
365 +}
366 +
367 export function ipForUrl(ip: string) {
368 return ip.includes(':') ? '[' + ip + ']' : ip
369 }
src/misc.ts
+2 -2
@@ -14,7 +14,7 @@ import { Readable } from 'stream'
14 import { SocketAddress, BlockList } from 'node:net'
15 import { ApiError } from './apiMiddleware'
16 import { HTTP_BAD_REQUEST } from './const'
17 -import { ipLocalHost, makeMatcher } from './cross'
17 +import { isIpLocalHost, makeMatcher } from './cross'
18 import { isIPv6 } from 'net'
19
20 type ProcessExitHandler = (signal:string) => any
@@ -58,7 +58,7 @@ export function onOff(em: EventEmitter, events: { [eventName:string]: (...args:
58
59 export function isLocalHost(c: Connection | Koa.Context | string) {
60 const ip = typeof c === 'string' ? c : c.socket.remoteAddress // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
61 - return ip && ipLocalHost(ip)
61 + return ip && isIpLocalHost(ip)
62 }
63
64 export function makeNetMatcher(mask: string, emptyMaskReturns=false) {