feat(node): configure system environment through the desktop workflow

This commit is contained in:
DCCONSTRUCTIONS
2026-09-05 19:46:52 +03:00
parent 15bb793e5e
commit 59e14c5bc0
29 changed files with 929 additions and 81 deletions
+68 -4
View File
@@ -63,6 +63,8 @@ class NodeApplication(Gtk.Application):
self.pending = False
self.initial_login = False
self.cancelled_downloads = set()
self.environment_timer = None
self.environment_previous = None
def do_activate(self):
if self.window:
@@ -84,7 +86,7 @@ class NodeApplication(Gtk.Application):
self.view.connect("web-process-terminated", self.process_failed)
manager = self.view.get_user_content_manager()
manager.add_script(WebKit2.UserScript.new(
"Object.defineProperty(window, 'missionCoreDesktop', {value: Object.freeze({networkSetup: true})});",
"Object.defineProperty(window, 'missionCoreDesktop', {value: Object.freeze({networkSetup: true, environmentSetup: true})});",
WebKit2.UserContentInjectedFrames.TOP_FRAME, WebKit2.UserScriptInjectionTime.START, None, None,
))
manager.register_script_message_handler("node")
@@ -108,9 +110,71 @@ class NodeApplication(Gtk.Application):
action = result.get_js_value().to_string()
if action == "authorize":
self.login()
elif action == "configure-system":
self.configure_environment()
elif action in ("install-tailscale", "connect-tailscale"):
self.network_action(action)
def environment_record(self):
try:
path = Path("/var/lib/mission-core-node-environment/last-run.json")
if path.stat().st_size > 32768:
return None
value = json.loads(path.read_text())
if value.get("schema") == "missioncore.node.environment/v1":
return value
except (OSError, ValueError, TypeError):
pass
return None
def environment_progress(self):
if not self.window or not self.pending:
self.environment_timer = None
return False
record = self.environment_record()
if record and record.get("run_id") != self.environment_previous:
script = "window.dispatchEvent(new CustomEvent('mission-core-environment-progress', {detail: " + json.dumps(record) + "}));"
self.view.evaluate_javascript(script, -1, None, None, None, None, None)
return True
def configure_environment(self):
if self.pending:
return
self.pending = True
previous = self.environment_record()
self.environment_previous = previous.get("run_id") if previous else None
self.environment_timer = GLib.timeout_add(1000, self.environment_progress)
def work():
value = {"ok": False}
try:
process = subprocess.run(["/usr/bin/pkexec", "/usr/lib/mission-core-node/configure-system"], capture_output=True, text=True)
if process.returncode:
value["error"] = "Системное подтверждение отменено или недоступно. Повторите действие."
else:
value = json.loads(process.stdout)
if type(value.get("ok")) is not bool:
raise ValueError("Unexpected environment result")
if value.get("login_uri") and not LOGIN.fullmatch(value["login_uri"]):
raise ValueError("Unexpected local login")
except (OSError, ValueError, TypeError, subprocess.SubprocessError):
value = {"ok": False, "error": "Не удалось завершить настройку окружения. Повторите действие."}
GLib.idle_add(self.environment_finished, value)
threading.Thread(target=work, daemon=True).start()
def environment_finished(self, value):
self.pending = False
if self.environment_timer:
GLib.source_remove(self.environment_timer)
self.environment_timer = None
uri = value.pop("login_uri", None)
value["reloading"] = bool(uri)
if self.window:
script = "window.dispatchEvent(new CustomEvent('mission-core-environment-result', {detail: " + json.dumps(value) + "}));"
self.view.evaluate_javascript(script, -1, None, None, None, None, None)
if uri:
self.view.load_uri(uri)
return False
def network_action(self, action):
if self.pending:
self.network_result({"action": action, "ok": False, "error": "Другая операция ещё выполняется."}, completed=False)
@@ -156,7 +220,7 @@ class NodeApplication(Gtk.Application):
value["browser_opened"] = True
except GLib.Error:
value["ok"] = False
value["error"] = "Не удалось открыть браузер. Проверьте браузер по умолчанию в Ubuntu и повторите вход."
value["error"] = "Не удалось открыть браузер. Проверьте системный браузер по умолчанию и повторите вход."
script = "window.dispatchEvent(new CustomEvent('mission-core-network-result', {detail: " + json.dumps(value) + "}));"
self.view.evaluate_javascript(script, -1, None, None, None, None, None)
return False
@@ -170,7 +234,7 @@ class NodeApplication(Gtk.Application):
uri = authorize(self.development_socket)
GLib.idle_add(self.login_ready, uri)
except (OSError, ValueError, KeyError, http.client.HTTPException, subprocess.SubprocessError):
GLib.idle_add(self.problem, "Не удалось подтвердить доступ. Повторите вход и подтвердите системный запрос Ubuntu.")
GLib.idle_add(self.problem, "Не удалось подтвердить доступ. Повторите вход и подтвердите системный запрос.")
finally:
GLib.idle_add(self.login_finished)
threading.Thread(target=work, daemon=True).start()
@@ -258,5 +322,5 @@ if __name__ == "__main__":
parser.add_argument("--development-socket", help="Engineering-only: private socket of an unprivileged development service")
arguments = parser.parse_args()
if os.geteuid() == 0:
raise SystemExit("Run the desktop application as your normal Ubuntu user")
raise SystemExit("Run the desktop application as your normal system user")
raise SystemExit(NodeApplication(arguments.development_socket).run([]))