feat(node): configure system environment through the desktop workflow
This commit is contained in:
@@ -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([]))
|
||||
|
||||
Reference in New Issue
Block a user