@@ -22,43 +22,58 @@ def setup_ngrok(port: int) -> Optional[str]:
22
22
try :
23
23
from pyngrok import ngrok , conf
24
24
from colorama import Fore , Style
25
-
25
+
26
26
# Get ngrok token using the standardized function
27
27
auth_token = get_ngrok_token ()
28
-
28
+
29
29
if not auth_token :
30
30
logger .error (f"{ Fore .RED } Ngrok auth token not found. Please configure it using 'locallab config'{ Style .RESET_ALL } " )
31
31
return None
32
-
32
+
33
33
# Ensure token is properly set in environment
34
34
set_env_var (NGROK_TOKEN_ENV , auth_token )
35
-
35
+
36
36
# Configure ngrok
37
37
ngrok .set_auth_token (auth_token )
38
38
conf .get_default ().auth_token = auth_token
39
-
39
+
40
40
# Start tunnel with simplified configuration
41
41
tunnel = ngrok .connect (
42
42
addr = port ,
43
43
proto = "http" ,
44
44
bind_tls = True # Enable HTTPS
45
45
)
46
-
46
+
47
47
public_url = tunnel .public_url
48
-
48
+
49
49
# Store the URL in environment for clients
50
50
os .environ ["LOCALLAB_NGROK_URL" ] = public_url
51
-
51
+
52
+ # Calculate banner width based on URL length (minimum 80 characters)
53
+ url_length = len (public_url )
54
+ banner_width = max (80 , url_length + 20 ) # Add padding for aesthetics
55
+
56
+ # Create dynamic width horizontal lines
57
+ h_line = "═" * (banner_width - 2 )
58
+
59
+ # Create centered title with proper padding
60
+ title = "✨ NGROK TUNNEL ACTIVE ✨"
61
+ title_padding = (banner_width - len (title ) - 2 ) // 2
62
+ padded_title = " " * title_padding + title + " " * title_padding
63
+ # Adjust if odd number
64
+ if len (padded_title ) < banner_width - 2 :
65
+ padded_title += " "
66
+
52
67
# Display banner
53
68
logger .info (f"""
54
- { Fore .GREEN } ╔═══════════════════════════════════════════════════════════════════ ╗{ Style .RESET_ALL }
55
- { Fore .GREEN } ✨ NGROK TUNNEL ACTIVE ✨ { Style .RESET_ALL }
56
- { Fore .GREEN } ╠═══════════════════════════════════════════════════════════════════ ╣{ Style .RESET_ALL }
57
- { Fore .CYAN } Public URL: { Fore .YELLOW } { public_url } { Style .RESET_ALL }
58
- { Fore .GREEN } ╚═══════════════════════════════════════════════════════════════════ ╝{ Style .RESET_ALL }
69
+ { Fore .CYAN } ╔ { h_line } ╗{ Style .RESET_ALL }
70
+ { Fore .CYAN } ║ { padded_title } ║ { Style .RESET_ALL }
71
+ { Fore .CYAN } ╠ { h_line } ╣{ Style .RESET_ALL }
72
+ { Fore .CYAN } ║ { Style . RESET_ALL } { Fore . GREEN } Public URL:{ Style . RESET_ALL } { Fore .YELLOW } { public_url } { Style . RESET_ALL } { " " * ( banner_width - len ( public_url ) - 14 ) } { Fore . CYAN } ║ { Style .RESET_ALL }
73
+ { Fore .CYAN } ╚ { h_line } ╝{ Style .RESET_ALL }
59
74
""" )
60
75
return public_url
61
-
76
+
62
77
except Exception as e :
63
78
logger .error (f"{ Fore .RED } Failed to setup ngrok: { str (e )} { Style .RESET_ALL } " )
64
79
logger .info (f"{ Fore .YELLOW } Please check your ngrok token using 'locallab config'{ Style .RESET_ALL } " )
@@ -91,7 +106,7 @@ def get_network_interfaces() -> List[Dict[str, str]]:
91
106
})
92
107
except Exception :
93
108
pass
94
-
109
+
95
110
return interfaces
96
111
97
112
def get_public_ip () -> Optional [str ]:
@@ -105,15 +120,15 @@ def get_public_ip() -> Optional[str]:
105
120
def get_network_interfaces () -> dict :
106
121
"""
107
122
Get information about available network interfaces
108
-
123
+
109
124
Returns:
110
125
A dictionary with interface names as keys and their addresses as values
111
126
"""
112
127
interfaces = {}
113
128
try :
114
129
import socket
115
130
import netifaces
116
-
131
+
117
132
for interface in netifaces .interfaces ():
118
133
addresses = netifaces .ifaddresses (interface )
119
134
# Get IPv4 addresses if available
@@ -143,13 +158,13 @@ def get_network_interfaces() -> dict:
143
158
except Exception as e :
144
159
logger .warning (f"Failed to get network interface information: { str (e )} " )
145
160
interfaces ["error" ] = str (e )
146
-
161
+
147
162
return interfaces
148
163
149
164
async def get_public_ip () -> str :
150
165
"""
151
166
Get the public IP address of this machine
152
-
167
+
153
168
Returns:
154
169
The public IP address as a string, or an empty string if it cannot be determined
155
170
"""
@@ -159,7 +174,7 @@ async def get_public_ip() -> str:
159
174
"https://checkip.amazonaws.com" ,
160
175
"https://ipinfo.io/ip"
161
176
]
162
-
177
+
163
178
try :
164
179
# Try to use httpx for async requests
165
180
import httpx
@@ -184,6 +199,6 @@ async def get_public_ip() -> str:
184
199
continue
185
200
except ImportError :
186
201
logger .warning (f"{ Fore .YELLOW } Neither httpx nor requests packages found. Cannot determine public IP.{ Style .RESET_ALL } " )
187
-
202
+
188
203
# If we couldn't get the IP from any service, return empty string
189
204
return ""
0 commit comments