KiWA001 commited on
Commit
2dc7238
Β·
1 Parent(s): ffbf0f9

Add free proxy rotation system for ChatGPT and Copilot

Browse files

Features:
- FreeProxyManager class that fetches proxies from multiple sources
- Proxy testing to ensure IPs are active and working
- Proxy rotation with one-click IP switching
- Endpoints:
* POST /qaz/proxy/fetch - Fetch and test new proxies
* POST /qaz/proxy/rotate - Rotate to new working proxy
* GET /qaz/proxy/status - Check proxy status
* POST /qaz/proxy/test - Test current proxy
* POST /qaz/portal/{provider}/restart-with-proxy - Restart portal with proxy
- UI controls in portal interface:
* πŸ”„ Fetch New IPs - Get fresh proxies
* 🎲 Rotate IP - Switch to different proxy
* βœ… Test Current IP - Verify proxy is working
* πŸš€ Restart with Proxy - Apply proxy to portal
- Shows proxy country and response time
- Works specifically for ChatGPT and Copilot to bypass IP restrictions

This helps avoid blocks by rotating through free proxies from different countries!

Files changed (4) hide show
  1. admin_router.py +132 -0
  2. browser_portal.py +16 -6
  3. proxy_manager.py +217 -0
  4. static/qaz.html +158 -0
admin_router.py CHANGED
@@ -662,3 +662,135 @@ async def get_all_portal_status():
662
  }
663
  except Exception as e:
664
  raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  }
663
  except Exception as e:
664
  raise HTTPException(status_code=500, detail=str(e))
665
+
666
+
667
+ # --- Proxy Management for Browser Portals ---
668
+
669
+ @router.post("/proxy/fetch")
670
+ async def fetch_new_proxies():
671
+ """Fetch new free proxies and test them."""
672
+ try:
673
+ from proxy_manager import get_proxy_manager
674
+
675
+ proxy_mgr = get_proxy_manager()
676
+
677
+ # Fetch new proxies
678
+ proxies = await proxy_mgr.fetch_proxies(limit=30)
679
+
680
+ # Test first few to find a working one
681
+ working_proxy = await proxy_mgr.get_working_proxy(max_attempts=5)
682
+
683
+ stats = proxy_mgr.get_proxy_stats()
684
+
685
+ return {
686
+ "status": "success",
687
+ "message": f"Fetched {len(proxies)} proxies",
688
+ "working_proxy": str(working_proxy) if working_proxy else None,
689
+ "stats": stats
690
+ }
691
+ except Exception as e:
692
+ raise HTTPException(status_code=500, detail=str(e))
693
+
694
+ @router.post("/proxy/rotate")
695
+ async def rotate_proxy():
696
+ """Rotate to a new working proxy."""
697
+ try:
698
+ from proxy_manager import get_proxy_manager
699
+
700
+ proxy_mgr = get_proxy_manager()
701
+
702
+ # Rotate to new proxy
703
+ new_proxy = await proxy_mgr.rotate_proxy()
704
+
705
+ if new_proxy:
706
+ return {
707
+ "status": "success",
708
+ "proxy": str(new_proxy),
709
+ "country": new_proxy.country,
710
+ "response_time": f"{new_proxy.response_time:.2f}s"
711
+ }
712
+ else:
713
+ raise HTTPException(status_code=503, detail="No working proxy available")
714
+ except HTTPException:
715
+ raise
716
+ except Exception as e:
717
+ raise HTTPException(status_code=500, detail=str(e))
718
+
719
+ @router.get("/proxy/status")
720
+ async def get_proxy_status():
721
+ """Get current proxy status."""
722
+ try:
723
+ from proxy_manager import get_proxy_manager
724
+
725
+ proxy_mgr = get_proxy_manager()
726
+
727
+ stats = proxy_mgr.get_proxy_stats()
728
+ current = proxy_mgr.get_current_proxy()
729
+
730
+ return {
731
+ "current_proxy": str(current) if current else None,
732
+ **stats
733
+ }
734
+ except Exception as e:
735
+ raise HTTPException(status_code=500, detail=str(e))
736
+
737
+ @router.post("/proxy/test")
738
+ async def test_current_proxy():
739
+ """Test if current proxy is working."""
740
+ try:
741
+ from proxy_manager import get_proxy_manager
742
+
743
+ proxy_mgr = get_proxy_manager()
744
+ current = proxy_mgr.get_current_proxy()
745
+
746
+ if not current:
747
+ raise HTTPException(status_code=400, detail="No proxy currently set")
748
+
749
+ is_working = await proxy_mgr.test_proxy(current)
750
+
751
+ return {
752
+ "status": "success",
753
+ "proxy": str(current),
754
+ "is_working": is_working,
755
+ "response_time": f"{current.response_time:.2f}s" if is_working else None
756
+ }
757
+ except HTTPException:
758
+ raise
759
+ except Exception as e:
760
+ raise HTTPException(status_code=500, detail=str(e))
761
+
762
+ @router.post("/portal/{provider}/restart-with-proxy")
763
+ async def restart_portal_with_proxy(provider: str):
764
+ """Restart portal with current proxy."""
765
+ try:
766
+ from browser_portal import get_portal_manager, PortalProvider
767
+ from proxy_manager import get_proxy_manager
768
+
769
+ prov = PortalProvider(provider.lower())
770
+ portal = get_portal_manager().get_portal(prov)
771
+ proxy_mgr = get_proxy_manager()
772
+
773
+ # Get current proxy
774
+ current_proxy = proxy_mgr.get_current_proxy()
775
+ if not current_proxy:
776
+ # Fetch and test a new one
777
+ current_proxy = await proxy_mgr.get_working_proxy()
778
+ if not current_proxy:
779
+ raise HTTPException(status_code=503, detail="No working proxy available")
780
+
781
+ # Close existing portal
782
+ await portal.close()
783
+
784
+ # Reinitialize with proxy
785
+ await portal.initialize(headless=True, proxy=current_proxy)
786
+
787
+ return {
788
+ "status": "success",
789
+ "provider": provider,
790
+ "proxy": str(current_proxy),
791
+ "message": f"{provider} portal restarted with proxy {current_proxy.ip}"
792
+ }
793
+ except HTTPException:
794
+ raise
795
+ except Exception as e:
796
+ raise HTTPException(status_code=500, detail=str(e))
browser_portal.py CHANGED
@@ -104,13 +104,16 @@ class BrowserPortal:
104
  self.last_activity = None
