feat: Tauri desktop shell — Rust backend + config files + build scripts
- desktop/Cargo.toml: Tauri + plugins (dialog, notification, fs, updater) - desktop/tauri.conf.json: Windows (.msi/.nsis) + Linux (.deb/.AppImage) config - desktop/src/main.rs: spawn Python backend, health check, tray icon, commands - desktop/build.rs: Tauri build hook - desktop/python-embed/sidecar.py: uvicorn launcher with port auto-detect - build-windows.bat: automated Windows build with Python embed download - build-linux.sh: automated Linux build with venv - desktop/README.md: architecture doc + usage guide - desktop/validate-structure.sh: CI validation script - ROADMAP.md: #77 Tauri Desktop section added
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ObsiGate Desktop — Sidecar Launcher
|
||||
====================================
|
||||
Lancé par le shell Tauri, démarre le backend FastAPI.
|
||||
Utilise le port 17890 (127.0.0.1 uniquement, pas d'exposition réseau).
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import signal
|
||||
|
||||
BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
HOST = "127.0.0.1"
|
||||
PORT = int(os.environ.get("OBSIGATE_PORT", "17890"))
|
||||
|
||||
|
||||
def find_free_port(start: int) -> int:
|
||||
"""Trouve le premier port libre à partir de `start`."""
|
||||
import socket
|
||||
port = start
|
||||
while port < start + 100:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
if s.connect_ex((HOST, port)) != 0:
|
||||
return port
|
||||
port += 1
|
||||
raise RuntimeError(f"No free port found in range {start}-{start+99}")
|
||||
|
||||
|
||||
def main():
|
||||
port = find_free_port(PORT)
|
||||
|
||||
# Create log directory
|
||||
if sys.platform == "win32":
|
||||
log_dir = os.path.join(os.environ.get("APPDATA", os.path.expanduser("~")), "ObsiGate", "logs")
|
||||
else:
|
||||
log_dir = os.path.join(os.path.expanduser("~"), ".local", "share", "obsigate", "logs")
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
log_file = os.path.join(log_dir, "backend.log")
|
||||
print(f"ObsiGate backend starting on {HOST}:{port} — logs: {log_file}")
|
||||
|
||||
# Launch uvicorn
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m", "uvicorn",
|
||||
"backend.main:app",
|
||||
"--host", HOST,
|
||||
"--port", str(port),
|
||||
"--log-level", "info",
|
||||
]
|
||||
|
||||
with open(log_file, "a") as log:
|
||||
log.write(f"\n--- ObsiGate started at {time.ctime()} ---\n")
|
||||
proc = subprocess.Popen(cmd, cwd=BACKEND_DIR, stdout=log, stderr=log)
|
||||
|
||||
def handle_signal(signum, frame):
|
||||
print("Shutting down ObsiGate backend...")
|
||||
proc.terminate()
|
||||
proc.wait(timeout=10)
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGTERM, handle_signal)
|
||||
signal.signal(signal.SIGINT, handle_signal)
|
||||
|
||||
# Wait for backend to finish
|
||||
proc.wait()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user