@@ -429,8 +429,20 @@ async def delete_server(server_id: str, delete_files: bool = False):
429429
430430@app .get ("/status" )
431431def get_status ():
432- if not state or not state .server_handler :
433- return {"status" : "not_configured" }
432+ if not state :
433+ return {"status" : "offline" , "cpu" : 0 , "ram" : 0 , "players" : 0 }
434+
435+ # If we are installing, return 'starting' so the UI knows we are busy
436+ installing = getattr (state , 'install_progress' , 0 ) > 0 and getattr (state , 'install_progress' , 0 ) < 100
437+
438+ if not state .server_handler :
439+ return {
440+ "status" : "starting" if installing else "not_configured" ,
441+ "cpu" : 0 ,
442+ "ram" : 0 ,
443+ "players" : 0 ,
444+ "recent_logs" : state .log_history [- 50 :]
445+ }
434446
435447 stats = state .server_handler .get_stats ()
436448
@@ -439,18 +451,21 @@ def get_status():
439451 return {
440452 "status" : state .server_handler .get_status (),
441453 "pid" : state .server_handler .get_pid (),
454+ "server_id" : state .server_handler .server_id ,
442455 "server_type" : state .server_handler .server_type ,
443456 "minecraft_version" : state .server_handler .minecraft_version ,
444457 "cpu" : stats ["cpu" ],
445458 "ram" : stats ["ram" ],
446459 "players" : players_count ,
447460 "max_players" : state .server_handler .get_max_players (),
448461 "online_players" : online_players ,
449- "online_players" : online_players ,
450462 "uptime" : stats ["uptime" ],
451- # Return last 50 lines for the mini console polling fallback
452- "recent_logs" : state .log_history [- 50 :] if state else [],
453- "shutdown_info" : state .server_handler .get_shutdown_info ()
463+ "recent_logs" : state .log_history [- 50 :],
464+ "shutdown_info" : state .server_handler .get_shutdown_info (),
465+ "tunnel" : {
466+ "active" : state .tunnel_process is not None and state .tunnel_process .poll () is None ,
467+ "address" : state .tunnel_address
468+ }
454469 }
455470
456471@app .post ("/server/open-folder" )
@@ -1337,6 +1352,40 @@ def start_tunnel(request: Request, region: str = Query("eu")):
13371352 state .tunnel_process = None
13381353 state .tunnel_address = None
13391354
1355+ # Verify SSH is available BEFORE starting the thread
1356+ ssh_executable = shutil .which ("ssh" )
1357+ if not ssh_executable and sys .platform == "win32" :
1358+ # Fallback for Windows if not in PATH
1359+ common_paths = [
1360+ os .path .join (os .environ .get ("SystemRoot" , "C:\\ Windows" ), "System32\\ OpenSSH\\ ssh.exe" ),
1361+ os .path .join (os .environ .get ("ProgramFiles" , "C:\\ Program Files" ), "OpenSSH\\ ssh.exe" ),
1362+ os .path .join (os .environ .get ("ProgramFiles(x86)" , "C:\\ Program Files (x86)" ), "OpenSSH\\ ssh.exe" ),
1363+ ]
1364+ for p in common_paths :
1365+ if os .path .exists (p ):
1366+ ssh_executable = p
1367+ logging .info (f"Found SSH at fallback path: { p } " )
1368+ break
1369+
1370+ if not ssh_executable :
1371+ logging .error ("SSH not found in PATH or common locations" )
1372+ raise HTTPException (
1373+ status_code = 400 ,
1374+ detail = "SSH no encontrado. Por favor, instala 'OpenSSH Client' en las características opcionales de Windows para usar 'Make Public'."
1375+ )
1376+
1377+ # Discover ssh-keygen as well
1378+ ssh_keygen_executable = shutil .which ("ssh-keygen" )
1379+ if not ssh_keygen_executable and ssh_executable :
1380+ # If we found ssh.exe in a folder, its likely ssh-keygen is there too
1381+ potential_keygen = os .path .join (os .path .dirname (ssh_executable ), "ssh-keygen.exe" )
1382+ if os .path .exists (potential_keygen ):
1383+ ssh_keygen_executable = potential_keygen
1384+
1385+ if not ssh_keygen_executable :
1386+ ssh_keygen_executable = "ssh-keygen" # Fallback to PATH and hope for the best if we couldn't find it explicitly
1387+
1388+
13401389 # Get server port (default 25565)
13411390 port = "25565"
13421391 if state .server_handler :
@@ -1365,7 +1414,7 @@ def _ensure_ssh_key():
13651414 if not os .path .exists (key_path ) or not os .path .exists (pub_path ):
13661415 logging .info ("Generating new SSH key for Pinggy..." )
13671416 subprocess .run (
1368- ["ssh-keygen" , "-t" , "rsa" , "-b" , "2048" , "-f" , key_path , "-N" , "" ],
1417+ [ssh_keygen_executable , "-t" , "rsa" , "-b" , "2048" , "-f" , key_path , "-N" , "" ],
13691418 check = True ,
13701419 stdout = subprocess .DEVNULL ,
13711420 stderr = subprocess .DEVNULL ,
@@ -1385,12 +1434,6 @@ def run_tunnel():
13851434 # regions: eu, us, ap, sa
13861435 host = f"{ region } .free.pinggy.io"
13871436
1388- # Verify SSH is available
1389- if not shutil .which ("ssh" ):
1390- logging .error ("SSH not found in PATH" )
1391- state .broadcast_log_sync ("❌ Error: 'ssh' is not installed or not in PATH. Cannot start public tunnel." , "error" )
1392- return
1393-
13941437 logging .info (f"Starting Pinggy tunnel ({ region .upper ()} ) for port { port } ..." )
13951438 state .broadcast_log_sync (f"🌐 Starting public tunnel ({ region .upper ()} ) for port { port } ..." , "info" )
13961439
@@ -1399,7 +1442,7 @@ def run_tunnel():
13991442
14001443 # Pinggy SSH command - optimized with identity
14011444 cmd = [
1402- "ssh" ,
1445+ ssh_executable ,
14031446 "-p" , "443" ,
14041447 "-o" , "StrictHostKeyChecking=no" ,
14051448 "-o" , "ServerAliveInterval=30" ,
0 commit comments