105
  self.is_logged_in = False
106
 
107
- async def initialize(self, headless: bool = True):
108
- """Initialize the browser with enhanced stealth."""
109
  if self.is_initialized:
110
  return
111
 
112
  try:
113
  logger.info(f"πŸš€ Portal [{self.provider.value}]: Launching browser...")
 
 
 
114
  self.playwright = await async_playwright().start()
115
 
116
  # Enhanced stealth args
@@ -125,10 +128,17 @@ class BrowserPortal:
125
  "--force-color-profile=srgb",
126
  ]
127
 
128
- self.browser = await self.playwright.chromium.launch(
129
- headless=headless,
130
- args=args,
131
- )
 
 
 
 
 
 
 
132
 
133
  self.context = await self.browser.new_context(
134
  viewport=self.config.viewport,
 
104
  self.last_activity = None
105
  self.is_logged_in = False
106
 
107
+ async def initialize(self, headless: bool = True, proxy: Optional[Any] = None):
108
+ """Initialize the browser with enhanced stealth and optional proxy."""
109
  if self.is_initialized:
110
  return
111
 
112
  try:
113
  logger.info(f"πŸš€ Portal [{self.provider.value}]: Launching browser...")
114
+ if proxy:
115
+ logger.info(f"Using proxy: {proxy}")
116
+
117
  self.playwright = await async_playwright().start()
118
 
119
  # Enhanced stealth args
 
128
  "--force-color-profile=srgb",
129
  ]
130
 
131
+ # Build browser launch options
132
+ launch_options = {
133
+ "headless": headless,
134
+ "args": args,
135
+ }
136
+
137
+ # Add proxy if provided
138
+ if proxy:
139
+ launch_options["proxy"] = proxy.to_playwright_format()
140
+
141
+ self.browser = await self.playwright.chromium.launch(**launch_options)
142
 
