@@ -746,6 +746,33 @@ def _write_runtime_env(tunnel: dict[str, Any]):
746746 os .chmod (TUNNEL_RUNTIME_ENV , 0o600 )
747747
748748
749+ def _start_tunnel_service_if_needed (tunnel : dict [str , Any ], service_status : str | None = None ) -> tuple [bool , str ]:
750+ if not tunnel .get ("tunnel_id" ):
751+ return False , "No tunnel configured"
752+ if not tunnel .get ("remote_port" ):
753+ return False , "Tunnel remote port missing"
754+ if not TUNNEL_KEY_FILE .exists ():
755+ return False , "Tunnel key not found. Create tunnel again."
756+
757+ current_status = service_status or _tunnel_service_status ()
758+ if current_status == "active" :
759+ return False , "Tunnel service already active"
760+
761+ if DEV_MODE :
762+ return True , "DEV MODE: would start tunnel service"
763+
764+ _write_runtime_env (tunnel )
765+ subprocess .run (
766+ ["systemctl" , "enable" , f"{ TUNNEL_SERVICE_NAME } .service" ],
767+ check = True , capture_output = True , timeout = 20
768+ )
769+ subprocess .run (
770+ ["systemctl" , "restart" , f"{ TUNNEL_SERVICE_NAME } .service" ],
771+ check = True , capture_output = True , timeout = 20
772+ )
773+ return True , "Tunnel service restarted"
774+
775+
749776def _tunnel_connect_script (tunnel : dict [str , Any ] | None ) -> str | None :
750777 if not tunnel :
751778 return None
@@ -2700,6 +2727,9 @@ def api_tunnel_poll():
27002727 state = _sync_tunnel_state_from_remote (state , client_id )
27012728 current = state .get ("current_tunnel" )
27022729 pending = state .get ("pending_invoice" )
2730+ service_status = _tunnel_service_status ()
2731+ auto_started = False
2732+ auto_start_message : str | None = None
27032733
27042734 if DEV_MODE and pending and pending .get ("action" ) == "renew" :
27052735 # Simple mock progression for local UI development
@@ -2713,14 +2743,23 @@ def api_tunnel_poll():
27132743 _save_tunnel_state (state )
27142744 pending = None
27152745 paid = True
2746+ try :
2747+ auto_started , auto_start_message = _start_tunnel_service_if_needed (current , service_status = service_status )
2748+ if auto_started :
2749+ service_status = "active" if DEV_MODE else _tunnel_service_status ()
2750+ except subprocess .CalledProcessError as e :
2751+ auto_started = False
2752+ auto_start_message = e .stderr .decode () or "Failed to start tunnel service"
27162753
27172754 payload = {
27182755 "paid" : paid ,
27192756 "client_id" : client_id ,
27202757 "current_tunnel" : state .get ("current_tunnel" ),
27212758 "pending_invoice" : pending ,
2722- "service_status" : _tunnel_service_status () ,
2759+ "service_status" : service_status ,
27232760 "connect_script" : _tunnel_connect_script (state .get ("current_tunnel" )),
2761+ "auto_started" : auto_started ,
2762+ "auto_start_message" : auto_start_message ,
27242763 }
27252764 return _json_response (status = "ok" , data = payload , ** payload )
27262765
@@ -2737,25 +2776,14 @@ def api_tunnel_start():
27372776 return jsonify ({"status" : "error" , "message" : "No tunnel configured" }), 404
27382777 if pending :
27392778 return jsonify ({"status" : "error" , "message" : "Tunnel invoice is still unpaid" }), 409
2740- if not current .get ("remote_port" ):
2741- return jsonify ({"status" : "error" , "message" : "Tunnel remote port missing" }), 400
2742- if not TUNNEL_KEY_FILE .exists ():
2743- return jsonify ({"status" : "error" , "message" : "Tunnel key not found. Create tunnel again." }), 400
2744-
2745- if DEV_MODE :
2746- return jsonify ({"status" : "ok" , "message" : "DEV MODE: would start tunnel service" })
27472779
27482780 try :
2749- _write_runtime_env (current )
2750- subprocess .run (
2751- ["systemctl" , "enable" , f"{ TUNNEL_SERVICE_NAME } .service" ],
2752- check = True , capture_output = True , timeout = 20
2753- )
2754- subprocess .run (
2755- ["systemctl" , "restart" , f"{ TUNNEL_SERVICE_NAME } .service" ],
2756- check = True , capture_output = True , timeout = 20
2757- )
2758- return jsonify ({"status" : "ok" , "message" : "Tunnel service restarted" })
2781+ started , message = _start_tunnel_service_if_needed (current )
2782+ if not started and message == "Tunnel service already active" :
2783+ return jsonify ({"status" : "ok" , "message" : message })
2784+ if not started :
2785+ return jsonify ({"status" : "error" , "message" : message }), 400
2786+ return jsonify ({"status" : "ok" , "message" : message })
27592787 except subprocess .CalledProcessError as e :
27602788 return jsonify ({"status" : "error" , "message" : e .stderr .decode ()}), 500
27612789 except Exception as e :
0 commit comments