From 466bad6c058a7bc286417b307987bb67f8d0dad1 Mon Sep 17 00:00:00 2001 From: localhost Date: Fri, 20 Mar 2026 22:17:17 +0100 Subject: [PATCH] add full metadata switching, more vpn ips --- bun.lockb | Bin 53201 -> 53590 bytes package.json | 1 + ranges/expressvpn.txt | 21 ++++++++++++++++ ranges/urbanvpn.txt | 32 +++++++++++-------------- ranges/vpnly.txt | 41 ++++++++++++++++++++++++++++++++ scripts/1clickvpn.ts | 2 ++ scripts/expressvpn.ts | 54 ++++++++++++++++++++++++++++++++++++++++++ scripts/urbanvpn.ts | 22 +++++++++++++++++ scripts/vpnly.ts | 32 +++++++++++++++++++++++++ src/utils/health.ts | 11 ++++++++- src/utils/metadata.ts | 7 +++--- src/utils/regex.ts | 4 +++- 12 files changed, 204 insertions(+), 23 deletions(-) create mode 100644 scripts/1clickvpn.ts create mode 100644 scripts/expressvpn.ts create mode 100644 scripts/urbanvpn.ts create mode 100644 scripts/vpnly.ts diff --git a/bun.lockb b/bun.lockb index ff755931a44c47a451103f9963e01c4099ff62b1..69e77244c350b7138e6546aad44673fab1fad6c2 100755 GIT binary patch delta 7121 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 pyOW=54)L&2{aT#G!`=M{4) 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 XU%6*vJbm(dZ%TQ6e|y9m6N3H${%0HY diff --git a/package.json b/package.json index 00a0156..8634b8e 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@elysiajs/static": "^1.4.0", "@types/html-minifier-next": "^2.1.0", "@types/pg": "^8.11.10", + "@types/ws": "^8.18.1", "age-encryption": "^0.2.4", "date-fns": "^4.1.0", "elysia": "^1.1.25", diff --git a/ranges/expressvpn.txt b/ranges/expressvpn.txt index 1fffe2f..044f5fe 100644 --- a/ranges/expressvpn.txt +++ b/ranges/expressvpn.txt @@ -6,6 +6,7 @@ 146.19.7.128/25 62.169.132.0/24 14.102.85.0/24 +158.173.167.0/24 2a0f:e385::/32 14.102.52.0/24 155.2.180.0/24 @@ -34,22 +35,27 @@ 14.102.53.0/24 185.64.78.0/25 185.64.78.128/25 +158.173.65.0/24 103.213.214.0/24 103.61.198.0/24 157.97.122.0/24 +158.173.67.0/24 185.114.34.0/24 198.55.29.0/24 2a0e:4203::/32 155.2.217.0/24 170.62.235.0/24 170.62.244.0/24 +158.173.240.0/24 170.62.245.0/24 +158.173.242.0/24 170.62.237.0/24 66.56.86.0/24 158.173.36.0/24 62.169.134.0/24 157.97.120.0/24 158.173.49.0/24 +158.173.74.0/24 170.62.246.0/24 198.55.30.0/24 2a0b:64c3::/32 @@ -152,6 +158,7 @@ 81.95.58.0/24 81.95.59.0/24 14.102.87.0/24 +158.173.165.0/24 45.130.81.0/24 107.150.166.0/24 136.144.33.0/24 @@ -178,6 +185,8 @@ 2a11:53c5::/32 158.173.4.0/24 158.173.5.0/24 +158.173.162.0/24 +158.173.244.0/24 185.245.5.0/24 203.188.167.0/24 212.78.246.0/24 @@ -213,16 +222,20 @@ 158.173.135.0/24 212.32.50.0/24 45.130.141.0/24 +158.173.72.0/24 188.240.73.0/24 212.56.49.0/24 45.91.23.0/24 +158.173.164.0/24 212.32.72.0/24 2a0f:f42::/32 45.154.138.0/24 +158.173.77.0/24 170.62.238.0/24 45.91.20.0/24 2a07:e342::/32 194.61.40.0/24 +158.173.243.0/24 194.61.41.0/24 62.169.128.0/24 217.119.143.0/25 @@ -302,11 +315,13 @@ 203.25.124.0/25 203.25.124.128/25 212.32.76.0/24 +158.173.166.0/24 170.62.247.0/24 212.78.244.0/24 45.150.93.0/24 45.92.228.0/24 2a0b:64c2::/32 +158.173.66.0/24 170.62.228.0/24 158.173.157.0/24 158.173.158.0/24 @@ -391,6 +406,7 @@ 89.47.15.0/25 89.47.15.128/25 2a0e:4205::/32 +158.173.241.0/24 158.173.44.0/24 170.62.226.0/24 170.62.89.0/24 @@ -418,6 +434,7 @@ 193.39.215.128/25 85.8.130.0/25 85.8.130.128/25 +158.173.76.0/24 203.188.169.0/24 2a0b:64c6::/32 103.61.199.0/24 @@ -439,6 +456,7 @@ 89.251.0.0/24 93.115.255.0/24 2a0f:e386::/32 +158.173.75.0/24 45.86.200.0/24 85.203.13.0/24 155.2.178.0/24 @@ -455,6 +473,7 @@ 2a0e:4202::/32 158.173.16.0/24 158.173.17.0/24 +158.173.78.0/24 192.253.210.0/24 188.213.202.0/24 194.5.49.0/24 @@ -467,8 +486,10 @@ 45.148.25.0/24 45.95.243.0/24 2a0e:d783::/32 +158.173.73.0/24 212.56.52.0/24 62.169.135.0/24 +158.173.79.0/24 136.144.42.0/24 136.144.27.0/24 170.62.230.0/24 diff --git a/ranges/urbanvpn.txt b/ranges/urbanvpn.txt index d44c6fe..d7215ef 100644 --- a/ranges/urbanvpn.txt +++ b/ranges/urbanvpn.txt @@ -47,13 +47,11 @@ 67.43.236.226/32 148.113.221.150/32 148.113.221.152/32 -173.209.51.250/32 104.245.146.82/32 135.84.180.228/32 173.209.48.162/32 173.209.49.50/32 179.43.152.90/32 -146.70.135.14/32 208.69.78.7/32 66.163.116.199/32 102.220.17.118/32 @@ -70,17 +68,17 @@ 217.138.220.50/32 217.138.220.226/32 217.138.220.94/32 -89.163.221.18/32 -193.108.116.248/32 -193.108.116.232/32 -23.160.72.45/32 -23.160.72.205/32 -23.160.72.218/32 -23.158.56.244/32 -23.160.72.50/32 -51.38.111.185/32 -51.38.121.218/32 -85.114.138.43/32 +213.202.254.242/32 +193.108.116.242/32 +193.108.116.226/32 +23.160.72.37/32 +23.160.72.206/32 +23.160.72.211/32 +193.108.116.218/32 +23.160.72.56/32 +57.129.88.70/32 +51.38.121.161/32 +5.104.107.68/32 5.104.107.251/32 82.103.131.250/32 146.70.42.202/32 @@ -134,6 +132,7 @@ 95.129.46.100/32 170.80.111.119/32 169.150.222.197/32 +61.4.121.186/32 190.92.9.46/32 178.218.162.117/32 185.104.187.130/32 @@ -147,7 +146,6 @@ 185.253.73.216/32 182.54.236.193/32 182.54.236.194/32 -148.113.8.93/32 148.113.0.105/32 148.113.47.86/32 148.113.0.104/32 @@ -170,7 +168,6 @@ 102.68.86.97/32 91.213.233.111/32 91.213.233.176/32 -79.110.55.34/32 61.255.174.11/32 140.174.179.129/32 38.54.124.170/32 @@ -197,7 +194,6 @@ 131.196.35.40/32 167.17.70.173/32 172.99.188.95/32 -51.15.16.66/32 185.181.61.141/32 83.143.82.62/32 83.143.82.58/32 @@ -218,7 +214,6 @@ 199.255.116.5/32 38.158.220.26/32 185.113.141.65/32 -185.113.140.26/32 185.113.140.45/32 38.165.233.7/32 38.165.233.30/32 @@ -252,13 +247,14 @@ 176.103.50.127/32 176.103.54.71/32 146.70.228.82/32 -167.17.66.82/32 169.197.83.34/32 169.197.85.171/32 +162.249.172.18/32 169.197.85.170/32 169.197.142.208/32 38.128.66.22/32 38.68.134.126/32 +169.197.142.119/32 162.251.62.66/32 23.154.136.106/32 169.197.85.172/32 diff --git a/ranges/vpnly.txt b/ranges/vpnly.txt index 8c2d08a..63f69c0 100644 --- a/ranges/vpnly.txt +++ b/ranges/vpnly.txt @@ -51,3 +51,44 @@ 108.181.58.33/32 46.229.243.203/32 46.229.243.178/32 +80.92.204.6/32 +80.92.204.74/32 +45.12.138.188/32 +80.71.157.209/32 +80.92.204.84/32 +45.83.129.161/32 +80.92.204.82/32 +45.14.247.113/32 +80.92.204.73/32 +80.92.204.75/32 +80.71.157.218/32 +80.71.157.239/32 +80.92.204.85/32 +80.92.204.57/32 +80.92.204.42/32 +80.92.204.62/32 +208.87.240.255/32 +108.181.0.171/32 +108.181.0.21/32 +108.181.3.145/32 +208.87.241.235/32 +208.87.241.221/32 +108.181.1.241/32 +208.87.242.111/32 +208.87.242.23/32 +108.181.4.69/32 +208.87.240.19/32 +108.181.3.149/32 +108.181.0.109/32 +208.87.242.199/32 +45.12.133.18/32 +45.12.133.12/32 +94.232.247.207/32 +94.232.247.205/32 +45.12.133.22/32 +46.229.243.200/32 +46.229.243.201/32 +46.229.243.203/32 +46.229.243.197/32 +46.229.243.199/32 +46.229.243.178/32 diff --git a/scripts/1clickvpn.ts b/scripts/1clickvpn.ts new file mode 100644 index 0000000..6ccda7f --- /dev/null +++ b/scripts/1clickvpn.ts @@ -0,0 +1,2 @@ +const oneClickVpn = await (await fetch('https://1clickvpn.net/api/v1/servers/')).json() +Bun.write('ranges/1clickvpn.txt', oneClickVpn.flatMap(v => v.nodes.map(n => n.ip + '/32')).join('\n')) \ No newline at end of file diff --git a/scripts/expressvpn.ts b/scripts/expressvpn.ts new file mode 100644 index 0000000..11effd7 --- /dev/null +++ b/scripts/expressvpn.ts @@ -0,0 +1,54 @@ +import { sleep } from "bun"; + +const file = Bun.file('ranges/expressvpn.txt'); +const writer = file.writer(); + +function inetnumToCIDR(value: string): string[] { + let [start, end] = value.split(' - ').map(ip => + ip.split('.').reduce((acc, octet) => (acc << 8) | parseInt(octet), 0) >>> 0 + ) + + const cidrs: string[] = [] + + while (start <= end) { + const maxBits = Math.floor(Math.log2(start & -start)) // largest block start allows + const fitBits = Math.floor(Math.log2(end - start + 1)) // largest block size fits + const size = Math.min(maxBits, fitBits) + const ip = [(start >>> 24) & 255, (start >>> 16) & 255, (start >>> 8) & 255, start & 255].join('.') + cidrs.push(`${ip}/${32 - size}`) + start += 2 ** size + } + + return cidrs +} + +const orgs = await (await fetch('https://apps.db.ripe.net/db-web-ui/api/whois/search?abuse-contact=true&ignore404=true&managed-attributes=true&resource-holder=true&type-filter=ORGANISATION&flags=r&offset=0&limit=200&query-string=VPN%20Consumer')).json() +const orgId = orgs.objects.object.map(o => o.attributes.attribute.find(a => a.name === 'organisation').value) + +for (const o of orgId) { + const orgReq = await fetch(`https://apps.db.ripe.net/db-web-ui/api/whois/search?abuse-contact=true&ignore404=true&managed-attributes=true&resource-holder=true&type-filter=INETNUM,INET6NUM&inverse-attribute=ORG&flags=r&offset=0&limit=200&query-string=${o}`) + if (orgReq.status == 404) { + console.log(`no inetnum/inet6num for ${o}`) + continue + } + const org = await orgReq.json() + const primaries = org.objects.object.map(o => o['primary-key'].attribute[0]) + for (const p of primaries) { + if (p.name == 'inetnum') { + console.log(inetnumToCIDR(p.value).join('\n')) + writer.write(inetnumToCIDR(p.value).join('\n') + '\n') + } else if (p.name == 'inet6num') { + console.log(p.value) + writer.write(p.value + '\n') + } + } + + writer.flush() + + if (orgReq.headers.get('X-Rate-Limit-Remaining') == '1') { + console.log('sleeping 5s') + await sleep(5000) + } +} + +writer.end(); \ No newline at end of file diff --git a/scripts/urbanvpn.ts b/scripts/urbanvpn.ts new file mode 100644 index 0000000..4916e80 --- /dev/null +++ b/scripts/urbanvpn.ts @@ -0,0 +1,22 @@ +const post = await (await fetch('https://api-pro.falais.com/rest/v1/security/tokens/accs', { + method: 'POST', + headers: { + 'authorization': 'Bearer FihZXBoQi83OomWPQgj9VqEFPzRsLz6p', + 'content-type': 'application/json' + }, + body: JSON.stringify({ + "type": "accs", + "clientApp": { + "name": "URBAN_VPN_BROWSER_EXTENSION" + } + }) +})).json() + +const servers = await (await fetch('https://stats.falais.com/api/rest/v2/entrypoints/countries', { + headers: { + 'authorization': `Bearer ${post.value}`, + 'x-client-app': 'URBAN_VPN_BROWSER_EXTENSION' + } +})).json() + +Bun.write('ranges/urbanvpn.txt', servers.countries.elements.flatMap(c => c.servers.elements.map(s => s.address.primary.ip + '/32')).join('\n')) \ No newline at end of file diff --git a/scripts/vpnly.ts b/scripts/vpnly.ts new file mode 100644 index 0000000..c966cdc --- /dev/null +++ b/scripts/vpnly.ts @@ -0,0 +1,32 @@ +import * as fs from 'node:fs' + +async function getARecords(hostname: string): Promise { + const url = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent( + hostname + )}&type=A`; + + const res = await fetch(url, { + headers: { Accept: "application/dns-json" }, + }); + + if (!res.ok) { + throw new Error(`dns query failed: ${res.status} ${res.statusText}`); + } + + const body = (await res.json()) as { + Status: number; + Answer?: Array<{ name: string; type: number; data: string }>; + }; + + if (body.Status !== 0 || !body.Answer) { + return []; + } + + return body.Answer.filter((a) => a.type === 1).map((a) => a.data); +} + +const hostnames = ['de-hub.freeruproxy.ink', 'us-hub.freeruproxy.ink', 'fr-hub.freeruproxy.ink', 'nl-hub.freeruproxy.ink'] +for (const h of hostnames) { + const records = await getARecords(h) + fs.appendFileSync('ranges/vpnly.txt', records.map(r => r + '/32').join('\n') + '\n') +} \ No newline at end of file diff --git a/src/utils/health.ts b/src/utils/health.ts index d95d874..c51e915 100644 --- a/src/utils/health.ts +++ b/src/utils/health.ts @@ -1,5 +1,13 @@ const healthStatus: Record = {} +function getMetadataBackend() { + const primary = process.env.METADATA! + const alternative = process.env.ALTERNATIVE_METADATA! + + if (healthStatus[primary] === 'healthy' || !alternative) return primary + return alternative +} + async function checkHealth() { const metadataServers: string[] = [process.env.METADATA!, process.env.ALTERNATIVE_METADATA!] await Promise.all(metadataServers.map(async (m) => { @@ -17,4 +25,5 @@ async function checkHealth() { checkHealth() setInterval(checkHealth, 5 * 60000) -export default healthStatus \ No newline at end of file +export default healthStatus +export { getMetadataBackend } \ No newline at end of file diff --git a/src/utils/metadata.ts b/src/utils/metadata.ts index 45c7e2c..253460c 100644 --- a/src/utils/metadata.ts +++ b/src/utils/metadata.ts @@ -1,15 +1,16 @@ // metadata either returns innertube or { error: string } +import { getMetadataBackend } from '@/utils/health'; async function getVideo(id: string) { - return await (await fetch(`${process.env.METADATA}/video/${id}`)).json() + return await (await fetch(`${getMetadataBackend()}/video/${id}`)).json() } async function getChannel(id: string) { - return await (await fetch(`${process.env.METADATA}/channel/${id}`)).json() + return await (await fetch(`${getMetadataBackend()}/channel/${id}`)).json() } async function getChannelVideos(id: string) { - return await (await fetch(`${process.env.METADATA}/videos/${id}`)).json() + return await (await fetch(`${getMetadataBackend()}/videos/${id}`)).json() } export { getVideo, getChannel, getChannelVideos } \ No newline at end of file diff --git a/src/utils/regex.ts b/src/utils/regex.ts index 0530c37..40293c1 100644 --- a/src/utils/regex.ts +++ b/src/utils/regex.ts @@ -1,3 +1,5 @@ +import { getMetadataBackend } from '@/utils/health'; + function validateVideo(input: string): string | false { try { const url = new URL(input); @@ -87,7 +89,7 @@ async function validateChannel(input: string): Promise