From da37098d91556e90cdc1f3a60c6df970f9c48cfd Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Sat, 1 Nov 2025 03:58:43 -0600 Subject: [PATCH] Removed Legacy Server Codebase --- Data/Engine/CODE_MIGRATION_TRACKER.md | 51 - 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 ----------------- .../Screenshot 2025-10-17 052008.png | Bin .../borealis_assembly_editor.png | Bin .../borealis_assembly_list.png | Bin .../borealis_device_details.png | Bin .../borealis_device_list.png | Bin .../borealis_flow_editor.png | Bin .../borealis_powershell_screenshot.png | Bin .../borealis_scheduled_job_history.png | Bin readme.md | 12 +- 106 files changed, 6 insertions(+), 36891 deletions(-) delete mode 100644 Data/Engine/CODE_MIGRATION_TRACKER.md 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 rename {Data/Repository_Resources => Docs/images/repo_screenshots}/Screenshot 2025-10-17 052008.png (100%) rename {Data/Repository_Resources => Docs/images/repo_screenshots}/borealis_assembly_editor.png (100%) rename {Data/Repository_Resources => Docs/images/repo_screenshots}/borealis_assembly_list.png (100%) rename {Data/Repository_Resources => Docs/images/repo_screenshots}/borealis_device_details.png (100%) rename {Data/Repository_Resources => Docs/images/repo_screenshots}/borealis_device_list.png (100%) rename {Data/Repository_Resources => Docs/images/repo_screenshots}/borealis_flow_editor.png (100%) rename {Data/Repository_Resources => Docs/images/repo_screenshots}/borealis_powershell_screenshot.png (100%) rename {Data/Repository_Resources => Docs/images/repo_screenshots}/borealis_scheduled_job_history.png (100%) diff --git a/Data/Engine/CODE_MIGRATION_TRACKER.md b/Data/Engine/CODE_MIGRATION_TRACKER.md deleted file mode 100644 index 8a2fceb1..00000000 --- a/Data/Engine/CODE_MIGRATION_TRACKER.md +++ /dev/null @@ -1,51 +0,0 @@ -# Migration Prompt -You are working in the Borealis Automation Platform repo (root: ). The legacy runtime lives under Data/Server/server.py. Your objective is to introduce a new Engine runtime under Data/Engine that will progressively take over responsibilities (API first, then WebUI, then WebSocket). Execute the migration in the stages seen below (be sure to not overstep stages, we only want to work on one stage at a time, until I give approval to move onto the next stage): - -Everytime you do work, you indicate the current stage you are on by writing to the file in /Data/Engine/CODE_MIGRATION_TRACKER.md, inside of this file, you will keep an up-to-date ledger of the overall task list seen below, as well as the current stage you are on, and what task within that stage you are working on. You will keep this file up-to-date at all times whenever you make progress, and you will reference this file whenever making changes in case you forget where you were last at in the codebase migration work. You will never make modifications to the "# Migration Prompt" section, only the "# Borealis Engine Migration Tracker" section. - -Lastly, everytime that you complete a stage, you will create a pull request named "Stage - Implemented" I will merge your pull request associated with that stage into the "main" branch of the codebase, then I will create a new gpt-5-codex conversation to keep teh conversation fresh and relevant, instructing the agent to work from the next stage in-line, and I expect the Codex agent to read the aforementioned /Data/Engine/CODE_MIGRATION_TRACKER.md to understand what it has already done thus far, and what it needs to work on next. Every time that I start the new conversation, I will instruct gpt-5-codex to read /Data/Engine/CODE_MIGRATION_TRACKER.md to understand it's tasks to determine what to do. - - -# Borealis Engine Migration Tracker -## Task Ledger -- [x] **Stage 1 — Establish the Engine skeleton and bootstrapper** - - [x] Add Data/Engine/__init__.py plus service subpackages with placeholder modules and docstrings. - - [x] Scaffold Data/Engine/server.py with the create_app(config) factory and stub service registration hooks. - - [x] Return a shared context object containing handles such as the database path, logger, and scheduler. - - [x] Update project tooling so the Engine runtime can be launched alongside the legacy path. -- [x] **Stage 2 — Port configuration and dependency loading into the Engine factory** - - [x] Extract configuration loading logic from Data/Server/server.py into Data/Engine/config.py helpers. - - [x] Verify context parity between Engine and legacy startup. - - [x] Initialize logging to Logs/Server/server.log when Engine mode is active. - - [x] Document Engine launch paths and configuration requirements in module docstrings. -- [x] **Stage 3 — Introduce API blueprints and service adapters** - - [x] Create domain-focused API blueprints and register_api entry point. - - [x] Mirror route behaviour from the legacy server via service adapters. - - [x] Add configuration toggles for enabling API groups incrementally. -- [x] **Stage 4 — Build unit and smoke tests for Engine APIs** - - [x] Add pytest modules under Data/Engine/Unit_Tests exercising API blueprints. - - [x] Provide fixtures that mirror the legacy SQLite schema and seed data. - - [x] Assert HTTP status codes, payloads, and side effects for parity. - - [x] Integrate Engine API tests into CI/local workflows. -- [x] **Stage 5 — Bridge the legacy server to Engine APIs** - - [x] Delegate API blueprint registration to the Engine factory from the legacy server. - - [x] Replace legacy API routes with Engine-provided blueprints gated by a flag. - - [x] Emit transitional logging when Engine handles requests. -- [ ] **Stage 6 — Plan WebUI migration** - - [x] Move static/template handling into Data/Engine/services/WebUI. - - [x] Ensure that data from /Data/Server/WebUI is copied into /Engine/web-interface during engine Deployment via Borealis.ps1 - - [x] Preserve TLS-aware URL generation and caching. - - [ ] Add migration switch in the legacy server for WebUI delegation. - - [x] Extend tests to cover critical WebUI routes. - - [ ] Port device API endpoints into Engine services (device + admin coverage in progress). - - [x] Move authentication/token stack onto Engine services without legacy fallbacks. - - [x] Port enrollment request/poll flows to Engine services and drop legacy imports. -- [ ] **Stage 7 — Plan WebSocket migration** - - [ ] Extract Socket.IO handlers into Data/Engine/services/WebSocket. - - [ ] Provide register_realtime hook for the Engine factory. - - [ ] Add integration tests or smoke checks for key events. - - [ ] Update legacy server to consume Engine WebSocket registration. - -## Current Status -- **Stage:** Stage 6 — Plan WebUI migration -- **Active Task:** Continue Stage 6 device/admin API migration (focus on remaining device and admin endpoints now that auth, token, and enrollment paths are Engine-native). 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 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 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 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

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