143
  self.context = await self.browser.new_context(
144
  viewport=self.config.viewport,
proxy_manager.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Free Proxy Manager for Browser Portals
3
+ --------------------------------------
4
+ Fetches and manages free proxy servers for ChatGPT and Copilot.
5
+ Automatically tests proxies to ensure they're active.
6
+ """
7
+
8
+ import asyncio
9
+ import aiohttp
10
+ import random
11
+ import logging
12
+ from typing import Optional, List, Dict
13
+ from dataclasses import dataclass
14
+ from datetime import datetime, timedelta
15
+
16
+ logger = logging.getLogger("kai_api.proxy_manager")
17
+
18
+ @dataclass
19
+ class Proxy:
20
+ """Represents a proxy server."""
21
+ ip: str
22
+ port: int
23
+ country: str
24
+ protocol: str = "http"
25
+ last_tested: Optional[datetime] = None
26
+ is_working: bool = False
27
+ response_time: float = 0.0
28
+
29
+ def __str__(self):
30
+ return f"{self.protocol}://{self.ip}:{self.port}"
31
+
32
+ def to_playwright_format(self) -> Dict:
33
+ """Convert to Playwright proxy format."""
34
+ return {
35
+ "server": f"{self.protocol}://{self.ip}:{self.port}",
36
+ }
37
+
38
+
39
+ class FreeProxyManager:
40
+ """Manages free proxy servers for browser portals."""
41
+
42
+ def __init__(self):
43
+ self.proxies: List[Proxy] = []
44
+ self.current_proxy: Optional[Proxy] = None
45
+ self.proxy_history: List[Proxy] = []
46
+ self.max_history = 10
47
+
48
+ async def fetch_proxies(self, limit: int = 20) -> List[Proxy]:
49
+ """Fetch free proxies from multiple sources."""
50
+ proxies = []
51
+
52
+ # Source 1: proxylist.geonode.com
53
+ try:
54
+ url = f"https://proxylist.geonode.com/api/proxy-list?limit={limit}&page=1&sort_by=lastChecked&sort_type=desc&protocols=http%2Chttps"
55
+ async with aiohttp.ClientSession() as session:
56
+ async with session.get(url, timeout=10) as response:
57
+ if response.status == 200:
58
+ data = await response.json()
59
+ for item in data.get('data', []):
60
+ proxy = Proxy(
61
+ ip=item['ip'],
62
+ port=int(item['port']),
63
+ country=item.get('country', 'Unknown'),
64
+ protocol=item.get('protocols', ['http'])[0] if isinstance(item.get('protocols'), list) else 'http'
65
+ )
66
+ proxies.append(proxy)
67
+ logger.info(f"Fetched proxy: {proxy}")
68
+ except Exception as e:
69
+ logger.warning(f"Failed to fetch from geonode: {e}")
70
+
71
+ # Source 2: proxy-list.download
72
+ try:
73
+ url = "https://www.proxy-list.download/api/v1/get?type=http"
74
+ async with aiohttp.ClientSession() as session:
75
+ async with session.get(url, timeout=10) as response:
76
+ if response.status == 200:
77
+ text = await response.text()
78
+ lines = text.strip().split('\n')
79
+ for line in lines[:limit]:
80
+ if ':' in line:
81
+ ip, port = line.strip().split(':')
82
+ proxy = Proxy(
83
+ ip=ip,
84
+ port=int(port),
85
+ country='Unknown',
86
+ protocol='http'
87
+ )
88
+ if proxy not in proxies:
89
+ proxies.append(proxy)
90
+ except Exception as e:
91
+ logger.warning(f"Failed to fetch from proxy-list: {e}")
92
+
93
+ # Source 3: free-proxy-list.net (simple API)
94
+ try:
95
+ # This is a fallback - scrape a few common free proxies
96
+ fallback_proxies = [
97
+ ("20.235.104.105", 3128, "US"),
98
+ ("159.89.49.172", 3128, "US"),
99
+ ("20.210.113.32", 8123, "US"),
100
+ ("103.152.232.142", 8080, "ID"),
101
+ ("43.135.166.179", 8080, "SG"),
102
+ ("47.74.152.190", 8888, "JP"),
103
+ ("52.196.1.179", 8080, "JP"),
104
+ ("13.231.21.152", 3128, "JP"),
105
+ ("54.179.34.32", 3128, "SG"),
106
+ ("18.141.176.104", 3128, "SG"),
107
+ ]
108
+
109
+ for ip, port, country in fallback_proxies:
110
+ proxy = Proxy(ip=ip, port=port, country=country, protocol='http')
111
+ if proxy not in proxies:
112
+ proxies.append(proxy)
113
+
114
+ except Exception as e:
115
+ logger.warning(f"Fallback proxy error: {e}")
116
+
117
+ logger.info(f"Total proxies fetched: {len(proxies)}")
118
+ return proxies
119
+
120
+ async def test_proxy(self, proxy: Proxy, test_url: str = "https://httpbin.org/ip") -> bool:
121
+ """Test if a proxy is working."""
122
+ try:
123
+ timeout = aiohttp.ClientTimeout(total=10)
124
+
125
+ async with aiohttp.ClientSession(timeout=timeout) as session:
126
+ start_time = asyncio.get_event_loop().time()
127
+
128
+ async with session.get(
129
+ test_url,
130
+ proxy=f"http://{proxy.ip}:{proxy.port}",
131
+ ssl=False
132
+ ) as response:
133
+ elapsed = asyncio.get_event_loop().time() - start_time
134
+
135
+ if response.status == 200:
136
+ proxy.is_working = True
137
+ proxy.response_time = elapsed
138
+ proxy.last_tested = datetime.now()
139
+ logger.info(f"βœ… Proxy working: {proxy} ({elapsed:.2f}s)")
140
+ return True
141
+ else:
142
+ proxy.is_working = False
143
+ logger.warning(f"❌ Proxy failed with status {response.status}: {proxy}")
144
+ return False
145
+
146
+ except Exception as e:
147
+ proxy.is_working = False
148
+ logger.warning(f"❌ Proxy test failed: {proxy} - {str(e)}")
149
+ return False
150
+
151
+ async def get_working_proxy(self, max_attempts: int = 5) -> Optional[Proxy]:
152
+ """Get a working proxy, testing multiple if needed."""
153
+ # First, try to use current proxy if it exists and is working
154
+ if self.current_proxy and self.current_proxy.is_working:
155
+ # Retest it to make sure it's still working
156
+ if await self.test_proxy(self.current_proxy):
157
+ return self.current_proxy
158
+
159
+ # Fetch new proxies if we don't have enough
160
+ if len(self.proxies) < 5:
161
+ logger.info("Fetching new proxies...")
162
+ self.proxies = await self.fetch_proxies(limit=30)
163
+
164
+ # Test proxies until we find a working one
165
+ random.shuffle(self.proxies) # Randomize to distribute load
166
+
167
+ for i, proxy in enumerate(self.proxies[:max_attempts]):
168
+ logger.info(f"Testing proxy {i+1}/{max_attempts}: {proxy}")
169
+
170
+ if await self.test_proxy(proxy):
171
+ # Save current to history
172
+ if self.current_proxy:
173
+ self.proxy_history.insert(0, self.current_proxy)
174
+ if len(self.proxy_history) > self.max_history:
175
+ self.proxy_history.pop()
176
+
177
+ self.current_proxy = proxy
178
+ return proxy
179
+
180
+ logger.error("No working proxy found!")
181
+ return None
182
+
183
+ async def rotate_proxy(self) -> Optional[Proxy]:
184
+ """Rotate to a new working proxy."""
185
+ logger.info("Rotating to new proxy...")
186
+
187
+ # Mark current as not working
188
+ if self.current_proxy:
189
+ self.current_proxy.is_working = False
190
+
191
+ # Get a new working proxy
192
+ return await self.get_working_proxy()
193
+
194
+ def get_current_proxy(self) -> Optional[Proxy]:
195
+ """Get the current active proxy."""
196
+ return self.current_proxy
197
+
198
+ def get_proxy_stats(self) -> Dict:
199
+ """Get statistics about proxies."""
200
+ working = sum(1 for p in self.proxies if p.is_working)
201
+ return {
202
+ "total_proxies": len(self.proxies),
203
+ "working_proxies": working,
204
+ "current_proxy": str(self.current_proxy) if self.current_proxy else None,
205
+ "proxy_history": [str(p) for p in self.proxy_history[:5]]
206
+ }
207
+
208
+
209
+ # Global proxy manager instance
210
+ _proxy_manager: Optional[FreeProxyManager] = None
211
+
212
+ def get_proxy_manager() -> FreeProxyManager:
213
+ """Get the global proxy manager instance."""
214
+ global _proxy_manager
215
+ if _proxy_manager is None:
216
+ _proxy_manager = FreeProxyManager()
217
+ return _proxy_manager
static/qaz.html CHANGED
@@ -1150,8 +1150,166 @@
1150
  </div>
1151
  <div id="portal-response" style="display: none; margin-top: 15px; padding: 15px; background: var(--surface); border-radius: 6px; border: 1px solid var(--border); white-space: pre-wrap; font-family: monospace; font-size: 13px; max-height: 200px; overflow-y: auto;"></div>
1152
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1153
  </div>
1154
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1155
  </body>
1156
 
1157
  </html>
 
1150
  </div>
1151
  <div id="portal-response" style="display: none; margin-top: 15px; padding: 15px; background: var(--surface); border-radius: 6px; border: 1px solid var(--border); white-space: pre-wrap; font-family: monospace; font-size: 13px; max-height: 200px; overflow-y: auto;"></div>
1152
  </div>
1153
+
1154
+ <!-- 🌐 Proxy Rotation Controls -->
1155
+ <div style="background: linear-gradient(135deg, rgba(139, 92, 246, 0.1), rgba(59, 130, 246, 0.1)); border: 2px solid var(--accent); border-radius: 8px; padding: 15px; margin-top: 20px;">
1156
+ <h4 style="color: var(--accent); margin-bottom: 10px; display: flex; align-items: center;">
1157
+ 🌐 Proxy Rotation for ChatGPT & Copilot
1158
+ <span id="proxy-status-badge" style="font-size: 11px; background: var(--surface); padding: 2px 8px; border-radius: 4px; margin-left: 10px; color: var(--text-muted);">No Proxy</span>
1159
+ </h4>
1160
+ <p style="color: var(--text-muted); font-size: 12px; margin-bottom: 12px;">
1161
+ Rotate IPs to bypass restrictions. Free proxies from multiple countries.
1162
+ </p>
1163
+
1164
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px; margin-bottom: 12px;">
1165
+ <button onclick="fetchNewProxies()" style="background: var(--accent); color: white; border: none; padding: 10px; border-radius: 6px; font-weight: 600; cursor: pointer; font-size: 12px;">
1166
+ πŸ”„ Fetch New IPs
1167
+ </button>
1168
+ <button onclick="rotateProxy()" style="background: var(--warning); color: black; border: none; padding: 10px; border-radius: 6px; font-weight: 600; cursor: pointer; font-size: 12px;">
1169
+ 🎲 Rotate IP
1170
+ </button>
1171
+ <button onclick="testProxy()" style="background: var(--success); color: white; border: none; padding: 10px; border-radius: 6px; font-weight: 600; cursor: pointer; font-size: 12px;">
1172
+ βœ… Test Current IP
1173
+ </button>
1174
+ <button onclick="restartWithProxy()" style="background: var(--surface); color: var(--text); border: 1px solid var(--border); padding: 10px; border-radius: 6px; font-weight: 600; cursor: pointer; font-size: 12px;">
1175
+ πŸš€ Restart with Proxy
1176
+ </button>
1177
+ </div>
1178
+
1179
+ <div id="proxy-info" style="background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 10px; font-family: monospace; font-size: 12px; display: none;">
1180
+ <div><strong>Current IP:</strong> <span id="current-proxy-display">None</span></div>
1181
+ <div><strong>Country:</strong> <span id="proxy-country-display">-</span></div>
1182
+ <div><strong>Response Time:</strong> <span id="proxy-response-display">-</span></div>
1183
+ </div>
1184
+
1185
+ <div id="proxy-message" style="margin-top: 8px; font-size: 12px; color: var(--text-muted);"></div>
1186
+ </div>
1187
  </div>
1188
  </div>
1189
+
1190
+ <script>
1191
+ // 🌐 Proxy Management Functions
1192
+ async function fetchNewProxies() {
1193
+ const btn = event.target;
1194
+ const originalText = btn.textContent;
1195
+ btn.textContent = 'Fetching...';
1196
+ btn.disabled = true;
1197
+
1198
+ try {
1199
+ const res = await fetch('/qaz/proxy/fetch', { method: 'POST' });
1200
+ const data = await res.json();
1201
+
1202
+ if (data.status === 'success') {
1203
+ updateProxyDisplay(data.working_proxy, data.stats);
1204
+ showProxyMessage(`βœ… Fetched ${data.stats.total_proxies} proxies. Working: ${data.stats.working_proxies}`);
1205
+ } else {
1206
+ throw new Error(data.message);
1207
+ }
1208
+ } catch (e) {
1209
+ showProxyMessage('❌ Error: ' + e.message);
1210
+ } finally {
1211
+ btn.textContent = originalText;
1212
+ btn.disabled = false;
1213
+ }
1214
+ }
1215
+
1216
+ async function rotateProxy() {
1217
+ const btn = event.target;
1218
+ const originalText = btn.textContent;
1219
+ btn.textContent = 'Rotating...';
1220
+ btn.disabled = true;
1221
+
1222
+ try {
1223
+ const res = await fetch('/qaz/proxy/rotate', { method: 'POST' });
1224
+ const data = await res.json();
1225
+
1226
+ if (data.status === 'success') {
1227
+ document.getElementById('proxy-status-badge').textContent = 'Active';
1228
+ document.getElementById('proxy-status-badge').style.color = 'var(--success)';
1229
+ document.getElementById('current-proxy-display').textContent = data.proxy;
1230
+ document.getElementById('proxy-country-display').textContent = data.country;
1231
+ document.getElementById('proxy-response-display').textContent = data.response_time;
1232
+ document.getElementById('proxy-info').style.display = 'block';
1233
+ showProxyMessage(`βœ… Rotated to new IP: ${data.country} (${data.response_time})`);
1234
+ } else {
1235
+ throw new Error(data.message);
1236
+ }
1237
+ } catch (e) {
1238
+ showProxyMessage('❌ Error: ' + e.message);
1239
+ } finally {
1240
+ btn.textContent = originalText;
1241
+ btn.disabled = false;
1242
+ }
1243
+ }
1244
+
1245
+ async function testProxy() {
1246
+ const btn = event.target;
1247
+ const originalText = btn.textContent;
1248
+ btn.textContent = 'Testing...';
1249
+ btn.disabled = true;
1250
+
1251
+ try {
1252
+ const res = await fetch('/qaz/proxy/test', { method: 'POST' });
1253
+ const data = await res.json();
1254
+
1255
+ if (data.is_working) {
1256
+ showProxyMessage(`βœ… Proxy working! Response time: ${data.response_time}`);
1257
+ } else {
1258
+ showProxyMessage('❌ Proxy not working. Try rotating.');
1259
+ }
1260
+ } catch (e) {
1261
+ showProxyMessage('❌ Error: ' + e.message);
1262
+ } finally {
1263
+ btn.textContent = originalText;
1264
+ btn.disabled = false;
1265
+ }
1266
+ }
1267
+
1268
+ async function restartWithProxy() {
1269
+ if (!currentProvider) {
1270
+ alert('Please select a provider first');
1271
+ return;
1272
+ }
1273
+
1274
+ const btn = event.target;
1275
+ const originalText = btn.textContent;
1276
+ btn.textContent = 'Restarting...';
1277
+ btn.disabled = true;
1278
+
1279
+ try {
1280
+ const res = await fetch(`/qaz/portal/${currentProvider}/restart-with-proxy`, { method: 'POST' });
1281
+ const data = await res.json();
1282
+
1283
+ if (data.status === 'success') {
1284
+ showProxyMessage(`βœ… ${data.message}`);
1285
+ setTimeout(() => takePortalScreenshot(), 3000);
1286
+ } else {
1287
+ throw new Error(data.message);
1288
+ }
1289
+ } catch (e) {
1290
+ showProxyMessage('❌ Error: ' + e.message);
1291
+ } finally {
1292
+ btn.textContent = originalText;
1293
+ btn.disabled = false;
1294
+ }
1295
+ }
1296
+
1297
+ function updateProxyDisplay(proxy, stats) {
1298
+ if (proxy) {
1299
+ document.getElementById('current-proxy-display').textContent = proxy;
1300
+ document.getElementById('proxy-info').style.display = 'block';
1301
+ }
1302
+ }
1303
+
1304
+ function showProxyMessage(msg) {
1305
+ const el = document.getElementById('proxy-message');
1306
+ el.textContent = msg;
1307
+ el.style.color = msg.startsWith('βœ…') ? 'var(--success)' : 'var(--error)';
1308
+ setTimeout(() => {
1309
+ el.textContent = '';
1310
+ }, 5000);
1311
+ }
1312
+ </script>
1313
  </body>
1314
 
1315
  </html>