unitlab 2.3.16__tar.gz → 2.3.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unitlab-2.3.16/src/unitlab.egg-info → unitlab-2.3.18}/PKG-INFO +1 -1
- {unitlab-2.3.16 → unitlab-2.3.18}/setup.py +1 -1
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/cloudflare_api_tunnel.py +89 -28
- {unitlab-2.3.16 → unitlab-2.3.18/src/unitlab.egg-info}/PKG-INFO +1 -1
- {unitlab-2.3.16 → unitlab-2.3.18}/LICENSE.md +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/README.md +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/setup.cfg +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/__init__.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/__main__.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/binary_manager.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/client.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/exceptions.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/main.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/tunnel_config.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/tunnel_service_token.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab/utils.py +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab.egg-info/SOURCES.txt +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab.egg-info/dependency_links.txt +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab.egg-info/entry_points.txt +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab.egg-info/requires.txt +0 -0
- {unitlab-2.3.16 → unitlab-2.3.18}/src/unitlab.egg-info/top_level.txt +0 -0
@@ -142,12 +142,12 @@ class CloudflareAPITunnel:
|
|
142
142
|
print(f"🔧 Configuring tunnel routes...")
|
143
143
|
|
144
144
|
# Get current tunnel config first
|
145
|
-
get_url = f"{self.api_base}/accounts/{self.account_id}/
|
145
|
+
get_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{self.tunnel_id}/configurations"
|
146
146
|
|
147
147
|
try:
|
148
148
|
# Get existing config
|
149
149
|
response = requests.get(get_url, headers=self.headers)
|
150
|
-
current_config = response.json()
|
150
|
+
current_config = response.json() if response.status_code == 200 else {}
|
151
151
|
|
152
152
|
# Build new ingress rules
|
153
153
|
new_ingress = [
|
@@ -190,7 +190,7 @@ class CloudflareAPITunnel:
|
|
190
190
|
}
|
191
191
|
}
|
192
192
|
|
193
|
-
put_url = f"{self.api_base}/accounts/{self.account_id}/
|
193
|
+
put_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{self.tunnel_id}/configurations"
|
194
194
|
response = requests.put(put_url, headers=self.headers, json=config_data)
|
195
195
|
|
196
196
|
if response.status_code == 200:
|
@@ -206,7 +206,7 @@ class CloudflareAPITunnel:
|
|
206
206
|
print(" Assuming routes are configured in dashboard.")
|
207
207
|
return True
|
208
208
|
|
209
|
-
def create_device_tunnel(self):
|
209
|
+
def create_device_tunnel(self, retry_on_fail=True):
|
210
210
|
"""
|
211
211
|
Create a unique tunnel for this device if it doesn't exist
|
212
212
|
"""
|
@@ -260,8 +260,13 @@ class CloudflareAPITunnel:
|
|
260
260
|
print(f"❌ Failed to create tunnel: {create_response.text}")
|
261
261
|
return None
|
262
262
|
else:
|
263
|
-
# Tunnel exists -
|
264
|
-
print(f"♻️
|
263
|
+
# Tunnel exists - but we need to ensure it can be used
|
264
|
+
print(f"♻️ Found existing tunnel, setting up for use")
|
265
|
+
|
266
|
+
# Store tunnel info for later use
|
267
|
+
existing_tunnel['needs_token'] = True # Mark that we'll need to use token
|
268
|
+
|
269
|
+
# Configure tunnel routes (creates config file)
|
265
270
|
self._configure_tunnel_routes(existing_tunnel['id'])
|
266
271
|
|
267
272
|
# Ensure DNS records exist
|
@@ -353,11 +358,6 @@ class CloudflareAPITunnel:
|
|
353
358
|
try:
|
354
359
|
print("🚀 Starting Cloudflare tunnel...")
|
355
360
|
|
356
|
-
# First, try to set up DNS and routes via API
|
357
|
-
if self.api_token:
|
358
|
-
self.create_dns_records()
|
359
|
-
self.update_tunnel_config()
|
360
|
-
|
361
361
|
# Ensure cloudflared is available
|
362
362
|
cloudflared_path = self._ensure_cloudflared()
|
363
363
|
if not cloudflared_path:
|
@@ -366,11 +366,33 @@ class CloudflareAPITunnel:
|
|
366
366
|
# Create or get existing tunnel for this device
|
367
367
|
device_tunnel = self.create_device_tunnel()
|
368
368
|
|
369
|
+
# Now set up DNS and routes via API after tunnel is created/found
|
370
|
+
if self.api_token and device_tunnel:
|
371
|
+
self.tunnel_id = device_tunnel['id'] # Ensure tunnel_id is set
|
372
|
+
self.create_dns_records()
|
373
|
+
self.update_tunnel_config()
|
374
|
+
|
375
|
+
# Check if we need to recreate the tunnel
|
376
|
+
if device_tunnel and device_tunnel.get('needs_recreation'):
|
377
|
+
print("🔄 Recreating tunnel due to access issues...")
|
378
|
+
# Delete the old tunnel
|
379
|
+
tunnel_id = device_tunnel['id']
|
380
|
+
delete_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{tunnel_id}"
|
381
|
+
delete_response = requests.delete(delete_url, headers=self.headers)
|
382
|
+
if delete_response.status_code in [200, 204, 404]:
|
383
|
+
print(f" ✅ Deleted old tunnel")
|
384
|
+
# Try creating a new one with a timestamp suffix
|
385
|
+
import time
|
386
|
+
self.clean_device_id = f"{self.device_id.replace(' ', '').replace('-', '').replace('.', '').replace('_', '')[:24]}-{int(time.time())}"
|
387
|
+
device_tunnel = self.create_device_tunnel(retry_on_fail=False)
|
388
|
+
if device_tunnel:
|
389
|
+
print(f" ✅ Created new tunnel")
|
390
|
+
|
369
391
|
if not device_tunnel:
|
370
392
|
print("❌ Could not create/find device tunnel")
|
371
393
|
# Fallback to shared tunnel if API fails
|
372
394
|
print("⚠️ Falling back to shared tunnel...")
|
373
|
-
service_token = "
|
395
|
+
service_token = "eyJhIjoiYzkxMTkyYWUyMGE1ZDQzZjY1ZTA4NzU1MGQ4ZGM4OWIiLCJ0IjoiMDc3N2ZjMTAtNDljNC00NzJkLTg2NjEtZjYwZDgwZDYxODRkIiwicyI6Ik9XRTNaak5tTVdVdE1tWTRaUzAwTmpoakLTazBmalF0WXpjek1tSm1ZVGt4WlRRMCJ9"
|
374
396
|
cmd = [
|
375
397
|
cloudflared_path,
|
376
398
|
"tunnel",
|
@@ -387,34 +409,73 @@ class CloudflareAPITunnel:
|
|
387
409
|
|
388
410
|
# Check if credentials file exists
|
389
411
|
creds_file = Path.home() / '.cloudflared' / f"{tunnel_id}.json"
|
412
|
+
config_file = Path.home() / '.cloudflared' / f'config-{tunnel_id}.yml'
|
413
|
+
cmd = None # Initialize cmd
|
390
414
|
|
391
|
-
if not creds_file.exists():
|
415
|
+
if not creds_file.exists() or device_tunnel.get('needs_token'):
|
392
416
|
# Try to recreate credentials from stored secret
|
393
417
|
if device_tunnel.get('tunnel_secret'):
|
394
418
|
self._save_tunnel_credentials(device_tunnel)
|
419
|
+
# Continue to use credentials below
|
395
420
|
else:
|
396
|
-
print("⚠️ No credentials
|
421
|
+
print("⚠️ No stored credentials, requesting tunnel token...")
|
397
422
|
# Get token for this tunnel
|
398
423
|
token_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{tunnel_id}/token"
|
424
|
+
print(f" Token URL: {token_url}")
|
425
|
+
|
399
426
|
token_response = requests.get(token_url, headers=self.headers)
|
427
|
+
print(f" Token response status: {token_response.status_code}")
|
428
|
+
|
400
429
|
if token_response.status_code == 200:
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
"
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
430
|
+
result = token_response.json()
|
431
|
+
if 'result' in result:
|
432
|
+
token = result['result']
|
433
|
+
print(f" ✅ Got tunnel token")
|
434
|
+
else:
|
435
|
+
print(f" ❌ No token in response: {result}")
|
436
|
+
return None
|
437
|
+
|
438
|
+
# Check if config file exists with ingress rules
|
439
|
+
if config_file.exists():
|
440
|
+
# Use token with config file for ingress rules
|
441
|
+
print(f" Using token with config file: {config_file}")
|
442
|
+
cmd = [
|
443
|
+
cloudflared_path,
|
444
|
+
"tunnel",
|
445
|
+
"--no-autoupdate",
|
446
|
+
"--config", str(config_file),
|
447
|
+
"run",
|
448
|
+
"--token", token
|
449
|
+
]
|
450
|
+
else:
|
451
|
+
# Use token directly
|
452
|
+
print(f" Using token directly (no config file)")
|
453
|
+
cmd = [
|
454
|
+
cloudflared_path,
|
455
|
+
"tunnel",
|
456
|
+
"--no-autoupdate",
|
457
|
+
"run",
|
458
|
+
"--token", token
|
459
|
+
]
|
411
460
|
else:
|
412
|
-
print("❌ Could not get tunnel token")
|
413
|
-
|
461
|
+
print(f"❌ Could not get tunnel token: {token_response.status_code}")
|
462
|
+
if token_response.text:
|
463
|
+
print(f" Error: {token_response.text}")
|
464
|
+
|
465
|
+
# If 404, tunnel doesn't exist or we don't have access
|
466
|
+
if token_response.status_code == 404:
|
467
|
+
print(f"🔄 Tunnel not accessible (404), will try to recreate...")
|
468
|
+
# Mark for recreation
|
469
|
+
device_tunnel['needs_recreation'] = True
|
470
|
+
return None
|
471
|
+
else:
|
472
|
+
print(f" ℹ️ Token endpoint returned {token_response.status_code}")
|
473
|
+
print(f" This might mean the tunnel needs to be recreated manually")
|
474
|
+
return None
|
414
475
|
|
415
|
-
if
|
476
|
+
# Only use credentials if we haven't already set cmd with token
|
477
|
+
if cmd is None and creds_file.exists():
|
416
478
|
# Check if config file exists
|
417
|
-
config_file = Path.home() / '.cloudflared' / f'config-{tunnel_id}.yml'
|
418
479
|
if config_file.exists():
|
419
480
|
# Run tunnel with config file (includes routes)
|
420
481
|
cmd = [
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|