@samitouri / QOSami-HFS / commits / 997da1d5

admin/options: listen-interface "any IPv4" and "any IPv6" #611

Massimo Melina committed Jul 7, 2024 at 01:53 UTC 997da1d526054b417b2119205771f0a19d31be07
2 files changed +26 -5
admin/src/OptionsPage.ts
+13 -1
@@ -117,7 +117,19 @@ export default function OptionsPage() {
117 helperText: "Not applied to localhost. Doesn't work with proxies."
118 },
119
120 - { k: 'listen_interface', comp: SelectField, sm: 4, options: [{ label: "any", value: '' }, '127.0.0.1', '::1', ...status?.ips||[]] },
120 + {
121 + k: 'listen_interface',
122 + comp: SelectField,
123 + sm: 4,
124 + options: [
125 + { label: "any", value: '' },
126 + { label: "any IPv4", value: '0.0.0.0' },
127 + { label: "any IPv6", value: '::' },
128 + '127.0.0.1',
129 + '::1',
130 + ...status?.ips || []
131 + ]
132 + },
133 { k: 'max_kbps', ...maxSpeedDefaults, sm: 4, label: "Limit output", helperText: "Doesn't apply to localhost" },
134 { k: 'max_kbps_per_ip', ...maxSpeedDefaults, sm: 4, label: "Limit output per-IP" },
135
src/listen.ts
+13 -4
@@ -8,7 +8,7 @@ import { watchLoad } from './watchLoad'
8 import { networkInterfaces } from 'os';
9 import { newConnection } from './connections'
10 import open from 'open'
11 -import { debounceAsync, ipForUrl, makeNetMatcher, MINUTE, objSameKeys, onlyTruthy, prefix, runAt, wait, } from './misc'
11 +import { debounceAsync, ipForUrl, makeNetMatcher, MINUTE, objSameKeys, onlyTruthy, prefix, runAt, wait, xlate } from './misc'
12 import { PORT_DISABLED, ADMIN_URI, argv, DEV, IS_WINDOWS } from './const'
13 import findProcess from 'find-process'
14 import { anyAccountCanLoginAdmin } from './adminApis'
@@ -168,6 +168,14 @@ export const httpsPortCfg = defineConfig('https_port', PORT_DISABLED)
168 httpsPortCfg.sub(considerHttps)
169 listenInterface.sub(considerHttps)
170
171 +function renderHost(host: string) {
172 + return xlate(host, {
173 + '0.0.0.0': "any IPv4",
174 + '::': "any IPv6",
175 + '': "any network",
176 + })
177 +}
178 +
179 interface StartServer { port: number, host?:string }
180 export function startServer(srv: typeof httpSrv, { port, host }: StartServer) {
181 return new Promise<number>(async resolve => {
@@ -179,7 +187,7 @@ export function startServer(srv: typeof httpSrv, { port, host }: StartServer) {
187 srv.on('checkContinue', (req, res) => srv.emit('request', req, res))
188 port = await listen(host)
189 if (port)
182 - console.log(srv.name, "serving on", host||"any network", ':', port)
190 + console.log(srv.name, "serving on", renderHost(host || ''), ':', port)
191 resolve(port)
192 }
193 catch(e) {
@@ -272,8 +280,9 @@ const ignore = /^(lo|.*loopback.*|virtualbox.*|.*\(wsl\).*|llw\d|awdl\d|utun\d|a
280 const isLinkLocal = makeNetMatcher('169.254.0.0/16|FE80::/16')
281
282 export async function getIps(external=true) {
283 + const only = { '0.0.0.0': 'IPv4', '::' : 'IPv6' }[listenInterface.get()] || ''
284 const ips = onlyTruthy(Object.entries(networkInterfaces()).flatMap(([name, nets]) =>
276 - nets && !ignore.test(name) && nets.map(net => !net.internal && net.address)
285 + nets && !ignore.test(name) && nets.map(net => !net.internal && (!only || only === net.family) && net.address)
286 ))
287 const e = external && defaultBaseUrl.externalIp
288 if (e && !ips.includes(e))
@@ -289,7 +298,7 @@ export async function getIps(external=true) {
298
299 export async function getUrls() {
300 const on = listenInterface.get()
292 - const ips = on ? [on] : await getIps()
301 + const ips = on === renderHost(on) ? [on] : await getIps()
302 return Object.fromEntries(onlyTruthy([httpSrv, httpsSrv].map(srv => {
303 if (!srv?.listening)
304 return false