j=IqW~PZpYs)LU(mVyY)OW|HswtK?mAA$}5*I{f zge{QvYE`$y5NFl842AOo?;Q#N69`m+oT+Tr>A)y_pk-LIAvm!DFRucqH;EiE>ImQ= z-ip1L7@81r2r9lnn{$k}wsBW~gdctL|N1E4Fz64Y0wn_(4789+~=`iWW#xc|5_Z}{>xorrBAt|x(L0R2RhDZb@&`?ZbnK~r} zydY((u0M6=>Ml9H|yu7Hzu_O$!{N_I}`g*jiswH>|0ZmOdI~L>zUUl|u0!`G7ndJJp)X80{#=zl=_zt_MEaO-f zaS+QOB=ZiJnb6MKH0*YCFdFKyR^1U1U;DD}eE$o+<)7om&)$xPAH%U?#6a$>0g7jW zCJ72=lcI5o!dXO35O`6j7x>WbQbF)8K|v0uImHP_IO!bDAi)U`&L)gga5z%Rw{-Of!Xq8i;aIqR@!yFyR24IB4zxAyVBl|9N0Lh(7&M7H%%PnTzxBLKITZ zpEFvwfBvTCVwQ&Lvh%3{U27BT6hKT`razPL+qJGuG|Mf5Y(z8E#-vDkjLYkzY!b1vza|u`! zDjx72P(dORcAg0QB};YtP?$w5NWqvO!D*zpuae@Pw1V3b#x0I;ncyg2%~kZ zLZ*3hjyCjOT?&^TAvSB>34ci|~Rk|MH&!DSV!TDWprG zrF2o`+r<{IL}sS0VFs~;+P@%08=W9?@Ts;?vrOqK0N^5G ztO1&ssX4Ksu|S(6MKj2aMzI4!@U3I4d2Ab>sA3WhJGz8}gU{XFmz(_g8B(4CheY)aaZpHxMRPyWIbIq+Rqy4f7FMaV#pdh$34ahTr!(56a4MR=H7BLOF&l-wf z!-B>{@S-U40s`PupZX+ph5*-WwW%H|< zm4jaQe%2cpd`=OP2;?lZR(}_vf1@v51q26c{Mz;(ga*46ADP~Y?SQY3L2*@-A zK6s#H2n52BK4-KR5efmYameAs#zr(0KQf>}>Q-VfKBLM(Gd^As^rtnFMa;d2<~9hO z6fKw*H~~}=c(L~37UzVZk_oKd4+1FJ-JvV)h0-lQ3Oh%a^)R-dF6uItt$@?IjAFss zoTI3;T2%2xm68f2*L>M`e({$+A9sHC9(X@M0)clxp;ip2nm{#$B7uj4Cx@IB;yj%9 zC`eHxDe|g^V-Il^Ot^wiOM&-QTKKc9jStqYz)cVNeBAut$KuvUJ^{Bp>dCn6k&nhr zkA4EKfAo{^&WAk-e|Y61@S9g1!&}xj@vf|eI~q+)(gaQQ7$ssaiR4zzEQEvvq6A?D zN2wYxyed*Ez=?pJ;$!!m#TWjQZvm1D4f_)$6~VCI)pk~+cB6LAxYV;2H5x6Ne?E(F znqp_DYz_%+Hrq5SYtz`_@8|p+Ubfli+nW1+l%t9ZK)s$(edZ;NXi%25sIkMsna#*q z)%TFIo4uFXUY#_<_0`Xl3v5%PwMLD(@!V;Fv#xvR|Gqu9gWo9-*a>l*M=PKFlxsi; z6dtA^i>Lt!L0Pb(q1YrUI%#mU6jL8T`&q?W?!NB>A2=t$)$4Y2I1H_?hZipmzihyC zF#^8*I{qCMdb7%n^KN#jA+kqFVYyhFg=y25d1aNEEaTi9Iwvr^K+i}E!DN!dr4?vG zU=ou8GN{ympvWl>Y7-jNAP~EumMRj0R&#CPb06d#GF|xdeNuYA6QRK-C~N;xatchwD&l;!@Hz5Y`3QwF?*%se-9M;ur)*k_coB)YA$%os5yN z&aRd7dfFF4-RP+7Kt+VhpnnJ1UCej-nYY1&{pfNARRuKZ2*rB4xYW{@Zf=Mf`9VuxAqAI6)9KO z+ti1mGZ<;JRxiibsUTs66OVSIL2J#7)|wer!e-Ot>828OYt0r-(JcE+zt_=rmeE=W z;Cj7|I@>b?00uKiX)9~ee0HEW=xIC4sNJZ~Ja_wkv)-UVuTv^ck|?rzgB%f3NEtLW zL!l^#-8OJ;2BkkEXEmHzSld}6+*gD8y{`6qoieB$^}4#A)pwkW{eDN+ni;v!n!VX* zQafvss9@0D*1_C-CnRA5aBg6|lhf|Z1;_D+{q!~CrrEKD!u47$(2K0NgaC*q6l{TT zkaPjU;-&FKVyQ&W&RWriN9ocE0L4GN|NW;9;vJ7V=vX#j+Ao+m3KV6(j(_JB(_Ta# z_2p`Z<(@Ax4u=gy5Bo0c-8k&rIdm~N8i!fXu#V8L{a*=62uVqkP?$_# zS>PAyO*F8S6$ovM4e3xZ4Qf#nD+mjnQ9FyEv=P9yShy0wV)6lK3ltRVHXgbi+b3e& zZUBoC07ilw#0I_y1bLoAz3MQZsD|g&pIOHTmvO*6Z%YTzt*lA=p}VuY*rV88%uf1M z?2>2Oj*hoY_jyvlqkU}i#V>g&K6%F}L1nMM+h_ zt^%3>XLI0vzQD)F4)?De!^Wc@frr!t)j$73-14n2f7OSc{`r-Ec+OMnpZ(hBx*J~d zCGKNicCGu+bDvVb^_ySvwDJG^Ej&IM;lYo%3TIkt_zV+n%Bncw8mMss$_wOb%~v>b z$H<4ES->6u6(F?|0Hp-%C#X&e+;i7y{L}CJo<|_9;cUO>vs~Es$A($+wDzkv=qpsv z>G!oW80dDduj;{Gfp()wPz9^?q-$pl(ix}G?e}!885SENY@d_$pmNT%8x3mJGS_Of zNL9N|+q(s>0*-BERRx`XPj}1OboyHYoa=NJ zu~ybBTa)i4-xOseH;x@AQKiQ{`mqmD0)~?uE@*~&AwUW^4+J7V!fH0Heh(u+AfOPa zGMudzp<A}O~F9} z>1FPF(ZJ)V=s2r7ZhT*6?|qqLUkE-qEpUe20{KgQqjS|&SEfmlfGB~pojO=K1d~A@ zI=CKV>qZpSis?lJx(W$=+*@gVkqJm+h_=6zIk=ytbJO#sf-ctc`c?k$}GBm{V9NF@PI6{Cbu z5k=@Rh!YO@fcLl)6|6n%IIi_MKK0wbiaXx)rff?b^4}AQd>;K;AFaMV>e$c=9@pnG{pZ_!5Te%uH-G34Zr#OZom1)=c5S%V@ECPp<)aJt*WBcA+3Jo?4o{CmXw%~w{^cB7eW`Md(hQ;FzCt4)Lc zG%%~T+e9u=g@DL-fA!{?4tv{SQEJn4*9~mv67>{0KRk%`U89$>vI-|}3m4KQEQfGOETR!vYcVVVXxzp>N zH>SJUqMPHr?)xAc_(jg!%v!Xnw13uW(`?pl(c|v+xr1KUQKak-)JnbC+P4?L;dJC% zH2)6pOf<3Ru}xC*=-0YMp66kF$A!9&oor)c<1vH50M0oSLg6_C9eD^u2uKiuzMzY& zG42c;UL_P#APKs}x88Q^2Z^BDovkCer=4n|3F_0NT7@J=eXy2*rIrN+F1I51(I5w^ z#DR7JK#aIS95xNG3^riAW>!o!o&zV`sf4C7AvOs`i_tGDsML|Rx<3kqh(>*ho|%-4 znn4UF)8awcZInScR4hv=9F7A(87u_PCd;Rypc+v_8jLhiA|@4UqIDtYVHeUESbUdy zRJ-lH5R(g4P((~<*Xz`()u?~+giHYaq8ESFW44AvkROAUOiwwy#R?69hKaZfC16gC zxx|8$2~seecQ{i`as7CN2e^Xt*i~rVc@lsBbzkms1Mqrq=uy4@>#0^ZZ{RDZhp_HSAV(*K6j1ol0C$|~;H$s)yT1wKP~?-`RqGg!2U-LypweuT z!n#n^vnJJ=EgE(@#tC66i)Mzs?J~OT_d43^ZkKbWG84GgW}fRTe|WTyBX0zx8vul; ztR*ReU#m4*H0*4v_eL$KHJYShM?5O8apwi8(Q4DM-_>M>wL7~3i1*}_kTqKrJ95>U zEt(CAYmJNsVH|`)+RF}xe!m-*f^+R+C?lLbeM*T*U;4FQ3lO0wJeVB73|7D~g-URm zYD)#3^FZdXdhj5nWYs7P?>#t4kx$0xkA~+-*zFfQ?}vo$*Jgbdbw0|DmnEt)caHrK z;Q4@PKg!zwZAK#df9Qag?GQX?&DNd)qJ4vj%`w-z8vDYm+#rH>mo6nsvu!k zX6a(l<8(%c1uNhv6!euzDKIf41i)fJa8U0cRr9kHHJ=gMna^YbLtt(+i}3r{cqZ`3 zW5@2;2{8cRA)YbXqmL-G`cTMdBuQ(kV*G0j{VK{9iq2b*?mWB>Ov0PJkdPDAb5^|B zF}~FF=a)4GOtE1%i#NMhvB9!7KS}r6Ved3qSIv)XHrv$ecC<4bDp?*0+Gye7PkcPi zZfzqaLnuU?Ri!e)#oe3_8Kwe&7GNzfAqS@gz&Vmi757en&n6irS3Ve5-~U-e*jXg{rc02rnjCXH%wX3)t6DJlcu%_OaCR?|x56Yu=f z8@}qfU$A-8jX3s@CO%CGPNW2;0t~^}5fZf&kcM3;NAYwvp?ZP0my}!JvEEp2=j`5ddf=35MN{*3$%8y@E!if@2#SH0XD`~BweO*(dL zlj^k^YPBjhjvc3FCDAOcAgiZXuceyRQ?%+St~hp_n)L+rN{XzSqEW5WW;p)lu}#|C zI2QhWjIP+&bQ^7(PnCeQ&MH>S2REB*ElN{HrJ@-2dwOiGMWbF<2QwRmMkT?qH9KaN zbBx0AVgS#{?i#ljT4bIC^aam+x}iIcAP_)e$4elIowKYg8;a22C+1NhrCLWgqB(j1 zx7>Q$IatkZeAD^&c|Y`V)?dt4#K-T=qRvMZ95l05$gE~mz;aO0K>EknbLvurWY@G>u zg{;9flXVD;9O&2*kOSC-5VsP+)u zOMtyKNc+96wi->Ehu-(zu}@P%JEf)%n=0Jz`|W0P-!4SvTo~PcS9`sVHX1Dw@feK; z>WCOue%`~6UwIX7zoQEehmLo z3;hJ|`@a9@De1kp-jO!y=oNBj!PTPQ+19mM)wPpECRCwTdxJ-vv*o6BEP}KGIo+R( zHkz3mk0w4L`iJYk`OB_&?q7W1;Df%If1;!OidEtSOMTd_~gi%kP@bOdY${#Xk95De3MI0i@-b&bcPynd8QDgpr~0 zoPl&Q>nf&u{L*!R!gXmlmIJg1AB|&EAtDb@$_|Nwg-OP~(9T?HarPa#;PO{Q+Q?c^ z0bH8E70s9Rt90?9pF_J5ScepivHaGOHo48!D=*|EdoixfG8fg|DK#FdLgsjp8YJHXRo=j z)Awg-TttMllB6sOurH|+k?gD?90gEvxJ?3-BE{JH!~pS+#(fS|d$yr4M{6}sG+m2S zg7Dw8Wj8UV9BB;)kL7}4U}94jB0z{iB5>>=3^?_s^=vm+-m}K`;T#cYGO1px5(A(p zBsMa)PRN)^K+ev|%VB=*XA5zlyG!y2XJ7Dx4ANm2!9`^G=8sRrG#Yhfv)v*Bz}5Aq zB~-CM$)%VeM*@MUvsT)$NvW)`j+P8zf>VaTBb|7Rn{C`d4cwD&;$KxZ>{q-~_E5cNJn+YpnaxXq*E;tFcBzg|4+LwB4C#J8Mx@gmV$8WXChFbyTzF z;CMTA4|GnP`RI>--^-u%+kg1mMdBdHFexT*g%SH>i&P77jkhg|(1FO@ky8iw35Zf` z-P6HnBg23A&EJ0c_VwTQ@^}CB-(qm${`<~OCTGrUpZ>t`#F^h6<)f1#<8dvm9jmXk zzo^1Vn;O~YJEFu@=r|-9J4nKnv@N1Jphzi6y%ru`N8~Z~5YI`{g2;r)RVFGE0TrwQ zfOxa0P2oTF>Uj??z(Q@VM@m8=A~BuP?NNs_F2WxdsU zd?jmNh=?D2)m14mLQD7{duTHw@ZfAQ@JirPt%j!3 zcA{Vhs4}9wlyeCH#e3fU-ZNm*&0`xx;!7TTtC5kamPMbdv`fV)FWVSpR&wmHsQLUc z+RY~Q4=7c-j4z%IGMbGB?NrFRsvx$NHEj+BU}urzUTV^LyV+iIV41mbAvX*MwP!u+ zSr&|mWk^}AD4!gGRZwV7nsnB>Wb{eD4giVQxQAgC;3mQVjfxf}8B5_0o5D#Jpf`?bz z9yrIC_=$~=`S)|it(`TfGgC*^&YIM@h!mp3TB&rfjBfI7wpU@zq6>qi6ZJ?J!ucYS zX07FX!aD42%f{Lo<(*D4c>0%q;dOuXSMU7ft@oe~0Tte85JA9PofQj;0tDN&d9}ia zNuv0??R#;$KNri5*@0GyhGV z`88jUe0v+m#$#kG7^s*&H3_OD@PUivV<7_wjuhe*NElK8-Z2WFqrp`iyK)2T^@_c) z?BJNoV2Klkj_rjiR6tCSLP5@WSmMI&yHIFCP)|r$P=pSQKG0qqVlf)5nwn;|#^uXT zNQfXTp?D(T)oM~dg$4?NMS-@P2H;tciskwy`8Xh}Vk#4bfLti{AXq0!Z|8H&Kp~if zj*nUtwv!I6g&7pmg+W&{6nZ2{0#)OVrRt#UL88c^03}GYK&9e9o-iSYv7aESwWP!l zQcT2SQsfvF9z{Nti}Uah5vUiFXi|kr;Qnbu0(>au0|3I{%qa}co0uoQ%LnC+!la>4g@IBoDN^K$!Xr%*Jn6|# zeB!5X`pl=-*EaR+mURW&4Vhh)51O^C(WF)C)34fAS-dri?62y+4|9DNvM_DGb6w0L zxn=Eg2zpm(58VHNw%g4RSO$%G1Zi0ZkQms*RzX)%d(F*Rz{dnk3XeuJ!>N;}@Q|yn zW*>^yR%P=sLwruHR!ZG&u1ss$zy0p(dKf+uqq7dEY(Y^0k%LEJ#gUOby4`l3Kdxhn`_5$;>0%I^tUfy%{71mYt-#;@A^CzVGLZVmH^8&9ofqk z;WF>fGdlW#oZod4L!xp7so}Nh@nc^lkRX8W;haF#K^+4HAX*qdivScc;4`8>iGve@ zSH@O8MEh73Xa4XnK-*hzS*sW)X;N#oi&3|uYxRZ`mk2QB-p1hB_^>H zK}>fKKuAS}G#_A_01h!a)Pi4PLK1jlA`U{PgXsWxZvl83xM(B{bpp=}@`^O3y+R=c zlpxMB42e@iL`tW{P(*nm1Sd|$F;m-@GXpufvY%E(9Ed!M!ejySLQwdzp%Y}hKcWN# zk08rzoa7k^B0{)d#1h0vf^l{h3Ue`}%d*YmsBauY{mcX9oZ0E!d;V8^xuNu8lnj!P zs|b#i0^(t*Q)en1fd-_+5c1}P)nt>3+#{`|U~;(W#!uhqm?#F%!0yQGyboGNWeXFUCD-us^S zo`X@A9AS=)KnO%0Udghmn9>M95n;AMW>(;qn{R<=DRO)$+Y6~KpHoS1phUb=QYGam zJ*PmRSHyxn%iJOI8;lzb1n|QEt+MSmILP?L-i%@W32UJ(RSquHGpX@lLOn{LheW#a zOt6hOIJQ^@;)j&CV#_R*&myir8Z+)Wl*l;WLZE~&nT!DkH0v2Tu5iH1c6}a~ zYw!%r=|CS<;l^y^l}phq zCln|GAJhVFPYy~cI_lAHRPgAg!;NqG?+?z%Ij6$n^MZtFtyXnJg>-uzRhg!nqE7Fi z*VWhp69C%vhHKYq4gd=FU{RzgfIDuyzH`s5XtXorQXsr@G2()d5&Vff42uqwD2ig} zu5<*B7+eTy38Tr1mLp_NQI`TWEl|}7>JT&-XgEQWfTk#FC{Tr>S_z}cK+^$D zQq)A?bb^WsQV8lSXgQ#sC@NZ@S zgV^P+P_Ump<(g~X0$BD<0&o#Qf=GFY1%zcM)R<)>1uKD>Lk85qhtGWaCIE`f&11Wc z-Co-8v+vJd?_BIwJ5^L7BwVFf;0FT4n-8{Z<&+0JtAs%zWlEv9H^s;}1mC8+Z<$ge z7m({9L8m(9hzF3vkZOV#21yt*Oc8F85S%N4LgD2Uh1OD3!`70(H`ma8kc~p)Lgv=a zXW0T#f+r863G(qcP_I3NR3rJj-E30pGH%{=$6{!LP2-q>soIQ*F-FKZjM!u@v z7f9W>pj>MJd9_C{J4Vy;0PA`;R>r8`)0|mc;GyQ*_W3Jn)mncv8hzuh{BJz+@lV4E zM<8!$o&;*$ff(g3m=O>~wHGgz9}pOMK-@_Qwebj!1xNtz8ENQJCuL!Rw|sp-fs=%c zHLKpFd0@35mU&}TB5wmg0)l4(bP^C}4=wD?=u*K)jkH7zqcbfA=%*0L~a`n{!DJZdP69B9PuWIdR$%Qm*pnk`egDSuuzSfiu!x%(l^^L}PoZu7jwd*4Zmconvr4~_S@o6kIo zqPIoHYEimLY^<~DHR}B0_~nj&l;~NnfB1VoijC2#?3!z?SqCf-7Fc#JcHp!6JDmoo zA)t2gvjK6{kj<=ADgX$d_{0qWP-gOWvc0t77mYz;11@D{gk&x#fnkybM68gn)NKwl z3L@haNB(lmtlGwb&9|08HN`aN45vA|`mi3U1;tkLgh05^fLB}ON>N5mLq>5ncxIf# zsgC;>L&`a1)u#wFkr*dhVg^(JFcKDPDaM4saN8akGm2atRmf+}77kj6aVgt@T6rIZ zEz5acl_RaU+hj$O0v5sP?JW-gQWX8X5U7td1N*!xK|%$vrM|rtP$|Ky@nlH^1)C`R z@r`xd@zEQBqCiq%Ky#mOZEN1?t1?SxuFxiHG-F-fN=`JGb1NL5uBPR z;F(PuG9hap?imwd6ash!CP6|Eu*Ne=3NXOrkh^e_@X~u?C^@K0A(R-#%7j801rigR zcZ?zwHwXis1i1OwiK?09klWA#34{a zXj)u{)Cx$!_8DTUTqdKfniU#RA zo0d!=y(fSJgE?+I2#)NNZ-4tc0076YxPp6gQh-ah(LO?*^gU;2G0QSq$fSOq%ttYlDPo7sDdS~ zA0hB0f-+Avk%QkNC8TKruZnlS>m4rzfK#VVEq&hynKO=((D$!$B~b+;QKg83m`J#X z7(fVzy}%TVk`NdJGA%F+pn<^FTn(H-H7&d;FPvbS*|GIW?C-4zoMsI~XH(-Mgdm#j zcOp9Flo)rzvXJT!!()mPArYIAfNN7MPqa1B!-EsSXxxJiReOVhI);ZQcPTOtR$)sA z&=)(cWV)}b)3=NYEB#?#nM{6#0EcmLCeL$N^TAN8;QW-68rycOy#UGzR!GtRqCJwnvktYczF2Oiq zj9dl7w1QE^Vc;AJmtw>zCeERsFbXnG@x(Fm#9=~qZIdLi-;qPk4ijb!U5b%QFk!;T zF$(7}aTSbRiZL<9u7WWtm{4lpPf|?SVU#2oBf-cK#$b%uAx|AV6LPMhK#GY=FyRyh z+wlu>7(0iYlK+prHxIKctE$A;-sjvm-TPidW>yx(Pz4mDfQ+K8I14C<3Wy>NDB6Nx zJEFGJuT>PZwMDemc0z2hnVe`_aX>^t1ZlC5p%{d!qUNfs%#7j9_nx!&?~i@%iSpwE8J zvp)?GBm`vMTl-GeYIMi7bdn8`pmu3wE4CFd>)7-dQGxRwDvCG1`44^=#Hw?{JR8}# z9yP7ngG%%>HHs}wqK?LUUtc7j+r~b>z`dBJA)6J1>p!>o|L=uLGOq={U0iGvm5aS5 z?=>Fr*$)R06S`_)m;%Ao+VZs4cdLitBx%yu39pJJe6tCN;Nu_r_*)%Vd-=ky&&g)@ ze$non_gr=u^Is))~&A9ugEBFN@4(LI@ktkaRNnZ<-Ro= zcT>=+#iEJLOvZjlsatFEqM+n}XievY6sIRl6(7%PX4{O+h<2K0S`yV$6-EV_>FXtJ z=mfkGG-*xS>nkZjHJUq9qGYDDJBq`iY8_VWx`5tpN4x%JKIjwD9}e^&6*?w^Rc$A? zqQQ8YFhKy*^7g3;CJn`ulA~M%G?+p`R531S>c4PuQc_X`CQKU*6rsXZ9+-Ue!*7EG z^^SWXo~sTskhqlp5*bg&|%#0|$Dq0)BIz<4}D!$&w0s7vDP zF{ai$L*Y`voCd}czsv(r4PbC`MiL;hA-@oCLSdNzga~5e5i=rc0d}a^O()4dln96d zfkPBUjP*c-?TjVqfsm$~Sp*ya=TlN4<9B&7ih$8hk_!?WG%5n)zfp3;BLYR4As8M= z0`HOvO)*6YB=zWM$%TWML>pN`Au@#(QacAKYMfpLpb_d4Udyu?OiPn>|4WlolK?>p z>f+kYzaF})trInZ#Aq*0#W>As_U~3CC$}iWsS^^DWP~Vn5>o3;>%Sv&?7HpaADmrB z8{LP`d)_zS7XU&O>i|&wB(^~5TpN+*$FpTE`PXJWD(SN%oBw(DKfLP~0L7UzXJ^;% zMmDZTMRxA6V(Y=`qw(I?7s=Zl*K8ns6F%c^3d)^wi1$+@?v{45+t=z32fADC^hUAQ z4b@RbnKz3EKlnlSJ-&F{@(u_#mS*d$4X>qqtegf43AL3TX^nuIB%Hmn!P8%K(Y#-LU@XcDYJJKJC5ml{%i;1rJYY-%si3E4}bUMKqToLkZ2@e@Y( zA5K10CA?YTlGKc;H&C#Pk(G_FC4fXz=?D=}&j^`AJ60$>K+C8877}P$$!T#UbxVDr zSfD&#ij+oA2|eJqd0v6>1XN)LU&128(fj8HK|lQ0N7sBl6u1uPLP$wf|#>uklB zOL946yL%Qm1hnE9O)24HKr2qr2otmF^wL(?}w5oun zRA33n$_OU`S%_$8L>2^IZG(_SLRO{6XhbFvF4oT#z%ihy0Zs*8B3e=5Lja5Yjz!?q zj)kQ(05%4Y20$VpfRhp~Dx5_47~rCy5fu*hoIX@w1URYC2o<~pc-UYf*M)N`?2S?* zv*cC}bwmOp)wlpq>wB846U`_cy5@6>8$a-#*VNa_M)%$N;z0Q9d)ZK3YKUDOk9fo*-a<)|6sQqUO+bvTwJ{AR(_ArGn~`Wu zHGsmq3qX|-kTi)CqoPup=?wPyZH^x zjmS{KW5V8O7OsVf^G5A(4pYuBfzc|29VV2a@`iSo86h}_$^jE*l;kjRj0r2I#Hhds zM2G}bWIjfL9Af%;#e_;dDh4KOJ)qz`g4*|E1xhjv3T+q^ECf_ch|W3z5sHWaNy8En z1S+OpB4nNdP%Z zI2=Zg#VEQC3P`Ds{h&4l-8`qf=x#|1Tz~%7 zZ+><&NVwZ`zg@<6C6g-*D#n6nr%(L?#HK38W7%FK1knHgqlT~;c_9j-VOGu1TF?&`qxmN(JNpddP ze3OGIW{6?R0-3EYny{I=Sf^UcIu+Wa0^$lp0%9N|!Emw)RBPu;B@xB%hOy%UdZ!6% zs|nY`h~2(kC-0s&b>HgrxIZ=&_|)=h3{e&RmD6Q^j4=TNVKm7h69v>kga9XSP7`tq zP)s7{YCPnjckmK%c_sM5Cq3~?0EJeOozJi`u$B;$pg$Vw6fFS2%5bd9!;zLg)77C- zH9C2hqBkw*)!{&E)TdDtRF$G*K>x$ne8Y#DE`x?C5KBZya1;Tj5TBVU2W+^nipjHH z6)FNp3RD1>!3sLitS|x@x|qda$Z2WlDyt24KqxsfA|VF?7wvX+Vsr|s6i#3$YqTA% zBL%0b(Wo$1m|17Y5(mSYdaQt_RKzI&79@WZqcV`uHDcN%RP0a+p;U9v3uMMFV*q2H zVVZeVY-T2g3uma9q0S*Xhe(V-4wWO6WW$V+9V*Ah*(OHC4SNpu2${o}9H!(j;U-4p zG4>9lMgt??M9GYi&oJ~YjG9dhGLNCpP5HBW+*|Wd_$EMxtua znpS9yS0}0L#D^wK2WJpQgYo=zJ{V6&vu` z4=T1Q@akhfsL!K69O`h;-;@@((fiyF+PvFyzy9;34IW*uXFH{y90gtMuY1PV0W{C2 z#jKk(1EaN`I7;*|07zQ@Ht1}s7g?tXQsB?u`WK(QQK2@!=@@LnZQqp~p%O`=W*ZF; zC_0@n69HO6**p(aOr?vMz?29PnmWL!InkzcQ5$2C*7<&ILm`Rx%&3_dMk^!`*xAtb zY?j;}W&dgxMNOh0Mk>V<;yrsqNwp+xQR-rxQh}{z_QGimO3#j%hSoX#o|vIdF)b&v z%CghyGI0!}x$|D%--jsyd7)y*1$2Kn)T1)=vY!}jko+d2)!BVlbUNg^ZNk|*-h0Pg zcOqsjti}L%2XKUPgp%=??IMpR^)=IPG|p;Z2C4u#eFvWKgN;Ji3#OVv~H2i^nvGw8ZcK3 zi^&!#Q8HmkFP-!+lf*n?styATA+Fd8?s_6QMkM5x(oUICA01KJQKzJ2d}Wuo(To72 zM}(wD8f#`(LO;N!xDnHNQOaifb0y=$YZ_t{R;V)_@B|mk_}P(-NJE-9^l%VOk3o|0 zOy@Fn#?nS6=`WKd-kR&P#(|#uxTyZb6sK>zeh!vii!8Hi_nV*l{Qmo@Isf~If*g3ER{_gMJ{w)A-{KWA?&lg)+@@^N0)h*i+WpoFN z0A7fkywMO|ExWnVVCa71pbET$I5!(fu-2d7Ml_tm+*klxZrFuZG^qre(g1u+ir_ z-}z9iX&W1tnW@=q8u$_zDgml)jf!k?8wR{V4aus-2Wpg<>Jgjm@Vypj=%&_30bX z$-7tqVU%pEm106AZTD*;1B56>s3$nEI0RJ`)IeC8qIdEnp7LGa9j1UT=DC{;Rs;Yl zZ9qiPj@3EIel%E?UT%}Wo$k3Ur!4({x!>0iN_zNLKK-qo2Rt0Nt}dY&1Dc^imF(mk zlB1qYG$gKS>cymK25AtSOEdx0t+D#fx8kfKhpF?ZoJU25?8ekeQ;-#*NN6trafFhs z=!;CKKr^&Ak|oKY1YwtuapP7+MbdQ!0;TjwJky@Es*#P#6|9wI!*~QI0e~P&d}fjW zk3_SG#xu6TzyMMOQw6W}hCo4xGfo|5XQ1Y=tx?aQ3J)7pbg-W@3mQStj1dhHWFlyV zfR03DF`$JCjS$g@0bT(g?e#KJWU9y@$W-B?t|@EOdkm_jgyxU~O>V8}V-l^GlJr#- zw_khBhgO!)oVS>qN%Bt5dCqeZ^+Ulr8y89FwmmhoCDEELhtxKlVc%?nnT|}x(Ro`0 z-}}Dzy^aWac5Z<1V#QXd++lUgHg+`3X9)R0B7k*HKFmUTIP9MTp3Fo-{UU3o+n9LXjNJ|u_A}vwbNMUn4 zX#hS-1QYPizkAnO0lClE=yRR#e6R`tmzD@|3PQWpyO%_7a&;UbiJD=Qjr3k&8;06Q zacydw5?z9AV_=Cq<>7=j*Tt|;ph^AzIav*@rSG)nVW%faGNUsbB!N$BS>Fn2t^t>1 z$YrGSH2|lkNrK$k6M++Vn!#`ot}w1vP}_J9(|p-lPCU|kyIseT(ShDB4x{bV9}cue z3`l@>(WNK~=LkNO_~gI*bH9CT0jsWs713EM1rTTy1c`8L>Js8=zK}pP0vu7nBO=46 zuDcaq^`C!)H|noyFD27r<{@9Yc{JBY-1=kr@vPHZW)RDnUKq#4NKVq2*C{rUh+5vwwDb5uj{5 zYH^5a#kkN~(N;$-5?`aFrcTL#8X{}joeU70!Uc?PZSOZB?}=PXoSH?owW)HN7OsVK z%#4J2ZJ>>62_cBtdP__L6=~2-typvv;1UX6OU2pW094y?t0v@Nt$oM}xF|+nLE=XP za3X04FTx4XNX5DW;0Um&v6|C4C)nUA&D@>@DkelPDyZj0BGv{71&UZxIp&aMquS8g zR|R5J-1)H&eOvuEHKKCj#AQT8tg3SPJ+4UYItIHnwBA0GdT_+dW|E#%64D32G;`20 z2_{9T0y54JLIq++vWWxXL7-#1;gQZGc87%y?xY|*kB7Vg0Fgj$zxutOqUkV-#rYQN z{PXWd;%%ci3KW8^&h=4{g}b{ATh;M^2V8y6ullO50+PazQf=yC1s;&AL9s==^!UeIBn@sVaDdNTyMo1eUAECFa*gVcZ@$eP+k z{T1merLwxF=aH&`L-rS@NCH@mT7z7Px>4irv!TAEQevuBwReFP=rdn{^qf#Zt@8v< zRyC|M+DN^wTP0FT1Ge~@t`3h1qp z*>;Q5W{c@gr`&7AU^wpUc+l73c&wA9zP12=%X|LuX{84ytrpH!rD-Y$N_-^-k*RRh zH-?KyMUL$>olfDspg*4C4(;H3e&>xJEL}s(;gpXT9p?nv$`^Rz8Zu#zkfsbWr#3lPj8&$_3iT9?rv4X6nKqHArr zdNaNz#-=m0mlFMh(_^Bf0+VVpYc)={0|ajNUPK+ld82Ni=F+8Ml~jakO}o)qw+r>V zHX1{+kR?s_=reC8dkr!Kww_WJvZPc22t+9BuS90zLN%49R?tucFs8&9u)`E&Txytc zjTy$IahV#en5SjbsVG6jHs_}puAIemIzHX)En03&e|F}Kq0di#(pUbk7$d4G!1)Y- zS>h%?E!0N{afv@8CNo&){#Ym5x>G@j$!YT4?|BcBdjN{g+J}F!qU}wF=+|g z8fs7psUX(#8A&#lR1so8v)M$95pR6co9;&mS*U56eqp2-_gKBBn(($;eSn zx2!iSN#0g$ZKGN#1gR^Vj*&D%6J%x7QmZ4ZXpc$0*G`CM#-ti)B`fa+fL_sCV~gHR zgZI-;+bVhP1|^DH)$zLZ(`>ew?uHYYw>vana2Llf&ssncuD|Z4(|6v5&}m|+0#?w# zguS`N5ug;18Z)TJs!WK1fS5t0Mnapo>84NPvmWypeCw-U7YA884u@kD1Wbl255Y;7xZSizU2>nWUt`Okv51#EM7} zE7sH-H(Dg|pK3RE!mPNgFZo)MCpe!9N}J%&gmlh0Td2p|DBXlQ+x;pfZnMOsUzx~b zRHLa#_oVTtkP2f-iM0fv%BWyU9m0l&2iwbF$_@cQ2}VhpicmJ$H%-MX$kaVxIjCZz zoJid z5@Ae)2@?inMfAiGMr6k?WNwa04LWhS*ETsRkJdy3NemHBoTShrKsoX z*gx+D-|{WQj1WpltT%wh-$rVwT9QsfvPc6bTnqlEbEUSmi7_C{8UPVq|NH-YHn=z# zjkM+&@1!`qlsR#AF1%C=%Du*3i_Oit0h?({U#K`}hseFWpLx{uQkALOEWY{q&tId0 zQC%F?@@+8PX8@#Wkeq7dHQ}zRfLKlzRe0|)8BO3^wsy|u&iO%fo$|a(^HfTsXeYE* zDFQ&dS+uJtfW+jVU@KijFd9Zem`RhAUA=xLzTv262oT>RUB0% zR{tjetPWPy@kV2>cc0+BLh41QbIwkEeWD;e7g^qFVcBS~ua5rcU=#F4F&vL({pMCD zCymrt$mnx0UcTwpcl`tMZWDb4R;ht8c_<=;3~mlJUsVBx2a7@xF?I}zCbPy|p)GEnzT@}a63bSPSNa2LH=49SLgngOTeKafv+pwkcSumJ^p(7$l~Bcpe(}@3 z??+$zhky2|<+E@)LMKhg+VCBx9UY# zDH%Xp!DROWAt`wfDkYRmD48*ZS*hDG5eDQib&QIEDKSa~#*Q&&!UPT@;#5epITR*M z(2)3Mwem$!N;=SXY%x{&bxTtK1Qv{^6P*6k$F7gla=1F^>u}K5!Dyt5$4^kB*&!m* zuX);61FT?|8GS<&|63?&fRpi}>-L=*1qOi06#=v19#x|XWsL>|ia-9Nw-oiY+s$)Y z&oeGM`JuE`FLbQ$n$@}RGw9{cVzX|*Zmhj8W^4yM%~WA_@P5vdrklHl4og+0cM+uQ zUi#AS*L&aRJ_%G!)B+-)w(+j*@n-qh@|~$XSe_z7dcY*`?7;wj>(##nkwC|eAGcL( zZYX~*U8nl9t!tLyu&<;3+WMU8$n+lq;9aNL%|hzJ8OjhL%3!l8OXE8<<3-owvuV0$|N7MWQ0O9plgi=n5cWu8E4uA z1nRzW8tzXUAQb}F#AqRqW0av3!T1A>&J2Fq?diXyf`Pqp?*^lD-Fen0iox()X`q8z zSa!N2-|dZ)HeC>A3|H21|giY&dR8z`y?Mzu}Qz@I`p}JN~6y_=2Z?)1)eCx!>20GtchhdB-{L zaJ<{1g+}I34S6z{FcC3)15f@huZZ9I#y@)HE#rvgJD1QZji(fdAel$Z2wsBmR~^6_ z5FwBl0b)#@q9O*V$-M!i%+ zY%_$}j*k?PC>6V@@MoT(-=-6dwSRN z#?&#!;PhV7Af*CRW=s?q!w~8TjN(xO7!yp78??3op|uSL6tfM6Z9Hi9y9$EgppQGR zy#`CyedJ~1Fr8E41eVX9(W)Gi%NjV=>p~>8=Ono@CY0NTbF4!#Cw4k}efvM)(l98z zT}$2(?mBfBmikLtPr`46IuAy}4cn@lQ?V}0Lce?Wx%1!KL7;^PW1MpI<)ID=S=|rf zzaJT`?SZGAFF!jel6Uf%Zgo!AUstOan2UPF75B)#@1-xDafZ!F*5*uV^cAoTyQTte zHtUimxQRTJ1Vn@oBYfte5WM1NUcp4rnxZx@RkxGtY(4l_f3_PL>hy{(K?KaIMUhb` zH2}^noVbUB5TZJ$6DuqU&|uU)p3x!Zn!KDkp9aZW4H5-}+U(4M>%l!(flWyfi|h8S zAk7G+=h50#&=7i3TH|aB!HGgdOvOX%T09lwTFdG=n+GCFUkza>N3Om?%b}P)RJyQl*M9N(ClIS+haSMgv*H5t5m0B{55si>CL^Hyc2s32L^H zHQLCMrM446tSUeR)pU&M>RDa6?dFd!U3cw&UA_6bKkwgu$2kS`$;(a<0Q|&{{iuK# z)6oDvYovCcwJAuF>Iq;A8F6x|T$4Q_sd?lM%r+HYl_k(>;+}446h)6V)7qVngUaJueQv#?psi>@he;Va z=+9uk&v&u;^)G($OUjccE=%Br(F{=2!<-sQP)oKYM0QqtIcIbOHPa^sK`TY0XyBtC z{V0}}&XQ~(UkB{$I&(Xm1nUonns*8sjRx8-x-=eC3IL9dHM{u- zXL@#q{u0?hwrZ_-Wa}mgfjnlK(B}SU>u8M-&$!Bj*>H0fJCS-2Gr15GfSCB# zOh(I#(bP_6ML2BPOSC+YKqPq*3eULKA~uL`yla9+(<8CZg)y3pN??&qpLo|haKooQ zdHuK=uiWF_4|vqW9`k6v>VXf$<@dc8G&XSk?PqZIu4RM_U`KEj;3=AWo+F#kRY253 zIRKHN$z@0xh`t4$2$~oplE-!4;Zw&i!+nYl-uJ$MZWPA*jF2PX-jR5&H0a%R@sa^BfpkZJdY*mR6 zW;fg36IuEtgb=iuHA%cf zQv`$QWcJxF^cHDlb@{xTqPwt2{gq|y_7=z~VY$Do#}-e}(y|qb`NA-w6Nr_iR!D}$jPM1mnA zUS2*E7Z#3VG#bMD29zRLJ?g;5e(l$sZld~gP|+#6wBC^r0GuOQY&_uU-}9p1{gXRyY$NIb z&DIjCauLL)0wqXteQD{crDR2F?UA)vAQGL;xUkgJMgfxt5TRB5CuQ?Ca%1mTDZPldKUF*uKfH^~u3|Cx{<3rcI z>6-uZcfb62)hOM(pa{Zf*q^Om^Y>vVZARwL=T0}^M z<79$UjrQrU`0IDZPha^coVw{Y+`r*)RanKMICPZZ*&$E`iUdfsR*Nv;tnPDc9HgiO z76NMtM~aFGLwLBXh09JBxZ=cR=n*hwMHLkNQn7k^h%>88C}Ra5BHE;I2v85iNJ)U9 z-r<@PiHdQ=BHM8Y1jG>m4fr6aJmIDm@X4{_#KRtfmAAbY*L}xVwvTf^`ihRNB0JNO zs%NPTRefXDTYLv(VhoI00xnY|k&pB@3?Kr*1MP&a<_t6=(6?i?`a1|JB~%P#PLT&- z$PShm9N?6XlcOo1aFAZ4<|>mmIm%*idNaW{rdf8U)7aD~U}y#qBFPxazG62%(;vnY z3tc+1VxR9qkrRus5(zPZ;Q4HS<=jcT6bqf)O`Q`}#WYUR$*Uz)A)6?lU8|kKiuLq* zy}Y1}TC(*ueeC!NT3)ikRz+1bVmhthvkXwvah5d9lJX|%AxcfHygCUJn=YwHg!dWB z=@{+2gF9}!1NXZ3y`8E`k@sjYyf8^{qa=I~vEL%{sK4f3Uu(0+U8)GuVZ?6F#bJH! z^>JK`Yk59?`Bksd7r*4ikR&RvM(O;`d&CZ$4L7CD=D#&LWlZ1J} zl*DGvvy`?hk~EzGKs=OXO;{kA+tkH2!3O=A1sokJVO(+L3H;NwufFDO@Ba12cRMXz z9gcPV#-p3(L{O{_&!qrgs`cZqF?c_9&T5 zf0zUn2ao^_iHKftT0K5BVYrivXrH5+YH8XVvk#9r^ls zgU$Gi^)>C-bhv-%RllMy{hsf|bW)#{QLa_qbBcaw12$5OAsF|)&QrF_TFs7JHw350~I1HEy(P#b2@5T04J`Eqc`A*yu9`}lZMW<+!qJw~TOm@&Nf=U9M!b8EfRRqC7 z)ffp845xtS02YF1M2iSh7BC!2C>l|r9N}Sl)1^8@=K!(Zjw6MrgHnWp8M<&X)u@Qz zgg{iGA`caYG#n;M_&@+oH4EJD$`iQjl`rex@$0WxXtx_!9Zfas6hzLEx(KLhlt{{L z0G#x206-$Zj!2?1ISRg`kSPF=lhRAns994_|gtNrBN6iGj!k z@*Ni@eSn?1tRWOi27@)m2nEzNG{QL-$D+Q?B&7r31b`|cG^z?1?4TOO*#%CC4^C;* ztxOp8S2XV~5UFA?umV<9&Ov$spi}f{G*`&=IvpaWx(EjVEOxsDiHWyneBs(Uy+0i4 zLeZj?fzd>Ior07U4&deCFn05tRINqnRk4y5#l<{#%fn&pc5>RS+gsi#s80I5?gEX* zBS?(2yt*8_-5y4RAsWrhC?>=S$&iFLJ1d}J*OF}r5>HwaKQFcRVhCupI#5+O$3#pt z-%7pM$k$Gymv?FPGs+ovo7fAI)$J72zrgArdC?^z!us{*?iT<2&+pgIdf3CvAZ4cb zR4YDc>eAInt6{SB{N+sg9%&Fy6=kf@$TGAWEj;XzpZ%DRU3=|^HnQ*DNgNCKf|?kmg#l&^MraUB?FeEAI*krb5i7R zlp#WKkNf8M!?(WT?H~R0Up%>J^{78uk#?uYNM>i22I@LFwc~_7{26M zV+=(V2^=!0Q)op{$snnKjbc-{5elXVB&QF z_E&`AlsSXfqU}ZDDF)IISwd_9a4L}%Ac@g)=r<@#J%uR{og%_hm@*`?#^^MZUH}$> z3lUzYBMQ^t5TgrKtYQFH5j>rYyeD-O1IKdQEJO4$MhF~5I48069LFqxLp5TF(Mt%b zF#;NiqN!Gr1ZGuMQHEl&x}shIuL|;vQGdmdnf{8_DCcOfs`JHjE%U?@u#gwjD+=o7 zIVolcHWs=C^@=W;rncvm(MVT@LtP#WbagnGsnDzpCR$&At0VjQ^3ag&{&1*+(LgICg3Y(FPttNf*0r z^QqeP2en}oM+(n|Rg2gHapTRO1`)H0>im7SS@Aq?1-;+cukP|^is#+MVe4EM+vp~5 zCxN(^#dj~o^LDBj#WvUYaM0Ji7SH=p57{gZL%HQ;KlM}k@JBpiMhP6qa#l>Kfh-IGVsMgV0~`Oi zc5xFm^$G_yyG?PZNTIHciAE4JI#7TjCd|mtPWp{v+Kn2JMX`-0fJnhmLK3LBdgBfO zp{W86jLHirlo$wbvncM!3QQIkaXjzf|Go0(U)CH|+*hrNqDQ3zIx2cuv(d^MZpsy! zfx@Jr(xFOGkxm`F15H69o>j8wvav>mL-YbhU_bScrb-Ed_u}FdAh5tiB`f@^*dz!@ zJVb!#P;r0>`HkU-$Oq9WLmdQ>M4b>tQf(NmL5xrC5+7GR1RB*zAaX!}S41Jt0tFU} zoN9#>4(AU9*PVw>w?}JwpM-9^O@r}xcHOjlJ>rz{JKY{Z&f&#v$e~_;{cS{jDmpzH zjMf&@2$D{_OOD-)%20Fpc6zU#i~bUR@m0U{TvY&$;h2s7=ACij)Z`aQ&H1plR2L*0 z^W<0+2s6YQP&Bdz6pCMY<^NDXas2oR8x$#ew3XfSZmgiU%TGr|ce&_vX|PAl`n|+X zk(#~a_pKgtj`n}L#x|l8?6v!_9l&x>&T{)jy0&}lVGcqaMn|@OKL>>XKJIakecC(! z?p;ZfIi_Jiqh*j<2TR1}#5EOzwT+)bz{)nPEDTRbg`Uk_0Kkv@&<|H?d<5+kU0U7X zTHEMndyP78*XJhhWi(u^l+kD`_KtBi35FI6gJCLIPOU2pRY+1#N;GG%DkLOzBT?%d zlW8^^7nzam%KHz1;B7_ z!?@nC=%{xpH;W5T;n=~H<$MC4Z$}??tY0|lWa3ZUqm_lpG#~VHh-STS{lO0&`)~jK z%4=>qi(-5iy3#@?Yv4G>I9UmojMgE^{RiZbIK@z|K~N#ji&u)3um`S^-Zx;PW=31 z4kK!A?76SGK*NMO0|(nC+H5*|KI-f0Vo(~0h>2K5DiHxDu#O33;_Vj5Q-pWNDOE;GN>{-MZwd_Xg0`mDNr{qmDp@RbT_fx=W&pO*+;zvd(@Bb zV#YzAjXw0D4~1h!OapV^q%1)MmfEt4Ee)v}Cl_q0Q8mv2X6{AAD5oWQ-7aEO zWDUnmiV(LhV79xqx4RySPKVZeUI0Me&1f=VoNRygTfXD@ull;>I|Nmw(5!-D3Mbx* zQeklF>LqpOBr#j7SO9BpHSxm%fNL#&BvmB)4=3u2fL5@kggl47ON#8UJ)6M$+|VkK6%Gj!m{;Q{yRVDP7J#=rf>uPg+hIu&K9 ztme3->E0IV;b0f6OPaQGk>(2a`c^)#gV zx;l)avTl9V_gt~TeejA`yj%drcsxc%joEMr086Y9h$^#T7o_$bk{&6EEpP;ZhXacs z#sDzm_EUGDoGYG-&c!R9XAWWqXa{}4j9fN3=A9JJ+a2S%+WZ^P>e9Wt6*X@=N%7_T`h;L`tU&1R%|oSJ1!Yr~VOV% zZv&Yyx(0iOP_Xis%tQ??0Ei}DAkjP*0t2A~iCsul0I_LOcYXx;A$&FjH~L^Vg;owM zUHIw*Ts(m|DS*E_bv$nJm80v6y#Az*rp)w9#8+?;Iw}XMskJC;j1{$B%#Bm*S@D@4&sr zLv&?=tldT#GaQd&EJ#2Pij09~FbQQu)7&Mto5#h7=si?yU^Xa$nXTOt0~~-#l3YxZ znh}yU*Hb7$Q$UqstR7Qtpp3ws4dW_}B5+&ASY7PkzQ^u?<8S*jeBcMZ$z_9)cDe-+ zoF8RXC|9h=TkN!5#a`;qZ6VKTyFlA6lqTh2Ni(J~PmNVj@(!^o z5k=u#1Bwc3!u&kbF=uGf6eTk_2x%mxUI4_Be~1ySW((i*-QN{{ zZ^MJU-H;*J$CzTr{OZO&R!bCW&xsd zGEc#5cu(s04y>7dQ9_xMdPY4!iKHK0OV>eZh9sNB94DAMDQO@AG5yQ|2uUUn3XTM5 zBhfzuHX<%obUT7lNP~h+P+&|$10GFetrognk7j#>QFS9ecKtv6CQ3*`D*K5Djf>iaq8}q0vTzie3B(I(RjbmWBf9~HMFA6st4pA1D zwI2Ta|7URgbDw}~ZnzyWRw#6gW4?i7qY2mpJq28J=um_sz>C6z!7RwsI8DNYC+s^dxx%gd{#6oLOC~5&{dx%I4q)#jTmcU1;HKWVE_%EL@i3vX6cc*M7rO z+U@aj)yx~RG?<=8(dZQgMWG$<9E-e5P}z{XeiS!EcZcZaU0Tl}+bD>Lzw;&ES^oO3 z|GJw@M_`vF-EgFqBI@xH2uxvvh`^M*Yd~;BfC^%=+GSE}p*9=Fr6x0XUF`LAX=P>8 zW4chL5QiQ6$(1|ZSRB@dYO}c1^Zoj`&@_#C4(>dq>tfSIF7$Qu#b5MAKlZnO_jj*| zWofDt>>xpAeQrwYmr^Z{RvUfT?@j4PQ8ztn`WTU7T9)W`7w`)||8w}!AN?`6uy|aT zmv-lNx>0OY2h4wN^WW#49y3Av!&U7rW^S+|`uwkZRe9yY7rC>`cY%2dc!pFN$O|Z# zG+bTa0!R{kN!=nz%FSF}VO(WJs+CczlhADa*P5PzIh7Kwl}nO?VLHc33PcZf0!Xm3 z+nIAOt0Hm+ii9W`SQ*TUQca+s)h0AsIl6g<{&)%3-S{u~x9k7;)F*Cw?~@|Ve1v?Z z%?5Zp)N|Uq>(~5+qM((#LEhgi4jh5L&^PU2A4e}QNQgJ2o_KN;TZMnvD_&K0pYTJ+B59#0NU=8T zb)iwyc?9jCi{ranvAy58Scv#RaO3MZi1%>f#Bn-(`izJOCX)#$sh%RKPN4oCHau_s zC{`?+3TcwGOZpPTHk2XhU%S-;IYQ<#hJd0*0f2U=pz$ter1rX*?{rL6qBe;l>SW*j zO|LCoydQ?;3cMcz(IKJ(rs>*SR+khls20si)vv5|{}NA}Qe~{1(CDPT5y;pe0yU2X zL6Wh9C1Uy?lN!eFPx+-AqI+hS#X zGFcrCv{Ni_-dUjGXl2$i@|`XRjgk}Lqd)MSjn1d9Kl>lP|Nkp)9A`LvyJG2h7nnpG zb3mabyatSg;UH)bqoo8-9>@euSkYoKVHuEBhyt*8KolF=f;D}_IZQR6jJ7|G2pGE- zR#Y)^z*sVo0?)sq@xn4PG>ak`OOOjT1fwD>&4pM6q*YA6$ z=X;~1yIo@1Y4N<>aNdO|o;SJ==C7xPZb2*kJt)oW@cixv!6G7l@>f3T&CRTZaao!K zqIWY%DN!*UPt{~rQ0tN=TskTD6W2q93Hb;%3PVJA@pr!DTaxn9os&_eX^+`OWq~Yh>Zr9cDnG-cY_YP*xl~=4!gkK@ADUV*Mz`mMcc=Gsh42A==+8!FyRYsP`2?A9ILxmJ|@3eNZs(FisgaFKp z5GH7~JD8MHv|DX<%m^XsX4C4QiAyUk6^`*v-78%hcG;eoEh%p`wl$hwV9`NNaea=&U<`?h0p@eV+xJeQQXn+w60~XV^uKC|Y zHmA)=D_VdwFsZ01(3+eGHSxtcnVW#vHX_Ng+h)EQ?4xP&r#4}X5$YIB3KwER_!|IP z2&F@#ks~iWTD<_#Q@HcYhi?7Ahu-!6Yj67JmrSeEcM?3vRocp0Ui|+?tRsLe)vak=sxG$akdnkz3VPWe~63;E$7eyqoo2*X0uJHFfpO^g!avi z>=G2n6hMX`)|v^3s1z7e1CtOzUQr@~vIZ{eU4~Ypjq!W_FK&J1D;}cPUh{9=c8eyR z4!J7mWOY?fb~>$~v5H_>ozpq4lf`bO18u^b&eIY-j3RPA$?c`Mu4B6!_hR!GcN?9& zN26J5k@&L9PtaX=o(?LC7z1K58Dd0;f=;K6 zpZV#R<0oJC6I?qF?!@ByQi@CUxIYv7kE6DUxR}SY)oVLq{>|U~jrhXv_zqOlNg6(8 zh=|EuCf(sRRZL1WgCuL02H+?v)Xi<)4VFd}LI#Ic-o&%N={eW`!JFUopqW(W4%euI zp1tU_a~h3@%9Qb!eBL+z^_M;FKRxl*8-^e+KsEuZLy(4PCKH+7GpDghuE|UV}0D_^@Do{x>w<>T9go@!f>G*p>x6?$oXd$4) zt*39mC$9Ua>J$I=p8t1xeC?OjNOMt;8w@md7h2R`nP|JUkU7$5YXhi1Uft5~V129y zRt}27c~s)S3+RI`JoZy0Z52BmaO-t)T3z>n&ubeNttK%XdscYQywsnB03kg1F^_oc zPyYM|TaW(&T#F1VOUuAm0G*<%B|HK=3BVpjYQr=Dl%&YSlErG`ZMEERdhN;Q1nORP2cxH zx6dzR9A?pTSo+mFJ?3tnlMrv%LSN`E(8`?7H8Ig!|KhFkl&^d$rjs$)S)!$AE|pv- zVv^K0O_4To0Hq1S4h`_NU=ZRua&D$M<_nS%-?zRBf4MJo;-y6-Vj{RIW z>yP!YuEEXE$-(8@3%O}L$in8Z0h+C@-=kJ+A4Y6+eu;>G%5O6HAUFb=%{EToc_*&8&pkN?Iqy6l)GW+; zV1}4yhEEH{0<8?r9gudEx4!M!KXdEJ=HrjAo*h6kmo^O!Ttzd10RhoLeKasEA@HDN zv#oW2VS47!X0w2(X8Mk;M+Gs6g~+HCxD<0iB)$ zvQ?a3{{r2w$l?a*?6t=P@m9U6{D+RM8nfT7Jq zJCfGi?A+en;?NQ3y`H?iCiA^6V4E+_TOWTf?^10CoabFC7|q6baPPY!J{`dl9{*J@ zJ^7*+|KjCO`7*4=45Pb-SSbhKAc#=_G~nYD83M#*kf>0Kkf2cK0Po;r3a~>}5x9yF z5%@+MjT1Re^p3&jK>ykg;nW-c;6}XpHy+d;txCtCO|ACyuED74wwu%+PY?Y9-pRS& zC>hs_>0Yjt?OyX@(1R|fib5^#WNxvFX*mTco70ub8uF1K1eI!(uJ-!5Y^?sU z(a-j`IoxUN1~?%ie(v+0ciZp3?sfMN0*0$&WDQ!=kcZI_?2nC_VC(|1fjR(41!8J@ zq28@W0X#Een4;0{py4uH{lKfQxZ#EyPSxG6gNW_d_04A=y!X_gD<1#d-}b8am1j?4 zIvqhhL6pEu$t=bcHMmw1fgl1kF)kI!K5>;z#tv)%v<(KTQC<>NL!;O%qB2~Pn~TbD znnBqk!~m#(80dBiv^x&sBSw=`xaQjT;@XeD?N66RpZq4ls8m&H<>b60FgdKQRI!uy zD8hNCfFee6q@(`IdG9|jx&&ejH~3v0N1btSka5AC2oF0px-ky&1MTHGt}g(APg6~I30oM>IBo0f$0zku_mBe zd&UDm0J02#qS5Z6LqN03xPLVT-ElK+`cZLKI56s#AG@`GixD8oJlD$68~_v za_QP4mUz%aNkC~{QYsAu`OM*KpY}Cxd+S^O;wcBE!#W=qTkfvgDX72Cj@LnV&CZ(( z_;<8G{dXV^!L8yPu}vm3c+MN0+EL#IW~hTX>D^(iJAxnr+lM{j-WB2 z(n)AOB)&AMA`+q7Y2knV_V3`kUi=a#DjVjSqSK|p0hx7d2DWtCU2?4G4+q*?Y`FeP z)O%m?g@66RXZ_3*Pu)_Y)Cs)HpaQr=ep6CIj}_)3qe#XmlvK6SCL=~L6C_bJ>Y+j_ zpgn*9PQZr}fXE;s2pAzaWa7YRp_w<4cQa(IRos5&<9gj4@A{1ofAnA8e&^|%-Udv@ zoRP+Ia}cf5DF{^a(3wWVl}*m~;V=R&)!1_Zz0pE<)P@0f8#}dJ-tfJ*nndiO&Lar> z$kWh@gd$XnQK~9LU<{0>exZejJn|X%!YBMv@tDV-TzvRvW1O{6v|1?IZA`=xQD_9T zqJyglQx`Fvlo&3pU~V^mr=Tor zU}bfQYV=t(VmzIo=^8eILI4Xxcx_l!#rSc?iH-nqQko)bLF!C_6fr6q?FOo8iB_xS zwkn>tJ&(IEzB%V{ck|?ZR0^s2&u=qL+&boew%b9{!$OZgv%%P1>@;5z5x?LCFF5uixQAhKaM)aD>+1)BYrz$B*1p@8Ew+^j%V3G!YWQbqWz zah_>Pujo=#U^wjSUUSK|=Oph@Kh2cnd6!faqwzrVMVF0Me0;*AUpRc?6TY=|>!822bjr;nwF@Fm>ccYSgGd6Tn0E9E|n1gI_+Z_C`KmlL-E}A0(-0uhuUlBu41q7jPc5nbej#=;bvP^ z5Y=IVYB;HsMBzY)XnLTy{1Iq1z$fo{FX?u#B7)cH_$G{or#OU(tgHg#l^Zb`jdZwD zQPvJ|j|SU(j>i{| z)6&Wkz2Qx7jL-STXJa@Xq0wv_$^+xB0jWhuhHz+-s?&L-8A#GoWJ4*AKq{z2&}wG* z=I4LQUH|)kzy6BDK%aqgXA|~w-4uD(`2Gi!=r6P>(8VeOKT~|}EaumpZr&xKqwNBD zo&nGfuIG7vF)`6MJ?EQle&g%kcyEmnqsbJ`C%bA^)YYM?!Vw_?R#2;&YJ}8od?t}; zbOfDg9y1)G2q-EV%?!TL!1JE-?3-Wzra!pfVda{wqE{@?YT`W?oh~U6;3g2Qcrs}6 zbH3(R%ln>q9L_GC0cDkOor&NgW{!TUCIf1F1~r2~Q7f43Y}M5?D4AC|li@&g0Titm z7Z75|(1b^;XrVZkL39bv$o5 ztpYc|h1t~m%sDiIr_)UAp*|~7t~Nz`k!8#!_lN}`LWt9mC_&mh4bluHF-jsZF*Twn z#|Xy)2jMe=$%ch(P(6t26HuKO=>>Lv0Itb4qa36Oc07dbN~~W)HI`&90?Q=A`a{n8q;opF*8TefK7bt}18_~pI^`Hjz%>xnq0#V_0}x_>b3Q4j2U3L>jb5vg zL^O*N22|JqRcxZaZm6OtdIYg5YP(|NsP6_B@`6@|gY$Umd2fM+t9x%pT`Ex{F6*yB zBI5C699vln)3QVqfzO;RPzos51iZ9pu^11}I;=_bzt(@Y5*x$_u|nn>V8^)6y{~BA ze#fa$7tp&g8v6H)SY9Y4g{>CJo#g6Ey(PP!+;yQoqr-^9=wc8N-~X!n^xpr04=f!! zb^;*;#1O$CNV27l6ajcZgLScBxkT&YxHbk60BpmAg!D&ZsH`V~C=g>ryVC-*!{>kg z6Tahp|MZ?$tEwD@ypeYbQt~t!E(L&rDum zxT;mVajZq;n#2UOLU24{bzYPRgXPJ&^+0mtBy(iZ!4@=*o${cq&SDIY0A=(- z1{Iyoj&^0|42$fbu-$AB<)CA?rXL6W_>E%yPPbk3Xgt?uTI}Yu+&}-E_wp98f|o~Y z$nmju!&Q{YU^LW)R?C@YB#DV6FgCilcYd6^|6DdYU;A~TTd(CgkNoCEmqZEU;i@jQ zTMkUJG92r%Zce18wiyNDiT#=3pjyaV#GC;UUN{62=H=1w93+0B-ElR&W-;%Of*=C) z$2LHkiLyCAsX#r0qNpku#Ngz6orXe)NhE@agRBfEy3olT5g=-&tg6JTZ;zKY7u2 zzGV7azy3cx0E`ABQ@JF}1_do|q%zEz1vQfGbP_H$uCvz44#|R7*0|F4f6JxbJ2cvD z{EuJ#Wqj}V{vfMt%-g+Cu^GjSsL9WI(n~-5z^k75u(PKIATAMIH14t_@z0u`5mAd; z1F73bVn{R3h6vRtEzy)XL?XNcMDB|#u2{w!|KbOq|EU18Kuo_|-v0*GAOO&57sRAk z-x)nhdGAtH(b`LJtva#YADXkA{oQ=hoPX^6_w$Kow+W=TgX5XK&ISX7yLqkc66u=lRF% zB#OMd>%`v8Z6PW;=f2inif3~#OphO|&aN#0X*L2mfhK6s%5bRt@le~n z1rp~_`i6!^Ln_}u@J?5TLk&K|#AiBXmI-^EW?sh5>y$kza=J2{XuI2^iSsgXPNtd1 zr0KCb9>!L8?e#lz*Hh8$5s{+X$v6D$=Fd?bdC{efuF2QE{`VdOC}LT{IY1*rz{wU? z6B+_*A|90Y-&(P#qX@a^CBg4YlfCojAFyw~3A=Jzzz3>-EVePP=i}-eqsowIe{Zyflxm0ow&MA=+wQ>k zea}lB)8-7Zjn>Wm#zxn{=JOH0aoOefyI(miZFmv@t1}5uM0>NMfXr_ujNSpYU5Me- zl7=di^q(r6GZ{M~hm*&F>u>mHeCn2eeREwrx7!7c#si%%PK&g5?0MLFleK3*v7h33 zZ}ry2i{!Prvp?Rb#WkN~wvzzc=|OEL=)ToSKIj(YaM0h;24g;KvtbuzH!mn}uMzEg z>1JKuaw|GH4TpVobgpv1`T>c}F3#O1>iNZQqe5-%_}6|{zs5mGiLJlk`@{b1oYgLm z{ZU`*8@!npH0rPDWYpJDe?j0)J z1D!6}XBX#gu+iN0S=XwYS(|Q@6o-TUIp!rq#9#BYuX{({Do~XH#10~WdW9!zxk8Pe zQO|>uNpZ{$mdH>gsimf1i9J>k>+GxbwNkf^ z2r3GV(I^a1M2Tp&ngD=%-RlZcDDtAW=`nA#Zti!pOtk&O^F{Bx_f$0Q|GSIbD?5|v z7?3jAoH$5L;UK`WDnrcM++L$TBW&kNf!R55PJxUOG(d#VaBalKb$IK$Uw;j#3`N&d z(Qc>3=G+*JHU{H%8i#=t?`M@pbOo#b9`)Jk;;BwyBX`{ z$Bwx?V=dYqtp;n6Wmw2abFZPu2*W{N7mppME;DyC8~ZMpwCb-&;Trz3g=4Pp_BtKM zTsWq-W3(A)d57`psFTl!SC_Tzq820=udZsd*$4FhVk0hz2-~6K$K{xNE+Z#bpR-d@N+USm^BVj=X#B4 zAxw*jq&fRiE{m@AE&~Z!Dc%AtK_3 zKJ2p|F&Ylz%YXW15HLoAAwr1o?7`##a#jq2AVi2Yg-u$+AFONU=gl01*C5DinNs*p&oJIH0RDAZjG1*T&xm%m?iyaqDg=43#ZAXdQ zbD`rX3{dPOc47lte~#P98g9m)>b4s+np9*-+%j=}ch`vsWNK_RmzMz~YbQ#oqa+C_ zaY_ETetdeOSzXCV!c=jt>owP!`Dq-F0j}nl5UDu_XAh?cAorj_rP0on4g3`CHVww( zP0!hO)6OT3pP;i#XX$BQ{Z)VYXMg^ePmVzlsuIpS1hpm3F(I1e6_^vRSjSN5j<3Em{{n?*&swzb%CsoF9ytDUyA+Eo? z=N)m(hAY1O+An>1eE8K*!d-Wr0{jqOGblA?v=y_{Htl9oqt3C~#)Im`gfvOnF;p4R z5mVg1whxm6XW4Bf8_J%wt zceJgvt1t9~_-<~zy}JIL`+A$dzh0AxxXG*KL>3SSOR6?~HR3}vx z?@|(I)DcOX1eJ+MoFK+w6?Ii4l+4^9QjdsHmholcT~3TvLmNK2tkq&Zo;9=kK*IEc zZ~v>8c$sLWs@-G93F)kOawzm2w+tNn0X3P*D*vvyVb-y-ti7R@rh4zx`~ypp%jEm zVZ(b^8`ABHoeNoz9hNiOR&lYktPkULw|mZ`rq;2Wfi349sR%?=fA=-7*8lWv-wp*3 zL&S7CfvCbek6O=JG}&*HY4gmAKdq@>4k8g?2I-m}G{evZ5e1Wi;2;{IN?>pKhS;Ie zXyP4z|4uyd%fFO~ff)8)jO;e=?AYU>(=Nv=95I4m_YXNuNHNtD(@m1`kNzHZ}P{DAU zVtGGtu{Vqt`}Ow|N4kK%luqpYv7RfVxBZR1)uOjiLf@`v-3cN*d+j}-QWs)p+sGHQ za=2VwQ8Zd-Cp@j-{mcLP1LesFJP>Ek43MiJct!@o6iqM!hy@&h=p7ga|B5p)dq@PQ zp%00KNB~IS6{s}A5g^_}Lj;ink$}~KnBW>2f|hWiK$JD7EigO)7W;qY1YQ)82pR;Z zmioBs>@vzpi853`D2;kt(2?zQ*>#x?(-xf{;u!) z-uSDp`sK9vF}!1Fh!C~{n&Hf>z?p3~%^brF8B#L2I%Y;l)9~a>R;rOXFgcvK>>lpy z*;Bd^`n;cFd9!#nl`KZs=QQr9NCyt*u zPG`@YRU%?y)*t>aKP*4_Q$K}<&kztHA!0h2qS0sowX|hakmI^m1|dL&*Ra1keZ+t$-#WfQ_pcwdQ}wlhGjU3K1Ac{5PxGst6w&DjzF)pQgZl~cd)(h|C!aj-`)0@To zvCfP*@-7XwE1tJO5H>2rNj5hY-0ecJmve9s#dEil(`LNLUfv<4EFLdfuHEX!YP5_8 zz2GI+$-VB2x4!R_C}RZ<6*A|cN^r?4wh}^<6fs6bf(_Jx!7k1QUkDLCX^=+8Hc+QC zjV9B(R|ZHRLU7)s+`uM$Zw^xg4~KvVFZFmx#DoMM4oy`wIU&E+%Y^qH>Krttf|kV` za8Uu;z$f1Jx(_Vfc3U-n9%{dm^#!8K9D!M1`3wIYKpOr_3)75n0wAh_h!dFvHXx`# zB*63#?QI*1L`mG=L8}tf@c8ftKZ3JoPC@5(*?G~UVcNWHcAf3y8rpF?!dGohyi{$?St?oHj*G*CLfx(`VV=ee402L9Cd)NVejz2t!TPE~y$5D`D{zF+pt zqSL``caEVA)6l0RC!%b*)qZA))zIg;LORi3NF`A^OaofAg%}liH^*pl8~*iEfB8`~ zf${kKW3P+nqTQvz9L?pRq~k8srugo%VclS^b1gO+P+33NvDbm3gIe77N_3wu9Jd-& zsE0^)JGhY-^LFFiO`l`Ci{7S;&%M}Iw|LA3^J=?_^~bngvhJW_qvE;#ESU)2lX5ec zQ)iX}cTe`d^#^|F%1_?91Rn{PJH|b{!vZrFGD6o8iiX3nmP6qk@>T=Kn++Vx7{@(f z(Rp+oW6?8uEVzOgxO(NxqsNK;G>4>)+eCvY~6=ABODL zc2Vb$t%~RUDkp6IygSQZ-Q4o(eqXP*!}n2t4*Pk{t=6d%Cr-@OX~e68RU*gwlJ9uY zWIUb5H@@+WS9gjQMxznNqcNfiGEYzm2og-^8AiEKvSQk$&A)A*t;S`IrdMndMad8( zLWynonAm8~0tm#04;9EVhq5fuD+)wav|Fv2Y~F6q!(m-Jb&$vgYqiJK_j&je%84Rs zY2XGmP#dTq4QvAj=GXvD0xl9-uM%s53rHhK8BshU2_PPMGsh=yzUG!GuKjGC=&TDl ze-w2=KUl{RKS$bbhc($ogO-O`JfEARF8$&e*rtHq2%TJyOk6B!qWZ!*h(dfj1b8R< zwB4e&?Si&(%X;g?YE#CChbV8g5Zmi-=UjNc+}&=Y(_LhsE_w>8l+g}TKD)Av;0Q1J zwclQzWIc?QmvM|Mw5HP;${`RmLWNeGqBR}E>l95H!-olcD3M8pMm0rKDzrk0wp7Ss zgpVbfRYXGq+FGI&N;G1Lrj&4)pdt3VOamG+hL2NZag0W^?_C(9p%ZWzgQNm$1Zzm# zjCvN^LLmu>P74x{7A$}{;$MIF7oNTo7sXb)iSc9v715u4`AKnD`yK}qk~ zB`sE)1XqJcX498~+E&*vCniRW5lkMd{Q=(hPw#v6QLydaYiv|#?j&}jL%*BYD=NOx zg1FP1aL^0lRw^EKk+@Z^tUr&f?60?b>}HV{=kt>{%U}KB)_HY(0kzpRv6*VX`eUs> zhn;?I3*GI(mc^5cv%gtdJ_{mZBI2v=d)4Bb-u$LGolND|f8#f^%zI4ADJFv{m>A9x z96KmEgh*iWGhK+;6g8|%XFAQ+y@CMhcqTPQNJE6|Z9t9ym83VH{H7cey!RLlhgj&D zJl%cnb$Mei#dEzG+RL@GUCb*R4aUP+%dNZ6A^>p6yZ68EBd(fG0&4kE)tGX*0vZzH zZl30*cWtvLk|qoZ9?%G7XPrNCATT@#oH3etj*orfJ%0%owX@B8-70ok18k-KvXeN- zjm%-?+{2D-;mZB)($}Bl&7N;7gmd`;NxUu&L|yqj}&*e%5E5UBmW?pAVl z+udM!(WPFV6VEYH`on>W&!{t++)pFXW4`@|&ffpao`PF%KZR~N#WVy=Q^820pr8m~ zgh&VsL^y~##H2tPH6nl@(FWzB08z~SDN;m$Vj_$ZKt(_RBf_InLP?4kfXJNMn;A-F zlZRurBAFd*vC)(i)1pF{O{>>3s#;6L0T3dHBb@&Dhw#lf&pOVw8_vB5ud#5a{g~W|MT&#)2HH% zpT6G1ae2Gh>vRhCt4WXh~H=O;iw- zL`@*mR3?Q+z_UXX!DKSV!m&jFz(XGNp!?l<=jphYjrn$9&VFiw&4!B|D-(Gx@+<%Q z$;Ag1gYgu^0zm-5fs-Vt0MMwZvq;crBKt&!N1?1ejZ&ah=Cr>MYa zJk-VRaW@$(L9Tr8l|S%~f4Sl8N`=9xTg;wXJfgQ@!3>6SfXQ66l#?miowi%Q$nE5sznI*%Zl046@2imNo z>z{?i%TCzfG&8C)pbBF^9oTy)B=K`2C`qcB#Aia4DWT3OZ*l69r60@+YX#37_7s>= z6l#6rdN5f6jt~X+zwdoIx7>Eebi3ktqqSr=9N%7E&`$J(>sf*MzpuMQ z?P8G~4Ykqa=F*v}e8^Q_zw*4N|9ElJr*F4>X+m`{i|%Awo;(I;=e7Quqy|W2n~WF% zax#;$i>2b;SDeJZeEhBW%lH05qgb3co%G^hyj*Q{{D%>D7jE)}iUUKS*YCp5ckSAK zCv_0TbCLJ%l&nIS!hhJ{f0D1fO{0Z@z!K z;<=Z1XgnO~($ZOlg8ugHe>Y|#Ov*95^N1>dRQ3+jLdT?F1xOi8l-$ISR=Tv&Ac3l> zVnXMc=ox>8w%{?nm_F1TXZ+Szx%_X?zeC{jN*AS`}NIYqvCl!FKzW) z7aO78ORT?U_PY)obn#qtIy*k62Z0!_toCu{)LnY&j#KgU>C+%0W+K)o`n6y4)$e@M z8{ZTslX3jmM?e0F?|jLNas1>70E*#ogu&_nF;=GM&H!Revls%Zt)z+8ILvjZX*_e1 zznq;wn*051!p6E=Tx%O+P5+tIHslI|Oe2b-fQsTCCr|P%x8E_Ti|4Jb;d)(|KbN~1 zklVQ#hn)BKHL6O|=n@lDBmx;8eE&!0lW~2%N*fW>WHb{AAmcz=L$3xfQh620wD#3j zBKB%9J`=!#Rwo0uNBGdk-}a9{8AUZJBla!=`n*y8z7%uHg^DAEK<^|DYPx>@dG?A} z>~yEQRm>Yl?D;v;?tG)?I*4Q3NoLTMM*=89H97fFs@#nbXCMq1nvRcZ^d=!Y^?xCX{_Qv7|Gw~RY5qFwBXc{e^b=Cl* zc!?-Dsr1-J-SS9{K5JA+f~_5ZX$;8Nqm#FxA>i_RUg1tJol)ID=lNoGTQ)*lyLpfL z!&PnNCt8iHUrnc#l#`p2M2|+8lPDuO-LtOK>%=KvUcK(=O=1) zwh*M2XqpX#0EXrox=jqzhMq&4VZb#Kbr3b2BO>^#@A~@Q@~5xsZ*}h4t(?Y_p%O8x zK_SWzj3*N?8RuCf!n3p2tOp2FVNVLq4 z@RUFS0s$f^rzQ|UlTrt2`8EMXE5exwRfuS29*tHLE2~4Cy!<3r(+R1n=yXuR+@7v$-&f9Ms8wJme%!sOLrt>Ii&ZIt% z45bJXPQ%>_K?T;Pbv-G9+!QJqLe<8-?%BoL-~Y-ty#IrL`246U?bc#8o)~>2Z|5`| z+quf~E-~~R?s2b^(|T2|-S|M*x%mujW3O?^Aa}4G z5%DX3>6hd8fB$zQhKLXqoMh|Lu(yF(OzA~ul;;^&(Ka155+!SEy9gqHW+TJ293$^^ z9W!f;Q7?4CHvf9NdF{F#yxsM#go<@dy~%Z|Z02C;+Z zwTV2TdR{^iLO_fWDiKoI%mq0HI}b|EfVDg&3nNkhlS54Ie^4@3K~mY#`e#V05L_!s z(2UE>rD*3utr}W$a*b%B=8}f$F;4_bFKl%|o;t`+i0HD`h&=?{H zqqX~`z88y5PRz6?wU~_&^ihn|BT)=Fizd7t?q5+_bL(x^89lih@*W!wKz!LNE>z3F+ir8ePQ2 z?_#uaH;aR?;@&7WLoK`Qf`nCA14lu0X0aNh zSSK~YpkoJchyZzTRFI3Hs0faYU!B^p1kBvIl+>oxjYyr^b^xFl-*F@U`g@&GM7EL8h|D$MGmmFI^XotG$8Y(Q&)KhZ-Jad=_OU5jTL8{Y*qd1|U#xrZ zC>7q9avbF5u2=LZLNOfl^_&8kSW}%yfPkIT0W-Ro z@oQzxzyoB7hBXZf!~RdH!zP?$*5;Hp5?8k_id8M=QJtr#rk|zUmHKBYYPQpsNUYN* z0u73&5evNqD1bM={`Gjy^PcC3po`t(e0Jr`=FVR`i23__xA)IZ?(_L;Y2M(g(a5=~ zsPWvV|J>k;-siaGgA5n!Qv z9H*8)i2wbU-|O1F(y_3_aH1HGPp$0ZlmR%J#p_W#rv1c$BhZ(UlE&RNHb>{SZ0C_YdBF2D>Z8|!O5#$w8d627leMa)& zgw9Az1+Y^{Oo%oJkrbj1!KKBB%#xNxQ3+cS%SIbXjZMrqZWUZFKR~ST&hPrB-kH;P z3_KGWF`j3NQ@8BuL_<1-&yZLIQ6L3_0|J{L`1ixbC=>z94mm;CD&e(@JS@+-dL%kkicJOqub0o$%R z)R41cttehIvL+x3LrgrMm8xKW*j5s)kN^ zszlRvk3QcqawH<~GTj zKW_e*_Jj7%EKrAQwySx!2M3}~(IqDlff#D?7+CI4sD1MO5B%ZR{mBPDGEm&MvWgQ- zm}O4Xa1SSY44OQ6pjsyY^N3n*f66&0W^ipxFX^`*6|NHvW<2!B~ zajS{R%Bn7O3e(q{KY!yj7dj&1ANi3Vjd{C^vMM3b7F*`T$yO_gZKx7ZTAP%wq z-Hl|&I|E3J8{TX+K}2}Q)1UR*V5o#}Bo@1aY9k)@W`4ak?fUzszYP^?r?KDrbE}-Q zpX1xl`y4&KJtU?7~1>RGi^3`v7);B!!>7Vzw$K$>axIgkDpKUVh z;a?^oN<^q4L;|Mcp($244+#P6y$y}kh%qr79E2)lu3=I=vUY!h7-FO{qg9XY+3Bv4YbcxpI9>_?&iIDKR>4cs^gH_&cm zIJ0yEZo1_kzmQmUa8@P&&}lD}K%%CJo_CGko#qneV?UeWixpcU(CgpOZprC~9f!eP zK8VGxF6!^D6Y#r@ywjy&;=kqj0uP5PHpfE5XrJi*)Z5;*ytH^veCoD4(O`#0IfXbt zBf*pH_DLC}3UL67SjUoVhcE0QV$*;PC|oMGogA9Aav$RiucX z5j7yFhsYG74jC941VBVcgLjMwmt^cHaSWLV86vV;8x@$LPC~%SZ8zf+Fa3t&E0eHV zHd>+pCh6L37hPf{4Cm1Q+Nn#^N-X<};r0&)@vK=YGypp7yoy&1^OmP)8I%swt+SLab!%_DN84 z#+-(5t;jSgMa?lzbO?pXXsV6L3M%6U%4CacYg0a(vB~{@D0ISA98_ zmzKd4lVO%i=(0eCAa0&3PJpn@K-+4V!g3|O>{5E{7b@+LlU(;r{+XYcqQk5{c^ z?RYvg7;P@6d62p5VsM`CPAr8(86w=8DsW*ZJ}<;>ZL^)zZeM%-dCQ9~Iq0GK(;q~v zf1f)sqv&{|Qgi>px4x%rKJ0eBD~nbGUjMZ2LJ&M-T_r0WLl!Z3ULh6F13t8f3h#f9OS>98!PtK)KZZWi*KR_5-9V+#wk zvN|9@@o)cjU3|dR_s6)Jz@_07WuuT#8jh@!xiHe83Q(8t#^9hdP)HDpGx@0~f_AF~ zCE!?Zkypm6SS*fRbn3=dagc@iPV>UU-?U) zeCku43UcN#Ey?&}I2ysT!(=i>mbo;L%-}48S&*oy_N0Q7W&kjQVb5blW-4n_@oIG9 zq==Ca;90Fsn(X}SUKTQ>7i>i_$Y>Ir%sG^j(od?cm96TY@svfZbS9n^;8g3-6QkDi zu;aG52uetF{k|+$83oKFI2AT@Y*oFz6eGllyl8{LIDPsqJo1r`YTbVO?G-U=x3_?m zb@}6{-!tnAzP#Nbf~S)Ds^i7pcR&A)%cD^bW!Ze87EZCDN%tNKk#0RZ@;0m42VH{U74rbgUOv1141leF-Ql3G+Z6_N;p~|8d%y4dr@ZR{xS?1x`A2g!m4}BRh?r=z&)~@+aHvMXU_QU=Bvp(F~%Bs04z5U4W>YbsbGlJMrX*01^8_ z^`IxADg#a|ws6~BAHuDtul?a>I~@$Mvy}w1_p(`+eY^L&P7G$(F@K*OZZUr87SF&T zA<&CwLHhf&hXLV>}xbK^(Pwqj}$Eqj698tfL{0 zVkpm=<|@U&;yg84F3XNN@4D(-OO;}bani^;SAO~*-u~Ft>pppQC+CvLv1$~BszMPb z0->-r+#|N23z^k8Q_>zysvk8kT3Mlqf5%j_y^WU_$_jbjK^X$t%?^u%^A^F~(4*!z zczd}v5A%Ay*u1*!=o|+Tdo3UiyEVS$Ywx0%-maw$tZ|zk{NM*&{R_YNOP~Der#%%u zA+jM<7*9r45IQS(YXc#BFhJ3$3P2LpNop9v3P6(ET>V;<6fQK~m#L_hx>z;@*{b9< zvl5=6lscn;)PnI!iR+x6n^f0^{ZbK5s0V}DoKz`UN1JFfB-UN2RH)a}ZNM6GU7J58 zNe-jwO0oebQzl{sgUzTV1~goTPTmHH@Zk@B7*F{8&*Q;(Ow7=3_ZX^B9STr&F@va+ zJ^hK_@fQ!d&oiEQ>dX-2(q={!ip)BUsZ$EEjTf;M%pgIu^~8Fl>XT_rOr4JmCy#gW z_J8_c@%^88b)&mLZgp9zMyDVT!f0OZuU*h^e00|O&&0WLUkwvKn6j(s!*{&oC9lw zhnQ9=5kj5w>q&A3BrxD3R2?EK$axIB$5;dl%@Xf>&1>(}xBS|bVLS@u5#(BR@*a(b ztJ>}qG+tL+F%%#8z=!qGk9rs;!P1h$KY}J}I zZH6<>*=_%fuY3AmZd5!2*iavQvzYIW)YoP&&nbepMUCela}ZH<3L?@O!hBf8^G*xE z!!Diy8~oZ!)W!2*Y+$y_N4r6ywAJ z+gwKz|5y-b+*FMb5ey18nC!^3;3Fq%YvEbp3y@KxSZ7>hD{QSeu4{7ZYS{pDZMJ08 z3r!B%njp)>nMN|*P=Trgu_T2cr0GG-^yq0Oc}MA9uhn*dnSQ3TZ#8K?gCv=zq@UIA zM~%*=gqhJwRK+w_01+0uT@Vw>afzot>luIfmw)kBPXpFa=c0FvhlBpUWG^p9Y&Pf7 z?KP>tI@JvK`tkc;`Is*ot`04C{{QTKXMk*3RqnUeKBv;Ds_vU66NX`iK^Pc@3`0apFR+#{S|ZLqF}KR4U-` z;ludIM?Uhz^~$f?z1K#&t=qvXPx}68uBp{&>24e(w|Z~ut?OiL{j9gnCfTGwV3y~% zS@1loFS5^^LCj){exH~~RPpP-{_FnWt+!+s-}_<=`YF2I4qBZyh!_Qq5EmIL9+{*_ zGY|10c!of{;eHtaMGoZ1WT^?JTxzOHU>6z&5t;~dYR&v$Y5<9lH8%XQ3IXCn!Y9`>WsApV$J#JX$Th)P@tQ`2s=bLG^h`M z4^4x{?`hd+a;PU|EL08F^7_}l7R5@*ee@$AeWI$!sL$}};G_?nCe5!&Yo<~x6Wb{T zcRcWjU#eH`;`)Qs4ol91SwP}!+}WB0!FHI+*-?nsGWgbHYOok)52q2J73^84;<{Tu z^NlWUdcF5vTdVzb$1tf(zmBI}^VP~c;;gkxo)kP+cQ6JynQU$WbZ+yaH5#|AIPpoA zRO{RNoV;k3TYg>h&b~L>EgheP(~iM*cxN~Ij*al@qZ!LuQlq7AFD;i6DvF@dUe)%% z(_J3_j6ZqiFaO%shDa%YXFy{iT0??~C>Vm3@_%Yo!t@wZ*y5@p)RK)6(ifor#DY7=QP{zkLM2 z==%&_4TtE03^&MV_|_f(he~b8QX{YiIL=|3KnSo6I?gCp5&(pUKI~!V=btjk@y=^L zZ*|>H`u@?MP3xd{>QFv@U(ZslA3l6Ye(q;~4ho>(X+wMpW`}|^ic5j6{mE>c$RS(n;X-5@0%8@z zKn<7 zjU9tRh#sIHLJDPqdV>y_5Q<=!92JHeeu)WsFy;K!82VcxMnsG>8$dHfQb|xQmmuo! zwm*3%*hTcF-~C-SHoW5x+uiQTMM*cwrin?Ca+x~)u4W!^G0`QLJPN&@Va&ZBVLKrW zdzKmxzZk~cse*if9wHpH$N?E3Q5qPdA`lqDJ^a^~{`Ie&>t*BDQ5l>2?I{`xhp!zb zG1p0Qh1h2vG=iIb#&-(K#%8wT!NqnnS@~qx%!yC?J=?|nHCqiG{jPazF7h{_ik!K>};>C}6%=7=?T`#!mhC}d;24ePL zQK&P_vxX9<3qG1u0Z(ABh{R}>)G+`;%qfUH29+vqCqdWS z+kQaYXt&g*gSiDzI|Wf{t+oUJKK#*-NW>1^P6vfJ20LpN%#^I7d$|3vHKibILw z^4!DMUVrV%o;~}}?zGVF^?}GCm+tctngIj8n2Et2Aj)&Q28A%*<`5Sug&x5WD0y3J z8iY+Gs0j+uu(6Lokf1bYnCK8_Aq2T$-3|GhVV`XW;gH458-6JSxw!vMN=*N&J1QC%la5(87fJFayroM$)S_eVdmf@Q>#eQt#wO^U zwjB?kw;Oa!8#LzMQ{85Ud9%%?mQ=Tl^Zhyo|Nuz{^_rL?*~5m&7*y+E_YFg0nP+M5j>)ilBOW9iz!7QjsQmxE+7+U zUnVKMD`L4)#BBpXZU0&5eBo30@^Ab?VNugcU)6(4MIXCtA*qmePPSWh*s7Ik3skHh z``y(_ohpg_tX^9n0OsV$$9k zmb0jYjFklT0Bb%f6iQ$wJpAF85*d6G#d3wJm4wDKx-$o!Bcg7zI_iyZ$|oxL8?~tp z0h_GP$w7Eq*Zb^^4*Ii}M^9A4@i@$7J3&&r+fgE7=OVrQ@=y8qy!%i0GAUY(Hi#Xf zh=OFed07+6qtyTpI@YXlK835>|FMGKoB2G#P7Lw}3=aJ9%T2dP}>E=Hh z^&RW2i*f%l$=}Q~s2*K2lMZ&(axo0KqlZ2GaleeLg#Mt9Ol3`iU4l#epc~%hgxwD5pj0fozH*a`~LCP zqsmx0atH;^009P$k-D%sQD|f~X4Rla>j9rZJ)w^b%8Iz3q5#E8F~Ut*4e99z5MTKv zeDUSaKM?y)rb)HEkkm-Lhr(gC-BISelzIY;kaJa`T1hBLYSij9G{-a@4i%sOf+$5kVzwvFc3$;39Zk!^*ObrziSsO?sHbgS35Uf`n}Y&V+)OS!Pn z%JPz2`p}0V^#e3JJrv4C07h@g6b1IMEWjBkH08-KxTQb?X9b<0&X=o;Dxl13eCi|2 zH{bxhfWi-hsu27luxkWGx+sWjguJ9RM2GBl{-^z2ploG-(GW~403(&NyaE#lKKuj- z5jm7{+B%E4eHy4M1y*sgrV^NYF=TeH-$k$2L(C4v(E28iajU*@)zL-jGrmXp>`Y=02;-1B~Sd&V#H`wf67 z2sUb9-_S7pYZZYUvXlE^4OnyqL5Kpx!-28_%1H$&ZpSyS`Q*#lk!Bem<21+Dz<6KL z$z~=NxAnkxbiSObkdKoxfUZ}oG;PLp^Nevab&xuZ=@vd|z0*~*-C8#TwOt#{#Ba6{ zZWf$x_WK}7jY6$XNx9D5WK^X& zqi6sCXf;~v8oRfidQ zcDAYJj8yC_pw2hyJhN3G?7FwZf4`HN`TRL<_4?Ked^Xx7H>!9L5#RaTbN7jOO{!I- zgFbw!h@%Ls03!=Za9I#Jq9JPri8cSN{XA@WLU|M$e7fdj72!Jr`GtBAa|ng=EOWp? zI17IB7KI@t-EdpAro=TIG(whJ!uN%PE?c9SDh2i-4jxO5UQYtX78Xzkl97;ENAMLE zFbB43FbPN%-CiHwdw_w3Y7O;z9dWUMW}}H`{m?V7EftIIw|?tQ&N-$m3vqL>PN${g zEJ3+VJ2$3b^V!I6Oj^6+>uEE;Q>|1sUn{C0D)8Oz^vKUwix+UOw**8cVgq$nLCBwR zpafJPBsc89C2H}o9mK2};y^)eD?+&(;kp~YaPt5+|B25s?X)v3R~9(W# z`qc5zJZzSVZKKsZu2<{3$>ttF&totft(H!^scsg4Zd4`(tOPa7X0(;fKHoTYbE1uA z;v21&js|_(-F!#iUr%bZ+A*rS*bCa-hLnk1ueQJRy!U@_P`<;3xc1hY4Q9|YnyA#9 z@JB=j(aYszJsEbvcfhqZ^lTD_77?s<=bc#V20&qQ~R*gWW>G(g#Z3 z7b`Uqaja4pUhk;0uZ`gKwt9Nb9bJ8N9Ks=Gp<1TZc2@-vybu`7qYW3iQ>@h%h$!M8 zc*0}<;i=Df27Ed|vO19Rlq{zIWP$~in z-0zZ07y!~N9j=La24cHSV>#{NbK302R@djOo7+4N%{!Ty-|gqP9xFf`F%c0z-8XBQLir`sZ>xYmC+T&2mki3aq-3XdgS85g8Qq# z{Oj|bV^vl0UQSpBaHIJ)&Dz~;zK+r$PV#rv$|AR#0|nSU`2LTKyUPp6xRFa$h02A= znKKNt6Bq@#KQ3Y#S{TCK*^@z6&8 ze41X9T9VAn4L|yfrrl2_`OH%>5TAPheKMxvH!cx3=avPoY_gHgb8PBUOw`e!dGvI< zo%^kim7{660~nUI>d2LW@?(GRJ+0I4bw6Bp!iF(dl)3@_)bncjU{T{Ftgrx13}EkS;GvztdDv zl9gstYn2);jAeXBDR&yJ=ESUNJt#G$4htvq(^|f@M8I(u_b*u{;X;CtDKu~B33xJF~qaDiiAO^z)IERJVIcZ#;g=Pv24tW!wA_T}72E#hS z=)oaVZOx5jpKq6LxMp1o+A{$LX^KI=he4L1SSq7lUqG!^!=62RQAsN3wmbO8kNhJZ z_OOTje6T2j?&WirNj+}7Jer%gg^#H_NL4TO{YU8}Pq0C4(& zbI!iQ=@+_AODuzooJT*vaGa3?sx|BwX$uT*4>GT-q2ZmZWC|q@5kjS$pwU>yO$V?3 zyJ177Mn!?2jFG|QaoKFFHuC4waN=*b>3Y_lHDIkL>tu`_H?vbhiEup?zY}4j0t%;! zc+9M}5nWv_08?IAASVDzA5oxATUzNT#TEX6-+h-q^Rg%7+G}n=soRG{&V)ZQZ`3~G zA&%fEgO?a24xTc^A@dSzh6YL~Q5UfXnxzVEuS7WH8F#Ji!3}SJc2OQwd6%!2%GuiZkmw!m-qep&lg42^ zk%D=A-*=1Et2JtzB)(-g)D*{!i1hg93T^xtu?3w6Oeg>Ur0Dh z<~;*>=2(Mjsb!l(h?`TA2?5MQsyfoZvJrC0Lr!(35NxkCmPx|?r&)oO0>XE}8M6LsdM}`=ATAAUI69FoKTzxX|9hzF95(c7(q7>?5 zcp*eyan{*q<3F!>zyH{Oys1##tCi+aZIvo@Rsas7>#=W~DB-u=uv)cF&66aq+p3*( z80bl5hG~N5-RjnFx0{-0W7_LvFi&;c2;W9f)RG!?S68*wT-8Rqr$sF-qC1GlIePHx zf5kuaiBH7!S6zcrFN4IE%~A0XXYyDK1wI7{fMAU~PoxqH)1rIblBNy26m%>V%)Lk!E-)G z2U$ix{G8|9eCdN8f;97p;<6F*tArpq1j`h#m*LZFKXtGgvIJ!{8M<^9fE+_qkY$2W zp$r0W=_4L_nW)fWt-hts>5cKJ$Ny}*JfrJ356zyb8tnx*85ljLwZ@RsHD^1pd!Kii zb5dq-{AVX3cz((=pZ3k4e$}gyW&<=k&A>JjqC~CV zfg4!}Apj1mNAt{aAk_>#29DTz5t!{@8n~bVG4T^GmU%?T5{p8Ff}$ZChdYxscd${J z0Rg$8geVRj8X}O)L&Y2WL$8nappRa!122MNseo#whNMtBzv&N!n$MDFx6&gB2|fBp|&f8#AnqAH>y))u&yEt^;HydL~!+O;-o@LU-(eH{P3 z(Y2AUmsY2()ueU|hPYgrP1TPO`jAJ59p~G1GZZdEvi2{Vi{K)PLT1 z8w!mTkj4-tM9ySPi3rX!2$=h3ditNsJg?{RC zeElU)b+tje&`|Bgdk@5}+xNY0Lu_1jKXWMW;8m0Y(S z>$9KwhShxw52`i~F9Xp4j0#lx0N4a;)aXR5(bN32(S9qK0rdb_Aeqr8N;H6^WoTN& z!oCRI?CZUE{ngLzkE21Smv-m+EWVSo)F-<|z!9SFLKV~p858t{_gKS>TA~?M6cgP1u-%P zAl9So#F{LGQY&M6gC`IR3c|=37_cDD9Ex607QyY6GQK8+h11VO_3E$VzhD2NbE839 zi%H}GAoAYF#FUqq*W(i1$)I}JP&g7$jz4n|L9M<(?M@RQ=*_p>s^^?_CfeN&;z9xN zIk+E4b8eg{t5j>$*`@V;vSG%{NtL=gcul);SRZFPPc(Vu?X+2CxGEB zFl9Cl!;tg{T zj6i`DLN4_GaHvXHGpmb$OcXIZGKs*5P%BpOr7JK0GeN5xM_lK1^T2sGg6Gj7y>(20vITtYNJeJd)>|x-f_D4a?Bkw))})hd z3P!ip(4=Z2AgdkGy$|@_$35%qZ-4O>hnncM+Nebt`mO+E0!JBu2n25>Fc?>>CqW$8 zBOn<($51ew_gM5E?MfN9$^f!=KkCbE{P*)NI}k73aT&T!_c{##GH+eqc`3e<)Tncu$i=Da5O*thp4Fy3xNm3d zcQSYecK5(QOmy#iUHq^|Jo1qcpF$-9MS$^y|S;PGvSxTpy4`uO@c zFaHgYXtUeaN@+0yAW1Evof1+R&A5+RwN@vHZae7Ssb_bpArqqA`l8Vj>TZ=BcH%*- zcY>eicZ~e!C z!oH$ICG}{s1lV5;g=JE~WZ4@pfh8h@iOgW1A&wl1#OR@bD_x9%W88~-`1nsh_hE&j zw=6G~P`tg}>&3Obaj&_Q?kyE!a0vj?t=uOt{al=BVNZZXHJS}N^UN~}Z+z1mKmZ2W z0FiS6?+hSs1RQb7P#3zpjn`GgXzC>3ot149&s0E6@L50L@)R$6@rydwU4Q-2JVP~V zOSv9FsD~JpI>7Tym6RJSF(?zLN zh6JAFT$EziGOK=wVYUj4@i_}@Er@6chG9XsQFa&=Sx)r`oA;Th!O1}IX@)3raL&O+ zF&1j2A(TSGYuk+uZoB;;jw~HTzuyDAhbmC6lyT(968gO^R+_8$(BFUXbJu+9>gOLk za^%}bjvkUYE)kJH1mp-RN~Abu-4Qb@5osZgP>4$)$5>ujI&OX}uUhX%wUUs6hC$*+ z!ShyZD;2z2QX@|rA8dDe&QJ0I1VvJ*bGy?J=lUQQ(L(Wp-@eP)4?M5A+ygiRQwBr~ zralNUG7$iN*!)Rq*=SLlTAX`p=Kuw~ctlPi5<@^JR7<$+(3h~>S-PcE3fIB#m2yH9 z?Mw?=EvZtwy`~O%j9|%0&^Wr0ol3zou*Cp+qhO+5t&%7m7rc!w!joZh+sHP;cW!oq zUvKkX??gC_&6oZzp-K47(d!#$0B|zGwBs1{q(t3LAJM*ZPJij2fAr?BQ5nkzk6?in z$_|Y{-4rRCwqWRRHy%yEtH8N%hWAD_=HD~N~H?*dV_SaI$NW|xV73%oo17mW?|=JI6l5a#N=47{`R$92BF*OpinTL zYVQFDAIiu^l_fbDmeB=($jZnZ2x@cbUV>N&nXS`bJ&+_-TzS=%c-K4LUE{#(m6Xdw zQH)lzvF^Q&a;0u$K(j83;=>>OVDIyv``jbG^0lvjp|r0+>UCnKcs*lg zB4+qZLY)c7|6Hsu&`M)f7Z&%>>dLa#YKzott{gK@l5&OG-Q(A&GEekP8&xrV3*21-<0rC%%H+9<&BW5jzJ!fk6>qHC0^AIXBEf zYp#^bp;txhFh-g|IkE;+q!3>KxfI2uhc907$wR=vM>xpXA5l~U zRB+gpa5(GYjPve_o8SD~D>uLWSNH8_EFAW_%=`Ana&#$OES6&LC~J1QTB}s4xz3^H zL^+P}aU~-D+rRmMf67yzhQVNvr1c3u-Ar!8I;r- zj=Z8!DgYUwRj5E)91)O(bz!)TfNi#TI5g>v)Nj;;E`acR3L-?__!nIaSS+uypju@cuW$B9Gf>pjE5Z2_jg}@6B@PJel_D-Ll$3k^1QX z+TFA7C!hb0!`eN7X0L;YQWz(=2O$QO;K>eLoHv;Q2GLQgUJx-@0S|ADr|LkI0a69I z0g^i59q;|gr>1n%-vpt`A9Egtn+!GfU%{uo_W(yeTwVN3ap7RGd zO$IK1AZz&B>9Tt}Y2R(ZGq7=aY&4kK@O4R@95N6QR$FatM`e=6iWG?e7vq_K_s@f) zcQ_B%Uws{F5in4Ka|EZ17y?y>XQPnnD-ek>Z=``N)kjlrr2u0&G_r4n){wLmtZ0zjEgOF(tZ6`W-*-fa+M)zsV z8+YD!VBdZW1{nZT(IgNu6+nHc$|#fqc3A^`BLNTrQGi2H8deJDyfhF2L_C-ryb_XR z5zGRp;N9TK)EQ81clt_1eDV4Bd0Mq_ z8g9M44=JP&>OnmNu<9VO2dhBTXimV!yaDxV_<9w9VvrJ?SjH%G3TOp;su6BI_?cec zAN&VZudt3Ej0ex75T9VgPCey0S_z!W^t;|pk7uD^T{pXdN~BL$>&-?Iyn zlDnzCG-}fXM%8kKl&rC)AJ7}pSdkKu8*o8?;x9ic$sI4m4cAcLQCnj&LU z^~wNvQaIJ1=p<0`$S8s*1$&^DWpD$*5f|ZVW!&|QGm-u4r|`Kq{YFL8R0~4omB?5( zike|DDb01j z`Zpz02{ep{F=+sH@U$Ka%Nn}aAug0qN=m4dDo`2V1uuM2lbGoRFMOf<_`m-13z&(F zDS7QbFY8Aw6Jbzu#+T+n^Q?Tl^Rju(&+*`S-hAwv@sul!g-As-a`{(BIRCFmHZkgaSLR@tC^4CB1XF%!!pj@d> zjJMVco<}YB$5<1$owcKJ5Dsd>FoU!!0NwV^CqB(iyvD52ntFfw3&}T0HtS=Nw=nr}*#4%VEszE+j#Q2`c zoHQv1l78ckMWEF(Pi|b#2+C<3g#g-TNRk8t8Q_`Ec!mQAiwk=;yw36GKX-ACU7pXo zXv$c*Uz;`Erd5CHL!b?QN;TK%wG=J|5%Zqf_q_csXFTK{jjo4A0`&ya2*wwl1(89v z6WejJ?Pdhy>!u*A;)7OU1V*{w(9RCw+S|VNT2()>ijh=nH0%D=nA6x*w-~7Ibo_U! ziL3CWpkSvnKAUAmcGGt1UPqhoMzdCvHnrV8l#DvCYPC*DZDDwQ^|6xt8P#&B)#*qZ zk$>Xv{IL`s@ko5->g!QJiqfEO_yZrBFIb1zFk!LAALY=*?%};LN3ix$(-huCSmrWr z=K_*5_M^1CjL*OH#||W`M|@k~D47b`_Q|LUI{KBYB6ExJ4 zQ!8^at?jg6OdJO-+B{PZ67OPFja<_wFeaH#&Hy+NMGz0vYXw|$(^u0>+RfSm z#rAU5(Q$8wB-JeUx6zo)V$-NQ?}Tl(FYM&uZzqhW4GOmU&?dd_WKu=i%*LI?R>onY ztksE@7jCs0np4y*R?4n1@@y7sWoop0=>VjUddGYHSx^2UeDlg{P*KA;r^?9Kqy_|* z`61Z0CS(~_A^_gQI|d3488Hgt!Tk&^IDC@}XjK+*2W{aCFL?a9IDGR`oJ1J__(7_L zIMemAV3RiYjmCN>9e?f^_MdjZF)^#Eyz8Cst}1{m>kowz0B`ht6r6Afm{l1n3RYN& z5E_CBdkHdAyka7#dZ_n^;{rHh{N@|qh;Lne_0_5>N0$x>Fe(MT{`@+AbDrPfZYy*| zC(18uHZP_*WH;N5;i18@V6)G)t-R+{v(fpuZ{J?ZvH_{8kP;;GfoW!x-omngN_Lbq zl|fq+Lx*waJKvd!iT3Q-s{nBL@R2PGS&Yx^^&VPf ziY(t~56860oOxt$$Ehr5`OKY6_|%drHAXPP`E`v4R_l#br?b|tmH+7hA{RT3m|M%* zb5yNebp9h;b7cTQ21o`bZ`6oTD58*QCO{cJry5iO;6WpD?5ei=F#z%eA_vJBTw29f zzWT|l0fmd411vAXpkspL^%6cC&4p97>9by~Zc0RPveWF1+tfsa(>}EE3DIn~wv>&U z#db0mdcb?6TtKf}!jkvUQVDlCum_*{>6g5+ z`t7gZvRIC(t3@Qmg}9szBJX)(puE-0=&bLV=KHFZHOH`Y_^67CTzCDq)r+Fj8JG-l zpx7c9T!WPXFVtJB@*MwKyO{rzy(z-n}zk%}s^tg+PdM8x;H z+aupoU)YaMuZv=A&^RTAvQRxhEPDHZ3G)9x>d>q+UBOqj`6CZe+D;oo2m4up|w(h2)Q7x$v5uwp)C=v57{__9!zx)foYOEWua%d*BYK$Dj859aH1Qm~Bxdg`!dk^g8sz_k?ZU+o5P0YFEV2>M5tJmS z<3(ORx9SW5ky=DIubiQk}x&UYJYIBQ3!mQ#U*SMO4wi#%YFJUekHk!n+ z_)ZlXXUN(G>|4AWzIyN_zw_^({M`R87M$<(SBI=o)oPti6ntWQ}3OM_dKob zni3&k0P$lK2{)m@+A5FliIRg{?|XNe#s>h-eaIu8d7r00=Pwt3@L71^-+cy4%@(?y z6gQ>=NG5QtE+}G%WKdUu+gk(nD$+CVg3{gYSGhw8&vG8mO0WJFR=)L>R`-f8{`|ck z`}nu)d&YC{$s2FS-mHNHj4l<>afBF(Vm5#WgUNx&U>p!@fpeN?PYGGT`_|GFjw0Mn zjBl6AIQ#7V_{Q(Nd8PU1e{?=7MU5%*eT6gvq-5X1LbklRdfd3>0gauW`55BiqSUS(|na0Ks(Bsc@Hv)#8t`f~FIWX%u z>~>OOd>#7C3jjK>|1@R})fW{&Iq;yHtUZ&7FcBDJg&U)ogJr2H5_p7OuTTjq6bn#Q z{N|h9@JRqzT--}5BZ_|O-Fwba+wHV;9_$yh1P_zMGiD9oPnO^rn4FoPbuXNnPIJOR zV{|;LiyXI`oZa=TNBrq&i|4yrZ|zwVw*pSd*pW1XvbPo#4cQD5u(DPDNQcykECK(l z9uTV#>OfJBOCT5H8{fM8*BR^!QGASV-pN36t8vd}34$#v8i%SutJT=mgw}4_u4bT5 z2HPlDI@KO>X<>mbc+QKKAMx5>TR75P#mz^T(eGvOK1IRBfHTN|%M?(7cLa(A>;h$9j-7T%PKP96i!;5j^-KV6E()` z&56-@v~T}@T3uR(SE1W)yUias<8%yqT`)yZ)x1)@hI>X%9uOR|CwNvkmZhPVACXVtWB`&r*n5*55P;N!#5&*m zgFZCNK*Y$1Anb-6@Dhb!dkEf@K+i_za3q5YqZ<`a?OPW52mwv;IPbJG5MBN$T=|=? zah_Q^QGDe}r|$=?<+R^ws#lg)r=v-=c0z+^z~)4b>mTK)95Po_>!hB@tI+FT|2n__ zv@?+QyC9A%O$NXe(i9MrE@$L>KMkpp9GU2h(RJCdLID*I1zFRk7sTZ<0Kz@)b|J6d zzO*wo4PI_H8sSbPP2Mz)exq@!?hxL2(s$w{wplKz*lQqf%U=l3^OH3xk7{!r~5E&No8wu zWIZ1;kBY~s8ax9hNdTRfhIgwAeYD|Wyc>Bln`QH!r$fulEu6ma*gyZ6Ui#+Wf5aEt zJ=}El4OmRusHq1FFhB-M8PEtGF_auUMHoZ`4-+L&7l9~3h75=t21>}(WVWHku`0Pz zo@RhD1O)HB;lVQFv@e!tmj=<>=DkaPMQzxf8+;MG~s zD^}vu(Bx#eIXiC%LdjVzo2F|0-=t(1PzjeJfOwD0XDAj34un_#)K8(^Zjq`;We0e$ zb4#kzb-s_h5)uXa%Wo#ngMd;N)kf{d@RX&W$G-S)af1&a%@D;28@trQ>mu;=_{d$|vR+8%}s zs+I^cjVf<_C`?c`*oLD}?Jxilph4V9$uQC?p?xJry~SyTLQRlXg-B(7DZ{;b0mEveAilv!HjD&3oPrD5pK*2`~Ba zPhP%Kxb)%pEq z;5hKP{1!*v!F)|QiqZVN}6Js2}dZ#-hKN} zzVci6#><{vF0VGIU(q14Dk#lbq(sNWC^mt!umVNIQ8js8_3VmDJwonQZlYX3aWon86&Gv#Oxa|HtPa z>_y?2AezCk5$$T|p^#y)Y#cTc;2jzobS=b|z+!_SXQ3#R%UikC9kp54%dGxw)h{EfGcb`pgLAVE&r`xJ}}9)gs_G*QF@ z0(fSC0k6(BCN&laQ3qu!ZRPYB=Il%XOf@rVJ2-d{`e_fzfjSCb`zo$}{$*7@dUz0( z3j$a6eZYe~+O4M6%jIFodD@fdj&A_Rak<(*b0w!ize_*-?C0Ki!37r}LuS}G7qZPT z3^POn(}s-_Gg&ZT&`l@@iX;k|Y5>9nkOv_`<`so<5mD@L*E^rf9!m9vJv({Z0M^!J zzBx^Dnme6wo|YbK)TUAEI++6h?KT*XA2?42DB9HPl1L97Ix zVw}4I2pjek&ejx%1_LDq6TuOKIf5!7DU|>sW#%pIKY0dpo>X+3J)7}oIXaG;?JXxG zWnO;$qa5kezIVMqZ}Mjll1i1D9jji73Tg`l>Zh47s^$AX;2Gz28xee(0&3Kvlouz| z{B{60Tl02i5Td-PHK!TRdsEC&i$RP)AOYe+gl}E>Usr&7S-rC8%H=9G1NM0wraCTU zvzaARR?z0X_BUI{+wr4YRYN?PY|{XGngJSp&7=))vxmAFXX?qc+2yuT>1$pX&1#|G z+P#!6e*LdxPx__*v;WHnZ^NP025MO!5xQXM!8=BeBBTVQ>b<0oSnGMb=dz>Xl(O<1bTTQWT4^iELa2}K%WT9>`<=NkX-X^ zeCwq@wCFlVJNqleLZj2yl_>J0nGPBzRhvUM?0IkfY&Weu>NN8zhixQ7>}o@6NuAg^ zFcUd)`saWC?z07oL3e;Ca#qsQaG=O3145S*5rqu(T47pAp~4nSiv`xEf<;ix5XCWw zfFJv@7k69T4r)oFtIJ1s4EB_NX1mqY6HSY?Q|vVx`J|o1SZ@|U>^8soX5T+82lrNk zw}=?tr{o;LMG+)ZxR6s0AX8(Y8PbLUfbpjfsl*W0iw4y~Civ9Wjf$wM(!Txs*1f*< z=0cuz-_54Ljh^LZ>u{^rIa+nyjHh|4Hp-Dc`tz+MoF?68v)$5mr={hf2-<0{N^^Cf zy)M(ezUTY?_>QMtfM%l!I0`DcL4A;5`yF8W9RUId2?sR5LCFs*fu6|3tRPm^g@VK* zVnycLIC$jxkAeWNF4mFWfN=(+&7h;>e!bmXpHqB0NxRM7yV+hkY3)qACw(VS+Z_|7 zOuF%DPBf#h*=)m`Wr2<>3Y|>06`bQNwpfVbcIx$#UwMPS*Qvl|()SZ)B0P08nRH9~Dqg^siM6=z{#_F=F68-6)y<3PF{caCY!9kUvYEBj^raFk6@(7_6Xfd@l zKPgy3AfN;XHB26?UnR>F5fi@ott;`acfKPjxWX}OY`Z*^lg4y(z0E6lUN1159!s~q z?Ak1W>7?~LuYQKjf}9i0cYnNa)a>jX>#fs?v~YHyG+}P>hHvJ`d|Dnbiw=7N6dDvy zE$gR%B@TgbI2EvC46+mi#VcR=3ITwlM~)Xz`A)P+>twUJKkC~WyS&%V;$Nom|J`;& zE0r2m7owocecC-Qx!04P-DvbJ89|1x_fWt&FtI`DNUSF%Y)66q*#aarUo0WzA(o0B zV)BJN%B2LoUJKpM>gS>&KuV5y^CZxEer;0Bv!3tRj*s5#y_5FR&3yLB7_aS=9rJ{@ zbDEgec6))2+R2#7Ue6k-_m6zdFQj+>@t5MiuDTKAdnor)6bA!%Rd|k&Q4y*NCkjA7 znHnu6<(#Uf5~yAgh2U5ZvbB#!Y zygfsYkg#Tg_kv2L1n(91zvRBGr0A#pW3G91o9k(}vdilQhSOu|);OtW`QCiZPaBx7 zyP=&zLQW^5QT`nH>dinDq-V8AcUs%s=YA zH4z4NIApLM15bF~k3OH6_@sJv|4A5%~s1a36#JZF!Mo5kSR|pA*jSsV0c**>rn{}-mv#lz(x}Y>SMsgNX4U@wr;Rs zI~}k(DsPa%tyfP?%ERxb0C_Xp^oW!X*7`WH*}Hzn7w2v{{GEL8^9s(-tVC5J<_ll- zOWku{_By`e%9~I~TfiVguMnfdY?xFraRCUyJF%>#3kr5HrVOK_LvS1+>&Xgo42~4) zFhnkUQEQk89qg(w3?eCnXjsxx<)P#>1E$^-%3+8c3JkCxK)gXozW(Z$K7y|O_Kl^a zECq^^_G+a{ zwaWa~&MaBhdCm7)lF)1n7qdOnS=an=CFUu{cPE04?fAsa0*(B$w}LO9#p>1SalU?% z4a8HU)vyCTlLgYD3PwawVT)%ZUr$DtNl-Wvs5-0kSREc3`aTGPd*9<;@Zyysx6b}G zzu+|=&*S&EWB)xmC##iYOB~(n{ciNxk3Qec*1`B1oL68sueCAG2(+G6%ZssaG4fsX zOYy=TA9~3%PFr3wP!MPUCj!v|NF4|!53iviUf6n1gtH+Q+HlSUB8ME3L13dy41Ce* z82vOuwOE9Mi^&^=PcyNNcpYoBTDsX9JQ)u%)7r^G-M5oAHPAF)fa4EzlC$1uLu|I& zI=(nh7K)ecn456&--;dVO6N|OKI-}Rebws=m)~*-)b3z$&_zi+Idv?0k)y4R+Gvb+nj&{-5tn+JB7U_ha7kn01l!? zJ4FigMMB52q9}scr})ob__?Q+zxjpF>@7%Pwbju=QlnC(O7(CmZ?~G-m=HW~WaD#Z ztGt%ef8SV1JE$c7-kX2d7Yi}cG=&RL7Nb033`RprAUH&b z05F^ie3pTt2$x^}8GPUae|^Cu!IV~~rCa66o)j$4l4YIOVYJz9>(pvvb380eYtxMH zPGpg0^{ZR??5!LyE9*3ajc%5spFb_;^^>2v90Ebs_we55`9TmX7_uft6q>h`6h8D` zfQ<**788TWfujg%D&WZBRX_Dg>wdJw&p+6 zI*I?+HwY8edz+5R_toh7BfA%Cp_JH#0z^KelGcRlZc?-=wQwfie6IT1>!uu;R{ zX3uDr@!Jly+;(X`C)syGT(_AW=RIa!ypvvkqEh_h6KK5&wb?}5%E@WF+0H%Hs-vjP zIP3g-KK>8?=$+qOYM|Kepb#@M&*(%k98yF8JUa+S@T5?2V6TwSOydFu*~0{u22f-~ z$cQ1rcA5@N6B78H#YMqU2!x!$3t#~tGr@>nKOiL@<8Uj1BW_4Tpk(DzZWu+H2$D z>%WS@7yjk%$w7Qv>H`))_Bt(XjNq!adrEEZvceM)@xJ~0@yb{Hl<{I`nPtraww%-t z>9>#v$(mDbVOT1pkU;~fHFyH>Fh$5gCa5GeNapd#M_lUsoDOHBb}B&j?+qN!D&PH$B|##537W&^Zc` zoCnK6KL4g|%-%^G-M2^QY0fL2hLT>d)kD4f(Efey`_xCTG}=&fO*IfvphOIrWszZG z0}y!CfOBRLg=#J_pgv@<1xjKVXaLM6-lGb58lc_k<1v>$|H-w&gWG^c4PFh5xuOHu7c6*SM5rcp%bOp1A zQihB@G5|gkP?mu+14xskh{VcQ4uqm)05Kv0SOf!P5C&59@Pbe-wNCep2~Hgx0R>j$ zICg}BWYA0yE$jgiVfBjt?0)}~e(*ExRCKvzS?1ov#)$T?vT-t$t2Wvwh>5^X|MS1T z;1xix-wT*c0W(=vg2{qswdPLFX^s#?Qz!_g41;10m^-n+fe4ji5yXloKJf=W01)=< z-LvKUIZm($~o4{?eCCe^B)x>**3 zriJ+A^Jdn@-|g5|lL_A`IBK3{VQlu=@r`9WO^GK%FtuCOXt#AmaP+_rKK*?M9{LE} zblr_8QvoUta3*8|0tT*xL6lSP5a4W?r!Z1xAI0!Q(0+u}GddFEU|hoWNe$PP7jQ$Z zikpiL2T{N+U>u2J93sLJ#mFQ@2NC*Qgt#L5P!v_*M3F&7Ve=|NJk{ z;<%toN0!E_KGO%DyAcpg%8+c-ESzVx@@(d*;7qr2jnh1z6Tz*$U7L3XK5_-U;kp}+ zX5vwZjTJ~T0lWn6^iU|(you2KA)CA>z?Ky>T&lC+h6NEjbUJ+iz)N5GashzG>hehS zYw{kmn~9&%^_l-;Sx>h@h)qx>hXug|$E@aI{v70j0wG2yIP8$8E=B4H%ZFES&RKWHqrdkR zU(BRfYj#>%TjUs+o%?wsn^bL|73V$=n|CF1Cn-dC!bo-Y`>!UoxhiLEWVPh@PSsjc zqg_2^O))vQW95jcSFA-Z`n!J{e0$&Nxas!WP>V}o35{o(fg%^gP}KN@Lw3`Hz>*;X zV?Y3i(e@4*izx3a~>;g&`cl*zrO( zl}!pP1Hq{v@`@}592kQ%g{xHYkaN$(t?&B3;H$oI!{wj&_X}>l?ciXi#&4%>aqoU= ztS-St1@t-{0SWrOE(%fE@aB0|IaHX3(RPlr!;oylaCN~&10kEH1abgG5XS|8Jsg*q z38*GDtTt9ona%7w!RGDXCi$?fT9VXgJDb)#u4SJ0lN%B9yWjKw{PUjsJoNj06SE)( zK!aQ|S-G5Sotc^^LO%fw)#OywGK8>Z>Hc7VTCI+wM-Jh%GtOYiWJ}k5qWRD_vYmJy z^RV3VRN6Kq@2Apfe zTC#(Kl&nXnEGhv)a3NFeRZwshL`;bGmhiPN|Goe8fB!*2bs&H>LXyaiWb;qfIq;os zTT-i-$L2C^7GpAL`tF|4Hw&J()6Kq+)LqeQ5kL9M9Xj`1v<@9YQ5{q=!#FE}5QDSO z*_*#n1Of#MK(2rkFi{hANWBHW9bpU{VW2U@Fb~s9m z1{}KNkP@RCf@fBCT=#l=OeHdb_Wt(*##ncd3ZjW-n@uNt{En|E?x)Q4^N`Ar)uA{}T> zflmgp+Q0uaNG56wI$OEt_RLgpyJaV9;52RUJjfrzN-Wkxu$2d~ z-eR2Pvu6F??UcKE{hlPi+4p?j3!*RGzJwGJ%8@kzD!U%e%{RVcI-a)6W(QVu88uxZUz-QFvNsqai&9Ww1?WR^HFfVocg0s&$_v{DU z?~!zPi6JqdG(aSSaFPN*4hn`ypnWA{-B9s>S4hY@ThfX`qrC-5UKJ365W(}B{FnLk zyir`CsYB*tWF0}Tnc>k7f6*n2wKMKcj;OX^P>$*ro1pW!WZHRnqF^{DS`2D8ZEgW{ zyVaPQj%r$4??FroaMu9zlPAqA4|SH;*0#}|T&*thz$=~ms0Y8Ie)7Eb3=@;MsHM5RQJuz{W^(qzZ4Nmggk~Xa`n3L)>wbN~@iyQ%p zPks6m9~M;%1}QiqL<$fEr9q-xU~mYHW3~*3!Co||J?8u-BQNp_Ni#&H2o8k%J@mnx zcNjL>4XsQuzjl(|Ef0RTv$l6KX#7s$8l6}6{Fu+2qA__VJH{B8K=3 z@E+tf1U&hH6eDX000b-}i;qLbQA34}8R~7R{pN4}mVE>hE$rDl@xD#mV(nnI-a&mQ zZ8Y#XQS*DN^q9M0<3URl7YW(ozDGUuHPU@##s0uMo|1l7ns*QFpy;o{rSSnXl3aykIYKsCRf z^#gzUm1=y!t6NRY03cIEo}8&wDzs5GesunQrw69H$!;_+J1&48y@?Z1vYMuQe{>?{ z*Lb2DFJyh5^bM_6sn8(HfGEPfp84Weq(_%ggd+0>-2e#6f=n#vV)53Lrq(q~E*mmC zkSbysz%$VCz+sBfB}R$MxYL0uF04A7eeinR^5);T<=P*8^1Z+Pzn)zE(vLi$`i1BI zK=t#_c*5O2_4Fs)?JGa}j7MDmwl`cGefhJvYoUX)_tvq(MXYiRuRxS$h%_@wJLizQ z7*lg{5HCo{)W8bJP)suvsE9A$bQ{k2Uq9&{^q>d6V3zS|w2irAD|pXi!~`Y;5F2H9 znvG*=t;7qx`7Lku*N-Pcm3(Rv2tXID&^Yn`#YW1#M1F-U^GhUHE%nPcl#tOV~>}KPgMCZsn%#a-@RV; zeA;Z}q(FI^#@x}8@}v!Hmgl{ZjQ2+00RRj2MFN07e%ssMLtw;(0#Xrx9KdT>=(Q#T z8VVBG{}TWnDi{I?Ent9ij83pG^flWm<>My^LyhV!UakDSj2bpLxl=|A=#xEmU)%P6WbtthL(fy9}QB-^tH8k7Yw z6LTz2T9%l0SVeF3^-!@83kq+5oDPH9KjQV*`wK8LAjuK`jda|w+nRo z`4kcLLUINGnw^f0?s4ODWFGTxI~>ou8PuMN)T)~{5$;4Iz;+v9s%}{u$@J_E&E`@bJgr`Xh&s34`d!`knsipmJ~`3Bm{B-MFbW_kuqf7L0th$ zEV%!j&IaD{i~aZi;qRBG8Jl{gLf%DSk*TwnTW$2LCsk@p_>R5%_R{jwGM#?fe!lIt zLn&C%>+})37@1_Ch>i1E*LbsS-_GSG(5fM)k zP1$LyRuY;d>ablqS(;m_l?qK#yxiSus1lK$P=SH%Hb`#gd2Z&TiI`PIvb3)*Dx%$P zq7WAX&Da|K9H}uSxPXT?JnLvYZGuvOQE!rC^!k0&YBjJ$*W?_N_giZy+>Y&L1O7Bt ztyF0vT<*!RUskJi8mHu|RI1eJw6x%eD#>||eZdd??njRtsUq!m5fwY|8bMSMse?Gn zJgWi`2pNF9yW53xn5YHCye1TDiH{5>)HCw!5{_#KP!A%3vI2h1$EOh?5)U9kgd!IA z9>8tOpT}Q)=(T0lR&V@1c60#eXeZnJJ<+x)gkya1%tQKgyC;oFq~GmLD6#VEZnoRH zX_jQ`BHKPe!Aa4)M@Dv~bIhIQ->nM^TbBvyg zF~}UUv=7ZZ?si}=DqsF0u6gcrp1btNZ#}(o+x17P+SYxwgw9wXcSaN+*jFpN{d*ER zvshd_eX-8x)JqGCMYsbcifT7ry?o89pU+?YqqnY}ao_+3g%~{+Xrz#k1!C5~213vv z1frr)R`k3E2ug~gS6sKWgfkxd#NxSkzT@6$#zx2isxy`T^XEP(<2=dojLxLsd0}BM z=lrx^``VQ$F`?7y!o@LCcq{iQKybzjnrFbmpLsS6P&gQF(>k9*@mT=%8HfnQI6}mV z@B7|Iz0{H8`r@8lbxwJPb4{dV<81e=HvSAxlpw=AgXiiN)h}C}mTn~&86E#o<<0f7 z(;Hm$)A#yz*T}!G|EmufYgoya_|M6deQDaE*2x}IwOrVK1+-dC0KiYb=CvxKv}gamU7w57jQ?&{Q8sI8I~juK(Ij6zr@#!? zok}r!-1j~I_p@{X{Z1cLAWIR0;5dQ=b`2E;6X0`<9r4=Sfhp)hP0R`kcqJf{k>EKz z5@i_VUO{T=yeb@c$=4Ko*~2o|+DGO+jvPLUv-jQ`PkY?YH;9s9B55>uJ{iPn#%&xP z_&>q(v35)VJsq#?Yy)_*;98o;G-7S+6hbQwCl`u0effRW z7*Sg_Z4OjZh1|N*?RBE~@a>IGdT^=bZ*8@gJN<#&wAxud+SBwLP8@2=x9S{Gs<`P3xSSmvlw&9bJ_>WR-F##@$@bQ2BcYO9gKKnMWUdBty3pFck z?^Fr*xRP~PC&!9;Y(A#bZu--7p&u_3ef+(z=c_7PJn-gt;b}0ZM7vuc7#;sn<<0e) zgxam9&MOGp$X9MP6FvSso9&jix*grK#{$}Sz3Wd_RRmE~fOthvQ5j;N?agWnP06Ib zIcG@-KhIN+dH;bRl;VL`zw(s;Lzj;nJt>2M&4QGZm5FCsy!J*(sd294R;zIg#6jRt ztt6DxDJF7!@4G$i=5x+?^m&Jmt|BgHQ1k(fLCyi9aAd3+lp}sLu!<{!V}?=IDF=cM z3#d83C`pimJ|JrC{Nyxu??ahyqbChX5+%TCNT@h~Qy^~R$kA2Y`OL?~kG=HuU7|`n zyytKz9#xf3cAzI>E4C9=$?qgPCV*Z)d%N`odn)2uMr|j`&rFPr27QU0o_&vd;?N*N zkr60&oR$BD0dFT;`{1qcg$f3) zh}9^9XAK|(3kZcx7FEbLITDjNbsiug^ptQ@yN`VrUi26Mh@5l#_U)zBcK5ikoi*bz zO`ttXlU=jbP{$F0pznOwJNCi@{a(+S1&5*zLEs@o(HiiWp8QB~#Ry`qaB7X7c@Q7~ zBooAi7+L1=s7F6)2)!H6LWo%DdX{-OkH(`};V>I*`kP@t#}C=Z%S0c4@1xf@8ccV# z*b9;4g{PT+`D^kZ^+W_Tn?2*LWQxY;-cI3Q^XEA}Zp+IS(7yBky?c>~Q7jc~-B_G# zK@NiYs?pd0Y|UQ`3e;j&?J=<;XFVchS!;C;KJz&9>@z@M5D_mfPHrYVneAkNL`|}3 zefLHpOB=~vSCSg3Dq5Yk_S(qyRPS)lOD}!FX-mr)hz0<w-NM ztyHZMs1*5BfkKSxzI`~_Z6Q(*b%L0TzDY^J>Yy3mJzK!awqR2QSUsSEA~R^vM^@d7 zy?%hN|I#bZjRqYpRSIZov0bjzh=`qU*fY%KPIIeQYeS4%%!I75EY)hAR@!~#TA?4e zmipiN!2568Un^qS31ST=agFSBu2yR$R=B{~D#0;jP!v=EFqZp0lrDJ)_V3?IA|l07 zskqTN&9j^}%_(}c+ga4pTi^0~;+#V#>miCFFcHWZT*YoV(D@ODIA_w(WTN5cEC$0N z%WP1nW+($uA%ei+6|a1`AH_Y7XK0CLUwZQ_yUxP_Zzd@`>5x53P`aH4;%QhG@<4gq z+2{WlFH+6cc(=8~&ge6qR{%dg_coOe-KHH6@2OBNsVfojKmYSTfk-U_Eo&H5$`Pn} zVC?g(pMuz!6F}C~tLg!A*6i#Rkz@1*JupZ3U(bHd?EtW{GP#*>JGR~I|H;m~GjYR%>#aM&=E7~%xV&MHlUknOaMLzvMwU2S9nEbOV_ zs(<KU= zz@Ocv&~F0e3Vce4DQvD38(y1&Fo?2cfjCA=4h1!uyT~hAtp@l355ir}yXy~&h^Y8= z>)`dIGHh(wEl$+D+nJe2Rpn(bdldj6Qj_Yo)+FoeC!vsu4kct7=Ho-NrwPwk+AQDj z$tuXIkW3NBF%I8$E8g~pZ!5%wvep;(OuWzSa(Ql-;_gJT+pJ|Ncb3L$nlAS|o13qd zwSEfw_juqr+n8z=gPygeDQ-_zQb0_aJL zVYfHhjUM8}l5e}I8)ciedi}E!&Xd}LRRG1(-UBQVq5+Uf1eM5`HbN%FHVj+Il6!k4 zz?(#O%mm2<-AWC$)myRj-uFD7iJ*mQH%gThgm$~BOa$Tr0AQs#dEBa%3N_m;btD{n zuTb%lg6pMXRaYB>Yd-#ITv*SpgQ%jbfG+@PI)Dy< z181M}%EL#NG)vQTy)nuu>;QYt+i6@KcQhMq^yQ>Vs-Vw)?z2h&y1fp-<`@k(C&TnI zx$s0tEU*s1y1a=ge?7sJ&@=?q!azAGLB!*Xv(6$AbkOf;V`X_qoZ@=f_k0c4lIlzX zNSoQb8=$7irf)R^JL!GvJ%iEv8t((y&LMB3;P+(3x{g|YjoV}PuraSBH9C6qh?WWk zz4ew`8trZy#ZoCS@>&)_6@hvTQ1u`q%Qtvb9!Nt1gv0n2C@%>K5Eo)}`#mJ(3Lfy_ zhy3IuWx{;Whg5015Uk|$U?=vd$=|pBx|+5TdzNQD>mg4^;0)wFDy2Oo`y4L^_MUs` zgYNg_>cPVUWC~D$ZMXD>S@s@KXNN2ygIWkoTqm%M8q|(-IA1^kBF}3qi8A`WxPAQ_ps+mQ-m{r~EuQJ#(9taDLEe zD66nnAg=8_O_|a6KDc@xf>S#&t5Sdnu$7PkcB`m@LXi7z5TSV5*{FTx)4-u`e@v#!IZK$}97EF#EQ(f|p?=mK?*jmbk4#@W-Hvp7?r8F+j7d(v zRx|OGUZ+hLU3Ad{AM)UbAoCg6MSvGLn6or?4GPUmAtM$VnXFgIyt8BE#~%OyF@f2E z1LYrs@VmeLdmtiKSpxvdsLdjRupW0c$O(%}lG72Sk(Jx85_@ z=)Sg7CD>>q+fC?PKE9i6tmA?&>s{kC8+860!toTHkmM+q6Q~z@+EbtQUH~W-Vr#(V za8T8pk%J*?y5HebIbfwd0D=#F2S(S=h$sq7LWF<#yAS;V0JtbR=33iXLT7eaR4r78WjbR4{e5lQjrjv z3@yRpoCanPgoKMAb{SSXZTQ|k{P2^0{*of^J-yRGR;v|=WY8auPpBNv+fGzv@jK0U zr?VVeY3kX?DUD{%)_YB}-O^TPI~kFk9TR31B}i0=U7_H>nLxl`=aEGOA{RX`2VNk~vG$Kd}2H3%CB?L9bern8>omNvTK~A*OYU*Mw zAYy^qoD+^1VJ{?*f~~wjFp}-uPWCIQ)}~^Wn3wIO zQyvY}Mg!KZ9Ij3jHP^{dxf`7iqd`|bzMEaoG;-oVf1j)?zA*hMZ~fG%f~+c zJg**wxCqHYpu{6MXtIh=&d-rI5F&^>k%VA|j7pM*Exd&8poase?Zs1{`jjt;_s2Y! zBw4%u&DPIm_c`yqWWCQDT@xFX#3wPJb8;VDn}#aqcAxKq2A zAue{KVtFA>k{UHT{bT0WPW$uk6lK@lvbkZPw`!BRFgJ2aqnWdv3Uqfm^TSFJfl=67 zf2;zCB}K{r3nAj5G#D0Sz7d#UiL(L zN`Ydd)oy5~xvI^kDeZOIYa1cKqu^B|C>E+E0&F8&?RI<`W2K;;(Q7pey&l*R1`K#* zMB<^W7$}218_8y-$ebT?wQ`Ww_3W_JX`y`99cLP=-N?f2+qajpETc;=edufMa_4iQ zsSM>^8Ae%0n&%%vNl*gQvj&KrAuR#8zzM)$gKNTZ1Sq3gtpcjJ*F7#e%ZpNy)KMxQ z@32}kP%cigy!6~|HD~4>t|iHa(z5?!vs)ghPBWJ`gDY*e!EFYiX|~%#Wy|g2>({gG z9tw0YK=dDrvM0l z_U`xGi$LIzg|1YN75UokVQbPe-wq^gqd8QqRIJK2mc_P%^yYJLz2~;syxp#kuGZ>w z%t3ghdo@qM^}CQW&v?LZUwq*YR&P7hMqKa^48TxiA+e-lA}112VBWBQLpc}bHn?HP zG!tXkJV-&fjEF)&9xjJ1z@`A{Pyjw>RI(^LAV6}GMGZhc3ZP5lh!H5rGgOTYM8rdp zfujKqEu}bT{{!&k@Bis*H7iBUfn>}+Q&l7r_J>(*mMY1pf0(Czrv~PHq8T=}(}lT} zYoAxpy%{yvMkat{5JVK04tk(pEu!K(5GbH?bN;g~~noFYS~L$=gWG@j}H^AYp;^u#X7}LKYZ~JiJio<^}?(7C!XB58=9N zue)7UrMbGSz3%iI^lo5co9(u4mwIyEIOy#<-&x;3?{Urp)oDJr*=}x_7}zYJn}$Pp zngI7i;;&oXwpNomfuKM0bHBh@=25IvLI@-PGdu;^lt3*>BNSs27&<8A5fuPDAqtdt z>IFiCe!q`Ot%hg*(6g>p?`d)GKI(LudMrd}{u}>xAZZ)fdSXF#ke*i2bz#?^yQ-pE zS>s@Dx0+h5EHVHrE^>)uqKNN!*%Kf0il1t>Qiu*9;z4k>M+T^)gAdZ(ri!Z+n*P`X zcNh_n2gw5adyufyVZe>h2ye*~vGlVA)Rb2+^cc_(kkgxn1D)m&OoMJ%4~>J854h-n z&1MsGi3`BN!wuZ!?1$6CAM~TwYOfqA+DzO(j)iF}2fA4gv^!B18@=x8gc#&hwH?nu zuUAf*Z%6U;&VJUQyS|PC-He)RBO}AjQeVj@4S73@c*B zDop91m=S<-F-{9H+#o|FDFlQbJERiS_7q??wZw=FSEdL&)O=qh~ zg=$heX0DD?dTsW(`FlrSH*Xf2Y42^f5-atjN|lMp2!)F3Anns9KlN!ajLOro1E3%# zTZ6;gq73OHNp&cIW#?BDe9AP0NDMe8WU1j@|LARRzY~~sWyyG`RcY84u@#n-aes9x zG=t{WZmmukXpi6H)(#pu+~+uzPVxa#M19Rl(PJL<^1r3{j%c=9h@&pxJ(L-g1r>L0b1aJ{j&*(XZV~@C~sGfG#4*`I1L}a{cLmS1R*UJ?*X}Ic1m0Hb)uC{D) z7piqy>9jnnkK#;$v+i^sms9{B!}$S-GbkB-kCOm-P=Hwsi{@m_kwD?SBH{uDG#k5Y zqupM&$!)rfUQ0I$=$M(HO7D2bA1x>Ze&CU*2Qq~th6XNBQo}ie?Ut-um;eB9D&mynIRiIU8NaJcGQ+ z66Ui71>*{ulPJB8S6LkKArlBzcM4{4vRI?t($0ir#OR!Q^{Zd;NM-^%JE&4%BC^zB zP9bKEg~r0mBL#H=-~yY1*dbUQ*fBsE(sY1@#RXjUq#yi3&dAYhH?`SZ)moCw+@J~^ zzqjr5VdO+Wu8zOQt@)`*zP(ym;QaY#K8`NA_tU?8mvirr)x#|~HXQdGKf08)S# z0eeQ~VyJNF9uM^;*di2_K+_WzwJ`eLAp)>){)JDcI3^FE+gg=LPz?ZRbUVJ?Zs>Ss zOXS#F@XFCbl8_QI4^ZWME`EEHBOo0>oGCMj0va(4yy3tQb6mM>ZV-e~Z`tUG9a{Z9 zwrd-)D9mgMdh1)?;+>1o%RGwZl3^*(S{kJEoPCYT?@4dEIE@tXCiCL4wTg=W$m{T6l~^K zj#pV+^C9hKV{Q`M(+|ws#y@b2^6Ai&1S>}4Eh4^6@U5uzq%h7W6$Q)kTITCi0DKGI-9N6`bY&L zDOZWrx!N8o5k-9Z{tI64pa(zgdzKG30nVVp0qgGJ#Ut}M;VS`=!c#yWJ8K;jQ6p^u z0^KY-$sf&*eC+_J3k(niaJ1wmx32h|N2~1zA+{(s2`kp+X-)``=orf;$(gIM_#>(Md zs|i#HEoFG|c7k;-^ilZXf;Cu`A`=1?7;s_~Ii;qoO!?ANB1nQwKRKVHyfAC$FdmjCWA>yE(;WQ9X35QN&3mmO%YMh)< z0Xc#Xh=+c+In!888s`mZrIO7vm$?xUi;BGDCw>CzGq4K0_%+#X8p&c&2#%foj7bC5 zE_7>?NwiznAOTLnj!-DYAg_4PgC2NH>V4Wksg@*jXVMt|ylF%n=55UhEIHFsUQT-c z)eQo~X>F3@pA|xJyt>}(S?pO}zn+b+_jYqK&@L+{qKuk_+wvESS3gZ za8d4X$b2M8p!u}*m`gAlw$-q%Dr(tC55u`?2BHj|b{mC40Z)JW)2<>SUfi>9od6)2 zsWNjSt=VoJmn50z+OCZ;1#L{nX!9$d3*&QBRZvgLRIe0>B1Vs0p}DNRqA23TvDZgz7Tk^LMyQ`vH(1ULHeK~tB{<56i@=^doATG z8UYHVc`z+Q3qVZ>c_Or zsB44}r^Z2blFbCL+3E>)ngN=oRJooro^)O7UF&9~-P5Y#=ytG4-gD$!j8?PTTUkX^ zbjX}T*O`K!hy)Hx2N0VqZQe!99?lC$Qc&ywIP@DUXx!@};K1o;Q$)l*D`x7Y(`{>| zzDNLuGScIgW4yVv)SFVPdL7cR=S&E>)Ep^8SuBX=z~IhS)ysvf00-s>#DLcffA;P_ zy(_eJ9~R%`v7DQ^(t^`qB;o)7W(d$v@Hpvt$^w z54VYG$@e|#dGD)LiWu~k!Au5=4*L$18^Rlj7|bL4gdzy0u!^} ztpPr;C*_}E8z+a-Mjl*ajXqHWghMF=+o7R6C}jpPhuxl0$bfV$av*l_42ZLAb~Z31 z30TnY9R@@2^e4UIml^v{cM(yqwJJ%uOkT>A6f3k)tx&6dyx$vL)1!&Y(YdwWJezgS zXXhNk5;o32e48toL1`0ro}*hH*za?ct(YsSt}4 zW9Jf*KDyUlk8&YK7NoFK$)H5XNQN4^OF++A6FIk;Nn-l`Z@?8 z10S<1Ag(9WX{^|Fj(X6lx0d@27q38+0(D!)AvF8Jruhse2^C> zwv#id0-1-xDEN?b`$WMZ2#`ThtrQ`*-8{`Ot&Q_G_`F9ge?IM2Qx_NZP>w}@>XR=s z6-6-|+pz2g7o56vR3GzSUdmH*#73SUN_~NHP`ttwB4j?pPrd4uqzYQtvzI1a)4c9& z8ryB&;aLs}qjX!d*ffXUSp+J^$FtR*p2Yyzy^PAqSdyOyTyAu%Tg_EfRZ&rT+uQyC zjvZ2OP1Y`U$fdnQv;*Ya8>&!QYr!(*LJXDWn2aA7s`i=v45e}jkA2Kz|B8rsX=Qoc zXE<6_n6)~z**w}#fPSK}sO#NZJ*iNhv94B*&sI0PZ6%7>Lq({MLPT_ri(c8f)7g*s zp4F8mfC0P^gyRsvGBWkn+^3ez5(x^h!97z@e$C~!L3zNN(;tpxLo^oy3t4H+E59D0 znH5&YFtfs{T2(TLXAQI4K~i1;fE-9A{LWy1Y8Px9MQFhHa3FZFQ;_%2lmTj>(Z~?f z-K$T3@|$i|Uy1;r-R-I6VV;(ClA|A9a0@`ah|Q`YqyhxX|~(Co#e@hw3!0v z?R57~!+p6^DrQ^#u89o}LwVN`;`eHerafGEFhqQYs5nq4Zu#P8uPs5KT);qqfs4Zi zz)-jlI_KTK3SkAiA^=6q3YU2}Wn9tDaPE_yhUmge&Oem;)zxNG)5h9H-e`9f0E!C< zwMR~^`NS7Q0sxD(G66t;rKycY;X5}ewnP?%0OdYa;fFLOJQ^XMTJ+OD_ zFs@zc;?iIHtt(L|y8;oNSu2(Rpj2O=Lclf^Ge$j3O7MnLA_pb!=el#zLYsF4RnE<$IJ!ry-D zS7sUid9Aq7%vc`KKIKW5ft3*b96>o!~x&tt*}SAvJ@<6&P&E%d!HI~!_E(i z*u}X1x@+(k@B528PHG&?AqCsvL7&Cu1=HBhCcP7fp^a?ZVV|gGl~cJ%WQ5(a(kiOVOz(czyWzm_Ucl_Y>@27u0MJ}R#ln_6U?vYpG^7>F0eFP4roEuxVsv{0 zoO9;cc<4hP{3@x6tSaH;m$wys7L+p+uF?r zva|<|oe7tS@$xI>V%tHw8&*xwKs##ZAqQoH@BnMUH4xY`SOScPEYmCk1a=n<$E#cz zg;ds{8>$v~2dq6~tzp$Z--2_)RTFYv#azIJLq(Zj5N;x8a1)_WArS}$Q1wXFfn@-% z84lgHf_q=^taI*r&*$7mL|myk*XcIIgPkASSw`38G=66u36V*$-EoJz_Vh8*sbpIY zpeJD|f!P-Ii7q)!WAiS@wmU6tcDw3(X&h5Txbj<97r*$Qh!*x@ARc`#f`x}!=nNi0 z4E5f+co8~-LC^w&1Sl|}>N_}imA^E&!($$gn{K@w1_kzPLO?o&6s0T(zQ%Mo>Cy>U^~DWFRh56Tq?nP!JW@NZ{7Lb?i{P6d3>;U>Tt(S zvPs#7-3Tg14@%<>d#h`CyIjm0eeZ5GrR{{D9etMTZPc6X1?y#`j%#wwSsGO7M_=%x z1P03G1X-3EH63xFj?DWoFw8I-3@fl81Q^?Bm;_iuSX6LG(*cwO?|R33eups|-R87* zA~MsnynZJRdz0=tpZA@1Q=5&x_B*1grQ#DF|C+Zu+@;X#9D>GT+}(;mKNt8hC<4^r z5hNKD0+$PpIDkY0jdXxM2Hd;F;S$(d44PpcJp|Y!LD7%{$&3A-0}RAIG0-H2toItk zk^r_Kp8-gSuJfRtm_Tmrn(|K+qR?tcLue5|h!BA?26Gq611vA4c=W?waN7BIe&la= zTXwqZbyre0>6qb|j;_mT{M|J1+E#qjsd%{CiX9g~Puppn>uqd@0%_Jk-s)yE+CWs> zValdyukm27yId|hMZeX(_41E=yIx;}sv;E!A&*Q19tsu$kq4Z!lBBbkO^A>MF5(Q9 z`Hk4&y5(h@^|&YD{_l9dw2Bqq?)Cbdrcou1qjtBevG+`1KvMKuO)V!2tmLpiDa9p1 zR3(c2V1ToJ`ZxRMz4#}iFWz<-an`e*VJ;d46bXbB-ce|%unj6hD0_ty{lF7cV)RLI zMm56imw#fgyuAE((~jjN$2;u?G&;8!Ym%tUOsXp1_q~rY3?v&%h=SJe&m59hOwfoy zY-(5ep3D;R3W^Z{8G!?vLZJfhe8(SSob!3yMxEX9!Jhr@L{N;4+D7BOUSOG2kCj8- zNlV7WOOESJsN_^hTMt}zvKebPFs~91QRE{6|kmp}<#%XuQ%5n$f6r2GDdL#_N z>!B*BDkg~?5*fMFJA@&Qf&@1L`$bR%bm8#Cxg!sBq@)3|5qv)@-~~CWIIz~Bsv&`u zGr*9ephj!g$e|)>@M(j5s3AOz3KiDe$Oz4?V)*M^oI$`~79$XF9*#0_)J1pD#M148 zCp_|XKUJ?>^k?LVs&$H6-Nwv~>9g8qpF3*<;Jn45PNW?dKu^YP zXX`nb(FUUC4tK>*Gl?sE>QpNiNeeL`DZ2mqu}k_HP$))F?qe{-XE|g>GHINaaE9L! z8lIZ7n2?dfz!g!H4BB16S8lx(rAr@)hyCgMeg979Ev%&Qhg*GLD3oZm)AkWErr6e< zL|z-sRXx(~rH9*n-!3hRm0}UXjQviq@7q`W^M8HuzyD#{v23%u$Cl*ji%>LO)3Oi-;mF79k>d$xB}19Fy)l zV2y+0>W|~LK5NX0rTE!s!(UILdN;EV?J@C^<9^@CsgmXs1g|&mHVba1!T-%H$fO%z z(@xq%p5c*1#jN)->GPT+0xN0w8-eihm%WUYJc^})@%(Biiic9ZxlaYF1;_*dxv&!j zKT3HP+CEc+5V;tG_5c9kt#5g=K!uh=zr=W8S*@%!CC_r7+p+J&tez-9RN%Yc_51(* z{`a};yrsj3;nfGKxe$y}Xc&ZoDYKy0jS8g%h%(!2A%=lgVQ^r~5V8bG-sla`P_a^i zI+l|0Mw7r~wnZ#p7KGR3`D6>Y6@ZFB81N$SN=AViUXu%d19>((|19IE)=WbsGlz*d zkP@;?;R)~*nj@j$xQk|Y88SG4%O3x;&t~jDqt(!~R&zU3Dw)B?8vr+}VtglwLrsKm zY~~23UC6C=Q>USi+UWhWy#7Qvm|4qSo#lo$yM1jeFKH!;Y30^~(el;bK&@Pb_A?BW z(PxK2q{!4GQ=>{_O%0P3!OBJ6gE9|@N1p(|qppHz;BobJH=uLg#d!1w|LySiyz!5` z-t(c4NlVotQN+ugfwmfbA|f8N2cSZk>5Oyle8=a%;@YQw>_582$37Kb{q{9LzlkCV zB2nWwhC<21DMF{R9jztM?Wq!+DpWj}2z|upAi`p85zXta!fE|RXPWU@Ov=OYY_21j zJ{}Nl_6%wZi)+jqPkjnRJRC<*G4g$*2()a3B4i-gHdk+oDS2Dr8sBW_^7q~w?u|`i z_?54Gl?Z^$i*~{-w>Ob~-|e|O&xVfOl6?kd4;JSQUud>l6U9z8dw$bonN;drPHI%GRH&W= z7Ko1d!*;{#!rt?q{e6#q(L--PTEIYx;HV8@8VZ#_<24usIY0uhb9lzAu%wb09$6_M zqVuFRLJAlFH1GGP(C`wHRiW`Tfa3wD@D3P;NDMeKn4Ss^ff&jbR1*hnfWQ}R6ttmv zl|tYQ4H<44euo|CLPH?|LMg~msPJ1t%4Z5q4cKw};e*(poP#Gm>Lp)iu9f_NkEw9Z z;@5YoH$(Y^$Mm~ql$0wwN~FJ4GpBm;UDrIVURxjsFt-}zr62p5Z$JFiuQ~k-U%vsx z3c$=LKoNI4L zrXI^`mAtHN;`#ikl*+^|LaW_WBI3(GeYtlbIzV9klq{C&N;#$OHrzn(Q0?FP^~#8sFxY!qPA(@4eJH44Pv+8A+@A>45n~i z-<_*HPX5jl_Z*X2oyG-MYDtYwbOU&`R;O08p_QaYov@Bo)yUR~(QLO6b2P+~Mj&LG zK`577w%{dSvqQ=bWD~$l%;hb(R^cD>j1xLfjkqQSg0Yz4o;5?hG z0SW{fVzM=FR5e0syGHRKb~!3KWVtP14s3XK7zmP?Ifr>jwHvFIhe?*hsQHKi2Mw$s z3}hM3yi*l_{x`q$--njJc>j7;b+tLrTBS-thR^PFTAEapX4L`2ojojN7qVQYBSgx75C#bIG0N-s)o9_E|RAQ#|9*@BR3#S7bFDO`C|} zQE-H!XT%V=z+Y*NZXOVZb84^wRrFm1=OvV?JTe!-b8JNI;!!4`9u=`asiIUg`X3h& zq@QASrGuq@3K?W*MGi?6L469+K3E+<4gzn%qz9CF*oXraa~LXc3Z$xtH3N~u5|_}c zE#QIW6gR)(1wYcd`T7suaN|v@n~iBbNvJW$AY#v+eYDbC#c6vE@WDg3WblN(?}5Rg z{6ZAcR+gp68&S+xGYqlj;lW@y6pAr^;0K@h@sIw)M;{|P-XA^L<~bK9 zedca&rsGzvB*z@kcgm?x8prYAFAu=B^F8^xombE^?|^xy=hMkxwfZ8pnyd5|?|q;D z;b;F4x~&!{ilAa8eWSK>!vn54E(?NeZRbHyI539*O)>??4uf7FNxceUf@9{G8H3Ci z?(0dFw%XV{ja8Ex5p?RRLbXz%R;P2!e4O|Gw%yOFRuW>S0C51iA$!kMil6j@zt-J< z#)I9`!9!rqhFN8D0Q=Cm7ZSw`gy7hq61jXk0*=`OV6mf;Ll7QAz$a5+!jSMA3eAJT zj|=iWO~J}}qfuy(w`RX#6@v)F)<3a^LDtY<$N(~{f*{b<*}fvyQzHb&_I)PbYlk?% zS%!Nk6)r&FwH7jTH7Eru;L`|;2TsGmBcH_wKlTfS>OzL*N|pjZk}Pt&y&~2-!*kP7 z_#c~W+oG|{ZL=~}yVcZrZJN`FCLG5TJC;?M=Vs7sw{)Wn{-n*Zo+OmBB=sWpS#6=d z=eDKhSN`-p*Ou>g4pIm*Wuz%#;DC&Rsu7t3A_)zJwz7~R3QaNAkdVQFJfqU@VsE#D zs`QZpxLJX(9c|z^MyQZ~w`2n76p z_TD>QvaG5X|E{%9#Zy)HcA7j4Ll|I~89~Vs6c7Xj3Bpr;;xmChMNuDSA3hXB$;pfa z$w^QU1PMM#k{RMK$RIGoOm|Or-+L=poxRrYkF`(Ty46*6E8fu4Yd$m6eQ$+x_SxaP z*Z!{Wmm+Ay=IDe_84ZFLfW5#oAt8o<(LsWSCOEuu5Lf^EM{z-%d*CGC833jM&yyUH zo;r0x;XS?SZEsG97_D|I5SIy=$k{?lK?aF~!bzA|t;oavE(q1sMB&~QLJU%dix@ZE zd^6ttu77!mV>j{j&%ZboPd6s$H=@|(?BU?pSk@+bSUV18PUiv7#ds^{b#auN#h#*W z*PmwLOV8U!r=<&R>u49up`QGtpSSfZiqPqHAqsc}C@5%$WDG0{E_RmL z^Z+y>c<|NR0x5XzX{Aso7POza zF#m#s35qBZ}TOV2J`0*Qo! zo;ZT(mRgdF;X^TfK>4g8$t;#t*0h!efHKV1C+%XU9K)JYjxsPm0)iM?sBbcfC;ys0+tk2wTxxjuctffo2%(C#CP+ZW$MRO-&;dgvf_H$hhdP4u2`WCpG0Eb)E=C;Z zaEJN{ByV};!#CE~-ZyJ|#ijCTATf@tv{WYqp)EW(X&1+wSd_HfW zg((yR2XLjf!3PCMovI>)RAMS8#>7Bku7s9+6eLLk5ycB$`1~xenS@D|kH)P-{cFa= z^+J6c&2ekaU{UoVV`#locy`lidT(u;LZQMPF}Ns6i`z{{x4+#Zuf6kCKmNTpov0(n z9g~;~@^CJ-A0h2gGnkUChLdW<4Hbe4%r^l+>4Fe6paV}CCOwxbjzRz-v-Vas4GbXE zups3~bNDO*G4*I9jss{XGN`I%F)o2ejgSEs5QrJV5{z3?sYPekpagHSl5|mU$|DdbcI2cCNu8V6^rKmU z$SImp1W#FXDT5A2XcM9B9E2QL30Vjt&(MU?RYC&T?m96Vhme3Y0W^X0RtwP)ph1j| z5;!kN5aCt_&&4(W{5HL!Ko9uDCqMoAac!`*UAMXx)obIG7AA?8yyS&dW`a*TU^bUP zFG0SD)QnQX@oqYKaX8-PV5&la2Q6yav5p9dcHmg?{tvz%-@5+#x~j^F6UVn)>o|}# zZiAY(LI2`9Z#&Fe?=VRQb0^GUMqS5X`_yaIp+$e=V!KUxsSm{Lc7}g1#;g>FQan8^ z=7~S`MDj@orF@K()GA?qWR1Wf!|21O+W+iz9F{+?0z{_4%U+Bv?ILtL9&sU$JKgz; ztB6cA-!Y@n*FT1;ht%ss`&?zhemHK;8C6uhh#1!Paj8s$&t(uyZ#TXoR^`Z=zEOvs zf5G=X{lO3Vh1;&L5t5_216uBw8fY!AQF40)W)kTf_~2Yz7{wg0cjkr`MA)4AYepF_)=Q{YPN10}J*N*zg zDI$kpDrlOrHgO^x{TV+lJtpjBC3=M)4w488g(5^5k9pMN7z90dX!W$~7zeV3ZBWxb zw-=q8PUjgfUP5h>?8W39(b7a5@KixylJBmK05QvGi|tMfw!7YWGvw(#&7-C?X#kr* zWWNl1PUG5CgD&(xy!K5!MFkas3$zVTf`n29BTGE=IJ06@0JRE2m;?_@(inyg&OK2U^x3fE}eCGVES9M)bp3*1dR&{yIw((mAMBJx$hj5NM~rAL>jT>JS>$ z4RxHQh;RLbhyKu0|ESZ>q0?%BA@C3o6NDHNthg00ss$6o86VizGef4H1kAy8PyrGj zgf^%rxZwi8B`BE9B!xt*7_a9n8%)X&G+wI^{i}bK8%q+*D<1z}v>>j86{O!JD-k8?e zXV&0yna(N4nZr3`zx%Qmmb)K%KQ6lP2s|m8lmjIIwL0(}Mok$_H5x_FB9s{HoB?8W zp-3iVj$s2)n4l^k=AIOrS09QbHmi&i=8%mI*=|XXtr#9AP7eSHXV9h$I>@5X@=#J# z6GG%lxPx!w3(x*Pzj%iW&i#c?e(KY2p0phln*b#)Prh><%&ev~93s+pzUy5IB6Qkq zxQrEd_{2kkaS9}XfJ_e>)GPc@w#m{s-)T`un9Nxr;i&OdFMm0z)iqKPJ$Yhn0e!?i zVo{seMZ~DFzYI#iBFEXdY;ylSrCO%*eD|of*gWZ){&{Y9Vz6Bect7}=1x%;+H0?>` zcK14ITlBZ-sO$f^pLrq)0Z}HhIgWx`TOH&& z1Rg)~!#@J=6D4+Z;DG767xSB7ao#a7%eq%zKz|(<%^B`#s55c6w_=_MV0y%(e)At3 zU4V^Kb&$5;NuU}*)mx#w5`-Lt051f1pZZ>#=t}S)h+w6EqDi3Zn1Y2$s1ly!WVde}+h9Zd_+d;!AUu5kEkHja4 z@_C3?{OVKw7gK5_wSd?(DXQ%%m#iF@s8T9Xu~=G&;zWNeYjZHoCV!TD8VCHg+hmLa zTJzL~35=P_Nbi1%C0Dp4^5CojP?=0ie;^8L7cZ>5a4M zP0v~rQR+)>l;biDa5Q`5!?k$jl<{}1R~=iYYKqXbeOGv#gaoDfHD9(X$3OIfmjQ)IADY`?p!~sfIV7B9N~p03%H-yDsDWO@<}R$nNGwxhGKyiXm2|VG zMoA&91Fj2?7~Les1xJeb?;BqC*>4|xqf>2*awGuIsIK?=WUa9g%HEeB+ZQg!wA+rl z%ZjO0I2N(<-EL>J8_rsbh=ABmUcHo1-2}KB@!r1jApLl zID>AxiwsBj%fJ3BG`j*x#^(Yj%?1pa}sMWNwz&eOhkJmljXHHs} zI1UsKw(U63Q62&C2N#R}&zN{@{mn3{8?ncVL(4Cj&K?x0nkGNZQ9aZ z#0s!CdZhq>y*5#{LRS$K^BJ_d>v;bM-~1cw%%8Ki(HZ&-7byihPgLvmo>JL~ERq8T)7PmGQVx~veE;LK&4T4QT?ka*)a!!obN_wtC8fI!=T zDk2D!d{ChfP^cIaZQ*6-LKcgWbh}uo zRM1tyQ-0-FXuGSflqTch0!YbmV>e?vZM0DwqJlCj;F%RrZC98$Nrq!PE@yvKCj|Jz zK4YhjPPgj^lY**wHvZGZI3D%1&Z4Q6_OY9fNgsB1`%;gRY< z8&a|(b`OvSSuq1rJ;ys({w`4@a}v_`$EE9DYx z)IHtyl864sz3%z=i;jM`38nN>5^ zJ|v4IBtZmXSzb;h<^Y7+AW=0ZUpr_tTW%Lp%^EVs;lv`9GffjwNHg7PMTAg0>ST|QNZ_1V;zhvBHW9yS9dRiono9W@1$|OUT@XJcj;SS{^EcC#?N1H$?Y` zMN5FBBWQOLc=70ngKrU9Ak?Agl0(;<^qu#Ngp8))!SD)*6C_rF5vW_n_wz8B93N~{ z6SgC%L(XbGc64qXrujnLExRmtF_IvhD?70r+QGp_sHC(~VSf1CS#_vq1onf9J% zbv!E-OH>-dm;Rf-{cBYMnym($WAt)SX|ac6vw-ato&)fpo* z7H@8r_7aDETx`_F+h{K;rr}XjuHh^RH{N(6oKlYZ_Y0zt8uL#NZR4RLCl9H>}6 zNZENIoBxOdg+P19^{LJ~RKRd(wL2gpJpCE}rvofTj&-}(?s-glB2$jb)c;xYZ0-g( zpy{S{*ok4O;@n29O^4640QnHD&b_90d`;FQtL61)aKCsK6072RZ5;ktq*GU%|NLyU|>h%UY( zE`9T#;fv3ELE-k7o_8w-@YQR+cI~wHI_#OuVkO+O`VD@qwMG-!jN3d%V=GAEuu&j{ zEL6{RaI-KXBCC$pBmo@IZgx?s#83c^9XTedlvZ$RW3za0x8461?c~@Wlib@h?qz$; zl?^tv&LnW6x{S)XdC1-!>MG2XOtGH zg_EaX{hcIT6ypNgtqw|Y>;Q&_3bH|`%~@m8anQdu@5RU$%G_#JC2~YrI`@*t{>1aY z$$SCU*KP(yW=g^wlqtk2O29!h1pp-y^ph1T43|2@1|)2xkJQ#1fsUv>`|hBb5e#(< z*qXz4%G-fuiVeNOD~Eza4JQ_6`ED|GVuutV(@mksUeeQy0PLY55#h&EcF21#f!$Z$It$$uGRHT+V8zD;mmZdqGDC z-IVtyR^;3fLK_(*h|nUk0!08KU35gDEQl0HI*f*k(atcqO*m9s!DZ*Ip!T|#`Rktj zjO^_$I(#UX%^aEqJOjh~)iCh9Ra$Usdmmh>5I}hT3tpfAq1{P>KDJfh3!6SDu}LPS zIGf-DUoA|;Rx}Q;#&r`10A`xP#CYjTUj`y#FRK0Exm+yn2zXA%?6AJOOzztx_cje6 zK9j&Ru>4B&X-?tB%|)kgRO6BTX>Y~&%lqYD{pB~25;7SFRSG3xeltsnkX2*`_sdd- z;%Eu-VP@hXfG8Yi&~CPnFXr*+M?Lxz=qt|J4m?jzj~V_kr&F1xA1~!Nroqoy)@g35 zS(PY5B)VF8#83SCr?Qy~aN<-A6d7G1!&Z-D`xS2r8UYwH2;}UtI5@+mG>!0(4CtoC z-huv*QV)9+j+8P%##gqJhLoZKlOp1f}<`3wpMq#Z5%$oh_8I@-@C_7e))yD z638dc3J?2YST=g_G<4Y~P5TM_E+R-(Q7o0IvCWOAJLE}r%AZhM zLgY;)G9d*;S-^LZFrZ#RDOgPG7>p^{rYfVE$)O1c*I^XSDdVcvaeVE0fBKSBAAHZR z-{!&#&MB7T+h2Y47eBIV?NnK%jd~RjrNk%RF`?D&m{JJ4kZrcgZpVPA2h@%YIp}_d z6o)Nm8k~S(1x202BbUvCSl~FK%}n=ZluH$w*6d(hR(Fz&Z?RaSMHc#vi=E6E4?F!D zjAdrqM@3vL(WG>QxLBroP#3;nTXBo!ZBF9m2lA}cmgpXJ{-?0zz*GZ8!M|T`0jVE$HkZ2nzI>) zX1l$s_r8}-ZwhgVQt3Npq6hro%j8aXybL$oa0);k;5H)iP|1MUL40s`P3aCSAtP-L zeGb~oN^83L5u?_qa>(U+kZe*oL~OmX&oHM!1d4?xBV}+%XKP3yTY(?<64dsk_Qk3S zQkRM!+J=law>lE)iF*EpWS=iV^3)PBrrHEa>uC^9I@F30H55*g$=NXx#AktQ4I~*P zNe-2A2ATM%fAwqr;64f6_$lv2<6@ZrGxw_PkRr@7Bi+#Lh52NfU|>@NbzE#G#nd08 zcIAUW;7kV3iQHw%)G{kLB@W$gf?Ph2 zOD|l(!LNQEAAj;sUApnUfBy9=Z*|daGx^-%-2~62Vwu)A))XT2OHY1s(=nmbY9r$! zIF9T{jR{JT$^CgX;d&yFn5MH$pkYd64Owe7L4E-uVz`L$*MI%;9!(^D>%d67W2j z1sDgAC&~Cu3!aB@c&F9n-3cX-Ns1+RqtbjDF7Gh#49upyQCwmT$aur4;ZB&d$p8-T zH{&wj{qGr8ur^rm`P8RAwF4zkh3CbsPG^%>T-bU~lW^Y`F@6>ap6AU>rr?<<&fow3KlY z6J(<Hf5B0&?Hfg zOQ#i!FDnN7f9f{8SL@tH#ZV6PG%-n*Xg8x&EK;p8Jx^$w`x=%BNPo5{j$9h6r6P6P z%5Ck6mRF!?aCu^q39w5)x@gLgWhGN zgie?a*fTs?Q3Yo*=ynBBE{5kUEHgem;!bzE7U+#M?LD%Zt(PJSXAN?jj}f1D;fOlVq6_csZ=3WkBxd=iHNVf{L%0I z{`>y!gO8s27Lu+YLz{eNf*QA2t=JAkW?bMjoDguYKu^^8(V~{X00IaFESpP40kP9W zBVD9qX$|0W(;AyM)iuOS$3W@l3!H}H!jO3;Wq`babtfPnz->5|RG7S7S~RF+Alj%T zsdQ}UTL|n73fbsj^UbQ*q>I=_K*-z-#XCfq29j~&I&FJp8I8DZGTCUV0z;ii{DHZKs^bTYi@LcZmY!`6PNMO4dwxFaxTeLbr zha%LwO}~=OM1|VAbYJ$I_{+cczaG>1e;>p}ae~VapND)wk@pR7*Mk}fv?J&w0-toz zP3q9D@wS{euqSwL`)uwIARVBdfI12C)h5m@=Wx!U0xr^Z-0%;tZhh%zAA6+s)>q!Y z+-XOb-}z48fBeMFS1%eoPm&Gk*CF*HIC$tV5i>pThkoc4t1BfWUVzB7J^`S2s+Jz> z84NbPPZ*VhG)NrD<|GUecp~_&2SE7SzxpfmQ#b0Zwhn^lMP+9z3*f752Sz6yzvISb ztzOkdDHBY~@l31b$3+}u_;wTcjmr#85HfA`tG{iHXl5o5_K)HT>I0U-?^vrjIhjwQ5&cbtHK1AGb8Y+_;kw7f&Di zgKOM=QiTH+kb#~h`gh)o%3#mJJzYlZR7QR!j){mMzKezjl1^LQIk!5$e4hvYGSzau)U2XL&>LbcsN(&-|U#lZUn5l8SWP%p@7gd`)#L?vXR2&>sVR`Xrtt0z#q z=Bv2w-S2r;_p={<2C5s0Z@2W0x4rF!$VBlqGZqWZ6XP;qM8rpLI_i&{dkz|nrm6HX z!KN=UdUKljfLbP+6y(%I_mOg+4OkP%CcB7{&1B#uK_-)B6{T_6r&6gxwK@CZti0xB zWt2;CnUv0qjK*F+H9H%V$FTW;$Z=4~&_q01%?8XWR0C)>88KA{=1|NPG~ks&s*%t; zVkfrjA`_$2YNAwL!SDX|@8h}8|8rNV9Kibe$&quxAV+@|RiS0a@DtR{>ns*Z1m}oE zG^HfP@e0@L>#|ag+(ymop>x0gy^nqLA3S*Nl%n2QM}`>i3{V0^HkgD6;2Elkxw?{= zp-BLh21YHJ0Tz%tsCX#5psr!yRf#}imfWdscMk}MBEQrig;RwLgrBLJ?z5);hUqgl zN{{qf!Dt{906Za;Hx0j43Or`3j}e7st;5z!k@BGt3e!{2Z5s&^b2=mf7_Qgfz(ylU zT8D7nd4kV=LtY`{g)mug1mM>}^{r zM^7p0nkx`)bI051mG^ri?scEL;trQXIRg|kKvY76E)sHZq=IYh^f9!t8NHoD#;6PfL~$ygbU!z@?<3UYoF&I~mi@tO3H_H;u@6^VG{ z3wgZyRjSTR&7#LlqWYwhOXp-0Mmob^1SoG|p18r*F>mSwrdts59 zG#2#?_qu#q8gZ#Y(oN9nH0guy|B!#c10R6u#yWDjEE2E5n1jtlE*$$4U?7PAr~|9O zvr!EoLFEBv&XIJwSXn)Q6UR>Byo=7^tji(FW{`9fG@GH&a)tuYQn5_65q$Z*5dB4h zX{=Q8RI9eBRJ#2W9`}Sld(FoBDw@rc;7l8w6rhw?Hr6Rb9Go*F6qjcHLx$JpVNFqP zuYeCdmazfP2n6+-en2owNp+V2PuR?G_vbM+rDmXKtYSrdmI@|Y5Mb5w_I>sh3=t1f z?l~M1rHUH>Nh+EbE<5}|iF#RdtB*9$?$wI-HKltXRrJW1ahVb#*M*3pRJa{F{7HT7 zTYo*`DA7VLMpr=X#)ia&0>MT5KAEv!=9F(99QOmsyF=&$~(~7gz4s?Y3@q-A=7lJNe($we@Qhy-T&C59*I`71$L25f}y(ER!I}$ zN3irIgt?V0>7hQ=6txMJiQ&5m@^KMjy3s^Lyi%^{+Qxb~L8*{JCwAD|tFdUV{@Iv4 zils8KZeKupCS=m5Su3UqBH|x>;DcWJ!4JIums{-?d^Z7rO-E6cp2CJgj2p|m3E~_8 zNpYBjA~2f|3i&)T*$ghZ^wx*J{omg?b@<>R96xcamm%Cs>%t_led1!XpOp(0S8Zr`K(fLRNty0;LXvy&n_60+yi0(MJ{O(J)8@NDs10fitavn=Gt=ryeM# zkRn$Q$X*!gX;Lbnonk6{k9Oh|)~7^z*KJ7}QyAftC=}KpkX>y?GdO%f0dIfzpMCZ_ zH~zo7mn%*s$wD!G@$p~+Cw-Qa&K=9>%ljGg4aQO_9ODM&ZktWqQX`5AoFq-z{Ckd) zq^;RpfjaHxmgjN)`R8Y&$YopYcJuhL<9=D!R4P`e)|l>S3~ElGZyAVAU_izcn=;hW7$T#-VnO z(Q0+Dx>CX4|NSfRIVq z$h0!2MRsJST)GJ)iF`01v}6U51Jnn41<7L1lCO z4t>m$;?5`=o`DDu?LsBO`IQUtjT3+W)_?uL>z?5986?g1u07WZ)#_X4g}78Eh)$&p zv=cGODNH)g^wV$mn!M+BF%&@0JBjVL9r!IFCgCL&%N2^_f{U14k|fD8LGvPmi1@`X zdx`(mU;b5eIxR3cc=6znT?%tv?>{C-^TsUm*{F6~M0=inCR<}vy#NvO zQ=a;({>3kRF*~?^@D_66S${Q<=KPpUgjJ&v_qSK5zEaYRAN=7tzR zznyR*WV5ZSIWog8fud9>BA1Ernt%EMZng4}O0&D{XAA5=;$e@}s4Y5nw1{w)o$)e` zmZw<|m${o?R;N99=paQdVs`9Q)O+!I^YNRvWJwMlK1hxovz@C!yvXr^*X*_Ee-{0V zCOIabco7kFT1_~Yg+dWIHdPHxy`@6FKqc6+awsfGLMaK`d`b)wkFNK~7mN7vm%fCn zuDsIavU$zsa?m6>O?!4XW7>W+>FXzvfE+bG&IEqjtT7wlo;_)CrX?)4yPv^6w-Og= zy?%N>b&(^VcDR?B_gP0H( zWBh+V`IFyy!yDgt>;5csxmcv?4AQClAsi7OK6IEK`pD2%*{Q-if{ONd(j;tp{E}=I0dfj#2}G`E%dM%4Y{>o+ zYoK#5)Jp)tDSS86672)mLUv6Y;I5`lgqjuyYUmSz{sV`e8=(e^ZYK0@DgbpB*n9h& z4ntKcND~kukVfzVoO}KeyyyMz!FAXF$;4_Dvr6^uWSnVpR2rwr*v%V*){*@&Y@yvw zifI^luFNSDHqC<^dd=HNtxPJ_w5%Q9$rC5_*s){&rW0ra>wYlTjjX6Se{r zY<|f(Fqs~-3ILzLiy)gZ7pkB9ACJ2$0JPdo-Pl;u?ZET2rUcVSObvT&e?OX}?@ik8 zMor57{eRTvG6m1$=7ULN5@U+#K5w}uFtaQalYr+ztKL#^d#%@L`qp-RXHoKu{hu@J zS!~ySgMDrocm@EUcqJl!#j9Usph`WGkT6m%-#PJFPz)GRu06UdDP|DG2 znKI;v@|Amj@qrJ2MbefF-NyPcWT^$u4ulLq0`C$eNI>vl%gw`wqB$$#3)WO2%j;6Q z1_9o|DglX&Q>?)QP(75{z^SFM5%L`n_CRXKjRZ;pg@>mgJx65DB?Nd$sT>A?eedXj zds$;MH3?tAf*{q@lblN>?7*piKuQrY2NojqyacRHFaY#Wyy#&_EVpnAB_Cm~?Sw2&DR67}q z$iNdZU;Fj1`P<*-_Gq>0fOE!^Q!8GvqHUW5!=WJn2@7bbg67C3-%y?Hh|ubEP%4-3 zU)TH>F1!3P&N+uxD5?ZD`_GwF5U~>yTcac_mwAL(j*Y4^>N)L|v7BDV);2mlGkvyx zWb#Ng+ZE7m(nX`*LM9Uh-YN_jdo`dGj8m*()7+B1PAO0Y2!IC?^@xkH%@-LaVy1J? zIXAlLrW>~$x%V4BZ5IYeXeM6$JSP~%Qke$bh>P*TOrw6X3joDjhMJv(j+}eX*F5}@ zzxjktw}g%LlZZHLF4e*55n;}yunD1%!S(8Ud}4%+OR8|B#@euU5A`Qb0CEZuQ^H7{ zA>jdv)C^7#YFS{E3{$={?sBk5L8|qgQutV{P?UnQN~-aZf?@{Qi6yXU?XH_t5d!z5 zPfdx#1zElHA}r+`l&zsX(u#(^QBVz02pot#R0Z12AeSv;^|0V|Z~V2Vt+&4R{8^sk zG_lij-lTKZG~;D2L@xt9ua)Oc#$X0`a3?fL7H-}JXjtbf$7PDk(`6d=Q@bxZW&up4 z(7u(`14Kl8@X(50dYjuKfky%Yvjh21HJ@e`Qf?z`D)T*EPOIurl~6w@1P)DND4&HghDc%IfoVi_5~an~~lo|hqN-#>2ppZ_xXcS1DwInBF}Eb2L> z*Ri#YPQUJDgXek8P8MLERhXFD%@#5)10rwIWXuMR66u=3aP^Cfe3;^(xmyuNKb)6Li&r`F+divw)3S|Lc zB_`MGBy{d2Kk}^~e$*d6q1h^9V|@)S%D{`wv&v~AS7BPn=IAJDCwhTJ4u*nN!0IgX zPsaO&M=w*$FqIBf2S|Xpp027<9JAdC#dg{6Nn_GEYd?W!pbtQwE~LKPW6m_D zS)|iz8hjXoi|=k_ldAQq4r&x{|DH1=dpT{)x=5!3Y-8<|0>F!3{Ff3jqt$F8;s_!V zILr!#4;g@fmwUx2Hl0HpQddG1fY`w?!}|_0*$iscDz3izv;RJ-%6`ic?F1X4amoh6 zem)9R4r{x?&q%MW6qiR9vu!s{_ChuEuzMXgXN>Dl<0%gFFHp+@|0|{4*<8;%f4W_` z>Wv}bb8VxlN6tCy6pB~9>eV3u%81AXr_of`H`p(eQ85q-22=Sn?m3ofimU$yu6V>>|H#9C{i5~t95yy;R-7#fjM|_* z#gdfQ3{H(l421UPU~n+9QsWj&+QuPy3j_1afCj2vKULir6O7HYvIN?l{cCSvC=r|q zEE^yO_ayX`1HHoaoLhQzqqY_(Skl5$%b?r0rT1WT4K*D)yABR}F8fL_MaH34u~yNr z854p~WI8X}08s*w2ytA&v9-^%zVPKYR8$*XMn+nVs+Nl-@;d%`&b!&&u7B<|)1SQ- z`m=_rUWw+gOGSH3Pq#dKqjAochF3hsH1&HJPO7<|-cPBUlSlArgYqL43A zAj96;-+WUI9j|bl z1a!$k{&N$*nbRW~#exzrxTkOu{P&F3Kx$wTCQIj#&e@x^8Ly_2LCi+I=t;w+Cy5ja z3aQV~B!E@wX{Q(^gS4-vFHm$ewQILxK_PHSwmm{l+h?wZ8VZGMrsc?G z0zkP~qG<}^&(2F*g8@5on|CKOZ%q6B)p|`UJCLCn^lG0r(hS$Eoo=_E%U%r;(VzK+ zpKmAu?QX{wL?0M(oEz;|AhwbECo1J6sPN;5PD%sd%KE<`f9`94W!jaPr{A_rNxkjWQAtbkFuX$QuG; zDjk}Bt>wB^O#hmR;Zo%e0OASiylob|f+Yt>1sph#!GC`KpStU{Z~R>`1K!wZDW!LCI$8~Ve`YU4$i4~&`z(!R3H#bxl> z47DAflS4TQ;7-fK9_+JeyDj6{rkj~mouJXID@5rPuYMgkRd^uFDoc7=C7YR2`=xyr z2V_QU^8%?t#FQRf_&Xt!x{=vtN9*1& z+Z(jD7pYM(1O)N`1yv+bv+!0RWRwkXr)A;c!^0OT)DzSZ9Lyj~y+8v|JI$?l4N`SV zAW8zQFO@M21*KwijUGzY9#O^m2?rVv(hNC3&Ih`n^g&++kpqWyN65iT7NwOkPF6ps zS6}^_ifC6j6NP5#w39%cZ!~JU8wJC~3Fobi+T_>~Slq^Yuf)_ccDt3nAIHZ$o&UL* z$!A=)ZKpbQ+TN4MyzRvp1-KSbEb#Kb|GW2n?BgE=!`Ts8;ITOX(7N8<76l#6P;ri)4r@iwP7)p}hWcK`rHId*)( z-5>d=-@WGIiyw?{efKDYy5J~*fRMmJH5)*uWtF{{CACQ%2cEFWG#f}Ij}+WWs>2)# z3q{jxZ}``RO^mmqPE+pi0E@9gD11j+^bIk0LrHlSCTB-Xh(bYNPoOS6S`gXfwJQwy z2e~|ON)KV9^Fhr}2Lx497cm>|6nxtTi(uB=DfU)nXeyD~TVw!b0Wu2F3^Lg=a&ZeE z`sgeFn9%3|fM`(wpxLOZa&}jjpFvfY{usnp(}3db#{GWFW^;PMyLI7%N7Az#5f4uC z<0k!S9`;^x`yGW zPwgm9nE>bDB8TU{;5h&QQI*ny^jjz0(JwmY+41l9J~wjn7VL26X`69#!5$Ab5BZE6(GPB^H}Y&BB|*s|+P88!$~(?NW+@GN3-Nk3(8d zQWseY-zgqK3SpBc6*4D7Vurx?J`(^Ef?Zci^+>;ujL$5fo*lRStmmK;YGbVQG4(}J zXLn$hDw~f?)C_kVr;e2ban@E&GVqQ-PN6Iyp8-n_8OJz$IKs#O^Pjp$kALM^S->gCN=mPaf(#NCeOXjFuH3COMSLF>XBmpRLb-<@FWSW*6kJQR^s?o~C)Yn}BlE zc%KEMjiV&(=TlA|bhjE8J9VSe0*G=vn#ew?1Mbb8-SF5?*E$&Uaxf=}*Sz{QM_%xP z7qo9YdSlSDH^F-~O(C0fN$kii)d4k=S&*7kt4|c!$l)U&`4~=|I6=j@qyUi56;IQb zU3P5rO;D2poKa)5-rTuUlxZZpc2diDaKc}dw&`AtX>^#kTMX0RSL^kuxQ@$;(>(wt zyVvwZFM81nE>H%Fg#rLmanJ-o<2Pg<*#I(=!cm`+gq`zmDmlOk37f4Npo(An&0o6; z1Wnq>$X)9qi*Wlltdo9jsZgf45Yx&^)&W2}A-eMwkNb~DKKc*;XuVO!+WH!@t_2T5 zBB{KeK-3)n02j=stk_Q_aD)j#zRrLzs|yK>Q0NB z*D=6$C#*YBueKYpZ)NPx#_5rA7WbRP-=gBFPyIFd{ont6T3tN|U~85m%|8IJOh70g zaj@!Z=Q7CVGPvJ;@Bcd=`RIqAx3Y3T*Vays+UBgeizLv!0Dy!ID3TQw3t*s2LzSVb)mM;*dVrjR@3v7{ zSwW}MMlM%y{j!(EVu>2N_$-EvkJXg|)oU#zl8^3jugAXXs=NN|6TZ9FLbuaE)+GSC z@ID6saF9)U3%G}sjbOpXB@PDF5;DG#VzWSx14Ft`whlm`gcvxcFb_+LX2Wc4xH=fE z2iL(g^POris|0Q`HWf1hjP2u5r`Rs=mVv;(PJd1zn=G5k9-8VzbD(#G>r&%Yn;T)T zoG`34b}y_B-lcROK|}rafL{UY7GO>aa^W) zy{e^Rk#@?aJ1&-~-dM~$I&8k4M_zF+$J9lHqhhx>o@Zk-QqXK5dVEG=r{vIfGJE^t z7r%s`_x$Ih(WqOYNtnf=n)hu21mMkAL*JVA63L%bftJvrrs1 z?VoplERMVWYQ3)07$}Wv@3Y|f^q2-b&yx|IMB!oD_t^WEGiOv*RtW%J{px>!dV&{4 zB4UMRA=}6yd8wiBD+NJm(U|>Qf+2`$01E+~c7jYci&CMeu8#*j33y&+c5!fgREp%* z>n$X*QhDG*{`b*4fA3E};k(DH=(MWHatERwlw<sW+Q$7&S&zjblv)p%oIc%Y8C(B*D}saIGmVNDS_W2=H4C z+L*senl27X)EcNFWG<9F{Gg(XBfQTf_kfUN4(cOTObJp5bvLht%&;rz z6maO^D!%-+w_pF&>)v&Vs!DBLyTyEg0EJNBf)oBU4|tw-TrUbd1H3Pq4 z9@^am0D4?Dqj3N#uIG%^JEv1CB6->*ubYsxaJ<%4&%vYnpT)Y4o#z|sXo1wG~=czf_ZX-GDvnSbn_RksH_1|fL!p8cV0>Iz> z)nDC58OUXFP%%(QY)TwL0(K#*NC+w$2KtOXV6vB#DoBtb6Xnq9wgD)9_+bzGyrk%R zf|$e+be<@OL`k7gqPS3^Y7<(_op;`&9{=>~FSzu<2aX?8h_;Z;7>}DIi$t;pDm9g^ zb4G8WAnT{5oo#$%03rLk#c%}Heoo2w$}Y^aX8mZulTvv%E6t+-SiK-nG$_#0HQ?E> zsR3^)c%;l`vbq;&dQH8$@sPpBri)?Fl2$NSIqVMtWY+++O*k4%>1M$;1*xMgSyJ5q zb$S(K$|FRCNC^aI8!Qo2R*I;1uE)QBG zhk0wsw&20O`Ej1j?RH9H`{DF-Fcw1LC>3K`gk|ZtquN_4En(5o-;U`~(N2BePR7&! z=;`{qzw>)=jzh))cF-rOeknpBks``vk%^p5lf=AQSebE};Fgdj zWd7j*~h&WyohYhAb6fNYcg&Ja!Xl+mmU4> zSnp}GPIufhxDy_KT)#Wd?T?@F$Ml@%{y8K#>q^SuV5^uGi2Ydv*~sBr-}w$)hDlZA zRLDN3;Ca?#?onfF7W&Mn&sv5v;IyAPuCQy~=f5k?hckwQE)+bEI?0>oRAHx`EJuOo zSu>g5iX>Xb(#O&kTaHhlIj5R={@?hzRa^ zuX|vVbGwT{TeFbrJGjf;@AtSLebjF}xZb*zJAP82+C(N=hYHX|4gi5%8^i*5g4awh z%T7!|88oO|ASfG=S{6A~Cs8)Zx}a#HqMImGEvuYrDWi`Lp_)oG6Oh2Nw}D0xF2dyW zlt5~QoGH~o5L8owLuAjsMD0J6&ij&5InwJ#0QXi{*dwZlS#J}OQA%J%kb%b$WDZTO z#oERjrdI(F!OUiyal<)6rM!Yqe(GJkOpW@w41#B1w0YZJi&AlZ z>^KOXce-GI-r$ic6>)j2`oOq}cpmAZVQKzJ8+E@m{xZ!`Qs6Pk25H(wKV1~lg=w$y zQ2*~A{a^a?=lyw4ii$Y+Na<$bxXWgs0G{{kKlPG;4j(#1abc@6&A9d%M^_p(wq`l4 zNk3~4Je{sEF+V_>wm%F$|7k(^J%=+Ddv$yoD@zH+s(zZZmhO@ zUuDSVqSn`rN6tOR0l;7V&EGtq$;Q72!y(!%Py>L#wN*H1m^aene#=Bg2oTbgGBp7c zW=a>R!Y-}PZH zkvrY{&tG@rS`M|=^~hvSLLx$&@(>a@>OdnvBnJ-%i4<@G

TDk{ zK%GJ~502L0g>lZIOK|MifBH{<@wJ!aVxw;43ngmppgPjHm}kAH&A^-9Zu7t25~UMU zI4Y$Q&9Xq0;xZBDKh|94QQ%%YW}a55;B1VJ|ND$*=x_bj(}LZ%Ws^Wwc#_Lzp{j6> z$wf>)k*V5zoC?G;`qjMsVmJHxepk#`hRR7fE>oItnD_ICweL<)C1Yk&J7AZ9#4Jt{0FGVHcnV_RH3CdqgY0)2WZ(cGFe`vm$0G24k+zeMf)a)X8iO~t z!oUj#0U{0r21x?Al!~EXbfM1p$nY9y8YypvLYf*qzBGiv9B3w~YhWntgAyEDhC3w4 zfgnw2kH&c2Mz+x&Xl6!`-~*U?!gUZ4`(2=kq<74{(@pywD1wM$wY-9akK^@k{DWT- z{_fwYZ=Xi%DaK`LEOC7HuNSkd9cOXK_?TFQW7h0StzOkdJb*=V&aWo4>oIBrp#wO@_vN7Z^= z=jrpixd8V-2)|L>N$|Xkitc3;x%8jb&HMSo+IJ`T$Ag*jaTWK&z;k+uY4j@>EOXb zG|uvUnke_3`1bdi^;$~-K*_m${$QRNC}+qK<@mx&AN(JW`Kjl;hI4m1eB({W;I$5- z1R@!8DJO!X;M$rNN(Q~+U^f-Auz}pbKq?K_qcx=MCY#_}Ss_7pUz5Z#vx*>VrzzSE zVAFE`tN^n)4Js56Q$3NOdV-!&3j)VDTrbpsc@WYNO0S&2OyTiamYnRT@IL*!?{RjN zQgsf?e489g%GIVo;Rx5JY?){cfSJkY8$#wdNNr^J5OReCAA0|b{!Z}UL%}m(GJ(sC zejITYQ!G9fCS$i-F$@swmh!;h0$85W2D%y+nMHzt{>5o0=lnUE ztr{XLCr88)1i({%?bkmJf*v?{h?HyCtc&Qx)CQ_LE5~ z>BqfpuZ$^R{9M~V=Zx!rDXnCZ1%B{zrkU&0_a0b1NKnvUyzDOlSO&J6bfILxh*QZk zRX7sk+-l(NhKwpmOeun3&W1#2wwoYP-21-wc{PC3c*ql9=P*^J5!9P(yqYN&id3LR z(rTS-RqF}WG?KgA`{%y(;730B`%ktUPOP;+h`<0MWWXReboj2~X0#-mrDUjx#Y@G>ectlYP;BIhpj37?IkpkL(v01W9X00ak+E8*Hj!s!8FGxaPz&NZnTpofRT90OA-2yuYk z%ph;Cx28$oZ^|7#sgIC#S0T$(I{--r;)^(Ngz(uf{nM!%ZvNCSWs7#H#bV_&yp;rM z$TSKY^TZ_MrBp0Y`nt2AdwiVsK*vqar7_Y?i=Nc`7v5<J91T_hGT{gho$;|Us7r?#q{71#CHr2rN{d>}k@VM6v`_7#%4oqXzTpO3lv^D!R zTmKu(N)LX{G;@9WURDzce$}g9d6D-C@`bDcF9t{$5^|N6BQ;>Dsy!z>B@Xm~kO8&B ze`GTmFcYr4@=5@}$rC4Ly65UP+R>$Akt)R;72_2qVpN+AO|-)i5o_+EAA9g4pK;=j z-}m^7jvhp#| zO#fTN{JB!DP_aNoSBlSL4L=UJq=ZJFjsChp0Xt92`j@H4?NB267>va3}`cm1nh!7vO09T5%NwE zWpaoz9-X#Fk|-pho)VHGLWbG2lc8Xjz-c#pr$RI`BO4BO-oaJS@NUp*R_^hS#RolU zXRo!_1%W$CS=qsf5CF@j@=!HU zv$5Pb+Yc5SqY@)T#K(>w^M?){!rG~Ib5sLnTla^)CgXCREv8Vl^mswW83Q84V)$sb@ zo?;vk%Xljz1A-Sulm`wTV!ZPoU#Z_+f2UKgnvW~YzN?K^Q{!TZ90&<-HJ%&wtk3i~ z6Yh6MGSK~TCN$c-F>4zvV__{7i!_gA=(yNxCJKY`(?9dmCtm;B*Bw?5^6H0Xi*~EO z&hx$FHiP?R{4J`lp9Q1)CwBUDj)?eKfAS~(cmMGBvA(vBY&IKM3kqQc)P-U-P4y-= zWzSfHI)ox}CTRz+;9M422FPWIh`Ca(==#RmOxfjekuR00U2l`il?y-k@L&A!;R}B7 zuE$PvplG3p8ocDtwLEh$JyA1}f~ji|Tj8Gu?gFv146_i3jFzIzLGIH-h0WN6C}idB zeTJcfZ8j-5afpjWtgK}5+0Xyu^`HCr8?FGljZ*QlcON?M)_05xF^c8GhcCYH4*7EF z2nx9v?N$V#1RK=`+FgMNgPaGmL!=0}-Y%CvxoiaIvbgffD=xqG>(^f66F&wx*qATH6{^)o&&`F>3KJ0;)inWtLPXT^ zMCY7;x2NCtN1pO@L>JJ>Qzsx0L@p?UAb|l$hHA*NGF$dswday&nOz5E1Je|=lf?v( z%~>#f1~~xY!|NgtDZF;UV0iJ!WD2NMR-n|yr$6|Im;To`zIGF%)A8Ds$Ysb&ABqLL z$YoF}9J%*Jx4OdvFTM0Gk2rYb&flAfE<~*^sBQ=%X(7=rIF|s_G+Kst03s+mkO+j$ zDx63llB#^bSSgyy+F1c;Dpdz&AhBXX!`8vAmeb-yhfO_jg$TjfwhH7F(oqmcASZza z%{X+Rf^XmO3H`SZKBrhL)VuYD`eDypu^@}P-skbJxzqPO z<_RZi5$d%LGEo~&eBdZYrcvz`PGMHnY@A(!(GG&jv$aD|v4CTf^Am&|v*|OtfO!Lp zrB|Uxa7ff(dX!-tUOj+D`)jp-e$z|;E@@u#G#~E0T+ULp+R>E*c_*51hSasX()Ava zB!DDDK-T377ysPF7k=OKF2DRfr6cFw2Iw$08VTC<7T`=D#e)N=P2kwW48JAlHnSP6 z0N_+=FQ|qHrHTp8fN+YB!|j-pqGjui5-!FChZ>zl&EnlT2lyluPCB6Luv)2r_BRAMbVb0_1qB5QCO|!)Q351U z(3~(@LmH;DOu>E{unI|mGJ#c0U4tp;EeF*LY;uAD1v!N#4l0aXwt#bv6!Fb(z3<~6 zeAi$7kfUlN7iUo4=$xL2DMq9Y0F<30b{Zuo*Gn`UtP=irrch1RPZ>L}u?zwmi~#g%u)%_mNxSS&z%%86AF zIdkEYpu!Ni&!)&B05MnyLIQGxPP2{G)m41%3tzzZ-Sw_Rl{lu|q_K0PLUi$M?)$uZ zKlI7J*3rZ6)Tt&M+F))$;0z1~>WL{zh`A*O9=t?E-6c*%|=a2#R{o%1h!nW zkRufZD>-;LinK%UTCG>LypnNAlAu{vPn2cGf!ken=O6m;Z7#dtIfoA18t{URjT(|9 z&{Mny**y;?0i3`q!$INIAz~{!O!2a$8;3+doUx)%4F!T^L=7)y8E(oQDe+^o=#Vvm zg2BW%c=#|r^|^mIcJ&wDa8a?;mU>lvp|F~1HrKk7KHvQwgKNp8N z@#&x7<5HPOH}wwpEB|v`#N`UrC*U(LGxl4iZZF0%CYs&KNbY2RnDx6B1q9<_nd(bE zmYpSbVlEzR!~LECBO-p$OJ3@K?J55Yr%tUQ%49prIi?mOKp7M-~aN3-~7r4o~GK7%4&`_*4lkws!VWhy)V4z&gUo&VPn0EX3N7E%55+u zpdeT0p-@kD?IfX4&>6>>V3sXv1&|7bQ#Ee1Xs9>TO)cr*749er&Ww3B|A$|+qS}@ND|tG#-qKRBLbb+n*5^?a%V}elIdqZK#4{yGRTuV9`eQEuYhe3D zbGz%Dw{7}Af3LL3*_bSr#arLah>Ilx(?kX!J2eSzH%7O+j{RPUvRy0-qz;>pMxB%` zsxJ)kkar`}dpUUMAe}mSiXQ&(AAQ%qyz`$Qwo$Jc;1bIwLrua?0+EA2n|9A?g?0q= ztgS^AiSMFZs=%v9A)9lKSaYQkY8&fYXkmk!+)7-cje+(hB0h57RX_HChyUVh5#OeG z~o4>g1$_M?-_gr@0OfGW-Yim`syB&bq z!3-yYGMgd?i^=hkxk&;>QxUSw5vhPF0!WPBLBUwY2ktWf5)U8)b!{*Sq@6)|^?Y8XW8YApaLl*$C%O1+5-WvVxquevcyO#0n}^Tecr1jE0xLe((^v->Sh%RmpC zRQrFn-*y&*lU2D`qV1fX7Jdy=E*iUi1X3%Bh z*)|oMowER*ww?ON!okA_=+ucdy5PJEGS}bm?N+;+AW7P0z@es0k&+h#p+2uKa|#X= z0wyzFVS#iy$j2o(CS)^tr&i#$g~rjJlo@uKktL=sj@@ z5&-aUQ3CdWCxZ7GIOoxmY7+ue&}vo!OK|3cWmSU~n_a)AHx?xN7{Qi2D0Ca=Qj{mgvzQ?ts_E>{I=GCZ>%=e)T&(3Mh|HqPp`-eO|XqPVlM z|06DyiNs@BI^k#ImKTefGe&314S-C0D$z;W3S~6w4P-JIh;~7Uf~B*8N<-4YT*IMf zxaoG03AV@E*+o99If?x87`Rk#UQQD_n1^3t-ZR>>Mb_ znpErSS}K;Q)=23q&KXtPNo}K}aeO$h0F=Z#-%c|5R<4_fuT{0{h*SKuqG!sb$o~b`6S*X*k1dz99)zqoC=*0CkP?4hs4AE<}r9 zK8$nE%i!Jbe)?y>bK?i^-oH7DOBI6Jsl=$hx0g=Krb(>qx5VjA#oQd|Nn=@HYifmz zZ8Ff?alNO_=8YOKXCnAK6Ypsev6m*zy%3{RO6HAG{coQ3-|ZTTN#?gj_pkIj_eJhhg>R1;5D#aXm&BAjwmjFoWCt8O~l-Z~w>7rD;^d3*HUG@D>fFvide)5=Q zni(Mz&L9?u2b?2_H=eE|Lb{G~nl6U~LzDLp?X5m?sSpOG!Ri8$}Q9RMh4W)fuby=OJ>VW2{e`XQ)i|rtZ)!; z6yyB!B7FQ)FMs`~Klhd==1U;>vNjtV+gi4di=A8_c6&ZM(}L4Kn1RRWmv+rcl;ZNx>+7F5$K6Bv{$b!bE^U7HF#1aW&&Rt6ZGKLA{i6m(KP6_==gkwv((v}0 zHAc06f1Cx+TccbojqL-cy*~7X!M?xjh!@;fMn$D$_t!sW$ARZ*uWMBQ+pY#;+-FX@ z*pw$2A8C||!^ixvF*XS>9Cm;6^r7@Sw*$|Ue#f%rxHS6PXx#HG6^p0+?(pG*1OT7< z%%@BYjV&vxs_?21cyovja%L$Nf{D%E83bhxI>ZDQ?4bJ_E(OnpLQDx2NC0ZpPqdQy ziB=&`bfV5)a_8UvL67*=Pu}gmKZuR>>#%myjV86mCK<;RGR0oNq!2G)r*QCrv20q# zDrRe~Y;0&!z_Y>wfHzJzd4Q0BgY9(}5j4p`90O<}!&Q^6OAbJi7)K5`lyd)-yz$k~ zeE-+3`NFlLD(yryE)=LxERUVDX6c9ZT2-fcU$c{_h^mUJqdfn|$3Fb*H@xZD-~Yi6 z{N?+PAN^46$azO`;jP*z=DA*R&OLy*lDz!6Z)m$=()Q)MNm zz72TJK!u}LbG^G-=1i-hNn0{>(H$T5AK(8&PrOf)inMX^W~g@HB!?sv1u7JdVK&oX zv)qP4lt3&n%x?vQrZQpb#ZZQ(g2<{?OsKFTNb(RhzO+j+5LTm9a2pvFbhQBH5{?|K zVB^H6YVUvNb1rJOPE=L>7QncYFS+$rZK&(sPDim+CWfP0ZA}XKnA)8-R6Ug{7o2;y zD}U%2x4rBkPl8;CwRJ_k)kH)sM5G|sfRG0uV092=0dxQXSRyDDapdq}96$AGyyKmJ zP*AP603csj$+Vj5-NBQZN?fLm`edio`{k5po;Z_FV5jCl@5jcN6$tOdy-s_ivJ(Y< zXF=aBJI3|No#-1wH;4m^(X?9C{nx#lbJ(J}gnM<952V>gTAB^;xbK~`j~+Sa9CzZx z&GM9Aeu{t5OJ73OjWuLk7RqYUz(KZ-R3Oa8F;=y#G!az9itQNiP>=%IY!=Q%aE^(b zqhhIo`uZu2b49AR8yXjx8cil-4xW3TAO5K~TyohDJ>b}hF6!L`j3zRg7zkF&W>c^d zr;rI(G6xg|85g_HE!<@4G!#8jc#j0_XOpmlnv7k_=5Xr4&Md6GFyJ$Y@`UqO^Z2iC zeCR75`PaX{Pef}Q#Te0gRlD1%Fr@823>=Q?|I@C6scOksN5@L!R3P<+hbmLv-R^mJ zy8H*8a{2fCK&FsAfLgVUcCCX}Lg0wuI04i_h~P8{5{4O6Dg~(fw!ZdF|K~1@o4%MY z64$HZi}5NGL`fAXe|ypM<9=I<&3H_4RtGx7egn*Vc|kcFXJe;g)@tlCF_z^FdM3wi zov!S(iO=Cf2kFGgQ}pOZJ^B;>`1ZHmZEbBG>>QZQs3TD;($k>KKyZ*CCcJspOg;N& zfg^=M`hZf9sT1R4dMgD4rS>0zk|G4=&HJ|6HwKF$avmx`*DJiM zAeTQ6=asJ4&wltHUi9TJ{mauv*^%#;Sc%KDUau-e5y3M>Tq<97***X0&UbpylMYp` zC~b6ds8_p?ZW91Nj-Z+#@jxjqA&!sY&2M_z1x%o2$Gn`jNn#k^Y$w3;uKBe#h5n+H7a|h?<)nf|H-Xc*NuY5J5$eA%Hz0@g4+^#>Pp>v2wNDk!Cj^U3K4|eBMJI z^R(aS;xHXM*)Wq6?f}{{aIE(I%mUq1bcU*cL-t!qpaQer20)?^RVYiiMh%}8ve;zr zH%CH))~p02MYo$roAMx6#zlup$lzOe*FXNrW54{R_iX{sg(ZxIQgQqKI1}b5RydAV zxELSIBpOAPxJ2awp;GqL^(WWAa?QU!?JaMA_GNE>*Pma1{SEK;mDN)?=lmRsD_umH zF2o7WJshK4Jc)O{`$d1(^v7H$P7s|6Tx>nhzl{42-!?T0C1q>T&*sWAW<+pJP}A3&|vPN9IU6w^ok{@RreG^2++i|tOL+@q{iPC0uz2A5}I~jWw zpccy&YV5Lo_7adCEGS!|?za;GG>h=?EdqYr)0 zyWjb*4>^AP1maQ&>XY=BLD|Bb1}Ug)fGmUSRS*FXH8l=NS}4Z{P;YKv^}s>)-mBvb z9lH3cN8S6OPkMbfd)xfYCpVBq3z6$UAz;cuh2V)T%MANImyj8FkU@2ll*24R)Hx_M z3qc8HfFgnSET*}qMB(lBJ4XbtK*2~9U|}4r#K<_s)t`RHv1>l}+Vh=DH0!cxwA)%J z9mq6m<6Q-(iD}07tOa(XC|hw{30#{@&ZFCIHLAL@8f73J>l@l7$}&(WuM`j8yW=YV zyIH;V5sXHsQpr#^$%z+5vspWB^ROMB8Rjy1HqMOLmcp@&*e~EYmED>&Mjba#Bgr!f zcwV+F)UY@Uo@b5aHrp`p+}}UaKaZ*g9|ce*1<%vUR1M0#4gP&v@LXOpWoZ-@r9XBP zJnzO_&>zd7jHP{Yr}SzWggf2l@`s3c5Ov|b0IXm#D{Bvh__Sz{j20mRQ4c5~NeH4{ zBj-#ZF1JC6N=9hV68X8=BAC_q!@>#{0#^Iu!l0@Arv2Xyr-NV}L34dG+UfbpX z9=}MWi%38CE%vp;v>S%)c#X@<(CxGa@tGc@GAQXdnMOZ&(%7oAJS8BV^7;4o?aBmW zTLxY+Z*ku!3CmIKItd`|k5SqES>~}t@VN&UsCnmtX_EwlvD>B$ij@3kU3 z;UED=@QT1cW-}e}8vbrTR@E%Ci9Ns>I5319A_Ct0nzwz&WmmKJf7G8QNAC2n2cKwl z(5lzrl;BvQlmMD+8nM9H;LucN&cHE=0cZ*(fq_Jc!h=@tjiMj8w z?;_$ZAV3#+c<*p{C4&nOb?}*wzvV0c{FbL*lB|FI^>J*+(Fr*K*4O&`+o;V_+6Tw= z#W*f+$-?%}i*aeZ+}n12d}GY|I&94KTWjZv6$XG}T%ovN_a{k1%5j8BT%vNk($gm{ zjs%sZnNOg`nYxXOQT=`xc9`l!JG^pKkz}n=*Kwx;^;%Vj>8+osF+vA=`NycKR^qhp zo95aU&5hpfsOC&P$~{ZDIUCE2{oKFyBIRVS(!drG!}`j+_v@7)+E9Fg?_7U9Q~*r?!1%(ZQzu|@P%@PZL(<{@Z2HWO30^#M z8OBXFpTgsw`c}=Kd&N1>3{(^#0g<a!3fmQePN7izxdu$?|AJqZ&j~e`?|Pj(0#4Z z^rettb=dSp+3tj5-V=;P#9+S|7aR4uCbadE%W=E~JQw0J6~al?PSt_*yDz3=z8}9# zv(E1(crL|dN>Bf`%T}KWvFj=v{c)ydaHox}Dh{Xbtk1U7{)xSk4LB2HnEU23VmsrA z!PtrM85c`L&|Rlc?APdjM$C?$|GekQZ~VrukKhwux!Jyef-;7soxx<_SzvB}4jjO>N2(~@kIzjh&*&p0-oUgo>E{$? zlPZ|VoCw1|qy%CH`2-2FNGJlU!{L<}IX>w>^8S~<=awAvE;f6)W?+(-_4jtjnjZy4W3 z1bANd>BXW?&<59>@(AAQmS?1(BC;K-;J8hFlLz@qS^fC^GWZ6%{xs8_VwZ9M5u--~Cy z`ZGwZ?l72~1o{Obm~bZ{vuxVVN>+*}q+n7oli_8M(L5*x@d%dOmK?UCMx%0=y|{sC zAq7BYq!KCAc_hAoxRA%WhZtY|%$vUPzBm5ifo|jLFO1_z>rK^-T1SZ@QZHMcaa=5K zIc*u|xEGfzv{9>#RiIdOjC!7vs#(;VTl;gfzWI5zMqLMa(7Mg1PV%L@J%>*ci`XcS z0@&Mu_x+sJUaE}Uexw;JE8{Y3=f2&<)4$su*OrUeg!HQ!OsWjgfBpMmlGsl%IFV11 zksTG&OU-RAMuXQe2=KOh?vru|XPrc+1$eXc(@AL})p||m9S5V{Kg(R2j?bOuL@#;{ zU3P{_tW*zRMo_Q|N*E!to9aF@0*Yn4^P|_{aliK-eCucfz@S*A24EGyI0(u~u-69Q z$y@}90Jq7v)&Q7AQtf7#4mBIc8Gvb0n^XrB2^3=5&>#>4-V-1>NQ>nH7V&rIYm8Ni(v;VoQc;g6|p!0?)LK zQaO$%T4=_f04=`1$HfxO3qO`j$`%y!BM*;M-gQ*mkr!H7nWXGT6dZ*3q zEE@2R)6X1x9Si8JqjtGx8pv974CT-2u3g4b+8PE3w`lqj>*`7jAVG!&tOAkRUq*V{QIY_ z!OuM7-*C-$)~(X7kMB&XmNn+StaYy{Fv!kP$VU_Ghp8w!DTp`-dk~bx%u&b?I59)X zhp!5bh2p_zAwm~jDWT;Hh&aZDM?7x+`deDBd&S>g&}cTis*1R**llj!@;1x#=>a; zdb@P{Jn8sR4Lz!p@3tJ%tO_OLI`|}jcP9q@B%O7;$#K{&cR%hTcOu5M<#qshk>g#Uo`WyGjwbDGoIz_?gcW^T7md(wV1Pu4Fr=2+AQB)z6dPSB{I zbfHX#4jqDsag-AQIt+Mth;*@0YvB+6`akjfH+%(cFD4sD!8$v94gr7$XF%|#VNj5j z>sfEBDP^Rj4>1|(o{L~F>#W+t-}WsYL>@lPP_qIMkSBO?C>AR?_uwkN@Ud6?(#PNX zC$XYYEf%sK0ID12D%hybukAe8Zx>x8y&Yv@yBNHlc{ZWDF}656L>sj_eenyQ_jkJURk-PWqQmvsqf=OJe(70m2MZ_}X z?WUOzrXBy2yneSO`_9VM?)}(P0D2jVU^y<+qLe;>`8#zwDfCYclU(E0*U#Qgiw06> zvg_G0qu=>zFM_{GG(QKgyHYBXfJSDKmbD;F(tiEzJ?$7Ca8-tk3WYkSAU@L(tj z7}+2rCqa{0P0LvW5>^h19hO}t01Tsi5CyOeg%O)6WTOp=GbvO}hZ+D_A}DzG$ooxp>rb%*AB_0qt5~oN*vut#Dy#T}t2tf!SWz%*h zlhCuwbZ{2bpgqkDK^_^G$DtLEFMsS!|8&h~-uxKVo(DK8aZFwv)hBco-07HQY>hhs zT6FAmA0M{?o#npE#S-;P`jv_?^&2iNW5YXcKH9E5C#Co8)tCuD_w(^a#o&WmcFb}l zex_t$&jQ}Fv0r1agVkAL8H?~vf{00@SS(RvpJku>V`a5K8|y8)@VtvM&wus5w*KND zKcnr0NL4^aQVw|80ZbvAtby}vDjJ>@PAwCyfr23*aQZXAiUyTJ_Sq^5drW7A@u!82 zC#@!7Cm|~>%3yQ>5L7bYOaTXC#Yf)xqSv3i{$o#6^|DgTP>b?#L}=7Dv{KBGATzO& z#(s!Jt<{6`&7_}uOIu^NW}5fc#(5`WCKBp(fj9YB_pT{K6 zrjn|VENStcr-FYw@I0-hda*c}1X-y#fvjV&Y%Z3{lr5DhS1eJvP^5E9*?c)m#6-NY z-l2Tu()-@-;lFeA#c%yI+Fc@12Tu+P2X+~wVUP!l2L#}hq7hUvoB$#J+*8bm5Okme zvqRKguMjBMc+hI^QFy2zs6z)c6$cOtJOb02GXO_Ow2O-l74WsszVFz{>p$_txFB?3 zHRskFU2WH^+6Yv}gtKG8^SCu(+WDfg1l6Ts&waZBX?x!!8uwf08fnjPsegtC_Uod9WIM_-B<*lkl1(7Max>{H%1cx+U$# z_d}H9m{h0VxXzo|Ofxf!=yQWvo@ug9{og-sZYjlOg38!-Di>o)ojGT1pGmHLr%&l7 zjY&=zcPj?3EiRTw**NK?(hAjT>zY2Zav>(i9&7cMmWm~6H9O?d1#!Whd*^%p=xy)% zLw_N;3(xn$00|@k;IbelhzOVo1P>Ck)@EX;5E4>2C6kg90xOV^0O|slSvQLVgHlH@ zGXw=&a7M^t8wuKgvZ`q{3t2Xr!p}V5$)S+V;=EEz-|$zzbw}2tUoVw2yuQ})qg!h; zCaD$L_A@3a);&W`b@yYOZTp>%UFATZec;1mYf-@Lz+@*jDU)mjZi%te{NdFH{qS#h zue1Ex^G|4;NUJw%@Bpe+2}~ZMYDd88AOM)eiVuZBZ2W5?g=2?C2e{~P z1;@YsZ}0!udtds{ICfgAC7LgmsI^BoJBy+{l;aX9sF$?4rQ(J&k?ywNV=RSZ>=E36 zV^C>?Me4Yq1$2co(YT|3a+qhp%mSXLZO?x2ybN3M{Thn~&!fg-N_UWz!}rDSJ?D7l zY2FY>$3#F%9P9`>GEqo$K&k*5XE+l{4Jjd|vsFK2y!x;~U=q~}<+e)O1X z);6@=SeI(Oszv7s05+PSwQd2q!9{8KDJWq)C?Wu__csKJ1k zLWfiIC>#{344Gg&YXwLnWFyA<$?yCt0Mu%U7RwdN(AMVMOO&m7Xb=7OQLWds|FedT z!`+};?lksTyu|NpePt2~)%M@L`MmZsh8qi@#{sbZtzm6fb3W{K8o4W;P7N_?BU|Hk({{`anX z=r26y>hc|b^dU!^F^-=)iKMv!CjvqO_KDeKiw8VGoj?WfWCer59wZ9!eHms6Qg%ps ziWvb$gTw_2M<^4dkcB2d)EmWvwC7aFOg->TLYD9*g|Y)gEGsQQM*&emywQnlgOB6#mQ4D7K24aZh;T5b1;T@!eVLidWjvf|fySiv$AcZZ9G9q6-kRwj_1$~1 z;PGtCACq!Xl_IC!lmm=;$w=;W*T}u{U0V39XLd`64^2>Dvo{ytxKyTPZG`*B&7{SJ zzv2_dfu3&9mid7IWEn0oL-4fKe?Rd^d>G>sI+=GlP{KNW239hBunSq`ifiK z=O=&jix2sUr#&x!tGiuv?eQ$WeL_%g338-bLR+&`^AWW)izH;60jD115hUjv#2LsY z4L}xRb%9KXVCos9!8j#24-yFoRwM{0RE+Oz#ejrF6jmK**=P@eszR6nHk}S87uK~?k369qc-B2P(*Xv-q%ll#Zez-4(4QRWpIdfwUYs=+0-)!Oc{7)@ zc7|DFTA%EdQEH9t%V<>zWcOI$%GT$UYQur#&l6G##D}DDyGjbn?0z} zrj4-u3jmDH<=-1M|NJ$Q`Lma6@yCDWz45u{WR`JF%j!$@T2+^orRX0wlgfULyOv3= za~goSXhjm9q2^_yR?~5F)UcmVnq=6{O|UqoPpwueRIhGmeZ7WuQ*+UU-}m&p|I{D; z_dS04S>MbYx$@%wKDLgNCr==v24ePz81UrCI}!r3sKTov5)Y+x)F_kz7Ax)(&kzor zXb-d04%*GC4oZL{g{j&p6a>`(bVwU>dTv&T{h06TV~FG``)+R;Ivt5j2Yac}AfVkMTi=*Fl=g z(Dw{^)^Vv!qugQ_9i_NT<2D`rbKze092gZlUPQQ4>Kn_}ZQM(`kbAMXU@uGi z(t^WORK6IOX%X=s`@*b6LOU@g$IV~!6gTaq`D8buRH}?z)C;9DDS%e3sw?FZ5fn+gg?gfh zRB`0U3SV~bM}OeRogeam8*vV9J|&>;I*OEb!ac@+RqZCsykwpJL>|mNU5h;t|2bORBtZ7!>0@LGM>eDgZ7p@ zsP|h`;vyx8(5P?dN~u8VnUQ$bU;eGN&XxBpCXqu=Hw!>2rWO9!b2w8C8mKBj(t`WO ze|6m#Kly^p7eD!bX#JFQBM2w<-t~HOWZ&oq`-6)_xmciTWAuCXLhN?mpI4ouR4krW ze=|~49zT?44dIw2MK**eXtHKoz`{Hfy@x!I~z(ruGp;kW*ZF{g1 zGVINJL?R*ws8_(LDQ={WCq!U%sZcZ2!KCQas2U(OLlmW6hMEAR*w(P|W|JtmCr_u6 zfQVr^#!xV?sj!`Wl2NLS`e7z2R;cJ$0nY$@NL)uqG=qy)^Z3T6Uh~2)f9mZ|^Xg}E z8XTsYv`jnpWp5031BmT+_q;SF-P~+9H=L=Pq)B4#4D>XU^?u8;q(>yX$qn7>cFNnu zEqQP)GK!@NHTp7i`BIs3=pswt4b5rh*PfAdZhr^z6u`66rW0CCfR3TF7 z&4q>X3Nb7jOQHY=V{273>KmG7lvhecQWb2}TU)YqN(2uD!4awX@ilAfvQo+ti?H=w zC#n%|bQ9481VoN12QIwq&i8ukt?z!HKXa?Mjy76Av)V;HX(La;Jy5L}59~n_p-X~D zgRHUutPIZ{4C63bf>@zJ5rbjbW(tbUfE5iw8Oi`bz{*f4NEAd2frpYaszcyE1Flz- zKw(p>0br971VOQ*Hx|ioJp_OQ8D|7uBP5!|g@=l`{&R19#%DkJhClAN?;iWy_V^gx zi|fOvq}{aFdM1tkMMQsc@Jwl&Xx(!Q?U&vQFt@?VVTqaMJwMw&G+RHwN_Y(TDU@4O_{MR|C02B#y^#Jt1J&wp# z_xv!r9{E3bzViJa^3uk8-u~47E2<4}`v=EMAudxygmDHAJ87u2QxkSNH|^C0ViwRc zNUz{te^fL=RemvEc-!xL!)5n;$oCz&@X`awc)*#WXO9};)omt%{ed$padZX zqXUKmIGCA0;n_fK%0dn=2eE!kqaPLdsf^k0tW&` z628ndpnGg6@hk00b-^Y2GV1HS`q$QCR#f>jg&OeOk5}B6j=?2ljwC2PQjFCNDs3 z1JT#6zJcHKmfO{?|8{HGwQlDGxYx$cUXFPe+ezDdR)Dk;7ihiS)JmME{w#AExylN` z3zV6dNn7h{z1JLwoino%f>)OHMq7zo5KAs*^G7cFfjfNf1AgZGJKy#3(ds31b4yWO zZ^8R6po)xmI7+}Os)$Hb0S25V>Lns&zYvcVcm|57y#hJ@{>LVGm@ zDFAgqioZw(BAY`AN76%Fl@Cqh^dVrXr3%K`mq3dmSUz^ zVyCTR2PcwY6U#guZQ6-yuMO1xBdl^hU}Qfr=V8ng|dE*j}RnZByWnk^x! z&F_BmZR^*3>4q)W*~g0o_Q&GA-8kH9<8+c(CIzfq$Wyg>I%w{XLa9Pl92Z4ElC(p= zJ*Sd&s@;sXftp9VD`K0RZ$L>CuAU44Im4f`AKj$0hogWVc27mfnbI+X*#H#$goi?(s4xI6zG7U zXcDMW2=0Ym0*F?Dsx#9S3V=E|6B;zP{5SQT1*IEXW`!mUaFERdLwtnQashJl=f3iR ze|+9uR2y9YhzmukHz)UCI2$_`JB@4lV;)}nxPsng#(tb%DK3_J3-%%l_Rhwc8Dq1H zODsk?lVg%BV_LYf5*MjM4ha*w8ylJ-B3yFEOR|6XoNqQ=#;W%Fjo()O#D{*DKKYU7)~ah-D3z(X z&4gHr%S6znomf=jm^RLk4v*fEYL=ysjHP7 z!4uZ%EiL8>)a{TLR-;zyPTj9(*~m_tN!%=mU+$)PQB;WS)~#uP-Lf~~!#2cc*WKNV zu^H$|2Ea~cmX=j#kiNbw6=jCeU)Z@@I-jXp$uO5*)NVp(YhA`m0L%EPc)cWR)|MrGw-9DFHZ#0|B;a~2D)1v(x zd#!z!l_}rv0tl1DX#jdzjXCS*p6P`9v$40MR4ft^4K=FS3&%}aBFfzD!4Ji+{>FP+ zVzOCK4d4KFv>br1m3{>z5cs01cC;Om(`H*t07;rnOR1Kj_$g$rwI3X$9P1QsBhWz9 zvUt>Kfxi3guKUb~pH}_uYQUKSeZ-tt0OD{jVq;KXuN5LV1yVJRb~i#f z&Y>tr*Z<`;e{{P-=G2KsQ}$dR*$bQ1y)rkSjWa*SW}wsT>7uG(=Q(O9mnt-_ntz^H z)Pl8)&Bx$`H0`y<#S(!Cyp0;d*_bV+>C=M~`>dP8ejQRK;sZbVOP^@|@T2b5_68_O z!=lUx8Lpn*fPFyvjj7CCnt2Nf1D1RX+|R%bwPWd2fq;7-(gPqVwod6HqCpfPeZN^8 zLj+hm0shM8KJGsC!AHu`n@;r128v~>H>x@eAdhRSa$F*!;fght@(OKK$D2yTWpgr3 zi#hwp*r>U07M@^Ss!+Y}J^h)eVfR;#V^Sg1nsyxSnZaHrc1#3L*O^XrfGcs4Y@-7f zV%ccab?~+MN`)Xy@E#sOw^s4GLz!dob9F3B|4l8b)WDKnDP|YgTe#wz>qLI1~#J zRxdGA83mjw90=lAe*w z4j5DcKt3)}YhbRZlq#emP&O-SRz)pr5Yk1nP_9s_c|2|rtrQ$>G`+5rinLy9Xd#=W zPB#h0)y4t_vaWFOf;&|%y7a1ZF1hXF&%f}J2lL?z3jcq5e;OrQcAbZT-`?lmnC^|p ze6J={VPb&*C=eh(aI|EqrFO4wOLAM1wcOoy%XEt^xm#+1YD3rf6`53=)HaNvn#LOvYSgL4!g_ zHHa;}NSRD~Z#`IJbjvyK)e#g(RD_7hs&Fd8qWUIaeVpAX18ml5%3i!gV1V1K>TG!qiNQ~ShF1=@xm)eD_S2f zgaE-z1KL@J$IDs#vG4zjUz6dBALw)fLt1j|{qiCL=PK{6UA_1A@%}8kJyO+MW0`sp z$#qs6?BkuEj(Jmwf!oAw-g!GKt_+}WGSl>|xNK+HN5|gux3^*IYdLn&nP16UY1eh{ zM9gpc>)#*0_|zLQjuC()JKVZRLrNet#|@xXK-WqRciBi_I7vL5p{~876KJM|h9iM) zUi<`ZZEM@w$+5%HPE1?p{`cCAfV=>PMBv32L7(}ZXYkCg{(Hk`-upY7^u=Aa_vLc{ z)-SNuUE4;0yyy`_adO7BoF!#BD7!Rg#^~f&%WjVZz^De#haAmZrGAFv!EtSgmXiJ+ z>*hhU;wX^G6jC`}v|qFX?mYRsdvExPKYH-GFMF!@#B2YJ^3H1>?cRNagffh#ia0*O zq#~*2%u~!&xk-wMh7d^RN~}VNkp&bK5D=?^i3m~2H4%gWrdp09fHA;D{RB59OK0>m1~{j9lWvcU?Drcq{CAr(;Y-5T*G zWh5g!*2NgHBuj<{H82g>Vv`fnN9~@YY}#x@0cXH0=4f!h>+Z1r=zsUEUmdD1e4x__ zd~!06-A+mUTksA}P> zb>Gl??He9>>8(%xJyrDzg%(U|?7aGT@YN*~ zL&X_rmsznW5Y(JRR3Y{UWN=rI$jGt=UjImrANlXT{hPz|Ge6wvGM^mH!#zsvr(45%q<@ms&|zmLy03S*$18A~ttntcX<8Z6kqgp#O3*8=Do9LqxDak`pX z`j&!S$VrJ=bLP!XWI)=i^VGMjT~tyd5vtj1_48N`qX8Dveke%h_S;y2PWp&*>2HbJkeEYN&-#? zMM(lHUR1UV8jRh;z1=3)XdpPF8c3LhxsoV|!m|jO3Zw#vkU`OGpwlTEPrc>;^yF8( zFquaT$5TwFvsld|NA~>-3It*_ln|LgRfUWcO7l63f;2{Z zwj2~$Mj=+HC?g_xC2&Zrgo#<9q5>v{kSK#mf`kB4GgMUjky>9@*qKb`krr;`4jjsM zY`8>1e9@?|FALnNE@`7}=SBnwt^pBJXM_Qgw5Wt>vEjv7MBG!FVQIMwyGP5z^@1tR z{F5g`rjca}P;ViM5N&{9lB`UDxdsFdc>UvT{P+)i$9Gl7AO6;Zl5uo0yS4N0Wuol% zXs|_Lsq46=eaAkf8>^q+ww>;WJ+=bSFQ-m>*|=`dxUJ*Bt8u$ylQFc3AA6E1QM=?f zfB!#;Uu4Fp5~Rv$wHtt@ifULeXEla({y8NO0*Pc^P;83Y1`nqI^x``Skk?jr&Meo* zSA}X^9KHt7NGI@u7t3BD3y^Nx;c5W75-)0DfGD{aq$EX}j)50nnBe(OKcmln@_Xfz zANn5$pSOyM-J&1@a55a~c{XqtMW@}R(Rj_mY-RUnb+=#_@j&Iz-T|o+jr&JZ93GOY zq9MT1QsQK2w29&@m6u-1Bn?2;K&SWE8y@-cH^1Z2r@rzp6puaewdL!cK9u%j7)Ibr zBf)e&he|{xiZGueph6ZjW-?Pyphh*Rh(Lk>Xr&YznXn2tXEDEKh(v%Oq{O6E6%@=6 zAuy{l8wiJ}P+-I;2mt~Ff)~{Wg|y5|$_cewb1q_a4HF)ro=r5!}TK5 zyrHeEb_77q94FZloE~NSdpuw0i~W%An4bxt_f7Zjs%Xn)0d1#U&}h8>M7gUjI`+9}S(j|J?_xR+W43L2 z1{aNq)x}P?C`b@+*5r7Z$h$q7FEKQ9_{70mzW0ZYK3_$Qqq;5Dg25%rXga01Am3W1 z8UWiC^@;_qJ9>~OVY&5UrOB>3-BAsnk=kAJUz=?gy@U--haqc1(T zwj4RIzONp3yL8lF^ntqv2XycFG|ODxxl4ol_v+6Mx?MW%_ci_h)$_r6q(oi-Xk+Iz9TbVCrc``Ila|?w zO89rxx3~c;z^#KB0y+4uRn|2Tp1?ebEbQA#J&I?yv7a-6@^p#h_h`vnsj{3d zWF``USOL>9Fd0`EoSf)%)Yswga~Qnzp+1DBNRmwdEs{Tb(Z&-5=e4 zo6fD~mmUBJ*L4lk%8+6P4K_BPI_%;@zy9OD^pRix;r~V127xo_59WH^;N>ce^e*Si zo4^6r?JK{p_seRZIj;}B62b8%k=^tKTsD>m%~d+sCN^2y9)I1IM9sKu5Ub$%B0X}t zzVpEIx_j8gz&tO3Wna-NONyW?XY3~MeAP*~KOE>~w%WEi@5ykat+GqRz9F4wqQg1i z?xTm(tcgm8U?NniU@~6;1B@n!@VXESb~*hUK;qO?lOj4LS|y%Zy(IWGTI z>%V#-ms$jpB+q^fb~vAonG(B}4oXYPl6t>9)W=R>z2*X_KFfm1_K#MFp~8STs0qp& zK<6N%JFmH`gu5VmBNX3M5&d6S6b&dEfa|@DSts9*w`$A;rD)eD2F56eCDxI%{E}Qz z6yV@emvo!1mt~uli32KTv;oX0fU0PPLz<;ocIN>N3dmY*C>T+Ids^Y%yn;ZGX@txp zC{~a-M?RWC2KSV!FGvPKL!nGerLby7!BXysC`gnb#F9ZJqd{hU0!f@MqC`YhrjfNm zrj-IjjFc&-!Wvkis31a;u@Z5TSGYomS)y2>A8@E!AVx+A1ceZRC=i(+bXpx5BTS+! zflMr&T+5T%*>uQ4QAL>o2(VLG5th2=0am6y0Ayethz_0!&}iV^i+D5p*l>NcIfF2g z&fFdfgD67Cw}m7Q54-R zVBYr8Ey|rAzbL!s4*u>1_q2(}>_r{8+@Rf-na*w7;i6=|O$ONVceED^=&p8^lP-*P&Ac9Q4~+k0zsnyRfO+XFF>sN zE1DLNRP^-WRhRx&)4*w)+Sxr?{vw_FEpYnp_R-?I$$davs!>a(;p{N{n6#i}>(a_K zH3(1kvQo)Oz2O|6$vWz*Vl2an8cHiIe+kRbl~Q}vKUy@{5*}X`!z|VlWJ?fXn8bww zkOikFf=MX?gF*oE43gzQ-mu~t901OYP3Hg(j6lY8G88STP!e#5Bkp9t-Hg$xCMacy zgIOQ>_y~CES;Q}W3VifgWG_C8{N#(sj-LmeyohXgq%0R^$y%(43>lM>rb2Aj%9h(!eo71EgGzShpRLn459 zHza0C#pj8cn3W`wx3x%wMkSKeCF~eMto)zl<*6DnwTx3+V+w3}pb$8cU3zFr3PC{a z^PKt}QT^zZGwVwbuM)_Bnq7>fbzJ=hCI5%o?oVRAo$I!KlnNTb7g&q_M(PQ*>hM_! z6zn9ycTZvzL?~MAA_o9b$iZQo6lVjRWs9)0;$e|^oEMwS;bo^Fz`%C;?51_yn>-(2 zGmNq6v2H3hxrc`>cpsf<`IyVG%|Ja1M6AZTiDUUeU8Rsg8gLiIwv6j0?XcO!vCU;%p!1zm7>|*Q@jMWF>V1A5&yvQ19UPMj|Mp%P|ZNawVoC)%5yaW`CDa zBzYoMeS!sRYm{%ThemP{sD&0w03a2TNI%w8{K8AWStiz!G(%S?>H2aEB&XxlS`4b0 zYrz5^YXbW4KPH@Xs?{91FA>`d@Vs=e<$umrz2|3K241*>oYQEb-JM+$0-ALHT5sVf!6Y8uTdaMh zw#>6cNVCIJR>bK2o9u9DrIEEdIsBO%ADwSu_$qU_h(z{mN|;>NyJM5{Y=SY?*Ra?O z!(2z4bq}}c8n+GJhkj_=W3KB!SJ9^KNz4FK*5H8Xo-1 z^8eMvjnyo6^4qR!kSrOOxHCxlDkc57A0u63y1=xcsohPGWDXD|M5pi5E`mT2p{SUk zk|6^SKnM|_KqhV>Ce{FT!(oMhRFo+~*%JR$qbiw5g30UPu@JcON_%O^qhR*R9#Scj zC_^*=M2KQ1ljy8(24xlrnVMQ1fO#K7h*j0tc7nKxg(Ly7qz7)6b%t%pm|UuKyDdGZ9&jdr)+5-CIjVg)o>MHApa0N*K_q)K7A z?-pz3l*@v_-X(t5-EvO5ZIQ^HP2L^5AiDPXj=HURV$1O`T;o~67k_0xvCI;m60+X_ z&41qH@-S_0Z97TsdWC(J6>xtz*k~ob>$s@}Qi_WzW~K@HuxuqH0ss)k9AM<_&J%|1 zGLQtbwpDd9%^-}DF2T11Qk;==U|@hVv1JR8QM=nEr-7P|bH3HL%a?e$WumtRoXI>b zG0UXsr0Oj4X_*N~7nyj8{c~Wmd>@PREFsc#oU~p407BN9Az%9B^f#7{r>lQYP2^DC ziGKshwX+G9Rw#@12Nv8to%&?)fgifojsx}_b4pd}6ikXt8DJ*WfT#@444g#(m8gP= zSb~8-R}e$s$$11)hNvn;q;^?Rcxg!iNJ!Pa-u!oDx(t8y{Qc@vyoEnwX9bD8NnW}=6L=*2&UviV=-Jar(71KttQ7rFpo&_*( zdfrX3?)|jQPW)wvu8U-)r@wnU+G#m%yHw;lV=aKbjs`l5CEljzpK+d;a`_qSPHF0SYLwsf{#AYIqw(epN_Cumi)y9Y$-43C-xMYG#` zJi^#D@*QZXr9IiJ+1vvXA`W@Avz9yCHZ!8cj(aLA1 z4nyg-3|sPIOFwyTCoO9_wz`x+U|zyzTef`33wBf!sU57$GzkM_lAORmP;+WXrTf4H zCPAzeqE;_2CxVFRoV?u`L`qO5W&(r+N`htMyTxN55OHGC3B;5bQ+B^@hzLMn5oKat zep(XEDH%enU{+DEvbnY?*bpuNQmI{_1PmpH5=&GfmWZHnp;-pl(4~utd@07NF7{Ai zw^+0i@~TO)LF7&u-no-YOK@PUetgH=wU_zw^P|)k*vAjmeM^%($+gi~+O;7O)c!ZV zFiCX|l~VB_H@0SV8H@OeCxFDRk^CZ5fIM#kia-D&d52nxvh2}8x%)Y^SC(`h-Al~H)^sJim+YZu8ml@p88x0xko0Z@U&3gCcN z_poh%C7MGP2tdF*c>&jqvr~9T)^ae$%Rm)`4dkfgPIpaMZ_Stp6(>jeA^ zT^m^RD$Aq_FNc@D0ZO)F2@uy=TaMck9jmb7Dvm}33v0dvQWGrZC16|sS0eaMg<`=; zC#UKq5A3BLO0*DOqyyP$76(qnG?t|Sb_i!4ksKEMtZHc176eqYqku7)M!gbDdf*EZ zHdd1ge>@X#O>A?P-3!#Mjm{# zX0#l2(Oz)*0o*~v;b2rSS&I=vnCfL4h*XTECRXpIMt*|;Nlka-bPb)ndlZJt+w;Sy!Eww?WxVw zZ6=hMScDsj5*$FC7D%I|Ngy$>N4k?4(@O%37e5zLddt)?Re^VSYe_Tx9+X4N5}wDq@~J`%=S%R z3?$c+2!cfaJt7zZl?vui2t^11F&cmm&Ui8io)(C-pb4Zz0!TFTHAJyBQCc{NY9vq< zMHmEBETXDRk(j|CwPl1tg~;s1mE2fGmF>|8amFHEBQ(mojWEqTu!#eNYt|!?eB@O3evFDE658A!Q0R! z8{Q}E2nrR327vt`TboqI5T{~AgnH{j;Y@OpkEE{WI)ADEO|F+VBAH3t-p<^_$(>yU zhNlG#;gBK2Y&cOp8O(Hf2WL3w@6@4aHLh!3-R3;ItbK6a``R48wg;q}zQcF5ySeW9 zZ*uHy&cA6N+vMIiaYDPTaUp=d?10%8_?@+?+vfbcut{0n^4N9@WVI9S!xMR*5{G?Q z&0Mqq>s@hD?hgk`g4w0dt|2ycY5}&Jbjq%+%DY9{HYH5^eN{v~9*^R@s>TqaDrd}9 zfGi-36&h^S(-avb19+|rDMn0;vgtU<2ri^eeD-o531%bZCTRfII%kDO2N;RQOzgBv zBTNdSKp-d>q5zArs|iFe>yO3RJ+e!y4^;!t1kuCdGYbbT&L*zFYrAwTz_SJYlr|MgCvSj#vGxL zAOr?01()Ck2r+@d0)dc{QX~yZ#7aVnIRp_=RF$K0W;oEbAC7&Mta7Aykr*ZwAqWC0 zwd)XMS`n$G8mi%E5w8eDDo{nGR-47U7_piHT8T;BqFNnr`g2lt_m~I=ww@fA`i%7> zbHk{>Ry&);6K-Ah#0rREa%0^IbJ=r)?~hb!>Io& z;rlvgt1eWLz*FMElf78_!7{vdRxvFOBqg=!V(~!^<`bxPAY{cc7W^iHIYUIi3@d_3 zY78%0@+Uh*&je#1ojNyHHbUn2EDVUcY=A3N$`AzeJr*ddC?b@CkV}7Ss@+cE#n)M( zF$!x$rqQ2wL{&kA6o@ghLZNKxdI@T3A`?fjTE#(^o=9L+kpMxgpkOu&I=1GwY9mT2 zBw$ryVy#G&DGJ2Fu{nMSNF{YWGYRp|pR_XS%PU97WToZ>fQV{T5s(+IhZ%vSlHKWd z&I*&4))qT_ggN$6D$-;76K*x9ps4<+2@sRxSWzIWS}2?nXRU)PthJb}N7gbWT9Ocv z+2++l#gin-lSd)u5(2~u1QZRTlfg^@rsW0cb;S9#7j_4(_W{%|Ggn_GnG(1dloOIMQ><&JlpxwIx)12#&w4js#Y*_L#{&R*6CiSygT`{K&2GI!v59RMz7)%Y`y&1J zzN_Y4_FU`Q^(IW;uS3_i{Jd80`vLvc2HR7+*6eR$RdkhhS~upF@9AVXR936xMoy6m zV~V^HsM!Qn#&pgcC_^pbjGVk;&)%b?{Ul?bMctZ#;H;%HXljjlQq;#chXEViEEP;wQV2-o>1p(s5oN` zkQfMO2T2Juu_zFewORnF*)Kq4A%)S*+Aw%(p^y?0#Z&-9MAd9G%p)0I2*fLS*rtGe zw-VPnNb+5YP$E)|N*a|Uy7dOL2opsxSRqzb8$_naSEFkg0hOe+kQ%Cofp?a4-$I!$ zx7hG48TJfTSQ`W(f+!i3tyg%YiWowzlkmde{xlg%6kL}qYHe?piK0p=$yRJIXgY-R z%c{`mW5u8Ia!0b7F$-P4UB0+ZKRMjkUsT1k{4nPaAnle^_G^NJetV#rMV)lrgI(7F)~%qn>3rb^HVx`CJV^IzKl-&)&og=um;HuAL zz#!3;!FA8cd4tn7v@v&v6C5^BeGubYHGo(VG;?%GwRRwYtl)$L`~*c9>VCKmX;lqm zsUi~riy~8VS+m46I1{MO0Mn^LChH%H0~=8N`_rEwvAg-b?3A$Y? ztFh|pAQ)aQgh9x_o zi7Mu|5uw5mF=emGmkCT{C9YKzb3}%LbD}^Y2`HF|H76lZBuh6moEJq=MTLm4R8#@X zGPj~46ioP&1qLyRt*|WFl-Wf5D&|tC0%j}tLlA(Cd>7?pIj2_1gjFb**9g-BHX4Jg zn%v6G`;CbZ&4W&j3Ff5h_dM4?se`!ky!KyqpFq^ivG;vHoc9w0@U+yYIqBr>BP98M za`I4jWf9}-0R}1!z>*6{(&ba_2kk32C=kfYiW4f6id3DZJCUrInRhNo%Gx&(v&~NH z>U_F7@9mnQtqXhYG4b%SMNd{^dB^_p5{w5wTE4!QV|BpmjI984>g?OHw#&dEB@3=w zrK)UpSlaM=!0D}*)LHf|OW2owt1P;d2L5>yK(8$6y6o0=5m%YeH+`0=53aV$aM;&% zlgL?_-Dgd}yV#~0))J)Egvi4E4FG`f$U&Ec5fPdh@`l-wxg3L>5?1v+bAN$bvY@eR zlKg~87Se{a74EtS7SzBlS#FS;+#u9|l@J_c)=pYP-~gDvbLB*F<;ig=u~2JiM`Gs~ ziAHmI1UpTxrv0=9LUKHi+7FtO_`-kAtVyQ40-h7Ymp(mhP^T*|n)=@afTU)?6(-Vl z6>eSVgUJ^Gq~1|M-d9x7(J5m*&0R;im6Dug5nA!Dl{>g=qFiYl3Hoq~u| zL{$tJCz*|kStu)$DpOtfL)e5VImSi+x~#Rgryty=OzSd0I?n=T zw4uaf^%$G3qEctMNt?8b+U&f6HW)54tk0VOP6mU$W@6u_SobW~wPEUm>ns}AO(M&g z-Rr>gaIhYD?;=*gcz-xlQj-JB2S@VKao;dSmgT}t2WhtbZ`$BwpX;@KoSZ{~f0tBhwE#2eF z0%k>3vmn$;3GV+LRYfhSO(Uxcl-U*9#*Qkh33xPE8M}~&K<=%k0HPY#E+P<7Aw)!) zBgT1*v5HZms3=E4FjTFWQ>;uhA5Ct-Tz4q7ZG_6@KN+h?-Qi4{ArKDMd6`~ddIKl3+B*{gmQgavYP4`D9^?QEv8 zkBHXp=Hy@thz$OFLUMyPDFzcb>Cc^+WECiTn;MIX2nLA&9E^1)0yv}O>5UMG^2~fY zh{1sw(ZsdNL`t9-EH5x>h6;>Hgv^yN6_v=uSx!NlS(de1S*K{`2Sq!3^q`kN`RF69 zM|#~>yVGdq9Al;_ft0}nQeu`mV=-1tfGa}0Zz_nSQss=)J)l*DNCX_CvG}$P;ecLL zCmUl8tdSv~U>VluvrEDh!V#l&753EG z1E2xMev=3!xP-wgxRIy>&{Gem$0 zIRy%&Bq8TK1Zdt28D*Gp$<3TFmk8o42ni4?%%VajBTU9qRP(`nI-da&vREl~8bP0U z;>pHrG|=f}B!s}kmNprnfP~!iIg<7#SVftXp-~$|1YxZ>f{B>L72CurQ8a`kf6x#> zqH2a9EJ6f|${Gkkt#Dt|0AKu(x4FrmL+~{a#55nKOJUn{38GLERSuM_84dfpOo;%n zKikFf$`)OA7|_LlU9_E#?Z$ z7|m3>?FJ#VPc8CCS9s@ki;@(yw>h_0mUI#5yo}?;19>oKnj4&c52yd$gdt!3?Pa4| z_GlFsoh1P*i!KrL#w$1O;=L$0AT8Q=TnM0VTU_-)t{Vj1qS!byclGxMI4{1Q)pIUe z?wxl%7Xi&(gT1Thk+-=Prp-^Yi^ayKlizS)jP#9yXbKp5QJ=D1(qO#ko9$k&nn{Ks zFl%*MPtU=?ELmX@I1nTnt2?(HWeC)KhXI(D)sodz6OBr?-A~CW01m93jg6_4H|j0s zq&+rJZ`NVSXW5E8i-~We15-xrL2Znj0e4_=3D5v>X1cCAC=ucbbF?g@AcJbgL<1pG zEtN3VP!XsCB5KBwEl;fm&tSE`D>;bv_AtNVx|l{l0#IypCMlw6Dv*gll@T4>N)7yg z7;I4zxI$yDV44Gwz&v-CHU)782&2IODnVqR0YL?VNQOBYm}iVRH!%exN^s_xZ8w1! zO{qvYqb38CHPC4?8d`;JBS#KKhy!GChL+Beu^`MV%%?A*IT}C*{mJy?-t*OD@PZCs z`oyGv{L4qrJ^xQe_nv)mGOc7jjWErwJ?q5e4;0*f+gCmPmOt|6Kk}JYH=B%(ryV6g z18c}Qgh?eah*Z=-Min%gKH6HA#THHMh!V^yqQoTN08vphd=nB^FRN(9Auy>Rm>NPr zN)*(cV(K(LtDF)Gnm3r_RLL!EeVFTg^>{)u+V zF72}L%}vKC0KKcm-Zr)YNLK|m>juF#_q=MWbJdLIbUkIKpyW8Rd_CI$#~TNJTg2)_ zx;*$+!R{{DkZmTvn{Gp0I=i}!%wf@{qv2QyjFXf58i=U#n%DeE)(Gg-vh*V$Vv+{0 zZCrZ@*Df5X|0Z@Ah6jjDYZ=+>?Tke>psQrX9vGP=@5KtjTr;<3S8HHEVDN^lWSu1} z!hkeb67B1N$Nw)AR3Zq$Ty!nt(F=wcP*wL{2H36jG6TyB5XE`}p4bw#6mAQQ0767f z48j4uCKf(1mT*7dP=n^^O;jNPu;?sv0y=gtkrWvRh!R*Tq?Y0N+%($~FkuEl4m5(@ zt1u`iW(Wu(4%`$O31X}eDo@M{L=ZxU=eWy^tSM-r0_Oqk42T+$5hIr|bUMXg*hlr! zFrJ*;zc=XjKQkT9KB1$N508f@Ul@)CL!As?#9#1wo^Jq>|5zZtHl$f&UJiE=n=E8v^6+{s-EEUx30!)7#!D^r* zK~YLcQO?>C96+#D&P!Y2MB*+x&U{m!K}mp5(g$;#Sbu$k`e-$xn`8rHN?g| zC*8AJ0mW$dY#L%U3p!`kRfBDx69HU-U3Xfy83iaRsAdQZX!Im7@p}u_&dGo`P-Y<8 z(OZl_21S(-OeT<{Vnzg^5`}0B$TP55F`9~iEX&bs67r0|c?$&-H8Vv+=4e+HFdaf> zC&1CM&hOuQX?lGB)6XBj@XPm}{oFs8y!fS)*<|p7RMW9cCa~->1AhQO6x5s~m;$Lr zva;$*ggoTP@_@l`q@CU&aTG-eG&s30W$zG!lOqLyqO;I1HV?X#3qiFKBKoe+|BHk9 zYyaT4{5KzYagKaGL!dckF$6^j9H!GC%%H@!TCs5^h={Nb~O);^g>ZeFYrdD$tT)qoxI%OMVlnIN)2~qFTSkJe=x!IsC5S5X!LdYsf3IK`LR)K&ROd=Fhk}r|FdgKiN^Q8SQ zif%oJcDg+p-B4kv%fvR|`K&0)&F0{<$huvSPwT+*a;&}w&pPKWFwS|$Sl0%d-v2gu zV%s*|m8|tTR8)8fK7^Y>@re^x=yD5rnOUNoqyA3wMjc|1D>}X)SEo3 zU9%pfit?aE!~R5<+o>pfL`sO!Y!9-7N1hl*03m?A=!0eWo9z}c*W1qoK*eh~gRRJi zqh(e1m^IsA2veP4XhsQsU{I|EHKxwx6kcN6N)|gXwt{nrl%x=eg<~(K;*>C%&KWKp zsf(N#)$NN3X8@rPi2z$6k{~8m32Z<`N$k85T=JZlW`-cvP8>|0Wa){LiTxFrqXxMI z1};lAhFl@R+KnZ8`qnDl;zVHGtxc^~{`d>sfkI*V6i#4*8Ld~E| zW*3c|kfV{!IV0xCh^oTKxdI6VR){hYAu7x9aHzeaP2En1Mx#;c#|oeZ&j2uV)~{7` zDG)G770DV*ANj?f{5!MW6W{R0Z~CKu{JAd-Fv(gu&8>nvlvNxwgH;G3B%11ALX|n# zPKB_l2mz_ujtX(zZ~PW>O=F)HU4oQQ$m+|C+N;`5 zpyJ>=;dj`2E_mS~RW*r{xMgIWtg4n+>x)Rz;EOWvi+&A>(N<2*Hn#f8(&ARh`nX?@ z8Asbr4YCj@B@(Kot&W!GpbKnXyG55?Zq?yd=k&AY?X%uFmwoqNg*>^bozBaGXJEfO zxept72Ce}>ZwqQKOYwJ$f(APRrTdy-Qir-p8@;Q>>V(lNyOfeXwi$?587$X1Xn=C;z zmGTg`h=Yrx4W0yNwN%yZ*8((}Gp~V2vTblxK&_S?u|n!CZ@WZSMFRks*rm-{Gs#hd zm9?hCb(=qOVib*?_U&;pFztn$J&DfmLQIP)q(BbSe8X@DgWaGkRZSEp+$}lJGrDTV z-4o7WR@%w;Hy}FLaYobbR|SD=ap61u5E5jO5Xo3dii&Bofo^D^Q+7~-A;TjyMn{NW zd|@^meCegx$^B2v?%(@`;fr7Tr?cn3^xSYb7-8J+V?Kpl9Ba@>=Doql*nChjCRBL? z^T|w$PD#VjKo5F%8LD*LzaI}ft>B0(Rtmvf1ftxZ&SS6B7FB?Sp-_=1NZNWqc4}LE zb4x`RSt*bZJ`U^T{+ib`}x|!t9MN zPDKN#Dplg8oXy%Aj+BT3BB=mEERss5>r#$qvPc(ohiD~YQbZ*)5VwBqCBCiDjmw0v zkXV&bJ zF@0SFarGLOLHtd|a^KwaWpmL)x4c*w4NtUJbSVpgA*|eKqe27%Lv(J5ODtW2~)5~HTc3rCnH zn?V|p${1R)5vkw$=2~IsR6z(#h$5a0Y4>6FVgeGg5VOBo@s~&nMgX+(D_AgtfR$LL z#3-v+6+{hCik3`Bu8(B{P^MKip4YP@QE6hz8LZJ=K<1c(k>`xVg96%l2t+K}?j2CN*He9WWqhrA zoo03t06^uIZVe2lzw(2B_pRUb7ryhkgZ2Ta|2!kh1XZQP42DKw0SmJ~GRx6ZVzwq0 z(@3k-<>om?iNuQFwk7~EL`035R>1WU1R_nK*OyO3)|sqtN)3%cAlE0`?gC=c=KJ!C z?YR;K7rI+=XfiEVmklLfXLs**l3h`+uplP?L0BXjil$scA8#<5Ls<1h%R40y<_4@O z15-5T8UQ2}AYd@(X}kbSiHvKX*di_~^ScUozAO+JOgV++AhKKx>z;u-UMI+3tz`|1EaVGuj%vRVKe~0YTTxB<+}#Y z+xku?&(&QmAnIz9-QFUxvfZIxy9fecMi4L~Z@0l!1f*KfcIwbfD47MAE|^myUV?53 zNWzMG05AhGW4>z85lwF_&L}hd@bD^Z>K1y=-I=yH$yO61Z88iHfrIO^Q=y7le~r3| z9}o?suwdb^K%ROfHBcc$civ-L7?5kKHCi$u5)ju~Z_RE&r-rfoy40l|oE*r!(1|H& z#o1_fJ3s@305pOlA!ZSRCMwSG#K9woANZ}~kA44Ny>s$YKm2Fz|J;v%C%ymYe?-6d zsZVz*VU?&w+0%BX)Tk=$P6v4-6J)u(G|~Bl^68wL^I6EIBhAMzj^^_h$8mmtIMaKh zNh*O?hr|jLD;*DxLHBB9Uje1NDwP=z7?>)lSCc&Pn4o!*gsK-tnIsw6Z=jI*`=hmOkmzS5?q1h z%(Q?b`QpxQ&c>dmkpYcp+B(A)hKs!)|)g?!c6cD0cJ_IZQiBJuu3aaJ2_de2v#AT9!<%YVNu!1bmE z*0%S*{A^dB^I6Zg)*Ns6{CAzeXtnIj_9uPTACFXsWH9XO;MlaOF*GpG2&g0pIURwd zm$Vh)2mmzJWy|WMCKg#@@tmP$Km$P0yn}@o71yNB^Vm`JNYl^@sk(BSjOUB6Diw5yW7M1lZz*WX=j^ zFpH-B0THkDV)hwe)<_wIGW!js#S)V;y8|=e;v?n-G$#N|3#~HhHKRZ&6$VEw#c(AP zD%hfu#a(3V@tVZP^(CZY`2r5fQ!=ipE^P5AZqH~%jDiF02#A!faMr89@XJSC&6a*X%#!3(!FTI0^2>m@1VVj2K{Os?YwK}KYmiObXVx3-j zpXcijhq_4-o^{oEFB{iY^k*Nz3yYQWf%K+YpR&{0x-rh{^t&vVyxjTf3PhdqO#RJu zS>jv-M%Nj??<&d-nTp;y&!{N7bR8_bZFg;V|LeBL(mh}Fd~eDUX)rjd;YqJugwh=l z2BU%AEy{4%Y6WBgLWGF4AM7uAHtVeO`^2`@c!xLaz_I$S*eBVt`tDvn*Yb62 z^L{G3XYXiV_C0sr@3z4>S0(E%8tdJn*hnt3X|CEbzBsE5Zqh;Ps!>Zg&H|C!Jlvw} z(qNIvy>Xm3z}87TF1^ZJ%1*8;5RJ}%c$)&uhc(WV>@2r)#M#X-9Q5_36A{je)xOd# z%C#E*+qw!q84u%ff|n4aQUbCp%$p$*&j1oTO!lQ(s$nJnEUC#^=EUflMMuqsIk2b` ztekW;w#_9CtZED`i}T!R+Zzrl6v|12VW3>qz-rBqXAW32(a{8ivA92cCVL&M( z1HvL!o*JGECIn9`Bh#Boa{2HiM|VIl{HuT`C_=SntP^ucV#$gi({~%d5ohAfrZ2J@ zXwrzBfhh?lMF(Gb*uii8{lE1G@zUo$lNI^=Xkebnac<0ix9HMgVPLi?ksCV2VcDcW zf?Q^p^pCZyMpVd%3z=}c8lxd|G<06~g|btai_Z!u?k~+@gV9L)qp_a&e7-s#ch=13 z+b$GWb3~Te?NL?5HY0rg|NVE~K6(Cw(_ZgRUPTV(z!B$`u$K}DMUc!5HHi>JfI!3q zCKYzGZMRlbCQ2I{P=>hIvaQXSlp+OjJHko;3hsIVB&ylEqIt;GUf%XUlV1cT>wj)_ z$Ycw$(INn7UlY@N(ffDVmTp%&*O!Uq>)7V~w5<1Dz1PdW=g#}xb-GWi zj`h|1yNU*T-RH~i+TozDyCg(!L!6y$;IYla-6x1DmR5zAUC~|ymVq@I?LBUVUAK06 z+w(2A^GzDmr$~c)yZbnO*=O~gtFD5VuW9xAHkC=Oi`9vV2pCMq5uKuwQKQ~}wuC13 zgg&_J*g!y3)AowOHPCRqFiZyF%$B*;C9#8=CfWSHP#2ECvOvTkk0%qd3#3KdUzcLN z^{wu-K~|3$@Ur8q07W(3sAC~A?>Ki(P$d5{qTs)G@T<1vF67$O%(1n>ZfExZsCh7p zsCy(^G9?k$>QMrKz;&Cjim9_%T?-yTApp@pRu&`X%7~$br=GZrkN)UC{JZgeKl?MC z7Gt1oEt{PH04*YUE?iZbwb*iKykwSo5F2eE!nW$!znMFtnKRLcvCEKQFdvYNvODBlG)}ywt1eL zu8!A@kL7FPRHFJ4*YMzZ6rG|=EpNYEsM!jEkhSh`-awq0V?)ZB0{n%Z_-KW*Wdpu(=)_76VeVXN;s4;z32~QBg zUZjOAr!6CBOce?hl4Xb@c>N=fqyN75jGq7LfASv}gmy+vP?m?mbYu-4WwFaQvSK)M zWH=rSwc;G@qDv>Eq0YF0S!iLJwdH6uiL;O)QjYnSPNT^WxLcHTP!zPS&96OVFlRiMkP9&f#EL{AN(?YF5hyBy0;dIxEi&MqDX9CT zby3p>C4X^-H5_y{7Yi|3G3}*`;-Ig7OkhBe%sKW;p2UR=$)Q z>=f)Hc2!P(SM6@?HdLr$;_VlpZ73L7fVNvEeLw=i(YKjhfvK{PE@;{?7Skjz&}P~J zN77Lz_6$)HUI3WfD$5b6+F9ARrd2FyDzL%s3g&eiU4L2^s8G~s+KOhVz14*Pt-NrH zfe?=3&|YfGFt31N9GoScnWoyWsd5JqPFm*#sM7rtA!E~D+klc9yb}mP-6vbk=FU8O z8I?2i$?;?;8SXsNL-~c@#ut9@Z@rN*SMB9v5F=Kz+AU*_6$=Y2K<;>OqC}REqW~uT z6NM@|-2<}cf1+b-y(IPWwtHcy^H!+zh4=r$kG$|( z|LVPW@Ahbp9Hhh~Rb+x`dF|_Lwi{KYOU_7c+udL$h^eeiyX<{iC3>P@5=3z_X@QWJY)QV)u*!wZo`aBfTHH8V ztz%lBrt}TKDRcacDUn^@1`pnvX|T#nUeH5i%g-F~iYf3eAe z;Wm=Co5Y0@j!j~hI;V95d=nk=KD9Mh1w_lqYY*DQ3?G=@tX?+(o|{D5DQUE{pqPj31jIm|O;IVDni()nMpprgYJNt~S&8aYC_k_`1gX;QG^{x9<p`bQO`V|;XFPGxao!gAYJy+_PMgY+hJ(KLcJShor6bG+104?s8rp}g zX1k3Jru58D{G;EWPX;(F+Ys~j4ywd4mB}F|qm_+rX9B#MpUSegpfRqb2BgUsa730Y zYU{lE;imphZXc&n9I)1QkG_agt?lWATsnkMzZ@f_1s8(@pDMhPq)6uMLa4J{)hZPd zyJSOcftwbHO0Ks@(&#(9c2Tyl?EqC3AQExV>)@$JGu;2uGm~HZiGTD%qO<7u|A9HP zw*%0*8vEYOT*VLxWL`Caejm9lmub_Bq*a?ne>mK$tI4KO)C-&q06nk&$}(tvSlREL z0O(!noYwhQZlXip1_6OQM-dI4Y-R|XS*)^a@L^Og>}t^hW%@v^|lk| zZC+5+i>dNKP5-**JXiwH2dyGh^Esl5K(r}HNrMX3U|Y-13>zqP+cgl7oNAq66-2mO zo@+DR3;{6vIIw{WpCQ!QSWSveagm50&kC==K`TM5VjM^pEVLB}Nxsa^f_t&%OwFAD zF5MvYt#2{LoY<}AkhZ!dx#Ah4&Qde)LVj3X=w?L~>_FV~qef3sV$w7vNiHxD!IA|t zVGu>ASVE*I;mdkWeC}WTv!57#^27H!O%tOqM5it_Q&lUy&R%{Qz{zl|{b7IY9B@#y z32^X6B)y_V2c7o0{rEBo+pDn)4|P&SQJmf(%QBiB^#|JN zi9X2sE;A!QG>Jhp>HSqqi(LO34v-Zp5dv{faw`mkG)`2(q~PEPUaa3Gkv1`d*_dpk zpZKnffo%fV&OMVH^D0BD)x(pIA81#e9lZaizxz+W=f}RIcl^a?{%kX6Wth{-U^LWj z(W{-1PIl;yn>8k;=ioA9q(9WaB@P;bh8qjm>b8gSR$(astamo2k?QS5qK>NZ!BOk-eq zMT|9Os<`&;cruI+Yhv19)dj#_IMAem+m`7cMGSZo z1MY!qTh&@Kv*KXXz=GM01prXD{7S$XiM&lIQJFK@;vi>1b{l3AFU#%v=^$<&=6F_< z`7|#aLrC4oE)}ncu7S1B+2tgvKtyH9X%P44RZ9v)v^*`>)!MN$sH8F#jDQ5lYl$o7 zLYPpF_Q4(Ci=W5mfAXJxGv&?tndO++bL;ji*ApOhhse+ zjdY5D88Xa61IMGWrXo?>Fj%QS_1yJib$ngKI;Y>A|D7ckeLU3R@bpV^+P^Oc?FL28 zDsD9O{s(^chd=+(-}?BSvY>fn5R*XWqL65-B4!E7XUj4322aXisuy3zN-M%bS7iOm?GV{_G*Ycg z3w5nLd5Iu#SrwdW2hwYI;Rg*!Rbf_1{l7-} zou_5mXY8u}`NLdC{b7n-RFrzT#jeZVuSTyZ=wzvHcRM9zfiam(F*$s+_pZPFy+_?_i|9SX{HT|N}{E0*OY{{vEElPDXPOKv)qX+!@6IxS9q!C_ywL3l50ZP7f8w1=w7kY_oHN7{I_%y2MzU;N1XfBxG){{El) zuA|YMR2P|ftEH`bMM;|^hh8?8pZm6@ox4SsuA9*4xa_Uylr$U-wCr?g*$VX4SRdE@ zj_(v*T4m;oqD!~I@oUpyVE6R@CVJ$nITKv&IO{Tp*R9mo6%Dz_8RF$0w;enumaSWq z7Z!EdRe#?UJYRM<<7B08PeF9Qf1>00RB?2_FVB4rt-}LACkQe^Q4P?<2w9v0^Em>5 z$iO_yQDqIxI75|Zh}!^ z5xnXk{JNRhx6n-|EhI>)7I8KktWb`qQbL$iCTEe#&!3?RO*FK^TMt^e_e($f$(KI$ zvCpEDja4IB%{GmP{W`&ii0VFemdn9;E(Xg{Z1}zFh;8QDAQe$EgBKH9*JZuEDrK^7RR29Ipg~}8-MU|ZfOPKq=71_@ zRG|r>CR8)93K}{=gN6v0D2X6aj!7)T2_7lO)C(0gaM+&W=_e(=`Kbw7{SQ3<{-6E9 z-}vFb|J|>D?=St!|MqwyNZ`5SeL>~)W7rftUv{T&6AQbu-jCM>o|otJlrxfcer^+Z zzDXQ$)_X5upsQAm+%^wwovh_y0?)t=GSK^s^Hwos(WT`rnr&9p*Ug&W#OnQFi)|T^ zeV=0=jN?J4L!yL9$Y?$|mPgBGI3CU9&cFLDKiB&Y{?zaL#1~!w%M|5&ifI-QIR{la zB1I?#Xr7rSLKL+xoknmJFI63pyb2;QNF8J<15v@jwa;GFt(KOW0kbi_EO3KbScNvz zcCr#GxBFu9f-KAsaVt6^q>6jKvkp8ueSdt&tsoi!{;U7?fM*>eRoN6u_ht(($_E^>U?l4p<7ZELayMKe&t{NiE=bR zC^}HeaFTa0&3mZw4(0(s0GKP}(=oDYhOCOns)!KhrsZTS1s*9!WJX*ndyGgRiC~Ui z^4bcR1OZqgI4W2pl5wv_s*9gkiEabZL|#0_+^5Y^Bf=62g8`Y~rI8mbGHQ@x3?n(f zb!?;LCJH9EFgJ#!Br}OtW;=aH{1|XwY5%T>C0YhC?KhUda|H!d%+f4QkQHsbzG&c? z|M>^soez(ygKitsNdNy9hjQQrdglIJZo3*`s;Lvu1Ia zPpe8BI+6GNvmgB09#7D0H$hCun8ZtQ3%Ru&P*s*=Dn>agn{!=ag#n<}!2=?Wj49>* zMbG?GkPs2OF2g(;>vG-peQ_rnG8L56o;C)a{ckD+uvNm0$`HyRUS*I~kYkP}W}4Fs z%rk`s%+baSJ(_A8l>{_9gf~9k)Yl!(RbKdwPyE_XegBXA^MCMPKl$FD`5*tI;}<^t zQoAwNJ58c)yLq7&y;~FyfalB3gO_!&I&W@FAZwfXe>DMo_22!WNi(*+&V2*Ei%M3f z^N+T$vD+l}@!q=#pS%j57jbC@`Z}1uS8^<`@-CybEy^AZ&$Yf=|NQpBpq8DI1~=sG zypK3<1zs}^OZIcSVg;K;Ngzg3=U`Djo>Y4GKlqQH>HQzRpT_ra8@VoKz|7(BvFMs79`j+qb z!t?i{Oy^8_CZdW!Du|Ssxgj%=2#ZzTvy?o5Maiqi6S@3?059TW{Ypg5(oUG*W#Gwo zEQrYU(bgY~O%^D`HOc{VIDl6B5qX(!F|%o_L`b&l5*P?jfCZUE)kt-RV##gjbKD8X-#6= z%{2U?dcxvYbo*0Vw-v6fn>3P=M2iMw-DmLN#R=0=9O7l7TyjcP{N@(^8b4J(bz zP@`7G96f}s3^=R1ig>AKW2&Q9oFj7%Q3FqbjV08?E_w@+QOyQ`7HMTjbzwN#);m!I z2_vd~#!485h}RrEg4c{+!h8POw;s~y-icI;t@VRW8IDJTx^T*_BE4`fdQsjMnOE8A zkW#>K*w@uD-))!FAA6bUgF`wwK2plc{A>UHKlfpJ`s?5H(hCCsq79u_rp{?(p^Btz zd8~nw=Q2Q~0->M`MUrK}U0|FkW+Kz%Yf2teRU$HKTU3?F#Yn#6V(u>L9^wSlR)_+C z0|R13iWC4-j8;FJiBwU=0OSnSrY&A$1TvsOgu`wJ?WSUQ{DtEW{o23y`7i#?d;S9A z{m*8ow3C4wP4Fxjcy>F5?EDQKs9tlQ3P?0T`Wi{ouO>8^gim36{2u;TS^c*EUy zyz6g1`R2F&QU0oTW`o1Km`!thK?D*bFq;9BsiBcjfhz{f3=Krzl4evOXvjg;3>tFR zOo*e2h0FqKnqz=aA-Z9mv2#qu@FxASAcIm>vwu*twj;PT9t)^)M%-C!C9NGn0I`zF z>ZQfW{FoVPHh2_3i0(}-$iU_{O@si3#%TTuq+XoGDitOhF+g1UQGs9#eV9!#%^DcD zTKL-D0etwe{l>p~Pvr?+?M|1*qrToPi*Ro+JO!Z7&B54kH)&ml`eoxL*`Hp~ zCLjZ0hU4K>+r0xK0_Gj*bpZBt3%`Ayw=8-zSn}09tF4M=n;JQxrBaM1^J*e8pAtkxm0t74C-L-~zBc=^ zFaIw(Z++MA?tR(Q$a`JHD&k}^L4Q0$oXt%qL7Dx}i$@RcXeQ2PkLFs}#JHQZ!!Aof8cZglR-LXDEA$a?@VYl5|E*t{eCEIT_V%4N z1t7??seL9G8)@|J9|=;T?(pax+APU-?iVl=H#$_Y;gx~&*k~2-8sw~n2YE{ z2J;M0zWd+&jyL?T|I~NfJ30Z&G?Gvd4q%-{1QHSjB4y_DtU_M7gaAO?sM%9iNogT; zkK#Rxz+g&?zVzHp!r?k0^?u~NR9w?+`e>6r)rqHM6*Hp(l|}@KXlMi6%yHQ5ph+`1 zK6?K6*$@5d`#$sG-}o^MKKb`Th}vYJY~;;(M7%ei&6`D!0YXqy>XfM z(ZgboxBRZU?F84u7MB9hR{>><%0~Nph}X5^+66ehNQ-%0$pB z3K9VZW@D8N^pX{Gf*6{B&Ru>YsIq`++Q4Hi!qcq^KmS+1?P;7m|CvWK$zB-E=ck`fn*`aZ zU7U;Z4=xkeF{iBq5k=W0(#yLBJg}f|7t~+s%S1VU{r~RIyf^>yZ+iEqj^=1V(bCa8 zDzh>nz?4RSiYnOr#Z$RT0#y-A!M=hBQXoK7m~GP}1_FD5o@kC4rkIW@OrVMg!5p9= zmORRVkyUdrVq}JxyCbuLNhHHe0_GA>w3@h6=IDe8!tAB$i=Texr#|t4_x#;^pZ)01 z1G5LfgjbeHLYxSOuz^`vBkOSnlAf1L`(fVy%mP7c|*bTy-*8H|9EJ zmnsf!atnwe#FP8;gQ7(Rs-h76-F?WfR1@SQ;A9R+)o2s$zV7Srws(9}>nq>&f4}pl zr@Q6jk6_e3Kpf97I5|Qkf(gt_mo!2f45|#baExv%=P7$pL0%SIqQGE}SmK}pF>S2_ z&g!W%-{i6o0Y%d@8&o&;PZUsMgy@AWT&h9-b2H~~dowXf2zdgeXFYm3Z~|!rAp=Md z)YabZ^$WD2S|QU4s)7dP_!64<#wXhN%#Z)0@A=|S|C7HEB`N@v-2)o*kGEs0_K;9` zTjtSi$KLX8&cQ*8h9^_X%g4LF=l}Xw?|-s-lw>~8gGqln4Ot+{GfVITu~tzHK?4ZE zifG$JEC_)iNbe;4k0Tq^4s9Jk$h+OeV)ra%d1Yz2}GBjE+G&G z{S$3=?{JW)5mw}6(m&C%)1jb|U5_joDRap2!pTCfn=?~|0F}AsZ+yd(ci#5(|9ScL zcmA0>ProrgI($6_tp0oLiQs|~ zg4;PRY)Y5s>(%-6Ve2nfjZNQO7tOubvB2Eddp0e&H+jF@R*B{-CHA6$-p4k~rg2+W zM7L=xF@z$F;0OFZ9e(Mj*9-(BW7y>^>G;^}XMIdbWb2AcH_NI8%SLLLSXgtIW#YFW;BQOBQGh#uzAbi<7|48}OU-MtKzUtlY&foMpkYpHEQ%w3J%%egy zf`!1$kdOgVAwVHwYOjvREq!<%vA#^F^s{g272K( zbPh(KR-A>1019UP>qd8hTpprH`T=+D5Fkb{5zLA%0vTJOmnz335BT~6!Y}^qzxMm^ zvG@JrK@sR^P{mf+B`DEkcp?B$c6u}zoxf1K*hORaee<@?fvZt=Iur;v9*&eki@)aI z`*XjOzxM0j`nf)%8;_!ZIEVs4D=23G03`}g6%v7lV4uNPUaEvZAWQXMss< z5vbV!vS!A!3f0MQuI=6-0ZbAj0#rv!3;ABBq?3m*iN0I*sDDac(j_8b9do_f_x8~j zllSp$?c*1{)2=$^Hzi@N()PQWmu?f**hg#)pkM9{@@i}zHzl38N*_!G1C(y>1e{{i zN$jvJ=x8w1!?HmT;-k?twu_PsUl3IwC@O^#ngWc5C)#T_DTroF0EHF=QxVSOsBh1c zIJdHEp=^dXy#7x29q;^}JMVn=zuS7lm!mqogZ`Ls9OoF%Dqu1JD1cbN^9arfsu5up zq3n(rVACa&0A$OfZY=tK?ovO zRa7n0&#MeF>Qx zgW>a^e+Dw1fk=yHju0w@psbZj6?60_wj0sz9uQP&$B(o6Nzv)ja7_Z*zS{n(edb|V zly!@egy`1K?XSkmBS=+uHaNOPLE;4tHhJ-%x81)^H@IuSwyCY&RbH6a`LaFhIo<|Y z#lsxiywq-br=eZ+Xgoa8gR&&jh#WDiz;b^$iAi?QD+;QJA$h=DnZSgM6k#eD4UXh) zS%kcjHZXxqk)Hr305GGb;>joP;`ML&`o>p(-9LWKyT0yGx_cM%Kp2gN7@v$$#R@bh z^He6n9AwTJ8Ji5c67zJ1YnDAB5boW}4y=U~fP%ydRw&r`MJ0eDL>PhLthF^ad6Hrd zU`B)>YK1ji!a?e`XKa-d6%hfMv5v0g!{$en9m3L*-w3J zc>e=G`?roh@qzCGj`|INT1`j@$Us5v4XW5Idj{h*5EBQ{c{TC&zEd{o{$P`&CTu2xn@X=mjfPwu1+S}Z3UX9(yW#*cje#hOW*olPWRl)t~xD1PsI%T(0 zQhzkKl)il3}NdCoSuU*As6ZjXWjBY)xEEt)~Vax_d< zYaNOdFdX!?-R;qwnF2)>A#hY=%oz5M^|4N;#hJ!RAVh>pAVf-*$OoT1f-$W*})L1k>>hh%q!XZ9~C%1gS6tQfZFCc&x4BfZ1{l z1F=>htxLHd7A-m&P8KqTZjYdf$c#)CgZ^gu8Ovk;YQ;Zp4)DCv!PZUQ|8?yUr+&Ii z@A`)&&IHisv6k1NZGNQ>yw!MEoF?`a*PTpr(E?_93|yu_&@TJ;L8n8+f}?@Gv=3VC zpn+vhjCl?URe%K5`O&fFy)I>7mMmOo>8{EL2QEF&k$J%|+27 z;kLwHV>0>5sp2z3#fvjwK30wN_#2+YyT9qr<7`(N&G{b!K(wu}d5)dOs@2$*; zm4KK)pcsOZThS>10Wuy=W|8ta6B-maMxkcW1?463)&W8laqs@~c>bgB$H@nN@~`0J zbAPkJgHaIhK^yHcH-?0qXOT7Ld5FpliUKB?O@{a90MIKlvXb^Mb}u^xDe;+F$<^4T{jT!-dqsy-3H`cINI@r~p|;8{O$L1}+a+bB zd^8%y_Q4&FOem+*P|38KPlX%M8~|p(ynCl7Prl(^Agxx{6q~re5I6kiu^~(@@y;_Oznqr%Y(Pebr zH(lXymwoK2&#l`jsK3NE6x{v5u;u8j^R{n21N)Fy=> z5UH{Z&BveWlyCp4zj*lOxBc0J-}RM;C%wmUKWpQqSq1dRK-K^zBQVX-sstGnP(`Hx zlUPEb0FeSJn08u-P1`I0Gl9iy?{cX?N|2zC3dqz-I9C)fkH8~$yLi*!OZcV#>RTH; z8&?PICg1CyI3wAi;W@11whPy*KKD()!fhMJ7e!qH-N$6vDTyY-Y>+>C@Qr`+uYGRP ze!MmAzYv=`9h1hnfb*b~2||dWkEuOcbjN zfL%#b)-l&9)=CN5ZR(E~+Tcez&75*bsIq)A9mQ#c%mLNB11)-Y@#SCnbq8PbjX&6V z+dH0k{NOS4GsgY#2{4WrR*I^cLqhOMh8c1NfC?&tj0h|hsG5O^!3>B702Kg26b5mI z${jy)t&mrOan?j^wDA=OJ$(H8{>tyGp83UJDvB1SX!CGzZw>$lML_^?e8vlK*9p|u zJ?_@M+^=OP=7MuP-pleA`Z@S@U;Tss#~jWMqXOmk3F1Qk>wXtTt_gFD=5 z=7jOFe)eO}y#Euw{hRly&;QyV!h9YBW@rUaXtIRBeD7o)6JSs0Y8PD^5Bu8blr&nI z8!wZ4a$TVEvWxxeO!_a2F0ERIE>}NXN2p!3I^tF1QVIMwl%sUf{Jv>lylh|EDG~G# zno&G-5{`8iNH3R21NSzr(AHhk>g2cWi{mD=Z)rhsB`@g>7=i2D4!lefzjLT{hIu!*}#z~ zjw;4%GJ{kKl^Gy&fCxpLTSB2k1W`mU6D~psESOUUk_-Yu9wRcUKmbOO(R=cBXnyQB ztIz)Jzu0^<%sTUSV>r+zHH9%89ZA_KhzVzwT|TuFvSFXP%?W~+Io7WFQm5#W2EzE{ zM2l97hLed>)}(j*p+EJ{55DRT{J~GZFh(XoXab#Pj&3JIFZMBi?t@?W(0hOR+rRjU z-}q6?h7~kW&U2kb)jQn|-S68D!a>=hnz(P4aP4YrZ2@!ks;^xor|vNHoXIH z)3w}WZ2G<~i!KoamsvzUY{cC|2cWMzZi9W$w#T{70l&_p^t$6Z2i>}J?Xtc1Q3zn$ z>)UFFqbP}>rvT<^8#T)=O$U8#b`Lo~HO2@Ao!T~ks?)hsMVU|HbUcj&h007->5%Zwji;EYK&ps2BET7lGjrb%^J!p>2$djX zLXe0Gz$`S-YIgC~4)AM#{oB3*pa0ay@3b1>-mpJ!m0cntjD6?fDy88!VO93kf45zr z?rYvS&)#a)135(~9xdYV#o?WdT1Q&qZU4@n`=z(M^Y?$vY&OAkGDJ1FH~Q%B{PGXK z_<{GoFCKmJ?~%^X3ea|w8JtOs#7Cn^1b{}_BO=0dNtSf}mitN$$O>t*&-vz}W0##S z5u4xnO;!87j<~4rK1_P$hYb|&Gae{aUvx<~tQ&G0<3<7WZPNDaBe?;%8I9OB=Um+a zx(&?0GU(X#`Bo?9SNi0Zz|Hat=`t^;<>yqCJsPfARJF?#MVpUaH0JM>z+rr&9f93D(zWHr`qx~ z`Fv_cQz}JUBZw=s)ReH(tOE%--ga2vQ$PBB|Jm^8{^@sCs?zBl(CFl}_Pf1(3CiyZLx{Z~k~0@|T7hh1!kRz3pA^JUTjV4PW^DuW6jj zAaei&)k*;1Zns7E`llJ@)W6T_lk4KPg0o#+*FNU(+uCO5#U_iW%Z`Ul$Mi+-pR;!I zZgV{7y!G()jZI@C0R3`kfXhr8=WUJeyG?ePSq$LHE4)=EZFz#&m1b?16ZJ7Bgkxy$mTGO zMhmZd;t^!O`E%nBf6relJ1x@TSTrvWc|JIjJ4G|xA5LS@?NKCPvgF3G?!~y8g!wSW zsT_~3#?I~lcN$*@ML`h(gB8}lK|~RtHX&pUEAXRS;Aq&7ow8@;bQs_W42LHdf}fiz z{P3Xb&9>6F+wnfWP2|Ha^s|?l-*3x;^EzYK{qMH)=gY(?0DUQtxrf#4zPHJ*5}Q8k z+j70Y!`(r55tp4@FZ&F)nN)AnOK2Z)-u&4aAMjfsqkN^t0}S-82iwbK@Lo0c0hHDa$g|peQxLs;oo65G=2qS5`p)zjeZXdE z`^Cd9i>j>MvOlLUY%*TfxdmKw++8oC&forAF#6r1q)3E|n4MG-`%b45ghY~zqf_=s z8Hib+C>(?|l2Bl&Vj-0_sLEoc2!z0Ko>g;zfP4s?ObMb{i_p8%rPsdcU5z)s?f=$0 z=)Lo$U;meXLO$}|_x5s7)@n->LWKq?MGbKdkm6)`8kC)?Jh|bV0XGTA_O|QdJU6}A z+D8S&O#n>0?2>zYHwh90Vx<7agA;9cdo=E!=w%l=*X>C0a#$>1R43qJB5bY-pszLG z=w--uZ|iA(+hVPRW0!?9R^zPI(q#)2^olN>ToEvx6=wmJ<#;)C>W68<+jZ<(hulIZUu!vC`te`a{~x0QRk>8 z!h_i?5;qt`V2Pjrt*WX%M=Ih00Nr+nVh9%k{)aIxBm3F)3+^f)vQhLn5MjKuGqK7> zZZjVjWtUjbPhff&$K5vb`E7WAJ&bL9TjIQDeHE9hGmHN0VZnKu-N!BpUiaZ;Zldg% z*7l;`pS9Jn`kRZcq3reu08UmOW-6Pz$u(aT7lCIx--?H7ak+DEa?RaK9{ZxWN$|XS z-$}E*>p1JU2|RDxXU_V5e>l*=(5i}UdyVJy`QgAa?zcVWZHjJDP;bq1D9gio9-32* zcG;zN*{i=d9-e4R=jcwySvO9y&TNuRPENGrZ9L7IG7e3dWF-c9mrs!C2?$3g6U`ef z+$q}OPKW7!)>v)nRF}as0IY81i`q}y>G`g?-*k_|L0>PL`_ha3 z;%k0zZ0_nkWXIq9Sj)-47k~HW$)AS}G~Gm;_r5vnH&?NUJ?pral|r8P_w@W9#(wYs z1O2dQ{?cmYmCT;5i&Rzcrmy3+$8FZxzue=pWA?CU6BH~2NgCx8&F%q#1F0gARuyFd zJgYDo_O2^k_8!@2ohhK+bI)3uoEhU3Z+-KKu9hzJBlg>x$dh z=k_hTxo9rDjritaEO@f7cz}Uk2cCDilHOIMfp#68>n861G7KgF+(z(xSyq0ZF>vd) z<ssyaQe(GKRWyGlXFZ$S0c zt*+g^LS+as&dEf6M5kIk<+F{lS+i&Pl|fhvJWK%7IOAaai3;8at!dum zx;L3~cXj`}PTakzxRj&FS?|1Cn!LJrS8u*!cH3gF0Q!0}ocH?>&bpTeGmvjKDdVQk zzs&^pa`4Z+YTVR>zt64U2c1y1H`Z^LT^cWqtAnCN3}&Jzh?G^#)w-?x)?tsRVp2q9 zAsUTN@>4$BrDxeeMrl5BPv4~v{`+qldewJhB@ESq#RnA!rW(mFQc2(sVy(;w!My0y0=y%OWL^|#B!%b~yRs({!w@p5?fFQ-`R!m%8i=tM_@2lb@8h++rX?3Wp& z%P*Z(@OoL-h;7>^>5eu9≪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 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
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
    -
    - - - -