fix(panel-v3): bridge JS→Python, DETENER por PID, NVM, cierre limpio
- Bridge reescrito: usa oasis:// URI en lugar de UserContentManager - bash -lc para cargar NVM al lanzar OASIS (Node v22) - DETENER mata el grupo de procesos por PID (no pkill genérico) - _oasis_proc guarda el proceso activo entre INICIAR/DETENER - Flag _alive evita MemoryError al llamar GLib.idle_add tras cerrar - _on_destroy desconecta el main loop limpiamente Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1ed0aa03d9
commit
68b3ceff8d
2 changed files with 61 additions and 58 deletions
|
|
@ -138,12 +138,15 @@ def node_version() -> str:
|
|||
class OasisPanel:
|
||||
|
||||
def __init__(self):
|
||||
self._alive = True
|
||||
self._oasis_proc = None # proceso OASIS activo
|
||||
|
||||
self.win = Gtk.Window()
|
||||
self.win.set_title("SOLAR NET HUB")
|
||||
self.win.set_default_size(390, 620)
|
||||
self.win.set_resizable(False)
|
||||
self.win.set_position(Gtk.WindowPosition.CENTER)
|
||||
self.win.connect("destroy", Gtk.main_quit)
|
||||
self.win.connect("destroy", self._on_destroy)
|
||||
|
||||
# Fondo negro en la ventana GTK para evitar parpadeo blanco al cargar
|
||||
css = b"window { background-color: #000000; }"
|
||||
|
|
@ -154,24 +157,21 @@ class OasisPanel:
|
|||
)
|
||||
|
||||
# ── WebKit2 ───────────────────────────────────────────────────────
|
||||
self.manager = WebKit2.UserContentManager()
|
||||
self.manager.connect("script-message-received::bridge", self._on_message)
|
||||
self.manager.register_script_message_handler("bridge")
|
||||
|
||||
settings = WebKit2.Settings()
|
||||
settings.set_enable_javascript(True)
|
||||
settings.set_enable_developer_extras(True) # activa inspector por si hace falta depurar
|
||||
settings.set_allow_file_access_from_file_urls(True)
|
||||
settings.set_allow_universal_access_from_file_urls(True)
|
||||
settings.set_enable_page_cache(False) # sin caché de página
|
||||
settings.set_enable_page_cache(False)
|
||||
|
||||
self.webview = WebKit2.WebView.new_with_user_content_manager(self.manager)
|
||||
self.webview = WebKit2.WebView()
|
||||
self.webview.set_settings(settings)
|
||||
self.webview.set_background_color(Gdk.RGBA(0, 0, 0, 1))
|
||||
|
||||
# Bridge JS→Python: interceptar navegaciones a oasis://accion
|
||||
self.webview.connect("decide-policy", self._on_policy)
|
||||
|
||||
self.win.add(self.webview)
|
||||
|
||||
# Cargar HTML directamente por URI (evita caché de recursos)
|
||||
html_file = SCRIPT_DIR / "ui" / "index.html"
|
||||
self.webview.load_uri(f"file://{html_file}")
|
||||
|
||||
|
|
@ -182,6 +182,10 @@ class OasisPanel:
|
|||
# Poll continuo cada 3 s
|
||||
GLib.timeout_add_seconds(3, self._poll)
|
||||
|
||||
def _on_destroy(self, *_):
|
||||
self._alive = False
|
||||
Gtk.main_quit()
|
||||
|
||||
# ── Polling ───────────────────────────────────────────────────────────
|
||||
def _initial_poll(self):
|
||||
self._poll()
|
||||
|
|
@ -219,24 +223,26 @@ class OasisPanel:
|
|||
|
||||
# ── JS helpers ────────────────────────────────────────────────────────
|
||||
def _js(self, code):
|
||||
# evaluate_javascript (WebKit2 4.1+) o run_javascript (4.0 fallback)
|
||||
if hasattr(self.webview, "evaluate_javascript"):
|
||||
self.webview.evaluate_javascript(code, -1, None, None, None, None, None)
|
||||
else:
|
||||
if self._alive:
|
||||
self.webview.run_javascript(code, None, None, None)
|
||||
return False
|
||||
|
||||
def _log(self, text):
|
||||
self._js(f"appendLog({json.dumps(text)})")
|
||||
if self._alive:
|
||||
self._js(f"appendLog({json.dumps(text)})")
|
||||
|
||||
# ── Mensaje desde JS ──────────────────────────────────────────────────
|
||||
def _on_message(self, _manager, result):
|
||||
try:
|
||||
data = json.loads(result.get_value().to_string())
|
||||
action = data.get("action", "")
|
||||
except Exception:
|
||||
return
|
||||
self._dispatch(action)
|
||||
# ── Bridge JS→Python via navegación oasis://accion ───────────────────
|
||||
def _on_policy(self, webview, decision, decision_type):
|
||||
if decision_type == WebKit2.PolicyDecisionType.NAVIGATION_ACTION:
|
||||
uri = decision.get_navigation_action().get_request().get_uri()
|
||||
if uri.startswith("oasis://"):
|
||||
action = uri[len("oasis://"):]
|
||||
print(f"[bridge] action={action}", flush=True)
|
||||
GLib.idle_add(self._dispatch, action)
|
||||
decision.ignore()
|
||||
return True
|
||||
decision.use()
|
||||
return False
|
||||
|
||||
def _dispatch(self, action):
|
||||
installer_cmds = {
|
||||
|
|
@ -284,57 +290,56 @@ class OasisPanel:
|
|||
|
||||
def _start_oasis(self):
|
||||
import time
|
||||
PORT = 3000
|
||||
oasis_dir = find_oasis_dir()
|
||||
oasis_sh = oasis_dir / "oasis.sh"
|
||||
|
||||
if not oasis_sh.is_file():
|
||||
GLib.idle_add(self._log, f"[Error]: no se encontró oasis.sh en {oasis_dir}")
|
||||
return
|
||||
|
||||
# ¿Ya está escuchando?
|
||||
if oasis_running(PORT):
|
||||
GLib.idle_add(self._log, f"OASIS ya está escuchando en el puerto {PORT}")
|
||||
subprocess.Popen(["xdg-open", f"http://localhost:{PORT}"])
|
||||
GLib.idle_add(self._poll)
|
||||
return
|
||||
|
||||
# Lanzar en background igual que el installer original
|
||||
cmd = f'cd "{oasis_dir}" && exec bash oasis.sh'
|
||||
GLib.idle_add(self._log, f"$ nohup bash -lc '{cmd}' &")
|
||||
GLib.idle_add(self._log, f"$ cd {oasis_dir} && bash oasis.sh")
|
||||
try:
|
||||
subprocess.Popen(
|
||||
["bash", "-lc", cmd],
|
||||
stdout=open("/tmp/oasis_gui.log", "w"),
|
||||
proc = subprocess.Popen(
|
||||
["bash", "-lc", f"cd '{oasis_dir}' && bash oasis.sh"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
start_new_session=True,
|
||||
)
|
||||
except Exception as e:
|
||||
GLib.idle_add(self._log, f"[Error al lanzar]: {e}")
|
||||
GLib.idle_add(self._log, f"[Error]: {e}")
|
||||
return
|
||||
|
||||
# Esperar hasta 30 s a que el puerto esté activo
|
||||
GLib.idle_add(self._log, "Esperando que OASIS levante en el puerto 3000...")
|
||||
self._oasis_proc = proc
|
||||
GLib.idle_add(self._log, f"PID {proc.pid} — esperando puerto 3000...")
|
||||
|
||||
# Leer salida del proceso y mostrarla en el log
|
||||
def _read_output():
|
||||
for line in iter(proc.stdout.readline, b""):
|
||||
if self._alive:
|
||||
GLib.idle_add(self._log, line.decode("utf-8", errors="replace").rstrip())
|
||||
threading.Thread(target=_read_output, daemon=True).start()
|
||||
|
||||
# Esperar hasta 30s a que el puerto esté activo y abrir navegador
|
||||
for i in range(30):
|
||||
time.sleep(1)
|
||||
if oasis_running(PORT):
|
||||
GLib.idle_add(self._log, f"OASIS levantado en {PORT}. Abriendo navegador...")
|
||||
subprocess.Popen(["xdg-open", f"http://localhost:{PORT}"])
|
||||
if oasis_running():
|
||||
GLib.idle_add(self._log, "Puerto 3000 activo — abriendo navegador")
|
||||
subprocess.Popen(["xdg-open", "http://localhost:3000"])
|
||||
GLib.idle_add(self._poll)
|
||||
return
|
||||
if (i + 1) % 5 == 0:
|
||||
GLib.idle_add(self._log, f" ... {i+1}s")
|
||||
|
||||
GLib.idle_add(self._log, "[Error]: OASIS no levantó en 30 s. Revisa /tmp/oasis_gui.log")
|
||||
GLib.idle_add(self._log, "[Timeout] OASIS no levantó en 30s")
|
||||
GLib.idle_add(self._poll)
|
||||
|
||||
def _kill_oasis(self):
|
||||
GLib.idle_add(self._log, "Deteniendo OASIS...")
|
||||
try:
|
||||
subprocess.run(["pkill", "-f", "node.*server.js"], timeout=5)
|
||||
GLib.idle_add(self._log, "Servidor OASIS detenido.")
|
||||
import os, signal as sig
|
||||
if self._oasis_proc and self._oasis_proc.poll() is None:
|
||||
# Matar el grupo de procesos completo (bash -lc + node hijo)
|
||||
os.killpg(os.getpgid(self._oasis_proc.pid), sig.SIGTERM)
|
||||
self._oasis_proc.wait(timeout=5)
|
||||
self._oasis_proc = None
|
||||
GLib.idle_add(self._log, "OASIS detenido.")
|
||||
else:
|
||||
# Fallback: matar cualquier node backend.js corriendo
|
||||
subprocess.run(["pkill", "-f", "node.*backend.js"], timeout=5)
|
||||
GLib.idle_add(self._log, "OASIS detenido.")
|
||||
except Exception as e:
|
||||
GLib.idle_add(self._log, f"[Error]: {e}")
|
||||
GLib.idle_add(self._log, f"[Error al detener]: {e}")
|
||||
GLib.idle_add(self._poll)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue