From 18c64daa7b461cbbd7650956742d3d350c8d1d13 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Sun, 2 Nov 2025 01:35:02 -0600 Subject: [PATCH] Removal of Legacy Servere Codebase --- Data/Engine/bootstrapper.py | 10 +- Data/Server/Borealis.ico | Bin 51899 -> 0 bytes Data/Server/Modules/__init__.py | 1 - Data/Server/Modules/admin/__init__.py | 1 - Data/Server/Modules/admin/routes.py | 496 - Data/Server/Modules/agents/__init__.py | 1 - Data/Server/Modules/agents/routes.py | 218 - Data/Server/Modules/auth/__init__.py | 1 - Data/Server/Modules/auth/device_auth.py | 310 - Data/Server/Modules/auth/dpop.py | 109 - Data/Server/Modules/auth/jwt_service.py | 140 - Data/Server/Modules/auth/rate_limit.py | 41 - Data/Server/Modules/crypto/__init__.py | 1 - Data/Server/Modules/crypto/certificates.py | 372 - Data/Server/Modules/crypto/keys.py | 71 - Data/Server/Modules/crypto/signing.py | 125 - Data/Server/Modules/db_migrations.py | 488 - Data/Server/Modules/enrollment/__init__.py | 1 - Data/Server/Modules/enrollment/nonce_store.py | 35 - Data/Server/Modules/enrollment/routes.py | 759 -- Data/Server/Modules/guid_utils.py | 26 - Data/Server/Modules/jobs/__init__.py | 1 - Data/Server/Modules/jobs/prune.py | 110 - Data/Server/Modules/runtime.py | 168 - Data/Server/Modules/tokens/__init__.py | 1 - Data/Server/Modules/tokens/routes.py | 138 - Data/Server/Package-Borealis-Server.ps1 | 88 - .../Python_API_Endpoints/ocr_engines.py | 104 - .../Python_API_Endpoints/script_engines.py | 57 - Data/Server/Sounds/Short_Beep.wav | Bin 9702 -> 0 bytes Data/Server/WebUI/index.html | 22 - Data/Server/WebUI/package.json | 50 - Data/Server/WebUI/public/Borealis_Logo.png | Bin 334968 -> 0 bytes .../WebUI/public/Borealis_Logo_Full.png | Bin 397410 -> 0 bytes Data/Server/WebUI/public/favicon.ico | Bin 15406 -> 0 bytes .../Access_Management/Credential_Editor.jsx | 549 -- .../src/Access_Management/Credential_List.jsx | 464 - .../Access_Management/Github_API_Token.jsx | 325 - .../WebUI/src/Access_Management/Users.jsx | 680 -- Data/Server/WebUI/src/Admin/Server_Info.jsx | 73 - Data/Server/WebUI/src/App.jsx | 1392 --- .../WebUI/src/Assemblies/Assembly_Editor.jsx | 1269 --- .../WebUI/src/Assemblies/Assembly_List.jsx | 777 -- Data/Server/WebUI/src/Borealis.css | 252 - Data/Server/WebUI/src/Devices/Add_Device.jsx | 219 - .../WebUI/src/Devices/Agent_Devices.jsx | 13 - .../WebUI/src/Devices/Device_Approvals.jsx | 505 - .../WebUI/src/Devices/Device_Details.jsx | 1383 --- Data/Server/WebUI/src/Devices/Device_List.jsx | 1832 ---- .../WebUI/src/Devices/Enrollment_Codes.jsx | 371 - Data/Server/WebUI/src/Devices/SSH_Devices.jsx | 480 - .../WebUI/src/Devices/WinRM_Devices.jsx | 6 - Data/Server/WebUI/src/Dialogs.jsx | 514 - .../src/Flow_Editor/Context_Menu_Sidebar.jsx | 415 - .../WebUI/src/Flow_Editor/Flow_Editor.jsx | 374 - .../WebUI/src/Flow_Editor/Flow_Tabs.jsx | 100 - .../Node_Configuration_Sidebar.jsx | 485 - .../WebUI/src/Flow_Editor/Node_Sidebar.jsx | 260 - Data/Server/WebUI/src/Login.jsx | 332 - Data/Server/WebUI/src/Navigation_Sidebar.jsx | 409 - .../WebUI/src/Scheduling/Create_Job.jsx | 2141 ----- .../Server/WebUI/src/Scheduling/Quick_Job.jsx | 593 -- .../src/Scheduling/Scheduled_Jobs_List.jsx | 685 -- Data/Server/WebUI/src/Sites/Site_List.jsx | 385 - Data/Server/WebUI/src/Status_Bar.jsx | 93 - Data/Server/WebUI/src/index.jsx | 21 - .../WebUI/src/nodes/Agent/Node_Agent.jsx | 554 -- .../src/nodes/Agent/Node_Agent_Role_Macro.jsx | 310 - .../Agent/Node_Agent_Role_Screenshot.jsx | 271 - .../src/nodes/Alerting/Node_Alert_Sound.jsx | 326 - .../Node_Array_Index_Extractor.jsx | 142 - .../Node_JSON_Display.jsx | 179 - .../Node_JSON_Value_Extractor.jsx | 132 - .../Node_OCR_Text_Extraction.jsx | 238 - .../Node_Regex_Replace.jsx | 211 - .../Node_Regex_Search.jsx | 140 - .../Node_TextArray_Display.jsx | 190 - .../Data Collection/Node_API_Request.jsx | 193 - .../Data Collection/Node_Upload_Text.jsx | 123 - .../nodes/Flow Control/Node_Edge_Toggle.jsx | 218 - .../src/nodes/General Purpose/Node_Data.jsx | 100 - .../Node_Logical_Operators.jsx | 200 - .../General Purpose/Node_Math_Operation.jsx | 172 - .../Image Processing/Node_Adjust_Contrast.jsx | 113 - .../Image Processing/Node_BW_Threshold.jsx | 195 - .../Node_Convert_to_Grayscale.jsx | 135 - .../Image Processing/Node_Export_Image.jsx | 90 - .../Image Processing/Node_Image_Viewer.jsx | 146 - .../Image Processing/Node_Upload_Image.jsx | 175 - .../Organization/Node_Backdrop_Group_Box.jsx | 134 - .../nodes/Reporting/Node_Export_to_CSV.jsx | 145 - .../src/nodes/Templates/Node_Template.jsx | 193 - Data/Server/WebUI/tsconfig.json | 24 - Data/Server/WebUI/vite.config.mts | 89 - Data/Server/job_scheduler.py | 1838 ---- Data/Server/server-requirements.txt | 40 - Data/Server/server.py | 8486 ----------------- 97 files changed, 5 insertions(+), 36839 deletions(-) delete mode 100644 Data/Server/Borealis.ico delete mode 100644 Data/Server/Modules/__init__.py delete mode 100644 Data/Server/Modules/admin/__init__.py delete mode 100644 Data/Server/Modules/admin/routes.py delete mode 100644 Data/Server/Modules/agents/__init__.py delete mode 100644 Data/Server/Modules/agents/routes.py delete mode 100644 Data/Server/Modules/auth/__init__.py delete mode 100644 Data/Server/Modules/auth/device_auth.py delete mode 100644 Data/Server/Modules/auth/dpop.py delete mode 100644 Data/Server/Modules/auth/jwt_service.py delete mode 100644 Data/Server/Modules/auth/rate_limit.py delete mode 100644 Data/Server/Modules/crypto/__init__.py delete mode 100644 Data/Server/Modules/crypto/certificates.py delete mode 100644 Data/Server/Modules/crypto/keys.py delete mode 100644 Data/Server/Modules/crypto/signing.py delete mode 100644 Data/Server/Modules/db_migrations.py delete mode 100644 Data/Server/Modules/enrollment/__init__.py delete mode 100644 Data/Server/Modules/enrollment/nonce_store.py delete mode 100644 Data/Server/Modules/enrollment/routes.py delete mode 100644 Data/Server/Modules/guid_utils.py delete mode 100644 Data/Server/Modules/jobs/__init__.py delete mode 100644 Data/Server/Modules/jobs/prune.py delete mode 100644 Data/Server/Modules/runtime.py delete mode 100644 Data/Server/Modules/tokens/__init__.py delete mode 100644 Data/Server/Modules/tokens/routes.py delete mode 100644 Data/Server/Package-Borealis-Server.ps1 delete mode 100644 Data/Server/Python_API_Endpoints/ocr_engines.py delete mode 100644 Data/Server/Python_API_Endpoints/script_engines.py delete mode 100644 Data/Server/Sounds/Short_Beep.wav delete mode 100644 Data/Server/WebUI/index.html delete mode 100644 Data/Server/WebUI/package.json delete mode 100644 Data/Server/WebUI/public/Borealis_Logo.png delete mode 100644 Data/Server/WebUI/public/Borealis_Logo_Full.png delete mode 100644 Data/Server/WebUI/public/favicon.ico delete mode 100644 Data/Server/WebUI/src/Access_Management/Credential_Editor.jsx delete mode 100644 Data/Server/WebUI/src/Access_Management/Credential_List.jsx delete mode 100644 Data/Server/WebUI/src/Access_Management/Github_API_Token.jsx delete mode 100644 Data/Server/WebUI/src/Access_Management/Users.jsx delete mode 100644 Data/Server/WebUI/src/Admin/Server_Info.jsx delete mode 100644 Data/Server/WebUI/src/App.jsx delete mode 100644 Data/Server/WebUI/src/Assemblies/Assembly_Editor.jsx delete mode 100644 Data/Server/WebUI/src/Assemblies/Assembly_List.jsx delete mode 100644 Data/Server/WebUI/src/Borealis.css delete mode 100644 Data/Server/WebUI/src/Devices/Add_Device.jsx delete mode 100644 Data/Server/WebUI/src/Devices/Agent_Devices.jsx delete mode 100644 Data/Server/WebUI/src/Devices/Device_Approvals.jsx delete mode 100644 Data/Server/WebUI/src/Devices/Device_Details.jsx delete mode 100644 Data/Server/WebUI/src/Devices/Device_List.jsx delete mode 100644 Data/Server/WebUI/src/Devices/Enrollment_Codes.jsx delete mode 100644 Data/Server/WebUI/src/Devices/SSH_Devices.jsx delete mode 100644 Data/Server/WebUI/src/Devices/WinRM_Devices.jsx delete mode 100644 Data/Server/WebUI/src/Dialogs.jsx delete mode 100644 Data/Server/WebUI/src/Flow_Editor/Context_Menu_Sidebar.jsx delete mode 100644 Data/Server/WebUI/src/Flow_Editor/Flow_Editor.jsx delete mode 100644 Data/Server/WebUI/src/Flow_Editor/Flow_Tabs.jsx delete mode 100644 Data/Server/WebUI/src/Flow_Editor/Node_Configuration_Sidebar.jsx delete mode 100644 Data/Server/WebUI/src/Flow_Editor/Node_Sidebar.jsx delete mode 100644 Data/Server/WebUI/src/Login.jsx delete mode 100644 Data/Server/WebUI/src/Navigation_Sidebar.jsx delete mode 100644 Data/Server/WebUI/src/Scheduling/Create_Job.jsx delete mode 100644 Data/Server/WebUI/src/Scheduling/Quick_Job.jsx delete mode 100644 Data/Server/WebUI/src/Scheduling/Scheduled_Jobs_List.jsx delete mode 100644 Data/Server/WebUI/src/Sites/Site_List.jsx delete mode 100644 Data/Server/WebUI/src/Status_Bar.jsx delete mode 100644 Data/Server/WebUI/src/index.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Agent/Node_Agent.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Macro.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Screenshot.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Alerting/Node_Alert_Sound.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_Array_Index_Extractor.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Display.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Value_Extractor.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_OCR_Text_Extraction.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_Regex_Replace.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_Regex_Search.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_TextArray_Display.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Collection/Node_API_Request.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Data Collection/Node_Upload_Text.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Flow Control/Node_Edge_Toggle.jsx delete mode 100644 Data/Server/WebUI/src/nodes/General Purpose/Node_Data.jsx delete mode 100644 Data/Server/WebUI/src/nodes/General Purpose/Node_Logical_Operators.jsx delete mode 100644 Data/Server/WebUI/src/nodes/General Purpose/Node_Math_Operation.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Image Processing/Node_Adjust_Contrast.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Image Processing/Node_BW_Threshold.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Image Processing/Node_Convert_to_Grayscale.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Image Processing/Node_Export_Image.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Image Processing/Node_Image_Viewer.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Image Processing/Node_Upload_Image.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Organization/Node_Backdrop_Group_Box.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Reporting/Node_Export_to_CSV.jsx delete mode 100644 Data/Server/WebUI/src/nodes/Templates/Node_Template.jsx delete mode 100644 Data/Server/WebUI/tsconfig.json delete mode 100644 Data/Server/WebUI/vite.config.mts delete mode 100644 Data/Server/job_scheduler.py delete mode 100644 Data/Server/server-requirements.txt delete mode 100644 Data/Server/server.py diff --git a/Data/Engine/bootstrapper.py b/Data/Engine/bootstrapper.py index 3a5e9248..8520e3a1 100644 --- a/Data/Engine/bootstrapper.py +++ b/Data/Engine/bootstrapper.py @@ -77,11 +77,11 @@ def _stage_web_interface_assets(logger: Optional[logging.Logger] = None, *, forc project_root = _project_root() engine_web_root = project_root / "Engine" / "web-interface" - legacy_source = project_root / "Data" / "Server" / "WebUI" + modern_source = project_root / "Data" / "Engine" / "web-interface" - if not legacy_source.is_dir(): + if not modern_source.is_dir(): raise RuntimeError( - f"Engine web interface source missing: {legacy_source}" + f"Engine web interface source missing: {modern_source}" ) index_path = engine_web_root / "index.html" @@ -92,14 +92,14 @@ def _stage_web_interface_assets(logger: Optional[logging.Logger] = None, *, forc if engine_web_root.exists(): shutil.rmtree(engine_web_root) - shutil.copytree(legacy_source, engine_web_root) + shutil.copytree(modern_source, engine_web_root) if not index_path.is_file(): raise RuntimeError( f"Engine web interface staging failed; missing {index_path}" ) - logger.info("Engine web interface staged from %s to %s", legacy_source, engine_web_root) + logger.info("Engine web interface staged from %s to %s", modern_source, engine_web_root) return engine_web_root diff --git a/Data/Server/Borealis.ico b/Data/Server/Borealis.ico deleted file mode 100644 index 051a9a0c5f0d8347ab97b48a452a334a1358e141..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51899 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 jI0kOdZ0*tO*Ql;x?7H)K~B*m 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

Y23vqYr4#uFv19V6ou-;mgbJC+k)LQG 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 diff --git a/Data/Server/Modules/__init__.py b/Data/Server/Modules/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/admin/__init__.py b/Data/Server/Modules/admin/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/admin/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/admin/routes.py b/Data/Server/Modules/admin/routes.py deleted file mode 100644 index c20a925a..00000000 --- a/Data/Server/Modules/admin/routes.py +++ /dev/null @@ -1,496 +0,0 @@ -from __future__ import annotations - -import secrets -import sqlite3 -import uuid -from datetime import datetime, timedelta, timezone -from typing import Any, Callable, Dict, List, Optional - -from flask import Blueprint, jsonify, request - -from Modules.guid_utils import normalize_guid - - -VALID_TTL_HOURS = {1, 3, 6, 12, 24} - - -def register( - app, - *, - db_conn_factory: Callable[[], sqlite3.Connection], - require_admin: Callable[[], Optional[Any]], - current_user: Callable[[], Optional[Dict[str, str]]], - log: Callable[[str, str, Optional[str]], None], -) -> None: - blueprint = Blueprint("admin", __name__) - - def _now() -> datetime: - return datetime.now(tz=timezone.utc) - - def _iso(dt: datetime) -> str: - return dt.isoformat() - - def _lookup_user_id(cur: sqlite3.Cursor, username: str) -> Optional[str]: - if not username: - return None - cur.execute( - "SELECT id FROM users WHERE LOWER(username) = LOWER(?)", - (username,), - ) - row = cur.fetchone() - if row: - return str(row[0]) - return None - - def _hostname_conflict( - cur: sqlite3.Cursor, - hostname: Optional[str], - pending_guid: Optional[str], - ) -> Optional[Dict[str, Any]]: - if not hostname: - return None - cur.execute( - """ - SELECT d.guid, d.ssl_key_fingerprint, ds.site_id, s.name - FROM devices d - LEFT JOIN device_sites ds ON ds.device_hostname = d.hostname - LEFT JOIN sites s ON s.id = ds.site_id - WHERE d.hostname = ? - """, - (hostname,), - ) - row = cur.fetchone() - if not row: - return None - existing_guid = normalize_guid(row[0]) - existing_fingerprint = (row[1] or "").strip().lower() - pending_norm = normalize_guid(pending_guid) - if existing_guid and pending_norm and existing_guid == pending_norm: - return None - site_id_raw = row[2] - site_id = None - if site_id_raw is not None: - try: - site_id = int(site_id_raw) - except (TypeError, ValueError): - site_id = None - site_name = row[3] or "" - return { - "guid": existing_guid or None, - "ssl_key_fingerprint": existing_fingerprint or None, - "site_id": site_id, - "site_name": site_name, - } - - def _suggest_alternate_hostname( - cur: sqlite3.Cursor, - hostname: Optional[str], - pending_guid: Optional[str], - ) -> Optional[str]: - base = (hostname or "").strip() - if not base: - return None - base = base[:253] - candidate = base - pending_norm = normalize_guid(pending_guid) - suffix = 1 - while True: - cur.execute( - "SELECT guid FROM devices WHERE hostname = ?", - (candidate,), - ) - row = cur.fetchone() - if not row: - return candidate - existing_guid = normalize_guid(row[0]) - if pending_norm and existing_guid == pending_norm: - return candidate - candidate = f"{base}-{suffix}" - suffix += 1 - if suffix > 50: - return pending_norm or candidate - - @blueprint.before_request - def _check_admin(): - result = require_admin() - if result is not None: - return result - return None - - @blueprint.route("/api/admin/enrollment-codes", methods=["GET"]) - def list_enrollment_codes(): - status_filter = request.args.get("status") - conn = db_conn_factory() - try: - cur = conn.cursor() - sql = """ - SELECT id, - code, - expires_at, - created_by_user_id, - used_at, - used_by_guid, - max_uses, - use_count, - last_used_at - FROM enrollment_install_codes - """ - params: List[str] = [] - now_iso = _iso(_now()) - if status_filter == "active": - sql += " WHERE use_count < max_uses AND expires_at > ?" - params.append(now_iso) - elif status_filter == "expired": - sql += " WHERE use_count < max_uses AND expires_at <= ?" - params.append(now_iso) - elif status_filter == "used": - sql += " WHERE use_count >= max_uses" - sql += " ORDER BY expires_at ASC" - cur.execute(sql, params) - rows = cur.fetchall() - finally: - conn.close() - - records = [] - for row in rows: - records.append( - { - "id": row[0], - "code": row[1], - "expires_at": row[2], - "created_by_user_id": row[3], - "used_at": row[4], - "used_by_guid": row[5], - "max_uses": row[6], - "use_count": row[7], - "last_used_at": row[8], - } - ) - return jsonify({"codes": records}) - - @blueprint.route("/api/admin/enrollment-codes", methods=["POST"]) - def create_enrollment_code(): - payload = request.get_json(force=True, silent=True) or {} - ttl_hours = int(payload.get("ttl_hours") or 1) - if ttl_hours not in VALID_TTL_HOURS: - return jsonify({"error": "invalid_ttl"}), 400 - - max_uses_value = payload.get("max_uses") - if max_uses_value is None: - max_uses_value = payload.get("allowed_uses") - try: - max_uses = int(max_uses_value) - except Exception: - max_uses = 2 - if max_uses < 1: - max_uses = 1 - if max_uses > 10: - max_uses = 10 - - user = current_user() or {} - username = user.get("username") or "" - - conn = db_conn_factory() - try: - cur = conn.cursor() - created_by = _lookup_user_id(cur, username) or username or "system" - code_value = _generate_install_code() - issued_at = _now() - expires_at = issued_at + timedelta(hours=ttl_hours) - record_id = str(uuid.uuid4()) - cur.execute( - """ - INSERT INTO enrollment_install_codes ( - id, code, expires_at, created_by_user_id, max_uses, use_count - ) - VALUES (?, ?, ?, ?, ?, 0) - """, - (record_id, code_value, _iso(expires_at), created_by, max_uses), - ) - cur.execute( - """ - INSERT INTO enrollment_install_codes_persistent ( - id, - code, - created_at, - expires_at, - created_by_user_id, - used_at, - used_by_guid, - max_uses, - last_known_use_count, - last_used_at, - is_active, - archived_at, - consumed_at - ) - VALUES (?, ?, ?, ?, ?, NULL, NULL, ?, 0, NULL, 1, NULL, NULL) - ON CONFLICT(id) DO UPDATE - SET code = excluded.code, - created_at = excluded.created_at, - expires_at = excluded.expires_at, - created_by_user_id = excluded.created_by_user_id, - max_uses = excluded.max_uses, - last_known_use_count = 0, - used_at = NULL, - used_by_guid = NULL, - last_used_at = NULL, - is_active = 1, - archived_at = NULL, - consumed_at = NULL - """, - (record_id, code_value, _iso(issued_at), _iso(expires_at), created_by, max_uses), - ) - conn.commit() - finally: - conn.close() - - log( - "server", - f"installer code created id={record_id} by={username} ttl={ttl_hours}h max_uses={max_uses}", - ) - return jsonify( - { - "id": record_id, - "code": code_value, - "expires_at": _iso(expires_at), - "max_uses": max_uses, - "use_count": 0, - "last_used_at": None, - } - ) - - @blueprint.route("/api/admin/enrollment-codes/", methods=["DELETE"]) - def delete_enrollment_code(code_id: str): - conn = db_conn_factory() - try: - cur = conn.cursor() - cur.execute( - "DELETE FROM enrollment_install_codes WHERE id = ? AND use_count = 0", - (code_id,), - ) - deleted = cur.rowcount - if deleted: - archive_ts = _iso(_now()) - cur.execute( - """ - UPDATE enrollment_install_codes_persistent - SET is_active = 0, - archived_at = COALESCE(archived_at, ?) - WHERE id = ? - """, - (archive_ts, code_id), - ) - conn.commit() - finally: - conn.close() - - if not deleted: - return jsonify({"error": "not_found"}), 404 - log("server", f"installer code deleted id={code_id}") - return jsonify({"status": "deleted"}) - - @blueprint.route("/api/admin/device-approvals", methods=["GET"]) - def list_device_approvals(): - status_raw = request.args.get("status") - status = (status_raw or "").strip().lower() - approvals: List[Dict[str, Any]] = [] - conn = db_conn_factory() - try: - cur = conn.cursor() - params: List[str] = [] - sql = """ - SELECT - da.id, - da.approval_reference, - da.guid, - da.hostname_claimed, - da.ssl_key_fingerprint_claimed, - da.enrollment_code_id, - da.status, - da.client_nonce, - da.server_nonce, - da.created_at, - da.updated_at, - da.approved_by_user_id, - u.username AS approved_by_username - FROM device_approvals AS da - LEFT JOIN users AS u - ON ( - CAST(da.approved_by_user_id AS TEXT) = CAST(u.id AS TEXT) - OR LOWER(da.approved_by_user_id) = LOWER(u.username) - ) - """ - if status and status != "all": - sql += " WHERE LOWER(da.status) = ?" - params.append(status) - sql += " ORDER BY da.created_at ASC" - cur.execute(sql, params) - rows = cur.fetchall() - for row in rows: - record_guid = row[2] - hostname = row[3] - fingerprint_claimed = row[4] - claimed_fp_norm = (fingerprint_claimed or "").strip().lower() - conflict_raw = _hostname_conflict(cur, hostname, record_guid) - fingerprint_match = False - requires_prompt = False - conflict = None - if conflict_raw: - conflict_fp = (conflict_raw.get("ssl_key_fingerprint") or "").strip().lower() - fingerprint_match = bool(conflict_fp and claimed_fp_norm) and conflict_fp == claimed_fp_norm - requires_prompt = not fingerprint_match - conflict = { - **conflict_raw, - "fingerprint_match": fingerprint_match, - "requires_prompt": requires_prompt, - } - alternate_hostname = ( - _suggest_alternate_hostname(cur, hostname, record_guid) - if conflict_raw and requires_prompt - else None - ) - approvals.append( - { - "id": row[0], - "approval_reference": row[1], - "guid": record_guid, - "hostname_claimed": hostname, - "ssl_key_fingerprint_claimed": fingerprint_claimed, - "enrollment_code_id": row[5], - "status": row[6], - "client_nonce": row[7], - "server_nonce": row[8], - "created_at": row[9], - "updated_at": row[10], - "approved_by_user_id": row[11], - "hostname_conflict": conflict, - "alternate_hostname": alternate_hostname, - "conflict_requires_prompt": requires_prompt, - "fingerprint_match": fingerprint_match, - "approved_by_username": row[12], - } - ) - finally: - conn.close() - - return jsonify({"approvals": approvals}) - - def _set_approval_status( - approval_id: str, - status: str, - *, - guid: Optional[str] = None, - resolution: Optional[str] = None, - ): - user = current_user() or {} - username = user.get("username") or "" - - conn = db_conn_factory() - try: - cur = conn.cursor() - cur.execute( - """ - SELECT status, - guid, - hostname_claimed, - ssl_key_fingerprint_claimed - FROM device_approvals - WHERE id = ? - """, - (approval_id,), - ) - row = cur.fetchone() - if not row: - return {"error": "not_found"}, 404 - existing_status = (row[0] or "").strip().lower() - if existing_status != "pending": - return {"error": "approval_not_pending"}, 409 - stored_guid = row[1] - hostname_claimed = row[2] - fingerprint_claimed = (row[3] or "").strip().lower() - - guid_effective = normalize_guid(guid) if guid else normalize_guid(stored_guid) - resolution_effective = (resolution.strip().lower() if isinstance(resolution, str) else None) - - conflict = None - if status == "approved": - conflict = _hostname_conflict(cur, hostname_claimed, guid_effective) - if conflict: - conflict_fp = (conflict.get("ssl_key_fingerprint") or "").strip().lower() - fingerprint_match = bool(conflict_fp and fingerprint_claimed) and conflict_fp == fingerprint_claimed - if fingerprint_match: - guid_effective = conflict.get("guid") or guid_effective - if not resolution_effective: - resolution_effective = "auto_merge_fingerprint" - elif resolution_effective == "overwrite": - guid_effective = conflict.get("guid") or guid_effective - elif resolution_effective == "coexist": - pass - else: - return { - "error": "conflict_resolution_required", - "hostname": hostname_claimed, - }, 409 - - guid_to_store = guid_effective or normalize_guid(stored_guid) or None - - approved_by = _lookup_user_id(cur, username) or username or "system" - cur.execute( - """ - UPDATE device_approvals - SET status = ?, - guid = ?, - approved_by_user_id = ?, - updated_at = ? - WHERE id = ? - """, - ( - status, - guid_to_store, - approved_by, - _iso(_now()), - approval_id, - ), - ) - conn.commit() - finally: - conn.close() - resolution_note = f" ({resolution_effective})" if resolution_effective else "" - log("server", f"device approval {approval_id} -> {status}{resolution_note} by {username}") - payload: Dict[str, Any] = {"status": status} - if resolution_effective: - payload["conflict_resolution"] = resolution_effective - return payload, 200 - - @blueprint.route("/api/admin/device-approvals//approve", methods=["POST"]) - def approve_device(approval_id: str): - payload = request.get_json(force=True, silent=True) or {} - guid = payload.get("guid") - if guid: - guid = str(guid).strip() - resolution_val = payload.get("conflict_resolution") - resolution = None - if isinstance(resolution_val, str): - cleaned = resolution_val.strip().lower() - if cleaned: - resolution = cleaned - result, status_code = _set_approval_status( - approval_id, - "approved", - guid=guid, - resolution=resolution, - ) - return jsonify(result), status_code - - @blueprint.route("/api/admin/device-approvals//deny", methods=["POST"]) - def deny_device(approval_id: str): - result, status_code = _set_approval_status(approval_id, "denied") - return jsonify(result), status_code - - app.register_blueprint(blueprint) - - -def _generate_install_code() -> str: - raw = secrets.token_hex(16).upper() - return "-".join(raw[i : i + 4] for i in range(0, len(raw), 4)) diff --git a/Data/Server/Modules/agents/__init__.py b/Data/Server/Modules/agents/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/agents/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/agents/routes.py b/Data/Server/Modules/agents/routes.py deleted file mode 100644 index 990f684b..00000000 --- a/Data/Server/Modules/agents/routes.py +++ /dev/null @@ -1,218 +0,0 @@ -from __future__ import annotations - -import json -import time -import sqlite3 -from typing import Any, Callable, Dict, Optional - -from flask import Blueprint, jsonify, request, g - -from Modules.auth.device_auth import DeviceAuthManager, require_device_auth -from Modules.crypto.signing import ScriptSigner -from Modules.guid_utils import normalize_guid - -AGENT_CONTEXT_HEADER = "X-Borealis-Agent-Context" - - -def _canonical_context(value: Optional[str]) -> Optional[str]: - if not value: - return None - cleaned = "".join(ch for ch in str(value) if ch.isalnum() or ch in ("_", "-")) - if not cleaned: - return None - return cleaned.upper() - - -def register( - app, - *, - db_conn_factory: Callable[[], Any], - auth_manager: DeviceAuthManager, - log: Callable[[str, str, Optional[str]], None], - script_signer: ScriptSigner, -) -> None: - blueprint = Blueprint("agents", __name__) - - def _json_or_none(value) -> Optional[str]: - if value is None: - return None - try: - return json.dumps(value) - except Exception: - return None - - def _context_hint(ctx=None) -> Optional[str]: - if ctx is not None and getattr(ctx, "service_mode", None): - return _canonical_context(getattr(ctx, "service_mode", None)) - return _canonical_context(request.headers.get(AGENT_CONTEXT_HEADER)) - - def _auth_context(): - ctx = getattr(g, "device_auth", None) - if ctx is None: - log("server", f"device auth context missing for {request.path}", _context_hint()) - return ctx - - @blueprint.route("/api/agent/heartbeat", methods=["POST"]) - @require_device_auth(auth_manager) - def heartbeat(): - ctx = _auth_context() - if ctx is None: - return jsonify({"error": "auth_context_missing"}), 500 - payload = request.get_json(force=True, silent=True) or {} - context_label = _context_hint(ctx) - - now_ts = int(time.time()) - updates: Dict[str, Optional[str]] = {"last_seen": now_ts} - - hostname = payload.get("hostname") - if isinstance(hostname, str) and hostname.strip(): - updates["hostname"] = hostname.strip() - - inventory = payload.get("inventory") if isinstance(payload.get("inventory"), dict) else {} - for key in ("memory", "network", "software", "storage", "cpu"): - if key in inventory and inventory[key] is not None: - encoded = _json_or_none(inventory[key]) - if encoded is not None: - updates[key] = encoded - - metrics = payload.get("metrics") if isinstance(payload.get("metrics"), dict) else {} - def _maybe_str(field: str) -> Optional[str]: - val = metrics.get(field) - if isinstance(val, str): - return val.strip() - return None - - if "last_user" in metrics and metrics["last_user"]: - updates["last_user"] = str(metrics["last_user"]) - if "operating_system" in metrics and metrics["operating_system"]: - updates["operating_system"] = str(metrics["operating_system"]) - if "uptime" in metrics and metrics["uptime"] is not None: - try: - updates["uptime"] = int(metrics["uptime"]) - except Exception: - pass - for field in ("external_ip", "internal_ip", "device_type"): - if field in payload and payload[field]: - updates[field] = str(payload[field]) - - conn = db_conn_factory() - try: - cur = conn.cursor() - - def _apply_updates() -> int: - if not updates: - return 0 - columns = ", ".join(f"{col} = ?" for col in updates.keys()) - values = list(updates.values()) - normalized_guid = normalize_guid(ctx.guid) - selected_guid: Optional[str] = None - if normalized_guid: - cur.execute( - "SELECT guid FROM devices WHERE UPPER(guid) = ?", - (normalized_guid,), - ) - rows = cur.fetchall() - for (stored_guid,) in rows or []: - if stored_guid == ctx.guid: - selected_guid = stored_guid - break - if not selected_guid and rows: - selected_guid = rows[0][0] - target_guid = selected_guid or ctx.guid - cur.execute( - f"UPDATE devices SET {columns} WHERE guid = ?", - values + [target_guid], - ) - updated = cur.rowcount - if updated > 0 and normalized_guid and target_guid != normalized_guid: - try: - cur.execute( - "UPDATE devices SET guid = ? WHERE guid = ?", - (normalized_guid, target_guid), - ) - except sqlite3.IntegrityError: - pass - return updated - - try: - rowcount = _apply_updates() - except sqlite3.IntegrityError as exc: - if "devices.hostname" in str(exc) and "UNIQUE" in str(exc).upper(): - # Another device already claims this hostname; keep the existing - # canonical hostname assigned during enrollment to avoid breaking - # the unique constraint and continue updating the remaining fields. - existing_guid_for_hostname: Optional[str] = None - if "hostname" in updates: - try: - cur.execute( - "SELECT guid FROM devices WHERE hostname = ?", - (updates["hostname"],), - ) - row = cur.fetchone() - if row and row[0]: - existing_guid_for_hostname = normalize_guid(row[0]) - except Exception: - existing_guid_for_hostname = None - if "hostname" in updates: - updates.pop("hostname", None) - try: - rowcount = _apply_updates() - except sqlite3.IntegrityError: - raise - else: - try: - current_guid = normalize_guid(ctx.guid) - except Exception: - current_guid = ctx.guid - if ( - existing_guid_for_hostname - and current_guid - and existing_guid_for_hostname == current_guid - ): - pass # Same device contexts; no log needed. - else: - log( - "server", - "heartbeat hostname collision ignored for guid=" - f"{ctx.guid}", - context_label, - ) - else: - raise - - if rowcount == 0: - log("server", f"heartbeat missing device record guid={ctx.guid}", context_label) - return jsonify({"error": "device_not_registered"}), 404 - conn.commit() - finally: - conn.close() - - return jsonify({"status": "ok", "poll_after_ms": 15000}) - - @blueprint.route("/api/agent/script/request", methods=["POST"]) - @require_device_auth(auth_manager) - def script_request(): - ctx = _auth_context() - if ctx is None: - return jsonify({"error": "auth_context_missing"}), 500 - if ctx.status != "active": - return jsonify( - { - "status": "quarantined", - "poll_after_ms": 60000, - "sig_alg": "ed25519", - "signing_key": script_signer.public_base64_spki(), - } - ) - - # Placeholder: actual dispatch logic will integrate with job scheduler. - return jsonify( - { - "status": "idle", - "poll_after_ms": 30000, - "sig_alg": "ed25519", - "signing_key": script_signer.public_base64_spki(), - } - ) - - app.register_blueprint(blueprint) diff --git a/Data/Server/Modules/auth/__init__.py b/Data/Server/Modules/auth/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/auth/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/auth/device_auth.py b/Data/Server/Modules/auth/device_auth.py deleted file mode 100644 index 64600a60..00000000 --- a/Data/Server/Modules/auth/device_auth.py +++ /dev/null @@ -1,310 +0,0 @@ -from __future__ import annotations - -import functools -import sqlite3 -import time -from contextlib import closing -from dataclasses import dataclass -from datetime import datetime, timezone -from typing import Any, Callable, Dict, Optional - -import jwt -from flask import g, jsonify, request - -from Modules.auth.dpop import DPoPValidator, DPoPVerificationError, DPoPReplayError -from Modules.auth.rate_limit import SlidingWindowRateLimiter -from Modules.guid_utils import normalize_guid - -AGENT_CONTEXT_HEADER = "X-Borealis-Agent-Context" - - -def _canonical_context(value: Optional[str]) -> Optional[str]: - if not value: - return None - cleaned = "".join(ch for ch in str(value) if ch.isalnum() or ch in ("_", "-")) - if not cleaned: - return None - return cleaned.upper() - - -@dataclass -class DeviceAuthContext: - guid: str - ssl_key_fingerprint: str - token_version: int - access_token: str - claims: Dict[str, Any] - dpop_jkt: Optional[str] - status: str - service_mode: Optional[str] - - -class DeviceAuthError(Exception): - status_code = 401 - error_code = "unauthorized" - - def __init__( - self, - message: str = "unauthorized", - *, - status_code: Optional[int] = None, - retry_after: Optional[float] = None, - ): - super().__init__(message) - if status_code is not None: - self.status_code = status_code - self.message = message - self.retry_after = retry_after - - -class DeviceAuthManager: - def __init__( - self, - *, - db_conn_factory: Callable[[], Any], - jwt_service, - dpop_validator: Optional[DPoPValidator], - log: Callable[[str, str, Optional[str]], None], - rate_limiter: Optional[SlidingWindowRateLimiter] = None, - ) -> None: - self._db_conn_factory = db_conn_factory - self._jwt_service = jwt_service - self._dpop_validator = dpop_validator - self._log = log - self._rate_limiter = rate_limiter - - def authenticate(self) -> DeviceAuthContext: - auth_header = request.headers.get("Authorization", "") - if not auth_header.startswith("Bearer "): - raise DeviceAuthError("missing_authorization") - token = auth_header[len("Bearer ") :].strip() - if not token: - raise DeviceAuthError("missing_authorization") - - try: - claims = self._jwt_service.decode(token) - except jwt.ExpiredSignatureError: - raise DeviceAuthError("token_expired") - except Exception: - raise DeviceAuthError("invalid_token") - - raw_guid = str(claims.get("guid") or "").strip() - guid = normalize_guid(raw_guid) - fingerprint = str(claims.get("ssl_key_fingerprint") or "").lower().strip() - token_version = int(claims.get("token_version") or 0) - if not guid or not fingerprint or token_version <= 0: - raise DeviceAuthError("invalid_claims") - - if self._rate_limiter: - decision = self._rate_limiter.check(f"fp:{fingerprint}", 60, 60.0) - if not decision.allowed: - raise DeviceAuthError( - "rate_limited", - status_code=429, - retry_after=decision.retry_after, - ) - - context_label = _canonical_context(request.headers.get(AGENT_CONTEXT_HEADER)) - - with closing(self._db_conn_factory()) as conn: - cur = conn.cursor() - cur.execute( - """ - SELECT guid, ssl_key_fingerprint, token_version, status - FROM devices - WHERE UPPER(guid) = ? - """, - (guid,), - ) - rows = cur.fetchall() - row = None - for candidate in rows or []: - candidate_guid = normalize_guid(candidate[0]) - if candidate_guid == guid: - row = candidate - break - if row is None and rows: - row = rows[0] - - if not row: - row = self._recover_device_record( - conn, guid, fingerprint, token_version, context_label - ) - - if not row: - raise DeviceAuthError("device_not_found", status_code=403) - - db_guid, db_fp, db_token_version, status = row - db_guid_normalized = normalize_guid(db_guid) - - if not db_guid_normalized or db_guid_normalized != guid: - raise DeviceAuthError("device_guid_mismatch", status_code=403) - - db_fp = (db_fp or "").lower().strip() - if db_fp and db_fp != fingerprint: - raise DeviceAuthError("fingerprint_mismatch", status_code=403) - - if db_token_version and db_token_version > token_version: - raise DeviceAuthError("token_version_revoked", status_code=401) - - status_normalized = (status or "active").strip().lower() - allowed_statuses = {"active", "quarantined"} - if status_normalized not in allowed_statuses: - raise DeviceAuthError("device_revoked", status_code=403) - if status_normalized == "quarantined": - self._log( - "server", - f"device {guid} is quarantined; limited access for {request.path}", - context_label, - ) - - dpop_jkt: Optional[str] = None - dpop_proof = request.headers.get("DPoP") - if dpop_proof: - if not self._dpop_validator: - raise DeviceAuthError("dpop_not_supported", status_code=400) - try: - htu = request.url - dpop_jkt = self._dpop_validator.verify(request.method, htu, dpop_proof, token) - except DPoPReplayError: - raise DeviceAuthError("dpop_replayed", status_code=400) - except DPoPVerificationError: - raise DeviceAuthError("dpop_invalid", status_code=400) - - ctx = DeviceAuthContext( - guid=guid, - ssl_key_fingerprint=fingerprint, - token_version=token_version, - access_token=token, - claims=claims, - dpop_jkt=dpop_jkt, - status=status_normalized, - service_mode=context_label, - ) - return ctx - - def _recover_device_record( - self, - conn: sqlite3.Connection, - guid: str, - fingerprint: str, - token_version: int, - context_label: Optional[str], - ) -> Optional[tuple]: - """Attempt to recreate a missing device row for an authenticated token.""" - - guid = normalize_guid(guid) - fingerprint = (fingerprint or "").strip() - if not guid or not fingerprint: - return None - - cur = conn.cursor() - now_ts = int(time.time()) - try: - now_iso = datetime.now(tz=timezone.utc).isoformat() - except Exception: - now_iso = datetime.utcnow().isoformat() # pragma: no cover - - base_hostname = f"RECOVERED-{guid[:12].upper()}" if guid else "RECOVERED" - - for attempt in range(6): - hostname = base_hostname if attempt == 0 else f"{base_hostname}-{attempt}" - try: - cur.execute( - """ - INSERT INTO devices ( - guid, - hostname, - created_at, - last_seen, - ssl_key_fingerprint, - token_version, - status, - key_added_at - ) - VALUES (?, ?, ?, ?, ?, ?, 'active', ?) - """, - ( - guid, - hostname, - now_ts, - now_ts, - fingerprint, - max(token_version or 1, 1), - now_iso, - ), - ) - except sqlite3.IntegrityError as exc: - # Hostname collision – try again with a suffixed placeholder. - message = str(exc).lower() - if "hostname" in message and "unique" in message: - continue - self._log( - "server", - f"device auth failed to recover guid={guid} due to integrity error: {exc}", - context_label, - ) - conn.rollback() - return None - except Exception as exc: # pragma: no cover - defensive logging - self._log( - "server", - f"device auth unexpected error recovering guid={guid}: {exc}", - context_label, - ) - conn.rollback() - return None - else: - conn.commit() - break - else: - # Exhausted attempts because of hostname collisions. - self._log( - "server", - f"device auth could not recover guid={guid}; hostname collisions persisted", - context_label, - ) - conn.rollback() - return None - - cur.execute( - """ - SELECT guid, ssl_key_fingerprint, token_version, status - FROM devices - WHERE guid = ? - """, - (guid,), - ) - row = cur.fetchone() - if not row: - self._log( - "server", - f"device auth recovery for guid={guid} committed but row still missing", - context_label, - ) - return row - - -def require_device_auth(manager: DeviceAuthManager): - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - try: - ctx = manager.authenticate() - except DeviceAuthError as exc: - response = jsonify({"error": exc.message}) - response.status_code = exc.status_code - retry_after = getattr(exc, "retry_after", None) - if retry_after: - try: - response.headers["Retry-After"] = str(max(1, int(retry_after))) - except Exception: - response.headers["Retry-After"] = "1" - return response - - g.device_auth = ctx - return func(*args, **kwargs) - - return wrapper - - return decorator diff --git a/Data/Server/Modules/auth/dpop.py b/Data/Server/Modules/auth/dpop.py deleted file mode 100644 index 1049b9ab..00000000 --- a/Data/Server/Modules/auth/dpop.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -DPoP proof verification helpers. -""" - -from __future__ import annotations - -import hashlib -import time -from threading import Lock -from typing import Dict, Optional - -import jwt - -_DP0P_MAX_SKEW = 300.0 # seconds - - -class DPoPVerificationError(Exception): - pass - - -class DPoPReplayError(DPoPVerificationError): - pass - - -class DPoPValidator: - def __init__(self) -> None: - self._observed_jti: Dict[str, float] = {} - self._lock = Lock() - - def verify( - self, - method: str, - htu: str, - proof: str, - access_token: Optional[str] = None, - ) -> str: - """ - Verify the presented DPoP proof. Returns the JWK thumbprint on success. - """ - - if not proof: - raise DPoPVerificationError("DPoP proof missing") - - try: - header = jwt.get_unverified_header(proof) - except Exception as exc: - raise DPoPVerificationError("invalid DPoP header") from exc - - jwk = header.get("jwk") - alg = header.get("alg") - if not jwk or not isinstance(jwk, dict): - raise DPoPVerificationError("missing jwk in DPoP header") - if alg not in ("EdDSA", "ES256", "ES384", "ES512"): - raise DPoPVerificationError(f"unsupported DPoP alg {alg}") - - try: - key = jwt.PyJWK(jwk) - public_key = key.key - except Exception as exc: - raise DPoPVerificationError("invalid jwk in DPoP header") from exc - - try: - claims = jwt.decode( - proof, - public_key, - algorithms=[alg], - options={"require": ["htm", "htu", "jti", "iat"]}, - ) - except Exception as exc: - raise DPoPVerificationError("invalid DPoP signature") from exc - - htm = claims.get("htm") - proof_htu = claims.get("htu") - jti = claims.get("jti") - iat = claims.get("iat") - ath = claims.get("ath") - - if not isinstance(htm, str) or htm.lower() != method.lower(): - raise DPoPVerificationError("DPoP htm mismatch") - if not isinstance(proof_htu, str) or proof_htu != htu: - raise DPoPVerificationError("DPoP htu mismatch") - if not isinstance(jti, str): - raise DPoPVerificationError("DPoP jti missing") - if not isinstance(iat, (int, float)): - raise DPoPVerificationError("DPoP iat missing") - - now = time.time() - if abs(now - float(iat)) > _DP0P_MAX_SKEW: - raise DPoPVerificationError("DPoP proof outside allowed skew") - - if ath and access_token: - expected_ath = jwt.utils.base64url_encode( - hashlib.sha256(access_token.encode("utf-8")).digest() - ).decode("ascii") - if expected_ath != ath: - raise DPoPVerificationError("DPoP ath mismatch") - - with self._lock: - expiry = self._observed_jti.get(jti) - if expiry and expiry > now: - raise DPoPReplayError("DPoP proof replay detected") - self._observed_jti[jti] = now + _DP0P_MAX_SKEW - # Opportunistic cleanup - stale = [key for key, exp in self._observed_jti.items() if exp <= now] - for key in stale: - self._observed_jti.pop(key, None) - - thumbprint = jwt.PyJWK(jwk).thumbprint() - return thumbprint.decode("ascii") diff --git a/Data/Server/Modules/auth/jwt_service.py b/Data/Server/Modules/auth/jwt_service.py deleted file mode 100644 index ab5640b2..00000000 --- a/Data/Server/Modules/auth/jwt_service.py +++ /dev/null @@ -1,140 +0,0 @@ -""" -JWT access-token helpers backed by an Ed25519 signing key. -""" - -from __future__ import annotations - -import hashlib -import time -from datetime import datetime, timezone -from typing import Any, Dict, Optional - -import jwt -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ed25519 - -from Modules.runtime import ensure_runtime_dir, runtime_path - -_KEY_DIR = runtime_path("auth_keys") -_KEY_FILE = _KEY_DIR / "borealis-jwt-ed25519.key" -_LEGACY_KEY_FILE = runtime_path("keys") / "borealis-jwt-ed25519.key" - - -class JWTService: - def __init__(self, private_key: ed25519.Ed25519PrivateKey, key_id: str): - self._private_key = private_key - self._public_key = private_key.public_key() - self._key_id = key_id - - @property - def key_id(self) -> str: - return self._key_id - - def issue_access_token( - self, - guid: str, - ssl_key_fingerprint: str, - token_version: int, - expires_in: int = 900, - extra_claims: Optional[Dict[str, Any]] = None, - ) -> str: - now = int(time.time()) - payload: Dict[str, Any] = { - "sub": f"device:{guid}", - "guid": guid, - "ssl_key_fingerprint": ssl_key_fingerprint, - "token_version": int(token_version), - "iat": now, - "nbf": now, - "exp": now + int(expires_in), - } - if extra_claims: - payload.update(extra_claims) - - token = jwt.encode( - payload, - self._private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ), - algorithm="EdDSA", - headers={"kid": self._key_id}, - ) - return token - - def decode(self, token: str, *, audience: Optional[str] = None) -> Dict[str, Any]: - options = {"require": ["exp", "iat", "sub"]} - public_pem = self._public_key.public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - return jwt.decode( - token, - public_pem, - algorithms=["EdDSA"], - audience=audience, - options=options, - ) - - def public_jwk(self) -> Dict[str, Any]: - public_bytes = self._public_key.public_bytes( - encoding=serialization.Encoding.Raw, - format=serialization.PublicFormat.Raw, - ) - # PyJWT expects base64url without padding. - jwk_x = jwt.utils.base64url_encode(public_bytes).decode("ascii") - return {"kty": "OKP", "crv": "Ed25519", "kid": self._key_id, "alg": "EdDSA", "use": "sig", "x": jwk_x} - - -def load_service() -> JWTService: - private_key = _load_or_create_private_key() - public_bytes = private_key.public_key().public_bytes( - encoding=serialization.Encoding.DER, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - key_id = hashlib.sha256(public_bytes).hexdigest()[:16] - return JWTService(private_key, key_id) - - -def _load_or_create_private_key() -> ed25519.Ed25519PrivateKey: - ensure_runtime_dir("auth_keys") - _migrate_legacy_key_if_present() - - if _KEY_FILE.exists(): - with _KEY_FILE.open("rb") as fh: - return serialization.load_pem_private_key(fh.read(), password=None) - - if _LEGACY_KEY_FILE.exists(): - with _LEGACY_KEY_FILE.open("rb") as fh: - return serialization.load_pem_private_key(fh.read(), password=None) - - private_key = ed25519.Ed25519PrivateKey.generate() - pem = private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ) - with _KEY_FILE.open("wb") as fh: - fh.write(pem) - try: - if _KEY_FILE.exists() and hasattr(_KEY_FILE, "chmod"): - _KEY_FILE.chmod(0o600) - except Exception: - pass - return private_key - - -def _migrate_legacy_key_if_present() -> None: - if not _LEGACY_KEY_FILE.exists() or _KEY_FILE.exists(): - return - - try: - ensure_runtime_dir("auth_keys") - try: - _LEGACY_KEY_FILE.replace(_KEY_FILE) - except Exception: - _KEY_FILE.write_bytes(_LEGACY_KEY_FILE.read_bytes()) - except Exception: - return - diff --git a/Data/Server/Modules/auth/rate_limit.py b/Data/Server/Modules/auth/rate_limit.py deleted file mode 100644 index 5b0c9232..00000000 --- a/Data/Server/Modules/auth/rate_limit.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Tiny in-memory rate limiter suitable for single-process development servers. -""" - -from __future__ import annotations - -import time -from collections import deque -from dataclasses import dataclass -from threading import Lock -from typing import Deque, Dict, Tuple - - -@dataclass -class RateLimitDecision: - allowed: bool - retry_after: float - - -class SlidingWindowRateLimiter: - def __init__(self) -> None: - self._buckets: Dict[str, Deque[float]] = {} - self._lock = Lock() - - def check(self, key: str, limit: int, window_seconds: float) -> RateLimitDecision: - now = time.monotonic() - with self._lock: - bucket = self._buckets.get(key) - if bucket is None: - bucket = deque() - self._buckets[key] = bucket - - while bucket and now - bucket[0] > window_seconds: - bucket.popleft() - - if len(bucket) >= limit: - retry_after = max(0.0, window_seconds - (now - bucket[0])) - return RateLimitDecision(False, retry_after) - - bucket.append(now) - return RateLimitDecision(True, 0.0) diff --git a/Data/Server/Modules/crypto/__init__.py b/Data/Server/Modules/crypto/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/crypto/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/crypto/certificates.py b/Data/Server/Modules/crypto/certificates.py deleted file mode 100644 index d5f18a61..00000000 --- a/Data/Server/Modules/crypto/certificates.py +++ /dev/null @@ -1,372 +0,0 @@ -""" -Server TLS certificate management. - -Borealis now issues a dedicated root CA and a leaf server certificate so that -agents can pin the CA without requiring a re-enrollment every time the server -certificate is refreshed. The CA is persisted alongside the server key so that -existing deployments can be upgraded in-place. -""" - -from __future__ import annotations - -import os -import ssl -from datetime import datetime, timedelta, timezone -from pathlib import Path -from typing import Optional, Tuple - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.x509.oid import ExtendedKeyUsageOID, NameOID - -from Modules.runtime import ensure_server_certificates_dir, runtime_path, server_certificates_path - -_CERT_DIR = server_certificates_path() -_CERT_FILE = _CERT_DIR / "borealis-server-cert.pem" -_KEY_FILE = _CERT_DIR / "borealis-server-key.pem" -_BUNDLE_FILE = _CERT_DIR / "borealis-server-bundle.pem" -_CA_KEY_FILE = _CERT_DIR / "borealis-root-ca-key.pem" -_CA_CERT_FILE = _CERT_DIR / "borealis-root-ca.pem" - -_LEGACY_CERT_DIR = runtime_path("certs") -_LEGACY_CERT_FILE = _LEGACY_CERT_DIR / "borealis-server-cert.pem" -_LEGACY_KEY_FILE = _LEGACY_CERT_DIR / "borealis-server-key.pem" -_LEGACY_BUNDLE_FILE = _LEGACY_CERT_DIR / "borealis-server-bundle.pem" - -_ROOT_COMMON_NAME = "Borealis Root CA" -_ORG_NAME = "Borealis" -_ROOT_VALIDITY = timedelta(days=365 * 100) -_SERVER_VALIDITY = timedelta(days=365 * 5) - - -def ensure_certificate(common_name: str = "Borealis Server") -> Tuple[Path, Path, Path]: - """ - Ensure the root CA, server certificate, and bundle exist on disk. - - Returns (cert_path, key_path, bundle_path). - """ - - ensure_server_certificates_dir() - _migrate_legacy_material_if_present() - - ca_key, ca_cert, ca_regenerated = _ensure_root_ca() - - server_cert = _load_certificate(_CERT_FILE) - needs_regen = ca_regenerated or _server_certificate_needs_regeneration(server_cert, ca_cert) - if needs_regen: - server_cert = _generate_server_certificate(common_name, ca_key, ca_cert) - - if server_cert is None: - server_cert = _generate_server_certificate(common_name, ca_key, ca_cert) - - _write_bundle(server_cert, ca_cert) - - return _CERT_FILE, _KEY_FILE, _BUNDLE_FILE - - -def _migrate_legacy_material_if_present() -> None: - # Promote legacy runtime certificates (Server/Borealis/certs) into the new location. - if not _CERT_FILE.exists() or not _KEY_FILE.exists(): - legacy_cert = _LEGACY_CERT_FILE - legacy_key = _LEGACY_KEY_FILE - if legacy_cert.exists() and legacy_key.exists(): - try: - ensure_server_certificates_dir() - if not _CERT_FILE.exists(): - _safe_copy(legacy_cert, _CERT_FILE) - if not _KEY_FILE.exists(): - _safe_copy(legacy_key, _KEY_FILE) - except Exception: - pass - - -def _ensure_root_ca() -> Tuple[ec.EllipticCurvePrivateKey, x509.Certificate, bool]: - regenerated = False - - ca_key: Optional[ec.EllipticCurvePrivateKey] = None - ca_cert: Optional[x509.Certificate] = None - - if _CA_KEY_FILE.exists() and _CA_CERT_FILE.exists(): - try: - ca_key = _load_private_key(_CA_KEY_FILE) - ca_cert = _load_certificate(_CA_CERT_FILE) - if ca_cert is not None and ca_key is not None: - expiry = _cert_not_after(ca_cert) - subject = ca_cert.subject - subject_cn = "" - try: - subject_cn = subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value # type: ignore[index] - except Exception: - subject_cn = "" - try: - basic = ca_cert.extensions.get_extension_for_class(x509.BasicConstraints).value # type: ignore[attr-defined] - is_ca = bool(basic.ca) - except Exception: - is_ca = False - if ( - expiry <= datetime.now(tz=timezone.utc) - or not is_ca - or subject_cn != _ROOT_COMMON_NAME - ): - regenerated = True - else: - regenerated = True - except Exception: - regenerated = True - else: - regenerated = True - - if regenerated or ca_key is None or ca_cert is None: - ca_key = ec.generate_private_key(ec.SECP384R1()) - public_key = ca_key.public_key() - - now = datetime.now(tz=timezone.utc) - builder = ( - x509.CertificateBuilder() - .subject_name( - x509.Name( - [ - x509.NameAttribute(NameOID.COMMON_NAME, _ROOT_COMMON_NAME), - x509.NameAttribute(NameOID.ORGANIZATION_NAME, _ORG_NAME), - ] - ) - ) - .issuer_name( - x509.Name( - [ - x509.NameAttribute(NameOID.COMMON_NAME, _ROOT_COMMON_NAME), - x509.NameAttribute(NameOID.ORGANIZATION_NAME, _ORG_NAME), - ] - ) - ) - .public_key(public_key) - .serial_number(x509.random_serial_number()) - .not_valid_before(now - timedelta(minutes=5)) - .not_valid_after(now + _ROOT_VALIDITY) - .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) - .add_extension( - x509.KeyUsage( - digital_signature=True, - content_commitment=False, - key_encipherment=False, - data_encipherment=False, - key_agreement=False, - key_cert_sign=True, - crl_sign=True, - encipher_only=False, - decipher_only=False, - ), - critical=True, - ) - .add_extension( - x509.SubjectKeyIdentifier.from_public_key(public_key), - critical=False, - ) - ) - - builder = builder.add_extension( - x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key), - critical=False, - ) - - ca_cert = builder.sign(private_key=ca_key, algorithm=hashes.SHA384()) - - _CA_KEY_FILE.write_bytes( - ca_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - ) - ) - _CA_CERT_FILE.write_bytes(ca_cert.public_bytes(serialization.Encoding.PEM)) - - _tighten_permissions(_CA_KEY_FILE) - _tighten_permissions(_CA_CERT_FILE) - else: - regenerated = False - - return ca_key, ca_cert, regenerated - - -def _server_certificate_needs_regeneration( - server_cert: Optional[x509.Certificate], - ca_cert: x509.Certificate, -) -> bool: - if server_cert is None: - return True - - try: - if server_cert.issuer != ca_cert.subject: - return True - except Exception: - return True - - try: - expiry = _cert_not_after(server_cert) - if expiry <= datetime.now(tz=timezone.utc): - return True - except Exception: - return True - - try: - basic = server_cert.extensions.get_extension_for_class(x509.BasicConstraints).value # type: ignore[attr-defined] - if basic.ca: - return True - except Exception: - return True - - try: - eku = server_cert.extensions.get_extension_for_class(x509.ExtendedKeyUsage).value # type: ignore[attr-defined] - if ExtendedKeyUsageOID.SERVER_AUTH not in eku: - return True - except Exception: - return True - - return False - - -def _generate_server_certificate( - common_name: str, - ca_key: ec.EllipticCurvePrivateKey, - ca_cert: x509.Certificate, -) -> x509.Certificate: - private_key = ec.generate_private_key(ec.SECP384R1()) - public_key = private_key.public_key() - - now = datetime.now(tz=timezone.utc) - ca_expiry = _cert_not_after(ca_cert) - candidate_expiry = now + _SERVER_VALIDITY - not_after = min(ca_expiry - timedelta(days=1), candidate_expiry) - - builder = ( - x509.CertificateBuilder() - .subject_name( - x509.Name( - [ - x509.NameAttribute(NameOID.COMMON_NAME, common_name), - x509.NameAttribute(NameOID.ORGANIZATION_NAME, _ORG_NAME), - ] - ) - ) - .issuer_name(ca_cert.subject) - .public_key(public_key) - .serial_number(x509.random_serial_number()) - .not_valid_before(now - timedelta(minutes=5)) - .not_valid_after(not_after) - .add_extension( - x509.SubjectAlternativeName( - [ - x509.DNSName("localhost"), - x509.DNSName("127.0.0.1"), - x509.DNSName("::1"), - ] - ), - critical=False, - ) - .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True) - .add_extension( - x509.KeyUsage( - digital_signature=True, - content_commitment=False, - key_encipherment=False, - data_encipherment=False, - key_agreement=False, - key_cert_sign=False, - crl_sign=False, - encipher_only=False, - decipher_only=False, - ), - critical=True, - ) - .add_extension( - x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]), - critical=False, - ) - .add_extension( - x509.SubjectKeyIdentifier.from_public_key(public_key), - critical=False, - ) - .add_extension( - x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_key.public_key()), - critical=False, - ) - ) - - certificate = builder.sign(private_key=ca_key, algorithm=hashes.SHA384()) - - _KEY_FILE.write_bytes( - private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - ) - ) - _CERT_FILE.write_bytes(certificate.public_bytes(serialization.Encoding.PEM)) - - _tighten_permissions(_KEY_FILE) - _tighten_permissions(_CERT_FILE) - - return certificate - - -def _write_bundle(server_cert: x509.Certificate, ca_cert: x509.Certificate) -> None: - try: - server_pem = server_cert.public_bytes(serialization.Encoding.PEM).decode("utf-8").strip() - ca_pem = ca_cert.public_bytes(serialization.Encoding.PEM).decode("utf-8").strip() - except Exception: - return - - bundle = f"{server_pem}\n{ca_pem}\n" - _BUNDLE_FILE.write_text(bundle, encoding="utf-8") - _tighten_permissions(_BUNDLE_FILE) - - -def _safe_copy(src: Path, dst: Path) -> None: - try: - dst.write_bytes(src.read_bytes()) - except Exception: - pass - - -def _tighten_permissions(path: Path) -> None: - try: - if os.name == "posix": - path.chmod(0o600) - except Exception: - pass - - -def _load_private_key(path: Path) -> ec.EllipticCurvePrivateKey: - with path.open("rb") as fh: - return serialization.load_pem_private_key(fh.read(), password=None) - - -def _load_certificate(path: Path) -> Optional[x509.Certificate]: - try: - return x509.load_pem_x509_certificate(path.read_bytes()) - except Exception: - return None - - -def _cert_not_after(cert: x509.Certificate) -> datetime: - try: - return cert.not_valid_after_utc # type: ignore[attr-defined] - except AttributeError: - value = cert.not_valid_after - if value.tzinfo is None: - return value.replace(tzinfo=timezone.utc) - return value - - -def build_ssl_context() -> ssl.SSLContext: - cert_path, key_path, bundle_path = ensure_certificate() - context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - context.minimum_version = ssl.TLSVersion.TLSv1_3 - context.load_cert_chain(certfile=str(bundle_path), keyfile=str(key_path)) - return context - - -def certificate_paths() -> Tuple[str, str, str]: - cert_path, key_path, bundle_path = ensure_certificate() - return str(cert_path), str(key_path), str(bundle_path) diff --git a/Data/Server/Modules/crypto/keys.py b/Data/Server/Modules/crypto/keys.py deleted file mode 100644 index d3e6e1b7..00000000 --- a/Data/Server/Modules/crypto/keys.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Utility helpers for working with Ed25519 keys and fingerprints. -""" - -from __future__ import annotations - -import base64 -import hashlib -import re -from typing import Tuple - -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.serialization import load_der_public_key -from cryptography.hazmat.primitives.asymmetric import ed25519 - - -def generate_ed25519_keypair() -> Tuple[ed25519.Ed25519PrivateKey, bytes]: - """ - Generate a new Ed25519 keypair. - - Returns the private key object and the public key encoded as SubjectPublicKeyInfo DER bytes. - """ - - private_key = ed25519.Ed25519PrivateKey.generate() - public_key = private_key.public_key().public_bytes( - encoding=serialization.Encoding.DER, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - return private_key, public_key - - -def normalize_base64(data: str) -> str: - """ - Collapse whitespace and normalise URL-safe encodings so we can reliably decode. - """ - - cleaned = re.sub(r"\\s+", "", data or "") - return cleaned.replace("-", "+").replace("_", "/") - - -def spki_der_from_base64(spki_b64: str) -> bytes: - return base64.b64decode(normalize_base64(spki_b64), validate=True) - - -def base64_from_spki_der(spki_der: bytes) -> str: - return base64.b64encode(spki_der).decode("ascii") - - -def fingerprint_from_spki_der(spki_der: bytes) -> str: - digest = hashlib.sha256(spki_der).hexdigest() - return digest.lower() - - -def fingerprint_from_base64_spki(spki_b64: str) -> str: - return fingerprint_from_spki_der(spki_der_from_base64(spki_b64)) - - -def private_key_to_pem(private_key: ed25519.Ed25519PrivateKey) -> bytes: - return private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ) - - -def public_key_to_pem(public_spki_der: bytes) -> bytes: - public_key = load_der_public_key(public_spki_der) - return public_key.public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) diff --git a/Data/Server/Modules/crypto/signing.py b/Data/Server/Modules/crypto/signing.py deleted file mode 100644 index 1c6ff7b4..00000000 --- a/Data/Server/Modules/crypto/signing.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Code-signing helpers for delivering scripts to agents. -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Tuple - -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ed25519 - -from Modules.runtime import ( - ensure_server_certificates_dir, - server_certificates_path, - runtime_path, -) - -from .keys import base64_from_spki_der - -_KEY_DIR = server_certificates_path("Code-Signing") -_SIGNING_KEY_FILE = _KEY_DIR / "borealis-script-ed25519.key" -_SIGNING_PUB_FILE = _KEY_DIR / "borealis-script-ed25519.pub" -_LEGACY_KEY_FILE = runtime_path("keys") / "borealis-script-ed25519.key" -_LEGACY_PUB_FILE = runtime_path("keys") / "borealis-script-ed25519.pub" -_OLD_RUNTIME_KEY_DIR = runtime_path("script_signing_keys") -_OLD_RUNTIME_KEY_FILE = _OLD_RUNTIME_KEY_DIR / "borealis-script-ed25519.key" -_OLD_RUNTIME_PUB_FILE = _OLD_RUNTIME_KEY_DIR / "borealis-script-ed25519.pub" - - -class ScriptSigner: - def __init__(self, private_key: ed25519.Ed25519PrivateKey): - self._private = private_key - self._public = private_key.public_key() - - def sign(self, payload: bytes) -> bytes: - return self._private.sign(payload) - - def public_spki_der(self) -> bytes: - return self._public.public_bytes( - encoding=serialization.Encoding.DER, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - - def public_base64_spki(self) -> str: - return base64_from_spki_der(self.public_spki_der()) - - -def load_signer() -> ScriptSigner: - private_key = _load_or_create() - return ScriptSigner(private_key) - - -def _load_or_create() -> ed25519.Ed25519PrivateKey: - ensure_server_certificates_dir("Code-Signing") - _migrate_legacy_material_if_present() - - if _SIGNING_KEY_FILE.exists(): - with _SIGNING_KEY_FILE.open("rb") as fh: - return serialization.load_pem_private_key(fh.read(), password=None) - - if _LEGACY_KEY_FILE.exists(): - with _LEGACY_KEY_FILE.open("rb") as fh: - return serialization.load_pem_private_key(fh.read(), password=None) - - private_key = ed25519.Ed25519PrivateKey.generate() - pem = private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ) - with _SIGNING_KEY_FILE.open("wb") as fh: - fh.write(pem) - try: - if hasattr(_SIGNING_KEY_FILE, "chmod"): - _SIGNING_KEY_FILE.chmod(0o600) - except Exception: - pass - - pub_der = private_key.public_key().public_bytes( - encoding=serialization.Encoding.DER, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - _SIGNING_PUB_FILE.write_bytes(pub_der) - - return private_key - - -def _migrate_legacy_material_if_present() -> None: - if _SIGNING_KEY_FILE.exists(): - return - - # First migrate from legacy runtime path embedded in Server runtime. - try: - if _OLD_RUNTIME_KEY_FILE.exists() and not _SIGNING_KEY_FILE.exists(): - ensure_server_certificates_dir("Code-Signing") - try: - _OLD_RUNTIME_KEY_FILE.replace(_SIGNING_KEY_FILE) - except Exception: - _SIGNING_KEY_FILE.write_bytes(_OLD_RUNTIME_KEY_FILE.read_bytes()) - if _OLD_RUNTIME_PUB_FILE.exists() and not _SIGNING_PUB_FILE.exists(): - try: - _OLD_RUNTIME_PUB_FILE.replace(_SIGNING_PUB_FILE) - except Exception: - _SIGNING_PUB_FILE.write_bytes(_OLD_RUNTIME_PUB_FILE.read_bytes()) - except Exception: - pass - - if not _LEGACY_KEY_FILE.exists() or _SIGNING_KEY_FILE.exists(): - return - - try: - ensure_server_certificates_dir("Code-Signing") - try: - _LEGACY_KEY_FILE.replace(_SIGNING_KEY_FILE) - except Exception: - _SIGNING_KEY_FILE.write_bytes(_LEGACY_KEY_FILE.read_bytes()) - - if _LEGACY_PUB_FILE.exists() and not _SIGNING_PUB_FILE.exists(): - try: - _LEGACY_PUB_FILE.replace(_SIGNING_PUB_FILE) - except Exception: - _SIGNING_PUB_FILE.write_bytes(_LEGACY_PUB_FILE.read_bytes()) - except Exception: - return diff --git a/Data/Server/Modules/db_migrations.py b/Data/Server/Modules/db_migrations.py deleted file mode 100644 index 1e99275e..00000000 --- a/Data/Server/Modules/db_migrations.py +++ /dev/null @@ -1,488 +0,0 @@ -""" -Database migration helpers for Borealis. - -This module centralises schema evolution so the main server module can stay -focused on request handling. The migration functions are intentionally -idempotent — they can run repeatedly without changing state once the schema -matches the desired shape. -""" - -from __future__ import annotations - -import sqlite3 -import uuid -from datetime import datetime, timezone -from typing import List, Optional, Sequence, Tuple - - -DEVICE_TABLE = "devices" - - -def apply_all(conn: sqlite3.Connection) -> None: - """ - Run all known schema migrations against the provided sqlite3 connection. - """ - - _ensure_devices_table(conn) - _ensure_device_aux_tables(conn) - _ensure_refresh_token_table(conn) - _ensure_install_code_table(conn) - _ensure_install_code_persistence_table(conn) - _ensure_device_approval_table(conn) - - conn.commit() - - -def _ensure_devices_table(conn: sqlite3.Connection) -> None: - cur = conn.cursor() - if not _table_exists(cur, DEVICE_TABLE): - _create_devices_table(cur) - return - - column_info = _table_info(cur, DEVICE_TABLE) - col_names = [c[1] for c in column_info] - pk_cols = [c[1] for c in column_info if c[5]] - - needs_rebuild = pk_cols != ["guid"] - required_columns = { - "guid": "TEXT", - "hostname": "TEXT", - "description": "TEXT", - "created_at": "INTEGER", - "agent_hash": "TEXT", - "memory": "TEXT", - "network": "TEXT", - "software": "TEXT", - "storage": "TEXT", - "cpu": "TEXT", - "device_type": "TEXT", - "domain": "TEXT", - "external_ip": "TEXT", - "internal_ip": "TEXT", - "last_reboot": "TEXT", - "last_seen": "INTEGER", - "last_user": "TEXT", - "operating_system": "TEXT", - "uptime": "INTEGER", - "agent_id": "TEXT", - "ansible_ee_ver": "TEXT", - "connection_type": "TEXT", - "connection_endpoint": "TEXT", - "ssl_key_fingerprint": "TEXT", - "token_version": "INTEGER", - "status": "TEXT", - "key_added_at": "TEXT", - } - - missing_columns = [col for col in required_columns if col not in col_names] - if missing_columns: - needs_rebuild = True - - if needs_rebuild: - _rebuild_devices_table(conn, column_info) - else: - _ensure_column_defaults(cur) - - _ensure_device_indexes(cur) - - -def _ensure_device_aux_tables(conn: sqlite3.Connection) -> None: - cur = conn.cursor() - cur.execute( - """ - CREATE TABLE IF NOT EXISTS device_keys ( - id TEXT PRIMARY KEY, - guid TEXT NOT NULL, - ssl_key_fingerprint TEXT NOT NULL, - added_at TEXT NOT NULL, - retired_at TEXT - ) - """ - ) - cur.execute( - """ - CREATE UNIQUE INDEX IF NOT EXISTS uq_device_keys_guid_fingerprint - ON device_keys(guid, ssl_key_fingerprint) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_device_keys_guid - ON device_keys(guid) - """ - ) - - -def _ensure_refresh_token_table(conn: sqlite3.Connection) -> None: - cur = conn.cursor() - cur.execute( - """ - CREATE TABLE IF NOT EXISTS refresh_tokens ( - id TEXT PRIMARY KEY, - guid TEXT NOT NULL, - token_hash TEXT NOT NULL, - dpop_jkt TEXT, - created_at TEXT NOT NULL, - expires_at TEXT NOT NULL, - revoked_at TEXT, - last_used_at TEXT - ) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_refresh_tokens_guid - ON refresh_tokens(guid) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_refresh_tokens_expires_at - ON refresh_tokens(expires_at) - """ - ) - - -def _ensure_install_code_table(conn: sqlite3.Connection) -> None: - cur = conn.cursor() - cur.execute( - """ - CREATE TABLE IF NOT EXISTS enrollment_install_codes ( - id TEXT PRIMARY KEY, - code TEXT NOT NULL UNIQUE, - expires_at TEXT NOT NULL, - created_by_user_id TEXT, - used_at TEXT, - used_by_guid TEXT, - max_uses INTEGER NOT NULL DEFAULT 1, - use_count INTEGER NOT NULL DEFAULT 0, - last_used_at TEXT - ) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_eic_expires_at - ON enrollment_install_codes(expires_at) - """ - ) - - columns = {row[1] for row in _table_info(cur, "enrollment_install_codes")} - if "max_uses" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes - ADD COLUMN max_uses INTEGER NOT NULL DEFAULT 1 - """ - ) - if "use_count" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes - ADD COLUMN use_count INTEGER NOT NULL DEFAULT 0 - """ - ) - if "last_used_at" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes - ADD COLUMN last_used_at TEXT - """ - ) - - -def _ensure_install_code_persistence_table(conn: sqlite3.Connection) -> None: - cur = conn.cursor() - cur.execute( - """ - CREATE TABLE IF NOT EXISTS enrollment_install_codes_persistent ( - id TEXT PRIMARY KEY, - code TEXT NOT NULL UNIQUE, - created_at TEXT NOT NULL, - expires_at TEXT NOT NULL, - created_by_user_id TEXT, - used_at TEXT, - used_by_guid TEXT, - max_uses INTEGER NOT NULL DEFAULT 1, - last_known_use_count INTEGER NOT NULL DEFAULT 0, - last_used_at TEXT, - is_active INTEGER NOT NULL DEFAULT 1, - archived_at TEXT, - consumed_at TEXT - ) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_eicp_active - ON enrollment_install_codes_persistent(is_active, expires_at) - """ - ) - cur.execute( - """ - CREATE UNIQUE INDEX IF NOT EXISTS uq_eicp_code - ON enrollment_install_codes_persistent(code) - """ - ) - - columns = {row[1] for row in _table_info(cur, "enrollment_install_codes_persistent")} - if "last_known_use_count" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes_persistent - ADD COLUMN last_known_use_count INTEGER NOT NULL DEFAULT 0 - """ - ) - if "archived_at" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes_persistent - ADD COLUMN archived_at TEXT - """ - ) - if "consumed_at" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes_persistent - ADD COLUMN consumed_at TEXT - """ - ) - if "is_active" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes_persistent - ADD COLUMN is_active INTEGER NOT NULL DEFAULT 1 - """ - ) - if "used_at" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes_persistent - ADD COLUMN used_at TEXT - """ - ) - if "used_by_guid" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes_persistent - ADD COLUMN used_by_guid TEXT - """ - ) - if "last_used_at" not in columns: - cur.execute( - """ - ALTER TABLE enrollment_install_codes_persistent - ADD COLUMN last_used_at TEXT - """ - ) - - -def _ensure_device_approval_table(conn: sqlite3.Connection) -> None: - cur = conn.cursor() - cur.execute( - """ - CREATE TABLE IF NOT EXISTS device_approvals ( - id TEXT PRIMARY KEY, - approval_reference TEXT NOT NULL UNIQUE, - guid TEXT, - hostname_claimed TEXT NOT NULL, - ssl_key_fingerprint_claimed TEXT NOT NULL, - enrollment_code_id TEXT NOT NULL, - status TEXT NOT NULL, - client_nonce TEXT NOT NULL, - server_nonce TEXT NOT NULL, - agent_pubkey_der BLOB NOT NULL, - created_at TEXT NOT NULL, - updated_at TEXT NOT NULL, - approved_by_user_id TEXT - ) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_da_status - ON device_approvals(status) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_da_fp_status - ON device_approvals(ssl_key_fingerprint_claimed, status) - """ - ) - - -def _create_devices_table(cur: sqlite3.Cursor) -> None: - cur.execute( - """ - CREATE TABLE devices ( - guid TEXT PRIMARY KEY, - hostname TEXT, - description TEXT, - created_at INTEGER, - agent_hash TEXT, - memory TEXT, - network TEXT, - software TEXT, - storage TEXT, - cpu TEXT, - device_type TEXT, - domain TEXT, - external_ip TEXT, - internal_ip TEXT, - last_reboot TEXT, - last_seen INTEGER, - last_user TEXT, - operating_system TEXT, - uptime INTEGER, - agent_id TEXT, - ansible_ee_ver TEXT, - connection_type TEXT, - connection_endpoint TEXT, - ssl_key_fingerprint TEXT, - token_version INTEGER DEFAULT 1, - status TEXT DEFAULT 'active', - key_added_at TEXT - ) - """ - ) - _ensure_device_indexes(cur) - - -def _ensure_device_indexes(cur: sqlite3.Cursor) -> None: - cur.execute( - """ - CREATE UNIQUE INDEX IF NOT EXISTS uq_devices_hostname - ON devices(hostname) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_devices_ssl_key - ON devices(ssl_key_fingerprint) - """ - ) - cur.execute( - """ - CREATE INDEX IF NOT EXISTS idx_devices_status - ON devices(status) - """ - ) - - -def _ensure_column_defaults(cur: sqlite3.Cursor) -> None: - cur.execute( - """ - UPDATE devices - SET token_version = COALESCE(token_version, 1) - WHERE token_version IS NULL - """ - ) - cur.execute( - """ - UPDATE devices - SET status = COALESCE(status, 'active') - WHERE status IS NULL OR status = '' - """ - ) - - -def _rebuild_devices_table(conn: sqlite3.Connection, column_info: Sequence[Tuple]) -> None: - cur = conn.cursor() - cur.execute("PRAGMA foreign_keys=OFF") - cur.execute("BEGIN IMMEDIATE") - - cur.execute("ALTER TABLE devices RENAME TO devices_legacy") - _create_devices_table(cur) - - legacy_columns = [c[1] for c in column_info] - cur.execute(f"SELECT {', '.join(legacy_columns)} FROM devices_legacy") - rows = cur.fetchall() - - insert_sql = ( - """ - INSERT OR REPLACE INTO devices ( - guid, hostname, description, created_at, agent_hash, memory, - network, software, storage, cpu, device_type, domain, external_ip, - internal_ip, last_reboot, last_seen, last_user, operating_system, - uptime, agent_id, ansible_ee_ver, connection_type, connection_endpoint, - ssl_key_fingerprint, token_version, status, key_added_at - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """ - ) - - for row in rows: - record = dict(zip(legacy_columns, row)) - guid = _normalized_guid(record.get("guid")) - if not guid: - guid = str(uuid.uuid4()) - hostname = record.get("hostname") - created_at = record.get("created_at") - key_added_at = record.get("key_added_at") - if key_added_at is None: - key_added_at = _default_key_added_at(created_at) - - params: Tuple = ( - guid, - hostname, - record.get("description"), - created_at, - record.get("agent_hash"), - record.get("memory"), - record.get("network"), - record.get("software"), - record.get("storage"), - record.get("cpu"), - record.get("device_type"), - record.get("domain"), - record.get("external_ip"), - record.get("internal_ip"), - record.get("last_reboot"), - record.get("last_seen"), - record.get("last_user"), - record.get("operating_system"), - record.get("uptime"), - record.get("agent_id"), - record.get("ansible_ee_ver"), - record.get("connection_type"), - record.get("connection_endpoint"), - record.get("ssl_key_fingerprint"), - record.get("token_version") or 1, - record.get("status") or "active", - key_added_at, - ) - cur.execute(insert_sql, params) - - cur.execute("DROP TABLE devices_legacy") - cur.execute("COMMIT") - cur.execute("PRAGMA foreign_keys=ON") - - -def _default_key_added_at(created_at: Optional[int]) -> Optional[str]: - if created_at: - try: - dt = datetime.fromtimestamp(int(created_at), tz=timezone.utc) - return dt.isoformat() - except Exception: - pass - return datetime.now(tz=timezone.utc).isoformat() - - -def _table_exists(cur: sqlite3.Cursor, name: str) -> bool: - cur.execute( - "SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", - (name,), - ) - return cur.fetchone() is not None - - -def _table_info(cur: sqlite3.Cursor, name: str) -> List[Tuple]: - cur.execute(f"PRAGMA table_info({name})") - return cur.fetchall() - - -def _normalized_guid(value: Optional[str]) -> str: - if not value: - return "" - return str(value).strip() diff --git a/Data/Server/Modules/enrollment/__init__.py b/Data/Server/Modules/enrollment/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/enrollment/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/enrollment/nonce_store.py b/Data/Server/Modules/enrollment/nonce_store.py deleted file mode 100644 index bcdb962e..00000000 --- a/Data/Server/Modules/enrollment/nonce_store.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Short-lived nonce cache to defend against replay attacks during enrollment. -""" - -from __future__ import annotations - -import time -from threading import Lock -from typing import Dict - - -class NonceCache: - def __init__(self, ttl_seconds: float = 300.0) -> None: - self._ttl = ttl_seconds - self._entries: Dict[str, float] = {} - self._lock = Lock() - - def consume(self, key: str) -> bool: - """ - Attempt to consume the nonce identified by `key`. - - Returns True on first use within TTL, False if already consumed. - """ - - now = time.monotonic() - with self._lock: - expire_at = self._entries.get(key) - if expire_at and expire_at > now: - return False - self._entries[key] = now + self._ttl - # Opportunistic cleanup to keep the dict small - stale = [nonce for nonce, expiry in self._entries.items() if expiry <= now] - for nonce in stale: - self._entries.pop(nonce, None) - return True diff --git a/Data/Server/Modules/enrollment/routes.py b/Data/Server/Modules/enrollment/routes.py deleted file mode 100644 index 948bd33b..00000000 --- a/Data/Server/Modules/enrollment/routes.py +++ /dev/null @@ -1,759 +0,0 @@ -from __future__ import annotations - -import base64 -import secrets -import sqlite3 -import uuid -from datetime import datetime, timezone, timedelta -import time -from typing import Any, Callable, Dict, Optional, Tuple - -AGENT_CONTEXT_HEADER = "X-Borealis-Agent-Context" - - -def _canonical_context(value: Optional[str]) -> Optional[str]: - if not value: - return None - cleaned = "".join(ch for ch in str(value) if ch.isalnum() or ch in ("_", "-")) - if not cleaned: - return None - return cleaned.upper() - -from flask import Blueprint, jsonify, request - -from Modules.auth.rate_limit import SlidingWindowRateLimiter -from Modules.crypto import keys as crypto_keys -from Modules.enrollment.nonce_store import NonceCache -from Modules.guid_utils import normalize_guid -from cryptography.hazmat.primitives import serialization - - -def register( - app, - *, - db_conn_factory: Callable[[], sqlite3.Connection], - log: Callable[[str, str, Optional[str]], None], - jwt_service, - tls_bundle_path: str, - ip_rate_limiter: SlidingWindowRateLimiter, - fp_rate_limiter: SlidingWindowRateLimiter, - nonce_cache: NonceCache, - script_signer, -) -> None: - blueprint = Blueprint("enrollment", __name__) - - def _now() -> datetime: - return datetime.now(tz=timezone.utc) - - def _iso(dt: datetime) -> str: - return dt.isoformat() - - def _remote_addr() -> str: - forwarded = request.headers.get("X-Forwarded-For") - if forwarded: - return forwarded.split(",")[0].strip() - addr = request.remote_addr or "unknown" - return addr.strip() - - def _signing_key_b64() -> str: - if not script_signer: - return "" - try: - return script_signer.public_base64_spki() - except Exception: - return "" - - def _rate_limited( - key: str, - limiter: SlidingWindowRateLimiter, - limit: int, - window_s: float, - context_hint: Optional[str], - ): - decision = limiter.check(key, limit, window_s) - if not decision.allowed: - log( - "server", - f"enrollment rate limited key={key} limit={limit}/{window_s}s retry_after={decision.retry_after:.2f}", - context_hint, - ) - response = jsonify({"error": "rate_limited", "retry_after": decision.retry_after}) - response.status_code = 429 - response.headers["Retry-After"] = f"{int(decision.retry_after) or 1}" - return response - return None - - def _load_install_code(cur: sqlite3.Cursor, code_value: str) -> Optional[Dict[str, Any]]: - cur.execute( - """ - SELECT id, - code, - expires_at, - used_at, - used_by_guid, - max_uses, - use_count, - last_used_at - FROM enrollment_install_codes - WHERE code = ? - """, - (code_value,), - ) - row = cur.fetchone() - if not row: - return None - keys = [ - "id", - "code", - "expires_at", - "used_at", - "used_by_guid", - "max_uses", - "use_count", - "last_used_at", - ] - record = dict(zip(keys, row)) - return record - - def _install_code_valid( - record: Dict[str, Any], fingerprint: str, cur: sqlite3.Cursor - ) -> Tuple[bool, Optional[str]]: - if not record: - return False, None - expires_at = record.get("expires_at") - if not isinstance(expires_at, str): - return False, None - try: - expiry = datetime.fromisoformat(expires_at) - except Exception: - return False, None - if expiry <= _now(): - return False, None - try: - max_uses = int(record.get("max_uses") or 1) - except Exception: - max_uses = 1 - if max_uses < 1: - max_uses = 1 - try: - use_count = int(record.get("use_count") or 0) - except Exception: - use_count = 0 - if use_count < max_uses: - return True, None - - guid = normalize_guid(record.get("used_by_guid")) - if not guid: - return False, None - cur.execute( - "SELECT ssl_key_fingerprint FROM devices WHERE UPPER(guid) = ?", - (guid,), - ) - row = cur.fetchone() - if not row: - return False, None - stored_fp = (row[0] or "").strip().lower() - if not stored_fp: - return False, None - if stored_fp == (fingerprint or "").strip().lower(): - return True, guid - return False, None - - def _normalize_host(hostname: str, guid: str, cur: sqlite3.Cursor) -> str: - guid_norm = normalize_guid(guid) - base = (hostname or "").strip() or guid_norm - base = base[:253] - candidate = base - suffix = 1 - while True: - cur.execute( - "SELECT guid FROM devices WHERE hostname = ?", - (candidate,), - ) - row = cur.fetchone() - if not row: - return candidate - existing_guid = normalize_guid(row[0]) - if existing_guid == guid_norm: - return candidate - candidate = f"{base}-{suffix}" - suffix += 1 - if suffix > 50: - return guid_norm - - def _store_device_key(cur: sqlite3.Cursor, guid: str, fingerprint: str) -> None: - guid_norm = normalize_guid(guid) - added_at = _iso(_now()) - cur.execute( - """ - INSERT OR IGNORE INTO device_keys (id, guid, ssl_key_fingerprint, added_at) - VALUES (?, ?, ?, ?) - """, - (str(uuid.uuid4()), guid_norm, fingerprint, added_at), - ) - cur.execute( - """ - UPDATE device_keys - SET retired_at = ? - WHERE guid = ? - AND ssl_key_fingerprint != ? - AND retired_at IS NULL - """, - (_iso(_now()), guid_norm, fingerprint), - ) - - def _ensure_device_record(cur: sqlite3.Cursor, guid: str, hostname: str, fingerprint: str) -> Dict[str, Any]: - guid_norm = normalize_guid(guid) - cur.execute( - """ - SELECT guid, hostname, token_version, status, ssl_key_fingerprint, key_added_at - FROM devices - WHERE UPPER(guid) = ? - """, - (guid_norm,), - ) - row = cur.fetchone() - if row: - keys = [ - "guid", - "hostname", - "token_version", - "status", - "ssl_key_fingerprint", - "key_added_at", - ] - record = dict(zip(keys, row)) - record["guid"] = normalize_guid(record.get("guid")) - stored_fp = (record.get("ssl_key_fingerprint") or "").strip().lower() - new_fp = (fingerprint or "").strip().lower() - if not stored_fp and new_fp: - cur.execute( - "UPDATE devices SET ssl_key_fingerprint = ?, key_added_at = ? WHERE guid = ?", - (fingerprint, _iso(_now()), record["guid"]), - ) - record["ssl_key_fingerprint"] = fingerprint - elif new_fp and stored_fp != new_fp: - now_iso = _iso(_now()) - try: - current_version = int(record.get("token_version") or 1) - except Exception: - current_version = 1 - new_version = max(current_version + 1, 1) - cur.execute( - """ - UPDATE devices - SET ssl_key_fingerprint = ?, - key_added_at = ?, - token_version = ?, - status = 'active' - WHERE guid = ? - """, - (fingerprint, now_iso, new_version, record["guid"]), - ) - cur.execute( - """ - UPDATE refresh_tokens - SET revoked_at = ? - WHERE guid = ? - AND revoked_at IS NULL - """, - (now_iso, record["guid"]), - ) - record["ssl_key_fingerprint"] = fingerprint - record["token_version"] = new_version - record["status"] = "active" - record["key_added_at"] = now_iso - return record - - resolved_hostname = _normalize_host(hostname, guid_norm, cur) - created_at = int(time.time()) - key_added_at = _iso(_now()) - cur.execute( - """ - INSERT INTO devices ( - guid, hostname, created_at, last_seen, ssl_key_fingerprint, - token_version, status, key_added_at - ) - VALUES (?, ?, ?, ?, ?, 1, 'active', ?) - """, - ( - guid_norm, - resolved_hostname, - created_at, - created_at, - fingerprint, - key_added_at, - ), - ) - return { - "guid": guid_norm, - "hostname": resolved_hostname, - "token_version": 1, - "status": "active", - "ssl_key_fingerprint": fingerprint, - "key_added_at": key_added_at, - } - - def _hash_refresh_token(token: str) -> str: - import hashlib - - return hashlib.sha256(token.encode("utf-8")).hexdigest() - - def _issue_refresh_token(cur: sqlite3.Cursor, guid: str) -> Dict[str, Any]: - token = secrets.token_urlsafe(48) - now = _now() - expires_at = now.replace(microsecond=0) + timedelta(days=30) - cur.execute( - """ - INSERT INTO refresh_tokens (id, guid, token_hash, created_at, expires_at) - VALUES (?, ?, ?, ?, ?) - """, - ( - str(uuid.uuid4()), - guid, - _hash_refresh_token(token), - _iso(now), - _iso(expires_at), - ), - ) - return {"token": token, "expires_at": expires_at} - - @blueprint.route("/api/agent/enroll/request", methods=["POST"]) - def enrollment_request(): - remote = _remote_addr() - context_hint = _canonical_context(request.headers.get(AGENT_CONTEXT_HEADER)) - - rate_error = _rate_limited(f"ip:{remote}", ip_rate_limiter, 40, 60.0, context_hint) - if rate_error: - return rate_error - - payload = request.get_json(force=True, silent=True) or {} - hostname = str(payload.get("hostname") or "").strip() - enrollment_code = str(payload.get("enrollment_code") or "").strip() - agent_pubkey_b64 = payload.get("agent_pubkey") - client_nonce_b64 = payload.get("client_nonce") - - log( - "server", - "enrollment request received " - f"ip={remote} hostname={hostname or ''} code_mask={_mask_code(enrollment_code)} " - f"pubkey_len={len(agent_pubkey_b64 or '')} nonce_len={len(client_nonce_b64 or '')}", - context_hint, - ) - - if not hostname: - log("server", f"enrollment rejected missing_hostname ip={remote}", context_hint) - return jsonify({"error": "hostname_required"}), 400 - if not enrollment_code: - log("server", f"enrollment rejected missing_code ip={remote} host={hostname}", context_hint) - return jsonify({"error": "enrollment_code_required"}), 400 - if not isinstance(agent_pubkey_b64, str): - log("server", f"enrollment rejected missing_pubkey ip={remote} host={hostname}", context_hint) - return jsonify({"error": "agent_pubkey_required"}), 400 - if not isinstance(client_nonce_b64, str): - log("server", f"enrollment rejected missing_nonce ip={remote} host={hostname}", context_hint) - return jsonify({"error": "client_nonce_required"}), 400 - - try: - agent_pubkey_der = crypto_keys.spki_der_from_base64(agent_pubkey_b64) - except Exception: - log("server", f"enrollment rejected invalid_pubkey ip={remote} host={hostname}", context_hint) - return jsonify({"error": "invalid_agent_pubkey"}), 400 - - if len(agent_pubkey_der) < 10: - log("server", f"enrollment rejected short_pubkey ip={remote} host={hostname}", context_hint) - return jsonify({"error": "invalid_agent_pubkey"}), 400 - - try: - client_nonce_bytes = base64.b64decode(client_nonce_b64, validate=True) - except Exception: - log("server", f"enrollment rejected invalid_nonce ip={remote} host={hostname}", context_hint) - return jsonify({"error": "invalid_client_nonce"}), 400 - if len(client_nonce_bytes) < 16: - log("server", f"enrollment rejected short_nonce ip={remote} host={hostname}", context_hint) - return jsonify({"error": "invalid_client_nonce"}), 400 - - fingerprint = crypto_keys.fingerprint_from_spki_der(agent_pubkey_der) - rate_error = _rate_limited(f"fp:{fingerprint}", fp_rate_limiter, 12, 60.0, context_hint) - if rate_error: - return rate_error - - conn = db_conn_factory() - try: - cur = conn.cursor() - install_code = _load_install_code(cur, enrollment_code) - valid_code, reuse_guid = _install_code_valid(install_code, fingerprint, cur) - if not valid_code: - log( - "server", - "enrollment request invalid_code " - f"host={hostname} fingerprint={fingerprint[:12]} code_mask={_mask_code(enrollment_code)}", - context_hint, - ) - return jsonify({"error": "invalid_enrollment_code"}), 400 - - approval_reference: str - record_id: str - server_nonce_bytes = secrets.token_bytes(32) - server_nonce_b64 = base64.b64encode(server_nonce_bytes).decode("ascii") - now = _iso(_now()) - - cur.execute( - """ - SELECT id, approval_reference - FROM device_approvals - WHERE ssl_key_fingerprint_claimed = ? - AND status = 'pending' - """, - (fingerprint,), - ) - existing = cur.fetchone() - if existing: - record_id = existing[0] - approval_reference = existing[1] - cur.execute( - """ - UPDATE device_approvals - SET hostname_claimed = ?, - guid = ?, - enrollment_code_id = ?, - client_nonce = ?, - server_nonce = ?, - agent_pubkey_der = ?, - updated_at = ? - WHERE id = ? - """, - ( - hostname, - reuse_guid, - install_code["id"], - client_nonce_b64, - server_nonce_b64, - agent_pubkey_der, - now, - record_id, - ), - ) - else: - record_id = str(uuid.uuid4()) - approval_reference = str(uuid.uuid4()) - cur.execute( - """ - INSERT INTO device_approvals ( - id, approval_reference, guid, hostname_claimed, - ssl_key_fingerprint_claimed, enrollment_code_id, - status, client_nonce, server_nonce, agent_pubkey_der, - created_at, updated_at - ) - VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?, ?) - """, - ( - record_id, - approval_reference, - reuse_guid, - hostname, - fingerprint, - install_code["id"], - client_nonce_b64, - server_nonce_b64, - agent_pubkey_der, - now, - now, - ), - ) - - conn.commit() - finally: - conn.close() - - response = { - "status": "pending", - "approval_reference": approval_reference, - "server_nonce": server_nonce_b64, - "poll_after_ms": 3000, - "server_certificate": _load_tls_bundle(tls_bundle_path), - "signing_key": _signing_key_b64(), - } - log( - "server", - f"enrollment request queued fingerprint={fingerprint[:12]} host={hostname} ip={remote}", - context_hint, - ) - return jsonify(response) - - @blueprint.route("/api/agent/enroll/poll", methods=["POST"]) - def enrollment_poll(): - payload = request.get_json(force=True, silent=True) or {} - approval_reference = payload.get("approval_reference") - client_nonce_b64 = payload.get("client_nonce") - proof_sig_b64 = payload.get("proof_sig") - context_hint = _canonical_context(request.headers.get(AGENT_CONTEXT_HEADER)) - - log( - "server", - "enrollment poll received " - f"ref={approval_reference} client_nonce_len={len(client_nonce_b64 or '')}" - f" proof_sig_len={len(proof_sig_b64 or '')}", - context_hint, - ) - - if not isinstance(approval_reference, str) or not approval_reference: - log("server", "enrollment poll rejected missing_reference", context_hint) - return jsonify({"error": "approval_reference_required"}), 400 - if not isinstance(client_nonce_b64, str): - log("server", f"enrollment poll rejected missing_nonce ref={approval_reference}", context_hint) - return jsonify({"error": "client_nonce_required"}), 400 - if not isinstance(proof_sig_b64, str): - log("server", f"enrollment poll rejected missing_sig ref={approval_reference}", context_hint) - return jsonify({"error": "proof_sig_required"}), 400 - - try: - client_nonce_bytes = base64.b64decode(client_nonce_b64, validate=True) - except Exception: - log("server", f"enrollment poll invalid_client_nonce ref={approval_reference}", context_hint) - return jsonify({"error": "invalid_client_nonce"}), 400 - - try: - proof_sig = base64.b64decode(proof_sig_b64, validate=True) - except Exception: - log("server", f"enrollment poll invalid_sig ref={approval_reference}", context_hint) - return jsonify({"error": "invalid_proof_sig"}), 400 - - conn = db_conn_factory() - try: - cur = conn.cursor() - cur.execute( - """ - SELECT id, guid, hostname_claimed, ssl_key_fingerprint_claimed, - enrollment_code_id, status, client_nonce, server_nonce, - agent_pubkey_der, created_at, updated_at, approved_by_user_id - FROM device_approvals - WHERE approval_reference = ? - """, - (approval_reference,), - ) - row = cur.fetchone() - if not row: - log("server", f"enrollment poll unknown_reference ref={approval_reference}", context_hint) - return jsonify({"status": "unknown"}), 404 - - ( - record_id, - guid, - hostname_claimed, - fingerprint, - enrollment_code_id, - status, - client_nonce_stored, - server_nonce_b64, - agent_pubkey_der, - created_at, - updated_at, - approved_by, - ) = row - - if client_nonce_stored != client_nonce_b64: - log("server", f"enrollment poll nonce_mismatch ref={approval_reference}", context_hint) - return jsonify({"error": "nonce_mismatch"}), 400 - - try: - server_nonce_bytes = base64.b64decode(server_nonce_b64, validate=True) - except Exception: - log("server", f"enrollment poll invalid_server_nonce ref={approval_reference}", context_hint) - return jsonify({"error": "server_nonce_invalid"}), 400 - - message = server_nonce_bytes + approval_reference.encode("utf-8") + client_nonce_bytes - - try: - public_key = serialization.load_der_public_key(agent_pubkey_der) - except Exception: - log("server", f"enrollment poll pubkey_load_failed ref={approval_reference}", context_hint) - public_key = None - - if public_key is None: - log("server", f"enrollment poll invalid_pubkey ref={approval_reference}", context_hint) - return jsonify({"error": "agent_pubkey_invalid"}), 400 - - try: - public_key.verify(proof_sig, message) - except Exception: - log("server", f"enrollment poll invalid_proof ref={approval_reference}", context_hint) - return jsonify({"error": "invalid_proof"}), 400 - - if status == "pending": - log( - "server", - f"enrollment poll pending ref={approval_reference} host={hostname_claimed}" - f" fingerprint={fingerprint[:12]}", - context_hint, - ) - return jsonify({"status": "pending", "poll_after_ms": 5000}) - if status == "denied": - log( - "server", - f"enrollment poll denied ref={approval_reference} host={hostname_claimed}", - context_hint, - ) - return jsonify({"status": "denied", "reason": "operator_denied"}) - if status == "expired": - log( - "server", - f"enrollment poll expired ref={approval_reference} host={hostname_claimed}", - context_hint, - ) - return jsonify({"status": "expired"}) - if status == "completed": - log( - "server", - f"enrollment poll already_completed ref={approval_reference} host={hostname_claimed}", - context_hint, - ) - return jsonify({"status": "approved", "detail": "finalized"}) - - if status != "approved": - log( - "server", - f"enrollment poll unexpected_status={status} ref={approval_reference}", - context_hint, - ) - return jsonify({"status": status or "unknown"}), 400 - - nonce_key = f"{approval_reference}:{base64.b64encode(proof_sig).decode('ascii')}" - if not nonce_cache.consume(nonce_key): - log( - "server", - f"enrollment poll replay_detected ref={approval_reference} fingerprint={fingerprint[:12]}", - context_hint, - ) - return jsonify({"error": "proof_replayed"}), 409 - - # Finalize enrollment - effective_guid = normalize_guid(guid) if guid else normalize_guid(str(uuid.uuid4())) - now_iso = _iso(_now()) - - device_record = _ensure_device_record(cur, effective_guid, hostname_claimed, fingerprint) - _store_device_key(cur, effective_guid, fingerprint) - - # Mark install code used - if enrollment_code_id: - cur.execute( - "SELECT use_count, max_uses FROM enrollment_install_codes WHERE id = ?", - (enrollment_code_id,), - ) - usage_row = cur.fetchone() - try: - prior_count = int(usage_row[0]) if usage_row else 0 - except Exception: - prior_count = 0 - try: - allowed_uses = int(usage_row[1]) if usage_row else 1 - except Exception: - allowed_uses = 1 - if allowed_uses < 1: - allowed_uses = 1 - new_count = prior_count + 1 - consumed = new_count >= allowed_uses - cur.execute( - """ - UPDATE enrollment_install_codes - SET use_count = ?, - used_by_guid = ?, - last_used_at = ?, - used_at = CASE WHEN ? THEN ? ELSE used_at END - WHERE id = ? - """, - ( - new_count, - effective_guid, - now_iso, - 1 if consumed else 0, - now_iso, - enrollment_code_id, - ), - ) - cur.execute( - """ - UPDATE enrollment_install_codes_persistent - SET last_known_use_count = ?, - used_by_guid = ?, - last_used_at = ?, - used_at = CASE WHEN ? THEN ? ELSE used_at END, - is_active = CASE WHEN ? THEN 0 ELSE is_active END, - consumed_at = CASE WHEN ? THEN COALESCE(consumed_at, ?) ELSE consumed_at END, - archived_at = CASE WHEN ? THEN COALESCE(archived_at, ?) ELSE archived_at END - WHERE id = ? - """, - ( - new_count, - effective_guid, - now_iso, - 1 if consumed else 0, - now_iso, - 1 if consumed else 0, - 1 if consumed else 0, - now_iso, - 1 if consumed else 0, - now_iso, - enrollment_code_id, - ), - ) - - # Update approval record with final state - cur.execute( - """ - UPDATE device_approvals - SET guid = ?, - status = 'completed', - updated_at = ? - WHERE id = ? - """, - (effective_guid, now_iso, record_id), - ) - - refresh_info = _issue_refresh_token(cur, effective_guid) - access_token = jwt_service.issue_access_token( - effective_guid, - fingerprint, - device_record.get("token_version") or 1, - ) - - conn.commit() - finally: - conn.close() - - log( - "server", - f"enrollment finalized guid={effective_guid} fingerprint={fingerprint[:12]} host={hostname_claimed}", - context_hint, - ) - return jsonify( - { - "status": "approved", - "guid": effective_guid, - "access_token": access_token, - "expires_in": 900, - "refresh_token": refresh_info["token"], - "token_type": "Bearer", - "server_certificate": _load_tls_bundle(tls_bundle_path), - "signing_key": _signing_key_b64(), - } - ) - - app.register_blueprint(blueprint) - - -def _load_tls_bundle(path: str) -> str: - try: - with open(path, "r", encoding="utf-8") as fh: - return fh.read() - except Exception: - return "" - - -def _mask_code(code: str) -> str: - if not code: - return "" - trimmed = str(code).strip() - if len(trimmed) <= 6: - return "***" - return f"{trimmed[:3]}***{trimmed[-3:]}" diff --git a/Data/Server/Modules/guid_utils.py b/Data/Server/Modules/guid_utils.py deleted file mode 100644 index 74791253..00000000 --- a/Data/Server/Modules/guid_utils.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -import string -import uuid -from typing import Optional - - -def normalize_guid(value: Optional[str]) -> str: - """ - Canonicalize GUID strings so the server treats different casings/formats uniformly. - """ - candidate = (value or "").strip() - if not candidate: - return "" - candidate = candidate.strip("{}") - try: - return str(uuid.UUID(candidate)).upper() - except Exception: - cleaned = "".join(ch for ch in candidate if ch in string.hexdigits or ch == "-") - cleaned = cleaned.strip("-") - if cleaned: - try: - return str(uuid.UUID(cleaned)).upper() - except Exception: - pass - return candidate.upper() diff --git a/Data/Server/Modules/jobs/__init__.py b/Data/Server/Modules/jobs/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/jobs/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/jobs/prune.py b/Data/Server/Modules/jobs/prune.py deleted file mode 100644 index f86b7245..00000000 --- a/Data/Server/Modules/jobs/prune.py +++ /dev/null @@ -1,110 +0,0 @@ -from __future__ import annotations - -from datetime import datetime, timedelta, timezone -from typing import Callable, List, Optional - -import eventlet -from flask_socketio import SocketIO - - -def start_prune_job( - socketio: SocketIO, - *, - db_conn_factory: Callable[[], any], - log: Callable[[str, str, Optional[str]], None], -) -> None: - def _job_loop(): - while True: - try: - _run_once(db_conn_factory, log) - except Exception as exc: - log("server", f"prune job failure: {exc}") - eventlet.sleep(24 * 60 * 60) - - socketio.start_background_task(_job_loop) - - -def _run_once(db_conn_factory: Callable[[], any], log: Callable[[str, str, Optional[str]], None]) -> None: - now = datetime.now(tz=timezone.utc) - now_iso = now.isoformat() - stale_before = (now - timedelta(hours=24)).isoformat() - conn = db_conn_factory() - try: - cur = conn.cursor() - persistent_table_exists = False - try: - cur.execute( - "SELECT 1 FROM sqlite_master WHERE type='table' AND name='enrollment_install_codes_persistent'" - ) - persistent_table_exists = cur.fetchone() is not None - except Exception: - persistent_table_exists = False - - expired_ids: List[str] = [] - if persistent_table_exists: - cur.execute( - """ - SELECT id - FROM enrollment_install_codes - WHERE use_count = 0 - AND expires_at < ? - """, - (now_iso,), - ) - expired_ids = [str(row[0]) for row in cur.fetchall() if row and row[0]] - cur.execute( - """ - DELETE FROM enrollment_install_codes - WHERE use_count = 0 - AND expires_at < ? - """, - (now_iso,), - ) - codes_pruned = cur.rowcount or 0 - if expired_ids: - placeholders = ",".join("?" for _ in expired_ids) - try: - cur.execute( - f""" - UPDATE enrollment_install_codes_persistent - SET is_active = 0, - archived_at = COALESCE(archived_at, ?) - WHERE id IN ({placeholders}) - """, - (now_iso, *expired_ids), - ) - except Exception: - # Best-effort archival; continue if the persistence table is absent. - pass - - cur.execute( - """ - UPDATE device_approvals - SET status = 'expired', - updated_at = ? - WHERE status = 'pending' - AND ( - EXISTS ( - SELECT 1 - FROM enrollment_install_codes c - WHERE c.id = device_approvals.enrollment_code_id - AND ( - c.expires_at < ? - OR c.use_count >= c.max_uses - ) - ) - OR created_at < ? - ) - """, - (now_iso, now_iso, stale_before), - ) - approvals_marked = cur.rowcount or 0 - - conn.commit() - finally: - conn.close() - - if codes_pruned: - log("server", f"prune job removed {codes_pruned} expired enrollment codes") - if approvals_marked: - log("server", f"prune job expired {approvals_marked} device approvals") diff --git a/Data/Server/Modules/runtime.py b/Data/Server/Modules/runtime.py deleted file mode 100644 index 40a841ff..00000000 --- a/Data/Server/Modules/runtime.py +++ /dev/null @@ -1,168 +0,0 @@ -"""Utility helpers for locating runtime storage paths. - -The Borealis repository keeps the authoritative source code under ``Data/`` -so that the bootstrap scripts can copy those assets into sibling ``Server/`` -and ``Agent/`` directories for execution. Runtime artefacts such as TLS -certificates or signing keys must therefore live outside ``Data`` to avoid -polluting the template tree. This module centralises the path selection so -other modules can rely on a consistent location regardless of whether they -are executed from the copied runtime directory or directly from ``Data`` -during development. -""" - -from __future__ import annotations - -import os -from functools import lru_cache -from pathlib import Path -from typing import Optional - - -def _env_path(name: str) -> Optional[Path]: - """Return a resolved ``Path`` for the given environment variable.""" - - value = os.environ.get(name) - if not value: - return None - try: - return Path(value).expanduser().resolve() - except Exception: - return None - - -@lru_cache(maxsize=None) -def project_root() -> Path: - """Best-effort detection of the repository root.""" - - env = _env_path("BOREALIS_PROJECT_ROOT") - if env: - return env - - current = Path(__file__).resolve() - for parent in current.parents: - if (parent / "Borealis.ps1").exists() or (parent / ".git").is_dir(): - return parent - - # Fallback to the ancestor that corresponds to ``/`` when the module - # lives under ``Data/Server/Modules``. - try: - return current.parents[4] - except IndexError: - return current.parent - - -@lru_cache(maxsize=None) -def server_runtime_root() -> Path: - """Location where the running server stores mutable artefacts.""" - - env = _env_path("BOREALIS_SERVER_ROOT") - if env: - return env - - root = project_root() - runtime = root / "Server" / "Borealis" - return runtime - - -def runtime_path(*parts: str) -> Path: - """Return a path relative to the server runtime root.""" - - return server_runtime_root().joinpath(*parts) - - -def ensure_runtime_dir(*parts: str) -> Path: - """Create (if required) and return a runtime directory.""" - - path = runtime_path(*parts) - path.mkdir(parents=True, exist_ok=True) - return path - - -@lru_cache(maxsize=None) -def certificates_root() -> Path: - """Base directory for persisted certificate material.""" - - env = _env_path("BOREALIS_CERTIFICATES_ROOT") or _env_path("BOREALIS_CERT_ROOT") - if env: - env.mkdir(parents=True, exist_ok=True) - return env - - root = project_root() / "Certificates" - root.mkdir(parents=True, exist_ok=True) - # Ensure expected subdirectories exist for agent and server material. - try: - (root / "Server").mkdir(parents=True, exist_ok=True) - (root / "Agent").mkdir(parents=True, exist_ok=True) - except Exception: - pass - return root - - -@lru_cache(maxsize=None) -def server_certificates_root() -> Path: - """Base directory for server certificate material.""" - - env = _env_path("BOREALIS_SERVER_CERT_ROOT") - if env: - env.mkdir(parents=True, exist_ok=True) - return env - - root = certificates_root() / "Server" - root.mkdir(parents=True, exist_ok=True) - return root - - -@lru_cache(maxsize=None) -def agent_certificates_root() -> Path: - """Base directory for agent certificate material.""" - - env = _env_path("BOREALIS_AGENT_CERT_ROOT") - if env: - env.mkdir(parents=True, exist_ok=True) - return env - - root = certificates_root() / "Agent" - root.mkdir(parents=True, exist_ok=True) - return root - - -def certificates_path(*parts: str) -> Path: - """Return a path under the certificates root.""" - - return certificates_root().joinpath(*parts) - - -def ensure_certificates_dir(*parts: str) -> Path: - """Create (if required) and return a certificates subdirectory.""" - - path = certificates_path(*parts) - path.mkdir(parents=True, exist_ok=True) - return path - - -def server_certificates_path(*parts: str) -> Path: - """Return a path under the server certificates root.""" - - return server_certificates_root().joinpath(*parts) - - -def ensure_server_certificates_dir(*parts: str) -> Path: - """Create (if required) and return a server certificates subdirectory.""" - - path = server_certificates_path(*parts) - path.mkdir(parents=True, exist_ok=True) - return path - - -def agent_certificates_path(*parts: str) -> Path: - """Return a path under the agent certificates root.""" - - return agent_certificates_root().joinpath(*parts) - - -def ensure_agent_certificates_dir(*parts: str) -> Path: - """Create (if required) and return an agent certificates subdirectory.""" - - path = agent_certificates_path(*parts) - path.mkdir(parents=True, exist_ok=True) - return path diff --git a/Data/Server/Modules/tokens/__init__.py b/Data/Server/Modules/tokens/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/Data/Server/Modules/tokens/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Data/Server/Modules/tokens/routes.py b/Data/Server/Modules/tokens/routes.py deleted file mode 100644 index 80058363..00000000 --- a/Data/Server/Modules/tokens/routes.py +++ /dev/null @@ -1,138 +0,0 @@ - -from __future__ import annotations - -import hashlib -import sqlite3 -from datetime import datetime, timezone -from typing import Callable - -from flask import Blueprint, jsonify, request - -from Modules.auth.dpop import DPoPValidator, DPoPVerificationError, DPoPReplayError - - -def register( - app, - *, - db_conn_factory: Callable[[], sqlite3.Connection], - jwt_service, - dpop_validator: DPoPValidator, -) -> None: - blueprint = Blueprint("tokens", __name__) - - def _hash_token(token: str) -> str: - return hashlib.sha256(token.encode("utf-8")).hexdigest() - - def _iso_now() -> str: - return datetime.now(tz=timezone.utc).isoformat() - - def _parse_iso(ts: str) -> datetime: - return datetime.fromisoformat(ts) - - @blueprint.route("/api/agent/token/refresh", methods=["POST"]) - def refresh(): - payload = request.get_json(force=True, silent=True) or {} - guid = str(payload.get("guid") or "").strip() - refresh_token = str(payload.get("refresh_token") or "").strip() - - if not guid or not refresh_token: - return jsonify({"error": "invalid_request"}), 400 - - conn = db_conn_factory() - try: - cur = conn.cursor() - cur.execute( - """ - SELECT id, guid, token_hash, dpop_jkt, created_at, expires_at, revoked_at - FROM refresh_tokens - WHERE guid = ? - AND token_hash = ? - """, - (guid, _hash_token(refresh_token)), - ) - row = cur.fetchone() - if not row: - return jsonify({"error": "invalid_refresh_token"}), 401 - - record_id, row_guid, _token_hash, stored_jkt, created_at, expires_at, revoked_at = row - if row_guid != guid: - return jsonify({"error": "invalid_refresh_token"}), 401 - if revoked_at: - return jsonify({"error": "refresh_token_revoked"}), 401 - if expires_at: - try: - if _parse_iso(expires_at) <= datetime.now(tz=timezone.utc): - return jsonify({"error": "refresh_token_expired"}), 401 - except Exception: - pass - - cur.execute( - """ - SELECT guid, ssl_key_fingerprint, token_version, status - FROM devices - WHERE guid = ? - """, - (guid,), - ) - device_row = cur.fetchone() - if not device_row: - return jsonify({"error": "device_not_found"}), 404 - - device_guid, fingerprint, token_version, status = device_row - status_norm = (status or "active").strip().lower() - if status_norm in {"revoked", "decommissioned"}: - return jsonify({"error": "device_revoked"}), 403 - - dpop_proof = request.headers.get("DPoP") - jkt = stored_jkt or "" - if dpop_proof: - try: - jkt = dpop_validator.verify(request.method, request.url, dpop_proof, access_token=None) - except DPoPReplayError: - return jsonify({"error": "dpop_replayed"}), 400 - except DPoPVerificationError: - return jsonify({"error": "dpop_invalid"}), 400 - elif stored_jkt: - # The agent does not yet emit DPoP proofs; allow recovery by clearing - # the stored binding so refreshes can succeed. This preserves - # backward compatibility while the client gains full DPoP support. - try: - app.logger.warning( - "Clearing stored DPoP binding for guid=%s due to missing proof", - guid, - ) - except Exception: - pass - cur.execute( - "UPDATE refresh_tokens SET dpop_jkt = NULL WHERE id = ?", - (record_id,), - ) - - new_access_token = jwt_service.issue_access_token( - guid, - fingerprint or "", - token_version or 1, - ) - - cur.execute( - """ - UPDATE refresh_tokens - SET last_used_at = ?, - dpop_jkt = COALESCE(NULLIF(?, ''), dpop_jkt) - WHERE id = ? - """, - (_iso_now(), jkt, record_id), - ) - conn.commit() - finally: - conn.close() - - return jsonify( - { - "access_token": new_access_token, - "expires_in": 900, - "token_type": "Bearer", - } - ) - - app.register_blueprint(blueprint) diff --git a/Data/Server/Package-Borealis-Server.ps1 b/Data/Server/Package-Borealis-Server.ps1 deleted file mode 100644 index 26b05a60..00000000 --- a/Data/Server/Package-Borealis-Server.ps1 +++ /dev/null @@ -1,88 +0,0 @@ -#////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/Server/Package-Borealis-Server.ps1 - -# ------------- Configuration ------------- -# (all paths are made absolute via Join-Path and $scriptDir) -$scriptDir = Split-Path $MyInvocation.MyCommand.Definition -Parent -$projectRoot = Resolve-Path (Join-Path $scriptDir "..\..") # go up two levels to \Borealis -$packagingDir = Join-Path $scriptDir "Packaging_Server" -$venvDir = Join-Path $packagingDir "Pyinstaller_Virtual_Environment" -$distDir = Join-Path $packagingDir "dist" -$buildDir = Join-Path $packagingDir "build" -$specPath = $packagingDir - -$serverScript = Join-Path $scriptDir "server.py" -$outputName = "Borealis-Server" -$finalExeName = "$outputName.exe" -$requirementsPath = Join-Path $scriptDir "server-requirements.txt" -$iconPath = Join-Path $scriptDir "Borealis.ico" - -# Static assets to bundle: -# - the compiled React build under Server/web-interface/build -$staticBuildSrc = Join-Path $projectRoot "Server\web-interface\build" -$staticBuildDst = "web-interface/build" -# - Tesseract-OCR folder must be nested under 'Borealis/Python_API_Endpoints/Tesseract-OCR' -$ocrSrc = Join-Path $scriptDir "Python_API_Endpoints\Tesseract-OCR" -$ocrDst = "Borealis/Python_API_Endpoints/Tesseract-OCR" -$soundsSrc = Join-Path $scriptDir "Sounds" -$soundsDst = "Sounds" - -# Embedded Python shipped under Dependencies\Python\python.exe -$embeddedPython = Join-Path $projectRoot "Dependencies\Python\python.exe" - -# ------------- Prepare packaging folder ------------- -if (-Not (Test-Path $packagingDir)) { - New-Item -ItemType Directory -Path $packagingDir | Out-Null -} - -# 1) Create or upgrade virtual environment -if (-Not (Test-Path (Join-Path $venvDir "Scripts\python.exe"))) { - Write-Host "[SETUP] Creating virtual environment at $venvDir" - & $embeddedPython -m venv --upgrade-deps $venvDir -} - -# helper to invoke venv's python -$venvPy = Join-Path $venvDir "Scripts\python.exe" - -# 2) Bootstrap & upgrade pip -Write-Host "[INFO] Bootstrapping pip" -& $venvPy -m ensurepip --upgrade -& $venvPy -m pip install --upgrade pip - -# 3) Install server dependencies -Write-Host "[INFO] Installing server dependencies" -& $venvPy -m pip install -r $requirementsPath -# Ensure dnspython is available for Eventlet's greendns support -& $venvPy -m pip install dnspython - -# 4) Install PyInstaller -Write-Host "[INFO] Installing PyInstaller" -& $venvPy -m pip install pyinstaller - -# 5) Clean previous artifacts -Write-Host "[INFO] Cleaning previous artifacts" -Remove-Item -Recurse -Force $distDir, $buildDir, "$specPath\$outputName.spec" -ErrorAction SilentlyContinue - -# 6) Run PyInstaller, bundling server code and assets -# Collect all Eventlet and DNS submodules to avoid missing dynamic imports -Write-Host "[INFO] Running PyInstaller" -& $venvPy -m PyInstaller ` - --onefile ` - --name $outputName ` - --icon $iconPath ` - --collect-submodules eventlet ` - --collect-submodules dns ` - --distpath $distDir ` - --workpath $buildDir ` - --specpath $specPath ` - --add-data "$staticBuildSrc;$staticBuildDst" ` - --add-data "$ocrSrc;$ocrDst" ` - --add-data "$soundsSrc;$soundsDst" ` - $serverScript - -# 7) Copy the final EXE back to Data/Server -if (Test-Path (Join-Path $distDir $finalExeName)) { - Copy-Item (Join-Path $distDir $finalExeName) (Join-Path $scriptDir $finalExeName) -Force - Write-Host "[SUCCESS] Server packaged at $finalExeName" -} else { - Write-Host "[FAILURE] Packaging failed." -ForegroundColor Red -} diff --git a/Data/Server/Python_API_Endpoints/ocr_engines.py b/Data/Server/Python_API_Endpoints/ocr_engines.py deleted file mode 100644 index 22e9c6e2..00000000 --- a/Data/Server/Python_API_Endpoints/ocr_engines.py +++ /dev/null @@ -1,104 +0,0 @@ -#////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/Python_API_Endpoints/ocr_engines.py - -import os -import io -import sys -import base64 -import torch -import pytesseract -import easyocr -import numpy as np -import platform -from PIL import Image - -# --------------------------------------------------------------------- -# Configure cross-platform Tesseract path -# --------------------------------------------------------------------- -SYSTEM = platform.system() - -def get_tesseract_folder(): - if getattr(sys, 'frozen', False): - # PyInstaller EXE - base_path = sys._MEIPASS - return os.path.join(base_path, "Borealis", "Python_API_Endpoints", "Tesseract-OCR") - else: - # Normal Python environment - base_dir = os.path.dirname(os.path.abspath(__file__)) - return os.path.join(base_dir, "Tesseract-OCR") - -if SYSTEM == "Windows": - TESSERACT_FOLDER = get_tesseract_folder() - TESSERACT_EXE = os.path.join(TESSERACT_FOLDER, "tesseract.exe") - TESSDATA_DIR = os.path.join(TESSERACT_FOLDER, "tessdata") - - if not os.path.isfile(TESSERACT_EXE): - raise EnvironmentError(f"Missing tesseract.exe at expected path: {TESSERACT_EXE}") - - pytesseract.pytesseract.tesseract_cmd = TESSERACT_EXE - os.environ["TESSDATA_PREFIX"] = TESSDATA_DIR -else: - # Assume Linux/macOS with system-installed Tesseract - pytesseract.pytesseract.tesseract_cmd = "tesseract" - -# --------------------------------------------------------------------- -# EasyOCR Global Instances -# --------------------------------------------------------------------- -easyocr_reader_cpu = None -easyocr_reader_gpu = None - -def initialize_ocr_engines(): - global easyocr_reader_cpu, easyocr_reader_gpu - if easyocr_reader_cpu is None: - easyocr_reader_cpu = easyocr.Reader(['en'], gpu=False) - if easyocr_reader_gpu is None: - easyocr_reader_gpu = easyocr.Reader(['en'], gpu=torch.cuda.is_available()) - -# --------------------------------------------------------------------- -# Main OCR Handler -# --------------------------------------------------------------------- -def run_ocr_on_base64(image_b64: str, engine: str = "tesseract", backend: str = "cpu") -> list[str]: - if not image_b64: - raise ValueError("No base64 image data provided.") - - try: - raw_bytes = base64.b64decode(image_b64) - image = Image.open(io.BytesIO(raw_bytes)).convert("RGB") - except Exception as e: - raise ValueError(f"Invalid base64 image input: {e}") - - engine = engine.lower().strip() - backend = backend.lower().strip() - - if engine in ["tesseract", "tesseractocr"]: - try: - text = pytesseract.image_to_string(image, config="--psm 6 --oem 1") - except pytesseract.TesseractNotFoundError: - raise RuntimeError("Tesseract binary not found or not available on this platform.") - elif engine == "easyocr": - initialize_ocr_engines() - reader = easyocr_reader_gpu if backend == "gpu" else easyocr_reader_cpu - result = reader.readtext(np.array(image), detail=1) - - # Group by Y position (line-aware sorting) - result = sorted(result, key=lambda r: r[0][0][1]) - lines = [] - current_line = [] - last_y = None - line_threshold = 10 - - for (bbox, text, _) in result: - y = bbox[0][1] - if last_y is None or abs(y - last_y) < line_threshold: - current_line.append(text) - else: - lines.append(" ".join(current_line)) - current_line = [text] - last_y = y - - if current_line: - lines.append(" ".join(current_line)) - text = "\n".join(lines) - else: - raise ValueError(f"OCR engine '{engine}' not recognized.") - - return [line.strip() for line in text.splitlines() if line.strip()] diff --git a/Data/Server/Python_API_Endpoints/script_engines.py b/Data/Server/Python_API_Endpoints/script_engines.py deleted file mode 100644 index 7c789b19..00000000 --- a/Data/Server/Python_API_Endpoints/script_engines.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import subprocess -import sys -import platform - - -def run_powershell_script(script_path: str): - """ - Execute a PowerShell script with ExecutionPolicy Bypass. - - Returns (returncode, stdout, stderr) - """ - if not script_path or not os.path.isfile(script_path): - raise FileNotFoundError(f"Script not found: {script_path}") - - if not script_path.lower().endswith(".ps1"): - raise ValueError("run_powershell_script only accepts .ps1 files") - - system = platform.system() - - # Choose powershell binary - ps_bin = None - if system == "Windows": - # Prefer Windows PowerShell - ps_bin = os.path.expandvars(r"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe") - if not os.path.isfile(ps_bin): - ps_bin = "powershell.exe" - else: - # PowerShell Core (pwsh) may exist cross-platform - ps_bin = "pwsh" - - # Build command - # -ExecutionPolicy Bypass (Windows only), -NoProfile, -File "script" - cmd = [ps_bin] - if system == "Windows": - cmd += ["-ExecutionPolicy", "Bypass"] - cmd += ["-NoProfile", "-File", script_path] - - # Hide window on Windows - creationflags = 0 - startupinfo = None - if system == "Windows": - creationflags = 0x08000000 # CREATE_NO_WINDOW - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - - proc = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - creationflags=creationflags, - startupinfo=startupinfo, - ) - out, err = proc.communicate() - return proc.returncode, out or "", err or "" - diff --git a/Data/Server/Sounds/Short_Beep.wav b/Data/Server/Sounds/Short_Beep.wav deleted file mode 100644 index 015e1f642e91b72578bca1c318f7a7c0d515d843..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9702 zcmZ{qRdZa~l7%yK?<14Uj54!jCYhO;@7s--n74VGA2EOAwn=7cQ@hP9vn?~KZJ8OW z&d!{*PnBf%otSW_qivU^QYmxgx7MoV^JdR}(1&QvwAHiz^S}PPdkZ3B{F+%w^gnBv zgj&#l{PVy6c>s?gvQp`+{bj7q*&#k})%@H( zi_;cTrh{cdnCsW1i<4>j5z>d+QzOdL8+#yE^ce54-*^`v4OQ-bc&b2LjDP4$xvy1xly=!~vPX~66@Ex> z@Le{tcHGBC$TVHdYiOQUxFMmNYpFtC=sDlEb8=X>(q`UIr|AYgr4OdkR6EH49V4@J zIbP{mHU_`zgg-U@NYCgdouLC%Nn7a0KX3ALe~JWMCa!m!RQ)JZ&ydRlAz`FE@3dsJmdWT&p@ zy?jEi$Rm9tsWy@h*2hM=>FE-`I?Uk;9&BAPB*LGyMymBJAHwsh#Pd6Y=lP62Vr-gG z7aB<8Xf~~+*Mo7!0e$>}S8K$rEX-<|#oA4lC~yHBqvM-8bR9y5Zb(qdZ6^TULsJYVKp1;=0c zh27>0b{Jn{6aQ`}`8qwZccP(*>lFIAF*<{m%gQjzjSB;$i!~Jr9|W(np7sawoBh^s z(47mn{9pNx3Fn(7UDJVXyw2uTw2WrZXzR-zsR;$jriM=1Ud-lpIieT%uD#%I=D9WX zuyUS63v8_{!dwrhUeuNv&<}cL_xKVOrzRY8SM&kBk$hvM45l(} zX(C_ruQ)Y)n6_cp_G9OYQ^QRyn;M>Fm>QeO*iPO$yaXQevSxX;Smg1Xrt{$)G0kMEFgRFdZ2B#+W7?nKz*HiRAS&v4P* z3$^my5^iHXc$iJ%g|-eKzig-0;xQNT*d6#cd*p;&p-24I^4!?kQ(qos(|D<^qPbEL z2DxsbxfA`wf7vZQM~7?+SK5BYcG^?=$k}$<03K(vWTh=DY$tcJCY-&_c8yQjzQT55 zgKpDvsFq_)jSSHWnP;nQv745T@O?r%Zp3+eO%Le`9j86K-gaWHFVTH^g*k74dGAHT zX$mc(b-W-AF`=+Esci??Ng~w1exHj!I_amL{uowvBUx zFcZy<_>(>3TbPxDyu~W<-?MsCYM}JY&Gm0n!=tI6b--sz3z|+Rsb15mxSiCRv7LnN zq;+_9Q}8V7w-e9)5+Bu_ZhhFz$FUFCPJEXQt)1%=py{@lpy@op26LIVa4!9vJWs3T zJRP>JvRQwZ(|Vnu>15QDI^$Z74Kv*esQN4(i%Z&BOT{d^o#9lnKes8_77o#Qxov;N z+ZkH8G8;@2XdbPx#ke3N7@7_ZPW25xqN{X*_R0M`$t_IR z`GsMU46`28hD-DtH2*GF@t?XwHrO6J?yiQ1f~ld#)Is{DBmMNSL{{4zs-VH#O`8j* zMldyY7L&ZiRi^v>nQ%j%*#|S%EOeHEHul%4iJHzD-X%|R*Gf+1_9dHin;n)5dYfM` zwzE#t-F$_nhbewUzE{#NG@zWk4)+~2op;;6@Ntx(>HONV?R33$I8Wup_&4))f|P5S zv{EP7PPwD!1=}f`bf28EYq%xvaZ^gE6Za3J{mgWkUztMFbpUtaX2JZ&^r?fU%K_cY zxSjaEs;$O8F(Ajfy20sqH#e+uOLdxz((HD|*BN*33N~yHZQz~Zh_4EFq*lLjLeO*? zY7@DBYOJSgo0j+=0VH5mbd+}~NWd}@(ANwSp!STZNv8c++;Gj0xd}8~%B%&sNCI@5 z&NE1WH&aFe{4~7x7D~A@XCz?D3&h0(tP^I*FjF9|aLVsbH@R(!sbOrVeP)NxSjI!F zg0Y={*K~Ok9^k@2(`h|85tl&I z`6Pk3ZgE)SKmx8jU~0(Q7kPncya3WDPK{pU;?x*4T}R7IT_!7a zmSAe6i-H6cB%n9sjP938EowSQC)0Fn$snoFdg7wK)B)QB;({Juwd1^3L0sX8s|p}4 z194ev>Pf?RvMr#0&_bFFeeYqdrG7iH$vbVm?A7CZl^)^qqy$ZuzFH)%bdHCnOE+sC ziOXuJn$PJ$w>4~*19qCB>HJZ%?eqilWBu%~LO@(P+V#iHDz!lG`N>aBkGGTW*mL?y zo?BTNmD5C;&$yj3RfkJ&Ys(FBBVS`1@jA!tq}_5Xa=N~xAJQPSalOJYHw8ZzkO25* zsCDPo;GJ(&t9RVR@CSaD8)zRx(-|bd!5ZU+_pKuVn#1FDpz9i%G0Ug)S#m3X#(~of zB%o)Y@Xz>DLDLJIPOEGw2p^hG;B*)CEkCAfdcy6)SJ@#)(hJGm^o4w*#ImW0#6{~2 zQ)9!Vm$tz({_b9Bk+^8RLDLDEE^jo)*iPz0ATC-`&~$~S>8{aD(mQ2Yn^y|WWnQTKj<8=E5&A(?C`A^#cecFqO zyJ`>VEvMFqI&l5eggH`SgQy#|;G(9(Hyq-vwwW;Gvuy=0w;4Rf z`V~lk-rHlo4sHgg*AbUKXH1Q?l<=JMGqL7F4@%3G5r61_c}n+ zF;%?{noiJkKR<=0Q<1prIp5(6dYHHKrm)YSO0PL^dO_2zpMbbzxvjv>0jK*e0o#dN z^i-hf?m)UZ*_uD-!RfeJ&~$6gATFxlxwZ;4G>t}qjBq<$8ldTR#g5BfTTeT24=>6+ zt>y2yk*%et4b@4>!aQzgI?2`FPR8pTF8V)H5SQ+e<9b;h)@eFr?k+5bznv=+Xoz(a zObvgrBBxuX=><-=_kyV@xac|(ps@sZ=b8p}AM^>`h@1{~*h+`xyjBaQhQaAnhR^(O znr;m#A32?_$nmfnjIz`H;-VRvPQw|*rEB3=;i9RBLes-nfuwBMesx5JNO~44PS9pu$?sY--*jg+>i97zaKIejSIC09GPur$hk(L9dd(_a41V` zH8g6X4dL$AlA-DJ0vdK6TDFZhQReP2t?xl1aCh2|$MFBrbo)Thtf=X>#o+EzxVwO< z;TGCe2J3hM3HYVyG&d^rf#ZdXwl`7JwdkS?n(pADr6ja=J$0x|)&=s9u)twE!R>9R z#4_TdD#?foQv(+bYRfd8;G#hp;B*3U;f6rde-c+=I~AG^TDhrD1)9$FF4~~!VFp}l zKadVIowMzPKlxoZa^|A>4!yvw^V*WTQ@Kq5Ib|+dMuc9zUE09sQuraegssfBlaJ{o z_aMB&)@HX8kIQbS%(n?#Ze_R)j^Lv8w!?N3wli{iB!OSE7yRs(L7`{!cpJ!F4cUu+ z+?ml1#k-b30$0Btpx@hoMC(ekApXznNcJgLig2R!x>@WJl^%+;xbX<>V zxFUbcxZvxd>6{Ul-gB?QcWGd4s25mfGE$_q0^-tgml0QCJCQj3!ML4nU%++-5ZBL) zi)J9LS_M`eFUVe`MPT_%nY*)tQPbr>I1SFNCjmE5$I@(uyW^QQn)_QvDn<4I7fsjW z)P#+0dpPVagxm6hzM5mWXb=;o#@6s+L-rDh3zGp&=LdR8ju~7uK91QXWHI1$&NQ7y z;IYv3pTvdF^)+g`ou@x|yKcnS%3QR(2Z@y8wqQFA*$b`Y**ew@gr{hxhG_?4rw1hDxKz+hE+0G$MfKLJCU)0xVSoQC)_>U9V4+bkbwRzv6B_ZN1^FR zV~U!Ngr=zJV2E>8?VhI~0c(Lvk!3GowObNE0=OjY2rgP*yN7z2j>lw_kc?`Tz(pH4 zy)ZR4nHC91z)cF^bZbQkQYN_Qy6goBRFSwOgvPETa?R2Dx9o+R6OvJ`v74N6x>mY_ z>6zqK?pg9F5I2YFV>@|HSm}|B@@VcCI`}4O>ffc0{IxVAE&->zi}^bxwYje)N!kj} zq}+1A#3jK9;Xy6ptl>?veijARtd1LCSnMtNSS@I%rp8I2iN9dU8yqLE%b zg-^DaKpTh)=@zC2iQ+iRI2}0#lF>R%FK{}3QuWEGfz!hrw_2BQmW+aq8Y3?Or|U1- zi(Ujx)e=a6>a&-~=>`|AJ>aYpPy$@EtI8iqc7}}8&e%w&%W}jOj>USJPa> zcH)+_K=y;}L`qwjno7;G7kU;s9n;oE}{?@{gI6N;b;ZWTfliB8I68O zvy7`Q83hX!GcJ0E%(w}6)V|0z>s>UQPIf!tm(g3;8L(E7xERT39dYSE8ehlhF;ACH zpdDzsdjdyW%+u{KoGwTpy1P0~M=ri5S)7}afCRW5bo&RgbL8of1Qgi|9}Aa~2f0^1 z2Zx>I=~2^h2QW2MF5T0XiO+p0d65>U#x}-0-LACWGlFxE~XV|VXU`|#OGh4NT8|6D9lJqkiEnfCf%BB zNe?(=i7|mTi+Oq^0h|^5zhNUZ^5obK$2A8 zN2Cu^9BwDlhz68Kt)c8f!QbzOFM=Q9*jveO_mbUibBRk&G4LYp<)Xohr$-ORS)txE+o(yKNfOckiZg}j+D4h zmR6vJVN8u6PnS)$1D#LS55Ze{k(T3TB1wt~G`o4Y0h_BIO!qhnMBiFf(Vs8<*lbPV|;)RMWOl%c7OQ7{59BC>2 zOzf6LvW27Sh};7?Z{$aJI>6mgc01EM9)3uQiCr;IFRCCse}gpVk=VtK6pdnbJEfRF zYl+J`(oAbsBk8N)A;{#>4@DB7x;%X|SoL(_cH-+7vlmSAvasCE3S$^)1)eYRbbSUD zhr82CL38~veSooKzVL=mu#11n#7p^PIxVX>{!QIKD z01~i0NbInkbQyioE0@EpBTq*!io}lB(l369vyQZoy`YEMhn@skqP=5R1`5AX_BRpVJy&onkv_YoQ-1G74H4?t;W|oX+BvTqIkh@{L3A@6*Q)oKC;vt7NY; z_~T{xCeU>1j-Pskn+JYGs~CF=c>a>u!Z7$zj@xcp4~EPVX#80oe}_zf(cPh0$JE3w zI_>6T7yXTq*cF;}LIPcw8jWd1WBGZZ&|AdLpF2}iGx-!SHH8a>i+jl7cB0Y7)KGKj z8nTX*R!LFQU3+wzA-qK+con^UY+<13u^-|{wn*%(*sP~(eYTyk^T(fm$KNBdgZ}SI zHzfP=C;T;iMDLI#f*-ZFADN%(q3JY7kR=v;XZY-&=c|+R=^-TJU=+(3MLvOE(8ACF zBrda%rf2PK%o5Swq9fHO1x|OH)9o&6Z>g5PnYfnuZY4u<6&@XFY;Oy`6Fq1?eeJXE zPWSKz+D*rBM>9W!JU2^{z>U!KLT_PZ*3$X(TkWePAkl5Ks zo933LXcco~OVBEMObxQ;8dn{%snIR|Q2wm1)*4V{F|mu?UDR}$9!9#ppwPyoV7MX) z;6fMsA$0oR3!0AX1c{xk!Luv2ig?DzdG$W75)!+x-tSG1`>W|g_cr7m_z`=OxjR~| zNTBnBO1kBmCz5_~;KxFq&h^_V=={;sX4y;Kc53E3kyd~okx^%f9Wv|f=;SZN1UhOu zqlLk{3C5$hTpbpnksa=OhqlNI>p4B$<2UFoLwlRMU-CNt!y|i%u7f9`hh9euxagt< zQ-jv=ew-Tkr>u)kulmOU2{dP#&L9E5BxEE&ATEl2=rep-wJ%Oh?4k{=V)*DOE%luP zy1V$kmpT%N?-b#zg@Fe=mA&r;WE(DcB+%{~^1mWKhPmkPq3Jq|dI|cW1XB~;9b-Fr zgY5=KUdHcZKLmHj=tz-lEP}tscA~}U7CZl#r^gm1W&%{N>9JLenvMw`%`Zq^qCr-v?nPzvJG6}*JeDoXwBEWGapiTx&Yo>`U{lm57! z0^*9Vv#gCte<=4zDquv~Wx5m;Iod!1X%kQ313!yXgG8(nzB6kT(dB%J-5tC+ z-uD8>wG!JoMY6O4^a|n%4>7|?>~sSfuWUQ}@ma%;-qL>B||!Wh3Dakyga)rNHS~l7s}>E*9EbIwX9z&+Cc1>DRU Ay#N3J diff --git a/Data/Server/WebUI/index.html b/Data/Server/WebUI/index.html deleted file mode 100644 index 35c37add..00000000 --- a/Data/Server/WebUI/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - Borealis - - - -

- - - - - \ No newline at end of file diff --git a/Data/Server/WebUI/package.json b/Data/Server/WebUI/package.json deleted file mode 100644 index 49cffbb9..00000000 --- a/Data/Server/WebUI/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "borealis-webui", - "version": "1.0.0", - "private": true, - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "@emotion/react": "11.14.0", - "@emotion/styled": "11.14.0", - "@fortawesome/fontawesome-free": "7.1.0", - "@fontsource/ibm-plex-sans": "5.0.17", - "@mui/icons-material": "7.0.2", - "@mui/material": "7.0.2", - "@mui/x-date-pickers": "8.11.3", - "@mui/x-tree-view": "8.10.0", - "ag-grid-community": "34.2.0", - "ag-grid-react": "34.2.0", - "dayjs": "1.11.18", - "normalize.css": "8.0.1", - "prismjs": "1.30.0", - "react-simple-code-editor": "0.13.1", - "react": "19.1.0", - "react-color": "2.19.3", - "react-dom": "19.1.0", - "react-resizable": "3.0.5", - "react-markdown": "8.0.6", - "reactflow": "11.11.4", - "react-simple-keyboard": "3.8.62", - "socket.io-client": "4.8.1" - }, - "devDependencies": { - "@vitejs/plugin-react": "^4.0.0", - "vite": "^5.0.0" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/Data/Server/WebUI/public/Borealis_Logo.png b/Data/Server/WebUI/public/Borealis_Logo.png deleted file mode 100644 index bf68420f6859c51a5658d14668f452364c46c9f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334968 zcmb@t1yo$kvM)Y31c%@@5G1${Fj%m_;1E2JU^Bx6mqCIC2oN-Aa0u@19^BpC2~HqL zAc4ns&Ueo{@4fr(|E{f;ZNgOZ^$0Dw#S*M$N|PNf6@Fb5Gj1}+9_s$dIyTOKn@dvhp{yRE|` zH2@$X?e1V^VGVTwnnU3TJ4vR~=5{6^!cvk+Us#P#%|Q-og;09$1by{hUB}|RwS|}^ zle832!X5m`z!vIa26VTzv2zByOEUd~FZi+iS2Zsa@E;^D){;yze+dK{sA&S_?46)M zVIB~-1;3CmP(+M}A0#LwCdLI6;NutNls*5O}l?VSHM)1$$7-OU_$`FZ&M>e4?5EiL{< z=YVvw`G;^z3tp%V)D~*z;`~U<|1VkxD|;7vXDj>v3+sP9{$B(u$&Xr%*EbG$KKxNKL$$k zKU4EfFw}|C3Y%%r6WU68m3BA5mjz=3@5$No;8WhS@vWnmzUzVQU75@;cbTnSlSY zBv{Vg#@^{s@S}Eu|6UJ~lhbsvhaqepA2@3($^sz@av**&5QtlVhyNeys;Pk?cFrzl zb{0^Gyd=}3eRvQEOEAA6)KcgXu-syz<^tT7mX9TKs3qSchy_3(b1^|nQ4x{vf3^STgs`-?c;xXv)f41{n!)%*VBEqW5g~3d3nAF!*CRKmIiH}Ixv06BC{*Y_ zxM?^c9_OT)&A)T~ODoGqj$&p)<^pEI=G>P2Lj2s87D58tV*DbHx57fAA|P{fC?B8Y zU&#Lln7~R1=f`38_3@Nee@b_@hq<_!IYDLMkGA{o0Fn3qroXe9>wm4iun-gk;}aI;HWz^katlC( zM7hO4<{)knn4qYTu(=?=u+ZaZ{!^|0Q~O8Y3j7;z{~xvgo4tjVnH?PZxYY78{lE9; zzlYBMqCfvW5&wVf5AR=#>_4H#`@f^`AJ6`6L4F+0e^foL@_!ZoQSqkA2Y;}=Uzc?WLRAv8MU+`-q7`gfN1wDSS+9IEzo2u&P<^eu;i7Pb6qTixa?#Y&zB^L|`)3HmCh~V#E!qS&fLo)enu!r9z)RafN3s>lN7yZ7+YhUXYn*HqSgtP0=^@ z#-(ONx)mkm=QHY#7V4H6A|aG@1yc*V;NJV3KImeLNXeV6^=Pdzdi@|tPo3t3Lj8H{ z!dT~J^`^IQgbB4`NRlGRWfk&l;W?5=6uG7!AHIaCfGI;KaD0_gQ`7mmPJF7=J{m*X z27P*g%6@YwJ9{emtk;Byuu0Y88Gl4u+t(4nsn0+w?fP{CYQ=~1p9t-_s@*mLsD?6~ zv*+Tj6S>HPM-DLvk-y%M0g_6p!v;{!0%YcgvC?Ta+935y2xlS?;|QPUq~4e8bJVZ0>^2gQ`4N(otXs3WcmO&m znc7{;0=Ckcc#}qKQ-bg{G`^Jzy>Tgryenk>;kE?CS*&6rRJ`8=c%2LwkM8zVKAZ?Ch8^#of`qe%vEjFScSsMXkFpy zx0>EPGXA5Xmf6&-Fvv`R?{ZH`_{=+P@(J*&@DqvGL!C7*WJNn=sX(uDm!4T~Qi8tb zIGzo*wc8=wmQrE+N=9L@MuO_+OQZfmhwC1T-miN$Q(-GLm&#=f9^VxeR@1nA$u1E` z;!qAOj!rZ{I*MOhvMaGbZU~FCE+K9@dc%c6^U{XaZL+c%RV8ba^L>8=F0`x7u~L&# z=-C-w+}uR~lN?ITG4_&}i~)T^__GmUCYa*m1gV4ZDJ;`BYKip^HX`*r=#y)iGh2q< zJpwfl#+UqR24R1iVcH2R1#b?P80uZUdT~|%wn`&xn$aduXZL0~9(}2&8Fgwr9)Qnx!%cIa9M!uI62{M>`_gIH4EnZ%cusFd$$D6!X{kBBD#=0te=IV;dx;-O z4!BYU<4P4t8K(Sbi9CP~33GpaS+VxZJ}#V&1BMy-m9@vB28mCtc5o6`Pe1HW23_1d z*UV=}?%V4w*Ko9qC`LpDWX3`VMT-are@<`>yFt5yoevXKndck!liaEhe*w@d@h%a11_WBv-xaDQ7g5&q(}`IZTin;LK+4)pcNA2+uSfj88>6nJPmc zUymCDzILb9ya0;)O&_zouN>2?+uS=w0;AODz_IwE&uVt15#&d8owl_qJy;aC%&2~0 zWtluX>v&+|X3%Z8*rQR+HRbNM>ZT`b2P-U(jnS=Z+6#Xg!JD6y>BJ#au!IrB?cWN)a}c&}nEV%ls(7iUkA>K$7@w2Ac*` zFPF6jV`=d|ZXzhKZDTK!tnmj08^aeR3GRFhmZGw0pdA-{Lp=o{WV%L@=aKa#RjS%e zX&`;FN=AE{-BSB}@x&T-RRgQ@!TYv!I%|mD>u_rEtpjTk2|~^xKF2itDOn_4;78CQ zdQk}JwG8H%LYnFn*X)TXHyy5cAGl4L*6fGd4$4@pYTed>dU4iuT{E5CwhIaOfrB?u z?@!Sx!WVlhwzbV$ijs+e%;FVg0vb)+oiF)fNYnCK?%3j8!t|~LgAarMNZDrY*k(gi zm7dYb1=YzQWBuvu4RJdgHntuOq*ugMc`G16v-{p|Ua z%s#8cAZy@Uok_U*E|Q7-Ok%85**AtJfGH)DYoOR#*WTf9l`pCqhIja2C;~)^A8VWD zQ=un0t`+akW=*R4)t2A3(xrV@dZ5TTZ<6U0?XU~2L?J1$-k;AyoBmuJ(U=nJ#yRu9%Ga0%)NuwU3?c$0N3 zLA=zYzmh{ZfYqaujfv!B>=P+XO=D}1H7Dm?s!!Fb?nmY!@e@ny3=ts$PdlI(2kXK5 z=JbGQZ87Q#*hjY#wA8%)anUWwBvA8_b5%~LaQ5?=ceI590~%$&I9OE7>z8&B-Sz3+ zdGdKSXX31z5EJM69ujLJE6PF1j~h$ zrB4yLsVDvXy%{;va#4>TY&hqY;dU=tP+19AVCC0nK0L`elSzM1mxGijT{(C+)U>z* zti8^G0-yA81y})B3u(JYC7$EHT=*oWO!?}!+Udz zmn_}PjUTPo+}X(utNeG^m#wJ#BVP1kDU=SU;-29rDNBpyk6!RXH0-Xx^Dsn||L|gU zpG$;i)Tr~NQd+8uUyGB)Qm<1}ZBGd^S@YK+02bJmxbRlVHm=b}M_DZ;D^x^s_MGFfEp%Z^ra@J$D~!u zT&&=I%-o`Q$-^flT0`;h!zhMW?;fDunFQ}`dJ-ceV?P=vhK9ST5|M=0PYZ^wXY}_D zBXO8A>^(!A#Wf!07UN?5mfAHt1(QrVbBrDDj&Sm;F56YCKTTzMhYth%t)P^~W(4JwWC<4oVMR4_*Z-@wWg%z7Ga3ilmf? z6RD!y=etn+q%Ze13bWz#X>3fDwI1fa*j@eLmEDSo0vCZY-0j6Zdilft@ohDh1Hr?K z6i(#J%l^BgXf}3CiKW__&?`E!>jrH1)eK%!hZWVSa-p+jJlP_qH$NIKoT&-LPFs_^A zJ1;IjB%Wg|sNT*`d>7^bP;bYP03r$k8l%jSvjs)+C?gUE0R_0^}c@&xpG^0b!VKI z-#W6={d?`ibv|wGeArR%p(qnM%#oW{kdt>_wq$tfl0`ZV*>#Ij>qS(9BeUQ$?Ab?J zCRI|%xpK0gniY1DPZaDak|frFYKx91mAW`*)-{!FQqEw(GFqJ`c`C`| zk5zzJYiF9;oHK7hu(Y~d-U_+y{$P$v8A0%*V-8thTzn0S3IR1)c1aGZ6)knlk%mwhmKl zRzCg^HF`28BFbTOrWaX+3s}3}41fR`_lNce@$|Dmw$J!c^-WEkhle!JhQy8)=Hd-G zhJ-AqDHCh229jxDrKyGjMX3sVIW@vr#6s-cj{P!ta*13QTsJ(Hmu7Uwu9`m=^I+m0ez9gKgBAGWX;qyPfT#Hs2 zi{WT^AH?!r^rKaydggeH$^tY2X#UvfwjW@qAvmh&0vXi;T!ereiQzl?L@MtYEEM<7 ztqcI*_m}eb@#r|K^P)0s8a1vrXFxVy$L+VSG6h@i^AQ&kw|}Ox5%KX_V32Y>%=X7< zGEI@jgRw?joYoE(cy@-6-$<;96@x3f?t{uq8)B4-o zBP?a*N$VTJ^i#QcE|ZcD=1j`74o$v|Oe!EzT%~*$0HKe!gjO_Ws^s2A2>5<^b@qEt zVy~&bZ7WR#0{}a&6U8rVmd;yQ^RSH3X>u&EGbHap7*xWJ-CIkB{p0HjXao6$BXvF` z1(i1Ed)}$Zy+a$%j}4Rw15>FRK-jw|nzYVU1g*3ZFACgC2%FJ?0j!`;_#Ax&y%&ZUS?1g@boD8|gr9@!XT0imD&yz6JPQZN|uS)Y<@7FzIE?1eI?4yN| z0LP81$?6S{JN?dw#ry>@Kb44024}T{L>fkVv9gUou>gTPCP4gG#`6C8!5{}@z$L}m z?jqLt0vE_SJmLYs>E1Xg*~`QnyK?py=rEdZpuKfAzi8c6KeVRIp_Fw$^G3nIB2Z86?bWcfFV$! zc;%RYm5W+yjcJ1Jyv~G-Enmwe;;G3*e!#D9^ED!*x)N-~YWXPXA2z*>(IxaDDHX>V zZbzV(%rFF~^Xr6h)osf3&B`|GO_J_Ah*pvzAZ@l|;1jbLHtU9Jy`XIL%BdpEBo#o} zPg=)YzMa&cFU%M7GcbTzL4@^AFbbnl&RCe7V50MPDi$C8MPi>fma=S-6ksxuf{)%G zkXS-&WMOXg!uDt?AQmg^M#?;(KB6a?3_wVa;|IuP#_pKP_`#KWa9sHI<``1Q~FwfN|r~X3b+3FaFi(FixWR?WDj)Lf7^g^X3b8r`Z-QzY-yY8GT(ObnHn$=qwZ+)teQp^gIziOpY)O%fnwxU2g&d$s$|O10r>9z79{NWi{oVu zRbcKun}^6bA}FKAv-#LIoh<#5&x97ldR`(pAVrgami?}<*t>W>kn9_OHePHshX^)- z3-ul>rlpF57IdCaA4;T%>~ElbV|pu5Q&lD1bF#gai4}%|bgLzJ?<`f=#@wU)ZoAkt zyd7skL8m0!@i|D`Zj$l9^aWoCQ=g0{Lke(09=&IA<1>1VAd`7eO^hEA-)Kl+ecLZ? z_uc9Yg>B+W%Qxa)akEix+oM##1AIo=qMl~s%nHBAKQL`obCj8~KDa0uPM#D_CXJQ8 z)>Ep5sBpaX2f8bklnE~OshU9nYws_L>iGss)_-}rN}1u3j$ypW4r^u` zuD((4Cw``CBc{%^7~hDQ_0Y+ilKQ3mE?1ICxJ^yaCR3vgK?{1)>=3oM*_#yF)p+zb|cyRLBq+ zz3X!;bV?U30CZY18{)t%sWzr6c z@DP-SRi2stK18wQBAek?f2^>yA!SVoo@nGgmDX>#b9uyFN$I$qmlXg(P!lxz6^|{M z>84Z~9#DmYu16Azlb&C?@z~6FJvz#ZYax)Ale5@mGZ1sqqxE{_M}{y#-a-_XhfR|< zoQ8hd5oGOTA-?*&nsorbLq_aJr9icMprQ6OSir{j^Ny*AHSS`=1aA<%v_55SHi2S< zBkS*`#_j4VO8jEDwp%2xKSp)lJHV{p@OXz&64wf?*i(|s z*do!{U8@$ypA3H3o#^#~jkqHstPotKH8vMYNfs()h{SnxRF9oo@L2P;#~6^QvelcZ zNYN7>-1XAJ@}VhZuf&nBh~aSDdZ@JIT^ZGG;Fgo#p)!-!p26epbE>D>mzHX_y=&2N zcCoB1#K=$}%tuiV#(1ksh4`2~l!iWpg(HE60r)9|X^>9f5YK5l9v>mAWeJ!oUDu^A zWIgQ392|U0_O?3oaqlyvXHXxWyi5l7O`A?TEN@pw?Vvk##{zIvF~82{BbF15ni}<| zHo!Hvc!wE+Pa8>8%`X=#GcoF%L@Wa{kgAbc!t=xbl00uI_4M}6=dJkmvkQBV z9?rMy@v?Cwv;Z&_`S+r|qOVD9$_IyhgZfGQroZM!o?@}OX(>m3(%MoheioCc>*%A9n?zK2Q-C45s)MykJ9D6G9W&Wyc0I62ZllYFOLj_Lrf)k=3|*!v?h zpOaH-mx$%1n-bIK=BF9nKK4_Ws91=MRg%9gXFy)D%M^ zZ+F{><%WC^rl2lunD@Lpk2OEUGjE324ezNWBga93c+u|x58qS=Jw3Lm*%*o57%k|~ z+sGp~U17I93`r!Q$MH}z$D~+@{v(=2WAa|!hiZ<>;T*2KfnRr-5OS%B8w*!Zn}R|E zS#hWN7p&)aH0vI&&pcM6DKzUiYTXYY)uSUX^Hb;pTU6JewOUTv8mz$u=86*PG-Lzo z@mH&sHR!L`Y6Ns^?|7>;Bdeb;d}LGJHeFp>n0RqF5H|kV#bA7O z$LpEI%v9M{DFZs$Hy_VBR!<3lh2?ZD6?Ha6=TUU+R@9uH8>mdVFwZD%`5D;+jc9p< zv&_UlyE8%j(cb&vvsm3XvmoGnSRst0OdN@)0otR|z@7E<7-F2^B&oX4A7kQ*`nBIG zF6udTyA3r4MQOg6?LcVmzJ=uQV#&O7X5!kc;2VyHk<Qb?z8vALG*tuk&(two4y1w#_wdN?1Y=9MOXQNvauBAqi z;Hbn@eO#m^_!{+G@im}`;{Wgj!1UfHiGmV@N)1qAgef2>pi>C6c8 zEQNRoUPc)4!L@egUSQZF7PE*yQj>2WieEzqcNy<>nkWH4FuX1eHrLZ6y+o^l(p zw&At$qk4i>nr3I(aCS0-MZxccOZ%h*3R1~mi6~n7f|jou$)GLx{0m+E666|MWOQ_U zdZp0EFSKoPSkw(EBVb8OCwxL*9fOk@=3+-bW`EZrA)XGV;TJ~w~=e5x6&bcA(}=bz_k3ca@-g<2+kb7W(E9=(Xw9~QY~I_qV% zJQ{a;J9tT$Ua=GD0;fo(dP~FGu~4kf?MR8H$GI}k6GiP`*M_RYmK31LR;S|aI6cW|IR25@-27OP!OhDb zLMtD;ZLf>SDRIk+7JsP?8XXhtmiQ!s&c>c)-|~yG!)xvJ9)u&d#s>$fiK7h?Q}!Ag z(3?1MqD9IJIcd7hVL*poyN3?DRoW1odl3xl? z5Ye^j7pJ(Eg?Ac9Ci)hI7N!eR%vE}nJf5-DM-AtAImB!jPi<$SEJ9Rx0BJh+To@y$ zXu2oEMN)agq+7zr$q;`*Pv$-h_QO)tO!xU0>{T7%OFTUFc>N5vo0-ZJ5h_=*Pp{Y3 zSzn=!4=4lHUF+Ms{(PmJeCcC>o1R95q(y@_`@K`L=FJqPR(!fQ8ZaVd6`MumaaKWC z{<1F}9W#fY!Fjm6u9Nju*37LaZThbi+iyZZxZYAQ=2a-5*cC>ZiLuzU-n|~cbzae$SV77 zNO*toa!nVro*eyxI5bZClwM^?nae%ae5gz({X6Zl4sx9TWd#H;^;GCUQl&|*WLZvg zXAy*Lt9l$o8&7;_Hkml7Ty!7ps`l%dkOtF&kWS%6tCC^b zeFSSfZD!9RpV`VBrj4}@c?##sc4-f#=&2{$f%Kq%*c7L6MpR%{-+QS2h%@6b+n%6n zTW*4B4h0zn7>5e-ELXQs-dH9_veO^Efugi23|S^IsdK%10U62G)6^^hECNt8RvH3#W1{Hyf4=!whHUDZcDMvn+$JMZGGNT{TA6rLy} zONYaobO)`%K?l-6HI8EGWZn$brvh}iW~*Lkx<&yPWpZ0JkeS)FD(|$~6S60S#!Kqat=!yL)W z8JkrY=N+}8*<`F_t<|N#3D(*Nn59vRwZ!K~dScmGQ!a$x|IJ3UG$ADaX=i7Clv>l%igu>>D{d|_U5<+RS8s1C?Z*hRe=~1 zTg1iQ<+|(PdTeML2X8?(-N}7zB$*JUbQyZRrhO6mF;}JHtIc<7oZfyF^ykM7SOVj- zekQouw!5-2^nlLMQG(U`y7~hEZ90}7iSoNdP941YOE&e`?`Jf-QO|2bd2sm?@pNXt zW~hPW?$`(ehtO=UZc+X-#Kn*mSul8n~W@AxV!UM zt_ayO>W)eI$TT2y?*BpHSfrE>wxyipM3}NxlsXjqM>qOm&2PRbPBMWPEUiyQUQrTW zlusi-MA@1UQGAWmEC_;(V-J97@d+N)SikWzR--O0@3!i7^vL=^2+eExtC z0NX4(80&y;^%kVuvTLS=8Stuo#+s(pH~N#8;f_qy*ra=SsfJ<+h`I#rq3B()lp5o_ z8OA6ir>#?QlVnMxYA4Ccam_+6vj5RTH1R6Fe%wwZ=W*30j1Xiv-t=qHStLmu|E6&K zN<76vsig{62?;swx)KpUbl{*9mOQPiUj&Zr@p}L87s>Fx8fjhov$BSX{z2&l|JN~+ z*+f-&{+=XNkgVo#0R3;anD3_{{v=Zc*(Ga4m@c^3=N+Guv&=~vye?j^-EO{0zl|FT zQ5wFJ;^r<7U$LJ`eFhW0KVtUNqF#4c6E9=5oF3TaO7Dlo4~&*0owdX2JWSRL$%5jp zEy1TZbov=D#lA;mlnT)KvE8){{^B4M&8XS&(w6lgK|GDGAl95nTPXQNLyS63iXhWF zaR}F$NI#86wID!scD_hVc?-b7w*#r$Y}cJPT|Vg|k3fy_kSrstGVl{SL7&A=)=|)_ z(WBH(z7(^mD=5&dBm*Iur(jGZOKIE-ct)B|gg^#aY^?aI9kuRtJ(>K?NCR?eO&YL3 zCgm{LWx3t=4DDtiDxZ?S z@EN%Y>z~FvqovYl#w;QQAyQDeMIkUy-Qr%q{5`(UMNc~;0F_g>*oUoeMJ66GYcbv2 zmz19@!}lW)qzfT~*4uh!r;~f`<}7hOYHo?9gZ!mV@uxr;lWTnIEM$c)0%L5%^oICX ziGk~s<%8qARW9WGYYcfJkPGW^WdbrW-o-SQH?Xm!9c6URUa{KRre_`>mwsQI>9Veg zTbIN%i!DJ4sr-u73;T9tW5^y&$Qs2TKQwdf;6zM)Kxy@z;Vxa+TrSy-EnQ&+sM6Zc zSmztEY3UVA{usp$txW@srjc*!dDNHaV*PWyRt*^OLI-cf@NK`&A5YF*V$QGT)uo9KO~nfMqLC`DdPKxw$I$)G=W?GzYo7Ht(~td(MXU z7Msr#fOg%w=I!_+_serX#z4iP(s=(r`~_?S7-iuKLchX~`Fj^?BGrRO@VhoezeIDL zoK`rd2ixgVB&P1RB$dP_N4ziw{G?55gbWt;Dco_D@>ZquJ2<6ir8jB33#fo>O)3=) z5=QH$BkVg_IY84ovl{bO;|11(zXK!uCVk3Je$9MoE~|6!O_rf-Me}oC7`p#l`&{pq zAwj!})6+Bm#yR7T_FvQst!a`H}N$v+Aq&%)Zp>t;-pi(f1y7 z(uhA_X7XddA3`3uhDV}xjNERk#uk-I6byH&e3#ne{*2z+N^1^tCB<*P%D+mC-&B;m ztvtm7#+=%MzHsTP0*H9;ODX!s>K}Atpl`va`g&Vi*DrS*akuY_Hq906H64?3Enx%7 z5WSLydbiNrqiSt$Dc2)P+$e1Mnx+=&OK;5ygUZ{-$g|yVu~r{y86{+iHP$8E;I(RY z8iDp2dW3nC4lJ_p-5aM2i@7U_F#>YJ-S@9?vEC=<{HQ%Ya~P6vJG)!h{lVI(AkTc_ zbz(w)R%-8cF%?RNo@7Z`^6JgeC5wa%@EcL5qD%M4i~6N+Z(Db3-TPjzy>CAKbB0B+ z0Dr=0pqxf%hd_L+t5Xl(ZuuC2iF9=hs#_9mDUi6{-H2!&YmCmXeH+N81bIwli9J*0 z6C<8Tx23i-r&iS#0G1q&4)<;lnzGk!r306*>gt<+^-!3&kX4V6w!J0a-L)!T%dfWg zx!8qLyscvQB2-o;@&LvLE#a8M-&hZfYZVf|5_AWmzggV7(#OnZV3O&6*GZZkZJ3c?6vvhRz{d`*>-!-_b-f}oc%$tip_(0a-cnuUXfjPv z94@_BaT_QT+fn53GkQvwl_0Y(o@NkLPJWZ~?OkX~-^vLJJIWZ%?{u;NbQw7ml-$Qd zUuFq+|4ajCSwjx5n{0+I&bL3#^}TKdGwxTg6S2Q>v6OT3PTlVAb}u?FV;l!fKIn$+ z{c^eaPW-S~T)z6On(7$|If)$GCTM*IV}c+-&y+M`6tnHw^Fk&Ej>lz{g#3j_ipsQ| z{qbROZoKZ|s)P*sy0r9f;$QuWQmA;X*%Uc)qcVnk$6&tR&OBpB{)lSelwb8TgM4bX zsNrisH0u2H%5-P5x^lht zJit6HV`WS|7=Ymc)!U1%Al6=Rc%Pp#On}0XKb$3-`)OzZKY+EdWo!EI_0Yuiwud}= z+fw88IJrnw>R7`;PQX4Y5fR=^=M?Y*l{Uxop3ht|=^fb&f0VoqrZV0aYAgF}bmDHy zF%hT{fRMz;j%#SJ@5V7hapjp zCl#^g5*+HveBz-o&7z|bJbMdzA5@DRMH8$r(o#QC*)aM4gfajgO#|@zGMC^E&ljBj z_IxPAEH09Khfi-KF7}$LPMs+m6N-}O`IbDSB!x!eNz3dHpZf9(@w%NKu>)-|TDBfT z2fTWyl0Q7teR+pTz}id+jx22^mgp0x27pfkGv*piCpXo5)9g~3#pSF>!tmeOi|d)@ zejm{AS&V`_qcAAu@9{_w#_F>StM>I|1_t`=;oPMADbfQ#2A^c)8|J)6yZWTBCiXa3 zeucelc_H=iGwZX3DF;O4NiX( zOzy({B-EDpOa=XmEG2M`k-Y=sn6)7yjz&M83IYL9=sM+N=ydQtInLhL91v z`yu_-LF+m_`TQviQkg5=qo@4@i7=?kf;=uiv_dN?T0T_xW*G|mSlQE+k!E~0VVTbTN-7P6`VkI zw}DskWIZ{Kt9^mW=e@;x?)kJqYwDy-aJ+$ zEH}o3BH~0nI2KyQ83xph()WbBeYQ|V{rxy6Het-qM>jF?#;KO9GL#Kp?WVv~C5<*%n_tgXBna!W zwze$Y-ohJnv^46=(1kW~JyZCmN4@?-;;JJvB?CE2++o)yr0Z`^;u2`^teCg44kqwW zzij#S!P=Y~&2MjvxEX@~irH2h2Aesynb0m~k@_0fxd96@NR_bukc9xtEvbk zTki2egLDX+HL(z%-KG-#3mqD0_^fhL!#q0d17}&2)81Khm!(Y2-rZRUM=jnA|Jp7J zp$E4Il5M4d5n__yRxGCgI`Jq^i&J<%pDaoAhiMCZo$Ts%n{-S~wh?GYuraAZD)d1M+HZ|di`7kv=GMNnf;7h_-5tVVf}P=>Huo)V znUa`v1C`h!0X4ZJ7cRS^dL&$T0MbZWDN)J*S+3HM7x4ST*ltKd$f>fZekLV@f$X%hy?gT@kVpMHN$CRi`$kS@n4`ZSis(w`7 z3v+zh?6xzt6e%R*exznbx-|qO*2R3$pN@w~fW1|6ipt7P+5|NCsDAM~x-h+;<=9EGhk!G=FzX~UExL%<{&M@GG?rkz{#uR0_F12wosSppd z1wBQQ+xZ)D_Ar-KVNbnkJ60B|(P=tf-GkShc&#Br`DWnQTdGcq71|P}3I^ zNc361#fxxl7TMio^-+FDmdC0k)&X+2rf0&B;Bk}lxc6T$g zBU3TGUx&4#jmzp@yy&D^&tk6AzB{wTm0y&#vv4g1TiTg^SK9#`_CVFlsWy1R4~WNc z?dpy06(ba+5hPaVA(Ex^sR2CgM>CtseHKAPvjN@e23K!bwcOOdIgq^7it;EIXyW_| zjGyixUp(OM9|zg6c4ALEKb>2YOf+LoiP`kc?)yw{^aO|_XNJ;te`)XabJVSAO0smd zJ|O}B@vL}>eIg(R%#YhL$+e%8y1o-F`^hXwLRw6!IuG)8M%|Vd3Yo@(U7hkg68=geVYZ2 zm}s)5qZz@cBQjTHPyN#4K5P4bI$Ot*L_hUiYhTl6Pk1=X6M=xJc0-hG>}*dDHR{V_ zF3UI;@ITvsML>;T#dq}=ph!oqCRE-tuhlyrJXLAM?HW9P5(1dP5_OVQ$Wa1&Kvefb z=3fwNEjCbBeVFUk90p?loVV0qlod4QSg8YLUIa^@Z?G{)wy@r|N_mm^eHH96wm}_k z;LD9zmFC0T^v-T3q@xuNBJWg=!eH`~^yl-VHuXAOTD#wTJU$O@rfOv{3eHgvKjXsFj8ED!j_w%UT*v@|e)BK6LxA1kzoMyQ$((A@0U9Ao~|(0ud) zphvV#BDnn4T$AV<2H6sxZR)Dh(WsyNu!UMT)-xiBH8h-$jbg_-J=!vZKHVwaSArQR zfR=^T1P$L?-Zvv5bVMko9#HqEQCw1vM(yxRwP=@8MEdfJKLpPV-y1*O4xb#8m}N@& zBW~~n@SM-lD}-3V23DbhJLxVKNxJvMFj()aJ==?`V+hXpNsi)Lf02q&3SYI+nyAdh z#l`BE_FL#N5El?iifD@%pd@$>ixdf;5Yc_myT5KhFgm3)D|F{Y)2RQ)L&PT&w?dyRr>PEe^E9L3g;`)X(&37=GyZ@a+5z^ zN(<9u8M4Ku(7J`Xo?7d6_>L7{*Nse;Wni#PLqY*XA}P%QP2ZUluuSzKb{h1@>WA2j z#>UKm&m|#Lnn2As&2cjNiY)eufQami0~yZL%9pQxiU2e%N1UZ2qDiwt=+pDR3C*+G zq9jKE?0xTEkD0RX__jQtMeDw|zCg#>m{?pi`ud4olI-`HPr7ae!1B;h>kst|W@Cv0 zGnITxT?=Xt`i93u+ow`ej^}lo0^*ANuGKq2!qo(5!HHO{ZjXTnbJVX3Mp->^AK8m} z9(N@)qwL{3n!;unyk&Xx zyBOu}`ty5{MCRvqvu8-AZ=jzf^lJ=m|ah@4Nc46vdMXx$`k*f8xT56aLb$=Q?9{SZ}SlX&yT{4o@6@C%Nz-!aU%4?nuP0 z-$>!d@xhwtWbCC#c&w3IS6wIHwJd++_M$flWl6uFXiz+k+~>==4a;A=m*yv}1Y+_m6t8h=(9et)nm zd`x;iy%Ut?Yr^27M5MHXJ88(HW%48AewcSu`D1p3K8=e&Qv*a-iM=APAEw5+Q*l@|4UlhF8>G#w5e+)Q+fV_*E z0*ay(gEr{%(-9Vz4CALL9KjT?GYl!;)S9PNel8JSHJ+IJF{g2ywy6bdqS9$+=L8e7 z?aeTeSJ)}Db`D#^@#f>Ax52MI3X5{4ELZs8`JWT`Z=eXGhgXF*+lm|ltn-(4Ybu&z zV<2tyA-u0vW1|8nckSL%HVY84ziU;oE-*`-cBBRoioAQu(ygAerCI=1JNmxDW*Ac~ znLrMbqwVP)!^S&2bjiXEjm)|f;SY>-pE_F1rE{SBiD}X>#hc=V_E(W!^Opq(d`O+~d1d4oRiI9C)&%ERVE+4`WAiPY_i$CND6@e#mS0 zW7*v!AK$ML4k6!v^YQq^wXCP+2w|KKKgF{tIUkWhgQjQ?Uvm}GFgJ8$KC%9~DAE^6 z-b^*{KLGAP5x*a+Ii6QKLkt0R^kv$dHqJcf&JoG_PXl0;8Jp7rf_dIsWc;M*Ul`GW z4j$O8Lj(|8Dav+yw-YZEv*xH!mK!zj3^S1yUVMV|V>&Bo$DSlae1;5V9KbYqRu-|4 za_(?R8Og_K(7-WXh)?B{*V=2MnA2p703nhW+3WO2M8>g8Fd#H2A*F%VOMd+~AeH2Q z{^$2dRyuvSwc4#^WI5b&HZ)<-q;ZaiU42aUU)p3+CBNQEJn7VPd^c>8k7zCo99mca z07wZaGG`1*MwVx?kl2mh;s{5|V{N}Mj;lIR4~;m`8n(C()F+YlzSC`U*j$c4^mo4d z-LK#LmbYBdmUA7~TA8%=R<+|;9MW;1O`6o>O{50Soa8~pCp&CNO{`#z&SBWZ55D(Z zX2qNZqTTGFuDH5amXc6(zAnQry3$1NLI(S?BFAbEeN;$a~s_v=el?NKy8#H9kR5vLR(uW;^VrY zN!@Onn_-LEoox;q0nOTCJ}oJut3n7mv3WwZT1$`$@W9$pjse%3)Fvs3B2pkG_{3^J z_D1aWdYJb;oZLBuMk^Fb%3M~E(h(9Co8&F{Xm4#%7=+wzZ$+`~`bxnUUX-?8DZQ!F zHBpGI=FWea_4PtT10ntvQSGfCrIZSu(20sXbL|BeUV!uOcYklr^Eqs_(C_uJ-A<20 zGEB6Ec>Jt%zVpjT#aL`)P|=(h9>$9pw+Q6lu3<$+Q#D9aIT} zCc&&TRTw&_A(>mex}8Gm1gpb#rCAp8X@mK$N$0It0E$QvM~l0*98&~d*m^s~W@A`S zFq>$_f@fX(cu$E|`sV&pbfr|5%7mCgn0&OQ2Fw86J54 z190W#S8iW?@x}5d?{w$;6A3{?Vr%OJuO5ygcSH-gi7#9RJm-n^JZmQ=d8CZ45`ek6 zIp{;>K3EWxiZ@k}UKu-b-1o7)S!d|8x@VG_*pP7^gO$qFmPHpEw~xBJZDyT8QFXd) z0ugO@I$$OUP0Ds6#%MlwqcRJgg2v=~RBojc7Yk`<1 z;eUVfj#x>GT6iWsk!XV%v*mX(954$&bB}=mVzc?&CR<_iKZ}^7dntaNewq2$IW%J= z@3HX7Vi1jtiAh_W0f$E#TkG_n#`%yUW;gcwJun!zxW%pToo{~gvz^TidCz;^`)7$r zoLJu=09ac)GDd#|Wcw^ntmmrZRu(x@tZI{*XA)E7qX2N#2%WfUp+Zv!Uvl8AM9bQ=z-~8^kAOu5k-w>l=I8Wwx ziUT{^nsIUnsraPC#<*tdN9fw54Y68t|21HL>QjCJuX@$1B@v0nLbd~C-Wc{-5oX+Z zIS8ASb_AQIgJtI9PPbmGkS&wl5T=#ej?cX1Poql}ad;XX6{1P4u$g%RG)YTjT6^VP zuST_JR{esYNp-I6leXkmZ78g|{CQ|BMWopXXuu#*@yK#Ey4f!0Uv*qk$I=KFSnq;1T*ej=vxRLjlHd4FD7?2=bywt#6*d{qB3;+x_tS zKe$NW&T94W5!yI@Y-akdjAR{$S3MSn(K;H70iD|0#ddo~1&fC?#YX5PA%XxmkOV`4 zSPX=HrszBUUzgmWWFO@D=fqBdfS!F$-bF0x=-lC-_u z@}3RAW{zna`%4fT!QIacE@#q_3tWl-87n9hMW9U@GyBT&GJf#A@BiSk%P*I=x$W(5 zrY+LP2>@1>%BP0TlCt_;)M+JaVIH+m4klL^{mwy)+hE;xcDvfdb6_wA`)4B zTxI6LDxQ-iRc3s(lH?H20uUGmp7V^oQPC~(hPW*56k1K&CBbvuiE4_ed_o7-0pO^5d?rz z@PvQ@LqH@>8j`0Dh;)o2B13F}mgZS|d#A9lu;?)>wbWXst?kW;w)f$qN4<^pbp-}4 z`0QuYz3+QISoI*_gFSr$LL@X$wianZG&Tn6KcK&?sU5<(#+se&jnH58l){`4kc#2Y z3&H*RJ+$kvl+`iEO?u(4+xlNts{{k z$^&$^I~~rWSe$iTck4?M-LWscEjxIbA}e?Dk{C@V9v9ti zYR1Zd8Eq-|;iKOtNYd8L`zVw^=Y&%Kl8 z%C);~P8&Bv7gc83Ik}@P&Z-~nd&oqdh%#D&oDkp1#|>zT{+%VjfDnYL5?c?OcSg1K z(ozcmaNfPo^S=GvZ^82jvafj;Y$DeX08bc2j6#l>e-z?<13PtPbEnz->EuP`J~qq_ z2ACDV9@dVm;e8+cpmYaEr$A=EEZG+sVocq$^L`ev7{!gPgXU3GY#UU*xfmHUdORoO zVdbqe0Z&r$$C2*vI6Caock+e#@qGsYXb zW`@b(qN!mQILB8+Pp~XW4AT{o8o<*otaDofMdcDx?J4Rh&$4wd!FR7=?D{I6A zKCymWYYhq5K!j`@O_^f7(Kj0;$@V(iyatS#KczvNWNihtCUcZl@ ze)MDUM}PP->8AT0CmURrUL+UkNpe`HMI)GmH-FtpBZifCf5MCXdXp^Qq_ak5`W`u_ z*^|=6))*kIY6nX9gtX|h0E`HO z><8D;JDCQwkVt@yRofE;Pxt^SNU39D|LLFn2^1^&=tn>LeNswVKYlE&bO7=#sM`f6 zxNaxk#5T^tk@oy7@YGJRbZGlD!5~$G=4J{t z*XqeAdTpBVv~1YByfSN4l~Gb{kiiK^9Xr}Oj~MbQ#Ln*EWOV(p<2p1Jbcigri#m~B zuo;mVFpinPmftaO{BDowqu9{hO{3s zMX{w>F^B~`U&E4~)Kr5H{l|wMe`0e>-s;x3K0-PhivWO@N%=f&sT#zLGqDO^^IJAI zH#8u&dPENG!p*W1olGb&I~)hQvvQ9tgwSccJ^di1#xp~gX}5M~i`$*}1fsLs=8Y5U z%-SSOcfaf1y^DWz3Fd?c)zg+_GQ10672N=9vqv?_SSa2E+6`$G)g#`3TD=vfJy{6& z!uTS9@ue?)=^f0hTubypkuqSoPOEgg+exJ_B_%7qZ`pB__r59s&@6xm%hk3ZxoTk7 z?sjEN(HNW=;?kGb9*G%@CJdeDSqk`r;R)G61F9 zZ36(7TJ->FcQTxHZkN?9+mxNoPMjr@E%%vIbifD+C(HNX=1Uu^Rm6jzrG{`9+~wsJ z27q&JcaFH?@~gme0>Wr26X`IDCT63 zbP}Ox4+_Y#I+%~C83l=CX6zP`Kg!Oj2YrbLVl-{;mctCB)#6Y&7k%^(A6A*KaHts^0jFbd{ zv(CEd;)JR;RP2Hq1})m#+r#0*N5D*U^IP3qTyw>h01;$Q>P&(_oPiK9`e+>~DaOmB z;?0Hy+U9z9TzEvrJwSncJ-$c)D@!eW_H&+64lL_RT)aQ zyKNq3(Hn;vkTss`2G*mVKg{8-iuuR_vb_CWxGyW9mL$Po5#5}HO{pz^nqD`9Ff|lq z(Tze@)Q>vqzdM#={=wJ)<&nZiiU;NRBq^g7rdg@i+=5R4aLrZM0x-+8ld(^qHrtGT zvBehl&!`rIK-fBoF%$#?QYrNMJ?!=N^n%X@MkS+J*!zeFAIpen77^fe3z-y(e~I{| z#>(fw7w1}XlCU^c6S|y25QH~pGR*0_U-pN8gznCcdeoyH|1U%&${d-ydeF+bQ?82h znWlqTU?Golua#pao8u)vx(EQn6DBDL8!7|hSm6U_1_Z;EHvC_gu0zfptiky0Z-4v$ z3jo6`to!O$V={tHcbmK2HXl27y<(OMOmy>G-AtV7Nka+JdIaI_6Ai^h848QxXRryo z5w{OVrz`_!EhfG+LqM3#s8kXPjQijBzIfg1UMt6?a_kH^Db-1rNQp2|a-OUboDMsM z%$nzuX~T=06lRxwG%(4NY-3m-j_Vj$gg%hhB3wr2GA*wo+Q;6P?4Kged6USpII5kC z=vQ)#4SRMTr#^<58ors8Q=Pgbn#l9+Qi|@1hCO$d8h6^qLe^9J__5;(0FE8I4uDR1 zO$k6s(SIodpfR}N#4&SRE#{bJAr!&$ea!j32@6b6QfCHK%#g|f$S^>j8Bthiin9so z_YhL)A1TD=0p6)on_;+KTx22= zzVAg(4H_XehD3uh;^P1&XdX0xJKgC{FVOEQFsxIq9knQwJpyD_iX~`q ze$Hb7fEA>&oM^!6lmb9Po0i$|GqhD+nf3uJAKGs6VrWef8X9qHP`RPoXi9`jm%|?$UDz2PbqcdJP)8MIePToQ8q$Azr1|@w{NyKJWSt6ER#ppG2=}+kBsj`x;HyqxGw$45 z=b~K{hy-Ci&bBLcSWMH(kjJW9q`r-mO;wQ%-YlE~cROva!ndo4OcUVVDC0@06{OB1 z4_7(sQkp8l1_|a|yY^9%oVZ4{?aGQi{=Vqq@4rDC_e|Phz(Js<2oBGfjki}66`HIy z%pA{0U|d_%1U!7=)1TV=t>1cycklD=?fJe(x4+{ZZui%(e?8VWw{%=v5)huJt#bW7 z_WC^#7*c90b%cK@jzw*)#G3X&0~SUCB2cY)rpZ(t&PKr)L77>HShLlSfHa_N9bs=F zctU`cLSOEIN#J*1`ciavPN=)z<8F^9BC*xo0z+cCwM^aJ9S$9Um-Gyur4te>MHl1l z?hY%%-93K%*avN%YKnd4oQ;a}JLwR099jO&zc!^e+dH5WlrZ5IXP*uINE&$m7}c#@ z){m{LMzh64Bvucv2?9X~&z{SGNa<)ArX^SyjW$(MgCq_^adJ1WJU~|h3@2J?`V^R< zq{7u#U(EopwY{~kjOmuy+@RMfd|)Ct_AHyTCXH%sHMcCpsQN&hq&Ax-Puv;G4>tgam< zR*HK{vFa;kX4d~5{EeAeDaFi+Rq`5@yj)+g*XuJg^Cv&`DJG(*9{>mtNRXg8&!?w9 z_32k$a`9!Xm|056N-37I&%J)%ywiEMa?7O5$C6nlORQ3^&nWl4dYZ45Q!$ln8>gmH zalhY>KKu87|2HD0;%Ac$g2y~O1++=~3Vx<)KrQF|cDsE&o z@MWTnm5Tb6nR$12k9&Q|AN%-6S?lgvuJ&vM^|ibEYAfA``giqRC?SnGxvHuW|sWgzkVG95RF5Nh1(%4 zsk4*N^{^38oqb#8XV%J7c7 zRQZ$Ms6lfgldS!{&cRl+E=gdY22S$Valdk=J#TR_0d^n+GecRh7%kY$E@)(C2cV`w z$icrX(C+Q&1^nyZ_y$`rxpw%lU}h{YuMjgsXx?xHHh<&Se}gwRH(5#ZeyRB6sXdmm z&$8EZUa0}FREh^s)&NA_zZGz99lUPvgA~zi6Mf|XLkqM|?d?TZpW5s5Lm&LW*VwwP zuCDCsk4X&pvWwBEv62R?nysbiGbIfe?kAg5a6x`81G<9T(QIWqJ469M}5gNJ?>udW4 zJoQd#K)JE8!2m?q2q>)%Yip}y%L71=@aQHty~&|>z2`mLSFvSRD#iW19xKP%tHEy) zK&QbkWgp`FwGW-_tCjvGerEKWQua})t)fi1Kl<^HGP4FyZk-!Jy})1^KTFLPfr#ek z4w2ndNHwTa``c8R|C@hpmq^7b0T%a8onpnzT^&BIFn})dQ^({BK>We){egwfrgrYp ziHe&h+SH{fnR_B4O;f744V7XkHK5+>>oIuh)JeT#ChxS+wQ+umtP@p^%~@Wbl0QKL zkTt?|1JSs3vhSJp960Lzb!~!1S&8#nk&Db%RqNqF(CcLX9)vQxKNHD})$O!-8V&+^ z-dTmiN%9%ZAfO^Vs!nH{-LPb49j0DSrylP2IgX0pJ~bk-8fQJn;YqQ*fZ%yLnt+Y( zD})DDjK#14W>)QPhtt|&_v9V`;PBz2VrglGmF)91S6{RDlwbG-Z%!C{BbD> z426e3^x*)5AN%OXd{Fp;$YgF2jtAU9^dL@igxuWTVqbVrN`hHM{Bqqe3Ob^*GRLtu2nTkYu~{_PRbH8d^I0-#MYn*zPZ4gaeNSDE{&{6oKNs=WDuO{qML)?K9VPF4 z6@*Qyngd?d^QxSGl6{cu-&x_o=M`;XEFsMxpghVO2F=tU;j=2E%3F1P!bU)8gONyx zjm;BsWvK}%0rJR1(f|gdRdC|(q{Kj#Sw%5wm15}Z%S1#Zn0a4J3_uSaSILhaJ}j<3 zeq4DZgd%1@;P$t@?N9va%U}MZpMBipA$$)YK=$_lc<_YKVZn$ZM=W;mMzV|w457?- z#yN4&S5SY+1FmW@T#n$hy^+azfcaMepgur49`XgBzYq_6(1V0x308x#CxWm=9vIu5 zHn+l%@0PZcaBT4WP3m z2%5R9uVL7tPD<#nst3a)m7x_hDR1oHVY1v>p{@2Nef68)ly|?&T_Baz(Iw3Hf3-b&7EC1v9@|Cat zEAl;012GUF8N$?|jks1o!&#w3|TYgioDQJ8EHW&a}y33l45G?0-!Z6!V3R?&5B0lv02o zuvG9oFWQim1~12(yf~e}a7Se2-eqCG<|68Y>3y>9zmKu2xTG1js68SC>5X!uWJxN8BhOJ(W}fz(lT8|U2F5etryVtnchpxbE^t}!oIG0d zv}qV${o)q^j>EV?kwK4x;FujC#1U|`%QBfv&gO4x*;RT;|H(Vu;jIARd&v}p>9UlF zu^omjuwi(f+B-?Fe(mf0`T2SA!WaBXU-tSC(AIO+myptcXQw`#3FBo=Xnq&mN82SL4)RK+;cx#&_N5{lH#%%IDeLI-pdGFE z8MM8*1%S|(QuBQFH6IVqZDQj+N0bh0*pGV4^1+0_?QVD5+t??S1J7aRLTcxnvh61B$T2et%H_Bd=-AQEzunWv% zMTkEemqRvek{zW0X?2~H70jJf4lM>WH}81>@QrVNU0Ol`114jjy9zrX$4Ueo=u-0>!__$*T1`b$Rvo zzVCsh0x5<6`OE(U>+8qlP0qT>0tuqs-5pw3oJ(z!I4yAXlUY3?arJfALotJu0=v`% z#J`Z+3DKs*^Z`fb3K3_Tgn*KIBXswB-1C#;lyKM>wsTI|O);rdmePPQv-;Mzzj?v$ z{LXKiPdxOE#+gGDSY6}jZ}WV_oN24IME2xEYkHjc(e4BZf(Vi%p7f-j$DQwd=ks=V zI<&f`6RcEyOy21Z`lF0G+O6{4)09@py5vnRQe{7lYw?~X88?1;I3mMq85x!IDzCJ{ zkm@MkFtOv+>SdL{b{M8Bg7LQ=sH83ehYa542A+@b@ec=zT)bg-~Qd-R|*On z>nDbefl1R1%8C$zuekCG2pBy8V3ft#g}JS`U&vVZEqJzxLzL9nBJXjJd)~|=umylg z$uqeYO;5d)vKe>9j>A!uh-TQLrPc~DvwGz#Ug>@6gu*5qmd39U>TxO$Ebn%?j=5xCOr$eik zquRCVL=x3y#jU5UljU>Qvm)z8nj}xE_dD!Ylj*RGL2$d%;bZ`SNy{t(+Zz-gecGW= zrV$fxT5$-AgaiUetgf!=P<^LT z#{EnSA>@Yd3&IvLGdxcapcB0P{vW-J`_cpoQ<9ncJP_O3hE9tr;M6zunyjYCii@p= zw4EuJ?CZoatoXa%`yS8FAEK-_D*KanT&2ZXEfczFX=#N3h*$sBU$bK7Q@uTQLv`C& z+R5n81M$UE+D$3NO6hQH%#uI;@sDKc)2#gD8UG;AoafQ@iA`qasCDe`QVu3k8Pr&n z(r(H`qm0AgMRDGZNE6fi@80-E1|X6l%ZB|}eM+m_v_muA7%TY3pX5({i9zbo?}HLv zZ867KD>AQ?dH(SJnT^=%Rs9TDpwlcF zs4cYb^5{E)4)dpJbKBi6TPs(Vjwbbg zb8~|M!ViA%{XUEp--!aK!>+NhGWBSklsRQ;Cd-gF%lhLZk#xawOb1yT%O7qfx z>6d>QH$CgDL*3mjt*)e7@{?lqDXnhqk@Ykj3lBOa;f8c1Jt?vJT6No2q2Fm`s>-UG zb-GqnJEW&mAC%5cCo`<@D9n@R`NQO(oYv-*0npSb5K}U*Osm1FQ244An^8x4ODW2% zq>ciidcBC20cZ>v#iNZRQOtoAn`o=;Dhm0ka0JN*Sjsb1d%0y|b9wh6#&=q62iq=j#A5 zgNYr^slD8Q75ldfY@FYT1el>?CjV7~K#%u3kUzp?Lj+Uy=bHx4c0 znyatcd)Bj`wW(E40m^`1OMr=d5}eSAks`&q%EUUnxn<0lEA+V37kR=*UrI0-U;5G) zyCCq!iH(_W%QQKiKxeg4p~k0COd{U&!225Yap7n2xWANtS5AFrPJi^McV^MF%9bVT zeWz38o=M2mfk2v6VfoEV?oUo=N#z>=fF%}%W)O^wm${Xp%tR!Pt*=MKX4fhhgGT-L zez;^TU=jeu_cGg%S`v^7%nHwZ`ZKQvfGk)z)Wdwlrpq0u+wCph*xclm)-sq`U32Yq zd$+yAPl%uUnI~Q-`!d=bD5YHZKVW$V7);8w?dkSV77bE#kNqEDtbX=dr#)PcLqQG) z8=CuSlg=om@YJU~6<1w(h0<-6491sF!j#2m+(k7_FS9nTmt1k#+h%3PWfmXWO;pj@*xha0>`MLf|WHfu0pm9fU0k8`CL^J1+C|MXEJ)f)@Q|5K?z1XZXMZAg>B(2Eo20Luni1 z;m;T!Yczvkz{hGBCptMV=Y795Zsu7lW^q$;x=Q7XoU0JY!QaTaldWPGlocj#|GNei z+Eu<}tj#itS;hjt)LgO{hChGd1pos|*h6%{Mn|wlSyoY83Ai3i41henOl#B!5V5vS zk_V|2K!p3=?|uM)p7vebCsosIHcqT-%%PaM&-}jk|Lgg43v=Qh-~1*3&<<6KA$xn; z@e#&?XBs66r6vKGV|CpCF&K=g@cmR8oHSBA|Ng+ca0lFTFqjDJew>SZeoQ#04Ct*j6LTzB2I{|x{nJSf(n*TUe) zuFCsck~y~Sn@{u=22=|eM?1y1&24Xc+i~iXN8C0WcfrVGVSC^M&KD>5`VjEUX3aQa zig-5>w;yRMJ^{+ztl5(w5&)SkFKcLR?-!&qMv@}@-5cL18X?J9wzN>cV7nG zqOsG5AC7vrO4{TD@`TR5Luv*O&`JctSB z-u-R>w1qfXmT#%!vc*lFnc8 zufD|cIdph;{kgu^7d{mA!HV#@*ZdXtO$P;yFjw-7ERZ?QMy*PJkgPi7savgie7p!kLyOH`aGz@HB!Z zt*;+rCHu_G>Y^WAbj5Nj6o2@#m%;Pq;E_OI_Q6tuS!ydX$M$wzC8vlK|I5aGGQ!i* zzmaV@`iXxN1CMb5%wd)?9h6tSZ5@B@Pi&C0APLNIHy_Mv!sqirN~^A z)t(#c>&&dwWtU#I{`%Ly&RCE&KYn1}#$=oWjh5N($Z;_=R3rH+kKjG+l%N=ny=j?X-QQ|WvRT!O|^Q^^VwHj{?(Pk;>6}U4xf3%yZ-8H_do)y zSleNR0e`G85K6%T$J#&80m}ev_zi_PHm&`3Wo1=tZk@pL(h5#Y&~d31Hp#{cZ8ew3 z_dRTGZ-NyQGpj4EzM9Xz=}k2V?)SmMhaf=q8QvTX><8@c(MR@gR)9&@jYN|S-01wt z6sHo8zSZP$*nH#+1!BSve)vP&{wHrQz?zl<=v{WFz0FyvYlle{>4TH9++3pVPKU^2 z?(Zq3NWmlF&^QKaia1@3yT>*bd!~VprDAx3u-DteoIi)Fue=&(-~4Qkd9vrvRgJ;4 zw8E>aYqYVk&O{{I?QMA|XoCBF%=x~)fMTBnQUD8gKsAox;dCiqs*1xb3P#G(1mMjJ#?N+E0 z$+oLI4f>6eEtOF?#!a=FcQCbPH0zr!e^qpnjT6TeGpo&G$NQFjpra|r*Dz@d>p;V? z;xfla?(U%;J)Xb_Z|qssD$3hoqqD~G;2gNNayA1^2P<+Zg26%i49g# zaibA338Gux>}>ICzy7PN0Fdu#@GSfAJc8`2$RUeOb>X&1WJMJNZ4+G|Nup@vJi{^W zN2l>obPxf>66f6R97w5yTU$%C-DQ_qWQa!JQq&T)+1_HsQb8F@F>~yf30+0y{0<0! z1$dyPNY`B`<4tbtsAMP&@IXvBa>g0}U~yrw(y`hc5xTFc1v$#{}Uys9))a-*9r;(LTQJ>+u!as_^F5e)ZeE$ zW|rlh&MJ5*BeUY12i8`Q|39zdCh%B9!uyf>YrW9ih}1+r;ADOJnpGj zvh9-KxfO;~_n4TaF*i+_dK8XE!NI#d<2$XY=p?PBWdeY{WIDcn0sx|v!u#-{y~)Ow zb*_nz-|fG#>6wV3w09!z`+)mDi6jW#@SNVs!J7O#jdR%U4)Su{^sd|84mVp%`zMKR zTj!pTieLZNulEH&*I$1O03dvz&k~+T5vq+J;2>Uum`QQu9GJAc4?^kWZUX}p25anB z1SsQgDTJpv+L~6vYpZJ!h%OR($dxSKrYl7kAiJnBfc5p`5Q4x`Iyw{wh1Z`chf5A; zqZ5N>dnGYzz4A1>W^Vovm|30NJ6Wp!t&BOLof5jQs);(^^Nc5j9) zN~U*hZ|`?rbR}%In25v~XPoK1`n9hCu@B$#006SzgRX}GCp>7T*F4ljQxCk227iyf zQ>jvgoKG1KK&Tmgc%E)Q<3oZS`+ zbCN+Zvx8w`Ad7)`rX)yPp@jz}C76MSKI~y{1c0MQk9bAPx}9TAu{F# z$||1wyyvy0lC+$%;f%^%aN$gXoviz330UVzgfq)BEf9W#=LyugPtTPDS0<{wtbS;`cdt1x=~HUq;>?|6w7QG$Evz_R(Y&{qa?W~r47a+ zl$Q%GxBw!`{zjSuZSk9p01!Pb!}J`PrY$)^|JNfOa_ihX-5IjqPc2hx%!SF-v380c zj2g7pjW2%ji)E2m$W`lg5H=}wd=dD+|NFaO zsbfG#AkQ`28!Sy=MB%mJo^BYhX!c~93PSJ!NGU;JJoxoWwA zIJ|mzpp4dHT(&<(dt$eTMs5k4E$a98xZmGnW>!D=!4EE4Y#j0~xbUkG1b6`YvZp}{ zlQugm5m^N-P+^evzZ_^0YgJW<+RiBn4_@lrDF(h^Fxd07YnT#v<^Opl{^9@qKS^_f z$av`u3$R~Svh=CjX|p@N8ix+i+S)QPgMRd*ADS(O1PPA(0@+57aJE>223gQLLdT$0 zng|*!0`NQ!1jgO(d9OnC$OC>xr7xe996xqk9hyJHpZWCXUh=sMF9ZqgjL9mt|8D+E#7XflX#WEymhZfE%XH0@*QIyR8qZkr>^c7+agBi~z5|4us!N7+6Zg{P{lSK4}{ z5}!=E9VeCOsVW(p!Z8X2kF!&$Wnvg}faYB+vKF;r3c1gkW02o>zw@0Z0SYXHW3}49 zTht$N;``)P1RJ+kdnPjata+HveD-s$)k}FS!D5_Dph$n%c1jM2G7exmwsQQvVx>F) z_dowW-gBPwoJ~N%_vXM#r|;I5Xce(tq6B)b*BqSoAz&bGo4fyruI0gd9MRBQ2f!mC zAWXo8=RWUw_}r&HtDOr4QIP=qd1W-;GNfQ}k#AX;GW?nI~h^(UW`UGqoj>$ENASsRLJm&-~JHmT0^%*B!eao+lsx^m~2HBNYOq96C6;5leUgF4|PzS%tGf$s+rq!xR@T zDuS^_oAlrJJOBQ;_L^&$;DJbpm8DghWTvNfx5M4t-GORd^n;6Dz*55ZeXS18x5{Y& z0>!?R!dO==a5litB0k{(I>CaY90wEmg4Q)QN+C2zUvK4I#1+Uy#*f(fK`pY|z* zm1#z9IUS`|06h(*$r^kvkh6m)YoKQ>sMc~(WWgPGH@>*Az)JRT{dLzG&Z#*Twij5B z{Rj{nWT%9oGq{k^0%iky%-Qmsr_(y$^B(tvVx~ENAqG7qG7*m3?@33_hEs-Mago|4 z)89&Ki4-ffzOg}Po^ccLrZ>M041z!Bqup-u<~*3H4SZ07g0*g)bI$F55?~xYa#R4ts0Pyp0eL|w zE9Q5+^(|*yapjNUF_{E4@u>t$HXvB@poP*KUmdE@-G!NW8;TrmdCaL6fgGu6?n)UQuG3GW2$(Hv;Z64AL9-OsC8EyVm#Z|BvM3d`1{4V?ROWc4=x*Fv zCvBN`PM%~i=zW0#jdYS7wLW;%vfIbc$oau<~5Dfi9Jz6G&y$c0n@D=4KRE3)0_>R|3xr$|#b zOuKtPq>N6rhgj&7yv$syTEq4yHI_}3k!?<*0;Xx2zeeIKYlG}ZVnKD z>PbY0EeT7smDmVtKn*(=84F^<78{8Tj=~1~D5WPa=bd+N+~IcT+(yv;)JH{(0>fk$QUez$Lo*0UWyiBRRSHPw02XQ2xLniZ ziQ6n%m8T(j+F{iEQHSI9FcI#lQckIA16#G;bauK!oo7oubH?!2xKddv?=+cqipe5J zX<3OA>U?LKq@_speL=Ul)h%y&@sBRKQc8v=+(lLx&n^ZGy~Cp9IGJz^4~@SHSs!h`>Mvg-fAjx)k-36x2JV>mZT>4j zCX@smE3@nTWk0iSV1w<-CXtA6n%Np|Ek05JRA0gO1$upjLx+5!q)t~}Mg7CDMR||% zJ>k>0zWoh#hdbN}O7`H-%^B*CFh?8Y62~K(blS#&2Cg^lssNCJl)&8FoX3{ekRf@- zO!Atr5mILaC1l+BCvRHjVZOC46sP%a|t z6_E+W7F(tVY=*|sF!Gdhz%u}_l$aNDAR%zu+x$diQ5#lHYpt0D=ZI#b5rJnwTLe82 z|F4()rg-SX9`<((-DbjoyvQ0zE-Z9t3YH6PKR4(lwi~+tYAd2}o?(Hu06;;%zMGPp z0QkOuloE5kK(E(RY3)49;VL;cqJZ`zz_aC%pK;`&LyLL~8 zrv5}agt(=w1DqmdSwhkM48Zq2tR7hd5sAZxSEF(-HH&AGi!7228^Ax0<*&PmP*>bXs#Tqbx8YXr*tLW57 z_#Oa(BS((ne&;{v#Y7|qV?)PPY1ZIiIIUyYqM$SD`n*={DlPEtS~zL&oXlL9l!FeQ z$LXg!9lFb|jaA^EDjaw7L@TKx@!Ua{iALcVhwib@k)k5cZu_orjuz|$l~?hLEI`Xk zE7Vt#Rli3kPoC7t6_f%)X~3dfZm$soWFjGoIjsY4*1%f`4-AIvD-a1h{b|2=B@jEE zSs8R*(F?*XWEN^W*0#VTYv{9L)tCIqPkiD91ft#U4w!+yRK^Nzow+D#FB=D$C{Apx ztO<}@cpM+J5B_R5H2Ocv+$5nnu?!t2xhMM|@&JGjA3g#XM2`Z7B5`6gYB6pG0R@Wz zSsuA$R)E4czWxmzyV(~`oMKaaazL`77Le5;AmL+aPClTm&OVgGpO3xvnri_N6zkIs5&0=Nry4}jhQn^; z;zJNNGLURXiGYDyO z>jbZ^t`R`^^rt=pW3X<2;pTi=H7X}-6L+Jm8@CXB7o_SoFms~y4yVE|E+ zX>W3@)&^7?2(G~qbRrx8@I4<=^#EpEf9!gB_RY=?(k%YzZC7l*YInQrI)ZBJI`F>t z{rgQ77`{^@xA+wQ8MD6`5eHuzQ1#~`+HO#k(a-|!XFdCwcL0F(<6CU?N6G%aa+cMT zDjnd);Y+qV+q}HIN~A#dIq$y91b~Eql$u=5h7<(W5jgaia~{C41s@D3p%VdVhhPCh z47iB^4?X~eX0wS$J>n7XBqFi3xnY!=GfyPO0aIyp5=2QRNGdf9Bwe2%* z(wtK7tZ0l?$w#i!@ilLsSzSx6i)r_lp5OL4q*klbtrVZ4q)aLR#6jdo7Y5KpcL`BK zjYddm9g)S!%6eW|OHmSl=ao@AtRlPLJUjX1QC}Fgv{x1w|MbpxT?a6PrvaG|?&B!x zOmhAkOPuR0vZk`(`U(xKgr_;M_rKqHAR$K9YbW7=nl`D8nvC-FN7(@!Hgpo0<<=52 zfVvX45FM0+ArT_>xPm^Mnr&nL`Z6Ns^s^ z8BTm+0tx*AQS8mimRE0DMll=9J>mTO-ycMSxay(%KYxLa-s};S$xtYmV{W|JW!Xr93>6VQn9NB-v?+_XG711OgFS`4ULOqLt^f4S zC%aR5naDY#WYoN0N9yCT>2Nh>&>I(2%mVeTsEkbDGD1g^<7*Z`TNx=AnlD`!i#z0} zUtZRFyKTyqHrDJjhejQl;eMKg)apbyjwGF;nn6H{M1f%JO|mJXhuxFrXk_m#U;5Ef z#BpPwj0~16K@boG4QkAFv6Xc2cL3%%8L`I>blP`J4yWVvRR)As&55mNt)5te+noBk!fEXdWpCO9C zVVhHdAdJ_d4eG&Q4W?OvJOL#om;p}+kRY6U?zs>6*0;WOL1Qr(d2}|e5Rp*Kk^soQ zg69huraf)p#I7|crQ%#`F|ut7PZ{0umzI+uX$29DuSq@(+k?1MZXf`B{x{(=^P{daXX0TL%0)FKMzkD?SEHAB2ML((p&j7HweZpW8 zR_}cGyKjAqo8NL7#PB@NSfT-`tVP*rNw&7xA;qy^Qj>)N7<<1HmO~KW5rGARgnolZ z0>1A>5#+x4&2N78agTk>M|XC+W4C!8%fJu5{{sLzwm9He&UuhVZy!YVLpE(>xXcO^ zSytVho05Gf01r6-fiD3VM-H!1GhmQ7j-=pZ#>gy7GVGg65fKsa zWZ1ZOo-k1v7%aCQQvDvhInof_9toQYfC`c`KUkytyNzUe)k=Tj?bdcQM4-tesPI7jvQIOVM-F;e-pKIu@Ld7U#p z(>6e(HbqsRIP3@M!=E1nO|qFUy9vYKuzN=($cF!J;5A{_!9G;akjraPbun{FC_H+L>waBj^Ym0KLX_$O#CXrU-h1m`eEL(b9v4Js$*s^pA zW?~#ihUsb=kSLoX`^1TLR*IR3#Gn89%fX5uK_JD^sS$w&Hot7b1hUDz5{Kc|)@!K9 z6*c;t6#x%l!zurl_x;<|0I+^sZ+fi{K%V*^R7sm7hRx5eRVSm_Wl2SUq@%M%J_3h7%T}zfMCVMKzqy# zy>&>M?j;ineOA0*l=-h3{9y2Vwf_%$$6mk3l9|8o zg)cJz!OUm|0W|~NR?Q%wu#sKHS^2SjJgR-6nHBWAzw>*hQ6%@I9B4~sW~CDC%FL{! z^WFY-R*B!7$FaRp+3(HgQud?gKJ#h6c&+UN_oq2TM~3|biw$CCEX>Uj0MYNAVpfV} zqTnhjl8pbhb$4_}v0`&cR8J-K=RMWq-rgQ7R($m68Gc$l!o|g?9%0H@95xna>BlYm zA?J61#P9yjZ#(@O>r!7zmdvao9k2~4nWbrDCF6FNGSU@&53@A?_WF{0lKIVVelr6Q zS-L-~KVhShYpf3YvAV5)4=FHnRI|~bMx!Mb7efjTHO1m$K#fL|8jIP_J`{u$q(jDE)U~bem@b3e|q=3<&%H@Nsv;( z^L=B19<&5n+hCgAnb~|sgVt%2^Gqij@d#2WEO_&t@CD1>aLeIAwrW`+BJtCYf81yO z{rw*}9{~EY51)Lnun{OIZa>?0iVYFUX@8seB4WE@Fmi>x3~uBOis&^2psyr+K>!h+ z`jn^s_+9UQ_pQ04CAtwb$%Be$L#?G{g6yNy?b4-}{#f4P7B_=b49~O4KSUgE%~;!$ z2Im3WGbatzIw%`4qu1NR{QLrb{uh4XhyU{4_uhVaWo2J{@q;eSMo5;A83autR#6Bw zAp|iqNySp1MA*{})@^M(H*HA6NMDE?OSRBS2TkWtHVrr%dItm~_jP=6DKR%cFIXuG zn@il@-mJ8(XVF5|3Y(g8&k{C;dDv)@RG>K@yltX4^c6@zK(O$b7#>TteX=dno@r0C z!k|fv>f#QMYl%y1|1uWAs-@^DK#_Ksmi2O4?=3sV!e)y)?a{Qeytt(qgrv+uHwm0` z5KkW_nn)$DoEQX;QKw!@E0SU~DIh=d9yF66&)gYHE#k=;PgJ(cT=y?AJKKP*X?tQPPTR@@@ z&vj%Hf`oE2i2zRM3P(sptaE)CIgvULMY$jVtSH+2*ldvuKnM+H8H9g&*Sl_U?|a?r zDV7u63YRD=Rco4O`cAja?&9WwF~6_?2JoKuz6XlP)XM-niiWAR7 z-!ec%@H`(1jORV)+2=5WssPX9L=Gw*cXf0bTxX_kx6SS%u)MMY0C?4_{u0DWr}tJ2 z743NqCpykR6YYGpkE!OTGg}8uvyHRYX^%p9!beZ`AV}b4fABH|0PXEf&T6Z&9`dsW zo&jLH(>9~hi&EBicG|pqvaPnA=nW@#+iGWbOLb13=)vFVyWlwtThweU2|xpfolcvB zutlA2o2!`T^TgC0cAu5B_OB8=myOF`*0HzhYpaxLy?1z`wd`@52RsA$aZ9_~;cllI z{m&x@4Lkz{IM9W8%8ii9cwwHlT$u{R_dV+M`!316RT5WqI3yXDv$ zOEsY3G8jY|=Q|r~p7;_2CImgKtj=pL{f$A+|P8478`U7Ur>B4x7iPZH;y< zR*l7wc6K`)1T6wH)O^?9-Bdjy;lv;UJRjWeF*jQaBs>pWCw9!-5)ugykbMXtu$@R( zUOv%JKnyXpjYo2xJ?QJ}v`?L*awOU@ul0A_liEr3->~;A1x?y2VPWB{kej{Df@sh4`1)(ER;x#j0xSWt8T8_|jW-_F z_L=~b96RRegnTsE{_fKv%QQz0g+#*KqCg5bdb6_+9l!dTUTHdq<7`Ppf(;8>SU_eh zqS`N#wDsL&Vnx@nlcRPa0z$Br=XEK~M8GK~dx=wj1s5_>+{TbuOm zcfD6W=}AAULnivrU>ODg+v5rrT2I()V39{GrO zeB@&vd* zYvUW={ld@u@RA>0*6Vk78GQ)ynFP@9?{To&pzg*_YPqu9Umz7B;1)3GHbNr;+ zyNnzZkeD@RRZ;$A61-t@;HjU}CSfx(f*Kvt8ewr~n#K$|$XN%2mE~0eAfEn=r(Mg; zEc?dcD9xLYIr2>#baHc?!IZv0$w5a#$x8b%Ddq=1_(A^{fLL8yrQlFG=axGAIJ3=i zIGI?-$bqtkEm~dG?aa(Le*73SGxvKv-9}QeG|pX84YsYa-gi;MCpyCZ&hce~PKELJ z_E-D-zGCM79y2qL;*dA&XECEK01};chnbnB)d8uU+WOi{jkR-IT*y>aQ;9lBeNRs+ zRz2;a__U`z^~$2pkAkxn;deiUs_B)eCZRoh4Dx(0(IuA-#N;0cHYyZu` zaeM0INtS)dAO7%17=UOw^IzV26shkhXW~hWds~J>#_11$xX(F1^S-Zs>^NWbHRh{6 z&wRxN%ol%-`I{fvn6YxNX0(h;JxjAM=qq&q>?t|#O zX6L!E5zw$cnT55lVH{w`an3x5eN?tjD{uc9(1C;UGt>fo(2sjusUkJ>B-+Cst7cIW z4zC@i^^J8BKC#?Wiix2l!E-VNC)pcp{_9#tqtw%BZf%h@S|@6G=R4kor$6JV9y6b{kJKsTgzfo`3@JnATh(694vZ?~_0MGf#jrrw+!x z8S94|Rt>S0eSeK3#fq%d3JQ=C!k@!G{nJ0;X-|9FP#Q;;%#yC6G5gux^7CKQlhLAO*=1P#z$F<)A6LySt=V z6>9H1R-sw7v=MpY+}q#nE+2Z^SH6DzbvwB9@(uWXAE$b|2pR$A<_}?YWdW;e3y`9R z?_T-?eCkv0+k4l){o|dl-Ms1|U=M)yhp4h#2TgwuqEy|+lkT%BPZ*}*keAn*oR{u$ z5TsTL$FNSUN;zGQ>BV61fZmZ@Vpch$;O!u##hq;!ip^U85Z>@kWENv zKx<+{V_S^$7Dy=xPk1=7wT{)5BLd76HkVkjLN_rfEj3%THDaP7BJrEQ@tg8huYQ$@ z{9$)ufU6tsXZBN#`J8PS4c5WlC;L74{v3{PtmDYqkx~G9mZcShw4Gz&AVGA?Tijyx z(n~Hot|>YafKI5w1{M<$6g$yd6oWhwjYT$*H;4&}bi@>?6oe33HwZX&s*m}_1^mU! zUyk4Vo!=R&z0>0^>xHt)hEp?Y}t7_t8i(7 zNC>n~>~bq;P@gruh9J=09X+o#f+p>h*l>&ss`65rraeDT`~5d0NyZLO%7LdQ}uQN-}gZvZRF>1CbK; znWY^N9sd{cZw&My;gg4R?{e<_zWd+b`9h-+!djrqNV1Kp+;ss#CG?DBsw(r&cFZE-u>@*AAIdwm*dbOA1r(bDWQ5wbIS?n zvpG$X0=+&#oDyg*HPCD=;qXxag7KB_d=CHezJI;&6JPk?Zy(>f;5`y*7EGyQQ5;8}@`;OZ!CfjE%wX)y#2`I4A$Hu}V{PVy53x45=Px6>qg^dQ; zc&oD@bvoB9HUZUamK+|3y*j#gk6k<(*D{E!48?0x!EliUywe9(hmdco&D`+tX7MGsos*Sz!M z^2#c0ZJwa}+~>Z}|NI4?dm}4`=V`}OYsk=TYunm3k%&%}h;$ltp#Vh$n>1ecl+;uY zsUVr)`-HpQ_3jV-#y7w5=^|F>H2o<%#wJ1aSX(|ix3P6xKIw5U_~PsT_v`QZm9Jh6 zULask0dowmr=!^r0gwuk1W5LvXilI0NDoT(AQb>1usA=5<)Z;sS^-#`!q>n1-+1?X z{^q?O`t-YgaS!c1KvO(|<{Wj~J-uc$**FpN^Ldez=MkOQ+ET5sg+804ABgc+41kPe0_en-F~r^rGmf-VHrADpXn6Xi#_eBz4-FTE??Wl2+sX3)r62@>&8Q`#7&e`^g3!@T1ONq1;b0vnS{iVzDK!#O zGUj|AU-{bCaPNEG(_?0})NImLyS_TsWnf9`Z`Y_mo==Ccdghlt_06k0Czf#R>UALS zA@)27>Vbr5LuRnj#!7EtU#F+-l51AK+0Mu4MhErct;dgnU)$0z;; zfA{vk|HZ4W`{q0C261j~f%o?GoIK3eCJwb%pQTQp!U5xi^i2AO9CUg`)nhyFY4@mm zZ^&}cb;?TRiByt4BU2S4b~lmdKDTmL-R3~|gGxXkkXdNIZr_L6<+XbzlxW=?0-u^+U{$?vu=;!P1)JmVIq$|^q>C;Apuqj z?6Ibls03q!0kEzWu~8-we`kFM*;w4cjvjd)gb;Yj)1C|lP!>D``I0siog>CwU~?s) zXoR%7yg~rsf4<;O z+6qA@gdq?lE90Q3EAROpwOY$Ku~9rTVOb0GG^`%UBlG3f5_Nal=xNS60}8!f4|D$f zfVH1tsXm&6u7R_Nx#+O%t+_o!LbtWy$@h9a2!9TqApFBWya~^L{&Ophk}W(Yd|UjdV5`JoVGFF!6z;k_k;oG0Zo8J1W7hdqykN<5?oz%}Vkx!OZ zR;kzPAsN4Tm?d)9nlMWD?e}F9na#RX5lt(P)I|&2?*o)L?VJ&sY#k}*Pe-9(v2oz? zk5?(~XNO+L4hsL3X z=&=M#Ra>3MnMBG=d_@Pln|OtY#KOWN@9uULgTO4|S&u*ys*c!jT!p1@aFoW`ZGP?5 z&tS4q7-9~EHf$S*KB07`Lh{j95-bV+96%6w#3LW}j*orx6VJ3@DUWjr5s9;Ja@PE% zS6sfU>CYOBdCX z0G*mhr@~{;G~LQGhN~XuL?r(9Z{HyQ$NzW%SSsyY3YRmZ4Bwm~NJo2#g1FcoFneAi zJ>pCnPQegBU`+D9}CJ_;ty@TcAE0!QK5og$o_~t!U@N64%9}vE_OGv#j^t}Zr zCY(IA3y*-s#Sm*ZnaAqkCU$l<@Ne(?0RHVm?|J8cU;LTp^`InUPcrv2H?(1+Nu7-B zs)gsJW{Z-McCht<7(!34PM!Kt^wu`?l@FQZjhTM{XQ(ZiB;h_g8#KmGWh{=^4A_<=_(HCwz{z^nft z?nqPlfQt?4?hZOo5|Nl+Xzpl2XxLuP22dD5dEkigd7eo#2~dJu>Br z?BWAx&g#>kQ>u~*3<=Mh!{5B&4fvH8zOdGlj8UPQT*zmd1 zFXR!|;kd{~XxJicK|c|R<>q1UtDpMu-nBnGiDR34nDZpy^}(0}vD8!z(^BZ`(0>eo zMIR*f=Mp|hCo`A0c^miGpwGcPIX^8&3 z#HH7K6(9f1f9`$g6Yo6xM?e1II&9{3#k{<-NSh~ibG5@PIc)2re9lFoN=n)Jzbddc zYra^gv)qu{CKW)tyUH6nAG%(8K_U0g{I1 z{ZQL^viU?A%b1S1@cGYw77uvf180WDkw@wV@5{@pw6%320z~W_7c0dOz8C;KKn$KW zci0IaNXFD?2+#~f$u$(k9CxDF!BB%M?ON!A(1XIf@JJAemBUA{e(d^zzDm%sHqHFhSYo!-C(0O}UD7HG5G0#m%HGMWdFi7Ra= zG|Upj9{&DK{|~?X{9npFZfAAgZ!Cnu^N72>4!93`$Lqf=?{~L*;+x;R9R55Y*aslN zLV|l9SO^GviW8+iH=8`umbziJjw`n9#@1I8!kh>xHXBLSb0WiX{Q*Ft&wa=~u;2w) zS)Rw*;WZqRz}4H^c;5%#+k5-_{_@X{?Oyt4z^(*={RYJR+?;l9+&iQM39w`V!K$;< z=0?M#ot-#rUZc?@4}yr8x4U{{KWZJSYVMmAYQZQ;J;7QGsk=KY?(q%FqFSWBRYdd{ zPFFe@dX*(3Ezn8pOx^+63|rK$fvYsD6FX4fKy=$Yt^PzLHr6-f%F41n+7}|GUqC^? za8Dg)LL|XZhFCZxrHn#ud2>Q=pGj2+qKCCz-GgjgooY3gX}i72L?kSTmOX|y2LJ*r z0rprsiY3CLSXdeXXw%^78y#!qwZB@cqy|rl2%gCRc;!`B;AS_wxhDuLRrVlXYBq_5 zkM{N^{q^f#D}U`*UWmP3AHMJD40%ooqEKpVtec2ciLE7_D84SLnZ91Jj#6SONhEyY zl8JeMgL$gq>KX~6r$6%<$KU>sZ(Ehz2S_`fMz($-V(vTu9l#C%1jum9Vdz;(0fIE2 zo{bXe*x?Ka#@yS(d{3g!fKmb; zDShhW381fOEqaWYh#XKbzMAlaWy@6RWnzTGFcw=sgMEzq(DkQ2{ul* z4DJc^Xb+GA4R0R7@*$jYMhNmHF8tc(@xBke>mC33nSXm>A6spJaA^Jz@1Bb1zTi-U zx+ixyTnwo%$v?T*?u}}fNk)+=Dpu#G4VLPDf8OWc&;WZ885K2Z)?{Q9j^xp1-UX`; z@C*Qgd2FxHx}=Ul(F}qCkdOuR{?Dsk4Mlp<)sYgc<;sgMBI_t)?V*gphGgfr7=i10#Ky6 zmYzNfunu?33>ED?OjHFpK*@lncy~e(428j`*1~9SA|iM&xzh+Z`>eC^k&k{vs$>@@ zWUJlgdBdB3+ne9YB#_Z-4XK)=u_LLI@u{+1LF`1Q2O|H`i&9 zDB?g90W9<=WoB)4j;zxLhU-i?=t*FqaR|5iiQC-NCIreWc_J>teJ!jn02eTy2=obR2yJ1`E^v z9zl_WA_KlP85&9$uncHgjgkPdrhRx4o)GZo2z%(`*s*PV`@gTj#oyn=op1dhy!!Y5 z@#&xYmn$~^@K@g{Z+qrL{%dcKeIgQch^P^g*FC8z8$GEo=TgBO2^%4Wjex?2o?i<) zqF1EPdBo~`*?Bxqgp52g&N`B(z4AW)h6tY1B5S3!=|KX|K#4GR<0x}pFg5HQ?QVxh zDQa4Xmm=?ou4%;OEnDwV_l?>NWbyzLb^P-`{bRC@FvbB~Tl*YgW6shKqzHh;FL3Dv z@o~I@HhlWNPCW2`UipgK5EHGhZ}8IcN~!V}kzsl>D&$>O`)4`sl$BsnzlWI>38KY? zMFBw&sb#?Ysc2e@lb4nwbzg9glIuPmB6FVlF9Sv<$v)PIxHOnf`lHI zl3_PjfTmIj25eVq?lVl>XU&ynf(eJBxgPB4jTK7?$&8o(>7QL$w2WB`S};bawIb5) zb~soJ2!e2`+u_C5JTZfQ`ROk{cITVk2A5rW3=5uT{69Pml8Heq!9IhP2ZDk$eh50m zJsB&s0n8?YCRl-m*)&Q9Jcd$w!^p%YX0#4}2NN$^SQ?eyH>0m~AK4V^42C~Xm=las zJ&7M*avWd(+EqAlOyXxB`4qhGZ6Ch>d;aFX_MY&7=l-L2sqc+Nu*bL0-sdrL}gn)HroY0ARByEG& z?XWw{uA|eqs z8oar+r99u)?M7a-MTsMA5C*yd_J}#AKJ4Rl7z;Ga;%(DF6OoR@LIR3=Ao4*VJnAuz zdf!Jr{E;WR{MWRjG9nTuwzlMQvx&Wa557OIi$#F8%NcM?nh&q{UmO88%XcQ2Bp;@> z)-MhO$eseg=+0J-u95JEd^Etg4wMGU;MEp0WDt7FVG|v~B_Cb=-_ZVOSUolu{UkDN&w4s_I zA<*ylaLAv-88|%34q%Ey3pMnPLkI-ZAt9mn1VT-~`H=vjgcKkY z(+sw8!Tmk8-BsDWXXf|E%(;8DZzb)nR^=Hc!B5h*yZ5vy-+W&m5#0tA;x#Q!q+Lu9Yl#Wp}dtmQmUR3`}lUVqN(>krdY3wK&;GM#Gel^_yEVsXtp&Up!uMWYTWi#%xQ!*sVs6Z%Qz{}{UtA=g zB-CuB@XjMd^)oBQs}Q?7JMG&~IcM5w`FRyeI`O2QHS3oK$ga!Y4-gyt- zyLTUywLmjiYh@O{^!YDjxcKNeXCG?VlBkH6v4_CE2P7+NJG}==*%z;m0q+T}Kqyzn zAtJogT%qYbli9a#)N8V#&_oYwEbcFBK@hgnd7j8!L%C9=lJ$qoFU(1V{e16x-v&!S z5QZ@n1AuXGTfoF*jijX6YM4B=V_oM&prGnE3RiGo3JM1a44*h0fBXsiR$41GHNAJk zd$=2%X8@=*=D4vmZ{;=6EC2l5=2)qW`phCmnc>8N5VT=3vTJ$`@ZgbUo;6bs%^KUA ziAV;S0s!(>U5Nmf>Z*8S_p0hpq4>A zTDbg@>+sVbU5WZa0Z)0t3yRokOd!djAxq2itwEgU_0FYA zsk~;BSHH5~-5jCy*4jZLSDln;8&s1wTj8uVR2g!u$2NNXTABZD-+4RtoZg-1%FvzX zezV|*s`CsCAp^aU?R0drk!|<&^+T`Kt5u3+YAiLhsZZ;Q`VV<+jG@dUL%ykNwRL2m zEnBUBwFVn<2om6kP+wd|wNx6IjicA7zErA^BU%?zzm<{wX0zx-jqe02u9D5P?WQ;u5e5nRw-@X3k3?1HfVX4s+M- zzYe#*{TU<0*zZ*do$yY!gA6GLnMBpPYHd z6`%g}XKuJ}ddgjM-TpA>Js!k%3q9D>-hDKC-~gR|>svkKhd=n?7Zp;#U=}1^p$6je z)P?~;9Fisp}+pmA71$H_Y>5YM1;%LQK~Pj>|FI^m!Ii} zf(?3R2Um^`ioa9Pao>cYOMP{r7)=;83Ep+bZi;0xgv* zG@2xsou4BSVGbhi++kYYkMhpQjVqW{m=L7Q3#3?2Jq1-Xd#>#%aUvCtG?_X>h-MZ##e*`dIyvDFUhq3Qr{ z);4S+92B76SgcEDd842A>c#oqt8kGcb>xux=&LM!hC8bF)&N+#^Kf zoOi^g$d_fLQk7t5F&rivRT)dxz?a>8w58e=!hx*(OwPfpI|b)R8%Q$`T|mM(_6En| zlb`x@2tnB|COR4lIRpVguDR|Sb2{2oUivPQO`^8*oU!5BwY7hC)>J4EgQA$1og@Gp zZg>0J0sw~TJZ~qvz#z6zuSxzM0YFHV>-Xg^eH92E-l_>~RRDqpLrxsB0LolEAuT#- zlc&qptG-6(Pyi#O0U+2bkNeS$T=v%>pM33PCf)!S*|k zEK?wenl@r0NRm?#)#?sQP-sYCP+%nO)d>fNmtdhLD@&3WkW0ZLfEN%Y2*H7nKwLos z-w_8l-a^VvTzT~s`039s#Gc~uIQLZ_e9|Z1{k^5z-1rgSCn~t;JOjvcEe&MdoS(JE zx|fE<2(#L(^W1B5WwVLPpY0BJc=Xn^cV_WUq_KRCz>#^Owe|OrhS0d~OV4jbH zc5`-mE6q8gR*TtrM%oe-3W=5**9n!nCZTg`85vixWmd#+mgInuk)mt0mTAwPX_=YX zf5_hNVU#5|%0xt-Xh9H31TI_((tTk4pQiVXnJuG_Xb{%ZLsgz1I zIyQ>gxp{;TL_}S|z-h+24v=wZQ@bG(#)e`HCv7B9JNRW%yl;TQh0-*@JCE0#^E$lY z_2=wbF{Dm-5un-U&wt=M;l{_@62HA*2EODF!U`Y`&JqL=h9I#NF@wdmlK@UL$zqPN zNCX(gJr3Lj}t36~2H8mobcuh#CCD6BrB#<3%ri0Ruok&U5~rb!GsznoegeTMzfy z`+GEGy}2G6#`aDf;a!pdA_yUX4EHPqcu+P#LePv=JCTg+G>@Rz?5e5#kE2Ysj+_ID z7WO&Syx7~{aW0NN_LwTUHlU|buSvb8j_L*YFa5~o?QFV z$THOC+(-W*kUE46!3~c+W)E|q>Am|jW0hOCrBdY}=UMM__pxd;YBKChOYhvSgMDtT z(UAQIW+a3lMC5*T;coyauX6~&ouZH7@E+jE)-sIq4?OFrkbu;o(aka3>- z^$!L;b9-%yt*>I)+FT`cr!_0`pWo|V)-9Xg&;E9(Q`7Zh8}6bTEy9H!7HQ{u&rhgf9JZUyT4p@8eOya7XIqG{XU`M?f~TH+Hj#5+YbNuKG7U35ob_L{bDC(o z+j@|Y9k}|S9Uf4a0f9gP036PF-8pbxkE-eE^*NzCzcX-fiEpd$a)WM!{qqj5zaC1Z z3Czsy$I(aLa12E7-a`W&*(Yq6ILuKV;Bi%`h|a>BQ)?0;20Kk)8aiuyz6E?p$OJxW2zM1dzra!$`MTuVg{1<)=Af(K2 z-a=cuGUo)sCQS`n-|V!qKn9aB$Jt2~RAC6G>Kt04v{WubBrp-VjaUx0+bX3J)pCwb zZEi_`asRrP{>8ly%(T!7DTG?emf=V#;>>Y&P6I@6taz|NnhcXqHh)bqP&v$j+$4p- z>2^l~VoM=Vdm0KcNbyM4v}tsqP8AH}a}`QWTCuY#5;H54N_3@7+=ayM^f%g*H*#7leI#jy!SVs@ z-2O6%f3n$q_xhN3>oPk# zW3*V#U3lR|ND3aI*@7MmX0g_b$dv#9u@sqU%(b5uABRE(GEI&|o5Yerf;-;fb}uI9 zs2fhCvJJsfFV^oySRLZ{#x+waSEyc}V-eviuK8W__+yXV0|3q`N3G&t+u7dL9Lqqa zSV%b@>Fg@ALUOD@x0xH9;$fK;dXqB27YLXB?sw{%6S>B+@&X5D2^{GA|NVXQ0@^GB z5&OCNzb^N)WEg0IHLq%ymHq3~%_ahKfC%FWXFMJNaP4*b831Yv^D>{C-<#bddfm+X zfr)MAde*W7`*rR9>v>P{<9PSMn((WzvnviqFZU5>y(_Mhu-H2Upnsa!*Ru>Gw^VrP|$!b^?*rl ztMVcP#Kx9kK|cY2S5%XbI?2Y7t=!MbSfqi9QFU_A$6cJq$Y=qDyuH>NqRA=$wgfQi zdjKlP!X%d2juil<)N^?hyhz5F;;ipM;1HbhM~iq!P>yP!I0RBWwolvy08Ex9JfI1q zMy^^@w7@8Twry2k$*`+Fo6Xv88-A2}{+qx5@S7n*2lgL0=$dOo=92Aaj5o7T7Y4ZhYi&|- z&E)61u*fBsUAAz-3CADRV#TJ0kY*)cmzAXrGa(UH1FZ6!^H(VmZef{VilQ6(;+PFf zyhp3m!m&3v3V;9p4~6_&OjITa06zNRkJLeg#5-u?V|+&LE7}F=Uz}ZUI}07LCnDZ> zr3FL`M?ir+h#ZbP=>$0M#W_cNs?~$m@n#O^t#BBItw>lcS14bR3nKK*uYUssLMx=k z)|*#nWKgHSUv#O&bL+Q9_9dLgHSB)gC6=I4*+YCaWsac)R|v{-NvI_s!(U6 zVtiiW6Y?@X`r4PA^MFgPT7u9rDADFvVS-CIn~p-z`vYyhjI?suU9jx4!aE2;4uEgN zqsX|;32@$LyCAdfdocX8@z((q)Vb!Myvy2Y|%4VIgs4JI0hSdzU69N^0k%F57fRt5sHrVxcW)JgcTjx2~%lhax&xh>*bza9#P~V^y${qCb{Wufy8zHyxTy{epSjY~Qn5D5i6|y|%8a*gvI6l}v?bk~7PFi9HZhq>? zn3$XrBIou_O#s(GD8H}zex+3hXqh(a)N~u;HRZa8v`~AJeJ^dGeAlIHC?b6E#g|-j z``g_5z5>7}9x100X92)O8UVxwJ_0(b0~xwFYif$Ikmh^|aAYt}f%A&@^hp5>!P!rJ zGM@b8r_4nt2eU#p-v7Z5RD=z#Lpeqe{Up1N=p3}2wa5$-K|xVo9QI6R#Xn1;Jk{h9 z-0Qyg{yc%;3ffqhALDXqVqmcDAm!Kf)^Vv^$wZAd=h(MDW1(J?IK+ER+~j31fBB;Y zj6#xtS@EzDZyOs%O*hXlz|#Id7X?;y%-AxV1f|8(u^`;*p7%zXkKz!C)j{C=(B{Q>Q!%=m0>=@Y~FgG&Bf+us9$W*x5eQt#_c) zdRZUc=J~K4pq}&qwvnCTP~*5>&o}DV4q{I0IQu$WouG}L zx6$Lfq3bhVV_d-bYsycWwICy%wjGNz(vv;@j5EHb@K}Y(wf(Z3Glq6Kv7H``SV~y7 z3&`9wqKsOLpydAm@UC~f-E?gAT`H9cDpjiFb0b`9)MYCgTXwn{P$`u*?AngBj4|6- zE*3RECc;1Z@lU^gpZnbRLm|LKPxRo=8kb@Q*?uEU>msA6lNc7o-&cRO!0?#R3?>=P z9W#)U;6oq!U}bu0uMm-Ac#MsV3K6-*rAC(Fqg4dr`OhN|bCx|@^{MfiMIAm-@6McZ zAZ?}~0FQh0A3vOh>A->2!XBk^h3bv@ZDopBZyh)4HR)yjR_ze#HSrMpNcJDtFCxM} z_`&zTkfusu7n|Gy1+XZiLnh_{LohHI)FvxCC_4wa_L!?{2MCa+DGG%GPB{7Go2MbA z>Allkud^46nW|p*zi!s~+Qw2t>h&f@Q5<>JGhaA<^;LBcv5EaLm@N_P1b~X6z-d!z z200Q9D4b=M#rW`Jz(=vhBPmRNupMw11*9{0hAkj)une(@r35ep%GIq|Z_R20Rw0mJ zLt--DaOV8`Y;T1S7%KiE5~bOly_Y)i>;$p3l3fa<0v1*!6+JY?9YqA~LgYC*tbo-q3=5goTi1`1g zf>$h6NJRMS|Nd`hKjDdIUO_Oa;h6S{ncN(;1&d_fBcxobiH?tdQG}z?)$OzpkG^Mk<&OHMuW)8f91TdXx7*fm;quBjXu2H zZ*3#g(tbM(7LgomNB<4PfYZic9|J4_p7g{segY7>L9e#c$~i9TvNf|JIC{Y|Ub|EV z;hJmbL5Wvr3IWQg%&O*R*m{bwd!#544}d@%lpO=4-0A?pVwrre6*>-pQ+604hDx$1 zURmYSV=+z_R)8u9W&uvjOh^EO5i$x;2H0sP+Y%RwaSpbA)X`=MdQBwl=0=x7OjyOS zQ+Hng*Bk*k59bOc0B9`heo`!MzPh)b^|QbB%673))^ayzHS><jm%>jcA*1>qH_Iq>^;YS-5uCD8QcgYNIq{*Ilir2bkVB-vYuYtf; zZwUGC(@KNDi9GFTXU`D;B#F|Qk+ZUIJZR8%ypaFbEc32A-0bMwiOT3Y@;LY0zZPMk zSc>j;U0Y?1dQG;2-KEVlKAg{5w0SEc{12b{^fAwQ?(+nmAaDx15E!4WHGPtbYLFw# zNa+AdEEO2{Gl0yIrmX5&*Yo_u6A?=gn{Y%clpiah22rJ_N+pkQp7i(1vCaOz^|C=L$@n zP{mbZAA@E_&wA3I;-K|zw$)8ld8;qA_s_aDvyR~g_kHSHoCZsp@!HR2bES15iUgvwIHJnbmE~MgkoIHV!~5FA9oSfW=REW zmKBMVks4fr96-UKReEhAlG}5ERDx!!3Dz8H|6?Hb7%&JNg5t3~qm>Grr(%A9lsCIS zIhB6qd@p-=IoB-R%X@B=wLDa^n(N)0UU9;mzCpLw?X<|qZb=mGCUZb{SV6yom4A=0 z-V<@~<~(d%a6M~u>^`x(eYZ|*&w4g!=6u*2LjHSnF2L%ZDM}dl*hfD$y|latkG3)y zxhRZ+b}2Cr<%pYn{2hSgGSB)?`;d+hj~Bk^MIh&7W@ctb7FvC=A^lJk^=jQVj$Y?L zug{yPPSR+hAn$qiyOLMD@>OsYRF+#(b(lFrO=c(@5|}td=3c8A?pCSazP6`4WhtaIimYn)0=@SZ1i5U2lmTIQjq`t@9%#g z*w`~i*h^e&tPl>Ri7+fMENf~G9|(+KSPykP!B#a&3J?i6;_$r?5l&(mDQCl=5M0YT z_uopXOvOr-#>y2MD<|X~(Hmd=;q!d6jQN=clEfQD9}tj&j6XSml)lbG@yO5t5~!#lbqtsH7*nl7Rf85g3ZBY#Z#Lwt0~-wF@1Pl3~b>o zS!*Hb7a^x(<|An}lV+k38wsuHTcFqCoOsnUbg$$SQ4(=3Q z|3K>2sokq}%Pj8(Jk@4P)4=NQ(YXK@W@n@&O3-x9>;4h~R>eEYl`<*sCr94l{6Vco z87?qB-{2pXF@g6;(-wpUXP^DFWr1Mt^j?ZM-$AXH#orlx)i#;2?E0QV#%*`Iw)urQ zS#CB(MEI}X_?P~}ANd3TfSfkgWrwpR7WGgDbR4oAbe4e5#xe_dG`H1e^016Fsbeux zD1f<%7ryZMxa-~S@do7UkPaZ@yj73F zGJd2PRF;50d(Lwz0I;&MI{e;^9r0UTNvc#T0Dv!j?#qZm8(gEh&+4>bm zR6}yCnN(fR4Dqm_m$i3+1dn{!BmXC`(DYu-eCLIXDy7mYhLAy>igE{ZucK!WwMJbQ zYYXC?!%D-^ElxQ7CXc%Ry>Rhw=P;6tfRO@TASpotX(rbR;29(awXpjj2&U?%G<6!_ zixHI=)P(?Nfn|heaO5l(ZB7h!A7kUIJqZHE{EMX&QWq;MqPHGSQ6$U|Ccu%Z5XPbq zn`0be@M+K|Th9_9Ru&Qw%sJ0F^5VQw@^xy$uVcNOxocG&dWB>FowRwwVd>gRM|!O@ zrax)w{=esTW!K}`RVr83t-y8KuzaM2>p6U6*GqY?W$VdOZdR6gGg6*;Iq_j!%VsTc zL_t6-AsBOT8xCL;3GM!{n30k8&rAo0S!@}Z_$mQ7G~s-LG-ZsBk2^*Hr)zLz{T^AV zRH1&ec|J*qL!i%o{tMyJk9ZiEwHYe*463!8q9}uJ09l4P8_O8xvyvv6uuYEAWprT-)Jx zB4Fb%C(^kc*qm6yE)&E8m5@P^KmdZSkw5}N*4X?GU{m5`Q8h^sOISFAb!1HY_TQKV zSMS}nRstwRU&39muZ+)oMGFc zD1p9}d(uw(FuU0{E#=*5+a<-k-hlQx*n`lYZFE06C-G)glX|so%~BJUDy6Kh%sJ=0 zUYl7MMr}n|l5mwp`lX1Gjx$7J(lR>Fl0Bf@sRb|tK5=;FGoPMH=p07tmlO;5Qkvru%NfwoM`W^9<)y?zd=IKMX96HBEk7412rV@105z;yut{@>TW zc2`|)O5f%{x;~vH{6U#qI!T0e*d1no4N}em1ZT~stn3~^0sx0w-1Mdf<49vO8ZwPvTO$4(P#6f=S`&jFIdza3(4a<4_b|5}Sv~l6#mA9} zrbtfin~g}rGR_tNNdO!T|c`vu-?2YTEjmzEKdfLs-Gq85WaW@LC zMeB7>8m!-K_~W%6{Pa+=O-p%qTBj8AS~sV(jZVy3*6Aqsg7q49WIC+L*^LV_6~W}> zv;zchIOmN{X{~INw&Ou@cR-eobkWVq{n@f3kwZ3bdeFrDCc}}#+u!jnt6SA7hZTF3a(%ZX6Akh;7O2Xjb?y47WS2- zEzV8^%ZAz58p0wY4}d7X(#Dp($FavAgLl0B?EwP%{O3Na=m9H(GukhX?Q1Y+gyQ>0 z$}ob>sVFW1<-s)vLKWqdD(sc7e)a#LgA}-*^6{GYbck!VXXiP`3%-#&eWS4;G0VQP zylDQG@Y2dMU2(+~>acNya0rO4i8Q-aIw!PoO<@Z{rf_F#H^~Raj^JGaNmCR?MsWMv z-TsUq!6d@lHps!2Uytjpi&#>QjpgI~^e;D>&0ESaZ8PR(Z1ntZ-pq$%Ie^>a7LNGX^rejEm^H;#Z zGW;>y9Gu;0XTH7hsmJvoibYqa+^b_v9EHa%5Y;b95P0X|9cgCWIrw%|y2^T?up0(p zKxC%s{{F++3wOe)+UWcrWFP6blD#qY@9bGzu) z^>SG61SWIP&ur9dGBhc3la1fGbN+PxzS9jW$_>0Z2d!MJQ2xLeA04Ne>kf#B zFsFe|I_V_8w73K&0SDujg_e7hNC>be56JT~l;=#E~iM5kNpv@DLI_>|qb>c+c+E z&wAL30KJ!fjD9K)c^bE9Qxj7jAiVoM?}5P9DG@RbJJvCb^k$G?j1gY3w+0^&ij8gp zz3m27a2`Q4q5a(FJm-@TkaYAC4sx*N*W-HYqEWA{Zt4_AFaEQ0mZpk(apjeB@JRv9 zW(pw&QvgIF?l-G$=r2x_0Zt=4A%Ube&rCItonc<$vJiztfwcNzjL`=BFgMytHsr)k zNiotH(k4Q0!Znc>KPpD%xY@-(*22y*&OoHx3lj6-QJOvSJ}XlQ9NX9d6oSH2MBhu& z7M5G-fzEqcDsN2LI7q^#dlPl{_t|JK?A0+IhEvtAGe3-AeY4i>{^j?(VTzUv${9Y4 zB-ZuC9MC^4(=cr-yS>Y$^?~!feD6xJld^6t-1X)o?{3#xDpz8AP*2t^ik_Q2ARv+# zzVOAwBJhO~dn9^ZIVUIYOGDa@GJv+kojr-L%6apy0O1sHz)N28Qh0LWT(SyJ);oK} z_LliJ>xge=6R9uOB!6Hmtt@3fPftw=02E6_Vs0Vvgwz-@06}2m-ijQvCM%rtWDzmP zG23iKWqrO?f-~rch-UDD2aDjhm;IJ6x#;3+_RkzJ3N+3{JOYO-3z%uES)4_jxg`3C zF%M8KTMnpTj3uivIy#Euk2~(LJe^%Xhj%v{bX?b>wJen?G^oJST608Pmpq0kczpU} zA1^u<1*rpB`JWL$65>cgB0~yt%hcutizcQyQmKIfN5dX8N=jJmt}69`4PC))tW{Z^})_8;~-R@ zn@JFK(y(nTN;UXCdN37SLwb3?t@M97;ah_=)_3lA`Fprt7P;3%L%(afTM}zGHhXV} zIbb-Lq+#=h_B*M|U83)Lqn+ibmP(l-Ph4odeDB&CQEBJ(4Wmrl?XYjuS36g0S==br zilT*Ch{%2VQ=ig9#qfTsEjAR!lZG*h4|Gq;zQRv;owL@x0o!~!G^%8DVdhCa(QTA3J5 zD+a=rg^OktWik(#$YsK9(d$y*)~P@a0aJUX@RoDm9NzNRZ$;vb0?Z`76%QR7Q)3C- z9021Y79cmz2%t9PM>Bi`Fe42uun3;}{O4YszmeP7%(Gfqbc4!S&Uy`=)|w-$IP7hG z%*@S+bCAogxoWxDYQYmBq$^p#8XGf}!p?N{SaM<`8aNSP*=v)b4$1{DCd?8fMWtGX za1ajZL3>)~0o`xHqt>WPv0SA}sj`}t#{_&<(q#t*UiIQPp1ZtM#NzS_kN||BaPMS9 z3l(1BLEwl_#;Qh1c)&RiB4?RktP1|4V4qSfBEt9qc;jhCCN@N*8SRLa5P@crZ4*>% zbA+_9I@UyxV9vZ@qXWgFox-VvS%Xe<*wx);qu3)AP+};bbnw|44j`>8Kp-wa!0_G| z+w>!ATW6MWqb%z%vWfZk=$D3VGwQ?L&dL1yt(D~3N=$Yqd@BlQ4a?TcBKMkT=w(fB zbl&!x@VdTat%nBT7a*hzJAx*KBX4yNmo6)jrX?!pyti>{1KqeKgU;uAoV1ie^NqjBg~0;}cb$3~mMX6bQg`4EHK+h5*a1 zX|#Z5Ot+g+dO6%j?huQsJ&xuWiAVNa;)djx- zpw3xF9Y{{`z+}h{B@9-4v}O3&JV5JZGcIK@EWJ(`N%gred)X@m0QO8&DX*N^c|Z?> zeO;_CNUc$m`r@KQCwj@mQ<`;=+n#!#hu`}ycgH2ayB01HMSh7W&ou)=0VF1ojodaR z8nip3o>PuyHYYRy!kM!TQpcN3RaL3vB>Oog4>U3DjRr6J;O0e(273p|+8drf(l=&= zIH9fF85<8AU?ND)1aV?d0opijX*<@A^z(^Y5b>JVW(Fe-05D;}_*#3aQo#0p#tdUF z?-Wh!M*H%i#;mbZ)|~@-xMR8-sDy`MTaJP5#d>u}x@`pOEs8;40A0(eJzD3aXlFFn z{(jrZlPE#6(BJ&VH{r-36;Y>0>y&7_Q#lj|aqjoLScm>coi01vOdNxthZ)G@?Qeg_ zLEmq&QIqwQA2(~OXau*QQDUnX|620jBEnZ(eNF51({6e9On=UK%{vnVZ4qaPaK?0@ zCaWMDI(*cAM#duy&7cXJRIvs}Py5re&-~VRz6s|OG}ESvb^sZjp{$xYmK~*7sc7e@ zGl)=OOrQCOe^iIcf*U6eZ1T{? zvk`OsD*5j8cjD0I{Z7QF*>rAbW*Pu19`{L4Iuih4c78z?>or-6Ib=}mYqWd@E-4Us zjQKad;vJu#Uj&*fEd*gW(LF^V2x!7_TfUx2j1oQ4^+81gVF5&hQIH0YWP(p@6E6~L za&(*tAlA>)JOBY;1O&@k>uu24$VMkL-x~H?b*O3?2dym|1D`Y^@>0 zXwqrcIa3$a>Vl$qbS-JY$s=)oOyAc6;5BAky-^#Al=Eg9O8b57pzCKdLL=L$yqUMH z&cqJ#{?`}hb~|ia9q6?p;e#E|?g!<1yw97zy6)Gq8Ea=W`+LuRO8otN_vt-T1n1-) z_q+!YfDxBiHj4mqs>jd<3IYJ6jjXinVf?%PH_K+mJfxjnY;;X8c){}_BJ2wVn%=vQ zO5>V=>PPvpUn`Z{O?kC0C9d7x^}9BWQiUeV75VW`fBKy#KIyFeFh@v23NdxGObpm) z=h_y#{w;Z&(RA5I85BcN7Se`!I1lE4M?C!Dm|s{>r>)@Q(QdCrhHaM2xdF(q*v{sL z((W;cH4~_qZF8@kLn}=o;_&R}JU5M3UCcGubt^x|`CsgEPz-Bd=yy-p%>3%M_kH}+ zpV;f*K;D7Um<^S7);~7aAxcUc=A59oUIIuwC?~hBnNDYvcmb!}

-!Ax~5;S7{^W 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ℑ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

j=IqW~PZpYs)LU(mVyY)OW|HswtK?mAA$}5*I{f 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

TDk{ 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≪ouHxXZZ;jlhnzQTD126ZPRe-sC4XIsjazb0)71{PW zZVEhK^*P#0WO!=MS!KM_e=mb)V=4z{KF@7pc}!fC1az6>UnNriJqtW1hmGZ+TlPxs z79}lT=XJK@W#_wX^Hx^Rv-#4{8ioe&f;Nr-B>ql)yQPQsZS${ax1fG}2;x1kc z*D)4u5?HL$%U&mVz6o#iB$L*!CH0(thXl`7<@+o^$bXn7ghLYklIk$Va&e zee6xea<$4=HM-Xmt1SMe77+Q zZQEzCFAdYSU~Ba`?&?`=3!W2`P?YNd%Vqlbd1Le<9p<`r*!B2bS8ect>sV@weKXwa zj=#C?e*O1$&6sTR43=|3HW?4+_4%{~59J z9k;la)xLPuzO`xJ-qpBUu2%Ufcz!VUF~8B$5xYgX@nUziU-yT@omFl=D~h7K@owS6 z#za2=pwojmZ#x{g9+&b3GT` z<7I%+HnF}q$Z~n;#r3V5d=<3uOQS5#W=@u`<0|8N-SIbVr?a-ucLfwKdbuqBjd$2P zH+?==nHM%$NcFF1J$liYEsO4@8MfuN-_~WSUVyAz-&#q-u&>MS)3f^4#?NfSzJ1wu zUAm5q$M}YYg)YJz`-*+bfKJ=o?n-tx*vX=$d(Qm3t1!vxX4m+zEx`W&5H6r29gIfv P00000NkvXXu0mjfj3-et diff --git a/Data/Server/WebUI/public/Borealis_Logo_Full.png b/Data/Server/WebUI/public/Borealis_Logo_Full.png deleted file mode 100644 index c800ceaad8eace1adeea771d670fe1dccfdaf911..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 397410 zcmZsCWmuE%|MpEIjZ)I00s<0>bPFmfrHqCFigXF1VT7YYML-%sr9(oRA*s|9q!}ZG z(KTSe#`f&@e_ryvk7LKZclU99u1}oTd7f9Cq5eZgI&L}u02p;2X+Hx12GU>h-?Y@E zSH1M|80mG%>yfz+0MK9g-;WH)%Haa2S=e;7HH`yuwvd4Z63*5F_}$%7HSEZC*+wwJ zI@%5;PxcNnWyM0q>Yo%Vx^Vf*3#~*~`@$3MD``EqCF6eKI>hK6Bn4V854@+1qNUcP zCGW0bVCWuF#m517=#dZV%BL>|v@g8MRSYtV7=}=^_ap7tw0Qd(IvQ0SneOuz-*E|$s=Q0wuo^qC z%EoS4u1pQe?YmWrB`e1FK7shA8uu~o;<^dgORN_jq|en(7?EkYyF9Yq*@|e!q--V6 z#duGDG%3ZJTZ)hvr9cgc(r9<1Jzf(&AS!hm4Qh9Lw6(0Nz6tvzJI8^P3v*3h~hKO=o!y?EK1P zy~^aODsGi8n7hBB|K)X<^($gOf~O9Ie)j&*g2=Q1Qn@cAi#oR>;@-qwcy^kZAXMQ+ zC7dFo)vP0JB7Wff$+RG&#*JlJCOs48g0mo9u}}dMHGRyYv|CFyw3#PlR-EHQ3iJxP zPCw&Bu4U14#;%gDIKteWfmnFo9%A`fsWY zpuO0R=p$x|_zCJ;uCouCn#x*36sljJgqr_uTMzh<0#CxM?^v$9XfX7B&Co>z1Sj~9 zuMLgAya^i!obtLEy@aus=H941wO}WJzq>r%&xF_SX{{1dosdxao8DP5#E5aU;)NY% z{WBvD4Efb>|FV^3Y#QG5YSpLtcVqom4NKwd=c0*E{^L76feI%!Q;?uBqgjD3d+NU{ zX8pW*A9Vj19U0I&_Rn!sGA@19um?Ros}DAQUe1=tmQw7}`zCN!GPN)6vu<^o$L=Qg zxP*{cG%*9hUH$&Xpy|Jl^3kMX`--*B-Gh4Pw|_oWy!UM+{zi7X@3IM9(GrD9zpP|+ zy5TON$|o6wO3ls@fUNp)@4{)a)I2#u^#iRYE)k`a^A8%tJF2(SlrAo1EwA!5Re02f z5TDX%AJX{2^0-<`u1~Jb`^QAu#QWLE%mfqb^MZ;bCdA-Q%CfYGsbZhQr0th+YB^c3 zT;;FFH~z1h&$VK{??iOZK(9PpG0O_$lvCX)YO}xJm>nAdi#6uh_^UV>W%aGNB57R5 zBv7A!#r3iFg$K)UmkM}z(|6iIGSHBTDB^vNaC)wY7vM}@7VN6kof%ZGRv2+^3z!#1YG+wx z=slNE|FEQ(hVXp82rZbdbH0LXeApequYICk*%ULA(+!$*nyQe zY^Qo0aYYAc};oZ~D#HOpLeAee%DO+rFtZ z?#FyZ{@kQ?MQ{6? zpH%X$x?cX5l;K-lwUKNURymc_s;}_c96PTA2g@FBQhqcgrSJx?wOabYJ(s%8!!{-a zhWC|MfGn>Q2zw0r%h%n4R_0}_SAf5N7PHlI4Bjjns|=bqlW6sy$gN)4BNo0ezo8oh za~vjM1!4;J+SoM)awjF6KCIHw`Xt`Ja%Vw5_)&10_X zJ~Tz2{OHBoBx<*;27Pb}b7L3sR@+y0m<@0f3km%FY2-7{pi`TIl_`72tU!Flj;IV~kDlHazimonD4(LKEV3yROvhaf*{-Y(76Kl*o2LBTWqV=I zdr#T5_BZ`F8|~9Q-;AdOg7oOFY>Z6mAsQBIpj%y?D}gpPS8w3khuI&^jMh(8a*VlU zoG%&<_RCzcul7!K=6!OSIU2@^H*4`(g`U?1t`Rh@+VM(AG{;zCON+DAVf?(W_T(iJ+lYbE<7Ys24CQ8z5MbR%tf>W4C~|9Df6 zx7Z;0kRu}RigM<{W2k9Hd-1T0n1JWy;@S}xR>*2^Oa3<}xLEcrj*Xk8hs;hk-$Om! zH*6R;hIWI?Ks&v%$-P~?kBz!DRlvdCLMwatuREuLNzY5gzFUGUm;WBlD_5bQ{}FNg zmg^$&bFf_Ht6a$+gY%XmHBE6E-lVbHl|u8U0bc4g2U-?_Rn0r+JbtMpB=q$JKA0i( zD~(W)nr$qbOWiwwC|QbKF?6>5D{|O@LbG6hjoaoyMo?zb*B)?Gc4d#!9m8 zZ_e%Zp1Usv%+l7HLO`ew!Y`WLW-SoqqiWPe?~)%-=#9=tsP{7oS>^pAjBzJZ&pf$2 z{kKl}4Y9nh9>4ppz7@}J2FV#fwCh#d7|QOVta;2!BQLh$(D#0R`Z z>^GbZqN51!cv^+|e(UrAo!$IrHT2M3SHk=@x*8s?tRBirJckpHqdYoJ@8=)#lv)J; z8^K7k<=>(?54km84p9#5M`}Mba8_9zI^-R`-bSY#P9Ri=ko`i=`W)wq3r#UKCWom&%+K`;nAS z2tuQ0Pe$@isy_WA(9L|FEpX38gdA`TQUx_Mjr#L-$3%5hUD3BW zo?p7>Jg7NxE34P*TdMDstI4yuQM|LI!82xu?~PY0&ouvI+YM|v&hM-Xzhjza1W!sGI;GZmOFO3GK zL#u>@RzcN4nHT%(|N_Az=s!?`*7@? z8QDV37jLns7th{!aV5KQp0ps3Ijy0rGMpp1{d}CeL&7H4Tv&CC2*DtoXod$uB@MAp zDRrnd->9G0sdv-o5kpw73$d8F9t|CvH@`j|9dhjb-xTeLUUsUxj#Uz8;_VA#?()t> zVA-CS);@?PhZYsSiQz88JPP%CbD7*hmoRv?Lrv565N z7>MexmNv^LiV}^#f^}D7*-ON1_NWjvKSZ;2@$8C_y^oy;O2dL@;!Cj{Z_#c6loQEi^ShBc+&oDFy=r;_7X7zQP*an zF^(^zBNlrLwR2SUw;<35xa^^KdX%GsldT(hZks--^&;MLvyG3n_nW_OU7hqhfzF3G zp5$LxVr*n4dH7;Lpjm(s*ImlLRyE$-@e5pO?sUsplQ@AnZ#%NYyW|-;+k`r|Mjc4JTIRm_&L^ z^=y}lO6)l0#jvaeTp1sB7$%)j&KY6TOE-peto;?E!O=hCr{4lC23{3)#|N2c$q1hCQl*gfCbT^j7z8r0Ti0CH4Y zE0vWqTp-zMVq_adz7jEZ)2d#7`yA)%7EAchDuc;&>vu~xlnZ%^@8s28*3Lz{@D|s) zOrCvDOF1p*;Ni&hXOx%m=y$N;_yrP>GnZA352c1NguJC{W)|RnOE+v}y4*SK@o;gGL~7 zOlOiD!))&T&g%2t#N6S1k{i5Uu#+HnJNw1p>Vy%!03ukfJ~T(K3X+Q6t6g>rsy1bh z1;L2O4z#*y0@}-Pk*Kt6qPFUQVsux#zRHJcb8U+Awv+`=oD)!N(Zq|b$SIQoOlHfM zptL-(eaItH%GJQb{7XtH@BcDy`Ypmi>BRTB*9QauqNy#aH171L?sApX@C030#O3O+uL1F(mnBg2JmA@ zHCzBFDMPO5O4Y1ydQVNZCX-GeUK*M^q@xrxsW7?BaoWz8)u1Jgp}$|fErhM*pFD(j zrxa5EEelb&iwr(Hxqy^e|ELbmPyxDgsf#6GR}oU0sQu&9=$twOU_I?57Fy z(5v5fX6( zBcrxhJC`aXVEY`1K^-7S^D5R?X%mGgk=5JI0>bW zz)8!kvy5AKoE_;5ll|i2#%uk}@dw)=rG&0u+3LS`Vy* zfFh2r=>*>=FO(}}s^edvP+mX73^Djw+HcjaX^xtwHfH||$W<*Y=J;w~RY(%^Oxv>C z6U$Gbw(Wsz#j55eAG{5j4uVUEwXz+S!ThNG_gb(Y0p8bsw;^0#^rkMs1qS+SDr(+e z%#EE;Zcd<{ox_TQvVyL$PdDhLR;te8$8iBfIb$7J&SokUsvMg?M$yF$SjFW3BL>Z=~bIq`XwJDDnerCV{ABCL} zHLb>=j8bPV0J}y{B}+k>G>|A$V{(n$w`V~<4AaOD8GNXgg+!}OJ|uw=oatvXN)~^l zqPvn6GqB7ZfqvZ?G@X-@TJIO}fUkVymx8VX(kmd9okVnSeAtBrqAiM@gmr_TH6WBG zPYPh0wL2=h%i1Q4+mpAo%fzh8gLa*MPwH_h|7~!Rlxz{%2tjHLo%V zBzm_5rr$$T8KO6;kk*nrH+wlh-obZ#_=i*Zb-(vFc)e1pJ?f^-eecr31cRq&uW^^7l)Snm_Em*!&h zeU6F$k90ZJhSZ(bwM6U`u|+FayiiQO_A5&4y#g(IBscX*hGblsMM;?!W-nAf6iw*D zNdQF~8&vp-Yd!f7&nh*Nc!V!08)7}!0ha|%rtpR;K@I#Yw)T>e5hVy#wosRk$?=5F z&U%~~7`>E@Gguzj`Qm(n@}Eg)) zp&9}W*!mq(v0BJb&m|>3`Gl(7LT?q97a2V?!7AB=Dzp9;6fx#YhJ^ag>#6)j=tVe1 zN7^EZ(Hr4S>*4dZvTnE8Ox2`d^?rEwH<_~xVI1A4{^?q;vhh>6BqCRN=fsy9NC}>M zK{nNv3UwNgDwKvH<6N=KC7rQnCdBZP6bw2J3TFsE5ex4M1HF4l__QI187HUHsMNyG zCeijZp$vqt>RV6tTB438z^D6@ZNhaNLPDqKDmf$)Z`N*A2cu-;-ND2`X>Hm}(?{SM z``ozO-fQT7nrG&RU-|a5Wz4a+3>$1YS=^RiWZVS8X3qHi4jp&2@!m?-lIawte_S@= zIqq$FMDQ#e-X%u;6_8ZlY07>S)Y;4b9|@{bVGR7f5za*(bnUSD7qHUS>4aqJTfuNA z#aJ+LNz#A$ZX_-3nYfXNQ_2?*DgDs;B?eQe^!0oE^@&SolJv#NeYmJ}JGD3AE~B~U zct_P`^KdRM#%ZX)Vf8;HeCt<&FzRpg8~9Uo8wvaju+(}^@qX6?$^5CLQ;JvcK|z_J z8T?AbBbr=MI^`a02-bgbDVMV@M*YBunL!6YBXUzcuDzYdOd5Pf&U*eiZ*%G2C<~2N zeAY&jRrOnj>zIcRQM)V^vk}AYQ&}Y~QXY8*X@N;8e?RM&A=b?V`AssqdkU>}>?q1! zos>&Jz0VA8&OJAQ4bDoiDV8K3t)Q{qcXv@DJ1BvN#a=NN%ORP`C^uhfv$ymx#T+0> zbI#X$KK6b4yB;$45jftXa_SRoLgxl$)-9#n+dwCC z+u8WZI|0JtPXRbp_;YWAs^l~7V%2YDP25UL{iSX~wk>P?6=&9#Sc!#rB_^?er$_mz z?%To7ef-|t(08zr>8f=cxI?1|&QMk6%yrDpAMN)I%cb+id?S2JPVJ_;3Ua z)!e}!@?C*xv6I;TYXi(64Jt7^YXBDiH2d6uc>8#yPZG3FvSF`pzpnR>Jo5G{N{q^J$Y#c%qK{#l+U88RR`CW4r%taW_E;B}p&+)ms7+ z)~J7?o|GB__kJHAd^;Csu49KQ*cRNY6%2WXQQ%DCt0AhGeA9P=2QYSpl_fWuVifM5 z2!zwGQ3T+Z`H87z99a$@k5?wAtMKHVCG==|fR(l)*!n9U@e>_;?hZAK&q-961t2$y zI2j>KK;Om^t!dB2=lnj^barvFlel~xKrB*3jd1tfw!^WS{M>I=R*I$?;*gnNs>LF4 z=^l9Hj1%?Te_kp(#-aU2>0C$BsYSg@K#&5zOKPy1Zm>9?KN5#ma8)#9!nLMBOc%JKMG-ti5^dE#^dw&OcID`To zQ<=MSfQt&~)o`T6OfW|A`=NJRejprP$a6CtO5g7@IfnX<%V15pb`zA9=>U1PmC|Z* z%1qG5HaMJq%^(Ud`4L>gA71t=7}ILXpp_psJVw}qQFE|h!%Y1bOjr!zNk%8<;#Swl ztgij=#dgq=RfTsx^?bpqnXYH6?KLZ+2EjgouYM7@%=(-O$IQVt%?4s$W4kT>-OOP} zzBy)0;T@so%qlC)FGQRCu=|Hg`5ia0g;16(j?lmF{?Z!9$nYdjfG@qUF@%@xRs1^T zhk;zfPhoO8E5S}_hPXngzeM&jq$723maSUnndpZyoV6($r3=Z^DcUzo@DaVu?#|Z_ zO}T){SpU2lVgo*V3k{OPAO0!((G*Aq*Q&+_Cn_xti;;rGUHtmvwPncai)u^HWR;yu zDHMXzeQ$Kg=G?rt&m84Pe9o7MD5_(m3)uwC*?rbA`eL#@Ny zjTC}jjVE*4&4ZLO8~Fy@H(S^s%mp={MouJ^8X@$JWnI|rn>v!^RrS&asRdy?Fc)|F z+2BHra5x`+13%`$PQq zLEh35n4Zx1q06gF{K7Vq~wX=NqW>$)9M4M(#(Cv`^h`A32c=q`r-x zi(d$9ni$NB(e+o#U#+bF{W8yU=@j}KYtE9l3t9Euw@7c4cOe^66UeUO^wl~Xnj7?$k(S1LD@ ztQoG4v@#_`mS^uAz$@ue%reG%7JB#>FrMa%hR_J`1>{w2qeF~D% zTzq8AR%p(@Ab%QsKCcaYGla~Rbrl~MTNA7uiRtU#91>KuT zFG_=nZZlqHd<9Y#H|yKBRIfFG8<4e<`0joDsd1P|-@Oj#OaNgQJZ^8?3RIRxiAvJ; zgC#C245hs=`J;t~oo_*VHO8_4^pIX)H1ym+2?AYr5Zs2-z zo0?~q?q{@DfMu>fvr9v`J(u@9L}9B7rj%~o|GLC?{$!*z%y~;G&eJph3agArqyE~& z1jI0#%!u+uBK=s5U`I6aO)`GxC^PEq?_TxSfnltocQmNV#+1lFFvd-~lP&cELlV_k zA!hqEW?OB>*5cX%q2{!Z+%6YbPBG~EL{3Ou(&uap!xt@cLVqQoM1@vI5;7#DxBt!S z1w9)e3b?Ep*L)^_8Bw+nmMcy4Jz|!3z7u`e%yz`UnZgbohjt9$G8;;eF5shY{(pCHCKQ+`Yb|AiV z7zs-J=y@$z?~)zGkRgJcdRWYvH=x!|{fVHX2^S`thVDI;lM=X9XUCp-w$L2C2N(1_s~%f`(gLlI!lpX?_nBb;d6PXboIuUS zA;c=QH8^|yWjqz#%Z^a^>P?zcKe|S4jgLo>)Ap2qylkrqG{%LZU|dn^C+#TWoWMy2 zQ7o!VJNL?>pbY{ke7YB7%l_8f><9bKMtw8IxaXxxUrDh_mHfAo#!^Ety(@m?^gN{v=IwlvYHw9%2g&T@(>&+H6*NWF28_jwhfxl{?1LglUwoQF=i|&xA^}j4mmf zooV1j-QyGZuQHl)i=#Q_4IdJc0$93;o%Kb;`}~ImxLDc>FrXo5Nc(gi44pAnf3tJ1 zn%Zl&m6Ng${z@C5N_^Giw=KD)Ct=*XJ?nTvwn@(b&^KxUf6tg-!x0&RKM^efXRtk} zS*T!}aHQzvPP*?;DX&wDQD+YCF-qa6@hh{a%;}-{Smzb<-~aflHo{5FQgN~pax?+W z(?v{ZNs!+@>xgq)GGZ)FiKehdhcIK>;xT?>{YE(%G=cDXB$C(^g6jX}oAyqpzir?| z8gFp1b(A}iIBT)k04}@vuVk}2S-eoG%;-C_400~Y&2E4O+)lbdA84uAm`kOKSkr;K zo+(0()wxGF^gnw`kn{vL^3b(zermIbe|*Q*522GU)!&>)v<-KN-DoSx;-nT`Frf!* z??qWq^(^%K+WZ|S#O3;_tZGlg8V?T9j9@k8we0~e`>oQke#IjQ{=nROvOBkRYU&=+ zX)n6Ha;5YLIeYGf+s+@*cgrBd|8bfGC{+l<4>2AAczlDJN8n;j;)W6W*ReP?e+b2HKe8KV5{Y>wamGlTqAf8498M;d3jScx^s zEV%BjS)SB?^or1=(UpX*PLO8L*To)4cb`TpDL(yRwF!b(L(N{T;md3?#Mk9n6Om1w z+;^M4dU^5>dJ7YDaA39@Oc))ugSRoNlq@irsswf8Pvin4SR|1wme!Vx-P@D$peBrV zx;$lzCk~+PTb2piHU-nOY82AC+?^WgJxt2w;B7LcyDy}q3W#>4lWXXFS}G#+oonIC z$b{W0$B@7V({oBAwjUp90k3?VRZ44>bpz*!`=*n^&;E_SzcybY6{Ytk{SCcy%WYik zk;(s7>|+*72xZ)mZc-hfgrjz|>j%80f;t*bC8WSo)19<2KIZl0Jwkfc z=&6Zs6O1Q~e^djfWm;=K(c1|#ws2N+2UNPwbB!~0PF%VyPC4s104z!Vrf{ zygr?ETwq9YU|qJDZw+lVIjXJ;8zVhcNk zwv`vy!GeM%5=SDiO_|?fPdOj$Yf!>K6!_iqv|m=eKt+SsAeC9ktB!(B+xHGl%S*|j zbISr)V^$f!>u9GndFZ^UOb5H~RTHG)pPD|6*+aY*1V*FyCEJD^S4IA?`QtasA_b zbiIGefx=RJRp-jcJ_v1C+!_E&RcgvEDjY(u&9Mf7(+)pFxfVY z2IVeAM%grnU(BLpbtCt@(u7pw_=rQ2Nc3CF>rUKr^LsTs5TS2|Za-{YH!1(8YjAOn zboib=+m^)jVjjQGlc4V+>DSmpruJ)sa1Wr4qy~D}<&k`fWi3PzXM-$1$S%oa6GOqy zIwr95%6TqoxQc8vLgwq+GE>fhNi78wxwPa_jmAu0?@B(ie7*duY0cTXHq=X}&GB@H~es1@8!lhP2z?09-prI;jAD2Ifa%_tm8 zf6pl%J`Vi7%-&xrduzc7cd~N}^B=5YatF!f$(DBC1JXs*_5Y6ZXr}PXso)@Rk`6ad z<$f89m|$z4?qtm+Aqni$?XS(x&XFOrb!fz|_4>wLpZor2fs5_0C)bv~A?U|PHswP8 zf*|VriP08Je3->`6o>C-)JmU+ug))fh{58uK-XA8_RgU+ol9HIb@rgjG7DCjl#stgg{Ex>D;dHQ zLh+O)cN@>bz4?epH9A&kZZxqtN*eb(?57xEm+tx!lkoyQeTX+;g@uy+Ue~TMG<&+o zhdLFTJ50~siW7~DHD6aG8yZP{Ex+X@aPQJ^I`)sU7Sm{)EERtX_~f)l~HIJ3sox#@Ak9B#A0F+?Kol_1VyOX3vDv zf$EcZ|FE@H%i5=(Y7B>Ed6=S)9f{qDm&}1bDH_@7SY?FlC3mgfTcfUp1>@ZKqj@(T@kgS`!ALKsX0sxmU4h1fj=tsIWjTrS4y%_n zDk|nsm*~wp<))=NT%oA~__TpOx8%CO`P4(!Oj>)&E4fXP6cBOpfAVEyJtn!8C(F0+rm|98 zqLMFJlACIrBE$p23tuo8U)P9mRNKrv*?Z{mExhaFqmje!JT@uQfzO!Msz%>+w51TZ zNxKo5`u%p099#eAB*&iw`^v>p>EOmb2-28~Af2DR;!|5b-tVr>E#lKU*nbc0J?3fx zxxKpm;?+uyVt?Iw+G`T>)PXre4lPys%9a(kgf3r%Y%PvXIbOUtFMspgk>TZ=i;WIs zKWyF)*vgI~#WO(KL(GJQ@;Ve@7k>Wvp$h)mwQ5HCgy)mFUFrmp>aN=YrzgFwJ1v79 zE4Mv=b>Pc8maMBbTP$T32GRy=9fZij>)P)I@|@`V?qx0nYHmQf5_=0_fuCkN;QUd4 z49lxX3cJP8SpaODC;b-ZRS9~7sw>i})9anF(BOZ{|6=rUVk~P<_fU|L{Al9+Dw_ik zQw0>+rQuDI%Ffh*h0(Mpr!qDj^+iYzI*_YQkILaS=A;gEFuY$ti$-X}gwv2ddm3*x zE?UoZh|RxcVNoxb1I+EmP~>-v<)?}jaD-ca>!g*=-1;%YfQ1v!z0kS-uk6`cyRCC( z)iS`r@BDVsq>0Qe%6`oYZ*m+E>yTbuvFsgcRPsu$e@v5$<|opXMEZN*ew*B{N?=@+uk z+v`E#x3Ku(R+QU#7i~dVI5#cZr0`Xt>nugmU!znCD?6QJdkly}h$TpM=ynl`_=9&!#N~w8?rkxlE#zcJ-EwH{S4fhfOHv1&dv|`YjXG zkQpCWPy;+IAp^8S7r^)f02KHX5=tCDzeFZGSU}EoJ>VZHs|%P>As7+RxJlbp!0WZ^ zlPW#ogK!#E(et$!&t`iiR=0GLd8Shy1wHp zw%g9CNGX4MA~d(Jl!1E)O)|WC&AIi)>7Y}4N;Ph0?WA6ucO|#}bpJZ2($OALCSsNA zidEKc{k$U>{|Z`da_zxF;Slx2t8h?k9LrUbBaAiT6$cbw42_<|pQN0hWE0WZaCK%A zo}w&=pq-(s)6K6}G+M7#U-{WCPxX10O@~hrdJY>QS*85+LS*7%FTD#Rxf;|$z!K!5 zf62{66UoIc+aK3t4tF2*aj~*qp#w$_>lvt9aaH5%+jXPx*XER+0_XerFQq~j=?h>Z zcshzHLk4NaJ><9N+oAxV=C+TQEZOmR1 z;~U+*A9*~c3aY^8klhyXA>#iFJSI!5czihxKU{G z4WxEcE$zh~?4G5V$gH=lkRl=CE#@s>=~AkO?C=*RwrjtO)yO~WDQ-$eFV*_RJFn_9 zgwV}&ivE^)N3U2eG$>I4dYPpu6vf%T1`8nB=MF%4GQY z_iE?j>kzr>t$PZS4Ic74MO?9S^`EmVLiH+!|AooVVKIR1dvkgJzc3=%$)0-1SmOtPT&TB-t;} z!{sfrZ1CVL3v|2trGAnlpJK`hed(&W8}qD{8*7B0f8DYq5Y)NijACP1S$zW02Ty4a@Gzg4J7J(1TCHj!cdjXqyV}`3 z>odsoYB(X9mOOOsO5UE5_coC%4)FH5&U}8|x0^HngTIf#d5Wa508_7?N;w^u;AM(d zc_T8f(}ORter0aPdU&vo`zsJ=+W`O1;DNK}EJMuo65850*uY`U0Z9T5lcYDksZJen z#(s%ygBjKSgM6Z8jO-wdtcQy>5vI%Eh2*MXeTj6r)I1tQ(oEnTa?Z5lu zzRYjVjy)=7w(B)6#oJBv`4TR3Dbz6Ex~Amq-sJU-u0I==<&MEug)_CDzA0AS&OGTQ z;`6HpkA;tumDSVEPvE$3TW35wvoalA8rnl6U!DY=!!WD(W}nL@x6DM0o08=sf0uJr zG`svz%8KqSC!aO?%*u99fI|_hi8lVf0+*~j2wYwVY)O)#E_dkk=4!SC>VMvhO6 zj~h1gjo8INAZ6~Ifpr^1+i4OsS-&3;`zaiJT5mEuw@-VZSOwf^phXgqf6P&*U_qhq zcbPb9QT_sTo=r~18jijugvq_8qttw^e7Kj57OTeJb-~Nd4R^7^THhdD2#9iN3X}H^ zYRt`LB}zIbi;aIAd0b#ZX;v@xz2{RWF@cDshpB7^^qKjhHkD^RgAcL(Sp6a-^_e0 zME_xWibU`3n(=kPc#?nEFk7?kQb8m=+OrcIhQ%%(&#DDtU)E-vkL;e?949)eoLed> zL@4|F`JxXbyTqsxv|uC-s+vVZf7GUX=X9IHtiBztWOC{7J3FNm&G*(xYY~%itpaTG zBDDs4s@szN-b72)ncWL0rI6D3r#pz<)q;}E0V$!HtRF7au}g_kY^Beb(u9#!K<9C0 zU75>Dpj5!8(>N%RJJ!foiBAJ5Miu!}&kLRf(*o|r+P+FQva<>W7AB}_U|%R-=|9K# zzu_keRfUkOJVCY~?|wdieYMTHyb~2xnJDp$1~Z2Decuk>zrwV-UHLR6yod(TXU$|) zui!A!JCl&Saffv>j)yQ;mQ}-4v_8_E*+nP31*We@U&u7rL<;}X`RHoM=FPYdrjiP= z!6obGV1Ecqu0Q%eHlWeO6rfG#p!*ORuNUdgK_chRMFEYkS3{t$fey#!5-B=SmdRAr z5T~$FZl!`J9l09Tni_1^;+4WJw)Cp(q*|%jJ0H=2kC&wSX6KKO!EJKQgB|O7p7r2^ zFH&J7>n=5It9tM&xh+**M&H#Q_qw)Mv$@&w$V)-fkV>Au3cASZ(a>NNx-|NUWV;PB z-4BzOfH=!_mKU!I36X|gg{O()TSJ`bT*TV!hh=mW6lRYof3Sl^pY?lfnZTQT10RCM zwb)VTswmPxp@*-*&DH(yn!S|s`4Q3tqDd?Pw|DY2tm<6a@MO$2=elpi|2UJ7x%*(Q z(t+&P+!R@7>->>C^z2Q)I-%5V9;@G~s7^A^sc-EnPafnblt^xrb0x>LJpKsRUF<46 z&)7u-VmF2F54%SX(75_qF>hF(nN*iW0r98?w zgV17KSi_kY;w>3U3w+`xx+cJDt68RnX{jHvaM0mTz{d5OI){uF$8Ydj9 z497iny@!K_t!LZ;HGE=O81R6CPpY{zB=Y@d>|;<$n$z<+?k%?=(qIlVt75<|ZD)1` z!Gj8Id{q@sqJq9(BcHldKpohr`jqw?fd(us^l3-C8{C6@x-m$hZ3nt0J>?@n7AvTy zU#;@p1q2!=?C0b6pH!(sN1h%Fuf>ujc9uaR!j2-sNS`elR)?xOqh=uV z?}Qi?P36cD?QDflGAZOMAZ_mLIv9DZt>KG?+-@_OLU$0^nc*9wj^c#L;IxHvVY;Pw zL}QWF*2U4P-9Okx9$v&E$ZMTTz1AZpoMk5-4>U+irZJ*^w(Vs&mnfzv&pIRgP}op0 zW4YLUJ7>s`9j%Y%g0V`hJ!Wq15UWMaSMMCIlWHPgoHDA_C zJl~&{++v6u#G(0)9b>hON&VfAtX9{ol$?vfp9kVy&3y&bpqwE&7w%K%o67< zUpwAbFt8^H;j37w2_T=O3f}*gA;i zbZ@j+A2dHII*FC+sGeMy8Of%BphLOp%C=2!S^S6RqO7(C(0H=A>cEGTzo`g$3 zfVX}x@H5rz$7)#)(*lI~U35A5mQAxnpZ4pWx#kBy2Fe6CYi$pW7?mk7QO$bj$y%9` z@k+}(u-H81I>vJHD>{M5m=>7u!hm{Lf`u8~b+!byXbX03y60?$4NZQN_cXMrBJ2Gv z^g)!a=-fds+20;^?1_2M>aqs9g>{3_phF(`X?CDLLE5rc;GWg?FW+8r8LuO$-b*|F zyW$*O>&_irnoZB~Qi6+t9>CDiRV;qtgo%(6Pfqk&bITZ*}>{O{Ta2|9iw8|{MZ;dWIF|3&#JTx zsdJNz-n3~WY=+FYk++0J;aulcN|blIKe{ieUAur>gy=Y^A@>DwHTP4x%T0^R0gVEa zLB=!sZ;t<pQF?x2Yux|T-2Jh4~$^Po@m&88`%vT2;@mSCvykfk>O`1whe%0%9E zMErC^U~WR4@At`fEP}erC%54E2!Wrn zb)?IHW9z)9wx|+Kn)R|O1V*Vu;KNcpMVfVO=oG4p>ZH=l%v(oT660wt4}Ej%rl>LO zAM{U~S&yFRrp3nfJJe-w+jYN^ZaK9!^uLgv8xOqp#{lL1@Oy@yF=*R0?6AKuqy2DG zE!`aUPE;z^XxQhhrsJ*VdGosO8DePHUX!BG84JC)H1b=l2SmY^&SAKw{Yj_^H-l`c zpcPD-l@iNrtnE=jJ}ddIOLOq2fS~GautsgeB-r>T30}6WUUQ<<=PjmJ?emtmPV1{f z`DM2Lof4X!{-TKFzWb^b%PKz+xs8|ar?rS1R4Ab@G8=1o&dy{S(55DFPuj?;E3|sc zO?PN5R_(FmCsyCMpeG#((D`?Bo{Ta(rb1QpK^^(dmo%on1cMb|THYEXyK_$aMYfff z^5yC(PdpBU$XTPFQn}n-O69}vF%wyg-H(^mk5Z>!-Z5g8vEQhq^-`LC`mtl=Xmk8z ztBmO%i0`lIDPE4P^(8h$^lyCc!~_RJ_)p*gm5>LBrtw=uyhZ`bb5Xfl){sA4$FySu z!>~dURpP0_0ZZkCY2W_=E1Q`l_EoEI)u~}F88GUdSGHK{XbaXzFPYo`tjLnc4gv*~ zaGc`K^NGhCA`Bfuws;-Guy|#s++q z{ycxzX?3dQsKsC4gI-Kd!jDzCmWJ6-#MFwGj1F>Sy%#;;rFm^kxXNMtu6r)pYX7Q{ z8nK%;w)(XvzscM75wu)0v}_g3l~VNmGQ~wKg2YJN2F!#hHF;tj9lJi`C`P<>nhX&4E&EnqKhJg*E8E_7ut7&QLX%kZ)4nA)9Be!-N?)14L<+^6Q-i!Zrmpb zpEF#7t<@gb@oRv>dW~85k+{d3uSDJV|BtP+3~1{8+xTc0Ev+D-BGRQGohnKSqgz60 zBt{P;r40l`BvioBAl(fL45VW;j2=B;V|&j3dF$^LuXuCro%?*_x<1zm2^y9Ihh&?O z@|cIdiuWmp=YCHvP+e`zu1|sp zZX+;^n??+;Q*+?W(x6D)ZmL4d19oM3l(;i2nkU~(EO>6?c|je5{9`?fT`SGue9Ksq z2?5E523hz+#Zarg>!bpJ}=55V?7F?L~EoG21pIa{lnAQT_)m8h_}W zw@knvcUk<~n$3)UBne-|B+HL_smhEpbN%yj_}oGU?okGw^HlE4lQneRKxR$zMrG2X3FEV|Zepdf|oOWCH zhetqT`t7Q>YTs9j2I{#!J=kvu{&riJ3?J;IUoD&XZ{M@8=rl{oCf~`%<#f&sdSucE z1{u@ZYL>jbsq3GOy+dy!)Ll-TTDJ!!952J-HUO_?xmBoB@Wz^Y^-gEPN;*}4*TQu* z!pe=GO*W0MN!v(P@k~#dH7rNpMCtBMZuA{IL|nLqtgPm2!p^(6hm}?j^#W?+fSm*I z%y(vvQAf81Ncuk$nU)6oK~?Xr*)D|0{x;4mCbo5Tr4Em_CB9GWTByl;(R>t&E1U|O zMcZi1pV>3=9X&$c9($5EIeGn0f!)l?{p(g-Mds8H*Vu0nDgX$C*NBXq*tGtl5c~o} zTlbbV!3GDLSTx}`s@s&tmG}IOt*|E^d7F5Hjm5zwFSDom>!EftehPML?hpU#t-r}- z*YFfh@*bFtL8N1`-7iKBI*j!-Zte7ady4a`Hj6#HBkp}ym&6janTtu z!Ln6l9GK|BI9mPMj*{VRjEID$Q)aJgmUt9U7BvkWp!m80MT>~4J@7=4~uN|s$edX1s39iqYDhHX3O6&7` z;6AzAp_Ymf|GpFOe(Bt>bHIF~%G;gQ{gb(g6Poyr2ApkL{3t>@{N*eb#_u4O;V;NW zC`A7)&kx^uYn?x~4zi@jbh?7Z8{1n6fwlCFyK`yE6`5A`i(4mbeEOy;cO3rMO>{o_ zh;~8!c2wBOM1-7B5cWxAn(B%bgYj=H8v{_I4FN)Nt45DltV&jIr#*;FdG%$G_6}69 z=QXCLc$2>Ofs!9ri68P%3c^mLQNU{$9bH64drYKV{?q1tM>*wkS@{ruYEk59K-^ei zolb2XlxIq4Ud9>W`Da}ybn`Ya_*~r!S{?JyAdlu#k4m;+Q747V?cv2*UursR%sV;N z5kfPj;T+u>J+z=WAqMeqV7}i0C@<4-TYy-dyYaLCu^Rl-67F2A)EHQ)u5| zJFEHzQHe_aNU5U0FZ&#cy^VcVM)7L&Xs7=LnAph)wr#?gs;{5B%?8RyddvoecwC~> zdBWpWLNSDj%5%|^iubX@Qf-4BXPHvx?O5Qka$lYIRX81 z8|N`(TkB!Y1YlOBR{j~$#}*3so@2eg3$5_4`JDalT1g5M?bKlziTs=3SloSIKrW!Es_g5e!@2?B_f`;Vt>hAlmAHc?(=L;sx zmL06tgMSXUq}^=y-otADm$`P+I)8#V91^*z;@4iyxsi}~UQ`nur}@Oj^=7BonXs5I z0mF693w~G8lHKY#`?)Q3^08fx(xq4x`gA5WKmeR^lWghp4>mU5)J{Tf&r*Qtz4j}& zZmuj-YU&;m?f_WGK_nWxS(;OSKX|%}JIsbrqUox&TT2kQXBct=d8BpED9lZ04Q?8l zY}yOdZBcwel#Xrn-~M9+d8c}u&&0Ae)fB+x=ak&Av%T?=V7I}wk0>pr8*fYqX@nLS zbf}%83sK6PCu|-~KI`wlHhHObvtQv501o0$T`QUWEr$>^iV$N}>=v;q*I{-hbyr^9uu5orwE6&L7mWF1JK&v$2u6B?k{@;a*Y8^O zWevy{30i1ounpP%2mUNXy3@X_HQD#ZU}xD79) z;Y6JYdL&Gf=s77SibJR9OF`*c$L}cbH0y)et+Y3L4+j9v zf^2xzv-YGhY?s9|^wyQWiIpSc(h+XNuRomjjgHN)MPvQAd{ycHy-kqVUjUz?EReT- zbfS1{s^^p)Gjw=tjmJYP@)QGa{z^X(sv}nM?(2=sphn*#Z~k%MQKxlp$^_p~@h03t zLdD>+dxat&Cp55l)0nM7DN%%hQf|{$lF@;$zobXXBZOSo2)5I9WlT<{(fwrn-Ci|{ zG=n@NDHE1CMRhe9h*m8HGtfLDDFxe#(vTJZJ};A?p|QOihY)1qxu?sJY{|R0DXj& z<=ed9{PLqyEWNp>D{*Aeipz4sH*z#txjeIdUKJk{?H_x~-rSb+sjt7*S>a?t5N~y7 zRrpwHxc1!dDnV$xq!BHx`$Lvu^4`tqDE`Ht!mK?1cKwiF#-NG;T{O}#IyYCXsznR` zEw1zaHTuzuku1yO9H+~4V01@~L?&1v9k07Qn*LR
5e(N+P=A_zxMkj+1nYr4WK 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

4=S}X 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˱eN^6iqH)FA$!t19F8ov7``p7bqXIM4?0gX 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�Y$A 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

kp<3BRjJu=`lQRi>fXU~}a{)x7g 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 diff --git a/Data/Server/WebUI/public/favicon.ico b/Data/Server/WebUI/public/favicon.ico deleted file mode 100644 index 901a213f51c7c26115c6ad26a6c5d7b89076eccb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 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 { - if (!open) return; - let canceled = false; - (async () => { - try { - const resp = await fetch("/api/sites"); - if (!resp.ok) return; - const data = await resp.json(); - if (canceled) return; - const parsed = Array.isArray(data?.sites) - ? data.sites - .filter((s) => s && s.id) - .map((s) => ({ - id: s.id, - name: s.name || `Site ${s.id}` - })) - : []; - parsed.sort((a, b) => String(a.name || "").localeCompare(String(b.name || ""))); - setSites(parsed); - } catch { - if (!canceled) setSites([]); - } - })(); - return () => { - canceled = true; - }; - }, [open]); - - useEffect(() => { - if (!open) return; - setError(""); - setPasswordDirty(false); - setPrivateKeyDirty(false); - setPassphraseDirty(false); - setBecomePasswordDirty(false); - setClearPassword(false); - setClearPrivateKey(false); - setClearPassphrase(false); - setClearBecomePassword(false); - if (isEdit && credentialId) { - const applyData = (detail) => { - const next = emptyForm(); - next.name = detail?.name || ""; - next.description = detail?.description || ""; - next.site_id = normalizeSiteId(detail?.site_id); - next.credential_type = (detail?.credential_type || "machine").toLowerCase(); - next.connection_type = (detail?.connection_type || "ssh").toLowerCase(); - next.username = detail?.username || ""; - next.become_method = (detail?.become_method || "").toLowerCase(); - next.become_username = detail?.become_username || ""; - setForm(next); - }; - - if (credential?.name) { - applyData(credential); - } else { - setFetchingDetail(true); - (async () => { - try { - const resp = await fetch(`/api/credentials/${credentialId}`); - if (resp.ok) { - const data = await resp.json(); - applyData(data?.credential || {}); - } - } catch { - /* ignore */ - } finally { - setFetchingDetail(false); - } - })(); - } - } else { - setForm(emptyForm()); - } - }, [open, isEdit, credentialId, credential]); - - const currentCredentialFlags = useMemo(() => ({ - hasPassword: Boolean(credential?.has_password), - hasPrivateKey: Boolean(credential?.has_private_key), - hasPrivateKeyPassphrase: Boolean(credential?.has_private_key_passphrase), - hasBecomePassword: Boolean(credential?.has_become_password) - }), [credential]); - - const disableSave = loading || fetchingDetail; - - const updateField = (key) => (event) => { - const value = event?.target?.value ?? ""; - setForm((prev) => ({ ...prev, [key]: value })); - if (key === "password") { - setPasswordDirty(true); - setClearPassword(false); - } else if (key === "private_key") { - setPrivateKeyDirty(true); - setClearPrivateKey(false); - } else if (key === "private_key_passphrase") { - setPassphraseDirty(true); - setClearPassphrase(false); - } else if (key === "become_password") { - setBecomePasswordDirty(true); - setClearBecomePassword(false); - } - }; - - const handlePrivateKeyUpload = async (event) => { - const file = event.target.files?.[0]; - if (!file) return; - try { - const text = await file.text(); - setForm((prev) => ({ ...prev, private_key: text })); - setPrivateKeyDirty(true); - setClearPrivateKey(false); - } catch { - setError("Unable to read private key file."); - } finally { - event.target.value = ""; - } - }; - - const handleCancel = () => { - if (loading) return; - onClose && onClose(); - }; - - const validate = () => { - if (!form.name.trim()) { - setError("Credential name is required."); - return false; - } - setError(""); - return true; - }; - - const buildPayload = () => { - const payload = { - name: form.name.trim(), - description: form.description.trim(), - credential_type: (form.credential_type || "machine").toLowerCase(), - connection_type: (form.connection_type || "ssh").toLowerCase(), - username: form.username.trim(), - become_method: form.become_method.trim(), - become_username: form.become_username.trim() - }; - const siteId = normalizeSiteId(form.site_id); - if (siteId) { - payload.site_id = Number(siteId); - } else { - payload.site_id = null; - } - if (passwordDirty) { - payload.password = form.password; - } - if (privateKeyDirty) { - payload.private_key = form.private_key; - } - if (passphraseDirty) { - payload.private_key_passphrase = form.private_key_passphrase; - } - if (becomePasswordDirty) { - payload.become_password = form.become_password; - } - if (clearPassword) payload.clear_password = true; - if (clearPrivateKey) payload.clear_private_key = true; - if (clearPassphrase) payload.clear_private_key_passphrase = true; - if (clearBecomePassword) payload.clear_become_password = true; - return payload; - }; - - const handleSave = async () => { - if (!validate()) return; - setLoading(true); - setError(""); - const payload = buildPayload(); - try { - const resp = await fetch( - isEdit ? `/api/credentials/${credentialId}` : "/api/credentials", - { - method: isEdit ? "PUT" : "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - } - ); - const data = await resp.json(); - if (!resp.ok) { - throw new Error(data?.error || `Request failed (${resp.status})`); - } - onSaved && onSaved(data?.credential || null); - } catch (err) { - setError(String(err.message || err)); - } finally { - setLoading(false); - } - }; - - const title = isEdit ? "Edit Credential" : "Create Credential"; - const helperStyle = { fontSize: 12, color: "#8a8a8a", mt: 0.5 }; - - return ( -

- {title} - - {fetchingDetail && ( - - - Loading credential details… - - )} - {error && ( - - {error} - - )} - - - - - Site - - - - Credential Type - - - - Connection - - - - - - - {isEdit && currentCredentialFlags.hasPassword && !passwordDirty && !clearPassword && ( - - setClearPassword(true)} sx={{ color: "#ff8080" }}> - - - - )} - - {isEdit && currentCredentialFlags.hasPassword && !passwordDirty && !clearPassword && ( - Stored password will remain unless you change or clear it. - )} - {clearPassword && ( - Password will be removed when saving. - )} - - - - - {isEdit && currentCredentialFlags.hasPrivateKey && !privateKeyDirty && !clearPrivateKey && ( - - setClearPrivateKey(true)} sx={{ color: "#ff8080" }}> - - - - )} - - {isEdit && currentCredentialFlags.hasPrivateKey && !privateKeyDirty && !clearPrivateKey && ( - Private key is stored. Upload or paste a new one to replace, or clear it. - )} - {clearPrivateKey && ( - Private key will be removed when saving. - )} - - - - {isEdit && currentCredentialFlags.hasPrivateKeyPassphrase && !passphraseDirty && !clearPassphrase && ( - - setClearPassphrase(true)} sx={{ color: "#ff8080" }}> - - - - )} - - {isEdit && currentCredentialFlags.hasPrivateKeyPassphrase && !passphraseDirty && !clearPassphrase && ( - A passphrase is stored for this key. - )} - {clearPassphrase && ( - Key passphrase will be removed when saving. - )} - - - - Privilege Escalation - - - - - - - {isEdit && currentCredentialFlags.hasBecomePassword && !becomePasswordDirty && !clearBecomePassword && ( - - setClearBecomePassword(true)} sx={{ color: "#ff8080" }}> - - - - )} - - {isEdit && currentCredentialFlags.hasBecomePassword && !becomePasswordDirty && !clearBecomePassword && ( - Escalation password is stored. - )} - {clearBecomePassword && ( - Escalation password will be removed when saving. - )} - - - - - - - ); -} diff --git a/Data/Server/WebUI/src/Access_Management/Credential_List.jsx b/Data/Server/WebUI/src/Access_Management/Credential_List.jsx deleted file mode 100644 index 878ed8c3..00000000 --- a/Data/Server/WebUI/src/Access_Management/Credential_List.jsx +++ /dev/null @@ -1,464 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { - Box, - Button, - IconButton, - Menu, - MenuItem, - Paper, - Typography, - CircularProgress, -} from "@mui/material"; -import MoreVertIcon from "@mui/icons-material/MoreVert"; -import AddIcon from "@mui/icons-material/Add"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import LockIcon from "@mui/icons-material/Lock"; -import WifiIcon from "@mui/icons-material/Wifi"; -import ComputerIcon from "@mui/icons-material/Computer"; -import { AgGridReact } from "ag-grid-react"; -import { ModuleRegistry, AllCommunityModule, themeQuartz } from "ag-grid-community"; -import CredentialEditor from "./Credential_Editor.jsx"; -import { ConfirmDeleteDialog } from "../Dialogs.jsx"; - -ModuleRegistry.registerModules([AllCommunityModule]); - -const myTheme = themeQuartz.withParams({ - accentColor: "#FFA6FF", - backgroundColor: "#1f2836", - browserColorScheme: "dark", - chromeBackgroundColor: { - ref: "foregroundColor", - mix: 0.07, - onto: "backgroundColor" - }, - fontFamily: { - googleFont: "IBM Plex Sans" - }, - foregroundColor: "#FFF", - headerFontSize: 14 -}); - -const themeClassName = myTheme.themeName || "ag-theme-quartz"; -const gridFontFamily = '"IBM Plex Sans", "Helvetica Neue", Arial, sans-serif'; -const iconFontFamily = '"Quartz Regular"'; - -function formatTs(ts) { - if (!ts) return "-"; - const date = new Date(Number(ts) * 1000); - if (Number.isNaN(date?.getTime())) return "-"; - return `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`; -} - -function titleCase(value) { - if (!value) return "-"; - const lower = String(value).toLowerCase(); - return lower.replace(/(^|\s)\w/g, (c) => c.toUpperCase()); -} - -function connectionIcon(connection) { - const val = (connection || "").toLowerCase(); - if (val === "ssh") return ; - if (val === "winrm") return ; - return ; -} - -export default function CredentialList({ isAdmin = false }) { - const [rows, setRows] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - const [menuAnchor, setMenuAnchor] = useState(null); - const [menuRow, setMenuRow] = useState(null); - const [editorOpen, setEditorOpen] = useState(false); - const [editorMode, setEditorMode] = useState("create"); - const [editingCredential, setEditingCredential] = useState(null); - const [deleteTarget, setDeleteTarget] = useState(null); - const [deleteBusy, setDeleteBusy] = useState(false); - const gridApiRef = useRef(null); - - const openMenu = useCallback((event, row) => { - setMenuAnchor(event.currentTarget); - setMenuRow(row); - }, []); - - const closeMenu = useCallback(() => { - setMenuAnchor(null); - setMenuRow(null); - }, []); - - const connectionCellRenderer = useCallback((params) => { - const row = params.data || {}; - const label = titleCase(row.connection_type); - return ( - - {connectionIcon(row.connection_type)} - - {label} - - - ); - }, []); - - const actionCellRenderer = useCallback( - (params) => { - const row = params.data; - if (!row) return null; - const handleClick = (event) => { - event.preventDefault(); - event.stopPropagation(); - openMenu(event, row); - }; - return ( - - - - ); - }, - [openMenu] - ); - - const columnDefs = useMemo( - () => [ - { - headerName: "Name", - field: "name", - sort: "asc", - cellRenderer: (params) => params.value || "-" - }, - { - headerName: "Credential Type", - field: "credential_type", - valueGetter: (params) => titleCase(params.data?.credential_type) - }, - { - headerName: "Connection", - field: "connection_type", - cellRenderer: connectionCellRenderer - }, - { - headerName: "Site", - field: "site_name", - cellRenderer: (params) => params.value || "-" - }, - { - headerName: "Username", - field: "username", - cellRenderer: (params) => params.value || "-" - }, - { - headerName: "Updated", - field: "updated_at", - valueGetter: (params) => - formatTs(params.data?.updated_at || params.data?.created_at) - }, - { - headerName: "", - field: "__actions__", - minWidth: 70, - maxWidth: 80, - sortable: false, - filter: false, - resizable: false, - suppressMenu: true, - cellRenderer: actionCellRenderer, - pinned: "right" - } - ], - [actionCellRenderer, connectionCellRenderer] - ); - - const defaultColDef = useMemo( - () => ({ - sortable: true, - filter: "agTextColumnFilter", - resizable: true, - flex: 1, - minWidth: 140, - cellStyle: { - display: "flex", - alignItems: "center", - color: "#f5f7fa", - fontFamily: gridFontFamily, - fontSize: "13px" - }, - headerClass: "credential-grid-header" - }), - [] - ); - - const getRowId = useCallback( - (params) => - params.data?.id || - params.data?.name || - params.data?.username || - String(params.rowIndex ?? ""), - [] - ); - - const fetchCredentials = useCallback(async () => { - setLoading(true); - setError(""); - try { - const resp = await fetch("/api/credentials"); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - const list = Array.isArray(data?.credentials) ? data.credentials : []; - list.sort((a, b) => String(a?.name || "").localeCompare(String(b?.name || ""))); - setRows(list); - } catch (err) { - setRows([]); - setError(String(err.message || err)); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - fetchCredentials(); - }, [fetchCredentials]); - - const handleCreate = () => { - setEditorMode("create"); - setEditingCredential(null); - setEditorOpen(true); - }; - - const handleEdit = (row) => { - closeMenu(); - setEditorMode("edit"); - setEditingCredential(row); - setEditorOpen(true); - }; - - const handleDelete = (row) => { - closeMenu(); - setDeleteTarget(row); - }; - - const doDelete = async () => { - if (!deleteTarget?.id) return; - setDeleteBusy(true); - try { - const resp = await fetch(`/api/credentials/${deleteTarget.id}`, { method: "DELETE" }); - if (!resp.ok) { - const data = await resp.json().catch(() => ({})); - throw new Error(data?.error || `HTTP ${resp.status}`); - } - setDeleteTarget(null); - await fetchCredentials(); - } catch (err) { - setError(String(err.message || err)); - } finally { - setDeleteBusy(false); - } - }; - - const handleEditorSaved = async () => { - setEditorOpen(false); - setEditingCredential(null); - await fetchCredentials(); - }; - - const handleGridReady = useCallback((params) => { - gridApiRef.current = params.api; - }, []); - - useEffect(() => { - const api = gridApiRef.current; - if (!api) return; - if (loading) { - api.showLoadingOverlay(); - } else if (!rows.length) { - api.showNoRowsOverlay(); - } else { - api.hideOverlay(); - } - }, [loading, rows]); - - if (!isAdmin) { - return ( - - - Access denied - - - You do not have permission to manage credentials. - - - ); - } - - return ( - <> - - - - - Credentials - - - Stored credentials for remote automation tasks and Ansible playbook runs. - - - - - - - - {loading && ( - - - Loading credentials… - - )} - {error && ( - - {error} - - )} - - - - - - - - - - handleEdit(menuRow)}>Edit - handleDelete(menuRow)} sx={{ color: "#ff8080" }}> - Delete - - - - { - setEditorOpen(false); - setEditingCredential(null); - }} - onSaved={handleEditorSaved} - /> - - setDeleteTarget(null)} - onConfirm={doDelete} - confirmDisabled={deleteBusy} - message={ - deleteTarget - ? `Delete credential '${deleteTarget.name || ""}'? Any jobs referencing it will require an update.` - : "" - } - /> - - ); -} diff --git a/Data/Server/WebUI/src/Access_Management/Github_API_Token.jsx b/Data/Server/WebUI/src/Access_Management/Github_API_Token.jsx deleted file mode 100644 index 9c4d541e..00000000 --- a/Data/Server/WebUI/src/Access_Management/Github_API_Token.jsx +++ /dev/null @@ -1,325 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { - Box, - Button, - CircularProgress, - InputAdornment, - Link, - Paper, - TextField, - Typography -} from "@mui/material"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import SaveIcon from "@mui/icons-material/Save"; -import VisibilityIcon from "@mui/icons-material/Visibility"; -import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; - -const paperSx = { - m: 2, - p: 0, - bgcolor: "#1e1e1e", - color: "#f5f7fa", - display: "flex", - flexDirection: "column", - flexGrow: 1, - minWidth: 0, - minHeight: 320 -}; - -const fieldSx = { - mt: 2, - "& .MuiOutlinedInput-root": { - bgcolor: "#181818", - color: "#f5f7fa", - "& fieldset": { borderColor: "#2a2a2a" }, - "&:hover fieldset": { borderColor: "#58a6ff" }, - "&.Mui-focused fieldset": { borderColor: "#58a6ff" } - }, - "& .MuiInputLabel-root": { color: "#bbb" }, - "& .MuiInputLabel-root.Mui-focused": { color: "#7db7ff" } -}; - -export default function GithubAPIToken({ isAdmin = false }) { - const [loading, setLoading] = useState(false); - const [saving, setSaving] = useState(false); - const [token, setToken] = useState(""); - const [inputValue, setInputValue] = useState(""); - const [fetchError, setFetchError] = useState(""); - const [showToken, setShowToken] = useState(false); - const [verification, setVerification] = useState({ - message: "", - valid: null, - status: "", - rateLimit: null, - error: "" - }); - - const hydrate = useCallback(async () => { - setLoading(true); - setFetchError(""); - try { - const resp = await fetch("/api/github/token"); - const data = await resp.json(); - if (!resp.ok) { - throw new Error(data?.error || `HTTP ${resp.status}`); - } - const storedToken = typeof data?.token === "string" ? data.token : ""; - setToken(storedToken); - setInputValue(storedToken); - setShowToken(false); - setVerification({ - message: typeof data?.message === "string" ? data.message : "", - valid: data?.valid === true, - status: typeof data?.status === "string" ? data.status : "", - rateLimit: typeof data?.rate_limit === "number" ? data.rate_limit : null, - error: typeof data?.error === "string" ? data.error : "" - }); - } catch (err) { - const message = err && typeof err.message === "string" ? err.message : String(err); - setFetchError(message); - setToken(""); - setInputValue(""); - setVerification({ message: "", valid: null, status: "", rateLimit: null, error: "" }); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - if (!isAdmin) return; - hydrate(); - }, [hydrate, isAdmin]); - - const handleSave = useCallback(async () => { - setSaving(true); - setFetchError(""); - try { - const resp = await fetch("/api/github/token", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ token: inputValue }) - }); - const data = await resp.json(); - if (!resp.ok) { - throw new Error(data?.error || `HTTP ${resp.status}`); - } - const storedToken = typeof data?.token === "string" ? data.token : ""; - setToken(storedToken); - setInputValue(storedToken); - setShowToken(false); - setVerification({ - message: typeof data?.message === "string" ? data.message : "", - valid: data?.valid === true, - status: typeof data?.status === "string" ? data.status : "", - rateLimit: typeof data?.rate_limit === "number" ? data.rate_limit : null, - error: typeof data?.error === "string" ? data.error : "" - }); - } catch (err) { - const message = err && typeof err.message === "string" ? err.message : String(err); - setFetchError(message); - } finally { - setSaving(false); - } - }, [inputValue]); - - const dirty = useMemo(() => inputValue !== token, [inputValue, token]); - - const verificationMessage = useMemo(() => { - if (dirty) { - return { text: "Token has not been saved yet — Save to verify.", color: "#f0c36d" }; - } - const message = verification.message || ""; - if (!message) { - return { text: "", color: "#bbb" }; - } - if (verification.valid) { - return { text: message, color: "#7dffac" }; - } - if ((verification.status || "").toLowerCase() === "missing") { - return { text: message, color: "#bbb" }; - } - return { text: message, color: "#ff8080" }; - }, [dirty, verification]); - - const toggleReveal = useCallback(() => { - setShowToken((prev) => !prev); - }, []); - - if (!isAdmin) { - return ( - - - Access denied - - - You do not have permission to manage the GitHub API token. - - - ); - } - - return ( - - - - - Github API Token - - - Using a Github "Personal Access Token" increases the Github API rate limits from 60/hr to 5,000/hr. This is important for production Borealis usage as it likes to hit its unauthenticated API limits sometimes despite my best efforts. -

Navigate to{' '} - - https://github.com/settings/tokens - {' '} - ❯ Personal Access Tokens ❯ Tokens (Classic) ❯ Generate New Token ❯ New Personal Access Token (Classic) -
- -

- - Note:{' '} - - Borealis Automation Platform - - - - Scope:{' '} - - public_repo - - - - Expiration:{' '} - - No Expiration - - -
-
- - setInputValue(event.target.value)} - fullWidth - variant="outlined" - sx={fieldSx} - disabled={saving || loading} - type={showToken ? "text" : "password"} - InputProps={{ - endAdornment: ( - - - - - ) - }} - /> - - - - {(verificationMessage.text || (!dirty && verification.rateLimit)) && ( - - {verificationMessage.text && `${verificationMessage.text} `} - {!dirty && - verification.rateLimit && - `- Hourly Request Rate Limit: ${verification.rateLimit.toLocaleString()}`} - - )} - - - {loading && ( - - - Loading token… - - )} - - {fetchError && ( - - {fetchError} - - )} - -
- ); -} diff --git a/Data/Server/WebUI/src/Access_Management/Users.jsx b/Data/Server/WebUI/src/Access_Management/Users.jsx deleted file mode 100644 index 794131fd..00000000 --- a/Data/Server/WebUI/src/Access_Management/Users.jsx +++ /dev/null @@ -1,680 +0,0 @@ -import React, { useEffect, useMemo, useState, useCallback } from "react"; -import { - Paper, - Box, - Typography, - Table, - TableBody, - TableCell, - TableHead, - TableRow, - TableSortLabel, - IconButton, - Menu, - MenuItem, - Button, - Dialog, - DialogTitle, - DialogContent, - DialogContentText, - DialogActions, - TextField, - Select, - FormControl, - InputLabel, - Checkbox, - Popover -} from "@mui/material"; -import MoreVertIcon from "@mui/icons-material/MoreVert"; -import FilterListIcon from "@mui/icons-material/FilterList"; -import { ConfirmDeleteDialog } from "../Dialogs.jsx"; - -/* ---------- Formatting helpers to keep this page in lockstep with Device_List ---------- */ -const tablePaperSx = { m: 2, p: 0, bgcolor: "#1e1e1e" }; -const tableSx = { - minWidth: 820, - "& th, & td": { - color: "#ddd", - borderColor: "#2a2a2a", - fontSize: 13, - py: 0.75 - }, - "& th .MuiTableSortLabel-root": { color: "#ddd" }, - "& th .MuiTableSortLabel-root.Mui-active": { color: "#ddd" } -}; -const menuPaperSx = { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" }; -const filterFieldSx = { - input: { color: "#fff" }, - minWidth: 220, - "& .MuiOutlinedInput-root": { - "& fieldset": { borderColor: "#555" }, - "&:hover fieldset": { borderColor: "#888" } - } -}; -/* -------------------------------------------------------------------- */ - -function formatTs(tsSec) { - if (!tsSec) return "-"; - const d = new Date((tsSec || 0) * 1000); - const date = d.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }); - const time = d.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" }); - return `${date} @ ${time}`; -} - -async function sha512(text) { - const enc = new TextEncoder(); - const data = enc.encode(text || ""); - const buf = await crypto.subtle.digest("SHA-512", data); - const arr = Array.from(new Uint8Array(buf)); - return arr.map((b) => b.toString(16).padStart(2, "0")).join(""); -} - -export default function UserManagement({ isAdmin = false }) { - const [rows, setRows] = useState([]); // {username, display_name, role, last_login} - const [orderBy, setOrderBy] = useState("username"); - const [order, setOrder] = useState("asc"); - const [menuAnchor, setMenuAnchor] = useState(null); - const [menuUser, setMenuUser] = useState(null); - const [resetOpen, setResetOpen] = useState(false); - const [resetTarget, setResetTarget] = useState(null); - const [newPassword, setNewPassword] = useState(""); - const [createOpen, setCreateOpen] = useState(false); - const [createForm, setCreateForm] = useState({ username: "", display_name: "", password: "", role: "User" }); - const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); - const [deleteTarget, setDeleteTarget] = useState(null); - const [confirmChangeRoleOpen, setConfirmChangeRoleOpen] = useState(false); - const [changeRoleTarget, setChangeRoleTarget] = useState(null); - const [changeRoleNext, setChangeRoleNext] = useState(null); - const [warnOpen, setWarnOpen] = useState(false); - const [warnMessage, setWarnMessage] = useState(""); - const [me, setMe] = useState(null); - const [mfaBusyUser, setMfaBusyUser] = useState(null); - const [resetMfaOpen, setResetMfaOpen] = useState(false); - const [resetMfaTarget, setResetMfaTarget] = useState(null); - - // Columns and filters - const columns = useMemo(() => ([ - { id: "display_name", label: "Display Name" }, - { id: "username", label: "User Name" }, - { id: "last_login", label: "Last Login" }, - { id: "role", label: "User Role" }, - { id: "mfa_enabled", label: "MFA" }, - { id: "actions", label: "" } - ]), []); - const [filters, setFilters] = useState({}); // id -> string - const [filterAnchor, setFilterAnchor] = useState(null); // { id, anchorEl } - const openFilter = (id) => (e) => setFilterAnchor({ id, anchorEl: e.currentTarget }); - const closeFilter = () => setFilterAnchor(null); - const onFilterChange = (id) => (e) => setFilters((prev) => ({ ...prev, [id]: e.target.value })); - - const fetchUsers = useCallback(async () => { - try { - const res = await fetch("/api/users", { credentials: "include" }); - const data = await res.json(); - if (Array.isArray(data?.users)) { - setRows( - data.users.map((u) => ({ - ...u, - mfa_enabled: u && typeof u.mfa_enabled !== "undefined" ? (u.mfa_enabled ? 1 : 0) : 0 - })) - ); - } else { - setRows([]); - } - } catch { - setRows([]); - } - }, []); - - useEffect(() => { - if (!isAdmin) return; - (async () => { - try { - const resp = await fetch("/api/auth/me", { credentials: "include" }); - if (resp.ok) { - const who = await resp.json(); - setMe(who); - } - } catch {} - })(); - fetchUsers(); - }, [fetchUsers, isAdmin]); - - const handleSort = (col) => { - if (orderBy === col) setOrder(order === "asc" ? "desc" : "asc"); - else { setOrderBy(col); setOrder("asc"); } - }; - - const filteredSorted = useMemo(() => { - const applyFilters = (r) => { - for (const [key, val] of Object.entries(filters || {})) { - if (!val) continue; - const needle = String(val).toLowerCase(); - let hay = ""; - if (key === "last_login") hay = String(formatTs(r.last_login)); - else hay = String(r[key] ?? ""); - if (!hay.toLowerCase().includes(needle)) return false; - } - return true; - }; - - const dir = order === "asc" ? 1 : -1; - const arr = rows.filter(applyFilters); - arr.sort((a, b) => { - if (orderBy === "last_login") return ((a.last_login || 0) - (b.last_login || 0)) * dir; - if (orderBy === "mfa_enabled") return ((a.mfa_enabled ? 1 : 0) - (b.mfa_enabled ? 1 : 0)) * dir; - return String(a[orderBy] ?? "").toLowerCase() - .localeCompare(String(b[orderBy] ?? "").toLowerCase()) * dir; - }); - return arr; - }, [rows, filters, orderBy, order]); - - const openMenu = (evt, user) => { - setMenuAnchor({ mouseX: evt.clientX, mouseY: evt.clientY, anchorEl: evt.currentTarget }); - setMenuUser(user); - }; - const closeMenu = () => { setMenuAnchor(null); setMenuUser(null); }; - - const confirmDelete = (user) => { - if (!user) return; - if (me && user.username && String(me.username).toLowerCase() === String(user.username).toLowerCase()) { - setWarnMessage("You cannot delete the user you are currently logged in as."); - setWarnOpen(true); - return; - } - setDeleteTarget(user); - setConfirmDeleteOpen(true); - }; - - const doDelete = async () => { - const user = deleteTarget; - setConfirmDeleteOpen(false); - if (!user) return; - try { - const resp = await fetch(`/api/users/${encodeURIComponent(user.username)}`, { method: "DELETE", credentials: "include" }); - const data = await resp.json(); - if (!resp.ok) { - setWarnMessage(data?.error || "Failed to delete user"); - setWarnOpen(true); - return; - } - await fetchUsers(); - } catch (e) { - console.error(e); - setWarnMessage("Failed to delete user"); - setWarnOpen(true); - } - }; - - const openChangeRole = (user) => { - if (!user) return; - if (me && user.username && String(me.username).toLowerCase() === String(user.username).toLowerCase()) { - setWarnMessage("You cannot change your own role."); - setWarnOpen(true); - return; - } - const nextRole = (String(user.role || "User").toLowerCase() === "admin") ? "User" : "Admin"; - setChangeRoleTarget(user); - setChangeRoleNext(nextRole); - setConfirmChangeRoleOpen(true); - }; - - const doChangeRole = async () => { - const user = changeRoleTarget; - const nextRole = changeRoleNext; - setConfirmChangeRoleOpen(false); - if (!user || !nextRole) return; - try { - const resp = await fetch(`/api/users/${encodeURIComponent(user.username)}/role`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ role: nextRole }) - }); - const data = await resp.json(); - if (!resp.ok) { - setWarnMessage(data?.error || "Failed to change role"); - setWarnOpen(true); - return; - } - await fetchUsers(); - } catch (e) { - console.error(e); - setWarnMessage("Failed to change role"); - setWarnOpen(true); - } - }; - - const openResetMfa = (user) => { - if (!user) return; - setResetMfaTarget(user); - setResetMfaOpen(true); - }; - - const doResetMfa = async () => { - const user = resetMfaTarget; - setResetMfaOpen(false); - setResetMfaTarget(null); - if (!user) return; - const username = user.username; - const keepEnabled = Boolean(user.mfa_enabled); - setMfaBusyUser(username); - try { - const resp = await fetch(`/api/users/${encodeURIComponent(username)}/mfa`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ enabled: keepEnabled, reset_secret: true }) - }); - const data = await resp.json(); - if (!resp.ok) { - setWarnMessage(data?.error || "Failed to reset MFA for this user."); - setWarnOpen(true); - return; - } - await fetchUsers(); - } catch (err) { - console.error(err); - setWarnMessage("Failed to reset MFA for this user."); - setWarnOpen(true); - } finally { - setMfaBusyUser(null); - } - }; - - const toggleMfa = async (user, enabled) => { - if (!user) return; - const previous = Boolean(user.mfa_enabled); - const nextFlag = enabled ? 1 : 0; - setRows((prev) => - prev.map((r) => - String(r.username).toLowerCase() === String(user.username).toLowerCase() - ? { ...r, mfa_enabled: nextFlag } - : r - ) - ); - setMfaBusyUser(user.username); - try { - const resp = await fetch(`/api/users/${encodeURIComponent(user.username)}/mfa`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ enabled }) - }); - const data = await resp.json(); - if (!resp.ok) { - setRows((prev) => - prev.map((r) => - String(r.username).toLowerCase() === String(user.username).toLowerCase() - ? { ...r, mfa_enabled: previous ? 1 : 0 } - : r - ) - ); - setWarnMessage(data?.error || "Failed to update MFA settings."); - setWarnOpen(true); - return; - } - await fetchUsers(); - } catch (e) { - console.error(e); - setRows((prev) => - prev.map((r) => - String(r.username).toLowerCase() === String(user.username).toLowerCase() - ? { ...r, mfa_enabled: previous ? 1 : 0 } - : r - ) - ); - setWarnMessage("Failed to update MFA settings."); - setWarnOpen(true); - } finally { - setMfaBusyUser(null); - } - }; - - const doResetPassword = async () => { - const user = resetTarget; - if (!user) return; - const pw = newPassword || ""; - if (!pw.trim()) return; - try { - const hash = await sha512(pw); - const resp = await fetch(`/api/users/${encodeURIComponent(user.username)}/reset_password`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ password_sha512: hash }) - }); - const data = await resp.json(); - if (!resp.ok) { - alert(data?.error || "Failed to reset password"); - return; - } - setResetOpen(false); - setResetTarget(null); - setNewPassword(""); - } catch (e) { - console.error(e); - alert("Failed to reset password"); - } - }; - - const openReset = (user) => { - if (!user) return; - setResetTarget(user); - setResetOpen(true); - setNewPassword(""); - }; - - const openCreate = () => { setCreateOpen(true); setCreateForm({ username: "", display_name: "", password: "", role: "User" }); }; - const doCreate = async () => { - const u = (createForm.username || "").trim(); - const dn = (createForm.display_name || u).trim(); - const pw = (createForm.password || "").trim(); - const role = (createForm.role || "User"); - if (!u || !pw) return; - try { - const hash = await sha512(pw); - const resp = await fetch("/api/users", { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ username: u, display_name: dn, password_sha512: hash, role }) - }); - const data = await resp.json(); - if (!resp.ok) { - alert(data?.error || "Failed to create user"); - return; - } - setCreateOpen(false); - await fetchUsers(); - } catch (e) { - console.error(e); - alert("Failed to create user"); - } - }; - - if (!isAdmin) return null; - - return ( - <> - - - - - User Management - - - Manage authorized users of the Borealis Automation Platform. - - - - - - - - - {/* Leading checkbox gutter to match Devices table rhythm */} - - {columns.map((col) => ( - - {col.id !== "actions" ? ( - - handleSort(col.id)} - > - {col.label} - - - - - - ) : null} - - ))} - - - - - {filteredSorted.map((u) => ( - - {/* Body gutter to stay aligned with header */} - - {u.display_name || u.username} - {u.username} - {formatTs(u.last_login)} - {u.role || "User"} - - { - event.stopPropagation(); - toggleMfa(u, event.target.checked); - }} - onClick={(event) => event.stopPropagation()} - sx={{ - color: "#888", - "&.Mui-checked": { color: "#58a6ff" } - }} - inputProps={{ "aria-label": `Toggle MFA for ${u.username}` }} - /> - - - openMenu(e, u)} sx={{ color: "#ccc" }}> - - - - - ))} - {filteredSorted.length === 0 && ( - - - No users found. - - - )} - -
- - {/* Filter popover (styled to match Device_List) */} - - {filterAnchor && ( - - c.id === filterAnchor.id)?.label || ""}`} - value={filters[filterAnchor.id] || ""} - onChange={onFilterChange(filterAnchor.id)} - onKeyDown={(e) => { if (e.key === "Escape") closeFilter(); }} - sx={filterFieldSx} - /> - - - )} - - - - { const u = menuUser; closeMenu(); confirmDelete(u); }} - > - Delete User - - { const u = menuUser; closeMenu(); openReset(u); }}>Reset Password - { const u = menuUser; closeMenu(); openChangeRole(u); }} - > - Change Role - - { const u = menuUser; closeMenu(); openResetMfa(u); }}> - Reset MFA - - - - setResetOpen(false)} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> - Reset Password - - - Enter a new password for {resetTarget?.username}. - - setNewPassword(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - - setCreateOpen(false)} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> - Create User - - setCreateForm((p) => ({ ...p, username: e.target.value }))} - sx={{ - "& .MuiOutlinedInput-root": { backgroundColor: "#2a2a2a", color: "#ccc", "& fieldset": { borderColor: "#444" }, "&:hover fieldset": { borderColor: "#666" } }, - label: { color: "#aaa" }, mt: 1 - }} - /> - setCreateForm((p) => ({ ...p, display_name: e.target.value }))} - sx={{ - "& .MuiOutlinedInput-root": { backgroundColor: "#2a2a2a", color: "#ccc", "& fieldset": { borderColor: "#444" }, "&:hover fieldset": { borderColor: "#666" } }, - label: { color: "#aaa" }, mt: 1 - }} - /> - setCreateForm((p) => ({ ...p, password: e.target.value }))} - sx={{ - "& .MuiOutlinedInput-root": { backgroundColor: "#2a2a2a", color: "#ccc", "& fieldset": { borderColor: "#444" }, "&:hover fieldset": { borderColor: "#666" } }, - label: { color: "#aaa" }, mt: 1 - }} - /> - - Role - - - - - - - - -
- - setConfirmDeleteOpen(false)} - onConfirm={doDelete} - /> - setConfirmChangeRoleOpen(false)} - onConfirm={doChangeRole} - /> - { setResetMfaOpen(false); setResetMfaTarget(null); }} - onConfirm={doResetMfa} - /> - setWarnOpen(false)} - onConfirm={() => setWarnOpen(false)} - /> - - ); -} diff --git a/Data/Server/WebUI/src/Admin/Server_Info.jsx b/Data/Server/WebUI/src/Admin/Server_Info.jsx deleted file mode 100644 index d2188394..00000000 --- a/Data/Server/WebUI/src/Admin/Server_Info.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { Paper, Box, Typography, Button } from "@mui/material"; -import { GitHub as GitHubIcon, InfoOutlined as InfoIcon } from "@mui/icons-material"; -import { CreditsDialog } from "../Dialogs.jsx"; - -export default function ServerInfo({ isAdmin = false }) { - const [serverTime, setServerTime] = useState(null); - const [error, setError] = useState(null); - const [aboutOpen, setAboutOpen] = useState(false); - - useEffect(() => { - if (!isAdmin) return; - let isMounted = true; - const fetchTime = async () => { - try { - const resp = await fetch('/api/server/time'); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - if (isMounted) { - setServerTime(data?.display || data?.iso || null); - setError(null); - } - } catch (e) { - if (isMounted) setError(String(e)); - } - }; - fetchTime(); - const id = setInterval(fetchTime, 60000); // update once per minute - return () => { isMounted = false; clearInterval(id); }; - }, [isAdmin]); - - if (!isAdmin) return null; - - return ( - - - Server Info - Basic server information will appear here for informative and debug purposes. - - Server Time - - {error ? `Error: ${error}` : (serverTime || 'Loading...')} - - - - - Project Links - - - - - - - setAboutOpen(false)} /> - - ); -} diff --git a/Data/Server/WebUI/src/App.jsx b/Data/Server/WebUI/src/App.jsx deleted file mode 100644 index 071c5a8d..00000000 --- a/Data/Server/WebUI/src/App.jsx +++ /dev/null @@ -1,1392 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/App.jsx - -//Shared Imports -import React, { useState, useEffect, useCallback, useRef } from "react"; -import { ReactFlowProvider } from "reactflow"; -import "reactflow/dist/style.css"; -import { - CloseAllDialog, RenameTabDialog, TabContextMenu, NotAuthorizedDialog -} from "./Dialogs"; -import NavigationSidebar from "./Navigation_Sidebar"; - -// Styling Imports -import { - AppBar, Toolbar, Typography, Box, Menu, MenuItem, Button, - CssBaseline, ThemeProvider, createTheme, Breadcrumbs - } from "@mui/material"; - import { - KeyboardArrowDown as KeyboardArrowDownIcon, - Logout as LogoutIcon, - NavigateNext as NavigateNextIcon - } from "@mui/icons-material"; - import ClickAwayListener from "@mui/material/ClickAwayListener"; - import SearchIcon from "@mui/icons-material/Search"; - import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; - import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp"; - -// Workflow Editor Imports -import FlowTabs from "./Flow_Editor/Flow_Tabs"; -import FlowEditor from "./Flow_Editor/Flow_Editor"; -import NodeSidebar from "./Flow_Editor/Node_Sidebar"; -import StatusBar from "./Status_Bar"; - -// Borealis Page Imports -import Login from "./Login.jsx"; -import SiteList from "./Sites/Site_List"; -import DeviceList from "./Devices/Device_List"; -import DeviceDetails from "./Devices/Device_Details"; -import AgentDevices from "./Devices/Agent_Devices.jsx"; -import SSHDevices from "./Devices/SSH_Devices.jsx"; -import WinRMDevices from "./Devices/WinRM_Devices.jsx"; -import AssemblyList from "./Assemblies/Assembly_List"; -import AssemblyEditor from "./Assemblies/Assembly_Editor"; -import ScheduledJobsList from "./Scheduling/Scheduled_Jobs_List"; -import CreateJob from "./Scheduling/Create_Job.jsx"; -import CredentialList from "./Access_Management/Credential_List.jsx"; -import UserManagement from "./Access_Management/Users.jsx"; -import GithubAPIToken from "./Access_Management/Github_API_Token.jsx"; -import ServerInfo from "./Admin/Server_Info.jsx"; -import EnrollmentCodes from "./Devices/Enrollment_Codes.jsx"; -import DeviceApprovals from "./Devices/Device_Approvals.jsx"; - -// Networking Imports -import { io } from "socket.io-client"; -if (!window.BorealisSocket) { - window.BorealisSocket = io(window.location.origin, { transports: ["websocket"] }); -} -if (!window.BorealisUpdateRate) { - window.BorealisUpdateRate = 200; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -// Load node modules dynamically -const modules = import.meta.glob('./Nodes/**/*.jsx', { eager: true }); -const nodeTypes = {}; -const categorizedNodes = {}; -Object.entries(modules).forEach(([path, mod]) => { - const comp = mod.default; - if (!comp) return; - const { type, component } = comp; - if (!type || !component) return; - const parts = path.replace('./Nodes/', '').split('/'); - const category = parts[0]; - if (!categorizedNodes[category]) categorizedNodes[category] = []; - categorizedNodes[category].push(comp); - nodeTypes[type] = component; -}); - -const darkTheme = createTheme({ - palette: { - mode: "dark", - background: { default: "#121212", paper: "#1e1e1e" }, - text: { primary: "#ffffff" } - }, - components: { - MuiTooltip: { - styleOverrides: { - tooltip: { backgroundColor: "#2a2a2a", color: "#ccc", fontSize: "0.75rem", border: "1px solid #444" }, - arrow: { color: "#2a2a2a" } - } - } - } -}); - -const LOCAL_STORAGE_KEY = "borealis_persistent_state"; - - export default function App() { - const [tabs, setTabs] = useState([{ id: "flow_1", tab_name: "Flow 1", nodes: [], edges: [] }]); - const [activeTabId, setActiveTabId] = useState("flow_1"); - const [currentPage, setCurrentPageState] = useState("devices"); - const [selectedDevice, setSelectedDevice] = useState(null); - - const [userMenuAnchorEl, setUserMenuAnchorEl] = useState(null); - const [confirmCloseOpen, setConfirmCloseOpen] = useState(false); - const [renameDialogOpen, setRenameDialogOpen] = useState(false); - const [renameTabId, setRenameTabId] = useState(null); - const [renameValue, setRenameValue] = useState(""); - const [tabMenuAnchor, setTabMenuAnchor] = useState(null); - const [tabMenuTabId, setTabMenuTabId] = useState(null); - const fileInputRef = useRef(null); - const [user, setUser] = useState(null); - const [userRole, setUserRole] = useState(null); - const [userDisplayName, setUserDisplayName] = useState(null); - const [editingJob, setEditingJob] = useState(null); - const [jobsRefreshToken, setJobsRefreshToken] = useState(0); - const [assemblyEditorState, setAssemblyEditorState] = useState(null); // { path, mode, context, nonce } - const [sessionResolved, setSessionResolved] = useState(false); - const initialPathRef = useRef(window.location.pathname + window.location.search); - const pendingPathRef = useRef(null); - const [notAuthorizedOpen, setNotAuthorizedOpen] = useState(false); - - // Top-bar search state - const SEARCH_CATEGORIES = [ - { key: "hostname", label: "Hostname", scope: "device", placeholder: "Search Hostname" }, - { key: "internal_ip", label: "Internal IP", scope: "device", placeholder: "Search Internal IP" }, - { key: "external_ip", label: "External IP", scope: "device", placeholder: "Search External IP" }, - { key: "description", label: "Description", scope: "device", placeholder: "Search Description" }, - { key: "last_user", label: "Last User", scope: "device", placeholder: "Search Last User" }, - { key: "serial_number", label: "Serial Number (Soon)", scope: "device", placeholder: "Search Serial Number" }, - { key: "site_name", label: "Site Name", scope: "site", placeholder: "Search Site Name" }, - { key: "site_description", label: "Site Description", scope: "site", placeholder: "Search Site Description" }, - ]; - const [searchCategory, setSearchCategory] = useState("hostname"); - const [searchOpen, setSearchOpen] = useState(false); - const [searchQuery, setSearchQuery] = useState(""); - const [searchMenuEl, setSearchMenuEl] = useState(null); - const [suggestions, setSuggestions] = useState({ devices: [], sites: [], q: "", field: "" }); - const searchAnchorRef = useRef(null); - const searchDebounceRef = useRef(null); - - // Gentle highlight helper for matched substrings - const highlightText = useCallback((text, query) => { - const t = String(text ?? ""); - const q = String(query ?? "").trim(); - if (!q) return t; - try { - const esc = q.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const re = new RegExp(`(${esc})`, "ig"); - const parts = t.split(re); - return parts.map((part, i) => - part.toLowerCase() === q.toLowerCase() - ? ( - {part} - ) - : {part} - ); - } catch { - return t; - } - }, []); - - const pageToPath = useCallback( - (page, options = {}) => { - switch (page) { - case "login": - return "/login"; - case "sites": - return "/sites"; - case "devices": - return "/devices"; - case "agent_devices": - return "/devices/agent"; - case "ssh_devices": - return "/devices/ssh"; - case "winrm_devices": - return "/devices/winrm"; - case "device_details": { - const device = - options.device || - selectedDevice || - (options.deviceId - ? { agent_guid: options.deviceId, hostname: options.deviceName || options.deviceId } - : null); - const deviceId = - device?.agent_guid || - device?.guid || - device?.summary?.agent_guid || - device?.hostname || - device?.id; - if (deviceId) { - return `/device/${encodeURIComponent(deviceId)}`; - } - return "/devices"; - } - case "jobs": - return "/scheduling"; - case "create_job": - return "/scheduling/create_job"; - case "workflows": - return "/workflows"; - case "workflow-editor": - return "/workflows/editor"; - case "assemblies": - return "/assemblies"; - case "scripts": - case "ansible_editor": { - const mode = page === "ansible_editor" ? "ansible" : "scripts"; - const params = new URLSearchParams(); - if (mode === "ansible") { - params.set("mode", "ansible"); - } - const state = options.assemblyState || assemblyEditorState; - if (state?.path) { - params.set("path", state.path); - } - const query = params.toString(); - return query ? `/assemblies/editor?${query}` : "/assemblies/editor"; - } - case "access_credentials": - return "/access_management/credentials"; - case "access_github_token": - return "/access_management/github_token"; - case "access_users": - return "/access_management/users"; - case "server_info": - return "/admin/server_info"; - case "admin_enrollment_codes": - return "/admin/enrollment-codes"; - case "admin_device_approvals": - return "/admin/device-approvals"; - default: - return "/devices"; - } - }, - [assemblyEditorState, selectedDevice] - ); - - const interpretPath = useCallback((rawPath) => { - try { - const url = new URL(rawPath || "/", window.location.origin); - let path = url.pathname || "/"; - if (path.length > 1 && path.endsWith("/")) { - path = path.slice(0, -1); - } - const segments = path.split("/").filter(Boolean); - const params = url.searchParams; - - if (path === "/login") return { page: "login", options: {} }; - if (path === "/" || path === "") return { page: "devices", options: {} }; - if (path === "/devices") return { page: "devices", options: {} }; - if (path === "/devices/agent") return { page: "agent_devices", options: {} }; - if (path === "/devices/ssh") return { page: "ssh_devices", options: {} }; - if (path === "/devices/winrm") return { page: "winrm_devices", options: {} }; - if (segments[0] === "device" && segments[1]) { - const id = decodeURIComponent(segments[1]); - return { - page: "device_details", - options: { device: { agent_guid: id, hostname: id } } - }; - } - if (path === "/sites") return { page: "sites", options: {} }; - if (path === "/scheduling") return { page: "jobs", options: {} }; - if (path === "/scheduling/create_job") return { page: "create_job", options: {} }; - if (path === "/workflows") return { page: "workflows", options: {} }; - if (path === "/workflows/editor") return { page: "workflow-editor", options: {} }; - if (path === "/assemblies") return { page: "assemblies", options: {} }; - if (path === "/assemblies/editor") { - const mode = params.get("mode"); - const relPath = params.get("path") || ""; - const state = relPath - ? { path: relPath, mode: mode === "ansible" ? "ansible" : "scripts", nonce: Date.now() } - : null; - return { - page: mode === "ansible" ? "ansible_editor" : "scripts", - options: state ? { assemblyState: state } : {} - }; - } - if (path === "/access_management/users") return { page: "access_users", options: {} }; - if (path === "/access_management/github_token") return { page: "access_github_token", options: {} }; - if (path === "/access_management/credentials") return { page: "access_credentials", options: {} }; - if (path === "/admin/server_info") return { page: "server_info", options: {} }; - if (path === "/admin/enrollment-codes") return { page: "admin_enrollment_codes", options: {} }; - if (path === "/admin/device-approvals") return { page: "admin_device_approvals", options: {} }; - return { page: "devices", options: {} }; - } catch { - return { page: "devices", options: {} }; - } - }, []); - - const updateStateForPage = useCallback( - (page, options = {}) => { - setCurrentPageState(page); - if (page === "device_details") { - if (options.device) { - setSelectedDevice(options.device); - } else if (options.deviceId) { - const fallbackId = options.deviceId; - const fallbackName = options.deviceName || options.deviceId; - setSelectedDevice((prev) => { - const prevId = prev?.agent_guid || prev?.guid || prev?.hostname || ""; - if (prevId === fallbackId || prevId === fallbackName) { - return prev; - } - return { agent_guid: fallbackId, hostname: fallbackName }; - }); - } - } else if (!options.preserveDevice) { - setSelectedDevice(null); - } - - if ((page === "scripts" || page === "ansible_editor") && options.assemblyState) { - setAssemblyEditorState(options.assemblyState); - } - }, - [setAssemblyEditorState, setCurrentPageState, setSelectedDevice] - ); - - const navigateTo = useCallback( - (page, options = {}) => { - const { replace = false, allowUnauthenticated = false, suppressPending = false } = options; - const targetPath = pageToPath(page, options); - - if (!allowUnauthenticated && !user && page !== "login") { - if (!suppressPending && targetPath) { - pendingPathRef.current = targetPath; - } - updateStateForPage("login", {}); - const loginPath = "/login"; - const method = replace ? "replaceState" : "pushState"; - const current = window.location.pathname + window.location.search; - if (replace || current !== loginPath) { - window.history[method]({}, "", loginPath); - } - return; - } - - if (page === "login") { - updateStateForPage("login", {}); - const loginPath = "/login"; - const method = replace ? "replaceState" : "pushState"; - const current = window.location.pathname + window.location.search; - if (replace || current !== loginPath) { - window.history[method]({}, "", loginPath); - } - return; - } - - pendingPathRef.current = null; - updateStateForPage(page, options); - - if (targetPath) { - const method = replace ? "replaceState" : "pushState"; - const current = window.location.pathname + window.location.search; - if (replace || current !== targetPath) { - window.history[method]({}, "", targetPath); - } - } - }, - [pageToPath, updateStateForPage, user] - ); - - const navigateByPath = useCallback( - (path, { replace = false, allowUnauthenticated = false } = {}) => { - const { page, options } = interpretPath(path); - navigateTo(page, { ...(options || {}), replace, allowUnauthenticated }); - }, - [interpretPath, navigateTo] - ); - - const navigateToRef = useRef(navigateTo); - const navigateByPathRef = useRef(navigateByPath); - - useEffect(() => { - navigateToRef.current = navigateTo; - navigateByPathRef.current = navigateByPath; - }, [navigateTo, navigateByPath]); - - // Build breadcrumb items for current view - const breadcrumbs = React.useMemo(() => { - const items = []; - switch (currentPage) { - case "sites": - items.push({ label: "Sites", page: "sites" }); - items.push({ label: "Site List", page: "sites" }); - break; - case "devices": - items.push({ label: "Inventory", page: "devices" }); - items.push({ label: "Devices", page: "devices" }); - break; - case "device_details": - items.push({ label: "Devices", page: "devices" }); - items.push({ label: "Device List", page: "devices" }); - items.push({ label: "Device Details" }); - break; - case "jobs": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Scheduled Jobs", page: "jobs" }); - break; - case "create_job": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Scheduled Jobs", page: "jobs" }); - items.push({ label: editingJob ? "Edit Job" : "Create Job", page: "create_job" }); - break; - case "workflows": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Workflows", page: "workflows" }); - break; - case "workflow-editor": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Workflows", page: "workflows" }); - items.push({ label: "Flow Editor" }); - break; - case "scripts": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Scripts", page: "scripts" }); - break; - case "ansible_editor": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Ansible Playbooks", page: "assemblies" }); - items.push({ label: "Playbook Editor" }); - break; - case "assemblies": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Assemblies", page: "assemblies" }); - break; - case "community": - items.push({ label: "Automation", page: "jobs" }); - items.push({ label: "Community Content", page: "community" }); - break; - case "agent_devices": - items.push({ label: "Inventory", page: "devices" }); - items.push({ label: "Devices", page: "devices" }); - items.push({ label: "Agent Devices", page: "agent_devices" }); - break; - case "ssh_devices": - items.push({ label: "Inventory", page: "devices" }); - items.push({ label: "Devices", page: "devices" }); - items.push({ label: "SSH Devices", page: "ssh_devices" }); - break; - case "winrm_devices": - items.push({ label: "Inventory", page: "devices" }); - items.push({ label: "Devices", page: "devices" }); - items.push({ label: "WinRM Devices", page: "winrm_devices" }); - break; - case "access_credentials": - items.push({ label: "Access Management", page: "access_credentials" }); - items.push({ label: "Credentials", page: "access_credentials" }); - break; - case "access_github_token": - items.push({ label: "Access Management", page: "access_credentials" }); - items.push({ label: "GitHub API Token", page: "access_github_token" }); - break; - case "access_users": - items.push({ label: "Access Management", page: "access_credentials" }); - items.push({ label: "Users", page: "access_users" }); - break; - case "server_info": - items.push({ label: "Admin Settings" }); - items.push({ label: "Server Info", page: "server_info" }); - break; - case "admin_enrollment_codes": - items.push({ label: "Admin Settings", page: "server_info" }); - items.push({ label: "Installer Codes", page: "admin_enrollment_codes" }); - break; - case "admin_device_approvals": - items.push({ label: "Admin Settings", page: "server_info" }); - items.push({ label: "Device Approvals", page: "admin_device_approvals" }); - break; - case "filters": - items.push({ label: "Filters & Groups", page: "filters" }); - items.push({ label: "Filters", page: "filters" }); - break; - case "groups": - items.push({ label: "Filters & Groups", page: "filters" }); - items.push({ label: "Groups", page: "groups" }); - break; - default: - // Fallback to a neutral crumb if unknown - if (currentPage) items.push({ label: String(currentPage) }); - } - return items; - }, [currentPage, selectedDevice, editingJob]); - - useEffect(() => { - let canceled = false; - const hydrateSession = async () => { - const session = localStorage.getItem("borealis_session"); - if (session) { - try { - const data = JSON.parse(session); - if (Date.now() - data.timestamp < 3600 * 1000) { - if (!canceled) { - setUser(data.username); - setUserRole(data.role || null); - setUserDisplayName(data.display_name || data.username); - } - } else { - localStorage.removeItem("borealis_session"); - } - } catch { - localStorage.removeItem("borealis_session"); - } - } - - try { - const resp = await fetch('/api/auth/me', { credentials: 'include' }); - if (resp.ok) { - const me = await resp.json(); - if (!canceled) { - setUser(me.username); - setUserRole(me.role || null); - setUserDisplayName(me.display_name || me.username); - } - localStorage.setItem( - "borealis_session", - JSON.stringify({ username: me.username, display_name: me.display_name || me.username, role: me.role, timestamp: Date.now() }) - ); - } - } catch {} - - if (!canceled) { - setSessionResolved(true); - } - }; - - hydrateSession(); - return () => { - canceled = true; - }; - }, []); - - useEffect(() => { - if (!sessionResolved) return; - - const navTo = navigateToRef.current; - const navByPath = navigateByPathRef.current; - - if (user) { - const stored = initialPathRef.current; - const currentLocation = window.location.pathname + window.location.search; - const targetPath = - stored && stored !== "/login" - ? stored - : currentLocation === "/login" || currentLocation === "" - ? "/devices" - : currentLocation; - navByPath(targetPath, { replace: true, allowUnauthenticated: true }); - initialPathRef.current = null; - pendingPathRef.current = null; - } else { - const stored = initialPathRef.current; - const currentLocation = window.location.pathname + window.location.search; - const rememberPath = - stored && !stored.startsWith("/login") - ? stored - : !currentLocation.startsWith("/login") - ? currentLocation - : null; - if (rememberPath) { - pendingPathRef.current = rememberPath; - } - navTo("login", { replace: true, allowUnauthenticated: true, suppressPending: true }); - } - }, [sessionResolved, user]); - - useEffect(() => { - if (!sessionResolved) return; - - const handlePopState = () => { - const path = window.location.pathname + window.location.search; - if (!user) { - if (!path.startsWith("/login")) { - pendingPathRef.current = path; - } - navigateToRef.current("login", { replace: true, allowUnauthenticated: true, suppressPending: true }); - return; - } - navigateByPathRef.current(path, { replace: true, allowUnauthenticated: true }); - }; - - window.addEventListener("popstate", handlePopState); - return () => window.removeEventListener("popstate", handlePopState); - }, [sessionResolved, user]); - - // Suggest fetcher with debounce - const fetchSuggestions = useCallback((field, q) => { - const query = String(q || "").trim(); - if (query.length < 3) { - setSuggestions({ devices: [], sites: [], q: query, field }); - return; - } - const params = new URLSearchParams({ field, q: query, limit: "5" }); - fetch(`/api/search/suggest?${params.toString()}`) - .then((r) => (r.ok ? r.json() : { devices: [], sites: [], q: query, field })) - .then((data) => setSuggestions(data)) - .catch(() => setSuggestions({ devices: [], sites: [], q: query, field })); - }, []); - - useEffect(() => { - if (!searchOpen) return; - if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current); - searchDebounceRef.current = setTimeout(() => { - fetchSuggestions(searchCategory, searchQuery); - }, 220); - return () => { if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current); }; - }, [searchOpen, searchCategory, searchQuery, fetchSuggestions]); - - const execSearch = useCallback(async (field, q, navigateImmediate = true) => { - const cat = SEARCH_CATEGORIES.find((c) => c.key === field) || SEARCH_CATEGORIES[0]; - if (cat.scope === "site") { - try { - localStorage.setItem('site_list_initial_filters', JSON.stringify( - field === 'site_name' ? { name: q } : { description: q } - )); - } catch {} - if (navigateImmediate) navigateTo("sites"); - } else { - // device field - // Map API field -> Device_List filter key - const fieldMap = { - hostname: 'hostname', - description: 'description', - last_user: 'lastUser', - internal_ip: 'internalIp', - external_ip: 'externalIp', - serial_number: 'serialNumber', // placeholder (ignored by Device_List for now) - }; - const k = fieldMap[field] || 'hostname'; - const qLc = String(q || '').toLowerCase(); - const exact = (suggestions.devices || []).find((d) => String(d.hostname || d.value || '').toLowerCase() === qLc); - if (exact && (exact.hostname || '').trim()) { - const device = { hostname: exact.hostname.trim() }; - if (navigateImmediate) { - navigateTo('device_details', { device }); - } else { - setSelectedDevice(device); - } - } else if (field === 'hostname') { - // Probe device existence and open directly if found - try { - const resp = await fetch(`/api/device/details/${encodeURIComponent(q)}`); - if (resp.ok) { - const data = await resp.json(); - if (data && (data.summary?.hostname || Object.keys(data).length > 0)) { - const device = { hostname: q }; - if (navigateImmediate) { - navigateTo('device_details', { device }); - } else { - setSelectedDevice(device); - } - } else { - try { localStorage.setItem('device_list_initial_filters', JSON.stringify({ [k]: q })); } catch {} - if (navigateImmediate) navigateTo('devices'); - } - } else { - try { localStorage.setItem('device_list_initial_filters', JSON.stringify({ [k]: q })); } catch {} - if (navigateImmediate) navigateTo('devices'); - } - } catch { - try { localStorage.setItem('device_list_initial_filters', JSON.stringify({ [k]: q })); } catch {} - if (navigateImmediate) navigateTo('devices'); - } - } else { - try { - const payload = (k === 'serialNumber') ? {} : { [k]: q }; - localStorage.setItem('device_list_initial_filters', JSON.stringify(payload)); - } catch {} - if (navigateImmediate) navigateTo("devices"); - } - } - setSearchOpen(false); - }, [SEARCH_CATEGORIES, navigateTo, suggestions.devices]); - - const handleLoginSuccess = ({ username, role }) => { - setUser(username); - setUserRole(role || null); - setUserDisplayName(username); - localStorage.setItem( - "borealis_session", - JSON.stringify({ username, display_name: username, role: role || null, timestamp: Date.now() }) - ); - // Refresh full profile (to get display_name) in background - (async () => { - try { - const resp = await fetch('/api/auth/me', { credentials: 'include' }); - if (resp.ok) { - const me = await resp.json(); - setUserDisplayName(me.display_name || me.username); - localStorage.setItem( - "borealis_session", - JSON.stringify({ username: me.username, display_name: me.display_name || me.username, role: me.role, timestamp: Date.now() }) - ); - } - } catch {} - })(); - if (pendingPathRef.current) { - navigateByPath(pendingPathRef.current, { replace: true, allowUnauthenticated: true }); - pendingPathRef.current = null; - } else { - navigateTo('devices', { replace: true, allowUnauthenticated: true }); - } - }; - - useEffect(() => { - const saved = localStorage.getItem(LOCAL_STORAGE_KEY); - if (saved) { - try { - const parsed = JSON.parse(saved); - if (Array.isArray(parsed.tabs) && parsed.activeTabId) { - setTabs(parsed.tabs); - setActiveTabId(parsed.activeTabId); - } - } catch (err) { - console.warn("Failed to parse saved state:", err); - } - } - }, []); - - useEffect(() => { - const timeout = setTimeout(() => { - const data = JSON.stringify({ tabs, activeTabId }); - localStorage.setItem(LOCAL_STORAGE_KEY, data); - }, 1000); - return () => clearTimeout(timeout); - }, [tabs, activeTabId]); - - const handleSetNodes = useCallback((callbackOrArray, tId) => { - const targetId = tId || activeTabId; - setTabs((old) => - old.map((tab) => - tab.id === targetId - ? { ...tab, nodes: typeof callbackOrArray === "function" ? callbackOrArray(tab.nodes) : callbackOrArray } - : tab - ) - ); - }, [activeTabId]); - - const handleSetEdges = useCallback((callbackOrArray, tId) => { - const targetId = tId || activeTabId; - setTabs((old) => - old.map((tab) => - tab.id === targetId - ? { ...tab, edges: typeof callbackOrArray === "function" ? callbackOrArray(tab.edges) : callbackOrArray } - : tab - ) - ); - }, [activeTabId]); - - const handleUserMenuOpen = (event) => setUserMenuAnchorEl(event.currentTarget); - const handleUserMenuClose = () => setUserMenuAnchorEl(null); - const handleLogout = async () => { - try { - await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }); - } catch {} - try { localStorage.removeItem('borealis_session'); } catch {} - setUser(null); - setUserRole(null); - setUserDisplayName(null); - navigateTo('login', { replace: true, allowUnauthenticated: true, suppressPending: true }); - }; - - const handleTabRightClick = (evt, tabId) => { - evt.preventDefault(); - setTabMenuAnchor({ x: evt.clientX, y: evt.clientY }); - setTabMenuTabId(tabId); - }; - - const handleCloseTab = () => { - setTabs((prev) => { - const filtered = prev.filter((t) => t.id !== tabMenuTabId); - if (filtered.length === 0) { - const newTab = { id: "flow_1", tab_name: "Flow 1", nodes: [], edges: [] }; - setActiveTabId(newTab.id); - return [newTab]; - } - if (activeTabId === tabMenuTabId) { - setActiveTabId(filtered[0].id); - } - return filtered; - }); - setTabMenuAnchor(null); - }; - - const handleRenameTab = () => { - const tab = tabs.find((t) => t.id === tabMenuTabId); - if (tab) { - setRenameTabId(tabMenuTabId); - setRenameValue(tab.tab_name); - setRenameDialogOpen(true); - } - setTabMenuAnchor(null); - }; - - const handleSaveRename = () => { - setTabs((prev) => - prev.map((t) => (t.id === renameTabId ? { ...t, tab_name: renameValue } : t)) - ); - setRenameDialogOpen(false); - }; - - const handleExportFlow = useCallback(() => { - const tab = tabs.find((t) => t.id === activeTabId); - if (!tab) return; - const payload = { - tab_name: tab.tab_name, - nodes: tab.nodes, - edges: tab.edges - }; - const fileName = `${tab.tab_name || "workflow"}.json`; - const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = fileName; - a.click(); - URL.revokeObjectURL(url); - }, [tabs, activeTabId]); - - const handleImportFlow = useCallback(() => { - if (fileInputRef.current) { - fileInputRef.current.value = null; - fileInputRef.current.click(); - } - }, []); - - const onFileInputChange = useCallback( - (e) => { - const file = e.target.files && e.target.files[0]; - if (!file) return; - const reader = new FileReader(); - reader.onload = () => { - try { - const data = JSON.parse(reader.result); - const newId = "flow_" + Date.now(); - setTabs((prev) => [ - ...prev, - { - id: newId, - tab_name: - data.tab_name || data.name || file.name.replace(/\.json$/i, ""), - nodes: data.nodes || [], - edges: data.edges || [] - } - ]); - setActiveTabId(newId); - navigateTo("workflow-editor"); - } catch (err) { - console.error("Failed to import workflow:", err); - } - }; - reader.readAsText(file); - e.target.value = ""; - }, - [navigateTo, setTabs] - ); - - const handleSaveFlow = useCallback( - async (name) => { - const tab = tabs.find((t) => t.id === activeTabId); - if (!tab || !name) return; - const payload = { - path: tab.folderPath ? `${tab.folderPath}/${name}` : name, - workflow: { - tab_name: tab.tab_name, - nodes: tab.nodes, - edges: tab.edges - } - }; - try { - const body = { - island: 'workflows', - kind: 'file', - path: payload.path, - content: payload.workflow - }; - await fetch("/api/assembly/create", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(body) - }); - setTabs((prev) => - prev.map((t) => (t.id === activeTabId ? { ...t, tab_name: name } : t)) - ); - } catch (err) { - console.error("Failed to save workflow:", err); - } - }, - [tabs, activeTabId] - ); - - const isAdmin = (String(userRole || '').toLowerCase() === 'admin'); - - useEffect(() => { - const requiresAdmin = currentPage === 'server_info' - || currentPage === 'admin_enrollment_codes' - || currentPage === 'admin_device_approvals' - || currentPage === 'access_credentials' - || currentPage === 'access_github_token' - || currentPage === 'access_users' - || currentPage === 'ssh_devices' - || currentPage === 'winrm_devices' - || currentPage === 'agent_devices'; - if (!isAdmin && requiresAdmin) { - setNotAuthorizedOpen(true); - navigateTo('devices', { replace: true, suppressPending: true }); - } - }, [currentPage, isAdmin, navigateTo]); - - const renderMainContent = () => { - switch (currentPage) { - case "sites": - return ( - { - try { - localStorage.setItem('device_list_initial_site_filter', String(siteName || '')); - } catch {} - navigateTo("devices"); - }} - /> - ); - case "devices": - return ( - { - navigateTo("device_details", { device: d }); - }} - /> - ); - case "agent_devices": - return ( - { - navigateTo("device_details", { device: d }); - }} - /> - ); - case "ssh_devices": - return ; - case "winrm_devices": - return ; - - case "device_details": - return ( - { - navigateTo("devices"); - setSelectedDevice(null); - }} - /> - ); - - case "jobs": - return ( - { setEditingJob(null); navigateTo("create_job"); }} - onEditJob={(job) => { setEditingJob(job); navigateTo("create_job"); }} - refreshToken={jobsRefreshToken} - /> - ); - - case "create_job": - return ( - { navigateTo("jobs"); setEditingJob(null); }} - onCreated={() => { navigateTo("jobs"); setEditingJob(null); setJobsRefreshToken(Date.now()); }} - /> - ); - - case "workflows": - return ( - { - const newId = "flow_" + Date.now(); - if (workflow && workflow.rel_path) { - const folder = workflow.rel_path.split("/").slice(0, -1).join("/"); - try { - const resp = await fetch(`/api/assembly/load?island=workflows&path=${encodeURIComponent(workflow.rel_path)}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - setTabs([{ id: newId, tab_name: data.tab_name || workflow.name || workflow.file_name || "Workflow", nodes: data.nodes || [], edges: data.edges || [], folderPath: folder }]); - } catch (err) { - console.error("Failed to load workflow:", err); - setTabs([{ id: newId, tab_name: workflow?.name || "Workflow", nodes: [], edges: [], folderPath: folder }]); - } - } else { - setTabs([{ id: newId, tab_name: name || "Flow", nodes: [], edges: [], folderPath: folderPath || "" }]); - } - setActiveTabId(newId); - navigateTo("workflow-editor"); - }} - onOpenScript={(rel, mode, context) => { - const nonce = Date.now(); - setAssemblyEditorState({ - path: rel || '', - mode, - context: context ? { ...context, nonce } : null, - nonce - }); - navigateTo(mode === 'ansible' ? 'ansible_editor' : 'scripts', { - assemblyState: { - path: rel || '', - mode, - context: context ? { ...context, nonce } : null, - nonce - } - }); - }} - /> - ); - - case "assemblies": - return ( - { - const newId = "flow_" + Date.now(); - if (workflow && workflow.rel_path) { - const folder = workflow.rel_path.split("/").slice(0, -1).join("/"); - try { - const resp = await fetch(`/api/assembly/load?island=workflows&path=${encodeURIComponent(workflow.rel_path)}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - setTabs([{ id: newId, tab_name: data.tab_name || workflow.name || workflow.file_name || "Workflow", nodes: data.nodes || [], edges: data.edges || [], folderPath: folder }]); - } catch (err) { - console.error("Failed to load workflow:", err); - setTabs([{ id: newId, tab_name: workflow?.name || "Workflow", nodes: [], edges: [], folderPath: folder }]); - } - } else { - setTabs([{ id: newId, tab_name: name || "Flow", nodes: [], edges: [], folderPath: folderPath || "" }]); - } - setActiveTabId(newId); - navigateTo("workflow-editor"); - }} - onOpenScript={(rel, mode, context) => { - const nonce = Date.now(); - setAssemblyEditorState({ - path: rel || '', - mode, - context: context ? { ...context, nonce } : null, - nonce - }); - navigateTo(mode === 'ansible' ? 'ansible_editor' : 'scripts', { - assemblyState: { - path: rel || '', - mode, - context: context ? { ...context, nonce } : null, - nonce - } - }); - }} - /> - ); - - case "scripts": - return ( - - setAssemblyEditorState((prev) => (prev && prev.mode === 'scripts' ? null : prev)) - } - onSaved={() => navigateTo('assemblies')} - /> - ); - - case "ansible_editor": - return ( - - setAssemblyEditorState((prev) => (prev && prev.mode === 'ansible' ? null : prev)) - } - onSaved={() => navigateTo('assemblies')} - /> - ); - - case "access_credentials": - return ; - - case "access_github_token": - return ; - - case "access_users": - return ; - - case "server_info": - return ; - - case "admin_enrollment_codes": - return ; - - case "admin_device_approvals": - return ; - - case "workflow-editor": - return ( - - - setConfirmCloseOpen(true)} - fileInputRef={fileInputRef} - onFileInputChange={onFileInputChange} - currentTabName={tabs.find((t) => t.id === activeTabId)?.tab_name} - /> - - {}} - onTabRightClick={handleTabRightClick} - /> - - {tabs.map((tab) => ( - - - handleSetNodes(val, tab.id)} - setEdges={(val) => handleSetEdges(val, tab.id)} - nodeTypes={nodeTypes} - categorizedNodes={categorizedNodes} - /> - - - ))} - - - - - - ); - - default: - return ( - - Select a section from navigation. - - ); - } - }; - if (!user) { - return ( - - - - - ); - } - - return ( - - - - - - - {/* Breadcrumbs inline in top bar (transparent), aligned to content area */} - - } - aria-label="breadcrumb" - sx={{ - color: "#9aa0a6", - fontSize: "0.825rem", // 50% larger than previous - '& .MuiBreadcrumbs-separator': { mx: 0.6 }, - pointerEvents: 'auto' - }} - > - {breadcrumbs.map((c, idx) => { - if (c.page) { - return ( - - ); - } - return ( - - {c.label} - - ); - })} - - - {/* Top search: category + input */} - setSearchOpen(false)}> - - - setSearchMenuEl(null)} - PaperProps={{ sx: { bgcolor: '#1e1e1e', color: '#fff', minWidth: 240 } }} - > - {SEARCH_CATEGORIES.map((c) => ( - { setSearchCategory(c.key); setSearchMenuEl(null); setSearchQuery(''); setSuggestions({ devices: [], sites: [], q: '', field: '' }); }}> - {c.label} - - ))} - - - { setSearchQuery(e.target.value); setSearchOpen(true); }} - onFocus={() => setSearchOpen(true)} - onKeyDown={(e) => { - if (e.key === 'Enter') { - execSearch(searchCategory, searchQuery); - } else if (e.key === 'Escape') { - setSearchOpen(false); - } - }} - placeholder={(SEARCH_CATEGORIES.find(c => c.key === searchCategory) || {}).placeholder || 'Search'} - style={{ - outline: 'none', border: 'none', background: 'transparent', color: '#e8eaed', paddingLeft: 10, paddingRight: 28, width: 360, height: '100%' - }} - /> - - {searchOpen && (((SEARCH_CATEGORIES.find(c=>c.key===searchCategory)?.scope==='device') && (suggestions.devices||[]).length>0) || ((SEARCH_CATEGORIES.find(c=>c.key===searchCategory)?.scope==='site') && (suggestions.sites||[]).length>0)) && ( - - {/* Devices group */} - {((suggestions.devices || []).length > 0 && (SEARCH_CATEGORIES.find(c=>c.key===searchCategory)?.scope==='device')) && ( - - Devices - {suggestions.devices && suggestions.devices.length > 0 ? ( - suggestions.devices.map((d, idx) => { - const primary = (searchCategory === 'hostname') - ? highlightText(d.hostname || d.value, searchQuery) - : (d.hostname || d.value); - // Choose a secondary value based on category; fallback to best-available info - let secVal = ''; - if (searchCategory === 'internal_ip') secVal = d.internal_ip || ''; - else if (searchCategory === 'external_ip') secVal = d.external_ip || ''; - else if (searchCategory === 'description') secVal = d.description || ''; - else if (searchCategory === 'last_user') secVal = d.last_user || ''; - const secHighlighted = (searchCategory !== 'hostname' && secVal) - ? highlightText(secVal, searchQuery) - : (d.internal_ip || d.external_ip || d.description || d.last_user || ''); - return ( - { navigateTo('device_details', { device: { hostname: d.hostname || d.value } }); setSearchOpen(false); }} sx={{ px: 1.2, py: 0.6, '&:hover': { bgcolor: '#22272e' }, cursor: 'pointer' }}> - {primary} - - {d.site_name || ''}{(d.site_name && (secVal || (d.internal_ip || d.external_ip || d.description || d.last_user))) ? ' • ' : ''}{secHighlighted} - - - ); - }) - ) : ( - - {searchCategory === 'serial_number' ? 'Serial numbers are not tracked yet.' : 'No matches'} - - )} - - )} - {/* Sites group */} - {((suggestions.sites || []).length > 0 && (SEARCH_CATEGORIES.find(c=>c.key===searchCategory)?.scope==='site')) && ( - - Sites - {suggestions.sites && suggestions.sites.length > 0 ? ( - suggestions.sites.map((s, idx) => ( - execSearch(searchCategory, s.value)} sx={{ px: 1.2, py: 0.6, '&:hover': { bgcolor: '#22272e' }, cursor: 'pointer' }}> - {searchCategory === 'site_name' ? highlightText(s.site_name, searchQuery) : s.site_name} - {searchCategory === 'site_description' ? highlightText(s.site_description || '', searchQuery) : (s.site_description || '')} - - )) - ) : ( - No matches - )} - - )} - - )} - - - - {/* Spacer to keep user menu aligned right */} - - - - { handleUserMenuClose(); handleLogout(); }}> - Logout - - - - - - - *': { - alignSelf: 'stretch', - minHeight: 'calc(100% - 32px)' // account for typical m:2 top+bottom margins - } - }} - > - {renderMainContent()} - - - - setConfirmCloseOpen(false)} onConfirm={() => {}} /> - setRenameDialogOpen(false)} - onSave={handleSaveRename} - /> - setTabMenuAnchor(null)} - onRename={handleRenameTab} - onCloseTab={handleCloseTab} - /> - setNotAuthorizedOpen(false)} /> - - ); -} diff --git a/Data/Server/WebUI/src/Assemblies/Assembly_Editor.jsx b/Data/Server/WebUI/src/Assemblies/Assembly_Editor.jsx deleted file mode 100644 index f9ef28f3..00000000 --- a/Data/Server/WebUI/src/Assemblies/Assembly_Editor.jsx +++ /dev/null @@ -1,1269 +0,0 @@ -import React, { useEffect, useMemo, useRef, useState } from "react"; -import { - Box, - Paper, - Typography, - Button, - TextField, - MenuItem, - Grid, - FormControlLabel, - Checkbox, - IconButton, - Tooltip, - Dialog, - DialogTitle, - DialogContent, - DialogActions, - ListItemText -} from "@mui/material"; -import { Add as AddIcon, Delete as DeleteIcon, UploadFile as UploadFileIcon } from "@mui/icons-material"; -import Prism from "prismjs"; -import "prismjs/components/prism-yaml"; -import "prismjs/components/prism-bash"; -import "prismjs/components/prism-powershell"; -import "prismjs/components/prism-batch"; -import "prismjs/themes/prism-okaidia.css"; -import Editor from "react-simple-code-editor"; -import { ConfirmDeleteDialog } from "../Dialogs"; - -const TYPE_OPTIONS_ALL = [ - { key: "ansible", label: "Ansible Playbook", prism: "yaml" }, - { key: "powershell", label: "PowerShell Script", prism: "powershell" }, - { key: "batch", label: "Batch Script", prism: "batch" }, - { key: "bash", label: "Bash Script", prism: "bash" } -]; - -const CATEGORY_OPTIONS = [ - { key: "script", label: "Script" }, - { key: "application", label: "Application" } -]; - -const VARIABLE_TYPE_OPTIONS = [ - { key: "string", label: "String" }, - { key: "number", label: "Number" }, - { key: "boolean", label: "Boolean" }, - { key: "credential", label: "Credential" } -]; - -const BACKGROUND_COLORS = { - field: "#1C1C1C", /* Shared surface color for text fields, dropdown inputs, and script editors */ - sectionCard: "#2E2E2E", /* Background for section container cards */ - menuSelected: "rgba(88,166,255,0.16)", /* Background for selected dropdown items */ - menuSelectedHover: "rgba(88,166,255,0.24)", /* Background for hovered selected dropdown items */ - primaryActionSaving: "rgba(88,166,255,0.12)", /* Background for primary action button while saving */ - primaryActionHover: "rgba(88,166,255,0.18)", /* Background for primary action button hover state */ - dialog: "#1a1f27" /* Background for modal dialogs */ -}; - -const INPUT_BASE_SX = { - "& .MuiOutlinedInput-root": { - bgcolor: BACKGROUND_COLORS.field, - color: "#e6edf3", /* Text Color */ - borderRadius: 1, /* Roundness of UI Elements */ - minHeight: 40, - "& fieldset": { borderColor: "#2b3544" }, - "&:hover fieldset": { borderColor: "#3a4657" }, - "&.Mui-focused fieldset": { borderColor: "#58a6ff" } - }, - - "& .MuiOutlinedInput-input": { - padding: "9px 12px", - fontSize: "0.95rem", - lineHeight: 1.4 - }, - - "& .MuiOutlinedInput-inputMultiline": { - padding: "9px 12px" - }, - - "& .MuiInputLabel-root": { - color: "#9ba3b4", - transform: "translate(12px, 11px) scale(0.8)" // label at rest (inside field) - }, - "& .MuiInputLabel-root.Mui-focused": { color: "#58a6ff" }, - "& .MuiInputLabel-root.MuiInputLabel-shrink": { - transform: "translate(12px, -6px) scale(0.75)" // floated label position - }, - - "& input[type=number]": { MozAppearance: "textfield" }, - "& input[type=number]::-webkit-outer-spin-button": { WebkitAppearance: "none", margin: 0 }, - "& input[type=number]::-webkit-inner-spin-button": { WebkitAppearance: "none", margin: 0 } -}; - -const SELECT_BASE_SX = { - ...INPUT_BASE_SX, - "& .MuiSelect-select": { - padding: "10px 12px !important", - display: "flex", - alignItems: "center" - } -}; - -const SECTION_TITLE_SX = { - color: "#58a6ff", - fontWeight: 400, - fontSize: "14px", - letterSpacing: 0.2 -}; - -const SECTION_CARD_SX = { - bgcolor: BACKGROUND_COLORS.sectionCard, - borderRadius: 2, - border: "1px solid #262f3d", -}; - -const MENU_PROPS = { - PaperProps: { - sx: { - bgcolor: BACKGROUND_COLORS.field, - color: "#e6edf3", - border: "1px solid #2b3544", - "& .MuiMenuItem-root.Mui-selected": { - bgcolor: BACKGROUND_COLORS.menuSelected - }, - "& .MuiMenuItem-root.Mui-selected:hover": { - bgcolor: BACKGROUND_COLORS.menuSelectedHover - } - } - } -}; - -function keyBy(arr) { - return Object.fromEntries(arr.map((o) => [o.key, o])); -} - -const TYPE_MAP = keyBy(TYPE_OPTIONS_ALL); - -const PAGE_BACKGROUND = "#0d1117"; /* Color of Void Space Between Sidebar and Page */ - -function highlightedHtml(code, prismLang) { - try { - const grammar = Prism.languages[prismLang] || Prism.languages.markup; - return Prism.highlight(code ?? "", grammar, prismLang); - } catch { - return (code ?? "").replace(/[&<>]/g, (c) => ({ "&": "&", "<": "<", ">": ">" }[c])); - } -} - -function sanitizeFileName(name = "") { - const base = name.trim().replace(/[^a-zA-Z0-9._-]+/g, "_") || "assembly"; - return base.endsWith(".json") ? base : `${base}.json`; -} - -function normalizeFolderPath(path = "") { - if (!path) return ""; - return path - .replace(/\\/g, "/") - .replace(/^\/+|\/+$/g, "") - .replace(/\/+/g, "/"); -} - -function formatBytes(size) { - if (!size || Number.isNaN(size)) return "0 B"; - if (size < 1024) return `${size} B`; - const units = ["KB", "MB", "GB", "TB"]; - let idx = -1; - let s = size; - while (s >= 1024 && idx < units.length - 1) { - s /= 1024; - idx += 1; - } - return `${s.toFixed(1)} ${units[idx]}`; -} - -function defaultAssembly(defaultType = "powershell") { - return { - name: "", - description: "", - category: defaultType === "ansible" ? "application" : "script", - type: defaultType, - script: "", - timeoutSeconds: 3600, - sites: { mode: "all", values: [] }, - variables: [], - files: [] - }; -} - -function normalizeVariablesFromServer(vars = []) { - return (Array.isArray(vars) ? vars : []).map((v, idx) => ({ - id: `${Date.now()}_${idx}_${Math.random().toString(36).slice(2, 8)}`, - name: v?.name || v?.key || "", - label: v?.label || "", - type: v?.type || "string", - defaultValue: v?.default ?? v?.default_value ?? "", - required: Boolean(v?.required), - description: v?.description || "" - })); -} - -function decodeBase64String(data = "") { - if (typeof data !== "string") { - return { success: false, value: "" }; - } - - const trimmed = data.trim(); - if (!trimmed) { - return { success: true, value: "" }; - } - - const sanitized = trimmed.replace(/\s+/g, ""); - - try { - if (typeof window !== "undefined" && typeof window.atob === "function") { - const binary = window.atob(sanitized); - if (typeof TextDecoder !== "undefined") { - try { - const decoder = new TextDecoder("utf-8", { fatal: false }); - return { - success: true, - value: decoder.decode(Uint8Array.from(binary, (c) => c.charCodeAt(0))) - }; - } catch (err) { - // fall through to manual reconstruction - } - } - - let decoded = ""; - for (let i = 0; i < binary.length; i += 1) { - decoded += String.fromCharCode(binary.charCodeAt(i)); - } - try { - return { success: true, value: decodeURIComponent(escape(decoded)) }; - } catch (err) { - return { success: true, value: decoded }; - } - } - } catch (err) { - // fall through to Buffer fallback - } - - try { - if (typeof Buffer !== "undefined") { - return { success: true, value: Buffer.from(sanitized, "base64").toString("utf-8") }; - } - } catch (err) { - // ignore - } - - return { success: false, value: "" }; -} - -function encodeBase64String(text = "") { - if (typeof text !== "string") { - text = text == null ? "" : String(text); - } - if (!text) return ""; - try { - if (typeof TextEncoder !== "undefined" && typeof window !== "undefined" && typeof window.btoa === "function") { - const encoder = new TextEncoder(); - const bytes = encoder.encode(text); - let binary = ""; - bytes.forEach((b) => { binary += String.fromCharCode(b); }); - return window.btoa(binary); - } - } catch (err) { - // fall through to Buffer fallback - } - try { - if (typeof Buffer !== "undefined") { - return Buffer.from(text, "utf-8").toString("base64"); - } - } catch (err) { - // ignore - } - return ""; -} - -function normalizeFilesFromServer(files = []) { - return (Array.isArray(files) ? files : []).map((f, idx) => ({ - id: `${Date.now()}_${idx}_${Math.random().toString(36).slice(2, 8)}`, - fileName: f?.file_name || f?.name || "file.bin", - size: f?.size || 0, - mimeType: f?.mime_type || f?.mimeType || "", - data: f?.data || "" - })); -} - -function fromServerDocument(doc = {}, defaultType = "powershell") { - const assembly = defaultAssembly(defaultType); - if (doc && typeof doc === "object") { - assembly.name = doc.name || doc.display_name || assembly.name; - assembly.description = doc.description || ""; - assembly.category = doc.category || assembly.category; - assembly.type = doc.type || assembly.type; - const legacyScript = Array.isArray(doc.script_lines) - ? doc.script_lines.map((line) => (line == null ? "" : String(line))).join("\n") - : ""; - const script = doc.script ?? doc.content ?? legacyScript; - if (typeof script === "string") { - const encoding = (doc.script_encoding || doc.scriptEncoding || "").toLowerCase(); - if (["base64", "b64", "base-64"].includes(encoding)) { - const decoded = decodeBase64String(script); - assembly.script = decoded.success ? decoded.value : script; - } else if (!encoding) { - const decoded = decodeBase64String(script); - assembly.script = decoded.success ? decoded.value : script; - } else { - assembly.script = script; - } - } else { - assembly.script = legacyScript; - } - const timeout = doc.timeout_seconds ?? doc.timeout ?? assembly.timeoutSeconds; - assembly.timeoutSeconds = Number.isFinite(Number(timeout)) - ? Number(timeout) - : assembly.timeoutSeconds; - const sites = doc.sites || {}; - assembly.sites = { - mode: sites.mode || (Array.isArray(sites.values) && sites.values.length ? "specific" : "all"), - values: Array.isArray(sites.values) ? sites.values : [] - }; - assembly.variables = normalizeVariablesFromServer(doc.variables); - assembly.files = normalizeFilesFromServer(doc.files); - } - return assembly; -} - -function toServerDocument(assembly) { - const normalizedScript = typeof assembly.script === "string" - ? assembly.script.replace(/\r\n/g, "\n") - : ""; - const timeoutNumeric = Number(assembly.timeoutSeconds); - const timeoutSeconds = Number.isFinite(timeoutNumeric) ? Math.max(0, Math.round(timeoutNumeric)) : 3600; - const encodedScript = encodeBase64String(normalizedScript); - return { - version: 1, - name: assembly.name?.trim() || "", - description: assembly.description || "", - category: assembly.category || "script", - type: assembly.type || "powershell", - script: encodedScript, - script_encoding: "base64", - timeout_seconds: timeoutSeconds, - sites: { - mode: assembly.sites?.mode === "specific" ? "specific" : "all", - values: Array.isArray(assembly.sites?.values) - ? assembly.sites.values.filter((v) => v && v.trim()).map((v) => v.trim()) - : [] - }, - variables: (assembly.variables || []).map((v) => ({ - name: v.name?.trim() || "", - label: v.label || "", - type: v.type || "string", - default: v.defaultValue ?? "", - required: Boolean(v.required), - description: v.description || "" - })), - files: (assembly.files || []).map((f) => ({ - file_name: f.fileName || "file.bin", - size: f.size || 0, - mime_type: f.mimeType || "", - data: f.data || "" - })) - }; -} - -function RenameFileDialog({ open, value, onChange, onCancel, onSave }) { - return ( - - Rename Assembly File - - onChange(e.target.value)} - sx={INPUT_BASE_SX} - /> - - - - - - - ); -} - -export default function AssemblyEditor({ - mode = "scripts", - initialPath = "", - initialContext = null, - onConsumeInitialData, - onSaved -}) { - const isAnsible = mode === "ansible"; - const defaultType = isAnsible ? "ansible" : "powershell"; - const [assembly, setAssembly] = useState(() => defaultAssembly(defaultType)); - const [currentPath, setCurrentPath] = useState(""); - const [fileName, setFileName] = useState(""); - const [folderPath, setFolderPath] = useState(() => normalizeFolderPath(initialContext?.folder || "")); - const [renameOpen, setRenameOpen] = useState(false); - const [renameValue, setRenameValue] = useState(""); - const [deleteOpen, setDeleteOpen] = useState(false); - const [saving, setSaving] = useState(false); - const [siteOptions, setSiteOptions] = useState([]); - const [siteLoading, setSiteLoading] = useState(false); - const contextNonceRef = useRef(null); - - const TYPE_OPTIONS = useMemo( - () => (isAnsible ? TYPE_OPTIONS_ALL.filter((o) => o.key === "ansible") : TYPE_OPTIONS_ALL.filter((o) => o.key !== "ansible")), - [isAnsible] - ); - - const siteOptionMap = useMemo(() => { - const map = new Map(); - siteOptions.forEach((site) => { - if (!site) return; - const id = site.id != null ? String(site.id) : ""; - if (!id) return; - map.set(id, site); - }); - return map; - }, [siteOptions]); - - const island = isAnsible ? "ansible" : "scripts"; - - useEffect(() => { - if (!initialPath) return; - let canceled = false; - (async () => { - try { - const resp = await fetch(`/api/assembly/load?island=${encodeURIComponent(island)}&path=${encodeURIComponent(initialPath)}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - if (canceled) return; - const rel = data.rel_path || initialPath; - setCurrentPath(rel); - setFolderPath(normalizeFolderPath(rel.split("/").slice(0, -1).join("/"))); - setFileName(data.file_name || rel.split("/").pop() || ""); - const doc = fromServerDocument(data.assembly || data, defaultType); - setAssembly(doc); - } catch (err) { - console.error("Failed to load assembly:", err); - } finally { - if (!canceled && onConsumeInitialData) onConsumeInitialData(); - } - })(); - return () => { - canceled = true; - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initialPath, island]); - - useEffect(() => { - const ctx = initialContext; - if (!ctx || !ctx.nonce) return; - if (contextNonceRef.current === ctx.nonce) return; - contextNonceRef.current = ctx.nonce; - const doc = defaultAssembly(ctx.defaultType || defaultType); - if (ctx.name) doc.name = ctx.name; - if (ctx.description) doc.description = ctx.description; - if (ctx.category) doc.category = ctx.category; - if (ctx.type) doc.type = ctx.type; - setAssembly(doc); - setCurrentPath(""); - const suggested = ctx.suggestedFileName || ctx.name || ""; - setFileName(suggested ? sanitizeFileName(suggested) : ""); - setFolderPath(normalizeFolderPath(ctx.folder || "")); - if (onConsumeInitialData) onConsumeInitialData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initialContext?.nonce]); - - useEffect(() => { - let canceled = false; - const loadSites = async () => { - try { - setSiteLoading(true); - const resp = await fetch("/api/sites"); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - if (canceled) return; - const items = Array.isArray(data?.sites) ? data.sites : []; - setSiteOptions(items.map((s) => ({ ...s, id: s?.id != null ? String(s.id) : "" })).filter((s) => s.id)); - } catch (err) { - if (!canceled) { - console.error("Failed to load sites:", err); - setSiteOptions([]); - } - } finally { - if (!canceled) setSiteLoading(false); - } - }; - loadSites(); - return () => { - canceled = true; - }; - }, []); - - const prismLanguage = TYPE_MAP[assembly.type]?.prism || "powershell"; - - const updateAssembly = (partial) => { - setAssembly((prev) => ({ ...prev, ...partial })); - }; - - const updateSitesMode = (modeValue) => { - setAssembly((prev) => ({ - ...prev, - sites: { - mode: modeValue, - values: modeValue === "specific" ? prev.sites.values || [] : [] - } - })); - }; - - const updateSelectedSites = (values) => { - const arr = Array.isArray(values) - ? values - : typeof values === "string" - ? values.split(",").map((v) => v.trim()).filter(Boolean) - : []; - setAssembly((prev) => ({ - ...prev, - sites: { - mode: "specific", - values: arr.map((v) => String(v)) - } - })); - }; - - const addVariable = () => { - setAssembly((prev) => ({ - ...prev, - variables: [ - ...prev.variables, - { - id: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, - name: "", - label: "", - type: "string", - defaultValue: "", - required: false, - description: "" - } - ] - })); - }; - - const updateVariable = (id, partial) => { - setAssembly((prev) => ({ - ...prev, - variables: prev.variables.map((v) => (v.id === id ? { ...v, ...partial } : v)) - })); - }; - - const removeVariable = (id) => { - setAssembly((prev) => ({ - ...prev, - variables: prev.variables.filter((v) => v.id !== id) - })); - }; - - const handleFileUpload = async (event) => { - const files = Array.from(event.target.files || []); - if (!files.length) return; - const reads = files.map((file) => new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = () => { - const result = reader.result || ""; - const base64 = typeof result === "string" && result.includes(",") ? result.split(",", 2)[1] : result; - resolve({ - id: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, - fileName: file.name, - size: file.size, - mimeType: file.type, - data: base64 - }); - }; - reader.onerror = () => resolve(null); - reader.readAsDataURL(file); - })); - const uploaded = (await Promise.all(reads)).filter(Boolean); - if (uploaded.length) { - setAssembly((prev) => ({ ...prev, files: [...prev.files, ...uploaded] })); - } - event.target.value = ""; - }; - - const removeFile = (id) => { - setAssembly((prev) => ({ ...prev, files: prev.files.filter((f) => f.id !== id) })); - }; - - const computeTargetPath = () => { - if (currentPath) return currentPath; - const baseName = sanitizeFileName(fileName || assembly.name || (isAnsible ? "playbook" : "assembly")); - const folder = normalizeFolderPath(folderPath); - return folder ? `${folder}/${baseName}` : baseName; - }; - - const saveAssembly = async () => { - if (!assembly.name.trim()) { - alert("Assembly Name is required."); - return; - } - const payload = toServerDocument(assembly); - payload.type = assembly.type; - const targetPath = computeTargetPath(); - if (!targetPath) { - alert("Unable to determine file path."); - return; - } - setSaving(true); - try { - if (currentPath) { - const resp = await fetch(`/api/assembly/edit`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, path: currentPath, content: payload }) - }); - const data = await resp.json().catch(() => ({})); - if (!resp.ok) { - throw new Error(data?.error || `HTTP ${resp.status}`); - } - if (data?.rel_path) { - setCurrentPath(data.rel_path); - setFolderPath(normalizeFolderPath(data.rel_path.split("/").slice(0, -1).join("/"))); - setFileName(data.rel_path.split("/").pop() || fileName); - } - } else { - const resp = await fetch(`/api/assembly/create`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: "file", path: targetPath, content: payload, type: assembly.type }) - }); - const data = await resp.json(); - if (!resp.ok) throw new Error(data?.error || `HTTP ${resp.status}`); - if (data.rel_path) { - setCurrentPath(data.rel_path); - setFolderPath(data.rel_path.split("/").slice(0, -1).join("/")); - setFileName(data.rel_path.split("/").pop() || ""); - } else { - setCurrentPath(targetPath); - setFileName(targetPath.split("/").pop() || ""); - } - } - onSaved && onSaved(); - } catch (err) { - console.error("Failed to save assembly:", err); - alert(err.message || "Failed to save assembly"); - } finally { - setSaving(false); - } - }; - - const saveRename = async () => { - try { - const nextName = sanitizeFileName(renameValue || fileName || assembly.name); - const resp = await fetch(`/api/assembly/rename`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: "file", path: currentPath, new_name: nextName, type: assembly.type }) - }); - const data = await resp.json(); - if (!resp.ok) throw new Error(data?.error || `HTTP ${resp.status}`); - const rel = data.rel_path || currentPath; - setCurrentPath(rel); - setFolderPath(rel.split("/").slice(0, -1).join("/")); - setFileName(rel.split("/").pop() || nextName); - setRenameOpen(false); - } catch (err) { - console.error("Failed to rename assembly:", err); - alert(err.message || "Failed to rename"); - setRenameOpen(false); - } - }; - - const deleteAssembly = async () => { - if (!currentPath) { - setDeleteOpen(false); - return; - } - try { - const resp = await fetch(`/api/assembly/delete`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: "file", path: currentPath }) - }); - if (!resp.ok) { - const data = await resp.json().catch(() => ({})); - throw new Error(data?.error || `HTTP ${resp.status}`); - } - setDeleteOpen(false); - setAssembly(defaultAssembly(defaultType)); - setCurrentPath(""); - setFileName(""); - onSaved && onSaved(); - } catch (err) { - console.error("Failed to delete assembly:", err); - alert(err.message || "Failed to delete assembly"); - setDeleteOpen(false); - } - }; - - const siteScopeValue = assembly.sites?.mode === "specific" ? "specific" : "all"; - const selectedSiteValues = Array.isArray(assembly.sites?.values) - ? assembly.sites.values.map((v) => String(v)) - : []; - - return ( - - - - - - {/* Left half */} - - - Assembly Editor - Create and edit variables, scripts, and other fields related to assemblies. - - - - {/* Right half */} - - - {currentPath ? ( - - - - ) : null} - {currentPath ? ( - - - - ) : null} - - - - - - - - - - Overview - - - - - - - updateAssembly({ name: e.target.value })} - fullWidth - variant="outlined" - sx={{ ...INPUT_BASE_SX, mb: 2 }} - /> - updateAssembly({ description: e.target.value })} - multiline - minRows={2} - maxRows={8} - fullWidth - variant="outlined" - sx={{ - ...INPUT_BASE_SX, - "& .MuiOutlinedInput-inputMultiline": { - padding: "6px 12px", - lineHeight: 1.4 - } - }} - /> - - - updateAssembly({ category: e.target.value })} - sx={{ ...SELECT_BASE_SX, mb: 2 }} - SelectProps={{ MenuProps: MENU_PROPS }} - > - {CATEGORY_OPTIONS.map((o) => ( - {o.label} - ))} - - - updateAssembly({ type: e.target.value })} - sx={SELECT_BASE_SX} - SelectProps={{ MenuProps: MENU_PROPS }} - > - {TYPE_OPTIONS.map((o) => ( - {o.label} - ))} - - - - - - - Script Content - - - updateAssembly({ script: value })} - highlight={(src) => highlightedHtml(src, prismLanguage)} - padding={12} - placeholder={currentPath ? `Editing: ${currentPath}` : "Start typing your script..."} - style={{ - fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace', - fontSize: 14, - color: "#e6edf3", - background: BACKGROUND_COLORS.field, /* Color of Script Box */ - outline: "none", - minHeight: 320, - lineHeight: 1.45, - caretColor: "#58a6ff" - }} - /> - - - - - - { - const nextValue = e.target.value.replace(/[^0-9]/g, ""); - updateAssembly({ timeoutSeconds: nextValue ? Number(nextValue) : 0 }); - }} - fullWidth - variant="outlined" - sx={INPUT_BASE_SX} - helperText="Timeout this script if not completed within X seconds" - /> - - - - updateSitesMode(e.target.value)} - sx={{ - ...SELECT_BASE_SX, - width: { xs: "100%", sm: 320, lg: 360 } - }} - SelectProps={{ MenuProps: MENU_PROPS }} - > - All Sites - Specific Sites - - {siteScopeValue === "specific" ? ( - updateSelectedSites(Array.isArray(e.target.value) ? e.target.value : [])} - sx={{ - ...SELECT_BASE_SX, - width: { xs: "100%", sm: 360, lg: 420 } - }} - SelectProps={{ - multiple: true, - renderValue: (selected) => { - if (!selected || selected.length === 0) { - return Select sites; - } - const names = selected.map((val) => siteOptionMap.get(String(val))?.name || String(val)); - return names.join(", "); - }, - MenuProps: MENU_PROPS - }} - > - {siteLoading ? ( - - - - ) : siteOptions.length ? ( - siteOptions.map((site) => { - const value = String(site.id); - const checked = selectedSiteValues.includes(value); - return ( - - - - - ); - }) - ) : ( - - - - )} - - ) : null} - - - - - - - Environment Variables - - - Variables are dynamically passed into the script as environment variables at runtime. They are written like $env:variableName in the script editor. - - {(assembly.variables || []).length ? ( - - {assembly.variables.map((variable) => ( - - - - - - updateVariable(variable.id, { name: e.target.value })} - fullWidth - variant="outlined" - sx={INPUT_BASE_SX} - /> - - - - - updateVariable(variable.id, { label: e.target.value })} - fullWidth - variant="outlined" - sx={INPUT_BASE_SX} - /> - - - - - - updateVariable(variable.id, { type: e.target.value })} - sx={SELECT_BASE_SX} - SelectProps={{ MenuProps: MENU_PROPS }} - > - {VARIABLE_TYPE_OPTIONS.map((opt) => ( - {opt.label} - ))} - - - - - {variable.type === "boolean" ? ( - - updateVariable(variable.id, { defaultValue: e.target.checked })} - sx={{ color: "#58a6ff" }} - /> - } - label="Default Value" - sx={{ - color: "#9ba3b4", - m: 0, - "& .MuiFormControlLabel-label": { fontSize: "0.95rem" } - }} - /> - - ) : ( - - updateVariable(variable.id, { defaultValue: e.target.value })} - fullWidth - variant="outlined" - sx={INPUT_BASE_SX} - /> - - )} - - - - updateVariable(variable.id, { description: e.target.value })} - fullWidth - variant="outlined" - sx={INPUT_BASE_SX} - /> - - - - - removeVariable(variable.id)} sx={{ color: "#ff6b6b" }}> - - - - - - - - - Required - - - updateVariable(variable.id, { required: e.target.checked }) - } - sx={{ - color: "#58a6ff", - p: 0.5, - }} - inputProps={{ "aria-label": "Required" }} - /> - - - - - ))} - - ) : ( - - No variables have been defined. - - )} - - - - - - Files - - - Upload supporting files. They will be embedded as Base64 and available to the assembly at runtime. - - {(assembly.files || []).length ? ( - - {assembly.files.map((file) => ( - - - {file.fileName} - {formatBytes(file.size)}{file.mimeType ? ` • ${file.mimeType}` : ""} - - removeFile(file.id)} sx={{ color: "#ff6b6b" }}> - - - - ))} - - ) : ( - - No files uploaded yet. - - )} - - - - - - - setRenameOpen(false)} - onSave={saveRename} - /> - setDeleteOpen(false)} - onConfirm={deleteAssembly} - /> - - ); -} \ No newline at end of file diff --git a/Data/Server/WebUI/src/Assemblies/Assembly_List.jsx b/Data/Server/WebUI/src/Assemblies/Assembly_List.jsx deleted file mode 100644 index 5f5fc1dd..00000000 --- a/Data/Server/WebUI/src/Assemblies/Assembly_List.jsx +++ /dev/null @@ -1,777 +0,0 @@ -import React, { useState, useEffect, useCallback } from "react"; -import { Paper, Box, Typography, Menu, MenuItem, Button } from "@mui/material"; -import { Folder as FolderIcon, Description as DescriptionIcon, Polyline as WorkflowsIcon, Code as ScriptIcon, MenuBook as BookIcon } from "@mui/icons-material"; -import { - SimpleTreeView, - TreeItem, - useTreeViewApiRef -} from "@mui/x-tree-view"; -import { - RenameWorkflowDialog, - RenameFolderDialog, - NewWorkflowDialog, - ConfirmDeleteDialog -} from "../Dialogs"; - -// Generic Island wrapper with large icon, stacked title/description, and actions on the right -const Island = ({ title, description, icon, actions, children, sx }) => ( - - - - {icon ? ( - - {icon} - - ) : null} - - - {title} - - {description ? ( - - {description} - - ) : null} - - - {actions ? ( - - {actions} - - ) : null} - - {children} - -); - -// ---------------- Workflows Island ----------------- -const sortTree = (node) => { - if (!node || !Array.isArray(node.children)) return; - node.children.sort((a, b) => { - const aFolder = Boolean(a.isFolder); - const bFolder = Boolean(b.isFolder); - if (aFolder !== bFolder) return aFolder ? -1 : 1; - return String(a.label || "").localeCompare(String(b.label || ""), undefined, { - sensitivity: "base" - }); - }); - node.children.forEach(sortTree); -}; - -function buildWorkflowTree(workflows, folders) { - const map = {}; - const rootNode = { id: "root", label: "Workflows", path: "", isFolder: true, children: [] }; - map[rootNode.id] = rootNode; - (folders || []).forEach((f) => { - const parts = (f || "").split("/"); - let children = rootNode.children; - let parentPath = ""; - parts.forEach((part) => { - const path = parentPath ? `${parentPath}/${part}` : part; - let node = children.find((n) => n.id === path); - if (!node) { - node = { id: path, label: part, path, isFolder: true, children: [] }; - children.push(node); - map[path] = node; - } - children = node.children; - parentPath = path; - }); - }); - (workflows || []).forEach((w) => { - const parts = (w.rel_path || "").split("/"); - let children = rootNode.children; - let parentPath = ""; - parts.forEach((part, idx) => { - const path = parentPath ? `${parentPath}/${part}` : part; - const isFile = idx === parts.length - 1; - let node = children.find((n) => n.id === path); - if (!node) { - node = { - id: path, - label: isFile ? ((w.tab_name && w.tab_name.trim()) || w.file_name) : part, - path, - isFolder: !isFile, - fileName: w.file_name, - workflow: isFile ? w : null, - children: [] - }; - children.push(node); - map[path] = node; - } - if (!isFile) { - children = node.children; - parentPath = path; - } - }); - }); - sortTree(rootNode); - return { root: [rootNode], map }; -} - -function WorkflowsIsland({ onOpenWorkflow }) { - const [tree, setTree] = useState([]); - const [nodeMap, setNodeMap] = useState({}); - const [contextMenu, setContextMenu] = useState(null); - const [selectedNode, setSelectedNode] = useState(null); - const [renameValue, setRenameValue] = useState(""); - const [renameOpen, setRenameOpen] = useState(false); - const [renameFolderOpen, setRenameFolderOpen] = useState(false); - const [folderDialogMode, setFolderDialogMode] = useState("rename"); - const [newWorkflowOpen, setNewWorkflowOpen] = useState(false); - const [newWorkflowName, setNewWorkflowName] = useState(""); - const [deleteOpen, setDeleteOpen] = useState(false); - const apiRef = useTreeViewApiRef(); - const [dragNode, setDragNode] = useState(null); - - const handleDrop = async (target) => { - if (!dragNode || !target.isFolder) return; - if (dragNode.path === target.path || target.path.startsWith(`${dragNode.path}/`)) { - setDragNode(null); - return; - } - const newPath = target.path ? `${target.path}/${dragNode.fileName}` : dragNode.fileName; - try { - await fetch("/api/assembly/move", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island: 'workflows', kind: 'file', path: dragNode.path, new_path: newPath }) - }); - loadTree(); - } catch (err) { - console.error("Failed to move workflow:", err); - } - setDragNode(null); - }; - - const loadTree = useCallback(async () => { - try { - const resp = await fetch(`/api/assembly/list?island=workflows`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - const { root, map } = buildWorkflowTree(data.items || [], data.folders || []); - setTree(root); - setNodeMap(map); - } catch (err) { - console.error("Failed to load workflows:", err); - setTree([]); - setNodeMap({}); - } - }, []); - - useEffect(() => { loadTree(); }, [loadTree]); - - const handleContextMenu = (e, node) => { - e.preventDefault(); - setSelectedNode(node); - setContextMenu( - contextMenu === null ? { mouseX: e.clientX - 2, mouseY: e.clientY - 4 } : null - ); - }; - - const handleRename = () => { - setContextMenu(null); - if (!selectedNode) return; - setRenameValue(selectedNode.label); - if (selectedNode.isFolder) { - setFolderDialogMode("rename"); - setRenameFolderOpen(true); - } else setRenameOpen(true); - }; - - const handleEdit = () => { - setContextMenu(null); - if (selectedNode && !selectedNode.isFolder && onOpenWorkflow) { - onOpenWorkflow(selectedNode.workflow); - } - }; - - const handleDelete = () => { - setContextMenu(null); - if (!selectedNode) return; - setDeleteOpen(true); - }; - - const handleNewFolder = () => { - if (!selectedNode) return; - setContextMenu(null); - setFolderDialogMode("create"); - setRenameValue(""); - setRenameFolderOpen(true); - }; - - const handleNewWorkflow = () => { - if (!selectedNode) return; - setContextMenu(null); - setNewWorkflowName(""); - setNewWorkflowOpen(true); - }; - - const saveRenameWorkflow = async () => { - if (!selectedNode) return; - try { - await fetch("/api/assembly/rename", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island: 'workflows', kind: 'file', path: selectedNode.path, new_name: renameValue }) - }); - loadTree(); - } catch (err) { - console.error("Failed to rename workflow:", err); - } - setRenameOpen(false); - }; - - const saveRenameFolder = async () => { - try { - if (folderDialogMode === "rename" && selectedNode) { - await fetch("/api/assembly/rename", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island: 'workflows', kind: 'folder', path: selectedNode.path, new_name: renameValue }) - }); - } else { - const basePath = selectedNode ? selectedNode.path : ""; - const newPath = basePath ? `${basePath}/${renameValue}` : renameValue; - await fetch("/api/assembly/create", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island: 'workflows', kind: 'folder', path: newPath }) - }); - } - loadTree(); - } catch (err) { - console.error("Folder operation failed:", err); - } - setRenameFolderOpen(false); - }; - - const handleNodeSelect = (_event, itemId) => { - const node = nodeMap[itemId]; - if (node && !node.isFolder && onOpenWorkflow) { - onOpenWorkflow(node.workflow); - } - }; - - const confirmDelete = async () => { - if (!selectedNode) return; - try { - if (selectedNode.isFolder) { - await fetch("/api/assembly/delete", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island: 'workflows', kind: 'folder', path: selectedNode.path }) - }); - } else { - await fetch("/api/assembly/delete", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island: 'workflows', kind: 'file', path: selectedNode.path }) - }); - } - loadTree(); - } catch (err) { - console.error("Failed to delete:", err); - } - setDeleteOpen(false); - }; - - const renderItems = (nodes) => - (nodes || []).map((n) => ( - !n.isFolder && setDragNode(n)} - onDragOver={(e) => { if (dragNode && n.isFolder) e.preventDefault(); }} - onDrop={(e) => { e.preventDefault(); handleDrop(n); }} - onContextMenu={(e) => handleContextMenu(e, n)} - > - {n.isFolder ? ( - - ) : ( - - )} - {n.label} - - } - > - {n.children && n.children.length > 0 ? renderItems(n.children) : null} - - )); - - const rootChildIds = tree[0]?.children?.map((c) => c.id) || []; - - return ( - } - actions={ - - } - > - { if (dragNode) e.preventDefault(); }} - onDrop={(e) => { e.preventDefault(); handleDrop({ path: "", isFolder: true }); }} - > - - {renderItems(tree)} - - - setContextMenu(null)} - anchorReference="anchorPosition" - anchorPosition={contextMenu ? { top: contextMenu.mouseY, left: contextMenu.mouseX } : undefined} - PaperProps={{ sx: { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" } }} - > - {selectedNode?.isFolder && ( - <> - New Workflow - New Subfolder - {selectedNode.id !== "root" && (Rename)} - {selectedNode.id !== "root" && (Delete)} - - )} - {!selectedNode?.isFolder && ( - <> - Edit - Rename - Delete - - )} - - setRenameOpen(false)} onSave={saveRenameWorkflow} /> - setRenameFolderOpen(false)} onSave={saveRenameFolder} title={folderDialogMode === "rename" ? "Rename Folder" : "New Folder"} confirmText={folderDialogMode === "rename" ? "Save" : "Create"} /> - setNewWorkflowOpen(false)} onCreate={() => { setNewWorkflowOpen(false); onOpenWorkflow && onOpenWorkflow(null, selectedNode?.path || "", newWorkflowName); }} /> - setDeleteOpen(false)} onConfirm={confirmDelete} /> - - ); -} - -// ---------------- Generic Scripts-like Islands (used for Scripts and Ansible) ----------------- -function buildFileTree(rootLabel, items, folders) { - // Some backends (e.g. /api/scripts) return paths relative to - // the Assemblies root, which prefixes items with a top-level - // folder like "Scripts". Others (e.g. /api/ansible) already - // return paths relative to their specific root. Normalize by - // stripping a matching top-level segment so the UI shows - // "Scripts/<...>" rather than "Scripts/Scripts/<...>". - const normalize = (p) => { - const candidates = [ - String(rootLabel || "").trim(), - String(rootLabel || "").replace(/\s+/g, "_") - ].filter(Boolean); - const parts = String(p || "").replace(/\\/g, "/").split("/").filter(Boolean); - if (parts.length && candidates.includes(parts[0])) parts.shift(); - return parts; - }; - - const map = {}; - const rootNode = { id: "root", label: rootLabel, path: "", isFolder: true, children: [] }; - map[rootNode.id] = rootNode; - - (folders || []).forEach((f) => { - const parts = normalize(f); - let children = rootNode.children; - let parentPath = ""; - parts.forEach((part) => { - const path = parentPath ? `${parentPath}/${part}` : part; - let node = children.find((n) => n.id === path); - if (!node) { - node = { id: path, label: part, path, isFolder: true, children: [] }; - children.push(node); - map[path] = node; - } - children = node.children; - parentPath = path; - }); - }); - - (items || []).forEach((s) => { - const parts = normalize(s?.rel_path); - let children = rootNode.children; - let parentPath = ""; - parts.forEach((part, idx) => { - const path = parentPath ? `${parentPath}/${part}` : part; - const isFile = idx === parts.length - 1; - let node = children.find((n) => n.id === path); - if (!node) { - node = { - id: path, - label: isFile ? (s.name || s.display_name || s.file_name || part) : part, - path, - isFolder: !isFile, - fileName: s.file_name, - meta: isFile ? s : null, - children: [] - }; - children.push(node); - map[path] = node; - } - if (!isFile) { - children = node.children; - parentPath = path; - } - }); - }); - sortTree(rootNode); - return { root: [rootNode], map }; -} - -function ScriptsLikeIsland({ - title, - description, - rootLabel, - baseApi, // e.g. '/api/scripts' or '/api/ansible' - newItemLabel = "New Script", - onEdit // (rel_path) => void -}) { - const [tree, setTree] = useState([]); - const [nodeMap, setNodeMap] = useState({}); - const [contextMenu, setContextMenu] = useState(null); - const [selectedNode, setSelectedNode] = useState(null); - const [renameValue, setRenameValue] = useState(""); - const [renameOpen, setRenameOpen] = useState(false); - const [renameFolderOpen, setRenameFolderOpen] = useState(false); - const [folderDialogMode, setFolderDialogMode] = useState("rename"); - const [newItemOpen, setNewItemOpen] = useState(false); - const [newItemName, setNewItemName] = useState(""); - const [deleteOpen, setDeleteOpen] = useState(false); - const apiRef = useTreeViewApiRef(); - const [dragNode, setDragNode] = useState(null); - - const island = React.useMemo(() => { - const b = String(baseApi || '').toLowerCase(); - return b.endsWith('/api/ansible') ? 'ansible' : 'scripts'; - }, [baseApi]); - - const loadTree = useCallback(async () => { - try { - const resp = await fetch(`/api/assembly/list?island=${encodeURIComponent(island)}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - const { root, map } = buildFileTree(rootLabel, data.items || [], data.folders || []); - setTree(root); - setNodeMap(map); - } catch (err) { - console.error(`Failed to load ${title}:`, err); - setTree([]); - setNodeMap({}); - } - }, [island, title, rootLabel]); - - useEffect(() => { loadTree(); }, [loadTree]); - - const handleContextMenu = (e, node) => { - e.preventDefault(); - setSelectedNode(node); - setContextMenu( - contextMenu === null ? { mouseX: e.clientX - 2, mouseY: e.clientY - 4 } : null - ); - }; - - const handleDrop = async (target) => { - if (!dragNode || !target.isFolder) return; - if (dragNode.path === target.path || target.path.startsWith(`${dragNode.path}/`)) { - setDragNode(null); - return; - } - const newPath = target.path ? `${target.path}/${dragNode.fileName}` : dragNode.fileName; - try { - await fetch(`/api/assembly/move`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: 'file', path: dragNode.path, new_path: newPath }) - }); - loadTree(); - } catch (err) { - console.error("Failed to move:", err); - } - setDragNode(null); - }; - - const handleNodeSelect = async (_e, itemId) => { - const node = nodeMap[itemId]; - if (node && !node.isFolder) { - setContextMenu(null); - onEdit && onEdit(node.path); - } - }; - - const saveRenameFile = async () => { - try { - const payload = { island, kind: 'file', path: selectedNode.path, new_name: renameValue }; - // preserve extension for scripts when no extension provided - if (selectedNode?.meta?.type) payload.type = selectedNode.meta.type; - const res = await fetch(`/api/assembly/rename`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - }); - const data = await res.json(); - if (!res.ok) throw new Error(data?.error || `HTTP ${res.status}`); - setRenameOpen(false); - loadTree(); - } catch (err) { - console.error("Failed to rename file:", err); - setRenameOpen(false); - } - }; - - const saveRenameFolder = async () => { - try { - if (folderDialogMode === "rename" && selectedNode) { - await fetch(`/api/assembly/rename`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: 'folder', path: selectedNode.path, new_name: renameValue }) - }); - } else { - const basePath = selectedNode ? selectedNode.path : ""; - const newPath = basePath ? `${basePath}/${renameValue}` : renameValue; - await fetch(`/api/assembly/create`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: 'folder', path: newPath }) - }); - } - setRenameFolderOpen(false); - loadTree(); - } catch (err) { - console.error("Folder operation failed:", err); - setRenameFolderOpen(false); - } - }; - - const confirmDelete = async () => { - if (!selectedNode) return; - try { - if (selectedNode.isFolder) { - await fetch(`/api/assembly/delete`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: 'folder', path: selectedNode.path }) - }); - } else { - await fetch(`/api/assembly/delete`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ island, kind: 'file', path: selectedNode.path }) - }); - } - setDeleteOpen(false); - loadTree(); - } catch (err) { - console.error("Failed to delete:", err); - setDeleteOpen(false); - } - }; - - const createNewItem = () => { - const trimmedName = (newItemName || '').trim(); - const folder = selectedNode?.isFolder - ? selectedNode.path - : (selectedNode?.path?.split("/").slice(0, -1).join("/") || ""); - const context = { - folder, - suggestedFileName: trimmedName, - defaultType: island === 'ansible' ? 'ansible' : 'powershell', - type: island === 'ansible' ? 'ansible' : 'powershell', - category: island === 'ansible' ? 'application' : 'script' - }; - setNewItemOpen(false); - setNewItemName(""); - onEdit && onEdit(null, context); - }; - - const renderItems = (nodes) => - (nodes || []).map((n) => ( - !n.isFolder && setDragNode(n)} - onDragOver={(e) => { if (dragNode && n.isFolder) e.preventDefault(); }} - onDrop={(e) => { e.preventDefault(); handleDrop(n); }} - onContextMenu={(e) => handleContextMenu(e, n)} - onDoubleClick={() => { if (!n.isFolder) onEdit && onEdit(n.path); }} - > - {n.isFolder ? ( - - ) : ( - - )} - {n.label} - - } - > - {n.children && n.children.length > 0 ? renderItems(n.children) : null} - - )); - - const rootChildIds = tree[0]?.children?.map((c) => c.id) || []; - - return ( - : } - actions={ - - } - > - { if (dragNode) e.preventDefault(); }} - onDrop={(e) => { e.preventDefault(); handleDrop({ path: "", isFolder: true }); }} - > - - {renderItems(tree)} - - - setContextMenu(null)} - anchorReference="anchorPosition" - anchorPosition={contextMenu ? { top: contextMenu.mouseY, left: contextMenu.mouseX } : undefined} - PaperProps={{ sx: { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" } }} - > - {selectedNode?.isFolder && ( - <> - { setContextMenu(null); setNewItemOpen(true); }}>{newItemLabel} - { setContextMenu(null); setFolderDialogMode("create"); setRenameValue(""); setRenameFolderOpen(true); }}>New Subfolder - {selectedNode.id !== "root" && ( { setContextMenu(null); setRenameValue(selectedNode.label); setRenameOpen(true); }}>Rename)} - {selectedNode.id !== "root" && ( { setContextMenu(null); setDeleteOpen(true); }}>Delete)} - - )} - {!selectedNode?.isFolder && ( - <> - { setContextMenu(null); onEdit && onEdit(selectedNode.path); }}>Edit - { setContextMenu(null); setRenameValue(selectedNode.label); setRenameOpen(true); }}>Rename - { setContextMenu(null); setDeleteOpen(true); }}>Delete - - )} - - {/* Simple inline dialogs using shared components */} - setRenameFolderOpen(false)} onSave={saveRenameFolder} title={folderDialogMode === "rename" ? "Rename Folder" : "New Folder"} confirmText={folderDialogMode === "rename" ? "Save" : "Create"} /> - {/* File rename */} -
} sx={{ display: renameOpen ? 'block' : 'none' }}> -
- - Rename - setRenameValue(e.target.value)} style={{ width: '100%', padding: 8, background: '#2a2a2a', color: '#ccc', border: '1px solid #444', borderRadius: 4 }} /> - - - - - -
- -
} sx={{ display: newItemOpen ? 'block' : 'none' }}> -
- - {newItemLabel} - setNewItemName(e.target.value)} placeholder="Name" style={{ width: '100%', padding: 8, background: '#2a2a2a', color: '#ccc', border: '1px solid #444', borderRadius: 4 }} /> - - - - - -
- - setDeleteOpen(false)} onConfirm={confirmDelete} /> - - ); -} - -export default function AssemblyList({ onOpenWorkflow, onOpenScript }) { - return ( - - - Assemblies - Collections of various types of components used to perform various automations upon targeted devices. - - - - {/* Left: Workflows */} - - - {/* Middle: Scripts */} - onOpenScript && onOpenScript(rel, 'scripts', ctx)} - /> - - {/* Right: Ansible Playbooks */} - onOpenScript && onOpenScript(rel, 'ansible', ctx)} - /> - - - - ); -} diff --git a/Data/Server/WebUI/src/Borealis.css b/Data/Server/WebUI/src/Borealis.css deleted file mode 100644 index f2881d9a..00000000 --- a/Data/Server/WebUI/src/Borealis.css +++ /dev/null @@ -1,252 +0,0 @@ -/* ///////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Borealis.css - -body { - font-family: "IBM Plex Sans", "Helvetica Neue", Arial, sans-serif; - background-color: #0b0f19; - color: #f5f7fa; -} - -/* ======================================= */ -/* FLOW EDITOR */ -/* ======================================= */ - -/* FlowEditor background container */ -.flow-editor-container { - position: relative; - width: 100%; - height: 100%; - overflow: hidden; -} - -/* Blue Gradient Overlay */ -.flow-editor-container::before { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - background: linear-gradient( - to bottom, - rgba(9, 44, 68, 0.9) 0%, - rgba(30, 30, 30, 0) 45%, - rgba(30, 30, 30, 0) 75%, - rgba(9, 44, 68, 0.7) 100% - ); - z-index: -1; -} - -/* helper lines for snapping */ -.helper-line { - position: absolute; - background: #0074ff; - z-index: 10; - pointer-events: none; - } - - .helper-line-vertical { - width: 1px; - height: 100%; - } - - .helper-line-horizontal { - height: 1px; - width: 100%; - } - -/* ======================================= */ -/* NODE SIDEBAR */ -/* ======================================= */ - -/* Emphasize Drag & Drop Node Functionality */ -.sidebar-button:hover { - background-color: #2a2a2a !important; - box-shadow: 0 0 5px rgba(88, 166, 255, 0.3); - cursor: grab; -} - -/* ======================================= */ -/* NODES */ -/* ======================================= */ - -/* Borealis Node Styling */ -.borealis-node { - background: linear-gradient( - to bottom, - #2c2c2c 60%, - #232323 100% - ); - border: 1px solid #3a3a3a; - border-radius: 4px; - color: #ccc; - font-size: 12px; - min-width: 160px; - max-width: 260px; - position: relative; - box-shadow: 0 0 5px rgba(88, 166, 255, 0.15), - 0 0 10px rgba(88, 166, 255, 0.15); - transition: box-shadow 0.3s ease-in-out; -} -.borealis-node::before { - content: ""; - display: block; - position: absolute; - left: 0; - top: 0; - width: 3px; - height: 100%; - background: linear-gradient( - to bottom, - var(--borealis-accent, #58a6ff) 0%, - var(--borealis-accent-dark, #0475c2) 100% - ); - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.borealis-node-header { - background: #232323; - padding: 6px 10px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - font-weight: bold; - color: var(--borealis-title, #58a6ff); - font-size: 10px; -} -.borealis-node-content { - padding: 10px; - font-size: 9px; -} -.borealis-handle { - background: #58a6ff; - width: 10px; - height: 10px; -} - -/* Global dark form inputs */ -input, -select, -button { - background-color: #1d1d1d; - color: #ccc; - border: 1px solid #444; - font-size: 12px; -} - -/* Label / Dark Text styling */ -label { - color: #aaa; - font-size: 9px; -} - -/* Node Header - Shows drag handle cursor */ -.borealis-node-header { - cursor: grab; -} - -/* Optional: when actively dragging */ -.borealis-node-header:active { - cursor: grabbing; -} - -/* Node Body - Just pointer, not draggable */ -.borealis-node-content { - cursor: default; -} - -/* ======================================= */ -/* FLOW TABS */ -/* ======================================= */ - -/* Multi-Tab Bar Adjustments */ -.MuiTabs-root { - min-height: 32px !important; -} - -.MuiTab-root { - min-height: 32px !important; - padding: 6px 12px !important; - color: #58a6ff !important; - text-transform: none !important; -} - -/* Highlight tab on hover if it's not active */ -.MuiTab-root:hover:not(.Mui-selected) { - background-color: #2C2C2C !important; -} - -/* We rely on the TabIndicatorProps to show the underline highlight for active tabs. */ - -/* ======================================= */ -/* REACT-SIMPLE-KEYBOARD */ -/* ======================================= */ - -/* Make the keyboard max width like the demo */ -.simple-keyboard { - max-width: 950px; - margin: 0 auto; - background: #181c23; - border-radius: 8px; - padding: 24px 24px 30px 24px; - box-shadow: 0 2px 24px 0 #000a; -} - -/* Set dark background and color for the keyboard and its keys */ -.simple-keyboard .hg-button { - background: #23262e; - color: #b0d0ff; - border: 1px solid #333; - font-size: 1.1em; - min-width: 48px; - min-height: 48px; - margin: 5px; - border-radius: 6px; - transition: background 0.1s, color 0.1s; - padding-top: 6px; - padding-left: 8px; -} - -.simple-keyboard .hg-button[data-skbtn="space"] { - min-width: 380px; -} - -.simple-keyboard .hg-button[data-skbtn="tab"], -.simple-keyboard .hg-button[data-skbtn="caps"], -.simple-keyboard .hg-button[data-skbtn="shift"], -.simple-keyboard .hg-button[data-skbtn="enter"], -.simple-keyboard .hg-button[data-skbtn="bksp"] { - min-width: 82px; -} - -.simple-keyboard .hg-button:hover { - background: #58a6ff; - color: #000; - border-color: #58a6ff; -} - -/* Make sure rows aren't squashed */ -.simple-keyboard .hg-row { - display: flex !important; - flex-flow: row wrap; - justify-content: center; - margin-bottom: 10px; -} - -/* Remove any unwanted shrink/stretch */ -.simple-keyboard .hg-button { - flex: 0 0 auto; -} - -/* Optional: on-screen keyboard input field (if you ever show it) */ -input[type="text"].simple-keyboard-input { - width: 100%; - height: 48px; - padding: 10px 20px; - font-size: 20px; - border: none; - box-sizing: border-box; - background: #181818; - color: #f5f7fa; - border-radius: 6px; - margin-bottom: 20px; -} \ No newline at end of file diff --git a/Data/Server/WebUI/src/Devices/Add_Device.jsx b/Data/Server/WebUI/src/Devices/Add_Device.jsx deleted file mode 100644 index f44945d8..00000000 --- a/Data/Server/WebUI/src/Devices/Add_Device.jsx +++ /dev/null @@ -1,219 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, - TextField, - Button, - MenuItem, - Typography -} from "@mui/material"; - -const TYPE_OPTIONS = [ - { value: "ssh", label: "SSH" }, - { value: "winrm", label: "WinRM" } -]; - -const initialForm = { - hostname: "", - address: "", - description: "", - operating_system: "" -}; - -export default function AddDevice({ - open, - onClose, - defaultType = null, - onCreated -}) { - const [type, setType] = useState(defaultType || "ssh"); - const [form, setForm] = useState(initialForm); - const [submitting, setSubmitting] = useState(false); - const [error, setError] = useState(""); - - useEffect(() => { - if (open) { - setType(defaultType || "ssh"); - setForm(initialForm); - setError(""); - } - }, [open, defaultType]); - - const handleClose = () => { - if (submitting) return; - onClose && onClose(); - }; - - const handleChange = (field) => (event) => { - const value = event.target.value; - setForm((prev) => ({ ...prev, [field]: value })); - }; - - const handleSubmit = async () => { - if (submitting) return; - const trimmedHostname = form.hostname.trim(); - const trimmedAddress = form.address.trim(); - if (!trimmedHostname) { - setError("Hostname is required."); - return; - } - if (!type) { - setError("Select a device type."); - return; - } - if (!trimmedAddress) { - setError("Address is required."); - return; - } - setSubmitting(true); - setError(""); - const payload = { - hostname: trimmedHostname, - address: trimmedAddress, - description: form.description.trim(), - operating_system: form.operating_system.trim() - }; - const apiBase = type === "winrm" ? "/api/winrm_devices" : "/api/ssh_devices"; - try { - const resp = await fetch(apiBase, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - }); - const data = await resp.json().catch(() => ({})); - if (!resp.ok) throw new Error(data?.error || `HTTP ${resp.status}`); - onCreated && onCreated(data.device || null); - onClose && onClose(); - } catch (err) { - setError(String(err.message || err)); - } finally { - setSubmitting(false); - } - }; - - const dialogTitle = defaultType - ? `Add ${defaultType.toUpperCase()} Device` - : "Add Device"; - - const typeLabel = (TYPE_OPTIONS.find((opt) => opt.value === type) || TYPE_OPTIONS[0]).label; - - return ( - - {dialogTitle} - - {!defaultType && ( - setType(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#1f1f1f", - color: "#fff", - "& fieldset": { borderColor: "#555" }, - "&:hover fieldset": { borderColor: "#888" } - }, - "& .MuiInputLabel-root": { color: "#aaa" } - }} - > - {TYPE_OPTIONS.map((opt) => ( - - {opt.label} - - ))} - - )} - - - - - {error && ( - - {error} - - )} - - - - - - - ); -} diff --git a/Data/Server/WebUI/src/Devices/Agent_Devices.jsx b/Data/Server/WebUI/src/Devices/Agent_Devices.jsx deleted file mode 100644 index 9f0f112f..00000000 --- a/Data/Server/WebUI/src/Devices/Agent_Devices.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import DeviceList from "./Device_List.jsx"; - -export default function AgentDevices(props) { - return ( - - ); -} diff --git a/Data/Server/WebUI/src/Devices/Device_Approvals.jsx b/Data/Server/WebUI/src/Devices/Device_Approvals.jsx deleted file mode 100644 index 7f4c2c87..00000000 --- a/Data/Server/WebUI/src/Devices/Device_Approvals.jsx +++ /dev/null @@ -1,505 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/Server/WebUI/src/Admin/Device_Approvals.jsx - -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { - Alert, - Box, - Button, - Chip, - CircularProgress, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - FormControl, - IconButton, - InputLabel, - MenuItem, - Paper, - Select, - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, - Tooltip, - Typography, -} from "@mui/material"; -import { - CheckCircleOutline as ApproveIcon, - HighlightOff as DenyIcon, - Refresh as RefreshIcon, - Security as SecurityIcon, -} from "@mui/icons-material"; - -const STATUS_OPTIONS = [ - { value: "all", label: "All" }, - { value: "pending", label: "Pending" }, - { value: "approved", label: "Approved" }, - { value: "completed", label: "Completed" }, - { value: "denied", label: "Denied" }, - { value: "expired", label: "Expired" }, -]; - -const statusChipColor = { - pending: "warning", - approved: "info", - completed: "success", - denied: "default", - expired: "default", -}; - -const formatDateTime = (value) => { - if (!value) return "—"; - const date = new Date(value); - if (Number.isNaN(date.getTime())) return value; - return date.toLocaleString(); -}; - -const formatFingerprint = (fp) => { - if (!fp) return "—"; - const normalized = fp.replace(/[^a-f0-9]/gi, "").toLowerCase(); - if (!normalized) return fp; - return normalized.match(/.{1,4}/g)?.join(" ") ?? normalized; -}; - -const normalizeStatus = (status) => { - if (!status) return "pending"; - if (status === "completed") return "completed"; - return status.toLowerCase(); -}; - -function DeviceApprovals() { - const [approvals, setApprovals] = useState([]); - const [statusFilter, setStatusFilter] = useState("all"); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - const [feedback, setFeedback] = useState(null); - const [guidInputs, setGuidInputs] = useState({}); - const [actioningId, setActioningId] = useState(null); - const [conflictPrompt, setConflictPrompt] = useState(null); - - const loadApprovals = useCallback(async () => { - setLoading(true); - setError(""); - try { - const query = statusFilter === "all" ? "" : `?status=${encodeURIComponent(statusFilter)}`; - const resp = await fetch(`/api/admin/device-approvals${query}`, { credentials: "include" }); - if (!resp.ok) { - const body = await resp.json().catch(() => ({})); - throw new Error(body.error || `Request failed (${resp.status})`); - } - const data = await resp.json(); - setApprovals(Array.isArray(data.approvals) ? data.approvals : []); - } catch (err) { - setError(err.message || "Unable to load device approvals"); - } finally { - setLoading(false); - } - }, [statusFilter]); - - useEffect(() => { - loadApprovals(); - }, [loadApprovals]); - - const dedupedApprovals = useMemo(() => { - const normalized = approvals - .map((record) => ({ ...record, status: normalizeStatus(record.status) })) - .sort((a, b) => { - const left = new Date(a.created_at || 0).getTime(); - const right = new Date(b.created_at || 0).getTime(); - return left - right; - }); - if (statusFilter !== "pending") { - return normalized; - } - const seen = new Set(); - const unique = []; - for (const record of normalized) { - const key = record.ssl_key_fingerprint_claimed || record.hostname_claimed || record.id; - if (seen.has(key)) continue; - seen.add(key); - unique.push(record); - } - return unique; - }, [approvals, statusFilter]); - - const handleGuidChange = useCallback((id, value) => { - setGuidInputs((prev) => ({ ...prev, [id]: value })); - }, []); - - const submitApproval = useCallback( - async (record, overrides = {}) => { - if (!record?.id) return; - setActioningId(record.id); - setFeedback(null); - setError(""); - try { - const manualGuid = (guidInputs[record.id] || "").trim(); - const payload = {}; - const overrideGuidRaw = overrides.guid; - let overrideGuid = ""; - if (typeof overrideGuidRaw === "string") { - overrideGuid = overrideGuidRaw.trim(); - } else if (overrideGuidRaw != null) { - overrideGuid = String(overrideGuidRaw).trim(); - } - if (overrideGuid) { - payload.guid = overrideGuid; - } else if (manualGuid) { - payload.guid = manualGuid; - } - const resolutionRaw = overrides.conflictResolution || overrides.resolution; - if (typeof resolutionRaw === "string" && resolutionRaw.trim()) { - payload.conflict_resolution = resolutionRaw.trim().toLowerCase(); - } - const resp = await fetch(`/api/admin/device-approvals/${encodeURIComponent(record.id)}/approve`, { - method: "POST", - credentials: "include", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(Object.keys(payload).length ? payload : {}), - }); - const body = await resp.json().catch(() => ({})); - if (!resp.ok) { - if (resp.status === 409 && body.error === "conflict_resolution_required") { - const conflict = record.hostname_conflict; - const fallbackAlternate = - record.alternate_hostname || - (record.hostname_claimed ? `${record.hostname_claimed}-1` : ""); - if (conflict) { - setConflictPrompt({ - record, - conflict, - alternate: fallbackAlternate || "", - }); - } - return; - } - throw new Error(body.error || `Approval failed (${resp.status})`); - } - const appliedResolution = (body.conflict_resolution || payload.conflict_resolution || "").toLowerCase(); - let successMessage = "Enrollment approved"; - if (appliedResolution === "overwrite") { - successMessage = "Enrollment approved; existing device overwritten"; - } else if (appliedResolution === "coexist") { - successMessage = "Enrollment approved; devices will co-exist"; - } else if (appliedResolution === "auto_merge_fingerprint") { - successMessage = "Enrollment approved; device reconnected with its existing identity"; - } - setFeedback({ type: "success", message: successMessage }); - await loadApprovals(); - } catch (err) { - setFeedback({ type: "error", message: err.message || "Unable to approve request" }); - } finally { - setActioningId(null); - } - }, - [guidInputs, loadApprovals] - ); - - const startApprove = useCallback( - (record) => { - if (!record?.id) return; - const status = normalizeStatus(record.status); - if (status !== "pending") return; - const manualGuid = (guidInputs[record.id] || "").trim(); - const conflict = record.hostname_conflict; - const requiresPrompt = Boolean(conflict?.requires_prompt ?? record.conflict_requires_prompt); - if (requiresPrompt && !manualGuid) { - const fallbackAlternate = - record.alternate_hostname || - (record.hostname_claimed ? `${record.hostname_claimed}-1` : ""); - setConflictPrompt({ - record, - conflict, - alternate: fallbackAlternate || "", - }); - return; - } - submitApproval(record); - }, - [guidInputs, submitApproval] - ); - - const handleConflictCancel = useCallback(() => { - setConflictPrompt(null); - }, []); - - const handleConflictOverwrite = useCallback(() => { - if (!conflictPrompt?.record) { - setConflictPrompt(null); - return; - } - const { record, conflict } = conflictPrompt; - setConflictPrompt(null); - const conflictGuid = conflict?.guid != null ? String(conflict.guid).trim() : ""; - submitApproval(record, { - guid: conflictGuid, - conflictResolution: "overwrite", - }); - }, [conflictPrompt, submitApproval]); - - const handleConflictCoexist = useCallback(() => { - if (!conflictPrompt?.record) { - setConflictPrompt(null); - return; - } - const { record } = conflictPrompt; - setConflictPrompt(null); - submitApproval(record, { - conflictResolution: "coexist", - }); - }, [conflictPrompt, submitApproval]); - - const conflictRecord = conflictPrompt?.record; - const conflictInfo = conflictPrompt?.conflict; - const conflictHostname = conflictRecord?.hostname_claimed || conflictRecord?.hostname || ""; - const conflictSiteName = conflictInfo?.site_name || ""; - const conflictSiteDescriptor = conflictInfo - ? conflictSiteName - ? `under site ${conflictSiteName}` - : "under site (not assigned)" - : "under site (not assigned)"; - const conflictAlternate = - conflictPrompt?.alternate || - (conflictHostname ? `${conflictHostname}-1` : "hostname-1"); - const conflictGuidDisplay = conflictInfo?.guid || ""; - - const handleDeny = useCallback( - async (record) => { - if (!record?.id) return; - const confirmDeny = window.confirm("Deny this enrollment request?"); - if (!confirmDeny) return; - setActioningId(record.id); - setFeedback(null); - setError(""); - try { - const resp = await fetch(`/api/admin/device-approvals/${encodeURIComponent(record.id)}/deny`, { - method: "POST", - credentials: "include", - }); - if (!resp.ok) { - const body = await resp.json().catch(() => ({})); - throw new Error(body.error || `Deny failed (${resp.status})`); - } - setFeedback({ type: "success", message: "Enrollment denied" }); - await loadApprovals(); - } catch (err) { - setFeedback({ type: "error", message: err.message || "Unable to deny request" }); - } finally { - setActioningId(null); - } - }, - [loadApprovals] - ); - - return ( - - - - Device Approval Queue - - - - - - Status - - - - - - - {feedback ? ( - setFeedback(null)}> - {feedback.message} - - ) : null} - - {error ? ( - - {error} - - ) : null} - - - - - - Status - Hostname - Fingerprint - Enrollment Code - Created - Updated - Approved By - Actions - - - - {loading ? ( - - - - - Loading approvals… - - - - ) : dedupedApprovals.length === 0 ? ( - - - - No enrollment requests match this filter. - - - - ) : ( - dedupedApprovals.map((record) => { - const status = normalizeStatus(record.status); - const showActions = status === "pending"; - const guidValue = guidInputs[record.id] || ""; - const approverDisplay = record.approved_by_username || record.approved_by_user_id; - return ( - - - - - {record.hostname_claimed || "—"} - - {formatFingerprint(record.ssl_key_fingerprint_claimed)} - - - {record.enrollment_code_id || "—"} - - {formatDateTime(record.created_at)} - {formatDateTime(record.updated_at)} - {approverDisplay || "—"} - - {showActions ? ( - - handleGuidChange(record.id, event.target.value)} - sx={{ minWidth: 200 }} - /> - - - - startApprove(record)} - disabled={actioningId === record.id} - > - {actioningId === record.id ? ( - - ) : ( - - )} - - - - - - handleDeny(record)} - disabled={actioningId === record.id} - > - - - - - - - ) : ( - - No actions available - - )} - - - ); - }) - )} - -
-
-
- - Hostname Conflict - - - - {conflictHostname - ? `Device ${conflictHostname} already exists in the database ${conflictSiteDescriptor}.` - : `A device with this hostname already exists in the database ${conflictSiteDescriptor}.`} - - - Do you want this device to overwrite the existing device, or allow both to co-exist? - - - {`Device will be renamed ${conflictAlternate} if you choose to allow both to co-exist.`} - - {conflictGuidDisplay ? ( - - Existing device GUID: {conflictGuidDisplay} - - ) : null} - - - - - - - - -
- ); -} - -export default React.memo(DeviceApprovals); diff --git a/Data/Server/WebUI/src/Devices/Device_Details.jsx b/Data/Server/WebUI/src/Devices/Device_Details.jsx deleted file mode 100644 index 1190b42d..00000000 --- a/Data/Server/WebUI/src/Devices/Device_Details.jsx +++ /dev/null @@ -1,1383 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Device_Details.js - -import React, { useState, useEffect, useMemo, useCallback } from "react"; -import { - Paper, - Box, - Tabs, - Tab, - Typography, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - Button, - IconButton, - Menu, - MenuItem, - LinearProgress, - TableSortLabel, - TextField, - Dialog, - DialogTitle, - DialogContent, - DialogActions -} from "@mui/material"; -import StorageRoundedIcon from "@mui/icons-material/StorageRounded"; -import MemoryRoundedIcon from "@mui/icons-material/MemoryRounded"; -import SpeedRoundedIcon from "@mui/icons-material/SpeedRounded"; -import DeveloperBoardRoundedIcon from "@mui/icons-material/DeveloperBoardRounded"; -import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; -import { ClearDeviceActivityDialog } from "../Dialogs.jsx"; -import Prism from "prismjs"; -import "prismjs/components/prism-yaml"; -import "prismjs/components/prism-bash"; -import "prismjs/components/prism-powershell"; -import "prismjs/components/prism-batch"; -import "prismjs/themes/prism-okaidia.css"; -import Editor from "react-simple-code-editor"; -import QuickJob from "../Scheduling/Quick_Job.jsx"; - -export default function DeviceDetails({ device, onBack }) { - const [tab, setTab] = useState(0); - const [agent, setAgent] = useState(device || {}); - const [details, setDetails] = useState({}); - const [meta, setMeta] = useState({}); - const [softwareOrderBy, setSoftwareOrderBy] = useState("name"); - const [softwareOrder, setSoftwareOrder] = useState("asc"); - const [softwareSearch, setSoftwareSearch] = useState(""); - const [description, setDescription] = useState(""); - const [connectionType, setConnectionType] = useState(""); - const [connectionEndpoint, setConnectionEndpoint] = useState(""); - const [connectionDraft, setConnectionDraft] = useState(""); - const [connectionSaving, setConnectionSaving] = useState(false); - const [connectionMessage, setConnectionMessage] = useState(""); - const [connectionError, setConnectionError] = useState(""); - const [historyRows, setHistoryRows] = useState([]); - const [historyOrderBy, setHistoryOrderBy] = useState("ran_at"); - const [historyOrder, setHistoryOrder] = useState("desc"); - const [outputOpen, setOutputOpen] = useState(false); - const [outputTitle, setOutputTitle] = useState(""); - const [outputContent, setOutputContent] = useState(""); - const [outputLang, setOutputLang] = useState("powershell"); - const [quickJobOpen, setQuickJobOpen] = useState(false); - const [menuAnchor, setMenuAnchor] = useState(null); - const [clearDialogOpen, setClearDialogOpen] = useState(false); - const [assemblyNameMap, setAssemblyNameMap] = useState({}); - // Snapshotted status for the lifetime of this page - const [lockedStatus, setLockedStatus] = useState(() => { - // Prefer status provided by the device list row if available - if (device?.status) return device.status; - // Fallback: compute once from the provided lastSeen timestamp - const tsSec = device?.lastSeen; - if (!tsSec) return "Offline"; - const now = Date.now() / 1000; - return now - tsSec <= 300 ? "Online" : "Offline"; - }); - - useEffect(() => { - setConnectionError(""); - }, [connectionDraft]); - - useEffect(() => { - if (connectionType !== "ssh") { - setConnectionMessage(""); - setConnectionError(""); - } - }, [connectionType]); - - useEffect(() => { - let canceled = false; - const loadAssemblyNames = async () => { - const next = {}; - const storeName = (rawPath, rawName, prefix = "") => { - const name = typeof rawName === "string" ? rawName.trim() : ""; - if (!name) return; - const normalizedPath = String(rawPath || "") - .replace(/\\/g, "/") - .replace(/^\/+/, "") - .trim(); - const keys = new Set(); - if (normalizedPath) { - keys.add(normalizedPath); - if (prefix) { - const prefixed = `${prefix}/${normalizedPath}`.replace(/\/+/g, "/"); - keys.add(prefixed); - } - } - const base = normalizedPath ? normalizedPath.split("/").pop() || "" : ""; - if (base) { - keys.add(base); - const dot = base.lastIndexOf("."); - if (dot > 0) { - keys.add(base.slice(0, dot)); - } - } - keys.forEach((key) => { - if (key && !next[key]) { - next[key] = name; - } - }); - }; - const ingest = async (island, prefix = "") => { - try { - const resp = await fetch(`/api/assembly/list?island=${island}`); - if (!resp.ok) return; - const data = await resp.json(); - const items = Array.isArray(data.items) ? data.items : []; - items.forEach((item) => { - if (!item || typeof item !== "object") return; - const rel = item.rel_path || item.path || item.file_name || item.playbook_path || ""; - const label = (item.name || item.tab_name || item.display_name || item.file_name || "").trim(); - storeName(rel, label, prefix); - }); - } catch { - // ignore failures; map remains partial - } - }; - await ingest("scripts", "Scripts"); - await ingest("workflows", "Workflows"); - await ingest("ansible", "Ansible_Playbooks"); - if (!canceled) { - setAssemblyNameMap(next); - } - }; - loadAssemblyNames(); - return () => { - canceled = true; - }; - }, []); - - const statusFromHeartbeat = (tsSec, offlineAfter = 300) => { - if (!tsSec) return "Offline"; - const now = Date.now() / 1000; - return now - tsSec <= offlineAfter ? "Online" : "Offline"; - }; - - const statusColor = (s) => (s === "Online" ? "#00d18c" : "#ff4f4f"); - - const resolveAssemblyName = useCallback((scriptName, scriptPath) => { - const normalized = String(scriptPath || "").replace(/\\/g, "/").trim(); - const base = normalized ? normalized.split("/").pop() || "" : ""; - const baseNoExt = base && base.includes(".") ? base.slice(0, base.lastIndexOf(".")) : base; - return ( - assemblyNameMap[normalized] || - (base ? assemblyNameMap[base] : "") || - (baseNoExt ? assemblyNameMap[baseNoExt] : "") || - scriptName || - base || - scriptPath || - "" - ); - }, [assemblyNameMap]); - - const formatLastSeen = (tsSec, offlineAfter = 120) => { - if (!tsSec) return "unknown"; - const now = Date.now() / 1000; - if (now - tsSec <= offlineAfter) return "Currently Online"; - const d = new Date(tsSec * 1000); - const date = d.toLocaleDateString("en-US", { - month: "2-digit", - day: "2-digit", - year: "numeric", - }); - const time = d.toLocaleTimeString("en-US", { - hour: "numeric", - minute: "2-digit", - }); - return `${date} @ ${time}`; - }; - - useEffect(() => { - if (device) { - setLockedStatus(device.status || statusFromHeartbeat(device.lastSeen)); - } - - const guid = device?.agent_guid || device?.guid || device?.agentGuid || device?.summary?.agent_guid; - const agentId = device?.agentId || device?.summary?.agent_id || device?.id; - const hostname = device?.hostname || device?.summary?.hostname; - if (!device || (!guid && !hostname)) return; - - const load = async () => { - try { - const agentsPromise = fetch("/api/agents").catch(() => null); - let detailResponse = null; - if (guid) { - try { - detailResponse = await fetch(`/api/devices/${encodeURIComponent(guid)}`); - } catch (err) { - detailResponse = null; - } - } - if ((!detailResponse || !detailResponse.ok) && hostname) { - try { - detailResponse = await fetch(`/api/device/details/${encodeURIComponent(hostname)}`); - } catch (err) { - detailResponse = null; - } - } - if (!detailResponse || !detailResponse.ok) { - throw new Error(`Failed to load device record (${detailResponse ? detailResponse.status : 'no response'})`); - } - - const [agentsData, detailData] = await Promise.all([ - agentsPromise?.then((r) => (r ? r.json() : {})).catch(() => ({})), - detailResponse.json(), - ]); - - if (agentsData && agentId && agentsData[agentId]) { - setAgent({ id: agentId, ...agentsData[agentId] }); - } - - const summary = - detailData?.summary && typeof detailData.summary === "object" - ? detailData.summary - : (detailData?.details?.summary || {}); - const normalizedSummary = { ...(summary || {}) }; - if (detailData?.description) { - normalizedSummary.description = detailData.description; - } - - const connectionTypeValue = - (normalizedSummary.connection_type || - normalizedSummary.remote_type || - "").toLowerCase(); - const connectionEndpointValue = - normalizedSummary.connection_endpoint || - normalizedSummary.connection_address || - detailData?.connection_endpoint || - ""; - setConnectionType(connectionTypeValue); - setConnectionEndpoint(connectionEndpointValue); - setConnectionDraft(connectionEndpointValue); - setConnectionMessage(""); - setConnectionError(""); - - const normalized = { - summary: normalizedSummary, - memory: Array.isArray(detailData?.memory) - ? detailData.memory - : Array.isArray(detailData?.details?.memory) - ? detailData.details.memory - : [], - network: Array.isArray(detailData?.network) - ? detailData.network - : Array.isArray(detailData?.details?.network) - ? detailData.details.network - : [], - software: Array.isArray(detailData?.software) - ? detailData.software - : Array.isArray(detailData?.details?.software) - ? detailData.details.software - : [], - storage: Array.isArray(detailData?.storage) - ? detailData.storage - : Array.isArray(detailData?.details?.storage) - ? detailData.details.storage - : [], - cpu: detailData?.cpu || detailData?.details?.cpu || {}, - }; - setDetails(normalized); - - const toYmdHms = (dateObj) => { - if (!dateObj || Number.isNaN(dateObj.getTime())) return ''; - const pad = (v) => String(v).padStart(2, '0'); - return `${dateObj.getUTCFullYear()}-${pad(dateObj.getUTCMonth() + 1)}-${pad(dateObj.getUTCDate())} ${pad(dateObj.getUTCHours())}:${pad(dateObj.getUTCMinutes())}:${pad(dateObj.getUTCSeconds())}`; - }; - - let createdDisplay = normalizedSummary.created || ''; - if (!createdDisplay) { - if (detailData?.created_at && Number(detailData.created_at)) { - createdDisplay = toYmdHms(new Date(Number(detailData.created_at) * 1000)); - } else if (detailData?.created_at_iso) { - createdDisplay = toYmdHms(new Date(detailData.created_at_iso)); - } - } - - const metaPayload = { - hostname: detailData?.hostname || normalizedSummary.hostname || hostname || "", - lastUser: detailData?.last_user || normalizedSummary.last_user || "", - deviceType: detailData?.device_type || normalizedSummary.device_type || "", - created: createdDisplay, - createdAtIso: detailData?.created_at_iso || "", - lastSeen: detailData?.last_seen || normalizedSummary.last_seen || 0, - lastReboot: detailData?.last_reboot || normalizedSummary.last_reboot || "", - operatingSystem: - detailData?.operating_system || normalizedSummary.operating_system || normalizedSummary.agent_operating_system || "", - agentId: detailData?.agent_id || normalizedSummary.agent_id || agentId || "", - agentGuid: detailData?.agent_guid || normalizedSummary.agent_guid || guid || "", - agentHash: detailData?.agent_hash || normalizedSummary.agent_hash || "", - internalIp: detailData?.internal_ip || normalizedSummary.internal_ip || "", - externalIp: detailData?.external_ip || normalizedSummary.external_ip || "", - siteId: detailData?.site_id, - siteName: detailData?.site_name || "", - siteDescription: detailData?.site_description || "", - status: detailData?.status || "", - connectionType: connectionTypeValue, - connectionEndpoint: connectionEndpointValue, - }; - setMeta(metaPayload); - setDescription(normalizedSummary.description || detailData?.description || ""); - - setAgent((prev) => ({ - ...(prev || {}), - id: agentId || prev?.id, - hostname: metaPayload.hostname || prev?.hostname, - agent_hash: metaPayload.agentHash || prev?.agent_hash, - agent_operating_system: metaPayload.operatingSystem || prev?.agent_operating_system, - device_type: metaPayload.deviceType || prev?.device_type, - last_seen: metaPayload.lastSeen || prev?.last_seen, - })); - - if (metaPayload.status) { - setLockedStatus(metaPayload.status); - } else if (metaPayload.lastSeen) { - setLockedStatus(statusFromHeartbeat(metaPayload.lastSeen)); - } - } catch (e) { - console.warn("Failed to load device info", e); - setMeta({}); - } - }; - load(); - }, [device]); - - const activityHostname = useMemo(() => { - return (meta?.hostname || agent?.hostname || device?.hostname || "").trim(); - }, [meta?.hostname, agent?.hostname, device?.hostname]); - - const saveConnectionEndpoint = useCallback(async () => { - if (connectionType !== "ssh") return; - const host = activityHostname; - if (!host) return; - const trimmed = connectionDraft.trim(); - if (!trimmed) { - setConnectionError("Address is required."); - return; - } - if (trimmed === connectionEndpoint.trim()) { - setConnectionMessage("No changes to save."); - return; - } - setConnectionSaving(true); - setConnectionError(""); - setConnectionMessage(""); - try { - const resp = await fetch(`/api/ssh_devices/${encodeURIComponent(host)}`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ address: trimmed }) - }); - const data = await resp.json().catch(() => ({})); - if (!resp.ok) throw new Error(data?.error || `HTTP ${resp.status}`); - const updated = data?.device?.connection_endpoint || trimmed; - setConnectionEndpoint(updated); - setConnectionDraft(updated); - setMeta((prev) => ({ ...(prev || {}), connectionEndpoint: updated })); - setConnectionMessage("SSH endpoint updated."); - setTimeout(() => setConnectionMessage(""), 3000); - } catch (err) { - setConnectionError(String(err.message || err)); - } finally { - setConnectionSaving(false); - } - }, [connectionType, connectionDraft, connectionEndpoint, activityHostname]); - - const loadHistory = useCallback(async () => { - if (!activityHostname) return; - try { - const resp = await fetch(`/api/device/activity/${encodeURIComponent(activityHostname)}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - setHistoryRows(data.history || []); - } catch (e) { - console.warn("Failed to load activity history", e); - setHistoryRows([]); - } - }, [activityHostname]); - - useEffect(() => { loadHistory(); }, [loadHistory]); - - useEffect(() => { - const socket = typeof window !== "undefined" ? window.BorealisSocket : null; - if (!socket || !activityHostname) return undefined; - - let refreshTimer = null; - const normalizedHost = activityHostname.toLowerCase(); - const scheduleRefresh = (delay = 200) => { - if (refreshTimer) clearTimeout(refreshTimer); - refreshTimer = setTimeout(() => { - refreshTimer = null; - loadHistory(); - }, delay); - }; - - const handleActivityChanged = (payload = {}) => { - const payloadHost = String(payload?.hostname || "").trim().toLowerCase(); - if (!payloadHost) return; - if (payloadHost === normalizedHost) { - const delay = payload?.change === "updated" ? 150 : 0; - scheduleRefresh(delay); - } - }; - - socket.on("device_activity_changed", handleActivityChanged); - - return () => { - if (refreshTimer) clearTimeout(refreshTimer); - socket.off("device_activity_changed", handleActivityChanged); - }; - }, [activityHostname, loadHistory]); - - // No explicit live recap tab; recaps are recorded into Activity History - - const clearHistory = async () => { - if (!activityHostname) return; - try { - const resp = await fetch(`/api/device/activity/${encodeURIComponent(activityHostname)}`, { method: "DELETE" }); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - setHistoryRows([]); - } catch (e) { - console.warn("Failed to clear activity history", e); - } - }; - - const saveDescription = async () => { - const targetHost = meta.hostname || details.summary?.hostname; - if (!targetHost) return; - try { - await fetch(`/api/device/description/${targetHost}`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ description }) - }); - setDetails((d) => ({ - ...d, - summary: { ...(d.summary || {}), description } - })); - setMeta((m) => ({ ...(m || {}), hostname: targetHost })); - } catch (e) { - console.warn("Failed to save description", e); - } - }; - - const formatDateTime = (str) => { - if (!str) return "unknown"; - try { - const [datePart, timePart] = str.split(" "); - const [y, m, d] = datePart.split("-").map(Number); - let [hh, mm, ss] = timePart.split(":").map(Number); - const ampm = hh >= 12 ? "PM" : "AM"; - hh = hh % 12 || 12; - return `${m.toString().padStart(2, "0")}/${d.toString().padStart(2, "0")}/${y} @ ${hh}:${mm - .toString() - .padStart(2, "0")} ${ampm}`; - } catch { - return str; - } - }; - - const formatMac = (mac) => (mac ? mac.replace(/-/g, ":").toUpperCase() : "unknown"); - - const formatBytes = (val) => { - if (val === undefined || val === null || val === "unknown") return "unknown"; - let num = Number(val); - const units = ["B", "KB", "MB", "GB", "TB"]; - let i = 0; - while (num >= 1024 && i < units.length - 1) { - num /= 1024; - i++; - } - return `${num.toFixed(1)} ${units[i]}`; - }; - - const formatTimestamp = (epochSec) => { - const ts = Number(epochSec || 0); - if (!ts) return "unknown"; - const d = new Date(ts * 1000); - const mm = String(d.getMonth() + 1).padStart(2, "0"); - const dd = String(d.getDate()).padStart(2, "0"); - const yyyy = d.getFullYear(); - let hh = d.getHours(); - const ampm = hh >= 12 ? "PM" : "AM"; - hh = hh % 12 || 12; - const min = String(d.getMinutes()).padStart(2, "0"); - return `${mm}/${dd}/${yyyy} @ ${hh}:${min} ${ampm}`; - }; - - const handleSoftwareSort = (col) => { - if (softwareOrderBy === col) { - setSoftwareOrder(softwareOrder === "asc" ? "desc" : "asc"); - } else { - setSoftwareOrderBy(col); - setSoftwareOrder("asc"); - } - }; - - const softwareRows = useMemo(() => { - const rows = details.software || []; - const filtered = rows.filter((s) => - s.name.toLowerCase().includes(softwareSearch.toLowerCase()) - ); - const dir = softwareOrder === "asc" ? 1 : -1; - return [...filtered].sort((a, b) => { - const A = a[softwareOrderBy] || ""; - const B = b[softwareOrderBy] || ""; - return String(A).localeCompare(String(B)) * dir; - }); - }, [details.software, softwareSearch, softwareOrderBy, softwareOrder]); - - const summary = details.summary || {}; - // Build a best-effort CPU display from summary fields - const cpuInfo = useMemo(() => { - const cpu = details.cpu || summary.cpu || {}; - const cores = cpu.logical_cores || cpu.cores || cpu.physical_cores; - let ghz = cpu.base_clock_ghz; - if (!ghz && typeof (summary.processor || '') === 'string') { - const m = String(summary.processor).match(/\(([^)]*?)ghz\)/i); - if (m && m[1]) { - const n = parseFloat(m[1]); - if (!Number.isNaN(n)) ghz = n; - } - } - const name = (cpu.name || '').trim(); - const fromProcessor = (summary.processor || '').trim(); - const display = fromProcessor || [name, ghz ? `(${Number(ghz).toFixed(1)}GHz)` : null, cores ? `@ ${cores} Cores` : null].filter(Boolean).join(' '); - return { cores, ghz, name, display }; - }, [summary]); - - const summaryItems = [ - { label: "Hostname", value: meta.hostname || summary.hostname || agent.hostname || device?.hostname || "unknown" }, - { - label: "Last User", - value: ( - - - {meta.lastUser || summary.last_user || 'unknown'} - - ) - }, - { label: "Device Type", value: meta.deviceType || summary.device_type || 'unknown' }, - { - label: "Created", - value: meta.created ? formatDateTime(meta.created) : summary.created ? formatDateTime(summary.created) : 'unknown' - }, - { - label: "Last Seen", - value: formatLastSeen(meta.lastSeen || agent.last_seen || device?.lastSeen) - }, - { - label: "Last Reboot", - value: meta.lastReboot ? formatDateTime(meta.lastReboot) : summary.last_reboot ? formatDateTime(summary.last_reboot) : 'unknown' - }, - { label: "Operating System", value: meta.operatingSystem || summary.operating_system || agent.agent_operating_system || 'unknown' }, - { label: "Agent ID", value: meta.agentId || summary.agent_id || 'unknown' }, - { label: "Agent GUID", value: meta.agentGuid || summary.agent_guid || 'unknown' }, - { label: "Agent Hash", value: meta.agentHash || summary.agent_hash || 'unknown' }, - ]; - - const MetricCard = ({ icon, title, main, sub, color }) => { - const edgeColor = color || '#232323'; - const parseHex = (hex) => { - const v = String(hex || '').replace('#', ''); - const n = parseInt(v.length === 3 ? v.split('').map(c => c + c).join('') : v, 16); - return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 }; - }; - const hexToRgba = (hex, alpha = 1) => { - try { const { r, g, b } = parseHex(hex); return `rgba(${r}, ${g}, ${b}, ${alpha})`; } catch { return `rgba(88,166,255, ${alpha})`; } - }; - const lightenToRgba = (hex, p = 0.5, alpha = 1) => { - try { - const { r, g, b } = parseHex(hex); - const mix = (c) => Math.round(c + (255 - c) * p); - const R = mix(r), G = mix(g), B = mix(b); - return `rgba(${R}, ${G}, ${B}, ${alpha})`; - } catch { return hexToRgba('#58a6ff', alpha); } - }; - return ( - - - {icon} - {title} - - {main} - - - {sub ? {sub} : null} - - ); - }; - - const Island = ({ title, children, sx }) => ( - - {title} - {children} - - ); - - const renderSummary = () => { - // Derive metric values - // CPU tile: model as main, speed as sub (like screenshot) - const cpuMain = (cpuInfo.name || (summary.processor || '') || '').split('\n')[0] || 'Unknown CPU'; - const cpuSub = cpuInfo.ghz || cpuInfo.cores - ? ( - - {cpuInfo.ghz ? `${Number(cpuInfo.ghz).toFixed(2)}GHz ` : ''} - {cpuInfo.cores ? ({cpuInfo.cores}-Cores) : null} - - ) - : ''; - - // MEMORY: total RAM - let totalRam = summary.total_ram; - if (!totalRam && Array.isArray(details.memory)) { - try { totalRam = details.memory.reduce((a, m) => a + (Number(m.capacity || 0) || 0), 0); } catch {} - } - const memVal = totalRam ? `${formatBytes(totalRam)}` : 'Unknown'; - // RAM speed best-effort: use max speed among modules - let memSpeed = ''; - try { - const speeds = (details.memory || []) - .map(m => parseInt(String(m.speed || '').replace(/[^0-9]/g, ''), 10)) - .filter(v => !Number.isNaN(v) && v > 0); - if (speeds.length) memSpeed = `Speed: ${Math.max(...speeds)} MT/s`; - } catch {} - - // STORAGE: OS drive (Windows C: if available) - let osDrive = null; - if (Array.isArray(details.storage)) { - osDrive = details.storage.find((d) => String(d.drive || '').toUpperCase().startsWith('C:')) || details.storage[0] || null; - } - const storageMain = osDrive && osDrive.total != null ? `${formatBytes(osDrive.total)}` : 'Unknown'; - const storageSub = (osDrive && osDrive.used != null && osDrive.total != null) - ? `${formatBytes(osDrive.used)} of ${formatBytes(osDrive.total)} used` - : (osDrive && osDrive.free != null && osDrive.total != null) - ? `${formatBytes(osDrive.total - osDrive.free)} of ${formatBytes(osDrive.total)} used` - : ''; - - // NETWORK: Speed of adapter with internal IP or first - const primaryIp = (summary.internal_ip || '').trim(); - let nic = null; - if (Array.isArray(details.network)) { - nic = details.network.find((n) => (n.ips || []).includes(primaryIp)) || details.network[0] || null; - } - function normalizeSpeed(val) { - const s = String(val || '').trim(); - if (!s) return 'unknown'; - const low = s.toLowerCase(); - if (low.includes('gbps') || low.includes('mbps')) return s; - const m = low.match(/(\d+\.?\d*)\s*([gmk]?)(bps)/); - if (!m) return s; - let num = parseFloat(m[1]); - const unit = m[2]; - if (unit === 'g') return `${num} Gbps`; - if (unit === 'm') return `${num} Mbps`; - if (unit === 'k') return `${(num/1000).toFixed(1)} Mbps`; - // raw bps - if (num >= 1e9) return `${(num/1e9).toFixed(1)} Gbps`; - if (num >= 1e6) return `${(num/1e6).toFixed(0)} Mbps`; - return s; - } - const netVal = nic ? normalizeSpeed(nic.link_speed || nic.speed) : 'Unknown'; - - return ( - - {/* Metrics row at the very top */} - - } - title="Processor" - main={cpuMain} - sub={cpuSub} - color="#132332" - /> - } - title="Installed RAM" - main={memVal} - sub={memSpeed || ' '} - color="#291a2e" - /> - } - title="Storage" - main={storageMain} - sub={storageSub || ' '} - color="#142616" - /> - } - title="Network" - main={netVal} - sub={(nic && nic.adapter) ? nic.adapter : ' '} - color="#2b1a18" - /> - - {/* Split pane: three-column layout (Summary | Storage | Memory/Network) */} - - {/* Left column: Summary table */} - - - - - - Description - - setDescription(e.target.value)} - onBlur={saveDescription} - placeholder="Enter description" - sx={{ - input: { color: '#fff' }, - '& .MuiOutlinedInput-root': { - '& fieldset': { borderColor: '#555' }, - '&:hover fieldset': { borderColor: '#888' } - } - }} - /> - - - {connectionType === "ssh" && ( - - SSH Endpoint - - - setConnectionDraft(e.target.value)} - placeholder="user@host or host" - sx={{ - maxWidth: 300, - input: { color: '#fff' }, - '& .MuiOutlinedInput-root': { - '& fieldset': { borderColor: '#555' }, - '&:hover fieldset': { borderColor: '#888' } - } - }} - /> - - - {connectionMessage && ( - {connectionMessage} - )} - {connectionError && ( - {connectionError} - )} - - - )} - {summaryItems.map((item) => ( - - {item.label} - {item.value} - - ))} - -
-
-
- - {/* Middle column: Storage */} - {renderStorage()} - - {/* Right column: Memory + Network */} - - {renderMemory()} - {renderNetwork()} - -
-
- ); - }; - - const placeholderTable = (headers) => ( - - - - - {headers.map((h) => ( - {h} - ))} - - - - - - No data available. - - - -
-
- ); - - const renderSoftware = () => { - if (!softwareRows.length) - return placeholderTable(["Software Name", "Version", "Action"]); - - return ( - - - setSoftwareSearch(e.target.value)} - sx={{ - input: { color: "#fff" }, - "& .MuiOutlinedInput-root": { - "& fieldset": { borderColor: "#555" }, - "&:hover fieldset": { borderColor: "#888" } - } - }} - /> - - {/* Constrain the table height within the page and enable scrolling */} - - - - - - handleSoftwareSort("name")} - > - Software Name - - - - handleSoftwareSort("version")} - > - Version - - - Action - - - - {softwareRows.map((s, i) => ( - - {s.name} - {s.version} - - - ))} - -
-
-
- ); - }; - - const renderMemory = () => { - const rows = details.memory || []; - if (!rows.length) return placeholderTable(["Slot", "Speed", "Serial Number", "Capacity"]); - return ( - - - - - Slot - Speed - Serial Number - Capacity - - - - {rows.map((m, i) => ( - - {m.slot} - {m.speed} - {m.serial} - {formatBytes(m.capacity)} - - ))} - -
-
- ); - }; - - const renderStorage = () => { - const toNum = (val) => { - if (val === undefined || val === null) return undefined; - if (typeof val === "number") { - return Number.isNaN(val) ? undefined : val; - } - const n = parseFloat(String(val).replace(/[^0-9.]+/g, "")); - return Number.isNaN(n) ? undefined : n; - }; - - const rows = (details.storage || []).map((d) => { - const total = toNum(d.total); - let usagePct = toNum(d.usage); - let usedBytes = toNum(d.used); - let freeBytes = toNum(d.free); - let freePct; - - if (usagePct !== undefined) { - if (usagePct <= 1) usagePct *= 100; - freePct = 100 - usagePct; - } - - if (usedBytes === undefined && total !== undefined && usagePct !== undefined) { - usedBytes = (usagePct / 100) * total; - } - - if (freeBytes === undefined && total !== undefined && usedBytes !== undefined) { - freeBytes = total - usedBytes; - } - - if (freePct === undefined && total !== undefined && freeBytes !== undefined) { - freePct = (freeBytes / total) * 100; - } - - if (usagePct === undefined && freePct !== undefined) { - usagePct = 100 - freePct; - } - - return { - drive: d.drive, - disk_type: d.disk_type, - used: usedBytes, - freePct, - freeBytes, - total, - usage: usagePct, - }; - }); - - if (!rows.length) { - return placeholderTable(["Drive", "Type", "Capacity"]); - } - - const fmtPct = (v) => (v !== undefined && !Number.isNaN(v) ? `${v.toFixed(0)}%` : "unknown"); - - return ( - - {rows.map((d, i) => { - const usage = d.usage ?? (d.total ? ((d.used || 0) / d.total) * 100 : 0); - const used = d.used; - const free = d.freeBytes; - const total = d.total; - return ( - - - - - {`Drive ${String(d.drive || '').replace('\\', '')}`} - {d.disk_type || 'Fixed Disk'} - - {total !== undefined ? formatBytes(total) : 'unknown'} - - - - - - - {used !== undefined ? `${formatBytes(used)} - ${fmtPct(usage)} in use` : 'unknown'} - - - {free !== undefined && total !== undefined ? `${formatBytes(free)} - ${fmtPct(100 - (usage || 0))} remaining` : ''} - - - - ); - })} - - ); - }; - - const renderNetwork = () => { - const rows = details.network || []; - const internalIp = meta.internalIp || summary.internal_ip || "unknown"; - const externalIp = meta.externalIp || summary.external_ip || "unknown"; - const ipHeader = ( - - - Internal IP: {internalIp || 'unknown'} - - - External IP: {externalIp || 'unknown'} - - - ); - if (!rows.length) { - return ( - - {ipHeader} - {placeholderTable(["Adapter", "IP Address", "MAC Address"])} - - ); - } - return ( - - {ipHeader} - - - - Adapter - IP Address - MAC Address - - - - {rows.map((n, i) => ( - - {n.adapter} - {(n.ips || []).join(", ")} - {formatMac(n.mac)} - - ))} - -
-
- ); - }; - - const jobStatusColor = (s) => { - const val = String(s || "").toLowerCase(); - if (val === "running") return "#58a6ff"; // borealis blue - if (val === "success") return "#00d18c"; - if (val === "failed") return "#ff4f4f"; - return "#666"; - }; - - const highlightCode = (code, lang) => { - try { - return Prism.highlight(code ?? "", Prism.languages[lang] || Prism.languages.markup, lang); - } catch { - return String(code || ""); - } - }; - - const handleViewOutput = useCallback(async (row, which) => { - if (!row || !row.id) return; - try { - const resp = await fetch(`/api/device/activity/job/${row.id}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - const lang = ((data.script_path || "").toLowerCase().endsWith(".ps1")) ? "powershell" - : ((data.script_path || "").toLowerCase().endsWith(".bat")) ? "batch" - : ((data.script_path || "").toLowerCase().endsWith(".sh")) ? "bash" - : ((data.script_path || "").toLowerCase().endsWith(".yml")) ? "yaml" : "powershell"; - setOutputLang(lang); - const friendly = resolveAssemblyName(data.script_name, data.script_path); - setOutputTitle(`${which === 'stderr' ? 'StdErr' : 'StdOut'} - ${friendly}`); - setOutputContent(which === 'stderr' ? (data.stderr || "") : (data.stdout || "")); - setOutputOpen(true); - } catch (e) { - console.warn("Failed to load output", e); - } - }, [resolveAssemblyName]); - - const handleHistorySort = (col) => { - if (historyOrderBy === col) setHistoryOrder(historyOrder === "asc" ? "desc" : "asc"); - else { - setHistoryOrderBy(col); - setHistoryOrder("asc"); - } - }; - - const historyDisplayRows = useMemo(() => { - return (historyRows || []).map((row) => ({ - ...row, - script_display_name: resolveAssemblyName(row.script_name, row.script_path), - })); - }, [historyRows, resolveAssemblyName]); - - const sortedHistory = useMemo(() => { - const dir = historyOrder === "asc" ? 1 : -1; - const key = historyOrderBy === "script_name" ? "script_display_name" : historyOrderBy; - return [...historyDisplayRows].sort((a, b) => { - const A = a[key]; - const B = b[key]; - if (key === "ran_at") return ((A || 0) - (B || 0)) * dir; - return String(A ?? "").localeCompare(String(B ?? "")) * dir; - }); - }, [historyDisplayRows, historyOrderBy, historyOrder]); - - const renderHistory = () => ( - - - - - Assembly - - handleHistorySort("script_name")}> - Task - - - - handleHistorySort("ran_at")} - > - Ran On - - - - handleHistorySort("status")} - > - Job Status - - - - StdOut / StdErr - - - - - {sortedHistory.map((r) => ( - - {(r.script_type || '').toLowerCase() === 'ansible' ? 'Ansible Playbook' : 'Script'} - {r.script_display_name || r.script_name} - {formatTimestamp(r.ran_at)} - - - {r.status} - - - - - {(String(r.script_type || '').toLowerCase() === 'ansible' && String(r.status||'') === 'Running') ? ( - - ) : null} - {r.has_stdout ? ( - - ) : null} - {r.has_stderr ? ( - - ) : null} - - - - ))} - {sortedHistory.length === 0 && ( - No activity yet. - )} - -
-
- ); - - - - const tabs = [ - { label: "Summary", content: renderSummary() }, - { label: "Installed Software", content: renderSoftware() }, - { label: "Activity History", content: renderHistory() } - ]; - // Use the snapshotted status so it stays static while on this page - const status = lockedStatus || statusFromHeartbeat(agent.last_seen || device?.lastSeen); - - return ( - - - - {onBack && ( - - )} - - - {agent.hostname || "Device Details"} - - - - setMenuAnchor(e.currentTarget)} - sx={{ - color: !(agent?.hostname || device?.hostname) ? "#666" : "#58a6ff", - borderColor: !(agent?.hostname || device?.hostname) ? "#333" : "#58a6ff", - border: "1px solid", - borderRadius: 1, - width: 32, - height: 32 - }} - > - - - setMenuAnchor(null)} - > - { - setMenuAnchor(null); - setQuickJobOpen(true); - }} - > - Quick Job - - { - setMenuAnchor(null); - setClearDialogOpen(true); - }} - > - Clear Device Activity - - - - - setTab(v)} - sx={{ borderBottom: 1, borderColor: "#333" }} - > - {tabs.map((t) => ( - - ))} - - {tabs[tab].content} - - setOutputOpen(false)} fullWidth maxWidth="md" - PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }} - > - {outputTitle} - - - {}} - highlight={(code) => highlightCode(code, outputLang)} - padding={12} - style={{ - fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace', - fontSize: 12, - color: "#e6edf3", - minHeight: 200 - }} - textareaProps={{ readOnly: true }} - /> - - - - - - - - {/* Recap dialog removed; recaps flow into Activity History stdout */} - - setClearDialogOpen(false)} - onConfirm={() => { - clearHistory(); - setClearDialogOpen(false); - }} - /> - - {quickJobOpen && ( - setQuickJobOpen(false)} - hostnames={[agent?.hostname || device?.hostname].filter(Boolean)} - /> - )} - - ); - } diff --git a/Data/Server/WebUI/src/Devices/Device_List.jsx b/Data/Server/WebUI/src/Devices/Device_List.jsx deleted file mode 100644 index e61164cb..00000000 --- a/Data/Server/WebUI/src/Devices/Device_List.jsx +++ /dev/null @@ -1,1832 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Device_List.jsx - -import React, { useState, useEffect, useCallback, useMemo, useRef } from "react"; -import { - Paper, - Box, - Typography, - Button, - IconButton, - Menu, - MenuItem, - Popover, - TextField, - Tooltip, - Checkbox, -} from "@mui/material"; -import MoreVertIcon from "@mui/icons-material/MoreVert"; -import ViewColumnIcon from "@mui/icons-material/ViewColumn"; -import AddIcon from "@mui/icons-material/Add"; -import CachedIcon from "@mui/icons-material/Cached"; -import { AgGridReact } from "ag-grid-react"; -import { ModuleRegistry, AllCommunityModule, themeQuartz } from "ag-grid-community"; -import { DeleteDeviceDialog, CreateCustomViewDialog, RenameCustomViewDialog } from "../Dialogs.jsx"; -import QuickJob from "../Scheduling/Quick_Job.jsx"; -import AddDevice from "./Add_Device.jsx"; - -ModuleRegistry.registerModules([AllCommunityModule]); - -const myTheme = themeQuartz.withParams({ - accentColor: "#FFA6FF", - backgroundColor: "#1f2836", - browserColorScheme: "dark", - chromeBackgroundColor: { - ref: "foregroundColor", - mix: 0.07, - onto: "backgroundColor", - }, - fontFamily: { - googleFont: "IBM Plex Sans", - }, - foregroundColor: "#FFF", - headerFontSize: 14, -}); - -const themeClassName = myTheme.themeName || "ag-theme-quartz"; -const gridFontFamily = '"IBM Plex Sans", "Helvetica Neue", Arial, sans-serif'; -const iconFontFamily = '"Quartz Regular"'; - -const getOsIconClass = (osName) => { - const value = (osName || "").toString().toLowerCase(); - if (!value) return ""; - - if (value.includes("mac") || value.includes("os x") || value.includes("darwin")) { - return "fa-brands fa-apple"; - } - - if (value.includes("win")) { - return "fa-brands fa-windows"; - } - - if ( - value.includes("linux") || - value.includes("ubuntu") || - value.includes("debian") || - value.includes("fedora") || - value.includes("red hat") || - value.includes("centos") || - value.includes("suse") || - value.includes("rhel") - ) { - return "fa-brands fa-linux"; - } - - return ""; -}; - -const DescriptionCellRenderer = React.memo(function DescriptionCellRenderer(props) { - const { value, data, onSaveDescription, fontFamily } = props; - const safeValue = typeof value === "string" ? value : value == null ? "" : String(value); - const [draft, setDraft] = useState(safeValue); - const [editing, setEditing] = useState(false); - const [saving, setSaving] = useState(false); - const [error, setError] = useState(""); - - useEffect(() => { - if (!editing && !saving) { - setDraft(safeValue); - } - }, [safeValue, editing, saving]); - - const handleFocus = useCallback((event) => { - event.stopPropagation(); - setEditing(true); - setError(""); - }, []); - - const handleChange = useCallback((event) => { - setDraft(event.target.value); - }, []); - - const handleKeyDown = useCallback( - async (event) => { - event.stopPropagation(); - if (event.key === "Enter") { - event.preventDefault(); - const trimmed = (draft || "").trim(); - if (trimmed === safeValue.trim()) { - setEditing(false); - setDraft(safeValue); - setError(""); - return; - } - if (typeof onSaveDescription !== "function" || !data) { - setEditing(false); - setError(""); - return; - } - setSaving(true); - setError(""); - const ok = await onSaveDescription(data, trimmed); - setSaving(false); - if (ok) { - setEditing(false); - } else { - setError("Failed to save description"); - } - } else if (event.key === "Escape") { - event.preventDefault(); - setDraft(safeValue); - setEditing(false); - setError(""); - } - }, - [data, draft, onSaveDescription, safeValue] - ); - - const handleBlur = useCallback( - (event) => { - event.stopPropagation(); - if (saving) return; - setEditing(false); - setDraft(safeValue); - setError(""); - }, - [saving, safeValue] - ); - - const stopPropagation = useCallback((event) => { - event.stopPropagation(); - }, []); - - const backgroundColor = saving - ? "rgba(255,255,255,0.04)" - : editing - ? "rgba(255,255,255,0.16)" - : "rgba(255,255,255,0.02)"; - - return ( - - ); -}); - -function formatLastSeen(tsSec, offlineAfter = 300) { - if (!tsSec) return "unknown"; - const now = Date.now() / 1000; - if (now - tsSec <= offlineAfter) return "Currently Online"; - const d = new Date(tsSec * 1000); - const date = d.toLocaleDateString("en-US", { - month: "2-digit", - day: "2-digit", - year: "numeric", - }); - const time = d.toLocaleTimeString("en-US", { - hour: "numeric", - minute: "2-digit", - }); - return `${date} @ ${time}`; -} - -function statusFromHeartbeat(tsSec, offlineAfter = 300) { - if (!tsSec) return "Offline"; - const now = Date.now() / 1000; - return now - tsSec <= offlineAfter ? "Online" : "Offline"; -} - -function formatUptime(seconds) { - const total = Number(seconds); - if (!Number.isFinite(total) || total <= 0) return ""; - const parts = []; - const days = Math.floor(total / 86400); - if (days) parts.push(`${days}d`); - const hours = Math.floor((total % 86400) / 3600); - if (hours) parts.push(`${hours}h`); - const minutes = Math.floor((total % 3600) / 60); - if (minutes) parts.push(`${minutes}m`); - const secondsPart = Math.floor(total % 60); - if (!parts.length && secondsPart) parts.push(`${secondsPart}s`); - return parts.join(' '); -} - -export default function DeviceList({ - onSelectDevice, - filterMode = "all", - title, - showAddButton, - addButtonLabel, - defaultAddType, -}) { - const [rows, setRows] = useState([]); - const [menuAnchor, setMenuAnchor] = useState(null); - const [selected, setSelected] = useState(null); - const [confirmOpen, setConfirmOpen] = useState(false); - // Track selection by agent id to avoid duplicate hostname collisions - const [selectedIds, setSelectedIds] = useState(() => new Set()); - const [quickJobOpen, setQuickJobOpen] = useState(false); - const [addDeviceOpen, setAddDeviceOpen] = useState(false); - const [addDeviceType, setAddDeviceType] = useState(null); - const computedTitle = useMemo(() => { - if (title) return title; - switch (filterMode) { - case "agent": - return "Agent Devices"; - case "ssh": - return "SSH Devices"; - case "winrm": - return "WinRM Devices"; - default: - return "Device Inventory"; - } - }, [filterMode, title]); - const derivedDefaultType = useMemo(() => { - if (defaultAddType !== undefined) return defaultAddType; - if (filterMode === "ssh" || filterMode === "winrm") return filterMode; - return null; - }, [defaultAddType, filterMode]); - const derivedAddLabel = useMemo(() => { - if (addButtonLabel) return addButtonLabel; - if (filterMode === "ssh") return "Add SSH Device"; - if (filterMode === "winrm") return "Add WinRM Device"; - return "Add Device"; - }, [addButtonLabel, filterMode]); - const derivedShowAddButton = useMemo(() => { - if (typeof showAddButton === "boolean") return showAddButton; - return filterMode !== "agent"; - }, [showAddButton, filterMode]); - - // Saved custom views (from server) - const [views, setViews] = useState([]); // [{id, name, columns:[id], filters:{}}] - const [selectedViewId, setSelectedViewId] = useState("default"); - const [createDialogOpen, setCreateDialogOpen] = useState(false); - const [newViewName, setNewViewName] = useState(""); - const [renameDialogOpen, setRenameDialogOpen] = useState(false); - const [renameViewName, setRenameViewName] = useState(""); - const [renameTarget, setRenameTarget] = useState(null); // {id, name} - const [viewActionAnchor, setViewActionAnchor] = useState(null); // anchor for per-item actions - const [viewActionTarget, setViewActionTarget] = useState(null); // view object for actions - - // Column configuration and rearranging state - const COL_LABELS = useMemo( - () => ({ - status: "Status", - agentVersion: "Agent Version", - site: "Site", - hostname: "Hostname", - description: "Description", - lastUser: "Last User", - type: "Type", - os: "OS", - internalIp: "Internal IP", - externalIp: "External IP", - lastReboot: "Last Reboot", - created: "Created", - lastSeen: "Last Seen", - agentId: "Agent ID", - agentHash: "Agent Hash", - agentGuid: "Agent GUID", - domain: "Domain", - uptime: "Uptime", - memory: "Memory", - network: "Network", - software: "Software", - storage: "Storage", - cpu: "CPU", - siteDescription: "Site Description", - }), - [] - ); - - const defaultColumns = useMemo( - () => [ - { id: "status", label: COL_LABELS.status }, - { id: "agentVersion", label: COL_LABELS.agentVersion }, - { id: "site", label: COL_LABELS.site }, - { id: "hostname", label: COL_LABELS.hostname }, - { id: "description", label: COL_LABELS.description }, - { id: "lastUser", label: COL_LABELS.lastUser }, - { id: "type", label: COL_LABELS.type }, - { id: "os", label: COL_LABELS.os }, - ], - [COL_LABELS] - ); - const [columns, setColumns] = useState(defaultColumns); - const [colChooserAnchor, setColChooserAnchor] = useState(null); - const gridRef = useRef(null); - - // Per-column filters - const [filtersState, setFiltersState] = useState({}); - - const sanitizeFilterModel = useCallback((raw) => { - if (!raw || typeof raw !== "object") return {}; - const sanitized = {}; - Object.entries(raw).forEach(([key, value]) => { - if (typeof value === "string") { - const trimmed = value.trim(); - if (trimmed) { - sanitized[key] = { - filterType: "text", - type: "contains", - filter: trimmed, - }; - } - return; - } - if (!value || typeof value !== "object") return; - const clone = JSON.parse(JSON.stringify(value)); - if (!clone.filterType) clone.filterType = "text"; - if (clone.filterType === "text") { - if (typeof clone.filter === "string") { - clone.filter = clone.filter.trim(); - } - if (Array.isArray(clone.conditions)) { - clone.conditions = clone.conditions - .map((condition) => { - if (!condition || typeof condition !== "object") return null; - const condClone = { ...condition }; - if (typeof condClone.filter === "string") { - condClone.filter = condClone.filter.trim(); - } - if ( - !condClone.filter && - !["blank", "notBlank"].includes(condClone.type ?? "") - ) { - return null; - } - return condClone; - }) - .filter(Boolean); - if (!clone.conditions.length) { - delete clone.conditions; - } - } - if ( - !clone.filter && - !clone.conditions && - !["blank", "notBlank"].includes(clone.type ?? "") - ) { - return; - } - } - sanitized[key] = clone; - }); - return sanitized; - }, []); - - const filterModelsEqual = useCallback( - (a, b) => JSON.stringify(a ?? {}) === JSON.stringify(b ?? {}), - [] - ); - - const replaceFilters = useCallback( - (raw) => { - const sanitized = - raw && typeof raw === "object" ? sanitizeFilterModel(raw) : {}; - setFiltersState((prev) => - filterModelsEqual(prev, sanitized) ? prev : sanitized - ); - }, - [filterModelsEqual, sanitizeFilterModel] - ); - - const mergeFilters = useCallback( - (raw) => { - if (!raw || typeof raw !== "object") return; - const sanitized = sanitizeFilterModel(raw); - if (!Object.keys(sanitized).length) return; - setFiltersState((prev) => { - const base = prev || {}; - const next = { ...base }; - let changed = false; - Object.entries(sanitized).forEach(([key, value]) => { - if (!value) return; - if (!next[key] || !filterModelsEqual(next[key], value)) { - next[key] = value; - changed = true; - } - }); - return changed ? next : base; - }); - }, - [filterModelsEqual, sanitizeFilterModel] - ); - - const filters = filtersState; - - const [sites, setSites] = useState([]); // sites list for assignment - const [assignDialogOpen, setAssignDialogOpen] = useState(false); - const [assignSiteId, setAssignSiteId] = useState(null); - const [assignTargets, setAssignTargets] = useState([]); // hostnames - - const [repoHash, setRepoHash] = useState(null); - const lastRepoFetchRef = useRef(0); - - const gridWrapperClass = themeClassName; - - const fetchLatestRepoHash = useCallback(async (options = {}) => { - const { force = false } = options || {}; - const now = Date.now(); - const elapsed = now - lastRepoFetchRef.current; - if (!force && repoHash && elapsed >= 0 && elapsed < 60_000) { - return repoHash; - } - try { - const params = new URLSearchParams({ repo: "bunny-lab-io/Borealis", branch: "main" }); - if (force) { - params.set("refresh", "1"); - } - const resp = await fetch(`/api/repo/current_hash?${params.toString()}`); - const json = await resp.json(); - const sha = (json?.sha || "").trim(); - if (!resp.ok || !sha) { - const err = new Error(`Latest hash status ${resp.status}${json?.error ? ` - ${json.error}` : ""}`); - err.response = json; - throw err; - } - lastRepoFetchRef.current = now; - setRepoHash((prev) => (sha ? sha : prev || null)); - return sha || null; - } catch (err) { - console.warn("Failed to fetch repository hash", err); - if (!force && repoHash) { - return repoHash; - } - lastRepoFetchRef.current = now; - setRepoHash((prev) => prev || null); - return null; - } - }, [repoHash]); - - const computeAgentVersion = useCallback((agentHashValue, repoHashValue) => { - const agentHash = (agentHashValue || "").trim(); - const repo = (repoHashValue || "").trim(); - if (!repo) return agentHash ? "Unknown" : "Unknown"; - if (!agentHash) return "Needs Updated"; - return agentHash === repo ? "Up-to-Date" : "Needs Updated"; - }, []); - - const fetchDevices = useCallback(async (options = {}) => { - const { refreshRepo = false } = options || {}; - let repoSha = repoHash; - if (refreshRepo || !repoSha) { - const fetched = await fetchLatestRepoHash({ force: refreshRepo }); - if (fetched) repoSha = fetched; - } - - const hashById = new Map(); - const hashByGuid = new Map(); - const hashByHost = new Map(); - try { - const hashResp = await fetch('/api/agent/hash_list'); - if (hashResp.ok) { - const hashJson = await hashResp.json(); - const list = Array.isArray(hashJson?.agents) ? hashJson.agents : []; - list.forEach((rec) => { - if (!rec || typeof rec !== 'object') return; - const hash = (rec.agent_hash || '').trim(); - if (!hash) return; - const agentId = (rec.agent_id || '').trim(); - const guidRaw = (rec.agent_guid || '').trim().toLowerCase(); - const hostKey = (rec.hostname || '').trim().toLowerCase(); - const isMemory = (rec.source || '').trim() === 'memory'; - if (agentId && (!hashById.has(agentId) || isMemory)) { - hashById.set(agentId, hash); - } - if (guidRaw && (!hashByGuid.has(guidRaw) || isMemory)) { - hashByGuid.set(guidRaw, hash); - } - if (hostKey && (!hashByHost.has(hostKey) || isMemory)) { - hashByHost.set(hostKey, hash); - } - }); - } - } catch (err) { - console.warn('Failed to fetch agent hash list', err); - } - - try { - const res = await fetch('/api/devices'); - if (!res.ok) { - const err = new Error(`Failed to fetch devices (${res.status})`); - try { - err.response = await res.json(); - } catch {} - throw err; - } - const payload = await res.json(); - const list = Array.isArray(payload?.devices) ? payload.devices : []; - - const normalizeJson = (value) => { - if (!value) return ''; - try { - return JSON.stringify(value); - } catch { - return ''; - } - }; - - const normalized = list.map((device, index) => { - const summary = device && typeof device.summary === 'object' ? { ...device.summary } : {}; - const rawHostname = (device.hostname || summary.hostname || '').trim(); - const hostname = rawHostname || `device-${index + 1}`; - const agentId = (device.agent_id || summary.agent_id || '').trim(); - const guidRaw = (device.agent_guid || summary.agent_guid || '').trim(); - const guidLookupKey = guidRaw.toLowerCase(); - const rowKey = guidRaw || agentId || hostname || `device-${index + 1}`; - let agentHash = (device.agent_hash || summary.agent_hash || '').trim(); - if (agentId && hashById.has(agentId)) agentHash = hashById.get(agentId) || agentHash; - if (!agentHash && guidLookupKey && hashByGuid.has(guidLookupKey)) { - agentHash = hashByGuid.get(guidLookupKey) || agentHash; - } - const hostKey = hostname.trim().toLowerCase(); - if (!agentHash && hostKey && hashByHost.has(hostKey)) { - agentHash = hashByHost.get(hostKey) || agentHash; - } - const lastSeen = Number(device.last_seen || summary.last_seen || 0) || 0; - const status = device.status || statusFromHeartbeat(lastSeen); - - if (guidRaw && !summary.agent_guid) { - summary.agent_guid = guidRaw; - } - - let createdTs = Number(device.created_at || 0) || 0; - let createdDisplay = summary.created || ''; - if (!createdTs && createdDisplay) { - const parsed = Date.parse(createdDisplay.replace(' ', 'T')); - if (!Number.isNaN(parsed)) createdTs = Math.floor(parsed / 1000); - } - if (!createdDisplay && device.created_at_iso) { - try { - createdDisplay = new Date(device.created_at_iso).toLocaleString(); - } catch {} - } - - const osName = - device.operating_system || - summary.operating_system || - summary.agent_operating_system || - "-"; - const type = (device.device_type || summary.device_type || '').trim(); - const lastUser = (device.last_user || summary.last_user || '').trim(); - const domain = (device.domain || summary.domain || '').trim(); - const internalIp = (device.internal_ip || summary.internal_ip || '').trim(); - const externalIp = (device.external_ip || summary.external_ip || '').trim(); - const lastReboot = (device.last_reboot || summary.last_reboot || '').trim(); - const uptimeSeconds = Number( - device.uptime || - summary.uptime_sec || - summary.uptime_seconds || - summary.uptime || - 0 - ) || 0; - const connectionType = (device.connection_type || summary.connection_type || '').trim().toLowerCase(); - const connectionLabel = connectionType === 'ssh' ? 'SSH' : connectionType === 'winrm' ? 'WinRM' : ''; - const connectionEndpoint = (device.connection_endpoint || summary.connection_endpoint || '').trim(); - - const memoryList = Array.isArray(device.memory) ? device.memory : []; - const networkList = Array.isArray(device.network) ? device.network : []; - const softwareList = Array.isArray(device.software) ? device.software : []; - const storageList = Array.isArray(device.storage) ? device.storage : []; - const cpuObj = - (device.cpu && typeof device.cpu === 'object' && device.cpu) || - (summary.cpu && typeof summary.cpu === 'object' ? summary.cpu : {}); - - const memoryDisplay = memoryList.length ? `${memoryList.length} module(s)` : ''; - const networkDisplay = networkList.length ? networkList.map((n) => n.adapter || n.name || '').filter(Boolean).join(', ') : ''; - const softwareDisplay = softwareList.length ? `${softwareList.length} item(s)` : ''; - const storageDisplay = storageList.length ? `${storageList.length} volume(s)` : ''; - const cpuDisplay = cpuObj.name || summary.processor || ''; - - return { - id: rowKey, - hostname, - status, - lastSeen, - lastSeenDisplay: formatLastSeen(lastSeen), - os: osName, - lastUser, - type: type || connectionLabel || '', - site: device.site_name || 'Not Configured', - siteId: device.site_id || null, - siteDescription: device.site_description || '', - description: (device.description || summary.description || '').trim(), - created: createdDisplay, - createdTs, - createdIso: device.created_at_iso || '', - agentGuid: guidRaw, - agentHash, - agentVersion: computeAgentVersion(agentHash, repoSha), - agentId, - domain, - internalIp, - externalIp, - lastReboot, - uptime: uptimeSeconds, - uptimeDisplay: formatUptime(uptimeSeconds), - memory: memoryDisplay, - memoryRaw: normalizeJson(memoryList), - network: networkDisplay, - networkRaw: normalizeJson(networkList), - software: softwareDisplay, - softwareRaw: normalizeJson(softwareList), - storage: storageDisplay, - storageRaw: normalizeJson(storageList), - cpu: cpuDisplay, - cpuRaw: normalizeJson(cpuObj), - summary, - details: device.details || {}, - connectionType, - connectionLabel, - connectionEndpoint, - isRemote: Boolean(connectionLabel), - }; - }); - - let filtered = normalized; - if (filterMode === "agent") { - filtered = normalized.filter((row) => !row.connectionType); - } else if (filterMode === "ssh") { - filtered = normalized.filter((row) => row.connectionType === "ssh"); - } else if (filterMode === "winrm") { - filtered = normalized.filter((row) => row.connectionType === "winrm"); - } - - setRows(filtered); - } catch (e) { - console.warn('Failed to load devices:', e); - setRows([]); - } - }, [repoHash, fetchLatestRepoHash, computeAgentVersion, filterMode]); - - const fetchViews = useCallback(async () => { - try { - const res = await fetch("/api/device_list_views"); - const data = await res.json(); - if (data && Array.isArray(data.views)) setViews(data.views); - else setViews([]); - } catch { - setViews([]); - } - }, []); - - useEffect(() => { - // Initial load only; removed auto-refresh interval - fetchDevices({ refreshRepo: true }); - }, [fetchDevices]); - - useEffect(() => { - fetchViews(); - }, [fetchViews]); - - // Sites helper fetch - const fetchSites = useCallback(async () => { - try { - const res = await fetch('/api/sites'); - const data = await res.json(); - setSites(Array.isArray(data?.sites) ? data.sites : []); - } catch { setSites([]); } - }, []); - - // Apply initial site filter from Sites page - useEffect(() => { - try { - // General initial filters (set by global search) - const json = localStorage.getItem('device_list_initial_filters'); - if (json) { - const obj = JSON.parse(json); - if (obj && typeof obj === 'object') { - mergeFilters(obj); - // Optionally ensure Site column exists when site filter is present - if (obj.site) { - setColumns((prev) => { - if (prev.some((c) => c.id === 'site')) return prev; - const hasAgentVersion = prev.some((c) => c.id === 'agentVersion'); - const remainder = prev.filter((c) => !['status', 'agentVersion'].includes(c.id)); - const base = [ - { id: 'status', label: COL_LABELS.status }, - ...(hasAgentVersion ? [{ id: 'agentVersion', label: COL_LABELS.agentVersion }] : []), - { id: 'site', label: COL_LABELS.site }, - ]; - if (!hasAgentVersion) { - return base.concat(prev.filter((c) => c.id !== 'status')); - } - return [...base, ...remainder]; - }); - } - } - localStorage.removeItem('device_list_initial_filters'); - } - - const site = localStorage.getItem('device_list_initial_site_filter'); - if (site && site.trim()) { - setColumns((prev) => { - const hasSite = prev.some((c) => c.id === 'site'); - if (hasSite) return prev; - const next = [...prev]; - const agentIndex = next.findIndex((c) => c.id === 'agentVersion'); - const insertAt = agentIndex >= 0 ? agentIndex + 1 : 1; - next.splice(insertAt, 0, { id: 'site', label: COL_LABELS.site }); - return next; - }); - mergeFilters({ site }); - localStorage.removeItem('device_list_initial_site_filter'); - } - } catch {} - }, [COL_LABELS.site, mergeFilters]); - - const applyView = useCallback((view) => { - if (!view || view.id === "default") { - setColumns(defaultColumns); - replaceFilters({}); - return; - } - try { - const ids = Array.isArray(view.columns) ? view.columns : []; - // Ensure status is present and first - const finalIds = ["status", ...ids.filter((x) => x !== "status")]; - const mapped = finalIds - .filter((id) => COL_LABELS[id]) - .map((id) => ({ id, label: COL_LABELS[id] })); - setColumns(mapped.length ? mapped : defaultColumns); - replaceFilters( - view.filters && typeof view.filters === "object" ? view.filters : {} - ); - } catch { - setColumns(defaultColumns); - replaceFilters({}); - } - }, [COL_LABELS, defaultColumns, replaceFilters]); - - const statusTokenTheme = useMemo( - () => ({ - Online: { - text: "#00d18c", - background: "rgba(0, 209, 140, 0.16)", - border: "1px solid rgba(0, 209, 140, 0.45)", - dot: "#00d18c", - }, - Offline: { - text: "#b0b8c8", - background: "rgba(176, 184, 200, 0.14)", - border: "1px solid rgba(176, 184, 200, 0.35)", - dot: "#c3cada", - }, - default: { - text: "#e2e6f0", - background: "rgba(226, 230, 240, 0.12)", - border: "1px solid rgba(226, 230, 240, 0.25)", - dot: "#e2e6f0", - }, - }), - [] - ); - - const formatCreated = useCallback((created, createdTs) => { - if (createdTs) { - const d = new Date(createdTs * 1000); - const mm = String(d.getMonth() + 1).padStart(2, "0"); - const dd = String(d.getDate()).padStart(2, "0"); - const yyyy = d.getFullYear(); - const hh = d.getHours() % 12 || 12; - const min = String(d.getMinutes()).padStart(2, "0"); - const ampm = d.getHours() >= 12 ? "PM" : "AM"; - return `${mm}/${dd}/${yyyy} @ ${hh}:${min} ${ampm}`; - } - return created || ""; - }, []); - - const filterModel = useMemo( - () => JSON.parse(JSON.stringify(filters || {})), - [filters] - ); - - useEffect(() => { - if (gridRef.current?.api) { - gridRef.current.api.setFilterModel(filterModel); - } - }, [filterModel]); - - const handleFilterChanged = useCallback( - (event) => { - const model = event.api.getFilterModel() || {}; - replaceFilters(model); - }, - [replaceFilters] - ); - - const handleSelectionChanged = useCallback(() => { - const api = gridRef.current?.api; - if (!api) return; - const selectedNodes = api.getSelectedNodes(); - const ids = selectedNodes - .map((node) => node.data?.id) - .filter((id) => id !== undefined && id !== null); - setSelectedIds(new Set(ids)); - }, []); - - const openMenu = useCallback((event, row) => { - setMenuAnchor(event.currentTarget); - setSelected(row); - }, []); - - const closeMenu = useCallback(() => setMenuAnchor(null), []); - - const confirmDelete = useCallback(() => { - closeMenu(); - setConfirmOpen(true); - }, [closeMenu]); - - const handleDelete = useCallback(async () => { - if (!selected) return; - const targetAgentId = selected.agentId || selected.summary?.agent_id || selected.id; - try { - if (targetAgentId) { - await fetch(`/api/agent/${encodeURIComponent(targetAgentId)}`, { method: "DELETE" }); - } - } catch (e) { - console.warn("Failed to remove agent", e); - } - setRows((r) => r.filter((x) => x.id !== selected.id)); - setSelectedIds((prev) => { - if (!prev.has(selected.id)) return prev; - const next = new Set(prev); - next.delete(selected.id); - return next; - }); - setConfirmOpen(false); - setSelected(null); - }, [selected]); - - const hostnameCellRenderer = useCallback( - (params) => { - const row = params.data; - if (!row) return null; - const handleClick = (event) => { - event.preventDefault(); - event.stopPropagation(); - if (onSelectDevice) onSelectDevice(row); - }; - const label = row.connectionLabel || ""; - let badgeBg = "#2d3042"; - let badgeColor = "#a4c7ff"; - if (label === "SSH") { - badgeBg = "#2a3b28"; - badgeColor = "#7cffc4"; - } else if (label === "WinRM") { - badgeBg = "#352e3b"; - badgeColor = "#ffb6ff"; - } - return ( - - {label ? ( - - {label} - - ) : null} -
- {row.hostname || ""} - - - ); - }, - [onSelectDevice] - ); - - const statusCellRenderer = useCallback( - (params) => { - const status = params.value || ""; - if (!status) return null; - const theme = statusTokenTheme[status] || statusTokenTheme.default; - return ( - - - {status} - - ); - }, - [statusTokenTheme, gridFontFamily] - ); - - const osCellRenderer = useCallback((params) => { - const rawValue = params.value; - const label = typeof rawValue === "string" ? rawValue : rawValue == null ? "" : String(rawValue); - const display = label.trim() || "-"; - const iconClass = getOsIconClass(label); - - return ( - - {iconClass ? ( - - ); - }, []); - - const actionCellRenderer = useCallback( - (params) => { - const row = params.data; - if (!row) return null; - const handleClick = (event) => { - event.stopPropagation(); - openMenu(event, row); - }; - return ( - - - - ); - }, - [openMenu] - ); - - const handleDescriptionSave = useCallback( - async (row, nextDescription) => { - if (!row) return false; - const trimmed = (nextDescription || "").trim(); - const targetHost = (row.hostname || row.summary?.hostname || "").trim(); - if (!targetHost) return false; - try { - const resp = await fetch(`/api/device/description/${targetHost}`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ description: trimmed }), - }); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const matchValue = row.id || row.agentGuid || row.hostname || targetHost; - setRows((prev) => - prev.map((item) => { - const itemMatch = item.id || item.agentGuid || item.hostname || ""; - if (itemMatch !== matchValue) return item; - const updated = { - ...item, - description: trimmed, - summary: { ...(item.summary || {}), description: trimmed }, - }; - if (item.details) { - updated.details = { ...item.details, description: trimmed }; - } - return updated; - }) - ); - setSelected((prev) => { - if (!prev) return prev; - const prevMatch = prev.id || prev.agentGuid || prev.hostname || ""; - if (prevMatch !== matchValue) return prev; - const updated = { - ...prev, - description: trimmed, - summary: { ...(prev.summary || {}), description: trimmed }, - }; - if (prev.details) { - updated.details = { ...prev.details, description: trimmed }; - } - return updated; - }); - return true; - } catch (e) { - console.warn("Failed to save description", e); - return false; - } - }, - [setRows, setSelected] - ); - - const columnDefs = useMemo(() => { - const defs = columns.map((col) => { - switch (col.id) { - case "status": - return { - field: "status", - headerName: col.label, - cellRenderer: statusCellRenderer, - cellClass: "status-pill-cell", - width: 112, - minWidth: 112, - flex: 0, - }; - case "agentVersion": - return { - field: "agentVersion", - headerName: col.label, - width: 140, - minWidth: 150, - flex: 0, - }; - case "site": - return { - field: "site", - headerName: col.label, - valueGetter: (params) => params.data?.site || "Not Configured", - width: 140, - minWidth: 140, - flex: 0, - }; - case "hostname": - return { - field: "hostname", - headerName: col.label, - cellRenderer: hostnameCellRenderer, - width: 210, - minWidth: 210, - flex: 0, - }; - case "description": - return { - field: "description", - headerName: col.label, - width: 280, - minWidth: 280, - flex: 0, - cellRenderer: DescriptionCellRenderer, - cellRendererParams: { - onSaveDescription: handleDescriptionSave, - fontFamily: gridFontFamily, - }, - }; - case "lastUser": - return { - field: "lastUser", - headerName: col.label, - width: 220, - minWidth: 220, - flex: 0, - }; - case "type": - return { - field: "type", - headerName: col.label, - width: 170, - minWidth: 170, - flex: 0, - }; - case "os": - return { - field: "os", - headerName: col.label, - width: 410, - minWidth: 410, - flex: 1, - cellRenderer: osCellRenderer, - }; - case "internalIp": - return { - field: "internalIp", - headerName: col.label, - width: 140, - minWidth: 140, - flex: 0, - }; - case "externalIp": - return { - field: "externalIp", - headerName: col.label, - width: 140, - minWidth: 140, - flex: 0, - }; - case "lastReboot": - return { - field: "lastReboot", - headerName: col.label, - width: 180, - minWidth: 180, - flex: 0, - }; - case "created": - return { - field: "created", - headerName: col.label, - valueGetter: (params) => - formatCreated(params.data?.created, params.data?.createdTs), - comparator: (a, b, nodeA, nodeB) => - (nodeA?.data?.createdTs || 0) - (nodeB?.data?.createdTs || 0), - width: 200, - minWidth: 200, - flex: 0, - }; - case "lastSeen": - return { - field: "lastSeen", - headerName: col.label, - valueGetter: (params) => formatLastSeen(params.data?.lastSeen), - comparator: (a, b, nodeA, nodeB) => - (nodeA?.data?.lastSeen || 0) - (nodeB?.data?.lastSeen || 0), - width: 200, - minWidth: 200, - flex: 0, - }; - case "agentId": - return { - field: "agentId", - headerName: col.label, - width: 290, - minWidth: 290, - flex: 0, - }; - case "agentHash": - return { - field: "agentHash", - headerName: col.label, - width: 365, - minWidth: 365, - flex: 0, - }; - case "agentGuid": - return { - field: "agentGuid", - headerName: col.label, - width: 345, - minWidth: 345, - flex: 0, - }; - case "domain": - return { - field: "domain", - headerName: col.label, - width: 160, - minWidth: 160, - flex: 0, - }; - case "uptime": - return { - field: "uptime", - headerName: col.label, - valueGetter: (params) => - params.data?.uptimeDisplay || - formatUptime(params.data?.uptime || 0), - comparator: (a, b, nodeA, nodeB) => - (nodeA?.data?.uptime || 0) - (nodeB?.data?.uptime || 0), - width: 140, - minWidth: 140, - flex: 0, - }; - case "memory": - case "network": - case "software": - case "storage": - case "cpu": - case "siteDescription": - return { - field: col.id, - headerName: col.label, - minWidth: 200, - }; - default: - return { - field: col.id, - headerName: col.label, - }; - } - }); - return [ - { - headerName: "", - field: "__select__", - width: 52, - maxWidth: 52, - checkboxSelection: true, - headerCheckboxSelection: true, - resizable: false, - sortable: false, - suppressMenu: true, - filter: false, - pinned: "left", - lockPosition: true, - }, - ...defs, - { - headerName: "", - field: "__actions__", - width: 64, - maxWidth: 64, - resizable: false, - sortable: false, - suppressMenu: true, - filter: false, - cellRenderer: actionCellRenderer, - pinned: "right", - }, - ]; - }, [ - columns, - actionCellRenderer, - formatCreated, - handleDescriptionSave, - hostnameCellRenderer, - statusCellRenderer, - ]); - - const defaultColDef = useMemo( - () => ({ - sortable: true, - filter: "agTextColumnFilter", - resizable: true, - flex: 1, - minWidth: 160, - }), - [] - ); - - const handleGridReady = useCallback( - (params) => { - params.api.setFilterModel(filterModel); - }, - [filterModel] - ); - - const getRowId = useCallback( - (params) => - params.data?.id || - params.data?.agentGuid || - params.data?.hostname || - String(params.rowIndex ?? ""), - [] - ); - - return ( - - {/* Header area with title on left and controls on right */} - - - - {computedTitle} - - - {/* Views dropdown + add button */} - - { - const val = e.target.value; - setSelectedViewId(val); - if (val === "default") applyView({ id: "default" }); - else { - const v = views.find((x) => String(x.id) === String(val)); - if (v) applyView(v); - } - }} - sx={{ - minWidth: 220, - mr: 0, - '& .MuiOutlinedInput-root': { - height: 32, - pr: 0, - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - '& fieldset': { borderColor: '#555', borderRight: '1px solid #555' }, - '&:hover fieldset': { borderColor: '#888' }, - }, - '& .MuiSelect-select': { - display: 'flex', - alignItems: 'center', - py: 0, - }, - }} - SelectProps={{ - MenuProps: { - PaperProps: { sx: { bgcolor: '#1e1e1e', color: '#fff' } }, - }, - renderValue: (val) => { - if (val === "default") return "Default View"; - const v = views.find((x) => String(x.id) === String(val)); - return v ? v.name : "Default View"; - } - }} - > - Default View - {views.map((v) => ( - - - {v.name} - { - e.stopPropagation(); - setViewActionAnchor(e.currentTarget); - setViewActionTarget(v); - }} - sx={{ color: '#ccc' }} - > - - - - - ))} - - { setNewViewName(""); setCreateDialogOpen(true); }} - sx={{ - ml: '-1px', - border: '1px solid #555', - borderLeft: '1px solid #555', - borderRadius: '0 4px 4px 0', - color: '#bbb', - height: 32, - width: 32, - }} - > - - - - - fetchDevices({ refreshRepo: true })} - sx={{ color: "#bbb", mr: 1 }} - > - - - - - setColChooserAnchor(e.currentTarget)} - sx={{ color: "#bbb", mr: 1 }} - > - - - - {derivedShowAddButton && ( - - )} - - - {/* Second row: Quick Job button aligned under header title */} - - - - - {/* The Size of the Grid itself and its margins relative to the overall page */} - - span": { - margin: 0, - }, - }} - > - - - - {/* View actions menu (rename/delete for custom views) */} - { setViewActionAnchor(null); setViewActionTarget(null); }} - PaperProps={{ sx: { bgcolor: '#1e1e1e', color: '#fff', fontSize: '13px' } }} - > - { - const v = viewActionTarget; - setViewActionAnchor(null); - if (!v) return; - setRenameTarget(v); - setRenameViewName(v.name || ""); - setRenameDialogOpen(true); - }}>Rename - { - const v = viewActionTarget; - setViewActionAnchor(null); - if (!v) return; - try { - await fetch(`/api/device_list_views/${encodeURIComponent(v.id)}`, { method: 'DELETE' }); - } catch {} - setViews((prev) => prev.filter((x) => String(x.id) !== String(v.id))); - if (String(selectedViewId) === String(v.id)) { - setSelectedViewId('default'); - applyView({ id: 'default' }); - } - }}>Delete - - - {/* Create new custom view dialog */} - setCreateDialogOpen(false)} - onSave={async () => { - const name = (newViewName || '').trim(); - if (!name) return; - // Build current config - const cols = (columns || []).map((c) => c.id); - const cfg = { name, columns: cols, filters }; - try { - const res = await fetch('/api/device_list_views', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(cfg) - }); - if (res.ok) { - const created = await res.json(); - setViews((prev) => [...prev, created].sort((a, b) => String(a.name).localeCompare(String(b.name)))); - setSelectedViewId(String(created.id)); - // Already applied in UI; we keep current state - setCreateDialogOpen(false); - setNewViewName(''); - } - } catch {} - }} - /> - - {/* Rename custom view dialog */} - setRenameDialogOpen(false)} - onSave={async () => { - const v = renameTarget; - const newName = (renameViewName || '').trim(); - if (!v || !newName) return; - try { - const res = await fetch(`/api/device_list_views/${encodeURIComponent(v.id)}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: newName }) - }); - if (res.ok) { - const updated = await res.json(); - setViews((prev) => prev.map((x) => String(x.id) === String(v.id) ? updated : x)); - setRenameDialogOpen(false); - setRenameViewName(''); - setRenameTarget(null); - } - } catch {} - }} - /> - {/* Column chooser popover */} - setColChooserAnchor(null)} - anchorOrigin={{ vertical: "bottom", horizontal: "right" }} - PaperProps={{ sx: { bgcolor: "#1e1e1e", color: '#fff', p: 1 } }} - > - - {Object.entries(COL_LABELS) - .filter(([id]) => id !== 'status') - .map(([id, label]) => ( - e.stopPropagation()} sx={{ gap: 1 }}> - c.id === id)} - onChange={(e) => { - const checked = e.target.checked; - setColumns((prev) => { - const exists = prev.some((c) => c.id === id); - if (checked) { - if (exists) return prev; - const nextLabel = COL_LABELS[id] || label || id; - return [...prev, { id, label: nextLabel }]; - } - return prev.filter((c) => c.id !== id); - }); - }} - sx={{ p: 0.3, color: '#bbb' }} - /> - {label || id} - - ))} - - - - - - - { - closeMenu(); - await fetchSites(); - const targets = new Set(selectedIds); - if (selected && !targets.has(selected.id)) targets.add(selected.id); - const idToHost = new Map(rows.map((r) => [r.id, r.hostname])); - const hostnames = Array.from(targets).map((id) => idToHost.get(id)).filter(Boolean); - setAssignTargets(hostnames); - setAssignSiteId(null); - setAssignDialogOpen(true); - }}>Add to Site - { - closeMenu(); - await fetchSites(); - const targets = new Set(selectedIds); - if (selected && !targets.has(selected.id)) targets.add(selected.id); - const idToHost = new Map(rows.map((r) => [r.id, r.hostname])); - const hostnames = Array.from(targets).map((id) => idToHost.get(id)).filter(Boolean); - setAssignTargets(hostnames); - setAssignSiteId(null); - setAssignDialogOpen(true); - }}>Move to Another Site - Delete - - setConfirmOpen(false)} - onConfirm={handleDelete} - /> - - {quickJobOpen && ( - setQuickJobOpen(false)} - hostnames={rows.filter((r) => selectedIds.has(r.id)).map((r) => r.hostname)} - /> - )} - {assignDialogOpen && ( - setAssignDialogOpen(false)} - anchorReference="anchorPosition" - anchorPosition={{ top: Math.max(Math.floor(window.innerHeight*0.5), 200), left: Math.max(Math.floor(window.innerWidth*0.5), 300) }} - PaperProps={{ sx: { bgcolor: '#1e1e1e', color: '#fff', p: 2, minWidth: 360 } }} - > - - Assign {assignTargets.length} device(s) to a site - setAssignSiteId(Number(e.target.value))} - sx={{ '& .MuiOutlinedInput-root': { '& fieldset': { borderColor: '#444' }, '&:hover fieldset': { borderColor: '#666' } }, label: { color: '#aaa' } }} - > - {sites.map((s) => ( - {s.name} - ))} - - - - - - - - )} - { - setAddDeviceOpen(false); - setAddDeviceType(derivedDefaultType ?? null); - }} - onCreated={() => { - setAddDeviceOpen(false); - setAddDeviceType(derivedDefaultType ?? null); - fetchDevices({ refreshRepo: true }); - }} - /> - - ); -} diff --git a/Data/Server/WebUI/src/Devices/Enrollment_Codes.jsx b/Data/Server/WebUI/src/Devices/Enrollment_Codes.jsx deleted file mode 100644 index db3e387e..00000000 --- a/Data/Server/WebUI/src/Devices/Enrollment_Codes.jsx +++ /dev/null @@ -1,371 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/Server/WebUI/src/Admin/Enrollment_Codes.jsx - -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { - Alert, - Box, - Button, - Chip, - CircularProgress, - FormControl, - IconButton, - InputLabel, - MenuItem, - Paper, - Select, - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Tooltip, - Typography, -} from "@mui/material"; -import { - ContentCopy as CopyIcon, - DeleteOutline as DeleteIcon, - Refresh as RefreshIcon, - Key as KeyIcon, -} from "@mui/icons-material"; - -const TTL_PRESETS = [ - { value: 1, label: "1 hour" }, - { value: 3, label: "3 hours" }, - { value: 6, label: "6 hours" }, - { value: 12, label: "12 hours" }, - { value: 24, label: "24 hours" }, -]; - -const statusColor = { - active: "success", - used: "default", - expired: "warning", -}; - -const maskCode = (code) => { - if (!code) return "—"; - const parts = code.split("-"); - if (parts.length <= 1) { - const prefix = code.slice(0, 4); - return `${prefix}${"•".repeat(Math.max(0, code.length - prefix.length))}`; - } - return parts - .map((part, idx) => (idx === 0 || idx === parts.length - 1 ? part : "•".repeat(part.length))) - .join("-"); -}; - -const formatDateTime = (value) => { - if (!value) return "—"; - const date = new Date(value); - if (Number.isNaN(date.getTime())) return value; - return date.toLocaleString(); -}; - -const determineStatus = (record) => { - if (!record) return "expired"; - const maxUses = Number.isFinite(record?.max_uses) ? record.max_uses : 1; - const useCount = Number.isFinite(record?.use_count) ? record.use_count : 0; - if (useCount >= Math.max(1, maxUses || 1)) return "used"; - if (!record.expires_at) return "expired"; - const expires = new Date(record.expires_at); - if (Number.isNaN(expires.getTime())) return "expired"; - return expires.getTime() > Date.now() ? "active" : "expired"; -}; - -function EnrollmentCodes() { - const [codes, setCodes] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - const [feedback, setFeedback] = useState(null); - const [statusFilter, setStatusFilter] = useState("all"); - const [ttlHours, setTtlHours] = useState(6); - const [generating, setGenerating] = useState(false); - const [maxUses, setMaxUses] = useState(2); - - const filteredCodes = useMemo(() => { - if (statusFilter === "all") return codes; - return codes.filter((code) => determineStatus(code) === statusFilter); - }, [codes, statusFilter]); - - const fetchCodes = useCallback(async () => { - setLoading(true); - setError(""); - try { - const query = statusFilter === "all" ? "" : `?status=${encodeURIComponent(statusFilter)}`; - const resp = await fetch(`/api/admin/enrollment-codes${query}`, { - credentials: "include", - }); - if (!resp.ok) { - const body = await resp.json().catch(() => ({})); - throw new Error(body.error || `Request failed (${resp.status})`); - } - const data = await resp.json(); - setCodes(Array.isArray(data.codes) ? data.codes : []); - } catch (err) { - setError(err.message || "Unable to load enrollment codes"); - } finally { - setLoading(false); - } - }, [statusFilter]); - - useEffect(() => { - fetchCodes(); - }, [fetchCodes]); - - const handleGenerate = useCallback(async () => { - setGenerating(true); - setError(""); - try { - const resp = await fetch("/api/admin/enrollment-codes", { - method: "POST", - credentials: "include", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ ttl_hours: ttlHours, max_uses: maxUses }), - }); - if (!resp.ok) { - const body = await resp.json().catch(() => ({})); - throw new Error(body.error || `Request failed (${resp.status})`); - } - const created = await resp.json(); - setFeedback({ type: "success", message: `Installer code ${created.code} created` }); - await fetchCodes(); - } catch (err) { - setFeedback({ type: "error", message: err.message || "Failed to create code" }); - } finally { - setGenerating(false); - } - }, [fetchCodes, ttlHours, maxUses]); - - const handleDelete = useCallback( - async (id) => { - if (!id) return; - const confirmDelete = window.confirm("Delete this unused installer code?"); - if (!confirmDelete) return; - setError(""); - try { - const resp = await fetch(`/api/admin/enrollment-codes/${encodeURIComponent(id)}`, { - method: "DELETE", - credentials: "include", - }); - if (!resp.ok) { - const body = await resp.json().catch(() => ({})); - throw new Error(body.error || `Request failed (${resp.status})`); - } - setFeedback({ type: "success", message: "Installer code deleted" }); - await fetchCodes(); - } catch (err) { - setFeedback({ type: "error", message: err.message || "Failed to delete code" }); - } - }, - [fetchCodes] - ); - - const handleCopy = useCallback((code) => { - if (!code) return; - try { - if (navigator.clipboard?.writeText) { - navigator.clipboard.writeText(code); - setFeedback({ type: "success", message: "Code copied to clipboard" }); - } else { - const textArea = document.createElement("textarea"); - textArea.value = code; - textArea.style.position = "fixed"; - textArea.style.opacity = "0"; - document.body.appendChild(textArea); - textArea.select(); - document.execCommand("copy"); - document.body.removeChild(textArea); - setFeedback({ type: "success", message: "Code copied to clipboard" }); - } - } catch (err) { - setFeedback({ type: "error", message: err.message || "Unable to copy code" }); - } - }, []); - - const renderStatusChip = (record) => { - const status = determineStatus(record); - return ; - }; - - return ( - - - - Enrollment Installer Codes - - - - - - Filter - - - - - Duration - - - - - Allowed Uses - - - - - - - - - {feedback ? ( - setFeedback(null)} - variant="outlined" - > - {feedback.message} - - ) : null} - - {error ? ( - - {error} - - ) : null} - - - - - - Status - Installer Code - Expires At - Created By - Usage - Last Used - Consumed At - Used By GUID - Actions - - - - {loading ? ( - - - - - Loading installer codes… - - - - ) : filteredCodes.length === 0 ? ( - - - - No installer codes match this filter. - - - - ) : ( - filteredCodes.map((record) => { - const status = determineStatus(record); - const maxAllowed = Math.max(1, Number.isFinite(record?.max_uses) ? record.max_uses : 1); - const usageCount = Math.max(0, Number.isFinite(record?.use_count) ? record.use_count : 0); - const disableDelete = usageCount !== 0; - return ( - - {renderStatusChip(record)} - {maskCode(record.code)} - {formatDateTime(record.expires_at)} - {record.created_by_user_id || "—"} - {`${usageCount} / ${maxAllowed}`} - {formatDateTime(record.last_used_at)} - {formatDateTime(record.used_at)} - - {record.used_by_guid || "—"} - - - - - handleCopy(record.code)} - disabled={!record.code} - > - - - - - - - handleDelete(record.id)} - disabled={disableDelete} - > - - - - - - - ); - }) - )} - -
-
-
-
- ); -} - -export default React.memo(EnrollmentCodes); diff --git a/Data/Server/WebUI/src/Devices/SSH_Devices.jsx b/Data/Server/WebUI/src/Devices/SSH_Devices.jsx deleted file mode 100644 index e993985b..00000000 --- a/Data/Server/WebUI/src/Devices/SSH_Devices.jsx +++ /dev/null @@ -1,480 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { - Paper, - Box, - Typography, - Button, - IconButton, - Table, - TableHead, - TableBody, - TableRow, - TableCell, - TableSortLabel, - Dialog, - DialogTitle, - DialogContent, - DialogActions, - TextField, - CircularProgress -} from "@mui/material"; -import AddIcon from "@mui/icons-material/Add"; -import EditIcon from "@mui/icons-material/Edit"; -import DeleteIcon from "@mui/icons-material/Delete"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import { ConfirmDeleteDialog } from "../Dialogs.jsx"; -import AddDevice from "./Add_Device.jsx"; - -const tableStyles = { - "& th, & td": { - color: "#ddd", - borderColor: "#2a2a2a", - fontSize: 13, - py: 0.75 - }, - "& th": { - fontWeight: 600 - }, - "& th .MuiTableSortLabel-root": { color: "#ddd" }, - "& th .MuiTableSortLabel-root.Mui-active": { color: "#ddd" } -}; - -const defaultForm = { - hostname: "", - address: "", - description: "", - operating_system: "" -}; - -export default function SSHDevices({ type = "ssh" }) { - const typeLabel = type === "winrm" ? "WinRM" : "SSH"; - const apiBase = type === "winrm" ? "/api/winrm_devices" : "/api/ssh_devices"; - const pageTitle = `${typeLabel} Devices`; - const addButtonLabel = `Add ${typeLabel} Device`; - const addressLabel = `${typeLabel} Address`; - const loadingLabel = `Loading ${typeLabel} devices…`; - const emptyLabel = `No ${typeLabel} devices have been added yet.`; - const descriptionText = type === "winrm" - ? "Manage remote endpoints reachable via WinRM for playbook execution." - : "Manage remote endpoints reachable via SSH for playbook execution."; - const editDialogTitle = `Edit ${typeLabel} Device`; - const newDialogTitle = `New ${typeLabel} Device`; - const [rows, setRows] = useState([]); - const [orderBy, setOrderBy] = useState("hostname"); - const [order, setOrder] = useState("asc"); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(""); - const [dialogOpen, setDialogOpen] = useState(false); - const [form, setForm] = useState(defaultForm); - const [formError, setFormError] = useState(""); - const [submitting, setSubmitting] = useState(false); - const [editTarget, setEditTarget] = useState(null); - const [deleteTarget, setDeleteTarget] = useState(null); - const [deleteBusy, setDeleteBusy] = useState(false); - const [addDeviceOpen, setAddDeviceOpen] = useState(false); - - const isEdit = Boolean(editTarget); - - const loadDevices = useCallback(async () => { - setLoading(true); - setError(""); - try { - const resp = await fetch(apiBase); - if (!resp.ok) { - const data = await resp.json().catch(() => ({})); - throw new Error(data?.error || `HTTP ${resp.status}`); - } - const data = await resp.json(); - const list = Array.isArray(data?.devices) ? data.devices : []; - setRows(list); - } catch (err) { - setError(String(err.message || err)); - setRows([]); - } finally { - setLoading(false); - } - }, [apiBase]); - - useEffect(() => { - loadDevices(); - }, [loadDevices]); - - const sortedRows = useMemo(() => { - const list = [...rows]; - list.sort((a, b) => { - const getKey = (row) => { - switch (orderBy) { - case "created_at": - return Number(row.created_at || 0); - case "address": - return (row.connection_endpoint || "").toLowerCase(); - case "description": - return (row.description || "").toLowerCase(); - default: - return (row.hostname || "").toLowerCase(); - } - }; - const aKey = getKey(a); - const bKey = getKey(b); - if (aKey < bKey) return order === "asc" ? -1 : 1; - if (aKey > bKey) return order === "asc" ? 1 : -1; - return 0; - }); - return list; - }, [rows, order, orderBy]); - - const handleSort = (column) => () => { - if (orderBy === column) { - setOrder((prev) => (prev === "asc" ? "desc" : "asc")); - } else { - setOrderBy(column); - setOrder("asc"); - } - }; - - const openCreate = () => { - setAddDeviceOpen(true); - setFormError(""); - }; - - const openEdit = (row) => { - setEditTarget(row); - setForm({ - hostname: row.hostname || "", - address: row.connection_endpoint || "", - description: row.description || "", - operating_system: row.summary?.operating_system || "" - }); - setDialogOpen(true); - setFormError(""); - }; - - const handleDialogClose = () => { - if (submitting) return; - setDialogOpen(false); - setForm(defaultForm); - setEditTarget(null); - setFormError(""); - }; - - const handleSubmit = async () => { - if (submitting) return; - const payload = { - hostname: form.hostname.trim(), - address: form.address.trim(), - description: form.description.trim(), - operating_system: form.operating_system.trim() - }; - if (!payload.hostname) { - setFormError("Hostname is required."); - return; - } - if (!payload.address) { - setFormError("Address is required."); - return; - } - setSubmitting(true); - setFormError(""); - try { - const endpoint = isEdit - ? `${apiBase}/${encodeURIComponent(editTarget.hostname)}` - : apiBase; - const resp = await fetch(endpoint, { - method: isEdit ? "PUT" : "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - }); - const data = await resp.json().catch(() => ({})); - if (!resp.ok) { - throw new Error(data?.error || `HTTP ${resp.status}`); - } - setDialogOpen(false); - setForm(defaultForm); - setEditTarget(null); - setFormError(""); - setRows((prev) => { - const next = [...prev]; - if (data?.device) { - const idx = next.findIndex((row) => row.hostname === data.device.hostname); - if (idx >= 0) next[idx] = data.device; - else next.push(data.device); - return next; - } - return prev; - }); - // Ensure latest ordering by triggering refresh - loadDevices(); - } catch (err) { - setFormError(String(err.message || err)); - } finally { - setSubmitting(false); - } - }; - - const handleDelete = async () => { - if (!deleteTarget) return; - setDeleteBusy(true); - try { - const resp = await fetch(`${apiBase}/${encodeURIComponent(deleteTarget.hostname)}`, { - method: "DELETE" - }); - const data = await resp.json().catch(() => ({})); - if (!resp.ok) throw new Error(data?.error || `HTTP ${resp.status}`); - setRows((prev) => prev.filter((row) => row.hostname !== deleteTarget.hostname)); - setDeleteTarget(null); - } catch (err) { - setError(String(err.message || err)); - } finally { - setDeleteBusy(false); - } - }; - - return ( - - - - - {pageTitle} - - - {descriptionText} - - - - - - - - - {error && ( - - {error} - - )} - {loading && ( - - - {loadingLabel} - - )} - - - - - - - Hostname - - - - - {addressLabel} - - - - - Description - - - - - Added - - - Actions - - - - {sortedRows.map((row) => { - const createdTs = Number(row.created_at || 0) * 1000; - const createdDisplay = createdTs - ? new Date(createdTs).toLocaleString() - : (row.summary?.created || ""); - return ( - - {row.hostname} - {row.connection_endpoint || ""} - {row.description || ""} - {createdDisplay} - - openEdit(row)}> - - - setDeleteTarget(row)}> - - - - - ); - })} - {!sortedRows.length && !loading && ( - - - {emptyLabel} - - - )} - -
- - - {isEdit ? editDialogTitle : newDialogTitle} - - setForm((prev) => ({ ...prev, hostname: e.target.value }))} - fullWidth - size="small" - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#1f1f1f", - color: "#fff", - "& fieldset": { borderColor: "#555" }, - "&:hover fieldset": { borderColor: "#888" } - }, - "& .MuiInputLabel-root": { color: "#aaa" } - }} - helperText="Hostname used within Borealis (unique)." - /> - setForm((prev) => ({ ...prev, address: e.target.value }))} - fullWidth - size="small" - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#1f1f1f", - color: "#fff", - "& fieldset": { borderColor: "#555" }, - "&:hover fieldset": { borderColor: "#888" } - }, - "& .MuiInputLabel-root": { color: "#aaa" } - }} - helperText={`IP or FQDN Borealis can reach over ${typeLabel}.`} - /> - setForm((prev) => ({ ...prev, description: e.target.value }))} - fullWidth - size="small" - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#1f1f1f", - color: "#fff", - "& fieldset": { borderColor: "#555" }, - "&:hover fieldset": { borderColor: "#888" } - }, - "& .MuiInputLabel-root": { color: "#aaa" } - }} - /> - setForm((prev) => ({ ...prev, operating_system: e.target.value }))} - fullWidth - size="small" - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#1f1f1f", - color: "#fff", - "& fieldset": { borderColor: "#555" }, - "&:hover fieldset": { borderColor: "#888" } - }, - "& .MuiInputLabel-root": { color: "#aaa" } - }} - /> - {error && ( - - {error} - - )} - - - - - - - - setDeleteTarget(null)} - onConfirm={handleDelete} - confirmDisabled={deleteBusy} - /> - setAddDeviceOpen(false)} - onCreated={() => { - setAddDeviceOpen(false); - loadDevices(); - }} - /> -
- ); -} diff --git a/Data/Server/WebUI/src/Devices/WinRM_Devices.jsx b/Data/Server/WebUI/src/Devices/WinRM_Devices.jsx deleted file mode 100644 index eb4a161a..00000000 --- a/Data/Server/WebUI/src/Devices/WinRM_Devices.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from "react"; -import SSHDevices from "./SSH_Devices.jsx"; - -export default function WinRMDevices(props) { - return ; -} diff --git a/Data/Server/WebUI/src/Dialogs.jsx b/Data/Server/WebUI/src/Dialogs.jsx deleted file mode 100644 index 68e3cd20..00000000 --- a/Data/Server/WebUI/src/Dialogs.jsx +++ /dev/null @@ -1,514 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Dialogs.jsx - -import React from "react"; -import { - Dialog, - DialogTitle, - DialogContent, - DialogContentText, - DialogActions, - Button, - Menu, - MenuItem, - TextField -} from "@mui/material"; - -export function CloseAllDialog({ open, onClose, onConfirm }) { - return ( - - Close All Flow Tabs? - - - This will remove all existing flow tabs and create a fresh tab named Flow 1. - - - - - - - - ); -} - -export function NotAuthorizedDialog({ open, onClose }) { - return ( - - Not Authorized - - - You are not authorized to access this section. - - - - - - - ); -} - -export function CreditsDialog({ open, onClose }) { - return ( - - - Borealis Logo - Borealis - Automation Platform - - Designed by Nicole Rappe @{" "} - - Bunny Lab - - - - - - - - ); -} - -export function RenameTabDialog({ open, value, onChange, onCancel, onSave }) { - return ( - - Rename Tab - - onChange(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { - borderColor: "#444" - }, - "&:hover fieldset": { - borderColor: "#666" - } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} - -export function RenameWorkflowDialog({ open, value, onChange, onCancel, onSave }) { - return ( - - Rename Workflow - - onChange(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { - borderColor: "#444" - }, - "&:hover fieldset": { - borderColor: "#666" - } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} - -export function RenameFolderDialog({ - open, - value, - onChange, - onCancel, - onSave, - title = "Folder Name", - confirmText = "Save" -}) { - return ( - - {title} - - onChange(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} - -export function NewWorkflowDialog({ open, value, onChange, onCancel, onCreate }) { - return ( - - New Workflow - - onChange(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} - -export function ClearDeviceActivityDialog({ open, onCancel, onConfirm }) { - return ( - - Clear Device Activity - - - All device activity history will be cleared, are you sure? - - - - - - - - ); -} - -export function SaveWorkflowDialog({ open, value, onChange, onCancel, onSave }) { - return ( - - Save Workflow - - onChange(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} - -export function ConfirmDeleteDialog({ open, message, onCancel, onConfirm }) { - return ( - - Confirm Delete - - {message} - - - - - - - ); -} - -export function DeleteDeviceDialog({ open, onCancel, onConfirm }) { - return ( - - Remove Device - - - Are you sure you want to remove this device? If the agent is still running, it will automatically re-enroll the device. - - - - - - - - ); -} - -export function TabContextMenu({ anchor, onClose, onRename, onCloseTab }) { - return ( - - Rename - Close Workflow - - ); -} - -export function CreateCustomViewDialog({ open, value, onChange, onCancel, onSave }) { - return ( - - Create a New Custom View - - - Saving a view will save column order, visibility, and filters. - - onChange(e.target.value)} - placeholder="Add a name for this custom view" - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} - -export function RenameCustomViewDialog({ open, value, onChange, onCancel, onSave }) { - return ( - - Rename Custom View - - onChange(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} - -export function CreateSiteDialog({ open, onCancel, onCreate }) { - const [name, setName] = React.useState(""); - const [description, setDescription] = React.useState(""); - - React.useEffect(() => { - if (open) { - setName(""); - setDescription(""); - } - }, [open]); - - return ( - - Create Site - - - Create a new site and optionally add a description. - - setName(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - setDescription(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 2 - }} - /> - - - - - - - ); -} - -export function RenameSiteDialog({ open, value, onChange, onCancel, onSave }) { - return ( - - Rename Site - - onChange(e.target.value)} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#2a2a2a", - color: "#ccc", - "& fieldset": { borderColor: "#444" }, - "&:hover fieldset": { borderColor: "#666" } - }, - label: { color: "#aaa" }, - mt: 1 - }} - /> - - - - - - - ); -} diff --git a/Data/Server/WebUI/src/Flow_Editor/Context_Menu_Sidebar.jsx b/Data/Server/WebUI/src/Flow_Editor/Context_Menu_Sidebar.jsx deleted file mode 100644 index 3f5ca090..00000000 --- a/Data/Server/WebUI/src/Flow_Editor/Context_Menu_Sidebar.jsx +++ /dev/null @@ -1,415 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { Box, Typography, Tabs, Tab, TextField, MenuItem, Button, Slider, IconButton, Tooltip } from "@mui/material"; -import ContentCopyIcon from "@mui/icons-material/ContentCopy"; -import ContentPasteIcon from "@mui/icons-material/ContentPaste"; -import RestoreIcon from "@mui/icons-material/Restore"; -import { SketchPicker } from "react-color"; - -const SIDEBAR_WIDTH = 400; - -const DEFAULT_EDGE_STYLE = { - type: "bezier", - animated: true, - style: { strokeDasharray: "6 3", stroke: "#58a6ff", strokeWidth: 1 }, - label: "", - labelStyle: { fill: "#fff", fontWeight: "bold" }, - labelBgStyle: { fill: "#2c2c2c", fillOpacity: 0.85, rx: 16, ry: 16 }, - labelBgPadding: [8, 4], -}; - -let globalEdgeClipboard = null; - -function clone(obj) { - return JSON.parse(JSON.stringify(obj)); -} - -export default function Context_Menu_Sidebar({ - open, - onClose, - edge, - updateEdge, -}) { - const [activeTab, setActiveTab] = useState(0); - const [editState, setEditState] = useState(() => (edge ? clone(edge) : {})); - const [colorPicker, setColorPicker] = useState({ field: null, anchor: null }); - - useEffect(() => { - if (edge && edge.id !== editState.id) setEditState(clone(edge)); - // eslint-disable-next-line - }, [edge]); - - const handleChange = (field, value) => { - setEditState((prev) => { - const updated = { ...prev }; - if (field === "label") updated.label = value; - else if (field === "labelStyle.fill") updated.labelStyle = { ...updated.labelStyle, fill: value }; - else if (field === "labelBgStyle.fill") updated.labelBgStyle = { ...updated.labelBgStyle, fill: value }; - else if (field === "labelBgStyle.rx") updated.labelBgStyle = { ...updated.labelBgStyle, rx: value, ry: value }; - else if (field === "labelBgPadding") updated.labelBgPadding = value; - else if (field === "labelBgStyle.fillOpacity") updated.labelBgStyle = { ...updated.labelBgStyle, fillOpacity: value }; - else if (field === "type") updated.type = value; - else if (field === "animated") updated.animated = value; - else if (field === "style.stroke") updated.style = { ...updated.style, stroke: value }; - else if (field === "style.strokeDasharray") updated.style = { ...updated.style, strokeDasharray: value }; - else if (field === "style.strokeWidth") updated.style = { ...updated.style, strokeWidth: value }; - else if (field === "labelStyle.fontWeight") updated.labelStyle = { ...updated.labelStyle, fontWeight: value }; - else updated[field] = value; - - if (field === "style.strokeDasharray") { - if (value === "") { - updated.animated = false; - updated.style = { ...updated.style, strokeDasharray: "" }; - } else { - updated.animated = true; - updated.style = { ...updated.style, strokeDasharray: value }; - } - } - updateEdge({ ...updated, id: prev.id }); - return updated; - }); - }; - - // Color Picker with right alignment - const openColorPicker = (field, event) => { - setColorPicker({ field, anchor: event.currentTarget }); - }; - - const closeColorPicker = () => { - setColorPicker({ field: null, anchor: null }); - }; - - const handleColorChange = (color) => { - handleChange(colorPicker.field, color.hex); - closeColorPicker(); - }; - - // Reset, Copy, Paste logic - const handleReset = () => { - setEditState(clone({ ...DEFAULT_EDGE_STYLE, id: edge.id })); - updateEdge({ ...DEFAULT_EDGE_STYLE, id: edge.id }); - }; - const handleCopy = () => { globalEdgeClipboard = clone(editState); }; - const handlePaste = () => { - if (globalEdgeClipboard) { - setEditState(clone({ ...globalEdgeClipboard, id: edge.id })); - updateEdge({ ...globalEdgeClipboard, id: edge.id }); - } - }; - - const renderColorButton = (label, field, value) => ( - - - {colorPicker.field === field && ( - - - - )} - - ); - - // Label tab - const renderLabelTab = () => ( - - - Label - - handleChange("label", e.target.value)} - sx={{ - mb: 2, - input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" }, - "& fieldset": { borderColor: "#444" }, - }} - /> - - - Text Color - {renderColorButton("Label Text Color", "labelStyle.fill", editState.labelStyle?.fill || "#fff")} - - - - Background - {renderColorButton("Label Background Color", "labelBgStyle.fill", editState.labelBgStyle?.fill || "#2c2c2c")} - - - - Padding - { - const val = e.target.value.split(",").map(x => parseInt(x.trim())).filter(x => !isNaN(x)); - if (val.length === 2) handleChange("labelBgPadding", val); - }} - sx={{ width: 80, input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" } }} - /> - - - - Background Style - = 11 ? "rounded" : "square"} - onChange={e => { - handleChange("labelBgStyle.rx", e.target.value === "rounded" ? 11 : 0); - }} - sx={{ - width: 150, - bgcolor: "#1e1e1e", - "& .MuiSelect-select": { color: "#fff" } - }} - > - Rounded - Square - - - - - Background Opacity - handleChange("labelBgStyle.fillOpacity", v)} - sx={{ width: 100, ml: 2 }} - /> - handleChange("labelBgStyle.fillOpacity", parseFloat(e.target.value) || 0)} - sx={{ width: 60, ml: 2, input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" } }} - /> - - - ); - - const renderStyleTab = () => ( - - - Edge Style - handleChange("type", e.target.value)} - sx={{ - width: 200, - bgcolor: "#1e1e1e", - "& .MuiSelect-select": { color: "#fff" } - }} - > - Step - Curved (Bezier) - Straight - Smoothstep - - - - - Edge Animation - { - const val = e.target.value; - handleChange("style.strokeDasharray", - val === "dashes" ? "6 3" : - val === "dots" ? "2 4" : "" - ); - }} - sx={{ - width: 200, - bgcolor: "#1e1e1e", - "& .MuiSelect-select": { color: "#fff" } - }} - > - Dashes - Dots - Solid - - - - - Color - {renderColorButton("Edge Color", "style.stroke", editState.style?.stroke || "#58a6ff")} - - - Edge Width - handleChange("style.strokeWidth", v)} - sx={{ width: 100, ml: 2 }} - /> - handleChange("style.strokeWidth", parseInt(e.target.value) || 1)} - sx={{ width: 60, ml: 2, input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" } }} - /> - - - ); - - // Always render the sidebar for animation! - if (!edge) return null; - - return ( - <> - {/* Overlay */} - - - {/* Sidebar */} - e.stopPropagation()} - > - - - - Edit Edge Properties - - - setActiveTab(v)} - variant="fullWidth" - textColor="inherit" - TabIndicatorProps={{ style: { backgroundColor: "#ccc" } }} - sx={{ - borderTop: "1px solid #333", - borderBottom: "1px solid #333", - minHeight: "36px", - height: "36px" - }} - > - - - - - - {/* Main fields scrollable */} - - {activeTab === 0 && renderLabelTab()} - {activeTab === 1 && renderStyleTab()} - - - {/* Sticky footer bar */} - - - - - - - - - - - - ); -} diff --git a/Data/Server/WebUI/src/Flow_Editor/Flow_Editor.jsx b/Data/Server/WebUI/src/Flow_Editor/Flow_Editor.jsx deleted file mode 100644 index 0c66aab6..00000000 --- a/Data/Server/WebUI/src/Flow_Editor/Flow_Editor.jsx +++ /dev/null @@ -1,374 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Flow_Editor.jsx -// Import Node Configuration Sidebar and new Context Menu Sidebar -import NodeConfigurationSidebar from "./Node_Configuration_Sidebar"; -import ContextMenuSidebar from "./Context_Menu_Sidebar"; - -import React, { useState, useEffect, useCallback, useRef } from "react"; -import ReactFlow, { - Background, - addEdge, - applyNodeChanges, - applyEdgeChanges, - useReactFlow -} from "reactflow"; - -import { Menu, MenuItem, Box } from "@mui/material"; -import { - Polyline as PolylineIcon, - DeleteForever as DeleteForeverIcon, - Edit as EditIcon -} from "@mui/icons-material"; - -import "reactflow/dist/style.css"; - -export default function FlowEditor({ - flowId, - nodes, - edges, - setNodes, - setEdges, - nodeTypes, - categorizedNodes -}) { - // Node Configuration Sidebar State - const [drawerOpen, setDrawerOpen] = useState(false); - const [selectedNodeId, setSelectedNodeId] = useState(null); - - // Edge Properties Sidebar State - const [edgeSidebarOpen, setEdgeSidebarOpen] = useState(false); - const [edgeSidebarEdgeId, setEdgeSidebarEdgeId] = useState(null); - - // Context Menus - const [nodeContextMenu, setNodeContextMenu] = useState(null); // { mouseX, mouseY, nodeId } - const [edgeContextMenu, setEdgeContextMenu] = useState(null); // { mouseX, mouseY, edgeId } - - // Drag/snap helpers (untouched) - const wrapperRef = useRef(null); - const { project } = useReactFlow(); - const [guides, setGuides] = useState([]); - const [activeGuides, setActiveGuides] = useState([]); - const movingFlowSize = useRef({ width: 0, height: 0 }); - - // ----- Node/Edge Definitions ----- - const selectedNode = nodes.find((n) => n.id === selectedNodeId); - const selectedEdge = edges.find((e) => e.id === edgeSidebarEdgeId); - - // --------- Context Menu Handlers ---------- - const handleRightClick = (e, node) => { - e.preventDefault(); - setNodeContextMenu({ mouseX: e.clientX + 2, mouseY: e.clientY - 6, nodeId: node.id }); - }; - - const handleEdgeRightClick = (e, edge) => { - e.preventDefault(); - setEdgeContextMenu({ mouseX: e.clientX + 2, mouseY: e.clientY - 6, edgeId: edge.id }); - }; - - // --------- Node Context Menu Actions --------- - const handleDisconnectAllEdges = (nodeId) => { - setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId)); - setNodeContextMenu(null); - }; - - const handleRemoveNode = (nodeId) => { - setNodes((nds) => nds.filter((n) => n.id !== nodeId)); - setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId)); - setNodeContextMenu(null); - }; - - const handleEditNodeProps = (nodeId) => { - setSelectedNodeId(nodeId); - setDrawerOpen(true); - setNodeContextMenu(null); - }; - - // --------- Edge Context Menu Actions --------- - const handleUnlinkEdge = (edgeId) => { - setEdges((eds) => eds.filter((e) => e.id !== edgeId)); - setEdgeContextMenu(null); - }; - - const handleEditEdgeProps = (edgeId) => { - setEdgeSidebarEdgeId(edgeId); - setEdgeSidebarOpen(true); - setEdgeContextMenu(null); - }; - - // ----- Sidebar Closing ----- - const handleCloseNodeSidebar = () => { - setDrawerOpen(false); - setSelectedNodeId(null); - }; - - const handleCloseEdgeSidebar = () => { - setEdgeSidebarOpen(false); - setEdgeSidebarEdgeId(null); - }; - - // ----- Update Edge Callback for Sidebar ----- - const updateEdge = (updatedEdgeObj) => { - setEdges((eds) => - eds.map((e) => (e.id === updatedEdgeObj.id ? { ...e, ...updatedEdgeObj } : e)) - ); - }; - - // ----- Drag/Drop, Guides, Node Snap Logic (unchanged) ----- - const computeGuides = useCallback((dragNode) => { - if (!wrapperRef.current) return; - const parentRect = wrapperRef.current.getBoundingClientRect(); - const dragEl = wrapperRef.current.querySelector( - `.react-flow__node[data-id="${dragNode.id}"]` - ); - if (dragEl) { - const dr = dragEl.getBoundingClientRect(); - const relLeft = dr.left - parentRect.left; - const relTop = dr.top - parentRect.top; - const relRight = relLeft + dr.width; - const relBottom = relTop + dr.height; - const pTL = project({ x: relLeft, y: relTop }); - const pTR = project({ x: relRight, y: relTop }); - const pBL = project({ x: relLeft, y: relBottom }); - movingFlowSize.current = { width: pTR.x - pTL.x, height: pBL.y - pTL.y }; - } - const lines = []; - nodes.forEach((n) => { - if (n.id === dragNode.id) return; - const el = wrapperRef.current.querySelector( - `.react-flow__node[data-id="${n.id}"]` - ); - if (!el) return; - const r = el.getBoundingClientRect(); - const relLeft = r.left - parentRect.left; - const relTop = r.top - parentRect.top; - const relRight = relLeft + r.width; - const relBottom = relTop + r.height; - const pTL = project({ x: relLeft, y: relTop }); - const pTR = project({ x: relRight, y: relTop }); - const pBL = project({ x: relLeft, y: relBottom }); - lines.push({ xFlow: pTL.x, xPx: relLeft }); - lines.push({ xFlow: pTR.x, xPx: relRight }); - lines.push({ yFlow: pTL.y, yPx: relTop }); - lines.push({ yFlow: pBL.y, yPx: relBottom }); - }); - setGuides(lines); - }, [nodes, project]); - - const onNodeDrag = useCallback((_, node) => { - const threshold = 5; - let snapX = null, snapY = null; - const show = []; - const { width: fw, height: fh } = movingFlowSize.current; - guides.forEach((ln) => { - if (ln.xFlow != null) { - if (Math.abs(node.position.x - ln.xFlow) < threshold) { snapX = ln.xFlow; show.push({ xPx: ln.xPx }); } - else if (Math.abs(node.position.x + fw - ln.xFlow) < threshold) { snapX = ln.xFlow - fw; show.push({ xPx: ln.xPx }); } - } - if (ln.yFlow != null) { - if (Math.abs(node.position.y - ln.yFlow) < threshold) { snapY = ln.yFlow; show.push({ yPx: ln.yPx }); } - else if (Math.abs(node.position.y + fh - ln.yFlow) < threshold) { snapY = ln.yFlow - fh; show.push({ yPx: ln.yPx }); } - } - }); - if (snapX !== null || snapY !== null) { - setNodes((nds) => - applyNodeChanges( - [{ - id: node.id, - type: "position", - position: { - x: snapX !== null ? snapX : node.position.x, - y: snapY !== null ? snapY : node.position.y - } - }], - nds - ) - ); - setActiveGuides(show); - } else { - setActiveGuides([]); - } - }, [guides, setNodes]); - - const onDrop = useCallback((event) => { - event.preventDefault(); - const type = event.dataTransfer.getData("application/reactflow"); - if (!type) return; - const bounds = wrapperRef.current.getBoundingClientRect(); - const position = project({ - x: event.clientX - bounds.left, - y: event.clientY - bounds.top - }); - const id = "node-" + Date.now(); - const nodeMeta = Object.values(categorizedNodes).flat().find((n) => n.type === type); - // Seed config defaults: - const configDefaults = {}; - (nodeMeta?.config || []).forEach(cfg => { - if (cfg.defaultValue !== undefined) { - configDefaults[cfg.key] = cfg.defaultValue; - } - }); - const newNode = { - id, - type, - position, - data: { - label: nodeMeta?.label || type, - content: nodeMeta?.content, - ...configDefaults - }, - dragHandle: ".borealis-node-header" - }; - setNodes((nds) => [...nds, newNode]); - - }, [project, setNodes, categorizedNodes]); - - const onDragOver = useCallback((event) => { - event.preventDefault(); - event.dataTransfer.dropEffect = "move"; - }, []); - - const onConnect = useCallback((params) => { - setEdges((eds) => - addEdge({ - ...params, - type: "bezier", - animated: true, - style: { strokeDasharray: "6 3", stroke: "#58a6ff" } - }, eds) - ); - }, [setEdges]); - - const onNodesChange = useCallback((changes) => { - setNodes((nds) => applyNodeChanges(changes, nds)); - }, [setNodes]); - - const onEdgesChange = useCallback((changes) => { - setEdges((eds) => applyEdgeChanges(changes, eds)); - }, [setEdges]); - - useEffect(() => { - const nodeCountEl = document.getElementById("nodeCount"); - if (nodeCountEl) nodeCountEl.innerText = nodes.length; - }, [nodes]); - - const nodeDef = selectedNode - ? Object.values(categorizedNodes).flat().find((def) => def.type === selectedNode.type) - : null; - - // --------- MAIN RENDER ---------- - return ( -
- {/* Node Config Sidebar */} - - - {/* Edge Properties Sidebar */} - { - // Provide id if missing - if (!edge.id && edgeSidebarEdgeId) edge.id = edgeSidebarEdgeId; - updateEdge(edge); - }} - /> - - computeGuides(node)} - onNodeDrag={onNodeDrag} - onNodeDragStop={() => { setGuides([]); setActiveGuides([]); }} - > - - - - {/* Helper lines for snapping */} - {activeGuides.map((ln, i) => - ln.xPx != null ? ( -
- ) : ( -
- ) - )} - - {/* Node Context Menu */} - setNodeContextMenu(null)} - anchorReference="anchorPosition" - anchorPosition={nodeContextMenu ? { top: nodeContextMenu.mouseY, left: nodeContextMenu.mouseX } : undefined} - PaperProps={{ sx: { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" } }} - > - handleEditNodeProps(nodeContextMenu.nodeId)}> - - Edit Properties - - handleDisconnectAllEdges(nodeContextMenu.nodeId)}> - - Disconnect All Edges - - handleRemoveNode(nodeContextMenu.nodeId)}> - - Remove Node - - - - {/* Edge Context Menu */} - setEdgeContextMenu(null)} - anchorReference="anchorPosition" - anchorPosition={edgeContextMenu ? { top: edgeContextMenu.mouseY, left: edgeContextMenu.mouseX } : undefined} - PaperProps={{ sx: { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" } }} - > - handleEditEdgeProps(edgeContextMenu.edgeId)}> - - Edit Properties - - handleUnlinkEdge(edgeContextMenu.edgeId)}> - - Unlink Edge - - -
- ); -} diff --git a/Data/Server/WebUI/src/Flow_Editor/Flow_Tabs.jsx b/Data/Server/WebUI/src/Flow_Editor/Flow_Tabs.jsx deleted file mode 100644 index 5ff89cdc..00000000 --- a/Data/Server/WebUI/src/Flow_Editor/Flow_Tabs.jsx +++ /dev/null @@ -1,100 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Flow_Tabs.jsx - -import React from "react"; -import { Box, Tabs, Tab, Tooltip } from "@mui/material"; -import { Add as AddIcon } from "@mui/icons-material"; - -/** - * Renders the tab bar (including the "add tab" button). - * - * Props: - * - tabs (array of {id, tab_name, nodes, edges}) - * - activeTabId (string) - * - onTabChange(newActiveTabId: string) - * - onAddTab() - * - onTabRightClick(evt: MouseEvent, tabId: string) - */ -export default function FlowTabs({ - tabs, - activeTabId, - onTabChange, - onAddTab, - onTabRightClick -}) { - // Determine the currently active tab index - const activeIndex = (() => { - const idx = tabs.findIndex((t) => t.id === activeTabId); - return idx >= 0 ? idx : 0; - })(); - - // Handle tab clicks - const handleChange = (event, newValue) => { - if (newValue === "__addtab__") { - // The "plus" tab - onAddTab(); - } else { - // normal tab index - const newTab = tabs[newValue]; - if (newTab) { - onTabChange(newTab.id); - } - } - }; - - return ( - - - {tabs.map((tab, index) => ( - onTabRightClick(evt, tab.id)} - sx={{ - minHeight: "36px", - height: "36px", - textTransform: "none", - backgroundColor: tab.id === activeTabId ? "#2C2C2C" : "transparent", - color: "#58a6ff" - }} - /> - ))} - {/* The "plus" tab has a special value */} - - } - value="__addtab__" - sx={{ - minHeight: "36px", - height: "36px", - color: "#58a6ff", - textTransform: "none" - }} - /> - - - - ); -} diff --git a/Data/Server/WebUI/src/Flow_Editor/Node_Configuration_Sidebar.jsx b/Data/Server/WebUI/src/Flow_Editor/Node_Configuration_Sidebar.jsx deleted file mode 100644 index bf8ea641..00000000 --- a/Data/Server/WebUI/src/Flow_Editor/Node_Configuration_Sidebar.jsx +++ /dev/null @@ -1,485 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Node_Configuration_Sidebar.jsx -import { Box, Typography, Tabs, Tab, TextField, MenuItem, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Button, Tooltip } from "@mui/material"; -import React, { useState } from "react"; -import { useReactFlow } from "reactflow"; -import ReactMarkdown from "react-markdown"; // Used for Node Usage Documentation -import EditIcon from "@mui/icons-material/Edit"; -import PaletteIcon from "@mui/icons-material/Palette"; -import { SketchPicker } from "react-color"; - -// ---- NEW: Brightness utility for gradient ---- -function darkenColor(hex, percent = 0.7) { - if (!/^#[0-9A-Fa-f]{6}$/.test(hex)) return hex; - let r = parseInt(hex.slice(1, 3), 16); - let g = parseInt(hex.slice(3, 5), 16); - let b = parseInt(hex.slice(5, 7), 16); - r = Math.round(r * percent); - g = Math.round(g * percent); - b = Math.round(b * percent); - return `#${r.toString(16).padStart(2,"0")}${g.toString(16).padStart(2,"0")}${b.toString(16).padStart(2,"0")}`; -} -// -------------------------------------------- - -export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, title, nodeData, setNodes, selectedNode }) { - const [activeTab, setActiveTab] = useState(0); - const contextSetNodes = useReactFlow().setNodes; - // Use setNodes from props if provided, else fallback to context (for backward compatibility) - const effectiveSetNodes = setNodes || contextSetNodes; - const handleTabChange = (_, newValue) => setActiveTab(newValue); - - // Rename dialog state - const [renameOpen, setRenameOpen] = useState(false); - const [renameValue, setRenameValue] = useState(title || ""); - - // ---- NEW: Accent Color Picker ---- - const [colorDialogOpen, setColorDialogOpen] = useState(false); - const accentColor = selectedNode?.data?.accentColor || "#58a6ff"; - // ---------------------------------- - - const renderConfigFields = () => { - const config = nodeData?.config || []; - const nodeId = nodeData?.nodeId; - - const normalizeOptions = (opts = []) => - opts.map((opt) => { - if (typeof opt === "string") { - return { value: opt, label: opt, disabled: false }; - } - if (opt && typeof opt === "object") { - const val = - opt.value ?? - opt.id ?? - opt.handle ?? - (typeof opt.label === "string" ? opt.label : ""); - const label = - opt.label ?? - opt.name ?? - opt.title ?? - (typeof val !== "undefined" ? String(val) : ""); - return { - value: typeof val === "undefined" ? "" : String(val), - label: typeof label === "undefined" ? "" : String(label), - disabled: Boolean(opt.disabled) - }; - } - return { value: String(opt ?? ""), label: String(opt ?? ""), disabled: false }; - }); - - return config.map((field, index) => { - const value = nodeData?.[field.key] ?? ""; - const isReadOnly = Boolean(field.readOnly); - - // ---- DYNAMIC DROPDOWN SUPPORT ---- - if (field.type === "select") { - let options = field.options || []; - - if (field.optionsKey && Array.isArray(nodeData?.[field.optionsKey])) { - options = nodeData[field.optionsKey]; - } else if (field.dynamicOptions && nodeData?.windowList && Array.isArray(nodeData.windowList)) { - options = nodeData.windowList - .map((win) => ({ - value: String(win.handle), - label: `${win.title} (${win.handle})` - })) - .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" })); - } - - options = normalizeOptions(options); - - // Handle dynamic options for things like Target Window - if (field.dynamicOptions && (!nodeData?.windowList || !Array.isArray(nodeData.windowList))) { - options = []; - } - - return ( - - - {field.label || field.key} - - { - if (isReadOnly) return; - const newValue = e.target.value; - if (!nodeId) return; - effectiveSetNodes((nds) => - nds.map((n) => - n.id === nodeId - ? { ...n, data: { ...n.data, [field.key]: newValue } } - : n - ) - ); - window.BorealisValueBus[nodeId] = newValue; - }} - SelectProps={{ - MenuProps: { - PaperProps: { - sx: { - bgcolor: "#1e1e1e", - color: "#ccc", - border: "1px solid #58a6ff", - "& .MuiMenuItem-root": { - color: "#ccc", - fontSize: "0.85rem", - "&:hover": { - backgroundColor: "#2a2a2a" - }, - "&.Mui-selected": { - backgroundColor: "#2c2c2c !important", - color: "#58a6ff" - }, - "&.Mui-selected:hover": { - backgroundColor: "#2a2a2a !important" - } - } - } - } - } - }} - sx={{ - "& .MuiOutlinedInput-root": { - backgroundColor: "#1e1e1e", - color: "#ccc", - fontSize: "0.85rem", - "& fieldset": { - borderColor: "#444" - }, - "&:hover fieldset": { - borderColor: "#58a6ff" - }, - "&.Mui-focused fieldset": { - borderColor: "#58a6ff" - } - }, - "& .MuiSelect-select": { - backgroundColor: "#1e1e1e" - } - }} - > - {options.length === 0 ? ( - - {field.label === "Target Window" - ? "No windows detected" - : "No options"} - - ) : ( - options.map((opt, idx) => ( - - {opt.label} - - )) - )} - - - ); - } - // ---- END DYNAMIC DROPDOWN SUPPORT ---- - - return ( - - - {field.label || field.key} - - { - if (isReadOnly) return; - const newValue = e.target.value; - if (!nodeId) return; - effectiveSetNodes((nds) => - nds.map((n) => - n.id === nodeId - ? { ...n, data: { ...n.data, [field.key]: newValue } } - : n - ) - ); - window.BorealisValueBus[nodeId] = newValue; - }} - /> - - ); - }); - }; - - // ---- NEW: Accent Color Button ---- - const renderAccentColorButton = () => ( - - setColorDialogOpen(true)} - sx={{ - ml: 1, - border: "1px solid #58a6ff", - background: accentColor, - color: "#222", - width: 28, height: 28, p: 0 - }} - > - - - - ); - // ---------------------------------- - - return ( - <> - setDrawerOpen(false)} - sx={{ - position: "absolute", - top: 0, - left: 0, - right: 0, - bottom: 0, - backgroundColor: "rgba(0, 0, 0, 0.3)", - opacity: drawerOpen ? 1 : 0, - pointerEvents: drawerOpen ? "auto" : "none", - transition: "opacity 0.6s ease", - zIndex: 10 - }} - /> - - e.stopPropagation()} - > - - - - - {"Edit " + (title || "Node")} - - - { - setRenameValue(title || ""); - setRenameOpen(true); - }} - sx={{ ml: 1, color: "#58a6ff" }} - > - - - {/* ---- NEW: Accent Color Picker button next to pencil ---- */} - {renderAccentColorButton()} - {/* ------------------------------------------------------ */} - - - - - - - - - - - - {activeTab === 0 && renderConfigFields()} - {activeTab === 1 && ( - - ( - - ), - p: ({ node, ...props }) => ( - - ), - ul: ({ node, ...props }) => ( -
    - ), - li: ({ node, ...props }) => ( -
  • - ) - }} - /> - - )} - - - - {/* Rename Node Dialog */} - setRenameOpen(false)} - PaperProps={{ sx: { bgcolor: "#232323" } }} - > - Rename Node - - setRenameValue(e.target.value)} - sx={{ - mt: 1, - bgcolor: "#1e1e1e", - "& .MuiOutlinedInput-root": { - color: "#ccc", - backgroundColor: "#1e1e1e", - "& fieldset": { borderColor: "#444" } - }, - label: { color: "#aaa" } - }} - /> - - - - - - - - {/* ---- Accent Color Picker Dialog ---- */} - setColorDialogOpen(false)} - PaperProps={{ sx: { bgcolor: "#232323" } }} - > - Pick Node Header/Accent Color - - { - const nodeId = selectedNode?.id || nodeData?.nodeId; - if (!nodeId) return; - const accent = color.hex; - const accentDark = darkenColor(accent, 0.7); - effectiveSetNodes((nds) => - nds.map((n) => - n.id === nodeId - ? { - ...n, - data: { ...n.data, accentColor: accent }, - style: { - ...n.style, - "--borealis-accent": accent, - "--borealis-accent-dark": accentDark, - "--borealis-title": accent, - }, - } - : n - ) - ); - }} - disableAlpha - presetColors={[ - "#58a6ff", "#0475c2", "#00d18c", "#ff4f4f", "#ff8c00", - "#6b21a8", "#0e7490", "#888", "#fff", "#000" - ]} - /> - - - The node's header text and accent gradient will use your selected color.
    - The accent gradient fades to a slightly darker version. -
    - - - - {accentColor} - - -
    -
    - - - -
    - {/* ---- END ACCENT COLOR PICKER DIALOG ---- */} - - ); -} diff --git a/Data/Server/WebUI/src/Flow_Editor/Node_Sidebar.jsx b/Data/Server/WebUI/src/Flow_Editor/Node_Sidebar.jsx deleted file mode 100644 index 9b408ffb..00000000 --- a/Data/Server/WebUI/src/Flow_Editor/Node_Sidebar.jsx +++ /dev/null @@ -1,260 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Node_Sidebar.jsx - -import React, { useState } from "react"; -import { - Accordion, - AccordionSummary, - AccordionDetails, - Button, - Tooltip, - Typography, - Box -} from "@mui/material"; -import { - ExpandMore as ExpandMoreIcon, - SaveAlt as SaveAltIcon, - Save as SaveIcon, - FileOpen as FileOpenIcon, - DeleteForever as DeleteForeverIcon, - DragIndicator as DragIndicatorIcon, - Polyline as PolylineIcon, - ChevronLeft as ChevronLeftIcon, - ChevronRight as ChevronRightIcon -} from "@mui/icons-material"; -import { SaveWorkflowDialog } from "../Dialogs"; - -export default function NodeSidebar({ - categorizedNodes, - handleExportFlow, - handleImportFlow, - handleSaveFlow, - handleOpenCloseAllDialog, - fileInputRef, - onFileInputChange, - currentTabName -}) { - const [expandedCategory, setExpandedCategory] = useState(null); - const [collapsed, setCollapsed] = useState(false); - const [saveOpen, setSaveOpen] = useState(false); - const [saveName, setSaveName] = useState(""); - - const handleAccordionChange = (category) => (_, isExpanded) => { - setExpandedCategory(isExpanded ? category : null); - }; - - return ( -
    -
    - {!collapsed && ( - <> - {/* Workflows Section */} - - } - sx={{ - backgroundColor: "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 } - }} - > - - Workflows - - - - - - - - - - - - - - - - {/* Nodes Section */} - - } - sx={{ - backgroundColor: "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 } - }} - > - - Nodes - - - - {Object.entries(categorizedNodes).map(([category, items]) => ( - - } - sx={{ - bgcolor: "#1e1e1e", - px: 2, - minHeight: "32px", - "& .MuiAccordionSummary-content": { margin: 0 } - }} - > - - {category} - - - - {items.map((nodeDef) => ( - - {nodeDef.description || "Drag & Drop into Editor"} - - } - placement="right" - arrow - > - - - ))} - - - ))} - - - - {/* Hidden file input */} - - - )} -
    - - {/* Bottom toggle button */} - - setCollapsed(!collapsed)} - sx={{ - height: "36px", - borderTop: "1px solid #333", - cursor: "pointer", - display: "flex", - alignItems: "center", - justifyContent: "center", - color: "#888", - backgroundColor: "#121212", - transition: "background-color 0.2s ease", - "&:hover": { - backgroundColor: "#1e1e1e" - }, - "&:active": { - backgroundColor: "#2a2a2a" - } - }} - > - {collapsed ? : } - - - setSaveOpen(false)} - onSave={() => { - setSaveOpen(false); - handleSaveFlow(saveName); - }} - /> -
    - ); -} - -const buttonStyle = { - color: "#ccc", - backgroundColor: "#232323", - justifyContent: "flex-start", - pl: 2, - fontSize: "0.9rem", - textTransform: "none", - "&:hover": { - backgroundColor: "#2a2a2a" - } -}; - -const nodeButtonStyle = { - color: "#ccc", - backgroundColor: "#232323", - justifyContent: "space-between", - pl: 2, - pr: 1, - fontSize: "0.9rem", - textTransform: "none", - "&:hover": { - backgroundColor: "#2a2a2a" - } -}; diff --git a/Data/Server/WebUI/src/Login.jsx b/Data/Server/WebUI/src/Login.jsx deleted file mode 100644 index 375bc61b..00000000 --- a/Data/Server/WebUI/src/Login.jsx +++ /dev/null @@ -1,332 +0,0 @@ -import React, { useMemo, useState } from "react"; -import { Box, TextField, Button, Typography } from "@mui/material"; - -export default function Login({ onLogin }) { - const [username, setUsername] = useState("admin"); - const [password, setPassword] = useState(""); - const [error, setError] = useState(""); - const [isSubmitting, setIsSubmitting] = useState(false); - const [step, setStep] = useState("credentials"); // 'credentials' | 'mfa' - const [pendingToken, setPendingToken] = useState(""); - const [mfaStage, setMfaStage] = useState(null); - const [mfaCode, setMfaCode] = useState(""); - const [setupSecret, setSetupSecret] = useState(""); - const [setupQr, setSetupQr] = useState(""); - const [setupUri, setSetupUri] = useState(""); - - const formattedSecret = useMemo(() => { - if (!setupSecret) return ""; - return setupSecret.replace(/(.{4})/g, "$1 ").trim(); - }, [setupSecret]); - - const sha512 = async (text) => { - try { - if (window.crypto && window.crypto.subtle && window.isSecureContext) { - const encoder = new TextEncoder(); - const data = encoder.encode(text); - const hashBuffer = await window.crypto.subtle.digest("SHA-512", data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); - } - } catch (_) { - // fall through to return null - } - // Not a secure context or subtle crypto unavailable - return null; - }; - - const resetMfaState = () => { - setStep("credentials"); - setPendingToken(""); - setMfaStage(null); - setMfaCode(""); - setSetupSecret(""); - setSetupQr(""); - setSetupUri(""); - }; - - const handleCredentialsSubmit = async (e) => { - e.preventDefault(); - setIsSubmitting(true); - setError(""); - try { - const hash = await sha512(password); - const body = hash - ? { username, password_sha512: hash } - : { username, password }; - const resp = await fetch("/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify(body) - }); - const data = await resp.json(); - if (!resp.ok) { - throw new Error(data?.error || "Invalid username or password"); - } - if (data?.status === "mfa_required") { - setPendingToken(data.pending_token || ""); - setMfaStage(data.stage || "verify"); - setStep("mfa"); - setMfaCode(""); - setSetupSecret(data.secret || ""); - setSetupQr(data.qr_image || ""); - setSetupUri(data.otpauth_url || ""); - setError(""); - setPassword(""); - return; - } - if (data?.token) { - try { - document.cookie = `borealis_auth=${data.token}; Path=/; SameSite=Lax`; - } catch (_) {} - } - onLogin({ username: data.username, role: data.role }); - } catch (err) { - const msg = err?.message || "Unable to log in"; - setError(msg); - resetMfaState(); - } finally { - setIsSubmitting(false); - } - }; - - const handleMfaSubmit = async (e) => { - e.preventDefault(); - if (!pendingToken) { - setError("Your MFA session expired. Please log in again."); - resetMfaState(); - return; - } - if (!mfaCode || mfaCode.trim().length < 6) { - setError("Enter the 6-digit code from your authenticator app."); - return; - } - setIsSubmitting(true); - setError(""); - try { - const resp = await fetch("/api/auth/mfa/verify", { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ pending_token: pendingToken, code: mfaCode }) - }); - const data = await resp.json(); - if (!resp.ok) { - const errKey = data?.error; - if (errKey === "expired" || errKey === "invalid_session" || errKey === "mfa_pending") { - setError("Your MFA session expired. Please log in again."); - resetMfaState(); - return; - } - const msgMap = { - invalid_code: "Incorrect code. Please try again.", - mfa_not_configured: "MFA is not configured for this account." - }; - setError(msgMap[errKey] || data?.error || "Failed to verify code."); - return; - } - if (data?.token) { - try { - document.cookie = `borealis_auth=${data.token}; Path=/; SameSite=Lax`; - } catch (_) {} - } - setError(""); - onLogin({ username: data.username, role: data.role }); - } catch (err) { - setError("Failed to verify code."); - } finally { - setIsSubmitting(false); - } - }; - - const handleBackToLogin = () => { - resetMfaState(); - setPassword(""); - setError(""); - }; - - const onCodeChange = (event) => { - const raw = event.target.value || ""; - const digits = raw.replace(/\D/g, "").slice(0, 6); - setMfaCode(digits); - }; - - const formTitle = step === "mfa" - ? "Multi-Factor Authentication" - : "Borealis - Automation Platform"; - - return ( - - - Borealis Logo - - {formTitle} - - - {step === "credentials" ? ( - <> - setUsername(e.target.value)} - margin="normal" - /> - setPassword(e.target.value)} - margin="normal" - /> - {error && ( - - {error} - - )} - - - ) : ( - <> - {mfaStage === "setup" ? ( - <> - - Scan the QR code with your authenticator app, then enter the 6-digit code to complete setup for {username}. - - {setupQr ? ( - MFA enrollment QR code - ) : null} - {formattedSecret ? ( - - - Manual code - - - {formattedSecret} - - - ) : null} - {setupUri ? ( - - {setupUri} - - ) : null} - - ) : ( - - Enter the 6-digit code from your authenticator app for {username}. - - )} - - - {error && ( - - {error} - - )} - - - - )} - - - ); -} diff --git a/Data/Server/WebUI/src/Navigation_Sidebar.jsx b/Data/Server/WebUI/src/Navigation_Sidebar.jsx deleted file mode 100644 index c2a7d712..00000000 --- a/Data/Server/WebUI/src/Navigation_Sidebar.jsx +++ /dev/null @@ -1,409 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Navigation_Sidebar.jsx - -import React, { useState } from "react"; -import { - Accordion, - AccordionSummary, - AccordionDetails, - Typography, - Box, - ListItemButton, - ListItemText -} from "@mui/material"; -import { - ExpandMore as ExpandMoreIcon, - Devices as DevicesIcon, - FilterAlt as FilterIcon, - Groups as GroupsIcon, - Work as JobsIcon, - Polyline as WorkflowsIcon, - Code as ScriptIcon, - PeopleOutline as CommunityIcon, - Apps as AssembliesIcon -} from "@mui/icons-material"; -import { LocationCity as SitesIcon } from "@mui/icons-material"; -import { - Dns as ServerInfoIcon, - VpnKey as CredentialIcon, - PersonOutline as UserIcon, - GitHub as GitHubIcon, - Key as KeyIcon, - AdminPanelSettings as AdminPanelSettingsIcon -} from "@mui/icons-material"; - -function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) { - const [expandedNav, setExpandedNav] = useState({ - sites: true, - devices: true, - automation: true, - filters: true, - access: true, - admin: true - }); - - const NavItem = ({ icon, label, pageKey, indent = 0 }) => { - const active = currentPage === pageKey; - return ( - onNavigate(pageKey)} - sx={{ - pl: indent ? 4 : 2, - py: 1, - color: active ? "#e6f2ff" : "#ccc", - position: "relative", - background: active - ? "linear-gradient(90deg, rgba(88,166,255,0.10) 0%, rgba(88,166,255,0.03) 60%, rgba(88,166,255,0.00) 100%)" - : "transparent", - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - boxShadow: active - ? "inset 0 0 0 1px rgba(88,166,255,0.25)" - : "none", - transition: "background 160ms ease, box-shadow 160ms ease, color 160ms ease", - "&:hover": { - background: active - ? "linear-gradient(90deg, rgba(88,166,255,0.14) 0%, rgba(88,166,255,0.06) 60%, rgba(88,166,255,0.00) 100%)" - : "#2c2c2c" - } - }} - selected={active} - > - - {icon && ( - - {icon} - - )} - - - ); - }; - - return ( - - - {/* Sites */} - {(() => { - const groupActive = currentPage === "sites"; - return ( - setExpandedNav((s) => ({ ...s, sites: e }))} - square - disableGutters - sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} - > - } - sx={{ - position: "relative", - background: groupActive - ? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" - : "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 }, - "&::before": { - content: '""', - position: "absolute", - left: 0, - top: 0, - bottom: 0, - width: groupActive ? 3 : 0, - bgcolor: "#58a6ff", - borderTopRightRadius: 2, - borderBottomRightRadius: 2, - transition: "width 160ms ease" - } - }} - > - - Sites - - - - } label="All Sites" pageKey="sites" /> - - - ); - })()} - {/* Inventory */} - {(() => { - const groupActive = ["devices", "ssh_devices", "winrm_devices", "agent_devices"].includes(currentPage); - return ( - setExpandedNav((s) => ({ ...s, devices: e }))} - square - disableGutters - sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} - > - } - sx={{ - position: "relative", - background: groupActive - ? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" - : "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 }, - "&::before": { - content: '""', - position: "absolute", - left: 0, - top: 0, - bottom: 0, - width: groupActive ? 3 : 0, - bgcolor: "#58a6ff", - borderTopRightRadius: 2, - borderBottomRightRadius: 2, - transition: "width 160ms ease" - } - }} - > - - Inventory - - - - } label="Device Approvals" pageKey="admin_device_approvals" /> - } label="Enrollment Codes" pageKey="admin_enrollment_codes" indent /> - } label="Devices" pageKey="devices" /> - } label="Agent Devices" pageKey="agent_devices" indent /> - } label="SSH Devices" pageKey="ssh_devices" indent /> - } label="WinRM Devices" pageKey="winrm_devices" indent /> - - - ); - })()} - - {/* Automation */} - {(() => { - const groupActive = ["jobs", "assemblies", "community"].includes(currentPage); - return ( - setExpandedNav((s) => ({ ...s, automation: e }))} - square - disableGutters - sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} - > - } - sx={{ - position: "relative", - background: groupActive - ? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" - : "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 }, - "&::before": { - content: '""', - position: "absolute", - left: 0, - top: 0, - bottom: 0, - width: groupActive ? 3 : 0, - bgcolor: "#58a6ff", - borderTopRightRadius: 2, - borderBottomRightRadius: 2, - transition: "width 160ms ease" - } - }} - > - - Automation - - - - } label="Assemblies" pageKey="assemblies" /> - } label="Scheduled Jobs" pageKey="jobs" /> - } label="Community Content" pageKey="community" /> - - - ); - })()} - - {/* Filters & Groups */} - {(() => { - const groupActive = currentPage === "filters" || currentPage === "groups"; - return ( - setExpandedNav((s) => ({ ...s, filters: e }))} - square - disableGutters - sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} - > - } - sx={{ - position: "relative", - background: groupActive - ? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" - : "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 }, - "&::before": { - content: '""', - position: "absolute", - left: 0, - top: 0, - bottom: 0, - width: groupActive ? 3 : 0, - bgcolor: "#58a6ff", - borderTopRightRadius: 2, - borderBottomRightRadius: 2, - transition: "width 160ms ease" - } - }} - > - - Filters & Groups - - - - } label="Filters" pageKey="filters" /> - } label="Groups" pageKey="groups" /> - - - ); - })()} - - {/* Access Management */} - {(() => { - if (!isAdmin) return null; - const groupActive = - currentPage === "access_credentials" || - currentPage === "access_users" || - currentPage === "access_github_token"; - return ( - setExpandedNav((s) => ({ ...s, access: e }))} - square - disableGutters - sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} - > - } - sx={{ - position: "relative", - background: groupActive - ? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" - : "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 }, - "&::before": { - content: '""', - position: "absolute", - left: 0, - top: 0, - bottom: 0, - width: groupActive ? 3 : 0, - bgcolor: "#58a6ff", - borderTopRightRadius: 2, - borderBottomRightRadius: 2, - transition: "width 160ms ease" - } - }} - > - - Access Management - - - - } label="Credentials" pageKey="access_credentials" /> - } label="GitHub API Token" pageKey="access_github_token" /> - } label="Users" pageKey="access_users" /> - - - ); - })()} - - {/* Admin */} - {(() => { - if (!isAdmin) return null; - const groupActive = - currentPage === "server_info" || - currentPage === "admin_enrollment_codes" || - currentPage === "admin_device_approvals"; - return ( - setExpandedNav((s) => ({ ...s, admin: e }))} - square - disableGutters - sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} - > - } - sx={{ - position: "relative", - background: groupActive - ? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" - : "#2c2c2c", - minHeight: "36px", - "& .MuiAccordionSummary-content": { margin: 0 }, - "&::before": { - content: '""', - position: "absolute", - left: 0, - top: 0, - bottom: 0, - width: groupActive ? 3 : 0, - bgcolor: "#58a6ff", - borderTopRightRadius: 2, - borderBottomRightRadius: 2, - transition: "width 160ms ease" - } - }} - > - - Admin Settings - - - - } label="Server Info" pageKey="server_info" /> - - - ); - })()} - - - ); -} - -export default React.memo(NavigationSidebar); diff --git a/Data/Server/WebUI/src/Scheduling/Create_Job.jsx b/Data/Server/WebUI/src/Scheduling/Create_Job.jsx deleted file mode 100644 index 89b09962..00000000 --- a/Data/Server/WebUI/src/Scheduling/Create_Job.jsx +++ /dev/null @@ -1,2141 +0,0 @@ -import React, { useEffect, useMemo, useState, useCallback, useRef } from "react"; -import { - Paper, - Box, - Typography, - Tabs, - Tab, - TextField, - Button, - IconButton, - Checkbox, - FormControl, - FormControlLabel, - Select, - InputLabel, - Menu, - MenuItem, - Divider, - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - TableSortLabel, - GlobalStyles, - CircularProgress -} from "@mui/material"; -import { - Add as AddIcon, - Delete as DeleteIcon, - FilterList as FilterListIcon, - PendingActions as PendingActionsIcon, - Sync as SyncIcon, - Timer as TimerIcon, - Check as CheckIcon, - Error as ErrorIcon, - Refresh as RefreshIcon -} from "@mui/icons-material"; -import { SimpleTreeView, TreeItem } from "@mui/x-tree-view"; -import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; -import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker"; -import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; -import dayjs from "dayjs"; -import Prism from "prismjs"; -import "prismjs/components/prism-yaml"; -import "prismjs/components/prism-bash"; -import "prismjs/components/prism-powershell"; -import "prismjs/components/prism-batch"; -import "prismjs/themes/prism-okaidia.css"; -import Editor from "react-simple-code-editor"; -import ReactFlow, { Handle, Position } from "reactflow"; -import "reactflow/dist/style.css"; - -const hiddenHandleStyle = { - width: 12, - height: 12, - border: "none", - background: "transparent", - opacity: 0, - pointerEvents: "none" -}; - -const STATUS_META = { - pending: { label: "Pending", color: "#aab2bf", Icon: PendingActionsIcon }, - running: { label: "Running", color: "#58a6ff", Icon: SyncIcon }, - expired: { label: "Expired", color: "#aab2bf", Icon: TimerIcon }, - success: { label: "Success", color: "#00d18c", Icon: CheckIcon }, - failed: { label: "Failed", color: "#ff4f4f", Icon: ErrorIcon } -}; - -const DEVICE_COLUMNS = [ - { key: "hostname", label: "Hostname" }, - { key: "online", label: "Status" }, - { key: "site", label: "Site" }, - { key: "ran_on", label: "Ran On" }, - { key: "job_status", label: "Job Status" }, - { key: "output", label: "StdOut / StdErr" } -]; - -function StatusNode({ data }) { - const { label, color, count, onClick, isActive, Icon } = data || {}; - const displayCount = Number.isFinite(count) ? count : Number(count) || 0; - const borderColor = color || "#333"; - const activeGlow = color ? `${color}55` : "rgba(88,166,255,0.35)"; - const handleClick = useCallback((event) => { - event?.preventDefault(); - event?.stopPropagation(); - onClick && onClick(); - }, [onClick]); - return ( - - - - - - - {Icon ? : null} - - {`${displayCount} ${label || ""}`} - - - - ); -} - -function SectionHeader({ title, action }) { - return ( - - {title} - {action || null} - - ); -} - -// Recursive renderer for both Scripts and Workflows trees -function renderTreeNodes(nodes = [], map = {}) { - return nodes.map((n) => ( - - {n.children && n.children.length ? renderTreeNodes(n.children, map) : null} - - )); -} - -// --- Scripts tree helpers (reuse approach from Quick_Job) --- -function buildScriptTree(scripts, folders) { - const map = {}; - const rootNode = { id: "root_s", label: "Scripts", path: "", isFolder: true, children: [] }; - map[rootNode.id] = rootNode; - (folders || []).forEach((f) => { - const parts = (f || "").split("/"); - let children = rootNode.children; let parentPath = ""; - parts.forEach((part) => { - const path = parentPath ? `${parentPath}/${part}` : part; - let node = children.find((n) => n.id === path); - if (!node) { node = { id: path, label: part, path, isFolder: true, children: [] }; children.push(node); map[path] = node; } - children = node.children; parentPath = path; - }); - }); - (scripts || []).forEach((s) => { - const parts = (s.rel_path || "").split("/"); - let children = rootNode.children; let parentPath = ""; - parts.forEach((part, idx) => { - const path = parentPath ? `${parentPath}/${part}` : part; - const isFile = idx === parts.length - 1; - let node = children.find((n) => n.id === path); - if (!node) { - node = { id: path, label: isFile ? (s.name || s.file_name || part) : part, path, isFolder: !isFile, fileName: s.file_name, script: isFile ? s : null, children: [] }; - children.push(node); map[path] = node; - } - if (!isFile) { children = node.children; parentPath = path; } - }); - }); - return { root: [rootNode], map }; -} - -// --- Ansible tree helpers (reuse scripts tree builder) --- -function buildAnsibleTree(playbooks, folders) { - return buildScriptTree(playbooks, folders); -} - -// --- Workflows tree helpers (reuse approach from Workflow_List) --- -function buildWorkflowTree(workflows, folders) { - const map = {}; - const rootNode = { id: "root_w", label: "Workflows", path: "", isFolder: true, children: [] }; - map[rootNode.id] = rootNode; - (folders || []).forEach((f) => { - const parts = (f || "").split("/"); - let children = rootNode.children; let parentPath = ""; - parts.forEach((part) => { - const path = parentPath ? `${parentPath}/${part}` : part; - let node = children.find((n) => n.id === path); - if (!node) { node = { id: path, label: part, path, isFolder: true, children: [] }; children.push(node); map[path] = node; } - children = node.children; parentPath = path; - }); - }); - (workflows || []).forEach((w) => { - const parts = (w.rel_path || "").split("/"); - let children = rootNode.children; let parentPath = ""; - parts.forEach((part, idx) => { - const path = parentPath ? `${parentPath}/${part}` : part; - const isFile = idx === parts.length - 1; - let node = children.find((n) => n.id === path); - if (!node) { - node = { id: path, label: isFile ? (w.tab_name?.trim() || w.file_name) : part, path, isFolder: !isFile, fileName: w.file_name, workflow: isFile ? w : null, children: [] }; - children.push(node); map[path] = node; - } - if (!isFile) { children = node.children; parentPath = path; } - }); - }); - return { root: [rootNode], map }; -} - -function normalizeVariableDefinitions(vars = []) { - return (Array.isArray(vars) ? vars : []) - .map((raw) => { - if (!raw || typeof raw !== "object") return null; - const name = typeof raw.name === "string" ? raw.name.trim() : typeof raw.key === "string" ? raw.key.trim() : ""; - if (!name) return null; - const label = typeof raw.label === "string" && raw.label.trim() ? raw.label.trim() : name; - const type = typeof raw.type === "string" ? raw.type.toLowerCase() : "string"; - const required = Boolean(raw.required); - const description = typeof raw.description === "string" ? raw.description : ""; - let defaultValue = ""; - if (Object.prototype.hasOwnProperty.call(raw, "default")) defaultValue = raw.default; - else if (Object.prototype.hasOwnProperty.call(raw, "defaultValue")) defaultValue = raw.defaultValue; - else if (Object.prototype.hasOwnProperty.call(raw, "default_value")) defaultValue = raw.default_value; - return { name, label, type, required, description, default: defaultValue }; - }) - .filter(Boolean); -} - -function coerceVariableValue(type, value) { - if (type === "boolean") { - if (typeof value === "boolean") return value; - if (typeof value === "number") return value !== 0; - if (value == null) return false; - const str = String(value).trim().toLowerCase(); - if (!str) return false; - return ["true", "1", "yes", "on"].includes(str); - } - if (type === "number") { - if (value == null || value === "") return ""; - if (typeof value === "number" && Number.isFinite(value)) return String(value); - const parsed = Number(value); - return Number.isFinite(parsed) ? String(parsed) : ""; - } - return value == null ? "" : String(value); -} - -function mergeComponentVariables(docVars = [], storedVars = [], storedValueMap = {}) { - const definitions = normalizeVariableDefinitions(docVars); - const overrides = {}; - const storedMeta = {}; - (Array.isArray(storedVars) ? storedVars : []).forEach((raw) => { - if (!raw || typeof raw !== "object") return; - const name = typeof raw.name === "string" ? raw.name.trim() : ""; - if (!name) return; - if (Object.prototype.hasOwnProperty.call(raw, "value")) overrides[name] = raw.value; - else if (Object.prototype.hasOwnProperty.call(raw, "default")) overrides[name] = raw.default; - storedMeta[name] = { - label: typeof raw.label === "string" && raw.label.trim() ? raw.label.trim() : name, - type: typeof raw.type === "string" ? raw.type.toLowerCase() : undefined, - required: Boolean(raw.required), - description: typeof raw.description === "string" ? raw.description : "", - default: Object.prototype.hasOwnProperty.call(raw, "default") ? raw.default : "" - }; - }); - if (storedValueMap && typeof storedValueMap === "object") { - Object.entries(storedValueMap).forEach(([key, val]) => { - const name = typeof key === "string" ? key.trim() : ""; - if (name) overrides[name] = val; - }); - } - - const used = new Set(); - const merged = definitions.map((def) => { - const override = Object.prototype.hasOwnProperty.call(overrides, def.name) ? overrides[def.name] : undefined; - used.add(def.name); - return { - ...def, - value: override !== undefined ? coerceVariableValue(def.type, override) : coerceVariableValue(def.type, def.default) - }; - }); - - (Array.isArray(storedVars) ? storedVars : []).forEach((raw) => { - if (!raw || typeof raw !== "object") return; - const name = typeof raw.name === "string" ? raw.name.trim() : ""; - if (!name || used.has(name)) return; - const meta = storedMeta[name] || {}; - const type = meta.type || (typeof overrides[name] === "boolean" ? "boolean" : typeof overrides[name] === "number" ? "number" : "string"); - const defaultValue = Object.prototype.hasOwnProperty.call(meta, "default") ? meta.default : ""; - const override = Object.prototype.hasOwnProperty.call(overrides, name) - ? overrides[name] - : Object.prototype.hasOwnProperty.call(raw, "value") - ? raw.value - : defaultValue; - merged.push({ - name, - label: meta.label || name, - type, - required: Boolean(meta.required), - description: meta.description || "", - default: defaultValue, - value: coerceVariableValue(type, override) - }); - used.add(name); - }); - - Object.entries(overrides).forEach(([nameRaw, val]) => { - const name = typeof nameRaw === "string" ? nameRaw.trim() : ""; - if (!name || used.has(name)) return; - const type = typeof val === "boolean" ? "boolean" : typeof val === "number" ? "number" : "string"; - merged.push({ - name, - label: name, - type, - required: false, - description: "", - default: "", - value: coerceVariableValue(type, val) - }); - used.add(name); - }); - - return merged; -} - -function ComponentCard({ comp, onRemove, onVariableChange, errors = {} }) { - const variables = Array.isArray(comp.variables) - ? comp.variables.filter((v) => v && typeof v.name === "string" && v.name) - : []; - const description = comp.description || comp.path || ""; - return ( - - - - - {comp.type === "script" ? comp.name : comp.name} - - - {description} - - - - - Variables - {variables.length ? ( - - {variables.map((variable) => ( - - {variable.type === "boolean" ? ( - <> - onVariableChange(comp.localId, variable.name, e.target.checked)} - /> - )} - label={ - - {variable.label} - {variable.required ? " *" : ""} - - } - /> - {variable.description ? ( - - {variable.description} - - ) : null} - - ) : ( - onVariableChange(comp.localId, variable.name, e.target.value)} - InputLabelProps={{ shrink: true }} - sx={{ - "& .MuiOutlinedInput-root": { bgcolor: "#1b1b1b", color: "#e6edf3" }, - "& .MuiInputBase-input": { color: "#e6edf3" } - }} - error={Boolean(errors[variable.name])} - helperText={errors[variable.name] || variable.description || ""} - /> - )} - - ))} - - ) : ( - No variables defined for this assembly. - )} - - - onRemove(comp.localId)} size="small" sx={{ color: "#ff6666" }}> - - - - - - ); -} - -export default function CreateJob({ onCancel, onCreated, initialJob = null }) { - const [tab, setTab] = useState(0); - const [jobName, setJobName] = useState(""); - const [pageTitleJobName, setPageTitleJobName] = useState(""); - // Components the job will run: {type:'script'|'workflow', path, name, description} - const [components, setComponents] = useState([]); - const [targets, setTargets] = useState([]); // array of hostnames - const [scheduleType, setScheduleType] = useState("immediately"); - const [startDateTime, setStartDateTime] = useState(() => dayjs().add(5, "minute").second(0)); - const [stopAfterEnabled, setStopAfterEnabled] = useState(false); - const [expiration, setExpiration] = useState("no_expire"); - const [execContext, setExecContext] = useState("system"); - const [credentials, setCredentials] = useState([]); - const [credentialLoading, setCredentialLoading] = useState(false); - const [credentialError, setCredentialError] = useState(""); - const [selectedCredentialId, setSelectedCredentialId] = useState(""); - const [useSvcAccount, setUseSvcAccount] = useState(true); - - const loadCredentials = useCallback(async () => { - setCredentialLoading(true); - setCredentialError(""); - try { - const resp = await fetch("/api/credentials"); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - const list = Array.isArray(data?.credentials) ? data.credentials : []; - list.sort((a, b) => String(a?.name || "").localeCompare(String(b?.name || ""))); - setCredentials(list); - } catch (err) { - setCredentials([]); - setCredentialError(String(err.message || err)); - } finally { - setCredentialLoading(false); - } - }, []); - - useEffect(() => { - loadCredentials(); - }, [loadCredentials]); - - const remoteExec = useMemo(() => execContext === "ssh" || execContext === "winrm", [execContext]); - const handleExecContextChange = useCallback((value) => { - const normalized = String(value || "system").toLowerCase(); - setExecContext(normalized); - if (normalized === "winrm") { - setUseSvcAccount(true); - setSelectedCredentialId(""); - } else { - setUseSvcAccount(false); - } - }, []); - const filteredCredentials = useMemo(() => { - if (!remoteExec) return credentials; - const target = execContext === "winrm" ? "winrm" : "ssh"; - return credentials.filter((cred) => String(cred.connection_type || "").toLowerCase() === target); - }, [credentials, remoteExec, execContext]); - - useEffect(() => { - if (!remoteExec) { - return; - } - if (execContext === "winrm" && useSvcAccount) { - setSelectedCredentialId(""); - return; - } - if (!filteredCredentials.length) { - setSelectedCredentialId(""); - return; - } - if (!selectedCredentialId || !filteredCredentials.some((cred) => String(cred.id) === String(selectedCredentialId))) { - setSelectedCredentialId(String(filteredCredentials[0].id)); - } - }, [remoteExec, filteredCredentials, selectedCredentialId, execContext, useSvcAccount]); - - // dialogs state - const [addCompOpen, setAddCompOpen] = useState(false); - const [compTab, setCompTab] = useState("scripts"); - const [scriptTree, setScriptTree] = useState([]); const [scriptMap, setScriptMap] = useState({}); - const [workflowTree, setWorkflowTree] = useState([]); const [workflowMap, setWorkflowMap] = useState({}); - const [ansibleTree, setAnsibleTree] = useState([]); const [ansibleMap, setAnsibleMap] = useState({}); - const [selectedNodeId, setSelectedNodeId] = useState(""); - - const [addTargetOpen, setAddTargetOpen] = useState(false); - const [availableDevices, setAvailableDevices] = useState([]); // [{hostname, display, online}] - const [selectedTargets, setSelectedTargets] = useState({}); // map hostname->bool - const [deviceSearch, setDeviceSearch] = useState(""); - const [componentVarErrors, setComponentVarErrors] = useState({}); - const [deviceRows, setDeviceRows] = useState([]); - const [deviceStatusFilter, setDeviceStatusFilter] = useState(null); - const [deviceOrderBy, setDeviceOrderBy] = useState("hostname"); - const [deviceOrder, setDeviceOrder] = useState("asc"); - const [deviceFilters, setDeviceFilters] = useState({}); - const [filterAnchorEl, setFilterAnchorEl] = useState(null); - const [activeFilterColumn, setActiveFilterColumn] = useState(null); - const [pendingFilterValue, setPendingFilterValue] = useState(""); - - const generateLocalId = useCallback( - () => `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, - [] - ); - - const getDefaultFilterValue = useCallback((key) => (["online", "job_status", "output"].includes(key) ? "all" : ""), []); - - const isColumnFiltered = useCallback((key) => { - if (!deviceFilters || typeof deviceFilters !== "object") return false; - const value = deviceFilters[key]; - if (value == null) return false; - if (typeof value === "string") { - const trimmed = value.trim(); - if (!trimmed || trimmed === "all") return false; - return true; - } - return true; - }, [deviceFilters]); - - const openFilterMenu = useCallback((event, columnKey) => { - setActiveFilterColumn(columnKey); - setPendingFilterValue(deviceFilters[columnKey] ?? getDefaultFilterValue(columnKey)); - setFilterAnchorEl(event.currentTarget); - }, [deviceFilters, getDefaultFilterValue]); - - const closeFilterMenu = useCallback(() => { - setFilterAnchorEl(null); - setActiveFilterColumn(null); - }, []); - - const applyFilter = useCallback(() => { - if (!activeFilterColumn) { - closeFilterMenu(); - return; - } - const value = pendingFilterValue; - setDeviceFilters((prev) => { - const next = { ...(prev || {}) }; - if (!value || value === "all" || (typeof value === "string" && !value.trim())) { - delete next[activeFilterColumn]; - } else { - next[activeFilterColumn] = value; - } - return next; - }); - closeFilterMenu(); - }, [activeFilterColumn, pendingFilterValue, closeFilterMenu]); - - const clearFilter = useCallback(() => { - if (!activeFilterColumn) { - closeFilterMenu(); - return; - } - setDeviceFilters((prev) => { - const next = { ...(prev || {}) }; - delete next[activeFilterColumn]; - return next; - }); - setPendingFilterValue(getDefaultFilterValue(activeFilterColumn)); - closeFilterMenu(); - }, [activeFilterColumn, closeFilterMenu, getDefaultFilterValue]); - - const renderFilterControl = () => { - const columnKey = activeFilterColumn; - if (!columnKey) return null; - if (columnKey === "online") { - return ( - - ); - } - if (columnKey === "job_status") { - const options = ["success", "failed", "running", "pending", "expired", "timed out"]; - return ( - - ); - } - if (columnKey === "output") { - return ( - - ); - } - const placeholders = { - hostname: "Filter hostname", - site: "Filter site", - ran_on: "Filter date/time" - }; - const value = typeof pendingFilterValue === "string" ? pendingFilterValue : ""; - return ( - setPendingFilterValue(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - e.preventDefault(); - applyFilter(); - } - }} - /> - ); - }; - - const handleDeviceSort = useCallback((key) => { - setDeviceOrderBy((prevKey) => { - if (prevKey === key) { - setDeviceOrder((prevDir) => (prevDir === "asc" ? "desc" : "asc")); - return prevKey; - } - setDeviceOrder(key === "ran_on" ? "desc" : "asc"); - return key; - }); - }, []); - - const fmtTs = useCallback((ts) => { - if (!ts) return ""; - try { - const d = new Date(Number(ts) * 1000); - return d.toLocaleString(undefined, { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "numeric", - minute: "2-digit" - }); - } catch { - return ""; - } - }, []); - - const deviceFiltered = useMemo(() => { - const matchStatusFilter = (status, filterKey) => { - if (filterKey === "pending") return status === "pending" || status === "scheduled" || status === "queued" || status === ""; - if (filterKey === "running") return status === "running"; - if (filterKey === "success") return status === "success"; - if (filterKey === "failed") return status === "failed" || status === "failure" || status === "timed out" || status === "timed_out" || status === "warning"; - if (filterKey === "expired") return status === "expired"; - return true; - }; - - return deviceRows.filter((row) => { - const normalizedStatus = String(row?.job_status || "").trim().toLowerCase(); - if (deviceStatusFilter && !matchStatusFilter(normalizedStatus, deviceStatusFilter)) { - return false; - } - if (deviceFilters && typeof deviceFilters === "object") { - for (const [key, rawValue] of Object.entries(deviceFilters)) { - if (rawValue == null) continue; - if (typeof rawValue === "string") { - const trimmed = rawValue.trim(); - if (!trimmed || trimmed === "all") continue; - } - if (key === "hostname") { - const expected = String(rawValue || "").toLowerCase(); - if (!String(row?.hostname || "").toLowerCase().includes(expected)) return false; - } else if (key === "online") { - if (rawValue === "online" && !row?.online) return false; - if (rawValue === "offline" && row?.online) return false; - } else if (key === "site") { - const expected = String(rawValue || "").toLowerCase(); - if (!String(row?.site || "").toLowerCase().includes(expected)) return false; - } else if (key === "ran_on") { - const expected = String(rawValue || "").toLowerCase(); - const formatted = fmtTs(row?.ran_on).toLowerCase(); - if (!formatted.includes(expected)) return false; - } else if (key === "job_status") { - const expected = String(rawValue || "").toLowerCase(); - if (!normalizedStatus.includes(expected)) return false; - } else if (key === "output") { - if (rawValue === "stdout" && !row?.has_stdout) return false; - if (rawValue === "stderr" && !row?.has_stderr) return false; - if (rawValue === "both" && (!row?.has_stdout || !row?.has_stderr)) return false; - if (rawValue === "none" && (row?.has_stdout || row?.has_stderr)) return false; - } - } - } - return true; - }); - }, [deviceRows, deviceStatusFilter, deviceFilters, fmtTs]); - - const deviceSorted = useMemo(() => { - const arr = [...deviceFiltered]; - const dir = deviceOrder === "asc" ? 1 : -1; - arr.sort((a, b) => { - let delta = 0; - switch (deviceOrderBy) { - case "hostname": - delta = String(a?.hostname || "").localeCompare(String(b?.hostname || "")); - break; - case "online": - delta = Number(a?.online ? 1 : 0) - Number(b?.online ? 1 : 0); - break; - case "site": - delta = String(a?.site || "").localeCompare(String(b?.site || "")); - break; - case "ran_on": - delta = Number(a?.ran_on || 0) - Number(b?.ran_on || 0); - break; - case "job_status": - delta = String(a?.job_status || "").localeCompare(String(b?.job_status || "")); - break; - case "output": { - const score = (row) => (row?.has_stdout ? 2 : 0) + (row?.has_stderr ? 1 : 0); - delta = score(a) - score(b); - break; - } - default: - delta = 0; - } - if (delta === 0) { - delta = String(a?.hostname || "").localeCompare(String(b?.hostname || "")); - } - return delta * dir; - }); - return arr; - }, [deviceFiltered, deviceOrder, deviceOrderBy]); - - const normalizeComponentPath = useCallback((type, rawPath) => { - const trimmed = (rawPath || "").replace(/\\/g, "/").replace(/^\/+/, "").trim(); - if (!trimmed) return ""; - if (type === "script") { - return trimmed.startsWith("Scripts/") ? trimmed : `Scripts/${trimmed}`; - } - return trimmed; - }, []); - - const fetchAssemblyDoc = useCallback(async (type, rawPath) => { - const normalizedPath = normalizeComponentPath(type, rawPath); - if (!normalizedPath) return { doc: null, normalizedPath: "" }; - const trimmed = normalizedPath.replace(/\\/g, "/").replace(/^\/+/, "").trim(); - if (!trimmed) return { doc: null, normalizedPath: "" }; - let requestPath = trimmed; - if (type === "script" && requestPath.toLowerCase().startsWith("scripts/")) { - requestPath = requestPath.slice("Scripts/".length); - } else if (type === "ansible" && requestPath.toLowerCase().startsWith("ansible_playbooks/")) { - requestPath = requestPath.slice("Ansible_Playbooks/".length); - } - if (!requestPath) return { doc: null, normalizedPath }; - try { - const island = type === "ansible" ? "ansible" : "scripts"; - const resp = await fetch(`/api/assembly/load?island=${island}&path=${encodeURIComponent(requestPath)}`); - if (!resp.ok) { - return { doc: null, normalizedPath }; - } - const data = await resp.json(); - return { doc: data, normalizedPath }; - } catch { - return { doc: null, normalizedPath }; - } - }, [normalizeComponentPath]); - - const hydrateExistingComponents = useCallback(async (rawComponents = []) => { - const results = []; - for (const raw of rawComponents) { - if (!raw || typeof raw !== "object") continue; - const typeRaw = raw.type || raw.component_type || "script"; - if (typeRaw === "workflow") { - results.push({ - ...raw, - type: "workflow", - variables: Array.isArray(raw.variables) ? raw.variables : [], - localId: generateLocalId() - }); - continue; - } - const type = typeRaw === "ansible" ? "ansible" : "script"; - const basePath = raw.path || raw.script_path || raw.rel_path || ""; - const { doc, normalizedPath } = await fetchAssemblyDoc(type, basePath); - const assembly = doc?.assembly || {}; - const docVars = assembly?.variables || doc?.variables || []; - const mergedVariables = mergeComponentVariables(docVars, raw.variables, raw.variable_values); - results.push({ - ...raw, - type, - path: normalizedPath || basePath, - name: raw.name || assembly?.name || raw.file_name || raw.tab_name || normalizedPath || basePath, - description: raw.description || assembly?.description || normalizedPath || basePath, - variables: mergedVariables, - localId: generateLocalId() - }); - } - return results; - }, [fetchAssemblyDoc, generateLocalId]); - - const sanitizeComponentsForSave = useCallback((items) => { - return (Array.isArray(items) ? items : []).map((comp) => { - if (!comp || typeof comp !== "object") return comp; - const { localId, ...rest } = comp; - const sanitized = { ...rest }; - if (Array.isArray(comp.variables)) { - const valuesMap = {}; - sanitized.variables = comp.variables - .filter((v) => v && typeof v.name === "string" && v.name) - .map((v) => { - const entry = { - name: v.name, - label: v.label || v.name, - type: v.type || "string", - required: Boolean(v.required), - description: v.description || "" - }; - if (Object.prototype.hasOwnProperty.call(v, "default")) entry.default = v.default; - if (Object.prototype.hasOwnProperty.call(v, "value")) { - entry.value = v.value; - valuesMap[v.name] = v.value; - } - return entry; - }); - if (!sanitized.variables.length) sanitized.variables = []; - if (Object.keys(valuesMap).length) sanitized.variable_values = valuesMap; - else delete sanitized.variable_values; - } - return sanitized; - }); - }, []); - - const updateComponentVariable = useCallback((localId, name, value) => { - if (!localId || !name) return; - setComponents((prev) => prev.map((comp) => { - if (!comp || comp.localId !== localId) return comp; - const vars = Array.isArray(comp.variables) ? comp.variables : []; - const nextVars = vars.map((variable) => { - if (!variable || variable.name !== name) return variable; - return { ...variable, value: coerceVariableValue(variable.type || "string", value) }; - }); - return { ...comp, variables: nextVars }; - })); - setComponentVarErrors((prev) => { - if (!prev[localId] || !prev[localId][name]) return prev; - const next = { ...prev }; - const compErrors = { ...next[localId] }; - delete compErrors[name]; - if (Object.keys(compErrors).length) next[localId] = compErrors; - else delete next[localId]; - return next; - }); - }, []); - - const removeComponent = useCallback((localId) => { - setComponents((prev) => prev.filter((comp) => comp.localId !== localId)); - setComponentVarErrors((prev) => { - if (!prev[localId]) return prev; - const next = { ...prev }; - delete next[localId]; - return next; - }); - }, []); - - const isValid = useMemo(() => { - const base = jobName.trim().length > 0 && components.length > 0 && targets.length > 0; - if (!base) return false; - const needsCredential = remoteExec && !(execContext === "winrm" && useSvcAccount); - if (needsCredential && !selectedCredentialId) return false; - if (scheduleType !== "immediately") { - return !!startDateTime; - } - return true; - }, [jobName, components.length, targets.length, scheduleType, startDateTime, remoteExec, selectedCredentialId, execContext, useSvcAccount]); - - const [confirmOpen, setConfirmOpen] = useState(false); - const editing = !!(initialJob && initialJob.id); - - // --- Job History (only when editing) --- - const [historyRows, setHistoryRows] = useState([]); - const [historyOrderBy, setHistoryOrderBy] = useState("started_ts"); - const [historyOrder, setHistoryOrder] = useState("desc"); - const activityCacheRef = useRef(new Map()); - const [outputOpen, setOutputOpen] = useState(false); - const [outputTitle, setOutputTitle] = useState(""); - const [outputSections, setOutputSections] = useState([]); - const [outputLoading, setOutputLoading] = useState(false); - const [outputError, setOutputError] = useState(""); - - const loadHistory = useCallback(async () => { - if (!editing) return; - try { - const [runsResp, jobResp, devResp] = await Promise.all([ - fetch(`/api/scheduled_jobs/${initialJob.id}/runs?days=30`), - fetch(`/api/scheduled_jobs/${initialJob.id}`), - fetch(`/api/scheduled_jobs/${initialJob.id}/devices`) - ]); - const runs = await runsResp.json(); - const job = await jobResp.json(); - const dev = await devResp.json(); - if (!runsResp.ok) throw new Error(runs.error || `HTTP ${runsResp.status}`); - if (!jobResp.ok) throw new Error(job.error || `HTTP ${jobResp.status}`); - if (!devResp.ok) throw new Error(dev.error || `HTTP ${devResp.status}`); - setHistoryRows(Array.isArray(runs.runs) ? runs.runs : []); - setJobSummary(job.job || {}); - const devices = Array.isArray(dev.devices) ? dev.devices.map((device) => ({ - ...device, - activities: Array.isArray(device.activities) ? device.activities : [], - })) : []; - setDeviceRows(devices); - } catch { - setHistoryRows([]); - setJobSummary({}); - setDeviceRows([]); - } - }, [editing, initialJob?.id]); - - useEffect(() => { - if (!editing) return; - let t; - (async () => { try { await loadHistory(); } catch {} })(); - t = setInterval(loadHistory, 10000); - return () => { if (t) clearInterval(t); }; - }, [editing, loadHistory]); - - const resultChip = (status) => { - const map = { - Success: { bg: '#00d18c', fg: '#000' }, - Running: { bg: '#58a6ff', fg: '#000' }, - Scheduled: { bg: '#999999', fg: '#fff' }, - Expired: { bg: '#777777', fg: '#fff' }, - Failed: { bg: '#ff4f4f', fg: '#fff' }, - Warning: { bg: '#ff8c00', fg: '#000' } - }; - const c = map[status] || { bg: '#aaa', fg: '#000' }; - return ( - - {status || ''} - - ); - }; - - const aggregatedHistory = useMemo(() => { - if (!Array.isArray(historyRows) || historyRows.length === 0) return []; - const map = new Map(); - historyRows.forEach((row) => { - const key = row?.scheduled_ts || row?.started_ts || row?.finished_ts || row?.id; - if (!key) return; - const strKey = String(key); - const existing = map.get(strKey) || { - key: strKey, - scheduled_ts: row?.scheduled_ts || null, - started_ts: null, - finished_ts: null, - statuses: new Set() - }; - if (!existing.scheduled_ts && row?.scheduled_ts) existing.scheduled_ts = row.scheduled_ts; - if (row?.started_ts) { - existing.started_ts = existing.started_ts == null ? row.started_ts : Math.min(existing.started_ts, row.started_ts); - } - if (row?.finished_ts) { - existing.finished_ts = existing.finished_ts == null ? row.finished_ts : Math.max(existing.finished_ts, row.finished_ts); - } - if (row?.status) existing.statuses.add(String(row.status)); - map.set(strKey, existing); - }); - const summaries = []; - map.forEach((entry) => { - const statuses = Array.from(entry.statuses).map((s) => String(s || "").trim().toLowerCase()).filter(Boolean); - if (!statuses.length) return; - const hasInFlight = statuses.some((s) => s === "running" || s === "pending" || s === "scheduled"); - if (hasInFlight) return; - const hasFailure = statuses.some((s) => ["failed", "failure", "expired", "timed out", "timed_out", "warning"].includes(s)); - const allSuccess = statuses.every((s) => s === "success"); - const statusLabel = hasFailure ? "Failed" : (allSuccess ? "Success" : "Failed"); - summaries.push({ - key: entry.key, - scheduled_ts: entry.scheduled_ts, - started_ts: entry.started_ts, - finished_ts: entry.finished_ts, - status: statusLabel - }); - }); - return summaries; - }, [historyRows]); - - const sortedHistory = useMemo(() => { - const dir = historyOrder === 'asc' ? 1 : -1; - const key = historyOrderBy; - return [...aggregatedHistory].sort((a, b) => { - const getVal = (row) => { - if (key === 'scheduled_ts' || key === 'started_ts' || key === 'finished_ts') { - return Number(row?.[key] || 0); - } - return String(row?.[key] || ''); - }; - const A = getVal(a); - const B = getVal(b); - if (typeof A === 'number' && typeof B === 'number') { - return (A - B) * dir; - } - return String(A).localeCompare(String(B)) * dir; - }); - }, [aggregatedHistory, historyOrderBy, historyOrder]); - - const handleHistorySort = (col) => { - if (historyOrderBy === col) setHistoryOrder(historyOrder === 'asc' ? 'desc' : 'asc'); - else { setHistoryOrderBy(col); setHistoryOrder('asc'); } - }; - - const renderHistory = () => ( - - - - - - handleHistorySort('scheduled_ts')}> - Scheduled - - - - handleHistorySort('started_ts')}> - Started - - - - handleHistorySort('finished_ts')}> - Finished - - - Status - - - - {sortedHistory.map((r) => ( - - {fmtTs(r.scheduled_ts)} - {fmtTs(r.started_ts)} - {fmtTs(r.finished_ts)} - {resultChip(r.status)} - - ))} - {sortedHistory.length === 0 && ( - - No runs in the last 30 days. - - )} - -
    -
    - ); - - // --- Job Progress (summary) --- - const [jobSummary, setJobSummary] = useState({}); - const counts = jobSummary?.result_counts || {}; - - const deviceStatusCounts = useMemo(() => { - const base = { pending: 0, running: 0, success: 0, failed: 0, expired: 0 }; - deviceRows.forEach((row) => { - const normalized = String(row?.job_status || "").trim().toLowerCase(); - if (!normalized || normalized === "pending" || normalized === "scheduled" || normalized === "queued") { - base.pending += 1; - } else if (normalized === "running") { - base.running += 1; - } else if (normalized === "success") { - base.success += 1; - } else if (normalized === "expired") { - base.expired += 1; - } else if (normalized === "failed" || normalized === "failure" || normalized === "timed out" || normalized === "timed_out" || normalized === "warning") { - base.failed += 1; - } else { - base.pending += 1; - } - }); - return base; - }, [deviceRows]); - - const statusCounts = useMemo(() => { - const merged = { pending: 0, running: 0, success: 0, failed: 0, expired: 0 }; - Object.keys(merged).forEach((key) => { - const summaryVal = Number((counts || {})[key] ?? 0); - const fallback = deviceStatusCounts[key] ?? 0; - merged[key] = summaryVal > 0 ? summaryVal : fallback; - }); - return merged; - }, [counts, deviceStatusCounts]); - - const statusNodeTypes = useMemo(() => ({ statusNode: StatusNode }), []); - - const handleStatusNodeClick = useCallback((key) => { - setDeviceStatusFilter((prev) => (prev === key ? null : key)); - }, []); - - const statusNodes = useMemo(() => [ - { - id: "pending", - type: "statusNode", - position: { x: -420, y: 170 }, - data: { - label: STATUS_META.pending.label, - color: STATUS_META.pending.color, - count: statusCounts.pending, - Icon: STATUS_META.pending.Icon, - onClick: () => handleStatusNodeClick("pending"), - isActive: deviceStatusFilter === "pending" - }, - draggable: false, - selectable: false - }, - { - id: "running", - type: "statusNode", - position: { x: 0, y: 0 }, - data: { - label: STATUS_META.running.label, - color: STATUS_META.running.color, - count: statusCounts.running, - Icon: STATUS_META.running.Icon, - onClick: () => handleStatusNodeClick("running"), - isActive: deviceStatusFilter === "running" - }, - draggable: false, - selectable: false - }, - { - id: "expired", - type: "statusNode", - position: { x: 0, y: 340 }, - data: { - label: STATUS_META.expired.label, - color: STATUS_META.expired.color, - count: statusCounts.expired, - Icon: STATUS_META.expired.Icon, - onClick: () => handleStatusNodeClick("expired"), - isActive: deviceStatusFilter === "expired" - }, - draggable: false, - selectable: false - }, - { - id: "success", - type: "statusNode", - position: { x: 420, y: 0 }, - data: { - label: STATUS_META.success.label, - color: STATUS_META.success.color, - count: statusCounts.success, - Icon: STATUS_META.success.Icon, - onClick: () => handleStatusNodeClick("success"), - isActive: deviceStatusFilter === "success" - }, - draggable: false, - selectable: false - }, - { - id: "failed", - type: "statusNode", - position: { x: 420, y: 340 }, - data: { - label: STATUS_META.failed.label, - color: STATUS_META.failed.color, - count: statusCounts.failed, - Icon: STATUS_META.failed.Icon, - onClick: () => handleStatusNodeClick("failed"), - isActive: deviceStatusFilter === "failed" - }, - draggable: false, - selectable: false - } - ], [statusCounts, handleStatusNodeClick, deviceStatusFilter]); - - const statusEdges = useMemo(() => [ - { - id: "pending-running", - source: "pending", - target: "running", - sourceHandle: "right-top", - targetHandle: "left-top", - type: "smoothstep", - animated: true, - className: "status-flow-edge" - }, - { - id: "pending-expired", - source: "pending", - target: "expired", - sourceHandle: "right-bottom", - targetHandle: "left-bottom", - type: "smoothstep", - animated: true, - className: "status-flow-edge" - }, - { - id: "running-success", - source: "running", - target: "success", - sourceHandle: "right-top", - targetHandle: "left-top", - type: "smoothstep", - animated: true, - className: "status-flow-edge" - }, - { - id: "running-failed", - source: "running", - target: "failed", - sourceHandle: "right-bottom", - targetHandle: "left-bottom", - type: "smoothstep", - animated: true, - className: "status-flow-edge" - } - ], []); - - const JobStatusFlow = () => ( - - - - { - if (node?.id && STATUS_META[node.id]) handleStatusNodeClick(node.id); - }} - selectionOnDrag={false} - proOptions={{ hideAttribution: true }} - style={{ background: "transparent" }} - /> - - {deviceStatusFilter ? ( - - - Showing devices with {STATUS_META[deviceStatusFilter]?.label || deviceStatusFilter} results - - - - ) : null} - - ); - const inferLanguage = useCallback((path = "") => { - const lower = String(path || "").toLowerCase(); - if (lower.endsWith(".ps1")) return "powershell"; - if (lower.endsWith(".bat")) return "batch"; - if (lower.endsWith(".sh")) return "bash"; - if (lower.endsWith(".yml") || lower.endsWith(".yaml")) return "yaml"; - return "powershell"; - }, []); - - const highlightCode = useCallback((code, lang) => { - try { - return Prism.highlight(code ?? "", Prism.languages[lang] || Prism.languages.markup, lang); - } catch { - return String(code || ""); - } - }, []); - - const loadActivity = useCallback(async (activityId) => { - const idNum = Number(activityId || 0); - if (!idNum) return null; - if (activityCacheRef.current.has(idNum)) { - return activityCacheRef.current.get(idNum); - } - try { - const resp = await fetch(`/api/device/activity/job/${idNum}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - activityCacheRef.current.set(idNum, data); - return data; - } catch { - return null; - } - }, []); - - const handleViewDeviceOutput = useCallback(async (row, mode = "stdout") => { - if (!row) return; - const label = mode === "stderr" ? "StdErr" : "StdOut"; - const activities = Array.isArray(row.activities) ? row.activities : []; - const relevant = activities.filter((act) => (mode === "stderr" ? act.has_stderr : act.has_stdout)); - setOutputTitle(`${label} - ${row.hostname || ""}`); - setOutputSections([]); - setOutputError(""); - setOutputLoading(true); - setOutputOpen(true); - if (!relevant.length) { - setOutputError(`No ${label} available for this device.`); - setOutputLoading(false); - return; - } - const sections = []; - for (const act of relevant) { - const activityId = Number(act.activity_id || act.id || 0); - if (!activityId) continue; - const data = await loadActivity(activityId); - if (!data) continue; - const content = mode === "stderr" ? (data.stderr || "") : (data.stdout || ""); - const sectionTitle = act.component_name || data.script_name || data.script_path || `Activity ${activityId}`; - sections.push({ - key: `${activityId}-${mode}`, - title: sectionTitle, - path: data.script_path || "", - lang: inferLanguage(data.script_path || ""), - content, - }); - } - if (!sections.length) { - setOutputError(`No ${label} available for this device.`); - } - setOutputSections(sections); - setOutputLoading(false); - }, [inferLanguage, loadActivity]); - - useEffect(() => { - let canceled = false; - const hydrate = async () => { - if (initialJob && initialJob.id) { - setJobName(initialJob.name || ""); - setPageTitleJobName(typeof initialJob.name === "string" ? initialJob.name.trim() : ""); - setTargets(Array.isArray(initialJob.targets) ? initialJob.targets : []); - setScheduleType(initialJob.schedule_type || initialJob.schedule?.type || "immediately"); - setStartDateTime(initialJob.start_ts ? dayjs(Number(initialJob.start_ts) * 1000).second(0) : (initialJob.schedule?.start ? dayjs(initialJob.schedule.start).second(0) : dayjs().add(5, "minute").second(0))); - setStopAfterEnabled(Boolean(initialJob.duration_stop_enabled)); - setExpiration(initialJob.expiration || "no_expire"); - setExecContext(initialJob.execution_context || "system"); - setSelectedCredentialId(initialJob.credential_id ? String(initialJob.credential_id) : ""); - if ((initialJob.execution_context || "").toLowerCase() === "winrm") { - setUseSvcAccount(initialJob.use_service_account !== false); - } else { - setUseSvcAccount(false); - } - const comps = Array.isArray(initialJob.components) ? initialJob.components : []; - const hydrated = await hydrateExistingComponents(comps); - if (!canceled) { - setComponents(hydrated); - setComponentVarErrors({}); - } - } else if (!initialJob) { - setPageTitleJobName(""); - setComponents([]); - setComponentVarErrors({}); - setSelectedCredentialId(""); - setUseSvcAccount(true); - } - }; - hydrate(); - return () => { - canceled = true; - }; - }, [initialJob, hydrateExistingComponents]); - - const openAddComponent = async () => { - setAddCompOpen(true); - try { - // scripts - const sResp = await fetch("/api/assembly/list?island=scripts"); - if (sResp.ok) { - const sData = await sResp.json(); - const { root, map } = buildScriptTree(sData.items || [], sData.folders || []); - setScriptTree(root); setScriptMap(map); - } else { setScriptTree([]); setScriptMap({}); } - } catch { setScriptTree([]); setScriptMap({}); } - try { - // workflows - const wResp = await fetch("/api/assembly/list?island=workflows"); - if (wResp.ok) { - const wData = await wResp.json(); - const { root, map } = buildWorkflowTree(wData.items || [], wData.folders || []); - setWorkflowTree(root); setWorkflowMap(map); - } else { setWorkflowTree([]); setWorkflowMap({}); } - } catch { setWorkflowTree([]); setWorkflowMap({}); } - try { - // ansible playbooks - const aResp = await fetch("/api/assembly/list?island=ansible"); - if (aResp.ok) { - const aData = await aResp.json(); - const { root, map } = buildAnsibleTree(aData.items || [], aData.folders || []); - setAnsibleTree(root); setAnsibleMap(map); - } else { setAnsibleTree([]); setAnsibleMap({}); } - } catch { setAnsibleTree([]); setAnsibleMap({}); } - }; - - const addSelectedComponent = useCallback(async () => { - const map = compTab === "scripts" ? scriptMap : (compTab === "ansible" ? ansibleMap : workflowMap); - const node = map[selectedNodeId]; - if (!node || node.isFolder) return false; - if (compTab === "workflows" && node.workflow) { - alert("Workflows within Scheduled Jobs are not supported yet"); - return false; - } - if (compTab === "scripts" || compTab === "ansible") { - const type = compTab === "scripts" ? "script" : "ansible"; - const rawPath = node.path || node.id || ""; - const { doc, normalizedPath } = await fetchAssemblyDoc(type, rawPath); - const assembly = doc?.assembly || {}; - const docVars = assembly?.variables || doc?.variables || []; - const mergedVars = mergeComponentVariables(docVars, [], {}); - setComponents((prev) => [ - ...prev, - { - type, - path: normalizedPath || rawPath, - name: assembly?.name || node.fileName || node.label, - description: assembly?.description || normalizedPath || rawPath, - variables: mergedVars, - localId: generateLocalId() - } - ]); - setSelectedNodeId(""); - return true; - } - setSelectedNodeId(""); - return false; - }, [compTab, scriptMap, ansibleMap, workflowMap, selectedNodeId, fetchAssemblyDoc, generateLocalId]); - - const openAddTargets = async () => { - setAddTargetOpen(true); - setSelectedTargets({}); - try { - const resp = await fetch("/api/agents"); - if (resp.ok) { - const data = await resp.json(); - const list = Object.values(data || {}).map((a) => ({ - hostname: a.hostname || a.agent_hostname || a.id || "unknown", - display: a.hostname || a.agent_hostname || a.id || "unknown", - online: !!a.collector_active - })); - list.sort((a, b) => a.display.localeCompare(b.display)); - setAvailableDevices(list); - } else { - setAvailableDevices([]); - } - } catch { - setAvailableDevices([]); - } - }; - - const handleCreate = async () => { - if (remoteExec && !(execContext === "winrm" && useSvcAccount) && !selectedCredentialId) { - alert("Please select a credential for this execution context."); - return; - } - const requiredErrors = {}; - components.forEach((comp) => { - if (!comp || !comp.localId) return; - (Array.isArray(comp.variables) ? comp.variables : []).forEach((variable) => { - if (!variable || !variable.name || !variable.required) return; - if ((variable.type || "string") === "boolean") return; - const value = variable.value; - if (value == null || value === "") { - if (!requiredErrors[comp.localId]) requiredErrors[comp.localId] = {}; - requiredErrors[comp.localId][variable.name] = "Required"; - } - }); - }); - if (Object.keys(requiredErrors).length) { - setComponentVarErrors(requiredErrors); - setTab(1); - alert("Please fill in all required variable values."); - return; - } - setComponentVarErrors({}); - const payloadComponents = sanitizeComponentsForSave(components); - const payload = { - name: jobName, - components: payloadComponents, - targets, - schedule: { type: scheduleType, start: scheduleType !== "immediately" ? (() => { try { const d = startDateTime?.toDate?.() || new Date(startDateTime); d.setSeconds(0,0); return d.toISOString(); } catch { return startDateTime; } })() : null }, - duration: { stopAfterEnabled, expiration }, - execution_context: execContext, - credential_id: remoteExec && !useSvcAccount && selectedCredentialId ? Number(selectedCredentialId) : null, - use_service_account: execContext === "winrm" ? Boolean(useSvcAccount) : false - }; - try { - const resp = await fetch(initialJob && initialJob.id ? `/api/scheduled_jobs/${initialJob.id}` : "/api/scheduled_jobs", { - method: initialJob && initialJob.id ? "PUT" : "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - }); - const data = await resp.json(); - if (!resp.ok) throw new Error(data.error || `HTTP ${resp.status}`); - onCreated && onCreated(data.job || payload); - onCancel && onCancel(); - } catch (err) { - alert(String(err.message || err)); - } - }; - - const tabDefs = useMemo(() => { - const base = [ - { key: "name", label: "Job Name" }, - { key: "components", label: "Assemblies" }, - { key: "targets", label: "Targets" }, - { key: "schedule", label: "Schedule" }, - { key: "context", label: "Execution Context" } - ]; - if (editing) base.push({ key: 'history', label: 'Job History' }); - return base; - }, [editing]); - - return ( - - - - Create a Scheduled Job - {pageTitleJobName && ( - - {`: "${pageTitleJobName}"`} - - )} - - - Configure advanced schedulable automation jobs for one or more devices. - - - - - setTab(v)} sx={{ minHeight: 36 }}> - {tabDefs.map((t, i) => ( - - ))} - - - - - - - - - {tab === 0 && ( - - - setJobName(e.target.value)} - onBlur={(e) => setPageTitleJobName(e.target.value.trim())} - InputLabelProps={{ shrink: true }} - error={jobName.trim().length === 0} - helperText={jobName.trim().length === 0 ? "Job name is required" : ""} - /> - - )} - - {tab === 1 && ( - - } onClick={openAddComponent} - sx={{ color: "#58a6ff", borderColor: "#58a6ff" }} variant="outlined"> - Add Assembly - - )} - /> - {components.length === 0 && ( - No assemblies added yet. - )} - {components.map((c) => ( - - ))} - {components.length === 0 && ( - At least one assembly is required. - )} - - )} - - {tab === 2 && ( - - } onClick={openAddTargets} - sx={{ color: "#58a6ff", borderColor: "#58a6ff" }} variant="outlined"> - Add Target - - )} - /> - - - - Name - Status - Actions - - - - {targets.map((h) => ( - - {h} - - - setTargets((prev) => prev.filter((x) => x !== h))} sx={{ color: "#ff6666" }}> - - - - - ))} - {targets.length === 0 && ( - - No targets selected. - - )} - -
    - {targets.length === 0 && ( - At least one target is required. - )} -
    - )} - - {tab === 3 && ( - - - - - Recurrence - - - {(scheduleType !== "immediately") && ( - - Start date and execution time - - setStartDateTime(val?.second ? val.second(0) : val)} - views={['year','month','day','hours','minutes']} - format="YYYY-MM-DD hh:mm A" - slotProps={{ textField: { size: "small" } }} - /> - - - )} - - - - - setStopAfterEnabled(e.target.checked)} />} - label={Stop running this job after} - /> - - Expiration - - - - )} - - {tab === 4 && ( - - - - {remoteExec && ( - - {execContext === "winrm" && ( - { - const checked = e.target.checked; - setUseSvcAccount(checked); - if (checked) { - setSelectedCredentialId(""); - } else if (!selectedCredentialId && filteredCredentials.length) { - setSelectedCredentialId(String(filteredCredentials[0].id)); - } - }} - /> - } - label="Use Configured svcBorealis Account" - /> - )} - - Credential - - - - {credentialLoading && } - {!credentialLoading && credentialError && ( - - {credentialError} - - )} - {execContext === "winrm" && useSvcAccount && ( - - Runs with the agent's svcBorealis account. - - )} - {!credentialLoading && !credentialError && !filteredCredentials.length && (!(execContext === "winrm" && useSvcAccount)) && ( - - No {execContext === "winrm" ? "WinRM" : "SSH"} credentials available. Create one under Access Management > Credentials. - - )} - - )} - - )} - - {/* Job History tab (only when editing) */} - {editing && tab === tabDefs.findIndex(t => t.key === 'history') && ( - - - Job History - - - Showing the last 30 days of runs. - - - - - - - Devices - Devices targeted by this scheduled job. Individual job history is listed here. - - - - {DEVICE_COLUMNS.map((col) => ( - - - handleDeviceSort(col.key)} - > - {col.label} - - openFilterMenu(event, col.key)} - sx={{ color: isColumnFiltered(col.key) ? "#58a6ff" : "#666" }} - > - - - - - ))} - - - - {deviceSorted.map((d, i) => ( - - {d.hostname} - - - {d.online ? 'Online' : 'Offline'} - - {d.site || ''} - {fmtTs(d.ran_on)} - {resultChip(d.job_status)} - - - {d.has_stdout ? ( - - ) : null} - {d.has_stderr ? ( - - ) : null} - - - - ))} - {deviceSorted.length === 0 && ( - - No targets found for this job. - - )} - -
    - - - {renderFilterControl()} - - - - - - -
    - - - Past Job History - Historical job history summaries. Detailed job history is not recorded. - - {renderHistory()} - - -
    - )} -
    - - setOutputOpen(false)} fullWidth maxWidth="md" - PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }} - > - {outputTitle} - - {outputLoading ? ( - Loading output… - ) : null} - {!outputLoading && outputError ? ( - {outputError} - ) : null} - {!outputLoading && !outputError ? ( - outputSections.map((section) => ( - - {section.title} - {section.path ? ( - {section.path} - ) : null} - - {}} - highlight={(code) => highlightCode(code, section.lang)} - padding={12} - style={{ - fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace', - fontSize: 12, - color: "#e6edf3", - minHeight: 160 - }} - textareaProps={{ readOnly: true }} - /> - - - )) - ) : null} - - - - - - - {/* Bottom actions removed per design; actions live next to tabs. */} - - {/* Add Component Dialog */} - setAddCompOpen(false)} fullWidth maxWidth="md" - PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }} - > - Select an Assembly - - - - - - - {compTab === "scripts" && ( - - { - const n = scriptMap[id]; - if (n && !n.isFolder) setSelectedNodeId(id); - }}> - {scriptTree.length ? (scriptTree.map((n) => ( - - {n.children && n.children.length ? renderTreeNodes(n.children, scriptMap) : null} - - ))) : ( - No scripts found. - )} - - - )} - {compTab === "workflows" && ( - - { - const n = workflowMap[id]; - if (n && !n.isFolder) setSelectedNodeId(id); - }}> - {workflowTree.length ? (workflowTree.map((n) => ( - - {n.children && n.children.length ? renderTreeNodes(n.children, workflowMap) : null} - - ))) : ( - No workflows found. - )} - - - )} - {compTab === "ansible" && ( - - { - const n = ansibleMap[id]; - if (n && !n.isFolder) setSelectedNodeId(id); - }}> - {ansibleTree.length ? (ansibleTree.map((n) => ( - - {n.children && n.children.length ? renderTreeNodes(n.children, ansibleMap) : null} - - ))) : ( - No playbooks found. - )} - - - )} - - - - - - - - {/* Add Targets Dialog */} - setAddTargetOpen(false)} fullWidth maxWidth="md" - PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }} - > - Select Targets - - - setDeviceSearch(e.target.value)} - sx={{ flex: 1, "& .MuiOutlinedInput-root": { bgcolor: "#1b1b1b" }, "& .MuiInputBase-input": { color: "#e6edf3" } }} - /> - - - - - - Name - Status - - - - {availableDevices - .filter((d) => d.display.toLowerCase().includes(deviceSearch.toLowerCase())) - .map((d) => ( - setSelectedTargets((prev) => ({ ...prev, [d.hostname]: !prev[d.hostname] }))}> - - setSelectedTargets((prev) => ({ ...prev, [d.hostname]: e.target.checked }))} - /> - - {d.display} - - - {d.online ? "Online" : "Offline"} - - - ))} - {availableDevices.length === 0 && ( - No devices available. - )} - -
    -
    - - - - -
    - - {/* Confirm Create Dialog */} - setConfirmOpen(false)} - PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> - {initialJob && initialJob.id ? "Are you sure you wish to save changes?" : "Are you sure you wish to create this Job?"} - - - - - -
    - ); -} diff --git a/Data/Server/WebUI/src/Scheduling/Quick_Job.jsx b/Data/Server/WebUI/src/Scheduling/Quick_Job.jsx deleted file mode 100644 index 5ffb1a4f..00000000 --- a/Data/Server/WebUI/src/Scheduling/Quick_Job.jsx +++ /dev/null @@ -1,593 +0,0 @@ -import React, { useEffect, useState, useCallback } from "react"; -import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, - Box, - Typography, - Paper, - FormControlLabel, - Checkbox, - TextField, - FormControl, - InputLabel, - Select, - MenuItem, - CircularProgress -} from "@mui/material"; -import { Folder as FolderIcon, Description as DescriptionIcon } from "@mui/icons-material"; -import { SimpleTreeView, TreeItem } from "@mui/x-tree-view"; - -function buildTree(items, folders, rootLabel = "Scripts") { - const map = {}; - const rootNode = { - id: "root", - label: rootLabel, - path: "", - isFolder: true, - children: [] - }; - map[rootNode.id] = rootNode; - - (folders || []).forEach((f) => { - const parts = (f || "").split("/"); - let children = rootNode.children; - let parentPath = ""; - parts.forEach((part) => { - const path = parentPath ? `${parentPath}/${part}` : part; - let node = children.find((n) => n.id === path); - if (!node) { - node = { id: path, label: part, path, isFolder: true, children: [] }; - children.push(node); - map[path] = node; - } - children = node.children; - parentPath = path; - }); - }); - - (items || []).forEach((s) => { - const parts = (s.rel_path || "").split("/"); - let children = rootNode.children; - let parentPath = ""; - parts.forEach((part, idx) => { - const path = parentPath ? `${parentPath}/${part}` : part; - const isFile = idx === parts.length - 1; - let node = children.find((n) => n.id === path); - if (!node) { - node = { - id: path, - label: isFile ? (s.name || s.file_name || part) : part, - path, - isFolder: !isFile, - fileName: s.file_name, - script: isFile ? s : null, - children: [] - }; - children.push(node); - map[path] = node; - } - if (!isFile) { - children = node.children; - parentPath = path; - } - }); - }); - - return { root: [rootNode], map }; -} - -export default function QuickJob({ open, onClose, hostnames = [] }) { - const [tree, setTree] = useState([]); - const [nodeMap, setNodeMap] = useState({}); - const [selectedPath, setSelectedPath] = useState(""); - const [running, setRunning] = useState(false); - const [error, setError] = useState(""); - const [runAsCurrentUser, setRunAsCurrentUser] = useState(false); - const [mode, setMode] = useState("scripts"); // 'scripts' | 'ansible' - const [credentials, setCredentials] = useState([]); - const [credentialsLoading, setCredentialsLoading] = useState(false); - const [credentialsError, setCredentialsError] = useState(""); - const [selectedCredentialId, setSelectedCredentialId] = useState(""); - const [useSvcAccount, setUseSvcAccount] = useState(true); - const [variables, setVariables] = useState([]); - const [variableValues, setVariableValues] = useState({}); - const [variableErrors, setVariableErrors] = useState({}); - const [variableStatus, setVariableStatus] = useState({ loading: false, error: "" }); - - const loadTree = useCallback(async () => { - try { - const island = mode === 'ansible' ? 'ansible' : 'scripts'; - const resp = await fetch(`/api/assembly/list?island=${island}`); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - const { root, map } = buildTree(data.items || [], data.folders || [], mode === 'ansible' ? 'Ansible Playbooks' : 'Scripts'); - setTree(root); - setNodeMap(map); - } catch (err) { - console.error("Failed to load scripts:", err); - setTree([]); - setNodeMap({}); - } - }, [mode]); - - useEffect(() => { - if (open) { - setSelectedPath(""); - setError(""); - setVariables([]); - setVariableValues({}); - setVariableErrors({}); - setVariableStatus({ loading: false, error: "" }); - setUseSvcAccount(true); - setSelectedCredentialId(""); - loadTree(); - } - }, [open, loadTree]); - - useEffect(() => { - if (!open || mode !== "ansible") return; - let canceled = false; - setCredentialsLoading(true); - setCredentialsError(""); - (async () => { - try { - const resp = await fetch("/api/credentials"); - if (!resp.ok) throw new Error(`HTTP ${resp.status}`); - const data = await resp.json(); - if (canceled) return; - const list = Array.isArray(data?.credentials) - ? data.credentials.filter((cred) => { - const conn = String(cred.connection_type || "").toLowerCase(); - return conn === "ssh" || conn === "winrm"; - }) - : []; - list.sort((a, b) => String(a?.name || "").localeCompare(String(b?.name || ""))); - setCredentials(list); - } catch (err) { - if (!canceled) { - setCredentials([]); - setCredentialsError(String(err.message || err)); - } - } finally { - if (!canceled) setCredentialsLoading(false); - } - })(); - return () => { - canceled = true; - }; - }, [open, mode]); - - useEffect(() => { - if (!open) { - setSelectedCredentialId(""); - } - }, [open]); - - useEffect(() => { - if (mode !== "ansible" || useSvcAccount) return; - if (!credentials.length) { - setSelectedCredentialId(""); - return; - } - if (!selectedCredentialId || !credentials.some((cred) => String(cred.id) === String(selectedCredentialId))) { - setSelectedCredentialId(String(credentials[0].id)); - } - }, [mode, credentials, selectedCredentialId, useSvcAccount]); - - const renderNodes = (nodes = []) => - nodes.map((n) => ( - - {n.isFolder ? ( - - ) : ( - - )} - {n.label} - - } - > - {n.children && n.children.length ? renderNodes(n.children) : null} - - )); - - const onItemSelect = (_e, itemId) => { - const node = nodeMap[itemId]; - if (node && !node.isFolder) { - setSelectedPath(node.path); - setError(""); - setVariableErrors({}); - } - }; - - const normalizeVariables = (list) => { - if (!Array.isArray(list)) return []; - return list - .map((raw) => { - if (!raw || typeof raw !== "object") return null; - const name = typeof raw.name === "string" ? raw.name.trim() : typeof raw.key === "string" ? raw.key.trim() : ""; - if (!name) return null; - const type = typeof raw.type === "string" ? raw.type.toLowerCase() : "string"; - const label = typeof raw.label === "string" && raw.label.trim() ? raw.label.trim() : name; - const description = typeof raw.description === "string" ? raw.description : ""; - const required = Boolean(raw.required); - const defaultValue = raw.hasOwnProperty("default") - ? raw.default - : raw.hasOwnProperty("defaultValue") - ? raw.defaultValue - : raw.hasOwnProperty("default_value") - ? raw.default_value - : ""; - return { name, label, type, description, required, default: defaultValue }; - }) - .filter(Boolean); - }; - - const deriveInitialValue = (variable) => { - const { type, default: defaultValue } = variable; - if (type === "boolean") { - if (typeof defaultValue === "boolean") return defaultValue; - if (defaultValue == null) return false; - const str = String(defaultValue).trim().toLowerCase(); - if (!str) return false; - return ["true", "1", "yes", "on"].includes(str); - } - if (type === "number") { - if (defaultValue == null || defaultValue === "") return ""; - if (typeof defaultValue === "number" && Number.isFinite(defaultValue)) { - return String(defaultValue); - } - const parsed = Number(defaultValue); - return Number.isFinite(parsed) ? String(parsed) : ""; - } - return defaultValue == null ? "" : String(defaultValue); - }; - - useEffect(() => { - if (!selectedPath) { - setVariables([]); - setVariableValues({}); - setVariableErrors({}); - setVariableStatus({ loading: false, error: "" }); - return; - } - let canceled = false; - const loadAssembly = async () => { - setVariableStatus({ loading: true, error: "" }); - try { - const island = mode === "ansible" ? "ansible" : "scripts"; - const trimmed = (selectedPath || "").replace(/\\/g, "/").replace(/^\/+/, "").trim(); - if (!trimmed) { - setVariables([]); - setVariableValues({}); - setVariableErrors({}); - setVariableStatus({ loading: false, error: "" }); - return; - } - let relPath = trimmed; - if (island === "scripts" && relPath.toLowerCase().startsWith("scripts/")) { - relPath = relPath.slice("Scripts/".length); - } else if (island === "ansible" && relPath.toLowerCase().startsWith("ansible_playbooks/")) { - relPath = relPath.slice("Ansible_Playbooks/".length); - } - const resp = await fetch(`/api/assembly/load?island=${island}&path=${encodeURIComponent(relPath)}`); - if (!resp.ok) throw new Error(`Failed to load assembly (HTTP ${resp.status})`); - const data = await resp.json(); - const defs = normalizeVariables(data?.assembly?.variables || []); - if (!canceled) { - setVariables(defs); - const initialValues = {}; - defs.forEach((v) => { - initialValues[v.name] = deriveInitialValue(v); - }); - setVariableValues(initialValues); - setVariableErrors({}); - setVariableStatus({ loading: false, error: "" }); - } - } catch (err) { - if (!canceled) { - setVariables([]); - setVariableValues({}); - setVariableErrors({}); - setVariableStatus({ loading: false, error: err?.message || String(err) }); - } - } - }; - loadAssembly(); - return () => { - canceled = true; - }; - }, [selectedPath, mode]); - - const handleVariableChange = (variable, rawValue) => { - const { name, type } = variable; - if (!name) return; - setVariableValues((prev) => ({ - ...prev, - [name]: type === "boolean" ? Boolean(rawValue) : rawValue - })); - setVariableErrors((prev) => { - if (!prev[name]) return prev; - const next = { ...prev }; - delete next[name]; - return next; - }); - }; - - const buildVariablePayload = () => { - const payload = {}; - variables.forEach((variable) => { - if (!variable?.name) return; - const { name, type } = variable; - const hasOverride = Object.prototype.hasOwnProperty.call(variableValues, name); - const raw = hasOverride ? variableValues[name] : deriveInitialValue(variable); - if (type === "boolean") { - payload[name] = Boolean(raw); - } else if (type === "number") { - if (raw === "" || raw === null || raw === undefined) { - payload[name] = ""; - } else { - const num = Number(raw); - payload[name] = Number.isFinite(num) ? num : ""; - } - } else { - payload[name] = raw == null ? "" : String(raw); - } - }); - return payload; - }; - - const onRun = async () => { - if (!selectedPath) { - setError(mode === 'ansible' ? "Please choose a playbook to run." : "Please choose a script to run."); - return; - } - if (mode === 'ansible' && !useSvcAccount && !selectedCredentialId) { - setError("Select a credential to run this playbook."); - return; - } - if (variables.length) { - const errors = {}; - variables.forEach((variable) => { - if (!variable) return; - if (!variable.required) return; - if (variable.type === "boolean") return; - const hasOverride = Object.prototype.hasOwnProperty.call(variableValues, variable.name); - const raw = hasOverride ? variableValues[variable.name] : deriveInitialValue(variable); - if (raw == null || raw === "") { - errors[variable.name] = "Required"; - } - }); - if (Object.keys(errors).length) { - setVariableErrors(errors); - setError("Please fill in all required variable values."); - return; - } - } - setRunning(true); - setError(""); - try { - let resp; - const variableOverrides = buildVariablePayload(); - if (mode === 'ansible') { - const playbook_path = selectedPath; // relative to ansible island - resp = await fetch("/api/ansible/quick_run", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - playbook_path, - hostnames, - variable_values: variableOverrides, - credential_id: !useSvcAccount && selectedCredentialId ? Number(selectedCredentialId) : null, - use_service_account: Boolean(useSvcAccount) - }) - }); - } else { - // quick_run expects a path relative to Assemblies root with 'Scripts/' prefix - const script_path = selectedPath.startsWith('Scripts/') ? selectedPath : `Scripts/${selectedPath}`; - resp = await fetch("/api/scripts/quick_run", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - script_path, - hostnames, - run_mode: runAsCurrentUser ? "current_user" : "system", - variable_values: variableOverrides - }) - }); - } - const data = await resp.json(); - if (!resp.ok) throw new Error(data.error || `HTTP ${resp.status}`); - onClose && onClose(); - } catch (err) { - setError(String(err.message || err)); - } finally { - setRunning(false); - } - }; - - const credentialRequired = mode === "ansible" && !useSvcAccount; - const disableRun = - running || - !selectedPath || - (credentialRequired && (!selectedCredentialId || !credentials.length)); - - return ( - - Quick Job - - - - - - - Select a {mode === 'ansible' ? 'playbook' : 'script'} to run on {hostnames.length} device{hostnames.length !== 1 ? "s" : ""}. - - {mode === 'ansible' && ( - - { - const checked = e.target.checked; - setUseSvcAccount(checked); - if (checked) { - setSelectedCredentialId(""); - } else if (!selectedCredentialId && credentials.length) { - setSelectedCredentialId(String(credentials[0].id)); - } - }} - size="small" - /> - } - label="Use Configured svcBorealis Account" - sx={{ mr: 2 }} - /> - - Credential - - - {useSvcAccount && ( - - Runs with the agent's svcBorealis account. - - )} - {credentialsLoading && } - {!credentialsLoading && credentialsError && ( - {credentialsError} - )} - {!useSvcAccount && !credentialsLoading && !credentialsError && !credentials.length && ( - - No SSH or WinRM credentials available. Create one under Access Management. - - )} - - )} - - - - {tree.length ? renderNodes(tree) : ( - - {mode === 'ansible' ? 'No playbooks found.' : 'No scripts found.'} - - )} - - - - Selection - - {selectedPath || (mode === 'ansible' ? 'No playbook selected' : 'No script selected')} - - - {mode !== 'ansible' && ( - <> - setRunAsCurrentUser(e.target.checked)} />} - label={Run as currently logged-in user} - /> - - Unchecked = Run-As BUILTIN\SYSTEM - - - )} - - - Variables - {variableStatus.loading ? ( - Loading variables… - ) : variableStatus.error ? ( - {variableStatus.error} - ) : variables.length ? ( - - {variables.map((variable) => ( - - {variable.type === "boolean" ? ( - handleVariableChange(variable, e.target.checked)} - /> - )} - label={ - - {variable.label} - {variable.required ? " *" : ""} - - } - /> - ) : ( - handleVariableChange(variable, e.target.value)} - InputLabelProps={{ shrink: true }} - sx={{ - "& .MuiOutlinedInput-root": { bgcolor: "#1b1b1b", color: "#e6edf3" }, - "& .MuiInputBase-input": { color: "#e6edf3" } - }} - error={Boolean(variableErrors[variable.name])} - helperText={variableErrors[variable.name] || variable.description || ""} - /> - )} - {variable.type === "boolean" && variable.description ? ( - - {variable.description} - - ) : null} - - ))} - - ) : ( - No variables defined for this assembly. - )} - - {error && ( - {error} - )} - - - - - - - - - ); -} diff --git a/Data/Server/WebUI/src/Scheduling/Scheduled_Jobs_List.jsx b/Data/Server/WebUI/src/Scheduling/Scheduled_Jobs_List.jsx deleted file mode 100644 index 7e22a10e..00000000 --- a/Data/Server/WebUI/src/Scheduling/Scheduled_Jobs_List.jsx +++ /dev/null @@ -1,685 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Scheduled_Jobs_List.jsx - -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState -} from "react"; -import { - Paper, - Box, - Typography, - Button, - Switch, - Dialog, - DialogTitle, - DialogActions, - CircularProgress -} from "@mui/material"; -import { AgGridReact } from "ag-grid-react"; -import { ModuleRegistry, AllCommunityModule, themeQuartz } from "ag-grid-community"; - -ModuleRegistry.registerModules([AllCommunityModule]); - -const myTheme = themeQuartz.withParams({ - accentColor: "#FFA6FF", - backgroundColor: "#1f2836", - browserColorScheme: "dark", - chromeBackgroundColor: { - ref: "foregroundColor", - mix: 0.07, - onto: "backgroundColor" - }, - fontFamily: { - googleFont: "IBM Plex Sans" - }, - foregroundColor: "#FFF", - headerFontSize: 14 -}); - -const themeClassName = myTheme.themeName || "ag-theme-quartz"; -const gridFontFamily = '"IBM Plex Sans", "Helvetica Neue", Arial, sans-serif'; -const iconFontFamily = '"Quartz Regular"'; - -function ResultsBar({ counts }) { - const total = Math.max(1, Number(counts?.total_targets || 0)); - const sections = [ - { key: "success", color: "#00d18c" }, - { key: "running", color: "#58a6ff" }, - { key: "failed", color: "#ff4f4f" }, - { key: "timed_out", color: "#b36ae2" }, - { key: "expired", color: "#777777" }, - { key: "pending", color: "#999999" } - ]; - const labelFor = (key) => - key === "pending" - ? "Scheduled" - : key - .replace(/_/g, " ") - .replace(/^./, (c) => c.toUpperCase()); - - const hasNonPending = sections - .filter((section) => section.key !== "pending") - .some((section) => Number(counts?.[section.key] || 0) > 0); - - return ( - - - {sections.map((section) => { - const value = Number(counts?.[section.key] || 0); - if (!value) return null; - const width = `${Math.round((value / total) * 100)}%`; - return ( - - ); - })} - - - {(() => { - if (!hasNonPending && Number(counts?.pending || 0) > 0) { - return Scheduled; - } - return sections - .filter((section) => Number(counts?.[section.key] || 0) > 0) - .map((section) => ( - - - {counts?.[section.key]} {labelFor(section.key)} - - )); - })()} - - - ); -} - -export default function ScheduledJobsList({ onCreateJob, onEditJob, refreshToken }) { - const [rows, setRows] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false); - const [selectedIds, setSelectedIds] = useState(() => new Set()); - const gridApiRef = useRef(null); - - const loadJobs = useCallback( - async ({ showLoading = false } = {}) => { - if (showLoading) { - setLoading(true); - setError(""); - } - try { - const resp = await fetch("/api/scheduled_jobs"); - const data = await resp.json().catch(() => ({})); - if (!resp.ok) { - throw new Error(data?.error || `HTTP ${resp.status}`); - } - const pretty = (st) => { - const s = String(st || "").toLowerCase(); - const map = { - immediately: "Immediately", - once: "Once", - every_5_minutes: "Every 5 Minutes", - every_10_minutes: "Every 10 Minutes", - every_15_minutes: "Every 15 Minutes", - every_30_minutes: "Every 30 Minutes", - every_hour: "Every Hour", - daily: "Daily", - weekly: "Weekly", - monthly: "Monthly", - yearly: "Yearly" - }; - if (map[s]) return map[s]; - try { - return s.replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase()); - } catch { - return String(st || ""); - } - }; - const fmt = (ts) => { - if (!ts) return ""; - try { - const d = new Date(Number(ts) * 1000); - if (Number.isNaN(d?.getTime())) return ""; - return d.toLocaleString(undefined, { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "numeric", - minute: "2-digit" - }); - } catch { - return ""; - } - }; - const mappedRows = (data?.jobs || []).map((j) => { - const compName = (Array.isArray(j.components) && j.components[0]?.name) || "Demonstration Component"; - const targetText = Array.isArray(j.targets) - ? `${j.targets.length} device${j.targets.length !== 1 ? "s" : ""}` - : ""; - const occurrence = pretty(j.schedule_type || "immediately"); - const resultsCounts = { - total_targets: Array.isArray(j.targets) ? j.targets.length : 0, - pending: Array.isArray(j.targets) ? j.targets.length : 0, - ...(j.result_counts || {}) - }; - if (resultsCounts && resultsCounts.total_targets == null) { - resultsCounts.total_targets = Array.isArray(j.targets) ? j.targets.length : 0; - } - return { - id: j.id, - name: j.name, - scriptWorkflow: compName, - target: targetText, - occurrence, - lastRun: fmt(j.last_run_ts), - nextRun: fmt(j.next_run_ts || j.start_ts), - result: j.last_status || (j.next_run_ts ? "Scheduled" : ""), - resultsCounts, - enabled: Boolean(j.enabled), - raw: j - }; - }); - setRows(mappedRows); - setError(""); - setSelectedIds((prev) => { - if (!prev.size) return prev; - const valid = new Set( - mappedRows.map((row, index) => row.id ?? row.name ?? String(index)) - ); - let changed = false; - const next = new Set(); - prev.forEach((value) => { - if (valid.has(value)) { - next.add(value); - } else { - changed = true; - } - }); - return changed ? next : prev; - }); - } catch (err) { - setRows([]); - setSelectedIds(() => new Set()); - setError(String(err?.message || err || "Failed to load scheduled jobs")); - } finally { - if (showLoading) { - setLoading(false); - } - } - }, - [] - ); - - useEffect(() => { - let timer; - let isMounted = true; - (async () => { - if (!isMounted) return; - await loadJobs({ showLoading: true }); - })(); - timer = setInterval(() => { - loadJobs(); - }, 5000); - return () => { - isMounted = false; - if (timer) clearInterval(timer); - }; - }, [loadJobs, refreshToken]); - - const handleGridReady = useCallback((params) => { - gridApiRef.current = params.api; - }, []); - - useEffect(() => { - const api = gridApiRef.current; - if (!api) return; - if (loading) { - api.showLoadingOverlay(); - } else if (!rows.length) { - api.showNoRowsOverlay(); - } else { - api.hideOverlay(); - } - }, [loading, rows]); - - useEffect(() => { - const api = gridApiRef.current; - if (!api) return; - api.forEachNode((node) => { - const shouldSelect = selectedIds.has(node.id); - if (node.isSelected() !== shouldSelect) { - node.setSelected(shouldSelect); - } - }); - }, [rows, selectedIds]); - - const anySelected = selectedIds.size > 0; - - const handleSelectionChanged = useCallback(() => { - const api = gridApiRef.current; - if (!api) return; - const selectedNodes = api.getSelectedNodes(); - const next = new Set(); - selectedNodes.forEach((node) => { - if (node?.id != null) { - next.add(String(node.id)); - } - }); - setSelectedIds(next); - }, []); - - const getRowId = useCallback((params) => { - return ( - params?.data?.id ?? - params?.data?.name ?? - String(params?.rowIndex ?? "") - ); - }, []); - - const nameCellRenderer = useCallback( - (params) => { - const row = params.data; - if (!row) return null; - const handleClick = (event) => { - event.preventDefault(); - event.stopPropagation(); - if (typeof onEditJob === "function") { - onEditJob(row.raw); - } - }; - return ( - - ); - }, - [onEditJob] - ); - - const resultsCellRenderer = useCallback((params) => { - return ; - }, []); - - const enabledCellRenderer = useCallback( - (params) => { - const row = params.data; - if (!row) return null; - const handleToggle = async (event) => { - event.stopPropagation(); - const nextEnabled = event.target.checked; - try { - await fetch(`/api/scheduled_jobs/${row.id}/toggle`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ enabled: nextEnabled }) - }); - } catch { - // ignore network errors for toggle - } - setRows((prev) => - prev.map((job) => { - if ((job.id ?? job.name) === (row.id ?? row.name)) { - const updatedRaw = { ...(job.raw || {}), enabled: nextEnabled }; - return { ...job, enabled: nextEnabled, raw: updatedRaw }; - } - return job; - }) - ); - }; - return ( - event.stopPropagation()} - sx={{ - "& .MuiSwitch-switchBase.Mui-checked": { - color: "#58a6ff" - }, - "& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track": { - bgcolor: "#58a6ff" - } - }} - /> - ); - }, - [] - ); - - const columnDefs = useMemo( - () => [ - { - headerName: "", - field: "__checkbox__", - checkboxSelection: true, - headerCheckboxSelection: true, - maxWidth: 60, - minWidth: 60, - sortable: false, - filter: false, - resizable: false, - suppressMenu: true, - pinned: false - }, - { - headerName: "Name", - field: "name", - cellRenderer: nameCellRenderer, - sort: "asc" - }, - { - headerName: "Assembly(s)", - field: "scriptWorkflow", - valueGetter: (params) => params.data?.scriptWorkflow || "Demonstration Component" - }, - { - headerName: "Target", - field: "target" - }, - { - headerName: "Recurrence", - field: "occurrence" - }, - { - headerName: "Last Run", - field: "lastRun" - }, - { - headerName: "Next Run", - field: "nextRun" - }, - { - headerName: "Results", - field: "resultsCounts", - minWidth: 280, - cellRenderer: resultsCellRenderer, - sortable: false, - filter: false - }, - { - headerName: "Enabled", - field: "enabled", - minWidth: 140, - maxWidth: 160, - cellRenderer: enabledCellRenderer, - sortable: false, - filter: false, - resizable: false, - suppressMenu: true - } - ], - [enabledCellRenderer, nameCellRenderer, resultsCellRenderer] - ); - - const defaultColDef = useMemo( - () => ({ - sortable: true, - filter: "agTextColumnFilter", - resizable: true, - flex: 1, - minWidth: 140, - cellStyle: { - display: "flex", - alignItems: "center", - color: "#f5f7fa", - fontFamily: gridFontFamily, - fontSize: "13px" - }, - headerClass: "scheduled-jobs-grid-header" - }), - [] - ); - - return ( - - - - - Scheduled Jobs - - - List of automation jobs with schedules, results, and actions. - - - - - - - - - {loading && ( - - - Loading scheduled jobs… - - )} - - {error && ( - - {error} - - )} - - - - - - - - setBulkDeleteOpen(false)} - PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }} - > - Are you sure you want to delete this job(s)? - - - - - - - ); -} diff --git a/Data/Server/WebUI/src/Sites/Site_List.jsx b/Data/Server/WebUI/src/Sites/Site_List.jsx deleted file mode 100644 index 478a7b2e..00000000 --- a/Data/Server/WebUI/src/Sites/Site_List.jsx +++ /dev/null @@ -1,385 +0,0 @@ -import React, { useEffect, useMemo, useState, useCallback, useRef } from "react"; -import { - Paper, - Box, - Typography, - Table, - TableBody, - TableCell, - TableHead, - TableRow, - TableSortLabel, - Checkbox, - Button, - IconButton, - Popover, - TextField, - MenuItem -} from "@mui/material"; -import AddIcon from "@mui/icons-material/Add"; -import DeleteIcon from "@mui/icons-material/DeleteOutline"; -import EditIcon from "@mui/icons-material/Edit"; -import FilterListIcon from "@mui/icons-material/FilterList"; -import ViewColumnIcon from "@mui/icons-material/ViewColumn"; -import { CreateSiteDialog, ConfirmDeleteDialog, RenameSiteDialog } from "../Dialogs.jsx"; - -export default function SiteList({ onOpenDevicesForSite }) { - const [rows, setRows] = useState([]); // {id, name, description, device_count} - const [orderBy, setOrderBy] = useState("name"); - const [order, setOrder] = useState("asc"); - const [selectedIds, setSelectedIds] = useState(() => new Set()); - - // Columns configuration (similar style to Device_List) - const COL_LABELS = useMemo(() => ({ - name: "Name", - description: "Description", - device_count: "Devices", - }), []); - const defaultColumns = useMemo( - () => [ - { id: "name", label: COL_LABELS.name }, - { id: "description", label: COL_LABELS.description }, - { id: "device_count", label: COL_LABELS.device_count }, - ], - [COL_LABELS] - ); - const [columns, setColumns] = useState(defaultColumns); - const dragColId = useRef(null); - const [colChooserAnchor, setColChooserAnchor] = useState(null); - - const [filters, setFilters] = useState({}); - const [filterAnchor, setFilterAnchor] = useState(null); // { id, anchorEl } - - const [createOpen, setCreateOpen] = useState(false); - const [deleteOpen, setDeleteOpen] = useState(false); - const [renameOpen, setRenameOpen] = useState(false); - const [renameValue, setRenameValue] = useState(""); - - const fetchSites = useCallback(async () => { - try { - const res = await fetch("/api/sites"); - const data = await res.json(); - setRows(Array.isArray(data?.sites) ? data.sites : []); - } catch { - setRows([]); - } - }, []); - - useEffect(() => { fetchSites(); }, [fetchSites]); - - // Apply initial filters from global search - useEffect(() => { - try { - const json = localStorage.getItem('site_list_initial_filters'); - if (json) { - const obj = JSON.parse(json); - if (obj && typeof obj === 'object') setFilters((prev) => ({ ...prev, ...obj })); - localStorage.removeItem('site_list_initial_filters'); - } - } catch {} - }, []); - - const handleSort = (col) => { - if (orderBy === col) setOrder(order === "asc" ? "desc" : "asc"); - else { setOrderBy(col); setOrder("asc"); } - }; - - const filtered = useMemo(() => { - if (!filters || Object.keys(filters).length === 0) return rows; - return rows.filter((r) => - Object.entries(filters).every(([k, v]) => { - const val = String(v || "").toLowerCase(); - if (!val) return true; - return String(r[k] ?? "").toLowerCase().includes(val); - }) - ); - }, [rows, filters]); - - const sorted = useMemo(() => { - const dir = order === "asc" ? 1 : -1; - const arr = [...filtered]; - arr.sort((a, b) => { - if (orderBy === "device_count") return ((a.device_count||0) - (b.device_count||0)) * dir; - return String(a[orderBy] ?? "").localeCompare(String(b[orderBy] ?? "")) * dir; - }); - return arr; - }, [filtered, orderBy, order]); - - const onHeaderDragStart = (colId) => (e) => { dragColId.current = colId; try { e.dataTransfer.setData("text/plain", colId); } catch {} }; - const onHeaderDragOver = (e) => { e.preventDefault(); }; - const onHeaderDrop = (targetColId) => (e) => { - e.preventDefault(); - const fromId = dragColId.current; if (!fromId || fromId === targetColId) return; - setColumns((prev) => { - const cur = [...prev]; - const fromIdx = cur.findIndex((c) => c.id === fromId); - const toIdx = cur.findIndex((c) => c.id === targetColId); - if (fromIdx < 0 || toIdx < 0) return prev; - const [moved] = cur.splice(fromIdx, 1); - cur.splice(toIdx, 0, moved); - return cur; - }); - dragColId.current = null; - }; - - const openFilter = (id) => (e) => setFilterAnchor({ id, anchorEl: e.currentTarget }); - const closeFilter = () => setFilterAnchor(null); - const onFilterChange = (id) => (e) => setFilters((prev) => ({ ...prev, [id]: e.target.value })); - - const isAllChecked = sorted.length > 0 && sorted.every((r) => selectedIds.has(r.id)); - const isIndeterminate = selectedIds.size > 0 && !isAllChecked; - const toggleAll = (e) => { - const checked = e.target.checked; - setSelectedIds((prev) => { - const next = new Set(prev); - if (checked) sorted.forEach((r) => next.add(r.id)); - else next.clear(); - return next; - }); - }; - const toggleOne = (id) => (e) => { - const checked = e.target.checked; - setSelectedIds((prev) => { - const next = new Set(prev); - if (checked) next.add(id); else next.delete(id); - return next; - }); - }; - - return ( - - - Sites - - - - - - - - - - - - - - {columns.map((col) => ( - - - handleSort(col.id)}> - {col.label} - - - - - - - ))} - - - - {sorted.map((r) => ( - - e.stopPropagation()}> - - - {columns.map((col) => { - switch (col.id) { - case 'name': - return ( - { - if (onOpenDevicesForSite) onOpenDevicesForSite(r.name); - }} - sx={{ color: '#58a6ff', '&:hover': { cursor: 'pointer', textDecoration: 'underline' } }} - > - {r.name} - - ); - case 'description': - return {r.description || ''}; - case 'device_count': - return {r.device_count ?? 0}; - default: - return ; - } - })} - - ))} - {sorted.length === 0 && ( - - No sites defined. - - )} - -
    - - {/* Column chooser */} - setColChooserAnchor(null)} - anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} - PaperProps={{ sx: { bgcolor: '#1e1e1e', color: '#fff', p: 1 } }} - > - - {[ - { id: 'name', label: 'Name' }, - { id: 'description', label: 'Description' }, - { id: 'device_count', label: 'Devices' }, - ].map((opt) => ( - e.stopPropagation()} sx={{ gap: 1 }}> - c.id === opt.id)} - onChange={(e) => { - const checked = e.target.checked; - setColumns((prev) => { - const exists = prev.some((c) => c.id === opt.id); - if (checked) { - if (exists) return prev; - return [...prev, { id: opt.id, label: opt.label }]; - } - return prev.filter((c) => c.id !== opt.id); - }); - }} - sx={{ p: 0.3, color: '#bbb' }} - /> - {opt.label} - - ))} - - - - - - - {/* Filter popover */} - - {filterAnchor && ( - - c.id === filterAnchor.id)?.label || ''}`} - value={filters[filterAnchor.id] || ''} - onChange={onFilterChange(filterAnchor.id)} - onKeyDown={(e) => { if (e.key === 'Escape') closeFilter(); }} - sx={{ - input: { color: '#fff' }, - minWidth: 220, - '& .MuiOutlinedInput-root': { '& fieldset': { borderColor: '#555' }, '&:hover fieldset': { borderColor: '#888' } }, - }} - /> - - - )} - - - {/* Create site dialog */} - setCreateOpen(false)} - onCreate={async (name, description) => { - try { - const res = await fetch('/api/sites', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, description }) }); - if (!res.ok) return; - setCreateOpen(false); - await fetchSites(); - } catch {} - }} - /> - - {/* Delete confirmation */} - setDeleteOpen(false)} - onConfirm={async () => { - try { - const ids = Array.from(selectedIds); - await fetch('/api/sites/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids }) }); - } catch {} - setDeleteOpen(false); - setSelectedIds(new Set()); - await fetchSites(); - }} - /> - - {/* Rename site dialog */} - setRenameOpen(false)} - onSave={async () => { - const newName = (renameValue || '').trim(); - if (!newName) return; - const selId = selectedIds.size === 1 ? Array.from(selectedIds)[0] : null; - if (selId == null) return; - try { - const res = await fetch('/api/sites/rename', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ id: selId, new_name: newName }) - }); - if (!res.ok) { - // Keep dialog open on error; optionally log - try { const err = await res.json(); console.warn('Rename failed', err); } catch {} - return; - } - setRenameOpen(false); - await fetchSites(); - } catch (e) { - console.warn('Rename error', e); - } - }} - /> -
    - ); -} diff --git a/Data/Server/WebUI/src/Status_Bar.jsx b/Data/Server/WebUI/src/Status_Bar.jsx deleted file mode 100644 index e2ec75da..00000000 --- a/Data/Server/WebUI/src/Status_Bar.jsx +++ /dev/null @@ -1,93 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Status_Bar.jsx - -import React, { useEffect, useState } from "react"; -import { Box, Button, Divider } from "@mui/material"; - -export default function StatusBar() { - const [apiStatus, setApiStatus] = useState("checking"); - - useEffect(() => { - fetch("/health") - .then((res) => (res.ok ? setApiStatus("online") : setApiStatus("offline"))) - .catch(() => setApiStatus("offline")); - }, []); - - const applyRate = () => { - const val = parseInt( - document.getElementById("updateRateInput")?.value - ); - if (!isNaN(val) && val >= 50) { - window.BorealisUpdateRate = val; - console.log("Global update rate set to", val + "ms"); - } else { - alert("Please enter a valid number (min 50)."); - } - }; - - return ( - - - Nodes: 0 - - Update Rate (ms): - - - - - - Backend API Server: - - {apiStatus === "checking" ? "..." : apiStatus.charAt(0).toUpperCase() + apiStatus.slice(1)} - - - - ); -} diff --git a/Data/Server/WebUI/src/index.jsx b/Data/Server/WebUI/src/index.jsx deleted file mode 100644 index a64e173b..00000000 --- a/Data/Server/WebUI/src/index.jsx +++ /dev/null @@ -1,21 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/index.js - -import React from 'react'; -import ReactDOM from 'react-dom/client'; - -// Global Styles -import "normalize.css/normalize.css"; -import "@fontsource/ibm-plex-sans/400.css"; -import "@fontsource/ibm-plex-sans/500.css"; -import "@fontsource/ibm-plex-sans/600.css"; -import "@fortawesome/fontawesome-free/css/all.min.css"; -import './Borealis.css'; // Global Theming for All of Borealis - -import App from './App.jsx'; - -const root = ReactDOM.createRoot(document.getElementById('root')); -root.render( - - - -); \ No newline at end of file diff --git a/Data/Server/WebUI/src/nodes/Agent/Node_Agent.jsx b/Data/Server/WebUI/src/nodes/Agent/Node_Agent.jsx deleted file mode 100644 index b892aceb..00000000 --- a/Data/Server/WebUI/src/nodes/Agent/Node_Agent.jsx +++ /dev/null @@ -1,554 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/nodes/Agent/Node_Agent.jsx -import React, { useEffect, useState, useCallback, useMemo, useRef } from "react"; -import { Handle, Position, useReactFlow, useStore } from "reactflow"; - -// Modern Node: Borealis Agent (Sidebar Config Enabled) -const BorealisAgentNode = ({ id, data }) => { - const { getNodes, setNodes } = useReactFlow(); - const edges = useStore((state) => state.edges); - const [agents, setAgents] = useState({}); - const [sites, setSites] = useState([]); - const [isConnected, setIsConnected] = useState(false); - const [siteMapping, setSiteMapping] = useState({}); - const prevRolesRef = useRef([]); - const selectionRef = useRef({ host: "", mode: "", agentId: "", siteId: "" }); - - const selectedSiteId = data?.agent_site_id ? String(data.agent_site_id) : ""; - const selectedHost = data?.agent_host || ""; - const selectedMode = - (data?.agent_mode || "currentuser").toString().toLowerCase() === "system" - ? "system" - : "currentuser"; - const selectedAgent = data?.agent_id || ""; - - // Group agents by hostname and execution context - const agentsByHostname = useMemo(() => { - if (!agents || typeof agents !== "object") return {}; - const grouped = {}; - Object.entries(agents).forEach(([aid, info]) => { - if (!info || typeof info !== "object") return; - const status = (info.status || "").toString().toLowerCase(); - if (status === "offline") return; - const host = (info.hostname || info.agent_hostname || "").trim() || "unknown"; - const modeRaw = (info.service_mode || "").toString().toLowerCase(); - const mode = modeRaw === "system" ? "system" : "currentuser"; - if (!grouped[host]) { - grouped[host] = { currentuser: null, system: null }; - } - grouped[host][mode] = { - agent_id: aid, - status: info.status || "offline", - last_seen: info.last_seen || 0, - info, - }; - }); - return grouped; - }, [agents]); - -// Locale-aware, case-insensitive, numeric-friendly sorter (e.g., "host2" < "host10") -const hostCollator = useMemo( - () => new Intl.Collator(undefined, { sensitivity: "base", numeric: true }), - [] -); - -const hostOptions = useMemo(() => { - const entries = Object.entries(agentsByHostname) - .map(([host, contexts]) => { - const candidates = [contexts.currentuser, contexts.system].filter(Boolean); - if (!candidates.length) return null; - - // Label is just the hostname (you already simplified this earlier) - const label = host; - - // Keep latest around if you use it elsewhere, but it no longer affects ordering - const latest = Math.max(...candidates.map((r) => r.last_seen || 0)); - - return { host, label, contexts, latest }; - }) - .filter(Boolean) - // Always alphabetical, case-insensitive, numeric-aware - .sort((a, b) => hostCollator.compare(a.host, b.host)); - - return entries; -}, [agentsByHostname, hostCollator]); - - // Fetch Agents Periodically - useEffect(() => { - const fetchAgents = () => { - fetch("/api/agents") - .then((res) => res.json()) - .then(setAgents) - .catch(() => {}); - }; - fetchAgents(); - const interval = setInterval(fetchAgents, 10000); // Update Agent List Every 10 Seconds - return () => clearInterval(interval); - }, []); - - // Fetch sites list - useEffect(() => { - const fetchSites = () => { - fetch("/api/sites") - .then((res) => res.json()) - .then((data) => { - const siteEntries = Array.isArray(data?.sites) ? data.sites : []; - setSites(siteEntries); - }) - .catch(() => setSites([])); - }; - fetchSites(); - }, []); - - // Fetch site mapping for current host options - useEffect(() => { - const hostnames = hostOptions.map(({ host }) => host).filter(Boolean); - if (!hostnames.length) { - setSiteMapping({}); - return; - } - const query = hostnames.map(encodeURIComponent).join(","); - fetch(`/api/sites/device_map?hostnames=${query}`) - .then((res) => res.json()) - .then((data) => { - const mapping = data?.mapping && typeof data.mapping === "object" ? data.mapping : {}; - setSiteMapping(mapping); - }) - .catch(() => setSiteMapping({})); - }, [hostOptions]); - - const filteredHostOptions = useMemo(() => { - if (!selectedSiteId) return hostOptions; - return hostOptions.filter(({ host }) => { - const mapping = siteMapping[host]; - if (!mapping || typeof mapping.site_id === "undefined" || mapping.site_id === null) { - return false; - } - return String(mapping.site_id) === selectedSiteId; - }); - }, [hostOptions, selectedSiteId, siteMapping]); - - // Align selected site with known host mapping when available - useEffect(() => { - if (selectedSiteId || !selectedHost) return; - const mapping = siteMapping[selectedHost]; - if (!mapping || typeof mapping.site_id === "undefined" || mapping.site_id === null) return; - const mappedId = String(mapping.site_id); - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - agent_site_id: mappedId, - }, - } - : n - ) - ); - }, [selectedHost, selectedSiteId, siteMapping, id, setNodes]); - - // Ensure host selection stays aligned with available agents - useEffect(() => { - if (!selectedHost) return; - - const hostExists = filteredHostOptions.some((opt) => opt.host === selectedHost); - if (hostExists) return; - - if (selectedAgent && agents[selectedAgent]) { - const info = agents[selectedAgent]; - const inferredHost = (info?.hostname || info?.agent_hostname || "").trim() || "unknown"; - const allowed = filteredHostOptions.some((opt) => opt.host === inferredHost); - if (allowed && inferredHost && inferredHost !== selectedHost) { - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - agent_host: inferredHost, - }, - } - : n - ) - ); - return; - } - } - - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - agent_host: "", - agent_id: "", - agent_mode: "currentuser", - }, - } - : n - ) - ); - }, [filteredHostOptions, selectedHost, selectedAgent, agents, id, setNodes]); - - const siteSelectOptions = useMemo(() => { - const entries = Array.isArray(sites) ? [...sites] : []; - entries.sort((a, b) => - (a?.name || "").localeCompare(b?.name || "", undefined, { sensitivity: "base" }) - ); - const mapped = entries.map((site) => ({ - value: String(site.id), - label: site.name || `Site ${site.id}`, - })); - return [{ value: "", label: "All Sites" }, ...mapped]; - }, [sites]); - - const hostSelectOptions = useMemo(() => { - const mapped = filteredHostOptions.map(({ host, label }) => ({ - value: host, - label, - })); - return [{ value: "", label: "-- Select --" }, ...mapped]; - }, [filteredHostOptions]); - - const activeHostContexts = selectedHost ? agentsByHostname[selectedHost] : null; - - const modeSelectOptions = useMemo( - () => [ - { - value: "currentuser", - label: "CURRENTUSER (Screen Capture / Macros)", - disabled: !activeHostContexts?.currentuser, - }, - { - value: "system", - label: "SYSTEM (Scripts)", - disabled: !activeHostContexts?.system, - }, - ], - [activeHostContexts] - ); - - useEffect(() => { - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - siteOptions: siteSelectOptions, - hostOptions: hostSelectOptions, - modeOptions: modeSelectOptions, - }, - } - : n - ) - ); - }, [id, setNodes, siteSelectOptions, hostSelectOptions, modeSelectOptions]); - - useEffect(() => { - if (!selectedHost) { - if (selectedAgent || selectedMode !== "currentuser") { - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - agent_id: "", - agent_mode: "currentuser", - }, - } - : n - ) - ); - } - return; - } - - const contexts = agentsByHostname[selectedHost]; - if (!contexts) { - if (selectedAgent || selectedMode !== "currentuser") { - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - agent_id: "", - agent_mode: "currentuser", - }, - } - : n - ) - ); - } - return; - } - - if (!contexts[selectedMode]) { - const fallbackMode = contexts.currentuser - ? "currentuser" - : contexts.system - ? "system" - : "currentuser"; - const fallbackAgentId = contexts[fallbackMode]?.agent_id || ""; - if (fallbackMode !== selectedMode || fallbackAgentId !== selectedAgent) { - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - agent_mode: fallbackMode, - agent_id: fallbackAgentId, - }, - } - : n - ) - ); - } - return; - } - - const targetAgentId = contexts[selectedMode]?.agent_id || ""; - if (targetAgentId !== selectedAgent) { - setNodes((nds) => - nds.map((n) => - n.id === id - ? { - ...n, - data: { - ...n.data, - agent_id: targetAgentId, - }, - } - : n - ) - ); - } - }, [selectedHost, selectedMode, agentsByHostname, selectedAgent, id, setNodes]); - - useEffect(() => { - const prev = selectionRef.current; - const changed = - prev.host !== selectedHost || - prev.mode !== selectedMode || - prev.agentId !== selectedAgent || - prev.siteId !== selectedSiteId; - if (!changed) return; - - const selectionChangedAgent = - prev.agentId && - (prev.agentId !== selectedAgent || prev.host !== selectedHost || prev.mode !== selectedMode); - if (selectionChangedAgent) { - setIsConnected(false); - prevRolesRef.current = []; - } - - selectionRef.current = { - host: selectedHost, - mode: selectedMode, - agentId: selectedAgent, - siteId: selectedSiteId, - }; - }, [selectedHost, selectedMode, selectedAgent, selectedSiteId]); - - // Attached Roles logic - const attachedRoleIds = useMemo( - () => - edges - .filter((e) => e.source === id && e.sourceHandle === "provisioner") - .map((e) => e.target), - [edges, id] - ); - const getAttachedRoles = useCallback(() => { - const allNodes = getNodes(); - return attachedRoleIds - .map((nid) => { - const fn = window.__BorealisInstructionNodes?.[nid]; - return typeof fn === "function" ? fn() : null; - }) - .filter((r) => r); - }, [attachedRoleIds, getNodes]); - - // Provision Roles to Agent - const provisionRoles = useCallback((roles) => { - if (!selectedAgent) return; - fetch("/api/agent/provision", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ agent_id: selectedAgent, roles }) - }) - .then(() => { - setIsConnected(true); - prevRolesRef.current = roles; - }) - .catch(() => {}); - }, [selectedAgent]); - const handleConnect = useCallback(() => { - const roles = getAttachedRoles(); - provisionRoles(roles); - }, [getAttachedRoles, provisionRoles]); - const handleDisconnect = useCallback(() => { - if (!selectedAgent) return; - fetch("/api/agent/provision", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ agent_id: selectedAgent, roles: [] }) - }) - .then(() => { - setIsConnected(false); - prevRolesRef.current = []; - }) - .catch(() => {}); - }, [selectedAgent]); - - // Auto-provision on role change - useEffect(() => { - const newRoles = getAttachedRoles(); - const prevSerialized = JSON.stringify(prevRolesRef.current || []); - const newSerialized = JSON.stringify(newRoles); - if (isConnected && newSerialized !== prevSerialized) { - provisionRoles(newRoles); - } - }, [attachedRoleIds, isConnected, getAttachedRoles, provisionRoles]); - - // Status Label - const selectedAgentStatus = useMemo(() => { - if (!selectedHost) return "Unassigned"; - const contexts = agentsByHostname[selectedHost]; - if (!contexts) return "Offline"; - const activeContext = contexts[selectedMode]; - if (!selectedAgent || !activeContext) return "Unavailable"; - const status = (activeContext.status || "").toString().toLowerCase(); - if (status === "provisioned") return "Connected"; - if (status === "orphaned") return "Available"; - if (!status) return "Available"; - return status.charAt(0).toUpperCase() + status.slice(1); - }, [agentsByHostname, selectedHost, selectedMode, selectedAgent]); - - // Render (Sidebar handles config) - return ( -
    - - -
    Device Agent
    -
    -
    Right-Click to Configure Agent
    - -
    - {selectedHost ? `${selectedHost} · ${selectedMode.toUpperCase()}` : "No device selected"} -
    -
    -
    - ); -}; - -// Node Registration Object with sidebar config and docs -export default { - type: "Borealis_Agent", - label: "Device Agent", - description: ` -Select and connect to a remote Borealis Agent. -- Assign roles to agent dynamically by connecting "Agent Role" nodes. -- Auto-provisions agent as role assignments change. -- See live agent status and re-connect/disconnect easily. -- Choose between CURRENTUSER and SYSTEM contexts for each device. -`.trim(), - content: "Select and manage an Agent with dynamic roles", - component: BorealisAgentNode, - config: [ - { - key: "agent_site_id", - label: "Site", - type: "select", - optionsKey: "siteOptions", - defaultValue: "" - }, - { - key: "agent_host", - label: "Device", - type: "select", - optionsKey: "hostOptions", - defaultValue: "" - }, - { - key: "agent_mode", - label: "Agent Context", - type: "select", - optionsKey: "modeOptions", - defaultValue: "currentuser" - }, - { - key: "agent_id", - label: "Agent ID", - type: "text", - readOnly: true, - defaultValue: "" - } - ], - usage_documentation: ` -### Borealis Agent Node - -This node allows you to establish a connection with a device running a Borealis "Agent", so you can instruct the agent to do things from your workflow. - -#### Features -- **Select** a site, then a device, then finally an agent context (CURRENTUSER vs SYSTEM). -- **Connect/Disconnect** from the agent at any time. -- **Attach roles** (by connecting "Agent Role" nodes to this node's output handle) to assign behaviors dynamically. - -#### How to Use -1. **Drag and drop in a Borealis Agent node.** -2. **Pick an agent** from the dropdown list (auto-populates from API backend). -3. **Click "Connect to Agent"**. -4. **Attach Agent Role Nodes** (e.g., Screenshot, Macro Keypress) to the "provisioner" output handle to define what the agent should do. -5. Agent will automatically update its roles as you change connected Role Nodes. - -#### Good to Know -- If an agent disconnects or goes offline, its status will show "Reconnecting..." until it returns. -- **Roles update LIVE**: Any time you change attached roles, the agent gets updated instantly. - -`.trim() -}; diff --git a/Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Macro.jsx b/Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Macro.jsx deleted file mode 100644 index 78101a5f..00000000 --- a/Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Macro.jsx +++ /dev/null @@ -1,310 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/nodes/Agent Roles/Node_Agent_Role_Macro.jsx -import React, { useState, useEffect, useRef } from "react"; -import { Handle, Position, useReactFlow, useStore } from "reactflow"; -import "react-simple-keyboard/build/css/index.css"; - -// Default update interval for window list refresh (in ms) -const WINDOW_LIST_REFRESH_MS = 4000; - -if (!window.BorealisValueBus) window.BorealisValueBus = {}; -if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100; - -const DEFAULT_OPERATION_MODE = "Continuous"; -const OPERATION_MODES = [ - "Run Once", - "Continuous", - "Trigger-Once", - "Trigger-Continuous" -]; - -const MACRO_TYPES = [ - "keypress", - "typed_text" -]; - -const statusColors = { - idle: "#333", - running: "#00d18c", - error: "#ff4f4f", - success: "#00d18c" -}; - -const MacroKeyPressNode = ({ id, data }) => { - const { setNodes, getNodes } = useReactFlow(); - const edges = useStore((state) => state.edges); - const [windowList, setWindowList] = useState([]); - const [status, setStatus] = useState({ state: "idle", message: "" }); - const socketRef = useRef(null); - - // Determine if agent is connected - const agentEdge = edges.find((e) => e.target === id && e.targetHandle === "agent"); - const agentNode = agentEdge && getNodes().find((n) => n.id === agentEdge.source); - const agentConnection = !!(agentNode && agentNode.data && agentNode.data.agent_id); - const agent_id = agentNode && agentNode.data && agentNode.data.agent_id; - - // Macro run/trigger state (sidebar sets this via config, but node UI just shows status) - const running = data?.active === true || data?.active === "true"; - - // Store for last macro error/status - const [lastMacroStatus, setLastMacroStatus] = useState({ success: true, message: "", timestamp: null }); - - // Setup WebSocket for agent macro status updates - useEffect(() => { - if (!window.BorealisSocket) return; - const socket = window.BorealisSocket; - socketRef.current = socket; - - function handleMacroStatus(payload) { - if ( - payload && - payload.agent_id === agent_id && - payload.node_id === id - ) { - setLastMacroStatus({ - success: !!payload.success, - message: payload.message || "", - timestamp: payload.timestamp || Date.now() - }); - setStatus({ - state: payload.success ? "success" : "error", - message: payload.message || (payload.success ? "Success" : "Error") - }); - } - } - - socket.on("macro_status", handleMacroStatus); - return () => { - socket.off("macro_status", handleMacroStatus); - }; - }, [agent_id, id]); - - // Auto-refresh window list from agent - useEffect(() => { - let intervalId = null; - async function fetchWindows() { - if (window.BorealisSocket && agentConnection) { - window.BorealisSocket.emit("list_agent_windows", { - agent_id - }); - } - } - fetchWindows(); - intervalId = setInterval(fetchWindows, WINDOW_LIST_REFRESH_MS); - - // Listen for agent_window_list updates - function handleAgentWindowList(payload) { - if (payload?.agent_id === agent_id && Array.isArray(payload.windows)) { - setWindowList(payload.windows); - - // Store windowList in node data for sidebar dynamic dropdowns - setNodes(nds => - nds.map(n => - n.id === id - ? { ...n, data: { ...n.data, windowList: payload.windows } } - : n - ) - ); - } - } - if (window.BorealisSocket) { - window.BorealisSocket.on("agent_window_list", handleAgentWindowList); - } - - return () => { - clearInterval(intervalId); - if (window.BorealisSocket) { - window.BorealisSocket.off("agent_window_list", handleAgentWindowList); - } - }; - }, [agent_id, agentConnection, setNodes, id]); - - // UI: Start/Pause Button - const handleToggleMacro = () => { - setNodes(nds => - nds.map(n => - n.id === id - ? { - ...n, - data: { - ...n.data, - active: n.data?.active === true || n.data?.active === "true" ? "false" : "true" - } - } - : n - ) - ); - }; - - // Optional: Show which window is targeted by name - const selectedWindow = (windowList || []).find(w => String(w.handle) === String(data?.window_handle)); - - // Node UI (no config fields, only status + window list) - return ( -
    - {/* --- INPUT LABELS & HANDLES --- */} -
    - Agent -
    - -
    - Trigger -
    - - -
    - Agent Role: Macro -
    -
    - -
    - Status:{" "} - {status.state === "error" - ? ( - - Error{lastMacroStatus.message ? `: ${lastMacroStatus.message}` : ""} - - ) - : running - ? ( - - Running{lastMacroStatus.message ? ` (${lastMacroStatus.message})` : ""} - - ) - : "Idle"} -
    - Agent Connection: {agentConnection ? "Connected" : "Not Connected"} -
    - Target Window:{" "} - {selectedWindow - ? `${selectedWindow.title} (${selectedWindow.handle})` - : data?.window_handle - ? `Handle: ${data.window_handle}` - : Not set} -
    - Mode: {data?.operation_mode || DEFAULT_OPERATION_MODE} -
    - Macro Type: {data?.macro_type || "keypress"} -
    - -
    - - {lastMacroStatus.timestamp - ? `Last event: ${new Date(lastMacroStatus.timestamp).toLocaleTimeString()}` - : ""} - -
    -
    - ); -}; - -// ----- Node Catalog Export ----- -export default { - type: "Macro_KeyPress", - label: "Agent Role: Macro", - description: ` -Send automated key presses or typed text to any open application window on the connected agent. -Supports manual, continuous, trigger, and one-shot modes for automation and event-driven workflows. -`, - content: "Send Key Press or Typed Text to Window via Agent", - component: MacroKeyPressNode, - config: [ - { key: "window_handle", label: "Target Window", type: "select", dynamicOptions: true, defaultValue: "" }, - { key: "macro_type", label: "Macro Type", type: "select", options: ["keypress", "typed_text"], defaultValue: "keypress" }, - { key: "key", label: "Key", type: "text", defaultValue: "" }, - { key: "text", label: "Typed Text", type: "text", defaultValue: "" }, - { key: "interval_ms", label: "Interval (ms)", type: "text", defaultValue: "1000" }, - { key: "randomize_interval", label: "Randomize Interval", type: "select", options: ["true", "false"], defaultValue: "false" }, - { key: "random_min", label: "Random Min (ms)", type: "text", defaultValue: "750" }, - { key: "random_max", label: "Random Max (ms)", type: "text", defaultValue: "950" }, - { key: "operation_mode", label: "Operation Mode", type: "select", options: OPERATION_MODES, defaultValue: "Continuous" }, - { key: "active", label: "Macro Enabled", type: "select", options: ["true", "false"], defaultValue: "false" }, - { key: "trigger", label: "Trigger Value", type: "text", defaultValue: "0" } - ], - usage_documentation: ` -### Agent Role: Macro - -**Modes:** -- **Continuous**: Macro sends input non-stop when started by button. -- **Trigger-Continuous**: Macro sends input as long as upstream trigger is "1". -- **Trigger-Once**: Macro fires once per upstream "1" (one-shot edge). -- **Run Once**: Macro runs only once when started by button. - -**Macro Types:** -- **Single Keypress**: Press a single key. -- **Typed Text**: Types out a string. - -**Window Target:** -- Dropdown of live windows from agent, stays updated. - -**Event-Driven Support:** -- Chain with other Borealis nodes (text recognition, event triggers, etc). - -**Live Status:** -- Displays last agent macro event and error feedback in node. - ---- - `.trim() -}; diff --git a/Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Screenshot.jsx b/Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Screenshot.jsx deleted file mode 100644 index fa184781..00000000 --- a/Data/Server/WebUI/src/nodes/Agent/Node_Agent_Role_Screenshot.jsx +++ /dev/null @@ -1,271 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/nodes/Agent/Node_Agent_Role_Screenshot.jsx -import React, { useCallback, useEffect, useRef, useState } from "react"; -import { Handle, Position, useReactFlow, useStore } from "reactflow"; -import ShareIcon from "@mui/icons-material/Share"; -import IconButton from "@mui/material/IconButton"; - -/* - Agent Role: Screenshot Node (Modern, Sidebar Config Enabled) - - - Defines a screenshot region to be captured by a remote Borealis Agent. - - Pushes live base64 PNG data to downstream nodes. - - Region coordinates (x, y, w, h), visibility, overlay label, and interval are all persisted and synchronized. - - All configuration is moved to the right sidebar (Node Properties). - - Maintains full bi-directional write-back of coordinates and overlay settings. -*/ - -if (!window.BorealisValueBus) window.BorealisValueBus = {}; -if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100; - -const AgentScreenshotNode = ({ id, data }) => { - const { setNodes, getNodes } = useReactFlow(); - const edges = useStore(state => state.edges); - - const resolveAgentData = useCallback(() => { - try { - const agentEdge = edges.find(e => e.target === id && e.sourceHandle === "provisioner"); - const agentNode = getNodes().find(n => n.id === agentEdge?.source); - return agentNode?.data || null; - } catch (err) { - return null; - } - }, [edges, getNodes, id]); - - - // Core config values pulled from sidebar config (with defaults) - const interval = parseInt(data?.interval || 1000, 10) || 1000; - const region = { - x: parseInt(data?.x ?? 250, 10), - y: parseInt(data?.y ?? 100, 10), - w: parseInt(data?.w ?? 300, 10), - h: parseInt(data?.h ?? 200, 10) - }; - const visible = (data?.visible ?? "true") === "true"; - const alias = data?.alias || ""; - const [imageBase64, setImageBase64] = useState(data?.value || ""); - const agentData = resolveAgentData(); - const targetModeLabel = ((agentData?.agent_mode || "").toString().toLowerCase() === "system") - ? "SYSTEM Agent" - : "CURRENTUSER Agent"; - const targetHostLabel = (agentData?.agent_host || "").toString(); - - // Always push current imageBase64 into BorealisValueBus at the global update rate - useEffect(() => { - const intervalId = setInterval(() => { - if (imageBase64) { - window.BorealisValueBus[id] = imageBase64; - setNodes(nds => - nds.map(n => - n.id === id ? { ...n, data: { ...n.data, value: imageBase64 } } : n - ) - ); - } - }, window.BorealisUpdateRate || 100); - return () => clearInterval(intervalId); - }, [id, imageBase64, setNodes]); - - // Listen for agent screenshot and overlay region updates - useEffect(() => { - const socket = window.BorealisSocket; - if (!socket) return; - - const handleScreenshot = (payload) => { - if (payload?.node_id !== id) return; - // Additionally ensure payload is from the agent connected upstream of this node - const agentData = resolveAgentData(); - const selectedAgentId = agentData?.agent_id; - if (!selectedAgentId || payload?.agent_id !== selectedAgentId) return; - - if (payload.image_base64) { - setImageBase64(payload.image_base64); - window.BorealisValueBus[id] = payload.image_base64; - } - const { x, y, w, h } = payload; - if ( - x !== undefined && - y !== undefined && - w !== undefined && - h !== undefined - ) { - setNodes(nds => - nds.map(n => - n.id === id ? { ...n, data: { ...n.data, x, y, w, h } } : n - ) - ); - } - }; - - socket.on("agent_screenshot_task", handleScreenshot); - return () => socket.off("agent_screenshot_task", handleScreenshot); - }, [id, setNodes, resolveAgentData]); - - // Register this node for the agent provisioning sync - window.__BorealisInstructionNodes = window.__BorealisInstructionNodes || {}; - window.__BorealisInstructionNodes[id] = () => { - const agentData = resolveAgentData() || {}; - const modeRaw = (agentData.agent_mode || "").toString().toLowerCase(); - const targetMode = modeRaw === "system" ? "system" : "currentuser"; - return { - node_id: id, - role: "screenshot", - interval, - visible, - alias, - target_agent_mode: targetMode, - target_agent_host: agentData.agent_host || "", - ...region - }; - }; - - // Manual live view copy button - const handleCopyLiveViewLink = () => { - const agentData = resolveAgentData(); - const selectedAgentId = agentData?.agent_id; - - if (!selectedAgentId) { - alert("No valid agent connection found."); - return; - } - - const liveUrl = `${window.location.origin}/api/agent/${selectedAgentId}/node/${id}/screenshot/live`; - navigator.clipboard.writeText(liveUrl) - .then(() => console.log(`[Clipboard] Live View URL copied: ${liveUrl}`)) - .catch(err => console.error("Clipboard copy failed:", err)); - }; - - // Node card UI - config handled in sidebar - return ( -
    - - - -
    - {data?.label || "Agent Role: Screenshot"} -
    -
    -
    - Region: X:{region.x} Y:{region.y} W:{region.w} H:{region.h} -
    -
    - Interval: {interval} ms -
    -
    - Agent Context: {targetModeLabel} -
    -
    - Target Host:{" "} - {targetHostLabel ? ( - targetHostLabel - ) : ( - unknown - )} -
    -
    - Overlay: {visible ? "Yes" : "No"} -
    -
    - Label: {alias || none} -
    -
    - {imageBase64 - ? `Last image: ${Math.round(imageBase64.length / 1024)} KB` - : "Awaiting Screenshot Data..."} -
    -
    -
    - - - -
    -
    - ); -}; - -// Node registration for Borealis catalog (sidebar config enabled) -export default { - type: "Agent_Role_Screenshot", - label: "Agent Role: Screenshot", - description: ` -Capture a live screenshot of a defined region from a remote Borealis Agent. - -- Define region (X, Y, Width, Height) -- Select update interval (ms) -- Optionally show a visual overlay with a label -- Pushes base64 PNG stream to downstream nodes -- Use copy button to share live view URL -- Targets the CURRENTUSER or SYSTEM agent context selected upstream -`.trim(), - content: "Capture screenshot region via agent", - component: AgentScreenshotNode, - config: [ - { - key: "interval", - label: "Update Interval (ms)", - type: "text", - defaultValue: "1000" - }, - { - key: "x", - label: "Region X", - type: "text", - defaultValue: "250" - }, - { - key: "y", - label: "Region Y", - type: "text", - defaultValue: "100" - }, - { - key: "w", - label: "Region Width", - type: "text", - defaultValue: "300" - }, - { - key: "h", - label: "Region Height", - type: "text", - defaultValue: "200" - }, - { - key: "visible", - label: "Show Overlay on Agent", - type: "select", - options: ["true", "false"], - defaultValue: "true" - }, - { - key: "alias", - label: "Overlay Label", - type: "text", - defaultValue: "" - } - ], - usage_documentation: ` -### Agent Role: Screenshot Node - -This node defines a screenshot-capture role for a Borealis Agent. - -**How It Works** -- The region (X, Y, W, H) is sent to the Agent for real-time screenshot capture. -- The interval determines how often the Agent captures and pushes new images. -- Optionally, an overlay with a label can be displayed on the Agent's screen for visual feedback. -- The captured screenshot (as a base64 PNG) is available to downstream nodes. -- Use the share button to copy a live viewing URL for the screenshot stream. - -**Configuration** -- All fields are edited via the right sidebar. -- Coordinates update live if region is changed from the Agent. - -**Warning** -- Changing region from the Agent UI will update this node's coordinates. -- Do not remove the bi-directional region write-back: if the region moves, this node updates immediately. - -**Example Use Cases** -- Automated visual QA (comparing regions of apps) -- OCR on live application windows -- Remote monitoring dashboards - - `.trim() -}; diff --git a/Data/Server/WebUI/src/nodes/Alerting/Node_Alert_Sound.jsx b/Data/Server/WebUI/src/nodes/Alerting/Node_Alert_Sound.jsx deleted file mode 100644 index ecd10521..00000000 --- a/Data/Server/WebUI/src/nodes/Alerting/Node_Alert_Sound.jsx +++ /dev/null @@ -1,326 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: Node_Alert_Sound.jsx - -/** - * ================================================== - * Borealis - Alert Sound Node (with Base64 Restore) - * ================================================== - * - * COMPONENT ROLE: - * Plays a sound when input = "1". Provides a visual indicator: - * - Green dot: input is 0 - * - Red dot: input is 1 - * - * Modes: - * - "Once": Triggers once when going 0 -> 1 - * - "Constant": Triggers repeatedly every X ms while input = 1 - * - * Supports embedding base64 audio directly into the workflow. - */ - -import React, { useEffect, useRef, useState } from "react"; -import { Handle, Position, useReactFlow, useStore } from "reactflow"; - -if (!window.BorealisValueBus) window.BorealisValueBus = {}; -if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100; - -const AlertSoundNode = ({ id, data }) => { - const edges = useStore(state => state.edges); - const { setNodes } = useReactFlow(); - - const [alertType, setAlertType] = useState(data?.alertType || "Once"); - const [intervalMs, setIntervalMs] = useState(data?.interval || 1000); - const [prevInput, setPrevInput] = useState("0"); - const [customAudioBase64, setCustomAudioBase64] = useState(data?.audio || null); - const [currentInput, setCurrentInput] = useState("0"); - - const audioRef = useRef(null); - - const playSound = () => { - if (audioRef.current) { - console.log(`[Alert Node ${id}] Attempting to play sound`); - try { - audioRef.current.pause(); - audioRef.current.currentTime = 0; - audioRef.current.load(); - audioRef.current.play().then(() => { - console.log(`[Alert Node ${id}] Sound played successfully`); - }).catch((err) => { - console.warn(`[Alert Node ${id}] Audio play blocked or errored:`, err); - }); - } catch (err) { - console.error(`[Alert Node ${id}] Failed to play sound:`, err); - } - } else { - console.warn(`[Alert Node ${id}] No audioRef loaded`); - } - }; - - const handleFileUpload = (event) => { - const file = event.target.files[0]; - if (!file) return; - - console.log(`[Alert Node ${id}] File selected:`, file.name, file.type); - - const supportedTypes = ["audio/wav", "audio/mp3", "audio/mpeg", "audio/ogg"]; - if (!supportedTypes.includes(file.type)) { - console.warn(`[Alert Node ${id}] Unsupported audio type: ${file.type}`); - return; - } - - const reader = new FileReader(); - reader.onload = (e) => { - const base64 = e.target.result; - const mimeType = file.type || "audio/mpeg"; - const safeURL = base64.startsWith("data:") - ? base64 - : `data:${mimeType};base64,${base64}`; - - if (audioRef.current) { - audioRef.current.pause(); - audioRef.current.src = ""; - audioRef.current.load(); - audioRef.current = null; - } - - const newAudio = new Audio(); - newAudio.src = safeURL; - - let readyFired = false; - - newAudio.addEventListener("canplaythrough", () => { - if (readyFired) return; - readyFired = true; - console.log(`[Alert Node ${id}] Audio is decodable and ready: ${file.name}`); - - setCustomAudioBase64(safeURL); - audioRef.current = newAudio; - newAudio.load(); - - setNodes(nds => - nds.map(n => - n.id === id - ? { ...n, data: { ...n.data, audio: safeURL } } - : n - ) - ); - }); - - setTimeout(() => { - if (!readyFired) { - console.warn(`[Alert Node ${id}] WARNING: Audio not marked ready in time. May fail silently.`); - } - }, 2000); - }; - - reader.onerror = (e) => { - console.error(`[Alert Node ${id}] File read error:`, e); - }; - - reader.readAsDataURL(file); - }; - - // Restore embedded audio from saved workflow - useEffect(() => { - if (customAudioBase64) { - console.log(`[Alert Node ${id}] Loading embedded audio from workflow`); - - if (audioRef.current) { - audioRef.current.pause(); - audioRef.current.src = ""; - audioRef.current.load(); - audioRef.current = null; - } - - const loadedAudio = new Audio(customAudioBase64); - loadedAudio.addEventListener("canplaythrough", () => { - console.log(`[Alert Node ${id}] Embedded audio ready`); - }); - - audioRef.current = loadedAudio; - loadedAudio.load(); - } else { - console.log(`[Alert Node ${id}] No custom audio, using fallback silent wav`); - audioRef.current = new Audio("data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YRAAAAAA"); - audioRef.current.load(); - } - }, [customAudioBase64]); - - useEffect(() => { - let currentRate = window.BorealisUpdateRate; - let intervalId = null; - - const runLogic = () => { - const inputEdge = edges.find(e => e.target === id); - const sourceId = inputEdge?.source || null; - const val = sourceId ? (window.BorealisValueBus[sourceId] || "0") : "0"; - - setCurrentInput(val); - - if (alertType === "Once") { - if (val === "1" && prevInput !== "1") { - console.log(`[Alert Node ${id}] Triggered ONCE playback`); - playSound(); - } - } - - setPrevInput(val); - }; - - const start = () => { - if (alertType === "Constant") { - intervalId = setInterval(() => { - const inputEdge = edges.find(e => e.target === id); - const sourceId = inputEdge?.source || null; - const val = sourceId ? (window.BorealisValueBus[sourceId] || "0") : "0"; - setCurrentInput(val); - if (String(val) === "1") { - console.log(`[Alert Node ${id}] Triggered CONSTANT playback`); - playSound(); - } - }, intervalMs); - } else { - intervalId = setInterval(runLogic, currentRate); - } - }; - - start(); - - const monitor = setInterval(() => { - const newRate = window.BorealisUpdateRate; - if (newRate !== currentRate && alertType === "Once") { - currentRate = newRate; - clearInterval(intervalId); - start(); - } - }, 250); - - return () => { - clearInterval(intervalId); - clearInterval(monitor); - }; - }, [edges, alertType, intervalMs, prevInput]); - - const indicatorColor = currentInput === "1" ? "#ff4444" : "#44ff44"; - - return ( -
    - - - {/* Header with indicator dot */} -
    - {data?.label || "Alert Sound"} -
    -
    - -
    -
    - Play a sound alert when input is "1" -
    - - - - - - setIntervalMs(parseInt(e.target.value))} - disabled={alertType === "Once"} - style={{ - ...inputStyle, - background: alertType === "Once" ? "#2a2a2a" : "#1e1e1e" - }} - /> - - - -
    - - -
    -
    -
    - ); -}; - -const dropdownStyle = { - fontSize: "9px", - padding: "4px", - background: "#1e1e1e", - color: "#ccc", - border: "1px solid #444", - borderRadius: "2px", - width: "100%", - marginBottom: "8px" -}; - -const inputStyle = { - fontSize: "9px", - padding: "4px", - color: "#ccc", - border: "1px solid #444", - borderRadius: "2px", - width: "100%", - marginBottom: "8px" -}; - -export default { - type: "AlertSoundNode", - label: "Alert Sound", - description: ` -Plays a sound alert when input = "1" - -- "Once" = Only when 0 -> 1 transition -- "Constant" = Repeats every X ms while input stays 1 -- Custom audio supported (MP3/WAV/OGG) -- Base64 audio embedded in workflow and restored -- Visual status indicator (green = 0, red = 1) -- Manual "Test" button for validation -`.trim(), - content: "Sound alert when input value = 1", - component: AlertSoundNode -}; diff --git a/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_Array_Index_Extractor.jsx b/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_Array_Index_Extractor.jsx deleted file mode 100644 index 0ff675db..00000000 --- a/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_Array_Index_Extractor.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Handle, Position, useReactFlow, useStore } from "reactflow"; - -// Ensure Borealis shared memory exists -if (!window.BorealisValueBus) window.BorealisValueBus = {}; -if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100; - -const ArrayIndexExtractorNode = ({ id, data }) => { - const edges = useStore((state) => state.edges); - const { setNodes } = useReactFlow(); - const [result, setResult] = useState("Line Does Not Exist"); - const valueRef = useRef(result); - - // Use config field, always 1-based for UX, fallback to 1 - const lineNumber = parseInt(data?.lineNumber, 10) || 1; - - useEffect(() => { - let intervalId = null; - let currentRate = window.BorealisUpdateRate; - - const runNodeLogic = () => { - const inputEdge = edges.find((e) => e.target === id); - if (!inputEdge) { - valueRef.current = "Line Does Not Exist"; - setResult("Line Does Not Exist"); - window.BorealisValueBus[id] = "Line Does Not Exist"; - return; - } - - const upstreamValue = window.BorealisValueBus[inputEdge.source]; - if (!Array.isArray(upstreamValue)) { - valueRef.current = "Line Does Not Exist"; - setResult("Line Does Not Exist"); - window.BorealisValueBus[id] = "Line Does Not Exist"; - return; - } - - const index = Math.max(0, lineNumber - 1); // 1-based to 0-based - const selected = upstreamValue[index] ?? "Line Does Not Exist"; - - if (selected !== valueRef.current) { - valueRef.current = selected; - setResult(selected); - window.BorealisValueBus[id] = selected; - } - }; - - intervalId = setInterval(runNodeLogic, currentRate); - - // Monitor update rate live - const monitor = setInterval(() => { - const newRate = window.BorealisUpdateRate; - if (newRate !== currentRate) { - clearInterval(intervalId); - currentRate = newRate; - intervalId = setInterval(runNodeLogic, currentRate); - } - }, 300); - - return () => { - clearInterval(intervalId); - clearInterval(monitor); - }; - }, [id, edges, lineNumber]); - - return ( -
    - -
    - {data?.label || "Array Index Extractor"} -
    -
    -
    - Output a specific line from an upstream array. -
    -
    - Line Number: {lineNumber} -
    - - -
    - -
    - ); -}; - -// ---- Node Registration Object with Sidebar Config & Markdown Docs ---- -export default { - type: "ArrayIndexExtractor", - label: "Array Index Extractor", - description: ` -Outputs a specific line from an upstream array, such as the result of OCR multi-line extraction. - -- Specify the **line number** (1 = first line) -- Outputs the value at that index if present -- If index is out of bounds, outputs "Line Does Not Exist" -`.trim(), - content: "Output a Specific Array Index's Value", - component: ArrayIndexExtractorNode, - config: [ - { - key: "lineNumber", - label: "Line Number (1 = First Line)", - type: "text", - defaultValue: "1" - } - ], - usage_documentation: ` -### Array Index Extractor Node - -This node allows you to extract a specific line or item from an upstream array value. - -**Typical Use:** -- Used after OCR or any node that outputs an array of lines or items. -- Set the **Line Number** (1-based, so "1" = first line). - -**Behavior:** -- If the line exists, outputs the value at that position. -- If not, outputs: \`Line Does Not Exist\`. - -**Input:** -- Connect an upstream node that outputs an array (such as OCR Text Extraction). - -**Sidebar Config:** -- Set the desired line number from the configuration sidebar for live updates. - ---- -`.trim() -}; diff --git a/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Display.jsx b/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Display.jsx deleted file mode 100644 index a6c5548c..00000000 --- a/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Display.jsx +++ /dev/null @@ -1,179 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Display.jsx - -import React, { useEffect, useState, useRef, useCallback } from "react"; -import { Handle, Position, useReactFlow, useStore } from "reactflow"; -// For syntax highlighting, ensure prismjs is installed: npm install prismjs -import Prism from "prismjs"; -import "prismjs/components/prism-json"; -import "prismjs/themes/prism-okaidia.css"; - -const JSONPrettyDisplayNode = ({ id, data }) => { - const { setNodes } = useReactFlow(); - const edges = useStore((state) => state.edges); - const containerRef = useRef(null); - const resizingRef = useRef(false); - const startPosRef = useRef({ x: 0, y: 0 }); - const startDimRef = useRef({ width: 0, height: 0 }); - - const [jsonData, setJsonData] = useState(data?.jsonData || {}); - const initW = parseInt(data?.width || "300", 10); - const initH = parseInt(data?.height || "150", 10); - const [dimensions, setDimensions] = useState({ width: initW, height: initH }); - const jsonRef = useRef(jsonData); - - const persistDimensions = useCallback(() => { - const w = `${Math.round(dimensions.width)}px`; - const h = `${Math.round(dimensions.height)}px`; - setNodes((nds) => - nds.map((n) => - n.id === id - ? { ...n, data: { ...n.data, width: w, height: h } } - : n - ) - ); - }, [dimensions, id, setNodes]); - - useEffect(() => { - const onMouseMove = (e) => { - if (!resizingRef.current) return; - const dx = e.clientX - startPosRef.current.x; - const dy = e.clientY - startPosRef.current.y; - setDimensions({ - width: Math.max(100, startDimRef.current.width + dx), - height: Math.max(60, startDimRef.current.height + dy) - }); - }; - const onMouseUp = () => { - if (resizingRef.current) { - resizingRef.current = false; - persistDimensions(); - } - }; - window.addEventListener("mousemove", onMouseMove); - window.addEventListener("mouseup", onMouseUp); - return () => { - window.removeEventListener("mousemove", onMouseMove); - window.removeEventListener("mouseup", onMouseUp); - }; - }, [persistDimensions]); - - const onResizeMouseDown = (e) => { - e.stopPropagation(); - resizingRef.current = true; - startPosRef.current = { x: e.clientX, y: e.clientY }; - startDimRef.current = { ...dimensions }; - }; - - useEffect(() => { - let rate = window.BorealisUpdateRate; - const tick = () => { - const edge = edges.find((e) => e.target === id); - if (edge && edge.source) { - const upstream = window.BorealisValueBus[edge.source]; - if (typeof upstream === "object") { - if (JSON.stringify(upstream) !== JSON.stringify(jsonRef.current)) { - jsonRef.current = upstream; - setJsonData(upstream); - window.BorealisValueBus[id] = upstream; - setNodes((nds) => - nds.map((n) => - n.id === id ? { ...n, data: { ...n.data, jsonData: upstream } } : n - ) - ); - } - } - } else { - window.BorealisValueBus[id] = jsonRef.current; - } - }; - const iv = setInterval(tick, rate); - const monitor = setInterval(() => { - if (window.BorealisUpdateRate !== rate) { - clearInterval(iv); - clearInterval(monitor); - } - }, 200); - return () => { clearInterval(iv); clearInterval(monitor); }; - }, [id, edges, setNodes]); - - // Generate highlighted HTML - const pretty = JSON.stringify(jsonData, null, 2); - const highlighted = Prism.highlight(pretty, Prism.languages.json, "json"); - - return ( -
    - - - -
    Display JSON Data
    -
    -
    - Display prettified JSON from upstream. -
    -
    -
    -        
    -
    - -
    -
    - ); -}; - -export default { - type: "Node_JSON_Pretty_Display", - label: "Display JSON Data", - description: "Display upstream JSON object as prettified JSON with syntax highlighting.", - content: "Display prettified multi-line JSON from upstream node.", - component: JSONPrettyDisplayNode -}; diff --git a/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Value_Extractor.jsx b/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Value_Extractor.jsx deleted file mode 100644 index 7e581f95..00000000 --- a/Data/Server/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Value_Extractor.jsx +++ /dev/null @@ -1,132 +0,0 @@ -////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/nodes/Data Analysis & Manipulation/Node_JSON_Value_Extractor.jsx - -import React, { useState, useEffect } from "react"; -import { Handle, Position, useReactFlow } from "reactflow"; - -const JSONValueExtractorNode = ({ id, data }) => { - const { setNodes, getEdges } = useReactFlow(); - const [keyName, setKeyName] = useState(data?.keyName || ""); - const [value, setValue] = useState(data?.result || ""); - - const handleKeyChange = (e) => { - const newKey = e.target.value; - setKeyName(newKey); - setNodes((nds) => - nds.map((n) => - n.id === id - ? { ...n, data: { ...n.data, keyName: newKey } } - : n - ) - ); - }; - - useEffect(() => { - let currentRate = window.BorealisUpdateRate; - let intervalId; - - const runNodeLogic = () => { - const edges = getEdges(); - const incoming = edges.filter((e) => e.target === id); - const sourceId = incoming[0]?.source; - let newValue = "Key Not Found"; - - if (sourceId && window.BorealisValueBus[sourceId] !== undefined) { - let upstream = window.BorealisValueBus[sourceId]; - if (upstream && typeof upstream === "object" && keyName) { - const pathSegments = keyName.split("."); - let nodeVal = upstream; - for (let segment of pathSegments) { - if ( - nodeVal != null && - (typeof nodeVal === "object" || Array.isArray(nodeVal)) && - segment in nodeVal - ) { - nodeVal = nodeVal[segment]; - } else { - nodeVal = undefined; - break; - } - } - if (nodeVal !== undefined) { - newValue = String(nodeVal); - } - } - } - - if (newValue !== value) { - setValue(newValue); - window.BorealisValueBus[id] = newValue; - setNodes((nds) => - nds.map((n) => - n.id === id - ? { ...n, data: { ...n.data, result: newValue } } - : n - ) - ); - } - }; - - runNodeLogic(); - intervalId = setInterval(runNodeLogic, currentRate); - - const monitor = setInterval(() => { - const newRate = window.BorealisUpdateRate; - if (newRate !== currentRate) { - clearInterval(intervalId); - currentRate = newRate; - intervalId = setInterval(runNodeLogic, currentRate); - } - }, 250); - - return () => { - clearInterval(intervalId); - clearInterval(monitor); - }; - }, [keyName, id, setNodes, getEdges, value]); - - return ( -
    -
    JSON Value Extractor
    -
    - - - -