mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
style: adopt formatting for CCTV and radio provider modules
This commit is contained in:
1 parent
79b4929b88
commit
c69416ff6b
13 files changed
+862
-364
No files matched your search
@@ -2,44 +2,67 @@ import https from 'node:https';
|
||||
import { Readable } from 'node:stream';
|
||||
import { isNonGlobalIpv4 } from './stations.js';
|
||||
export function radioMirrorOrigin(value) {
|
||||
const hostname = String(value ?? '').toLowerCase().replace(/\.$/, '');
|
||||
const hostname = String(value ?? '')
|
||||
.toLowerCase()
|
||||
.replace(/\.$/, '');
|
||||
if (!/^[a-z0-9-]+\.api\.radio-browser\.info$/.test(hostname)) return null;
|
||||
return `https://${hostname}`;
|
||||
}
|
||||
|
||||
/** Return whether a resolved Radio Browser address is safe for an outbound request. */
|
||||
export function isPublicRadioAddress(value) {
|
||||
const address = String(value ?? '').trim().toLowerCase().replace(/^\[|\]$/g, '');
|
||||
const address = String(value ?? '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/^\[|\]$/g, '');
|
||||
if (!address) return false;
|
||||
if (!address.includes(':')) {
|
||||
const ipv4 = address.split('.');
|
||||
return ipv4.length === 4
|
||||
&& ipv4.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255)
|
||||
&& !isNonGlobalIpv4(address);
|
||||
return (
|
||||
ipv4.length === 4 &&
|
||||
ipv4.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255) &&
|
||||
!isNonGlobalIpv4(address)
|
||||
);
|
||||
}
|
||||
const pieces = address.split('::');
|
||||
if (pieces.length > 2) return false;
|
||||
const left = pieces[0] ? pieces[0].split(':') : [];
|
||||
const right = pieces[1] ? pieces[1].split(':') : [];
|
||||
const missing = 8 - left.length - right.length;
|
||||
if ((pieces.length === 1 && missing !== 0) || (pieces.length === 2 && missing < 1)) return false;
|
||||
if (
|
||||
(pieces.length === 1 && missing !== 0) ||
|
||||
(pieces.length === 2 && missing < 1)
|
||||
)
|
||||
return false;
|
||||
const groups = [...left, ...Array(Math.max(0, missing)).fill('0'), ...right];
|
||||
if (groups.length !== 8 || groups.some((group) => !/^[0-9a-f]{1,4}$/.test(group))) return false;
|
||||
const numeric = groups.reduce((total, group) => (total << 16n) | BigInt(`0x${group}`), 0n);
|
||||
const inCidr = (base, prefix) => {
|
||||
const shift = 128n - BigInt(prefix);
|
||||
return (numeric >> shift) === (base >> shift);
|
||||
};
|
||||
const base = (text) => text.split(':').reduce(
|
||||
(total, group) => (total << 16n) | BigInt(`0x${group || '0'}`),
|
||||
if (
|
||||
groups.length !== 8 ||
|
||||
groups.some((group) => !/^[0-9a-f]{1,4}$/.test(group))
|
||||
)
|
||||
return false;
|
||||
const numeric = groups.reduce(
|
||||
(total, group) => (total << 16n) | BigInt(`0x${group}`),
|
||||
0n,
|
||||
);
|
||||
const inCidr = (base, prefix) => {
|
||||
const shift = 128n - BigInt(prefix);
|
||||
return numeric >> shift === base >> shift;
|
||||
};
|
||||
const base = (text) =>
|
||||
text
|
||||
.split(':')
|
||||
.reduce(
|
||||
(total, group) => (total << 16n) | BigInt(`0x${group || '0'}`),
|
||||
0n,
|
||||
);
|
||||
const cidr = (text, prefix) => inCidr(base(text), prefix);
|
||||
return cidr('2000:0:0:0:0:0:0:0', 3)
|
||||
&& !cidr('2001:0:0:0:0:0:0:0', 23)
|
||||
&& !cidr('2001:db8:0:0:0:0:0:0', 32)
|
||||
&& !cidr('2002:0:0:0:0:0:0:0', 16)
|
||||
&& !cidr('3fff:0:0:0:0:0:0:0', 20);
|
||||
return (
|
||||
cidr('2000:0:0:0:0:0:0:0', 3) &&
|
||||
!cidr('2001:0:0:0:0:0:0:0', 23) &&
|
||||
!cidr('2001:db8:0:0:0:0:0:0', 32) &&
|
||||
!cidr('2002:0:0:0:0:0:0:0', 16) &&
|
||||
!cidr('3fff:0:0:0:0:0:0:0', 20)
|
||||
);
|
||||
}
|
||||
|
||||
export function radioProxyDestination(value) {
|
||||
@@ -51,16 +74,18 @@ export function radioProxyDestination(value) {
|
||||
}
|
||||
const origin = radioMirrorOrigin(url.hostname);
|
||||
if (
|
||||
!origin
|
||||
|| url.origin !== origin
|
||||
|| url.username
|
||||
|| url.password
|
||||
|| url.port
|
||||
|| url.hash
|
||||
) return null;
|
||||
const discovery = url.hostname.toLowerCase() === 'all.api.radio-browser.info'
|
||||
&& url.pathname === '/json/servers'
|
||||
&& !url.search;
|
||||
!origin ||
|
||||
url.origin !== origin ||
|
||||
url.username ||
|
||||
url.password ||
|
||||
url.port ||
|
||||
url.hash
|
||||
)
|
||||
return null;
|
||||
const discovery =
|
||||
url.hostname.toLowerCase() === 'all.api.radio-browser.info' &&
|
||||
url.pathname === '/json/servers' &&
|
||||
!url.search;
|
||||
const directory = url.pathname === '/json/stations/search';
|
||||
const click = /^\/json\/url\/[0-9a-f-]+$/i.test(url.pathname) && !url.search;
|
||||
return discovery || directory || click ? url : null;
|
||||
@@ -70,9 +95,15 @@ export async function resolveRadioProxyAddresses(hostname, lookupImpl) {
|
||||
const resolved = await lookupImpl(hostname, { all: true, verbatim: true });
|
||||
const rows = Array.isArray(resolved) ? resolved : [resolved];
|
||||
const addresses = rows
|
||||
.map((row) => ({ address: String(row?.address || ''), family: Number(row?.family) || undefined }))
|
||||
.map((row) => ({
|
||||
address: String(row?.address || ''),
|
||||
family: Number(row?.family) || undefined,
|
||||
}))
|
||||
.filter((row) => row.address);
|
||||
if (!addresses.length || addresses.some((row) => !isPublicRadioAddress(row.address))) {
|
||||
if (
|
||||
!addresses.length ||
|
||||
addresses.some((row) => !isPublicRadioAddress(row.address))
|
||||
) {
|
||||
throw new Error('Radio Browser resolved to a forbidden address');
|
||||
}
|
||||
return addresses;
|
||||
@@ -81,26 +112,33 @@ export async function resolveRadioProxyAddresses(hostname, lookupImpl) {
|
||||
export function fetchPinnedRadioResponse(url, options, addresses) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const address = addresses[0];
|
||||
const request = https.request(url, {
|
||||
method: 'GET',
|
||||
headers: options.headers,
|
||||
signal: options.signal,
|
||||
lookup(_hostname, lookupOptions, callback) {
|
||||
if (lookupOptions?.all) callback(null, addresses);
|
||||
else callback(null, address.address, address.family);
|
||||
const request = https.request(
|
||||
url,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: options.headers,
|
||||
signal: options.signal,
|
||||
lookup(_hostname, lookupOptions, callback) {
|
||||
if (lookupOptions?.all) callback(null, addresses);
|
||||
else callback(null, address.address, address.family);
|
||||
},
|
||||
},
|
||||
}, (response) => {
|
||||
const headers = new Headers();
|
||||
for (const [name, value] of Object.entries(response.headers)) {
|
||||
if (Array.isArray(value)) value.forEach((item) => headers.append(name, item));
|
||||
else if (value !== undefined) headers.set(name, String(value));
|
||||
}
|
||||
resolve(new Response(Readable.toWeb(response), {
|
||||
status: response.statusCode || 500,
|
||||
statusText: response.statusMessage || '',
|
||||
headers,
|
||||
}));
|
||||
});
|
||||
(response) => {
|
||||
const headers = new Headers();
|
||||
for (const [name, value] of Object.entries(response.headers)) {
|
||||
if (Array.isArray(value))
|
||||
value.forEach((item) => headers.append(name, item));
|
||||
else if (value !== undefined) headers.set(name, String(value));
|
||||
}
|
||||
resolve(
|
||||
new Response(Readable.toWeb(response), {
|
||||
status: response.statusCode || 500,
|
||||
statusText: response.statusMessage || '',
|
||||
headers,
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
request.on('error', reject);
|
||||
request.end();
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user