| | const ACCOUNT_API = "https://api-pro.urban-vpn.com/rest/v1"; |
| | const STATS_API = "https://stats.urban-vpn.com/api/rest/v2"; |
| | const CLIENT_APP = "URBAN_VPN_BROWSER_EXTENSION"; |
| | const BROWSER = "CHROME"; |
| |
|
| | interface UrbanProxyResult { |
| | url: string; |
| | protocol: string; |
| | host: string; |
| | port: number; |
| | username?: string; |
| | password?: string; |
| | } |
| |
|
| | const PREFERRED_COUNTRIES = ["US", "GB", "CA", "DE", "FR", "NL", "ES", "IT", "JP", "KR", "SG", "AU"]; |
| |
|
| | export async function fetchUrbanProxy(targetCountryCode = "RANDOM"): Promise<UrbanProxyResult | null> { |
| | console.log(`[UrbanVPN] Fetching Urban VPN Proxy (Target: ${targetCountryCode})...`); |
| |
|
| | |
| | |
| | const regUrl = `${ACCOUNT_API}/registrations/clientApps/${CLIENT_APP}/users/anonymous`; |
| |
|
| | const regHeaders = { |
| | "content-type": "application/json", |
| | "accept": "application/json, text/plain, */*", |
| | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" |
| | }; |
| |
|
| | const regPayload = { |
| | clientApp: { |
| | name: CLIENT_APP, |
| | browser: BROWSER |
| | } |
| | }; |
| |
|
| | let regResp; |
| | try { |
| | regResp = await fetch(regUrl, { |
| | method: "POST", |
| | headers: regHeaders, |
| | body: JSON.stringify(regPayload) |
| | }); |
| | } catch (err) { |
| | console.error("[UrbanVPN] Network error during registration:", err); |
| | return null; |
| | } |
| |
|
| | if (!regResp.ok) { |
| | const text = await regResp.text(); |
| | console.error(`[UrbanVPN] Registration failed: ${regResp.status} ${regResp.statusText}`); |
| | console.error(text); |
| | return null; |
| | } |
| |
|
| | const regData = await regResp.json(); |
| | const idToken = regData.id_token || regData.idToken || regData.value; |
| |
|
| | if (!idToken) { |
| | console.error("[UrbanVPN] No ID token found in registration response."); |
| | return null; |
| | } |
| |
|
| | |
| | |
| | const secUrl = `${ACCOUNT_API}/security/tokens/accs`; |
| | const secHeaders = { |
| | ...regHeaders, |
| | "authorization": `Bearer ${idToken}` |
| | }; |
| | const secPayload = { |
| | type: "accs", |
| | clientApp: { |
| | name: CLIENT_APP |
| | } |
| | }; |
| |
|
| | const secResp = await fetch(secUrl, { |
| | method: "POST", |
| | headers: secHeaders, |
| | body: JSON.stringify(secPayload) |
| | }); |
| |
|
| | if (!secResp.ok) { |
| | const text = await secResp.text(); |
| | console.error(`[UrbanVPN] Security Token request failed: ${secResp.status}`); |
| | console.error(text); |
| | return null; |
| | } |
| |
|
| | const secData = await secResp.json(); |
| |
|
| | let tokenString = ""; |
| | let credUsername = ""; |
| | const credPassword = "1"; |
| |
|
| | if (secData.token && typeof secData.token === 'object' && secData.token.value) { |
| | tokenString = secData.token.value; |
| | credUsername = secData.token.value; |
| | } else if (typeof secData.token === 'string') { |
| | tokenString = secData.token; |
| | credUsername = secData.token; |
| |
|
| | } else if (secData.value) { |
| | tokenString = secData.value; |
| | credUsername = secData.value; |
| | } |
| |
|
| | if (!tokenString) { |
| | console.error("[UrbanVPN] No security token found."); |
| | return null; |
| | } |
| |
|
| | |
| | |
| | const countriesUrl = `${STATS_API}/entrypoints/countries`; |
| | const proxyHeaders = { |
| | ...regHeaders, |
| | "authorization": `Bearer ${tokenString}`, |
| | "X-Client-App": CLIENT_APP |
| | }; |
| |
|
| | |
| | delete proxyHeaders["content-type"]; |
| |
|
| | const countriesResp = await fetch(countriesUrl, { |
| | headers: proxyHeaders |
| | }); |
| |
|
| | if (!countriesResp.ok) { |
| | const text = await countriesResp.text(); |
| | console.error(`[UrbanVPN] Failed to fetch countries: ${countriesResp.status}`); |
| | console.error(text); |
| | return null; |
| | } |
| |
|
| | const countriesData = await countriesResp.json(); |
| |
|
| | if (!countriesData.countries || !countriesData.countries.elements) { |
| | console.error("[UrbanVPN] Invalid countries data format."); |
| | return null; |
| | } |
| |
|
| | const countries = countriesData.countries.elements; |
| |
|
| | |
| | let selectedCountryCode = targetCountryCode; |
| | if (selectedCountryCode === "RANDOM") { |
| | selectedCountryCode = PREFERRED_COUNTRIES[Math.floor(Math.random() * PREFERRED_COUNTRIES.length)]; |
| | } |
| |
|
| | |
| | |
| | let targetCountry = countries.find((c: any) => c.code.iso2 === selectedCountryCode); |
| |
|
| | |
| | if (!targetCountry) { |
| | targetCountry = countries[0]; |
| | console.log(`[UrbanVPN] Requested country ${selectedCountryCode} not found, falling back to ${targetCountry.code.iso2}`); |
| | } |
| |
|
| | if (targetCountry) { |
| | console.log(`[UrbanVPN] Selected Country: ${targetCountry.title} (${targetCountry.code.iso2})`); |
| |
|
| | let proxyHost = null; |
| | let proxyPort = null; |
| | let signature = null; |
| |
|
| | if (targetCountry.address && targetCountry.address.primary) { |
| | proxyHost = targetCountry.address.primary.host; |
| | proxyPort = targetCountry.address.primary.port; |
| | } |
| | else if (targetCountry.servers && targetCountry.servers.elements && targetCountry.servers.elements.length > 0) { |
| | |
| | const serverIndex = Math.floor(Math.random() * targetCountry.servers.elements.length); |
| | const srv = targetCountry.servers.elements[serverIndex]; |
| |
|
| | if (srv.address && srv.address.primary) { |
| | proxyHost = srv.address.primary.host; |
| | proxyPort = srv.address.primary.port || srv.address.primary.port_min; |
| | signature = srv.signature; |
| | } |
| | } |
| |
|
| | if (signature) { |
| | |
| | const proxyTokenUrl = `${ACCOUNT_API}/security/tokens/accs-proxy`; |
| | const proxyTokenPayload = { |
| | type: "accs-proxy", |
| | clientApp: { name: CLIENT_APP }, |
| | signature: signature |
| | }; |
| |
|
| | const proxyTokenHeaders = { |
| | ...regHeaders, |
| | "authorization": `Bearer ${tokenString}` |
| | }; |
| |
|
| | const ptResp = await fetch(proxyTokenUrl, { |
| | method: "POST", |
| | headers: proxyTokenHeaders, |
| | body: JSON.stringify(proxyTokenPayload) |
| | }); |
| |
|
| | if (ptResp.ok) { |
| | const ptData = await ptResp.json(); |
| | if (ptData.value) { |
| | credUsername = ptData.value; |
| | } else if (ptData.token && ptData.token.value) { |
| | credUsername = ptData.token.value; |
| | } |
| | } else { |
| | console.error(`[UrbanVPN] Failed to get Proxy Auth Token: ${ptResp.status}`); |
| | } |
| | } |
| |
|
| | if (proxyHost) { |
| | const proxyUrl = `http://${encodeURIComponent(credUsername)}:${encodeURIComponent(credPassword)}@${proxyHost}:${proxyPort}`; |
| | console.log(`[UrbanVPN] Proxy found: ${proxyHost}:${proxyPort}`); |
| | return { |
| | url: proxyUrl, |
| | protocol: 'http', |
| | host: proxyHost, |
| | port: proxyPort, |
| | username: credUsername, |
| | password: credPassword |
| | }; |
| | } |
| | } |
| |
|
| | console.error("[UrbanVPN] No proxy server details found."); |
| | return null; |
| | } |
| |
|