| 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; |
| } |
|
|