feat: Add Sequential Thinking to Code Review + Frontend Validation
Enhanced code review and frontend validation with intelligent triggers: Code Review Agent Enhancement: - Added Sequential Thinking MCP integration for complex issues - Triggers on 2+ rejections or 3+ critical issues - New escalation format with root cause analysis - Comprehensive solution strategies with trade-off evaluation - Educational feedback to break rejection cycles - Files: .claude/agents/code-review.md (+308 lines) - Docs: CODE_REVIEW_ST_ENHANCEMENT.md, CODE_REVIEW_ST_TESTING.md Frontend Design Skill Enhancement: - Automatic invocation for ANY UI change - Comprehensive validation checklist (200+ checkpoints) - 8 validation categories (visual, interactive, responsive, a11y, etc.) - 3 validation levels (quick, standard, comprehensive) - Integration with code review workflow - Files: .claude/skills/frontend-design/SKILL.md (+120 lines) - Docs: UI_VALIDATION_CHECKLIST.md (462 lines), AUTOMATIC_VALIDATION_ENHANCEMENT.md (587 lines) Settings Optimization: - Repaired .claude/settings.local.json (fixed m365 pattern) - Reduced permissions from 49 to 33 (33% reduction) - Removed duplicates, sorted alphabetically - Created SETTINGS_PERMISSIONS.md documentation Checkpoint Command Enhancement: - Dual checkpoint system (git + database) - Saves session context to API for cross-machine recall - Includes git metadata in database context - Files: .claude/commands/checkpoint.md (+139 lines) Decision Rationale: - Sequential Thinking MCP breaks rejection cycles by identifying root causes - Automatic frontend validation catches UI issues before code review - Dual checkpoints enable complete project memory across machines - Settings optimization improves maintainability Total: 1,200+ lines of documentation and enhancements Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
The file C:\Users\MikeSwanson\Claude\shared-data\credentials.md has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
61→ 1. Build: `cargo build --release --target x86_64-unknown-linux-gnu -p <package>`
|
||||
62→ 2. Rename old: `mv target/release/binary target/release/binary.old`
|
||||
63→ 3. Copy new: `cp target/x86_64.../release/binary target/release/binary`
|
||||
64→ 4. Kill old: `pkill -f binary.old` (systemd auto-restarts)
|
||||
65→- **GuruConnect:** Static files in /home/guru/guru-connect/server/static/
|
||||
66→- **GuruConnect Startup:** `~/guru-connect/start-server.sh` (ALWAYS use this, kills old process and uses correct binary path)
|
||||
67→- **GuruConnect Binary:** /home/guru/guru-connect/target/x86_64-unknown-linux-gnu/release/guruconnect-server
|
||||
68→
|
||||
69→---
|
||||
70→
|
||||
71→## Services - Web Applications
|
||||
@@ -0,0 +1,363 @@
|
||||
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
|
||||
package: C:\Users\MikeSwanson\Claude\guru-connect\agent\Cargo.toml
|
||||
workspace: C:\Users\MikeSwanson\Claude\guru-connect\Cargo.toml
|
||||
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
|
||||
package: C:\Users\MikeSwanson\Claude\guru-connect\server\Cargo.toml
|
||||
workspace: C:\Users\MikeSwanson\Claude\guru-connect\Cargo.toml
|
||||
Downloading crates ...
|
||||
Downloaded mime_guess v2.0.5
|
||||
Downloaded http-range-header v0.4.2
|
||||
Downloaded unicase v2.8.1
|
||||
Compiling serde_core v1.0.228
|
||||
Compiling serde v1.0.228
|
||||
Compiling icu_properties_data v2.1.2
|
||||
Compiling icu_normalizer_data v2.1.1
|
||||
Compiling stable_deref_trait v1.2.1
|
||||
Compiling futures-core v0.3.31
|
||||
Compiling windows-sys v0.61.2
|
||||
Compiling windows_x86_64_msvc v0.48.5
|
||||
Compiling subtle v2.6.1
|
||||
Compiling typenum v1.19.0
|
||||
Compiling serde_json v1.0.145
|
||||
Compiling futures-sink v0.3.31
|
||||
Compiling memchr v2.7.6
|
||||
Compiling generic-array v0.14.7
|
||||
Compiling writeable v0.6.2
|
||||
Compiling syn v2.0.111
|
||||
Compiling litemap v0.8.1
|
||||
Compiling bytes v1.11.0
|
||||
Compiling windows_x86_64_msvc v0.53.1
|
||||
Compiling num-traits v0.2.19
|
||||
Compiling digest v0.10.7
|
||||
Compiling pin-project-lite v0.2.16
|
||||
Compiling crossbeam-utils v0.8.21
|
||||
Compiling futures-io v0.3.31
|
||||
Compiling zerocopy v0.8.31
|
||||
Compiling parking_lot_core v0.9.12
|
||||
Compiling percent-encoding v2.3.2
|
||||
Compiling scopeguard v1.2.0
|
||||
Compiling mio v1.1.1
|
||||
Compiling getrandom v0.2.16
|
||||
Compiling synstructure v0.13.2
|
||||
Compiling serde_derive v1.0.228
|
||||
Compiling zerovec-derive v0.11.2
|
||||
Compiling displaydoc v0.2.5
|
||||
Compiling tracing-attributes v0.1.31
|
||||
Compiling tokio-macros v2.6.0
|
||||
Compiling thiserror-impl v2.0.17
|
||||
Compiling windows-targets v0.53.5
|
||||
Compiling futures-macro v0.3.31
|
||||
Compiling lock_api v0.4.14
|
||||
Compiling crypto-common v0.1.7
|
||||
Compiling block-buffer v0.10.4
|
||||
Compiling zerofrom-derive v0.1.6
|
||||
Compiling yoke-derive v0.8.1
|
||||
Compiling windows-sys v0.60.2
|
||||
Compiling tinyvec_macros v0.1.1
|
||||
Compiling foldhash v0.1.5
|
||||
Compiling allocator-api2 v0.2.21
|
||||
Compiling utf8_iter v1.0.4
|
||||
Compiling windows-targets v0.48.5
|
||||
Compiling concurrent-queue v2.5.0
|
||||
Compiling tinyvec v1.10.0
|
||||
Compiling form_urlencoded v1.2.2
|
||||
Compiling rand_core v0.6.4
|
||||
Compiling futures-util v0.3.31
|
||||
Compiling tracing v0.1.44
|
||||
Compiling http-body v1.0.1
|
||||
Compiling tracing-core v0.1.36
|
||||
Compiling thiserror v2.0.17
|
||||
Compiling parking v2.2.1
|
||||
Compiling pin-utils v0.1.0
|
||||
Compiling cpufeatures v0.2.17
|
||||
Compiling hashbrown v0.15.5
|
||||
Compiling ppv-lite86 v0.2.21
|
||||
Compiling itoa v1.0.16
|
||||
Compiling ryu v1.0.21
|
||||
Compiling crc-catalog v2.4.0
|
||||
Compiling base64 v0.22.1
|
||||
Compiling futures-task v0.3.31
|
||||
Compiling socket2 v0.6.1
|
||||
Compiling zerofrom v0.1.6
|
||||
Compiling slab v0.4.11
|
||||
Compiling cc v1.2.50
|
||||
Compiling unicode-normalization v0.1.25
|
||||
Compiling sha2 v0.10.9
|
||||
Compiling event-listener v5.4.1
|
||||
Compiling chrono v0.4.42
|
||||
Compiling crc v3.4.0
|
||||
Compiling rand_chacha v0.3.1
|
||||
Compiling hmac v0.12.1
|
||||
Compiling windows-sys v0.48.0
|
||||
Compiling crossbeam-queue v0.3.12
|
||||
Compiling yoke v0.8.1
|
||||
Compiling tokio v1.48.0
|
||||
Compiling hashlink v0.10.0
|
||||
Compiling home v0.5.12
|
||||
Compiling futures-channel v0.3.31
|
||||
Compiling getrandom v0.3.4
|
||||
Compiling unicode-bidi v0.3.18
|
||||
Compiling smallvec v1.15.1
|
||||
Compiling either v1.15.0
|
||||
Compiling uuid v1.19.0
|
||||
Compiling tower-service v0.3.3
|
||||
Compiling simd-adler32 v0.3.8
|
||||
Compiling zerovec v0.11.5
|
||||
Compiling zerotrie v0.2.3
|
||||
Compiling unicode-properties v0.1.4
|
||||
Compiling ring v0.17.14
|
||||
Compiling etcetera v0.8.0
|
||||
Compiling hkdf v0.12.4
|
||||
Compiling itertools v0.14.0
|
||||
Compiling rand v0.8.5
|
||||
Compiling miniz_oxide v0.8.9
|
||||
Compiling atoi v2.0.0
|
||||
Compiling md-5 v0.10.6
|
||||
Compiling stringprep v0.1.5
|
||||
Compiling thiserror-impl v1.0.69
|
||||
Compiling tower-layer v0.3.3
|
||||
Compiling parking_lot v0.12.5
|
||||
Compiling byteorder v1.5.0
|
||||
Compiling num-conv v0.1.0
|
||||
Compiling dotenvy v0.15.7
|
||||
Compiling tinystr v0.8.2
|
||||
Compiling potential_utf v0.1.4
|
||||
Compiling mime v0.3.17
|
||||
Compiling unicase v2.8.1
|
||||
Compiling time-core v0.1.6
|
||||
Compiling tokio-stream v0.1.17
|
||||
Compiling powerfmt v0.2.0
|
||||
Compiling whoami v1.6.1
|
||||
Compiling rustversion v1.0.22
|
||||
Compiling bitflags v2.10.0
|
||||
Compiling futures-intrusive v0.5.0
|
||||
Compiling httpdate v1.0.3
|
||||
Compiling icu_locale_core v2.1.1
|
||||
Compiling icu_collections v2.1.1
|
||||
Compiling hex v0.4.3
|
||||
Compiling mime_guess v2.0.5
|
||||
Compiling time-macros v0.2.24
|
||||
Compiling prost-derive v0.13.5
|
||||
Compiling deranged v0.5.5
|
||||
Compiling flate2 v1.1.5
|
||||
Compiling tempfile v3.23.0
|
||||
Compiling thiserror v1.0.69
|
||||
Compiling prettyplease v0.2.37
|
||||
Compiling http-body-util v0.1.3
|
||||
Compiling num-integer v0.1.46
|
||||
Compiling sha1 v0.10.6
|
||||
Compiling atomic-waker v1.1.2
|
||||
Compiling icu_provider v2.1.1
|
||||
Compiling compression-core v0.4.31
|
||||
Compiling prost v0.13.5
|
||||
Compiling sync_wrapper v1.0.2
|
||||
Compiling hyper v1.8.1
|
||||
Compiling num-bigint v0.4.6
|
||||
Compiling tungstenite v0.24.0
|
||||
Compiling toml_datetime v0.6.3
|
||||
Compiling time v0.3.44
|
||||
Compiling compression-codecs v0.4.35
|
||||
Compiling serde_spanned v0.6.9
|
||||
Compiling async-trait v0.1.89
|
||||
Compiling icu_properties v2.1.2
|
||||
Compiling icu_normalizer v2.1.1
|
||||
Compiling prost-types v0.13.5
|
||||
Compiling base64ct v1.8.1
|
||||
Compiling toml_edit v0.20.2
|
||||
Compiling async-compression v0.4.36
|
||||
Compiling tokio-tungstenite v0.24.0
|
||||
Compiling tower v0.5.2
|
||||
Compiling tokio-util v0.7.17
|
||||
Compiling serde_urlencoded v0.7.1
|
||||
Compiling pem v3.0.6
|
||||
Compiling hyper-util v0.1.19
|
||||
Compiling prost-build v0.13.5
|
||||
Compiling axum-core v0.4.5
|
||||
Compiling idna_adapter v1.2.1
|
||||
Compiling idna v1.1.0
|
||||
Compiling password-hash v0.5.0
|
||||
Compiling serde_path_to_error v0.1.20
|
||||
Compiling axum-macros v0.4.2
|
||||
Compiling nu-ansi-term v0.50.3
|
||||
Compiling blake2 v0.10.6
|
||||
Compiling matchit v0.7.3
|
||||
Compiling http-range-header v0.4.2
|
||||
Compiling url v2.5.7
|
||||
Compiling tower-http v0.6.8
|
||||
Compiling simple_asn1 v0.6.3
|
||||
Compiling argon2 v0.5.3
|
||||
Compiling tracing-subscriber v0.3.22
|
||||
Compiling toml v0.8.2
|
||||
Compiling guruconnect-server v0.1.0 (C:\Users\MikeSwanson\Claude\guru-connect\server)
|
||||
Compiling sqlx-core v0.8.6
|
||||
Compiling jsonwebtoken v9.3.1
|
||||
Compiling axum v0.7.9
|
||||
Compiling sqlx-postgres v0.8.6
|
||||
Compiling sqlx-macros-core v0.8.6
|
||||
Compiling sqlx-macros v0.8.6
|
||||
Compiling sqlx v0.8.6
|
||||
warning: unused variable: `config`
|
||||
--> server\src\main.rs:54:9
|
||||
|
|
||||
54 | let config = config::Config::load()?;
|
||||
| ^^^^^^ help: if this is intentional, prefix it with an underscore: `_config`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: struct `ValidateParams` is never constructed
|
||||
--> server\src\main.rs:138:8
|
||||
|
|
||||
138 | struct ValidateParams {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: fields `listen_addr`, `database_url`, `jwt_secret`, and `debug` are never read
|
||||
--> server\src\config.rs:10:9
|
||||
|
|
||||
8 | pub struct Config {
|
||||
| ------ fields in this struct
|
||||
9 | /// Address to listen on (e.g., "0.0.0.0:8080")
|
||||
10 | pub listen_addr: String,
|
||||
| ^^^^^^^^^^^
|
||||
...
|
||||
13 | pub database_url: Option<String>,
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
16 | pub jwt_secret: Option<String>,
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
19 | pub debug: bool,
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `Config` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
|
||||
|
||||
warning: constant `HEARTBEAT_TIMEOUT_SECS` is never used
|
||||
--> server\src\session\mod.rs:22:7
|
||||
|
|
||||
22 | const HEARTBEAT_TIMEOUT_SECS: u64 = 90;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `input_rx` is never read
|
||||
--> server\src\session\mod.rs:58:5
|
||||
|
|
||||
52 | struct SessionData {
|
||||
| ----------- field in this struct
|
||||
...
|
||||
58 | input_rx: Option<InputReceiver>,
|
||||
| ^^^^^^^^
|
||||
|
||||
warning: methods `is_session_timed_out`, `get_timed_out_sessions`, `get_session_by_agent`, and `remove_session` are never used
|
||||
--> server\src\session\mod.rs:185:18
|
||||
|
|
||||
72 | impl SessionManager {
|
||||
| ------------------- methods in this implementation
|
||||
...
|
||||
185 | pub async fn is_session_timed_out(&self, session_id: SessionId) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
195 | pub async fn get_timed_out_sessions(&self) -> Vec<SessionId> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
205 | pub async fn get_session_by_agent(&self, agent_id: &str) -> Option<Session> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
317 | pub async fn remove_session(&self, session_id: SessionId) {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
warning: struct `AuthenticatedUser` is never constructed
|
||||
--> server\src\auth\mod.rs:13:12
|
||||
|
|
||||
13 | pub struct AuthenticatedUser {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: struct `AuthenticatedAgent` is never constructed
|
||||
--> server\src\auth\mod.rs:21:12
|
||||
|
|
||||
21 | pub struct AuthenticatedAgent {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `validate_agent_key` is never used
|
||||
--> server\src\auth\mod.rs:54:8
|
||||
|
|
||||
54 | pub fn validate_agent_key(_api_key: &str) -> Option<AuthenticatedAgent> {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `list_sessions` is never used
|
||||
--> server\src\api\mod.rs:51:14
|
||||
|
|
||||
51 | pub async fn list_sessions(
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_session` is never used
|
||||
--> server\src\api\mod.rs:59:14
|
||||
|
|
||||
59 | pub async fn get_session(
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: struct `Database` is never constructed
|
||||
--> server\src\db\mod.rs:10:12
|
||||
|
|
||||
10 | pub struct Database {
|
||||
| ^^^^^^^^
|
||||
|
||||
warning: associated function `init` is never used
|
||||
--> server\src\db\mod.rs:17:18
|
||||
|
|
||||
15 | impl Database {
|
||||
| ------------- associated function in this implementation
|
||||
16 | /// Initialize database connection
|
||||
17 | pub async fn init(_database_url: &str) -> Result<Self> {
|
||||
| ^^^^
|
||||
|
||||
warning: struct `SessionEvent` is never constructed
|
||||
--> server\src\db\mod.rs:25:12
|
||||
|
|
||||
25 | pub struct SessionEvent {
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: enum `SessionEventType` is never used
|
||||
--> server\src\db\mod.rs:32:10
|
||||
|
|
||||
32 | pub enum SessionEventType {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: method `log_session_event` is never used
|
||||
--> server\src\db\mod.rs:41:18
|
||||
|
|
||||
39 | impl Database {
|
||||
| ------------- method in this implementation
|
||||
40 | /// Log a session event (placeholder)
|
||||
41 | pub async fn log_session_event(&self, _event: SessionEvent) -> Result<()> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `technician_id` is never read
|
||||
--> server\src\support_codes.rs:39:9
|
||||
|
|
||||
38 | pub struct CreateCodeRequest {
|
||||
| ----------------- field in this struct
|
||||
39 | pub technician_id: Option<String>,
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `CreateCodeRequest` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
|
||||
|
||||
warning: methods `get_code`, `is_valid_for_connection`, `list_codes`, and `get_by_session` are never used
|
||||
--> server\src\support_codes.rs:166:18
|
||||
|
|
||||
60 | impl SupportCodeManager {
|
||||
| ----------------------- methods in this implementation
|
||||
...
|
||||
166 | pub async fn get_code(&self, code: &str) -> Option<SupportCode> {
|
||||
| ^^^^^^^^
|
||||
...
|
||||
198 | pub async fn is_valid_for_connection(&self, code: &str) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
204 | pub async fn list_codes(&self) -> Vec<SupportCode> {
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
219 | pub async fn get_by_session(&self, session_id: Uuid) -> Option<SupportCode> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
warning: `guruconnect-server` (bin "guruconnect-server") generated 18 warnings (run `cargo fix --bin "guruconnect-server" -p guruconnect-server` to apply 1 suggestion)
|
||||
Finished `release` profile [optimized] target(s) in 1m 03s
|
||||
@@ -0,0 +1,3 @@
|
||||
Login: SUCCESS
|
||||
User: admin
|
||||
Role: admin
|
||||
@@ -0,0 +1 @@
|
||||
DELETE 1
|
||||
@@ -0,0 +1,630 @@
|
||||
1→# Credentials & Authorization Reference
|
||||
2→**Last Updated:** 2025-12-16
|
||||
3→**Purpose:** Centralized credentials for Claude Code context recovery across all machines
|
||||
4→
|
||||
5→---
|
||||
6→
|
||||
7→## Infrastructure - SSH Access
|
||||
8→
|
||||
9→### Jupiter (Unraid Primary)
|
||||
10→- **Host:** 172.16.3.20
|
||||
11→- **User:** root
|
||||
12→- **Port:** 22
|
||||
13→- **Password:** Th1nk3r^99##
|
||||
14→- **WebUI Password:** Th1nk3r^99##
|
||||
15→- **Role:** Primary container host (Gitea, NPM, GuruRMM, media)
|
||||
16→- **iDRAC IP:** 172.16.1.73 (DHCP)
|
||||
17→- **iDRAC User:** root
|
||||
18→- **iDRAC Password:** Window123!@#-idrac
|
||||
19→- **iDRAC SSH:** Enabled (port 22)
|
||||
20→- **IPMI Key:** All zeros
|
||||
21→
|
||||
22→### Saturn (Unraid Secondary)
|
||||
23→- **Host:** 172.16.3.21
|
||||
24→- **User:** root
|
||||
25→- **Port:** 22
|
||||
26→- **Password:** r3tr0gradE99
|
||||
27→- **Role:** Migration source, being consolidated to Jupiter
|
||||
28→
|
||||
29→### pfSense (Firewall)
|
||||
30→- **Host:** 172.16.0.1
|
||||
31→- **User:** admin
|
||||
32→- **Port:** 2248
|
||||
33→- **Password:** r3tr0gradE99!!
|
||||
34→- **Role:** Firewall, Tailscale gateway
|
||||
35→- **Tailscale IP:** 100.79.69.82 (pfsense-1)
|
||||
36→
|
||||
37→### OwnCloud VM (on Jupiter)
|
||||
38→- **Host:** 172.16.3.22
|
||||
39→- **Hostname:** cloud.acghosting.com
|
||||
40→- **User:** root
|
||||
41→- **Port:** 22
|
||||
42→- **Password:** Paper123!@#-unifi!
|
||||
43→- **OS:** Rocky Linux 9.6
|
||||
44→- **Role:** OwnCloud file sync server
|
||||
45→- **Services:** Apache, MariaDB, PHP-FPM, Redis, Datto RMM agents
|
||||
46→- **Storage:** SMB mount from Jupiter (/mnt/user/OwnCloud)
|
||||
47→- **Note:** Jupiter has SSH key auth configured
|
||||
48→
|
||||
49→### GuruRMM Build Server
|
||||
50→- **Host:** 172.16.3.30
|
||||
51→- **Hostname:** gururmm
|
||||
52→- **User:** guru
|
||||
53→- **Port:** 22
|
||||
54→- **Password:** Gptf*77ttb123!@#-rmm
|
||||
55→- **Sudo Password:** Gptf*77ttb123!@#-rmm (special chars cause issues with sudo -S)
|
||||
56→- **OS:** Ubuntu 22.04
|
||||
57→- **Role:** GuruRMM/GuruConnect dedicated server (API, DB, Dashboard, Downloads, GuruConnect relay)
|
||||
58→- **Services:** nginx, PostgreSQL, gururmm-server, gururmm-agent, guruconnect-server
|
||||
59→- **SSH Key Auth:** ✅ Working from Windows/WSL (ssh guru@172.16.3.30)
|
||||
60→- **Service Restart Method:** Services run as guru user, so `pkill` works without sudo. Deploy pattern:
|
||||
61→ 1. Build: `cargo build --release --target x86_64-unknown-linux-gnu -p <package>`
|
||||
62→ 2. Rename old: `mv target/release/binary target/release/binary.old`
|
||||
63→ 3. Copy new: `cp target/x86_64.../release/binary target/release/binary`
|
||||
64→ 4. Kill old: `pkill -f binary.old` (systemd auto-restarts)
|
||||
65→- **GuruConnect:** Static files in /home/guru/guru-connect/server/static/, binary at /home/guru/guru-connect/target/release/guruconnect-server
|
||||
66→
|
||||
67→---
|
||||
68→
|
||||
69→## Services - Web Applications
|
||||
70→
|
||||
71→### Gitea (Git Server)
|
||||
72→- **URL:** https://git.azcomputerguru.com/
|
||||
73→- **Internal:** http://172.16.3.20:3000
|
||||
74→- **SSH:** ssh://git@172.16.3.20:2222
|
||||
75→- **User:** mike@azcomputerguru.com
|
||||
76→- **Password:** Window123!@#-git
|
||||
77→- **API Token:** 9b1da4b79a38ef782268341d25a4b6880572063f
|
||||
78→
|
||||
79→### NPM (Nginx Proxy Manager)
|
||||
80→- **Admin URL:** http://172.16.3.20:7818
|
||||
81→- **HTTP Port:** 1880
|
||||
82→- **HTTPS Port:** 18443
|
||||
83→- **User:** mike@azcomputerguru.com
|
||||
84→- **Password:** Paper123!@#-unifi
|
||||
85→
|
||||
86→### Cloudflare
|
||||
87→- **API Token (Full DNS):** DRRGkHS33pxAUjQfRDzDeVPtt6wwUU6FwtXqOzNj
|
||||
88→- **API Token (Legacy/Limited):** U1UTbBOWA4a69eWEBiqIbYh0etCGzrpTU4XaKp7w
|
||||
89→- **Permissions:** Zone:Read, Zone:Edit, DNS:Read, DNS:Edit
|
||||
90→- **Used for:** DNS management, WHM plugin, cf-dns CLI
|
||||
91→- **Domain:** azcomputerguru.com
|
||||
92→- **Notes:** New full-access token added 2025-12-19
|
||||
93→
|
||||
94→---
|
||||
95→
|
||||
96→## Projects - GuruRMM
|
||||
97→
|
||||
98→### Dashboard/API Login
|
||||
99→- **Email:** admin@azcomputerguru.com
|
||||
100→- **Password:** GuruRMM2025
|
||||
101→- **Role:** admin
|
||||
102→
|
||||
103→### Database (PostgreSQL)
|
||||
104→- **Host:** gururmm-db container (172.16.3.20)
|
||||
105→- **Database:** gururmm
|
||||
106→- **User:** gururmm
|
||||
107→- **Password:** 43617ebf7eb242e814ca9988cc4df5ad
|
||||
108→
|
||||
109→---
|
||||
110→
|
||||
111→## Projects - GuruConnect
|
||||
112→
|
||||
113→### Database (PostgreSQL on build server)
|
||||
114→- **Host:** localhost (172.16.3.30)
|
||||
115→- **Port:** 5432
|
||||
116→- **Database:** guruconnect
|
||||
117→- **User:** guruconnect
|
||||
118→- **Password:** gc_a7f82d1e4b9c3f60
|
||||
119→- **DATABASE_URL:** `postgres://guruconnect:gc_a7f82d1e4b9c3f60@localhost:5432/guruconnect`
|
||||
120→- **Created:** 2025-12-28
|
||||
121→
|
||||
122→---
|
||||
123→
|
||||
124→## Projects - GuruRMM (continued)
|
||||
125→
|
||||
126→### API Server
|
||||
127→- **External URL:** https://rmm-api.azcomputerguru.com
|
||||
128→- **Internal URL:** http://172.16.3.20:3001
|
||||
129→- **JWT Secret:** ZNzGxghru2XUdBVlaf2G2L1YUBVcl5xH0lr/Gpf/QmE=
|
||||
130→
|
||||
131→### Microsoft Entra ID (SSO)
|
||||
132→- **App Name:** GuruRMM Dashboard
|
||||
133→- **App ID (Client ID):** 18a15f5d-7ab8-46f4-8566-d7b5436b84b6
|
||||
134→- **Object ID:** 34c80aa8-385a-4bea-af85-f8bf67decc8f
|
||||
135→- **Client Secret:** gOz8Q~J.oz7KnUIEpzmHOyJ6GEzYNecGRl-Pbc9w
|
||||
136→- **Secret Expires:** 2026-12-21
|
||||
137→- **Sign-in Audience:** Multi-tenant (any Azure AD org)
|
||||
138→- **Redirect URIs:** https://rmm.azcomputerguru.com/auth/callback, http://localhost:5173/auth/callback
|
||||
139→- **API Permissions:** openid, email, profile
|
||||
140→- **Notes:** Created 2025-12-21 for GuruRMM SSO
|
||||
141→
|
||||
142→### CI/CD (Build Automation)
|
||||
143→- **Webhook URL:** http://172.16.3.30/webhook/build
|
||||
144→- **Webhook Secret:** gururmm-build-secret
|
||||
145→- **Build Script:** /opt/gururmm/build-agents.sh
|
||||
146→- **Build Log:** /var/log/gururmm-build.log
|
||||
147→- **Gitea Webhook ID:** 1
|
||||
148→- **Trigger:** Push to main branch
|
||||
149→- **Builds:** Linux (x86_64) and Windows (x86_64) agents
|
||||
150→- **Deploy Path:** /var/www/gururmm/downloads/
|
||||
151→
|
||||
152→### Build Server SSH Key (for Gitea)
|
||||
153→- **Key Name:** gururmm-build-server
|
||||
154→- **Public Key:**
|
||||
155→```
|
||||
156→ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKSqf2/phEXUK8vd5GhMIDTEGSk0LvYk92sRdNiRrjKi guru@gururmm-build
|
||||
157→```
|
||||
158→- **Added to:** Gitea (azcomputerguru account)
|
||||
159→
|
||||
160→### Clients & Sites
|
||||
161→#### Glaztech Industries (GLAZ)
|
||||
162→- **Client ID:** d857708c-5713-4ee5-a314-679f86d2f9f9
|
||||
163→- **Site:** SLC - Salt Lake City
|
||||
164→- **Site ID:** 290bd2ea-4af5-49c6-8863-c6d58c5a55de
|
||||
165→- **Site Code:** DARK-GROVE-7839
|
||||
166→- **API Key:** grmm_Qw64eawPBjnMdwN5UmDGWoPlqwvjM7lI
|
||||
167→- **Created:** 2025-12-18
|
||||
168→
|
||||
169→---
|
||||
170→
|
||||
171→## Client Sites - WHM/cPanel
|
||||
172→
|
||||
173→### IX Server (ix.azcomputerguru.com)
|
||||
174→- **SSH Host:** ix.azcomputerguru.com
|
||||
175→- **Internal IP:** 172.16.3.10 (VPN required)
|
||||
176→- **SSH User:** root
|
||||
177→- **SSH Password:** Gptf*77ttb!@#!@#
|
||||
178→- **SSH Key:** guru@wsl key added to authorized_keys
|
||||
179→- **Role:** cPanel/WHM server hosting client sites
|
||||
180→
|
||||
181→### WebSvr (websvr.acghosting.com)
|
||||
182→- **Host:** websvr.acghosting.com
|
||||
183→- **SSH User:** root
|
||||
184→- **SSH Password:** r3tr0gradE99#
|
||||
185→- **API Token:** 8ZPYVM6R0RGOHII7EFF533MX6EQ17M7O
|
||||
186→- **Access Level:** Full access
|
||||
187→- **Role:** Legacy cPanel/WHM server (migration source to IX)
|
||||
188→
|
||||
189→### data.grabbanddurando.com
|
||||
190→- **Server:** IX (ix.azcomputerguru.com)
|
||||
191→- **cPanel Account:** grabblaw
|
||||
192→- **Site Path:** /home/grabblaw/public_html/data_grabbanddurando
|
||||
193→- **Site Admin User:** admin
|
||||
194→- **Site Admin Password:** GND-Paper123!@#-datasite
|
||||
195→- **Database:** grabblaw_gdapp_data
|
||||
196→- **DB User:** grabblaw_gddata
|
||||
197→- **DB Password:** GrabbData2025
|
||||
198→- **Config File:** /home/grabblaw/public_html/data_grabbanddurando/connection.php
|
||||
199→- **Backups:** /home/grabblaw/public_html/data_grabbanddurando/backups_mariadb_fix/
|
||||
200→
|
||||
201→### GoDaddy VPS (Legacy)
|
||||
202→- **IP:** 208.109.235.224
|
||||
203→- **Hostname:** 224.235.109.208.host.secureserver.net
|
||||
204→- **Auth:** SSH key
|
||||
205→- **Database:** grabblaw_gdapp
|
||||
206→- **Note:** Old server, data migrated to IX
|
||||
207→
|
||||
208→---
|
||||
209→
|
||||
210→## Seafile (on Jupiter - Migrated 2025-12-27)
|
||||
211→
|
||||
212→### Container
|
||||
213→- **Host:** Jupiter (172.16.3.20)
|
||||
214→- **URL:** https://sync.azcomputerguru.com
|
||||
215→- **Port:** 8082 (internal), proxied via NPM
|
||||
216→- **Containers:** seafile, seafile-mysql, seafile-memcached, seafile-elasticsearch
|
||||
217→- **Docker Compose:** /mnt/user0/SeaFile/DockerCompose/docker-compose.yml
|
||||
218→- **Data Path:** /mnt/user0/SeaFile/seafile-data/
|
||||
219→
|
||||
220→### Seafile Admin
|
||||
221→- **Email:** mike@azcomputerguru.com
|
||||
222→- **Password:** r3tr0gradE99#
|
||||
223→
|
||||
224→### Database (MariaDB)
|
||||
225→- **Container:** seafile-mysql
|
||||
226→- **Image:** mariadb:10.6
|
||||
227→- **Root Password:** db_dev
|
||||
228→- **Seafile User:** seafile
|
||||
229→- **Seafile Password:** 64f2db5e-6831-48ed-a243-d4066fe428f9
|
||||
230→- **Databases:** ccnet_db (users), seafile_db (data), seahub_db (web)
|
||||
231→
|
||||
232→### Elasticsearch
|
||||
233→- **Container:** seafile-elasticsearch
|
||||
234→- **Image:** elasticsearch:7.17.26
|
||||
235→- **Note:** Upgraded from 7.16.2 for kernel 6.12 compatibility
|
||||
236→
|
||||
237→### Microsoft Graph API (Email)
|
||||
238→- **Tenant ID:** ce61461e-81a0-4c84-bb4a-7b354a9a356d
|
||||
239→- **Client ID:** 15b0fafb-ab51-4cc9-adc7-f6334c805c22
|
||||
240→- **Client Secret:** rRN8Q~FPfSL8O24iZthi_LVJTjGOCZG.DnxGHaSk
|
||||
241→- **Sender Email:** noreply@azcomputerguru.com
|
||||
242→- **Used for:** Seafile email notifications via Graph API
|
||||
243→
|
||||
244→### Migration Notes
|
||||
245→- **Migrated from:** Saturn (172.16.3.21) on 2025-12-27
|
||||
246→- **Saturn Status:** Seafile stopped, data intact for rollback (keep 1 week)
|
||||
247→
|
||||
248→---
|
||||
249→
|
||||
250→## NPM Proxy Hosts Reference
|
||||
251→
|
||||
252→| ID | Domain | Backend | SSL Cert |
|
||||
253→|----|--------|---------|----------|
|
||||
254→| 1 | emby.azcomputerguru.com | 172.16.2.99:8096 | npm-1 |
|
||||
255→| 2 | git.azcomputerguru.com | 172.16.3.20:3000 | npm-2 |
|
||||
256→| 4 | plexrequest.azcomputerguru.com | 172.16.3.31:5055 | npm-4 |
|
||||
257→| 5 | rmm-api.azcomputerguru.com | 172.16.3.20:3001 | npm-6 |
|
||||
258→| - | unifi.azcomputerguru.com | 172.16.3.28:8443 | npm-5 |
|
||||
259→| 8 | sync.azcomputerguru.com | 172.16.3.20:8082 | npm-8 |
|
||||
260→
|
||||
261→---
|
||||
262→
|
||||
263→## Tailscale Network
|
||||
264→
|
||||
265→| Tailscale IP | Hostname | Owner | OS |
|
||||
266→|--------------|----------|-------|-----|
|
||||
267→| 100.79.69.82 (pfsense-1) | pfsense | mike@ | freebsd |
|
||||
268→| 100.125.36.6 | acg-m-l5090 | mike@ | windows |
|
||||
269→| 100.92.230.111 | acg-tech-01l | mike@ | windows |
|
||||
270→| 100.96.135.117 | acg-tech-02l | mike@ | windows |
|
||||
271→| 100.113.45.7 | acg-tech03l | howard@ | windows |
|
||||
272→| 100.77.166.22 | desktop-hjfjtep | mike@ | windows |
|
||||
273→| 100.101.145.100 | guru-legion9 | mike@ | windows |
|
||||
274→| 100.119.194.51 | guru-surface8 | howard@ | windows |
|
||||
275→| 100.66.103.110 | magus-desktop | rob@ | windows |
|
||||
276→| 100.66.167.120 | magus-pc | rob@ | windows |
|
||||
277→
|
||||
278→---
|
||||
279→
|
||||
280→## SSH Public Keys
|
||||
281→
|
||||
282→### guru@wsl (Windows/WSL)
|
||||
283→- **User:** guru
|
||||
284→- **Sudo Password:** Window123!@#-wsl
|
||||
285→- **SSH Key:**
|
||||
286→```
|
||||
287→ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAWY+SdqMHJP5JOe3qpWENQZhXJA4tzI2d7ZVNAwA/1u guru@wsl
|
||||
288→```
|
||||
289→
|
||||
290→### azcomputerguru@local (Mac)
|
||||
291→- **User:** azcomputerguru
|
||||
292→- **SSH Key:**
|
||||
293→```
|
||||
294→ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDrGbr4EwvQ4P3ZtyZW3ZKkuDQOMbqyAQUul2+JE4K4S azcomputerguru@local
|
||||
295→```
|
||||
296→
|
||||
297→---
|
||||
298→
|
||||
299→## Quick Reference Commands
|
||||
300→
|
||||
301→### NPM API Auth
|
||||
302→```bash
|
||||
303→curl -s -X POST http://172.16.3.20:7818/api/tokens \
|
||||
304→ -H "Content-Type: application/json" \
|
||||
305→ -d '{"identity":"mike@azcomputerguru.com","secret":"Paper123!@#-unifi"}'
|
||||
306→```
|
||||
307→
|
||||
308→### Gitea API
|
||||
309→```bash
|
||||
310→curl -H "Authorization: token 9b1da4b79a38ef782268341d25a4b6880572063f" \
|
||||
311→ https://git.azcomputerguru.com/api/v1/repos/search
|
||||
312→```
|
||||
313→
|
||||
314→### GuruRMM Health Check
|
||||
315→```bash
|
||||
316→curl http://172.16.3.20:3001/health
|
||||
317→```
|
||||
318→
|
||||
319→---
|
||||
320→
|
||||
321→## MSP Tools
|
||||
322→
|
||||
323→### Syncro (PSA/RMM) - AZ Computer Guru
|
||||
324→- **API Key:** T259810e5c9917386b-52c2aeea7cdb5ff41c6685a73cebbeb3
|
||||
325→- **Subdomain:** computerguru
|
||||
326→- **API Base URL:** https://computerguru.syncromsp.com/api/v1
|
||||
327→- **API Docs:** https://api-docs.syncromsp.com/
|
||||
328→- **Account:** AZ Computer Guru MSP
|
||||
329→- **Notes:** Added 2025-12-18
|
||||
330→
|
||||
331→### Autotask (PSA) - AZ Computer Guru
|
||||
332→- **API Username:** dguyqap2nucge6r@azcomputerguru.com
|
||||
333→- **API Password:** z*6G4fT#oM~8@9Hxy$2Y7K$ma
|
||||
334→- **API Integration Code:** HYTYYZ6LA5HB5XK7IGNA7OAHQLH
|
||||
335→- **Integration Name:** ClaudeAPI
|
||||
336→- **API Zone:** webservices5.autotask.net
|
||||
337→- **API Docs:** https://autotask.net/help/developerhelp/Content/APIs/REST/REST_API_Home.htm
|
||||
338→- **Account:** AZ Computer Guru MSP
|
||||
339→- **Notes:** Added 2025-12-18, new API user "Claude API"
|
||||
340→
|
||||
341→### CIPP (CyberDrain Improved Partner Portal)
|
||||
342→- **URL:** https://cippcanvb.azurewebsites.net
|
||||
343→- **Tenant ID:** ce61461e-81a0-4c84-bb4a-7b354a9a356d
|
||||
344→- **API Client Name:** ClaudeCipp2 (working)
|
||||
345→- **App ID (Client ID):** 420cb849-542d-4374-9cb2-3d8ae0e1835b
|
||||
346→- **Client Secret:** MOn8Q~otmxJPLvmL~_aCVTV8Va4t4~SrYrukGbJT
|
||||
347→- **Scope:** api://420cb849-542d-4374-9cb2-3d8ae0e1835b/.default
|
||||
348→- **CIPP-SAM App ID:** 91b9102d-bafd-43f8-b17a-f99479149b07
|
||||
349→- **IP Range:** 0.0.0.0/0 (all IPs allowed)
|
||||
350→- **Auth Method:** OAuth 2.0 Client Credentials
|
||||
351→- **Notes:** Updated 2025-12-23, working API client
|
||||
352→
|
||||
353→#### CIPP API Usage (Bash)
|
||||
354→```bash
|
||||
355→# Get token
|
||||
356→ACCESS_TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/ce61461e-81a0-4c84-bb4a-7b354a9a356d/oauth2/v2.0/token" \
|
||||
357→ -d "client_id=420cb849-542d-4374-9cb2-3d8ae0e1835b" \
|
||||
358→ -d "client_secret=MOn8Q~otmxJPLvmL~_aCVTV8Va4t4~SrYrukGbJT" \
|
||||
359→ -d "scope=api://420cb849-542d-4374-9cb2-3d8ae0e1835b/.default" \
|
||||
360→ -d "grant_type=client_credentials" | python3 -c "import sys, json; print(json.load(sys.stdin).get('access_token', ''))")
|
||||
361→
|
||||
362→# Query endpoints (use tenant domain or tenant ID as TenantFilter)
|
||||
363→curl -s "https://cippcanvb.azurewebsites.net/api/ListLicenses?TenantFilter=sonorangreenllc.com" \
|
||||
364→ -H "Authorization: Bearer ${ACCESS_TOKEN}"
|
||||
365→
|
||||
366→# Other useful endpoints:
|
||||
367→# ListTenants?AllTenants=true - List all managed tenants
|
||||
368→# ListUsers?TenantFilter={tenant} - List users
|
||||
369→# ListMailboxRules?TenantFilter={tenant} - Check mailbox rules
|
||||
370→# BECCheck?TenantFilter={tenant}&UserID={userid} - BEC investigation
|
||||
371→```
|
||||
372→
|
||||
373→#### Old API Client (403 errors - do not use)
|
||||
374→- **App ID:** d545a836-7118-44f6-8852-d9dd64fb7bb9
|
||||
375→- **Status:** Authenticated but all endpoints returned 403
|
||||
376→
|
||||
377→### Claude-MSP-Access (Multi-Tenant Graph API)
|
||||
378→- **Tenant ID:** ce61461e-81a0-4c84-bb4a-7b354a9a356d
|
||||
379→- **App ID (Client ID):** fabb3421-8b34-484b-bc17-e46de9703418
|
||||
380→- **Client Secret:** ~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO
|
||||
381→- **Secret Expires:** 2026-12 (24 months)
|
||||
382→- **Sign-in Audience:** Multi-tenant (any Entra ID org)
|
||||
383→- **Purpose:** Direct Graph API access for M365 investigations and remediation
|
||||
384→- **Admin Consent URL:** https://login.microsoftonline.com/common/adminconsent?client_id=fabb3421-8b34-484b-bc17-e46de9703418&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient
|
||||
385→- **Permissions:** User.ReadWrite.All, Directory.ReadWrite.All, Mail.ReadWrite, MailboxSettings.ReadWrite, AuditLog.Read.All, Application.ReadWrite.All, DelegatedPermissionGrant.ReadWrite.All, Group.ReadWrite.All, SecurityEvents.ReadWrite.All, AppRoleAssignment.ReadWrite.All, UserAuthenticationMethod.ReadWrite.All
|
||||
386→- **Created:** 2025-12-29
|
||||
387→
|
||||
388→#### Usage (Python)
|
||||
389→```python
|
||||
390→import requests
|
||||
391→
|
||||
392→tenant_id = "CUSTOMER_TENANT_ID" # or use 'common' after consent
|
||||
393→client_id = "fabb3421-8b34-484b-bc17-e46de9703418"
|
||||
394→client_secret = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
|
||||
395→
|
||||
396→# Get token
|
||||
397→token_resp = requests.post(
|
||||
398→ f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token",
|
||||
399→ data={
|
||||
400→ "client_id": client_id,
|
||||
401→ "client_secret": client_secret,
|
||||
402→ "scope": "https://graph.microsoft.com/.default",
|
||||
403→ "grant_type": "client_credentials"
|
||||
404→ }
|
||||
405→)
|
||||
406→access_token = token_resp.json()["access_token"]
|
||||
407→
|
||||
408→# Query Graph API
|
||||
409→headers = {"Authorization": f"Bearer {access_token}"}
|
||||
410→users = requests.get("https://graph.microsoft.com/v1.0/users", headers=headers)
|
||||
411→```
|
||||
412→
|
||||
413→---
|
||||
414→
|
||||
415→## Client - MVAN Inc
|
||||
416→
|
||||
417→### Microsoft 365 Tenant 1
|
||||
418→- **Tenant:** mvan.onmicrosoft.com
|
||||
419→- **Admin User:** sysadmin@mvaninc.com
|
||||
420→- **Password:** r3tr0gradE99#
|
||||
421→- **Notes:** Global admin, project to merge/trust with T2
|
||||
422→
|
||||
423→---
|
||||
424→
|
||||
425→## Client - BG Builders LLC
|
||||
426→
|
||||
427→### Microsoft 365 Tenant
|
||||
428→- **Tenant:** bgbuildersllc.com
|
||||
429→- **CIPP Name:** sonorangreenllc.com
|
||||
430→- **Tenant ID:** ededa4fb-f6eb-4398-851d-5eb3e11fab27
|
||||
431→- **Admin User:** sysadmin@bgbuildersllc.com
|
||||
432→- **Password:** Window123!@#-bgb
|
||||
433→- **Notes:** Added 2025-12-19
|
||||
434→
|
||||
435→### Security Investigation (2025-12-22)
|
||||
436→- **Compromised User:** Shelly@bgbuildersllc.com (Shelly Dooley)
|
||||
437→- **Symptoms:** Suspicious sent items reported by user
|
||||
438→- **Findings:**
|
||||
439→ - Gmail OAuth app with EAS.AccessAsUser.All (REMOVED)
|
||||
440→ - "P2P Server" app registration backdoor (DELETED by admin)
|
||||
441→ - No malicious mailbox rules or forwarding
|
||||
442→ - Sign-in logs unavailable (no Entra P1 license)
|
||||
443→- **Remediation:**
|
||||
444→ - Password reset: `5ecwyHv6&dP7` (must change on login)
|
||||
445→ - All sessions revoked
|
||||
446→ - Gmail OAuth consent removed
|
||||
447→ - P2P Server backdoor deleted
|
||||
448→- **Status:** RESOLVED
|
||||
449→
|
||||
450→---
|
||||
451→
|
||||
452→## Client - Dataforth
|
||||
453→
|
||||
454→### Network
|
||||
455→- **Subnet:** 192.168.0.0/24
|
||||
456→- **Domain:** INTRANET (intranet.dataforth.com)
|
||||
457→
|
||||
458→### UDM (Unifi Dream Machine)
|
||||
459→- **IP:** 192.168.0.254
|
||||
460→- **SSH User:** root
|
||||
461→- **SSH Password:** Paper123!@#-unifi
|
||||
462→- **Web User:** azcomputerguru
|
||||
463→- **Web Password:** Paper123!@#-unifi
|
||||
464→- **2FA:** Push notification enabled
|
||||
465→- **Notes:** Gateway/firewall, OpenVPN server
|
||||
466→
|
||||
467→### AD1 (Domain Controller)
|
||||
468→- **IP:** 192.168.0.27
|
||||
469→- **Hostname:** AD1.intranet.dataforth.com
|
||||
470→- **User:** INTRANET\sysadmin
|
||||
471→- **Password:** Paper123!@#
|
||||
472→- **Role:** Primary DC, NPS/RADIUS server
|
||||
473→- **NPS Ports:** 1812/1813 (auth/accounting)
|
||||
474→
|
||||
475→### AD2 (Domain Controller)
|
||||
476→- **IP:** 192.168.0.6
|
||||
477→- **Hostname:** AD2.intranet.dataforth.com
|
||||
478→- **User:** INTRANET\sysadmin
|
||||
479→- **Password:** Paper123!@#
|
||||
480→- **Role:** Secondary DC, file server
|
||||
481→
|
||||
482→### NPS RADIUS Configuration
|
||||
483→- **Client Name:** unifi
|
||||
484→- **Client IP:** 192.168.0.254
|
||||
485→- **Shared Secret:** Gptf*77ttb!@#!@#
|
||||
486→- **Policy:** "Unifi" - allows Domain Users
|
||||
487→
|
||||
488→### D2TESTNAS (SMB1 Proxy)
|
||||
489→- **IP:** 192.168.0.9
|
||||
490→- **Web/SSH User:** admin
|
||||
491→- **Web/SSH Password:** Paper123!@#-nas
|
||||
492→- **Role:** DOS machine SMB1 proxy
|
||||
493→- **Notes:** Added 2025-12-14
|
||||
494→
|
||||
495→---
|
||||
496→
|
||||
497→## Client - Valley Wide Plastering
|
||||
498→
|
||||
499→### Network
|
||||
500→- **Subnet:** 172.16.9.0/24
|
||||
501→
|
||||
502→### UDM (UniFi Dream Machine)
|
||||
503→- **IP:** 172.16.9.1
|
||||
504→- **SSH User:** root
|
||||
505→- **SSH Password:** Gptf*77ttb123!@#-vwp
|
||||
506→- **Notes:** Gateway/firewall, VPN server, RADIUS client
|
||||
507→
|
||||
508→### VWP-DC1 (Domain Controller)
|
||||
509→- **IP:** 172.16.9.2
|
||||
510→- **Hostname:** VWP-DC1
|
||||
511→- **User:** sysadmin
|
||||
512→- **Password:** r3tr0gradE99#
|
||||
513→- **Role:** Primary DC, NPS/RADIUS server
|
||||
514→- **Notes:** Added 2025-12-22
|
||||
515→
|
||||
516→### NPS RADIUS Configuration
|
||||
517→- **RADIUS Server:** 172.16.9.2
|
||||
518→- **RADIUS Ports:** 1812 (auth), 1813 (accounting)
|
||||
519→- **Clients:** UDM (172.16.9.1), VWP-Subnet (172.16.9.0/24)
|
||||
520→- **Shared Secret:** Gptf*77ttb123!@#-radius
|
||||
521→- **Policy:** "VPN-Access" - allows all authenticated users (24/7)
|
||||
522→- **Auth Methods:** All (PAP, CHAP, MS-CHAP, MS-CHAPv2, EAP)
|
||||
523→- **User Dial-in:** All VWP_Users set to Allow
|
||||
524→- **AuthAttributeRequired:** Disabled on clients
|
||||
525→- **Tested:** 2025-12-22, user cguerrero authenticated successfully
|
||||
526→
|
||||
527→### Dataforth - Entra App Registration (Claude-Code-M365)
|
||||
528→- **Tenant ID:** 7dfa3ce8-c496-4b51-ab8d-bd3dcd78b584
|
||||
529→- **App ID (Client ID):** 7a8c0b2e-57fb-4d79-9b5a-4b88d21b1f29
|
||||
530→- **Client Secret:** tXo8Q~ZNG9zoBpbK9HwJTkzx.YEigZ9AynoSrca3
|
||||
531→- **Permissions:** Calendars.ReadWrite, Contacts.ReadWrite, User.ReadWrite.All, Mail.ReadWrite, Directory.ReadWrite.All, Group.ReadWrite.All
|
||||
532→- **Created:** 2025-12-22
|
||||
533→- **Use:** Silent Graph API access to Dataforth tenant
|
||||
534→
|
||||
535→---
|
||||
536→
|
||||
537→## Client - CW Concrete LLC
|
||||
538→
|
||||
539→### Microsoft 365 Tenant
|
||||
540→- **Tenant:** cwconcretellc.com
|
||||
541→- **CIPP Name:** cwconcretellc.com
|
||||
542→- **Tenant ID:** dfee2224-93cd-4291-9b09-6c6ce9bb8711
|
||||
543→- **Default Domain:** NETORGFT11452752.onmicrosoft.com
|
||||
544→- **Notes:** De-federated from GoDaddy 2025-12, domain needs re-verification
|
||||
545→
|
||||
546→### Security Investigation (2025-12-22)
|
||||
547→- **Findings:**
|
||||
548→ - Graph Command Line Tools OAuth consent with high privileges (REMOVED)
|
||||
549→ - "test" backdoor app registration with multi-tenant access (DELETED)
|
||||
550→ - Apple Internet Accounts OAuth (left - likely iOS device)
|
||||
551→ - No malicious mailbox rules or forwarding
|
||||
552→- **Remediation:**
|
||||
553→ - All sessions revoked for all 4 users
|
||||
554→ - Backdoor apps removed
|
||||
555→- **Status:** RESOLVED
|
||||
556→
|
||||
557→---
|
||||
558→
|
||||
559→## Client - Khalsa
|
||||
560→
|
||||
561→### Network
|
||||
562→- **Subnet:** 172.16.50.0/24
|
||||
563→
|
||||
564→### UCG (UniFi Cloud Gateway)
|
||||
565→- **IP:** 172.16.50.1
|
||||
566→- **SSH User:** azcomputerguru
|
||||
567→- **SSH Password:** Paper123!@#-camden (reset 2025-12-22)
|
||||
568→- **Notes:** Gateway/firewall, VPN server, SSH key added but not working
|
||||
569→
|
||||
570→### Switch
|
||||
571→- **User:** 8WfY8
|
||||
572→- **Password:** tI3evTNBZMlnngtBc
|
||||
573→
|
||||
574→### Accountant Machine
|
||||
575→- **IP:** 172.16.50.168
|
||||
576→- **User:** accountant
|
||||
577→- **Password:** Paper123!@#-accountant
|
||||
578→- **Notes:** Added 2025-12-22, VPN routing issue
|
||||
579→
|
||||
580→---
|
||||
581→
|
||||
582→## Client - Scileppi Law Firm
|
||||
583→
|
||||
584→### DS214se (Source NAS - being migrated)
|
||||
585→- **IP:** 172.16.1.54
|
||||
586→- **SSH User:** admin
|
||||
587→- **Password:** Th1nk3r^99
|
||||
588→- **Storage:** 1.8TB (1.6TB used)
|
||||
589→- **Data:** User home folders (admin, Andrew Ross, Chris Scileppi, Samantha Nunez, etc.)
|
||||
590→
|
||||
591→### Unraid (Source - Migration)
|
||||
592→- **IP:** 172.16.1.21
|
||||
593→- **SSH User:** root
|
||||
594→- **Password:** Th1nk3r^99
|
||||
595→- **Role:** Data source for migration to RS2212+
|
||||
596→
|
||||
597→### RS2212+ (Destination NAS)
|
||||
598→- **IP:** 172.16.1.59
|
||||
599→- **Hostname:** SL-SERVER
|
||||
600→- **SSH User:** sysadmin
|
||||
601→- **Password:** Gptf*77ttb123!@#-sl-server
|
||||
602→- **SSH Key:** claude-code@localadmin added to authorized_keys
|
||||
603→- **Storage:** 25TB total, 6.9TB used (28%)
|
||||
604→- **Data Share:** /volume1/Data (7.9TB - Active, Closed, Archived, Billing, MOTIONS BANK)
|
||||
605→- **Notes:** Migration and consolidation complete 2025-12-29
|
||||
606→
|
||||
607→### RS2212+ User Accounts (Created 2025-12-29)
|
||||
608→| Username | Full Name | Password | Notes |
|
||||
609→|----------|-----------|----------|-------|
|
||||
610→| chris | Chris Scileppi | Scileppi2025! | Owner |
|
||||
611→| andrew | Andrew Ross | Scileppi2025! | Staff |
|
||||
612→| sylvia | Sylvia | Scileppi2025! | Staff |
|
||||
613→| rose | Rose | Scileppi2025! | Staff |
|
||||
614→| (TBD) | 5th user | - | Name pending |
|
||||
615→
|
||||
616→### Migration/Consolidation Status (COMPLETE)
|
||||
617→- **Completed:** 2025-12-29
|
||||
618→- **Final Structure:**
|
||||
619→ - Active: 2.5TB (merged Unraid + DS214se Open Cases)
|
||||
620→ - Closed: 4.9TB (merged Unraid + DS214se Closed Cases)
|
||||
621→ - Archived: 451GB
|
||||
622→ - MOTIONS BANK: 21MB
|
||||
623→ - Billing: 17MB
|
||||
624→- **Recycle Bin:** Emptied (recovered 413GB)
|
||||
625→- **Permissions:** Group "users" with 775 on /volume1/Data
|
||||
626→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1,4 @@
|
||||
remote: . Processing 1 references
|
||||
remote: Processed 1 references in total
|
||||
To https://git.azcomputerguru.com/azcomputerguru/guru-connect.git
|
||||
3c2e070..1cc94c6 main -> main
|
||||
@@ -0,0 +1,2 @@
|
||||
[main 1cc94c6] Add is_online/is_persistent for persistent agent sessions
|
||||
4 files changed, 81 insertions(+), 8 deletions(-)
|
||||
@@ -0,0 +1,7 @@
|
||||
1→[2m2025-12-30T04:04:03.724128Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m INITIAL ADMIN USER CREATED
|
||||
2→[2m2025-12-30T04:04:03.724145Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m Password: uwYmX6aygmJ@ZGqv
|
||||
3→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
Exit code 127
|
||||
bash: line 1: cargo: command not found
|
||||
@@ -0,0 +1,216 @@
|
||||
1→//! REST API endpoints
|
||||
2→
|
||||
3→pub mod auth;
|
||||
4→pub mod users;
|
||||
5→
|
||||
6→use axum::{
|
||||
7→ extract::{Path, State, Query},
|
||||
8→ Json,
|
||||
9→};
|
||||
10→use serde::{Deserialize, Serialize};
|
||||
11→use uuid::Uuid;
|
||||
12→
|
||||
13→use crate::session::SessionManager;
|
||||
14→use crate::db;
|
||||
15→
|
||||
16→/// Viewer info returned by API
|
||||
17→#[derive(Debug, Serialize)]
|
||||
18→pub struct ViewerInfoApi {
|
||||
19→ pub id: String,
|
||||
20→ pub name: String,
|
||||
21→ pub connected_at: String,
|
||||
22→}
|
||||
23→
|
||||
24→impl From<crate::session::ViewerInfo> for ViewerInfoApi {
|
||||
25→ fn from(v: crate::session::ViewerInfo) -> Self {
|
||||
26→ Self {
|
||||
27→ id: v.id,
|
||||
28→ name: v.name,
|
||||
29→ connected_at: v.connected_at.to_rfc3339(),
|
||||
30→ }
|
||||
31→ }
|
||||
32→}
|
||||
33→
|
||||
34→/// Session info returned by API
|
||||
35→#[derive(Debug, Serialize)]
|
||||
36→pub struct SessionInfo {
|
||||
37→ pub id: String,
|
||||
38→ pub agent_id: String,
|
||||
39→ pub agent_name: String,
|
||||
40→ pub started_at: String,
|
||||
41→ pub viewer_count: usize,
|
||||
42→ pub viewers: Vec<ViewerInfoApi>,
|
||||
43→ pub is_streaming: bool,
|
||||
44→ pub is_online: bool,
|
||||
45→ pub is_persistent: bool,
|
||||
46→ pub last_heartbeat: String,
|
||||
47→ pub os_version: Option<String>,
|
||||
48→ pub is_elevated: bool,
|
||||
49→ pub uptime_secs: i64,
|
||||
50→ pub display_count: i32,
|
||||
51→}
|
||||
52→
|
||||
53→impl From<crate::session::Session> for SessionInfo {
|
||||
54→ fn from(s: crate::session::Session) -> Self {
|
||||
55→ Self {
|
||||
56→ id: s.id.to_string(),
|
||||
57→ agent_id: s.agent_id,
|
||||
58→ agent_name: s.agent_name,
|
||||
59→ started_at: s.started_at.to_rfc3339(),
|
||||
60→ viewer_count: s.viewer_count,
|
||||
61→ viewers: s.viewers.into_iter().map(ViewerInfoApi::from).collect(),
|
||||
62→ is_streaming: s.is_streaming,
|
||||
63→ is_online: s.is_online,
|
||||
64→ is_persistent: s.is_persistent,
|
||||
65→ last_heartbeat: s.last_heartbeat.to_rfc3339(),
|
||||
66→ os_version: s.os_version,
|
||||
67→ is_elevated: s.is_elevated,
|
||||
68→ uptime_secs: s.uptime_secs,
|
||||
69→ display_count: s.display_count,
|
||||
70→ }
|
||||
71→ }
|
||||
72→}
|
||||
73→
|
||||
74→/// List all active sessions
|
||||
75→pub async fn list_sessions(
|
||||
76→ State(sessions): State<SessionManager>,
|
||||
77→) -> Json<Vec<SessionInfo>> {
|
||||
78→ let sessions = sessions.list_sessions().await;
|
||||
79→ Json(sessions.into_iter().map(SessionInfo::from).collect())
|
||||
80→}
|
||||
81→
|
||||
82→/// Get a specific session by ID
|
||||
83→pub async fn get_session(
|
||||
84→ State(sessions): State<SessionManager>,
|
||||
85→ Path(id): Path<String>,
|
||||
86→) -> Result<Json<SessionInfo>, (axum::http::StatusCode, &'static str)> {
|
||||
87→ let session_id = Uuid::parse_str(&id)
|
||||
88→ .map_err(|_| (axum::http::StatusCode::BAD_REQUEST, "Invalid session ID"))?;
|
||||
89→
|
||||
90→ let session = sessions.get_session(session_id).await
|
||||
91→ .ok_or((axum::http::StatusCode::NOT_FOUND, "Session not found"))?;
|
||||
92→
|
||||
93→ Ok(Json(SessionInfo::from(session)))
|
||||
94→}
|
||||
95→
|
||||
96→// ============================================================================
|
||||
97→// Machine API Types
|
||||
98→// ============================================================================
|
||||
99→
|
||||
100→/// Machine info returned by API
|
||||
101→#[derive(Debug, Serialize)]
|
||||
102→pub struct MachineInfo {
|
||||
103→ pub id: String,
|
||||
104→ pub agent_id: String,
|
||||
105→ pub hostname: String,
|
||||
106→ pub os_version: Option<String>,
|
||||
107→ pub is_elevated: bool,
|
||||
108→ pub is_persistent: bool,
|
||||
109→ pub first_seen: String,
|
||||
110→ pub last_seen: String,
|
||||
111→ pub status: String,
|
||||
112→}
|
||||
113→
|
||||
114→impl From<db::machines::Machine> for MachineInfo {
|
||||
115→ fn from(m: db::machines::Machine) -> Self {
|
||||
116→ Self {
|
||||
117→ id: m.id.to_string(),
|
||||
118→ agent_id: m.agent_id,
|
||||
119→ hostname: m.hostname,
|
||||
120→ os_version: m.os_version,
|
||||
121→ is_elevated: m.is_elevated,
|
||||
122→ is_persistent: m.is_persistent,
|
||||
123→ first_seen: m.first_seen.to_rfc3339(),
|
||||
124→ last_seen: m.last_seen.to_rfc3339(),
|
||||
125→ status: m.status,
|
||||
126→ }
|
||||
127→ }
|
||||
128→}
|
||||
129→
|
||||
130→/// Session record for history
|
||||
131→#[derive(Debug, Serialize)]
|
||||
132→pub struct SessionRecord {
|
||||
133→ pub id: String,
|
||||
134→ pub started_at: String,
|
||||
135→ pub ended_at: Option<String>,
|
||||
136→ pub duration_secs: Option<i32>,
|
||||
137→ pub is_support_session: bool,
|
||||
138→ pub support_code: Option<String>,
|
||||
139→ pub status: String,
|
||||
140→}
|
||||
141→
|
||||
142→impl From<db::sessions::DbSession> for SessionRecord {
|
||||
143→ fn from(s: db::sessions::DbSession) -> Self {
|
||||
144→ Self {
|
||||
145→ id: s.id.to_string(),
|
||||
146→ started_at: s.started_at.to_rfc3339(),
|
||||
147→ ended_at: s.ended_at.map(|t| t.to_rfc3339()),
|
||||
148→ duration_secs: s.duration_secs,
|
||||
149→ is_support_session: s.is_support_session,
|
||||
150→ support_code: s.support_code,
|
||||
151→ status: s.status,
|
||||
152→ }
|
||||
153→ }
|
||||
154→}
|
||||
155→
|
||||
156→/// Event record for history
|
||||
157→#[derive(Debug, Serialize)]
|
||||
158→pub struct EventRecord {
|
||||
159→ pub id: i64,
|
||||
160→ pub session_id: String,
|
||||
161→ pub event_type: String,
|
||||
162→ pub timestamp: String,
|
||||
163→ pub viewer_id: Option<String>,
|
||||
164→ pub viewer_name: Option<String>,
|
||||
165→ pub details: Option<serde_json::Value>,
|
||||
166→ pub ip_address: Option<String>,
|
||||
167→}
|
||||
168→
|
||||
169→impl From<db::events::SessionEvent> for EventRecord {
|
||||
170→ fn from(e: db::events::SessionEvent) -> Self {
|
||||
171→ Self {
|
||||
172→ id: e.id,
|
||||
173→ session_id: e.session_id.to_string(),
|
||||
174→ event_type: e.event_type,
|
||||
175→ timestamp: e.timestamp.to_rfc3339(),
|
||||
176→ viewer_id: e.viewer_id,
|
||||
177→ viewer_name: e.viewer_name,
|
||||
178→ details: e.details,
|
||||
179→ ip_address: e.ip_address,
|
||||
180→ }
|
||||
181→ }
|
||||
182→}
|
||||
183→
|
||||
184→/// Full machine history (for export)
|
||||
185→#[derive(Debug, Serialize)]
|
||||
186→pub struct MachineHistory {
|
||||
187→ pub machine: MachineInfo,
|
||||
188→ pub sessions: Vec<SessionRecord>,
|
||||
189→ pub events: Vec<EventRecord>,
|
||||
190→ pub exported_at: String,
|
||||
191→}
|
||||
192→
|
||||
193→/// Query parameters for machine deletion
|
||||
194→#[derive(Debug, Deserialize)]
|
||||
195→pub struct DeleteMachineParams {
|
||||
196→ /// If true, send uninstall command to agent (if online)
|
||||
197→ #[serde(default)]
|
||||
198→ pub uninstall: bool,
|
||||
199→ /// If true, include history in response before deletion
|
||||
200→ #[serde(default)]
|
||||
201→ pub export: bool,
|
||||
202→}
|
||||
203→
|
||||
204→/// Response for machine deletion
|
||||
205→#[derive(Debug, Serialize)]
|
||||
206→pub struct DeleteMachineResponse {
|
||||
207→ pub success: bool,
|
||||
208→ pub message: String,
|
||||
209→ pub uninstall_sent: bool,
|
||||
210→ pub history: Option<MachineHistory>,
|
||||
211→}
|
||||
212→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1 @@
|
||||
C:\Users\MikeSwanson\Claude\session-logs\2025-12-29-session.md
|
||||
@@ -0,0 +1,17 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\src\main.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
15→ include!(concat!(env!("OUT_DIR"), "/guruconnect.rs"));
|
||||
16→}
|
||||
17→
|
||||
18→use anyhow::Result;
|
||||
19→use axum::{
|
||||
20→ Router,
|
||||
21→ routing::{get, post, put, delete},
|
||||
22→ extract::{Path, State, Json, Query, Request},
|
||||
23→ response::{Html, IntoResponse},
|
||||
24→ http::StatusCode,
|
||||
25→ middleware::{self, Next},
|
||||
26→};
|
||||
27→use std::net::SocketAddr;
|
||||
28→use std::sync::Arc;
|
||||
29→use tower_http::cors::{Any, CorsLayer};
|
||||
30→use tower_http::trace::TraceLayer;
|
||||
@@ -0,0 +1,10 @@
|
||||
[detached HEAD 3fc4e1f] Add user management system with JWT authentication
|
||||
13 files changed, 2355 insertions(+), 71 deletions(-)
|
||||
create mode 100644 server/migrations/002_user_management.sql
|
||||
create mode 100644 server/src/api/auth.rs
|
||||
create mode 100644 server/src/api/users.rs
|
||||
create mode 100644 server/src/auth/jwt.rs
|
||||
create mode 100644 server/src/auth/password.rs
|
||||
create mode 100644 server/src/db/users.rs
|
||||
create mode 100644 server/static/users.html
|
||||
Successfully rebased and updated refs/heads/main.
|
||||
@@ -0,0 +1 @@
|
||||
Cloning into 'guru-connect'...
|
||||
@@ -0,0 +1 @@
|
||||
Command running in background with ID: b70d20e. Output is being written to: C:\Users\MIKESW~1\AppData\Local\Temp\claude\C--Users-MikeSwanson-Claude\tasks\b70d20e.output
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 127
|
||||
bash: line 1: cargo: command not found
|
||||
@@ -0,0 +1 @@
|
||||
Cloning into 'guru-connect'...
|
||||
@@ -0,0 +1,32 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\static\dashboard.html has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
697→ container.innerHTML = '<div class="empty-state"><h3>Select a machine</h3><p>Click a machine to view details</p></div>';
|
||||
698→ return;
|
||||
699→ }
|
||||
700→
|
||||
701→ const m = selectedMachine;
|
||||
702→ const started = new Date(m.started_at).toLocaleString();
|
||||
703→ const statusColor = m.is_online ? 'hsl(142, 76%, 50%)' : 'hsl(0, 0%, 50%)';
|
||||
704→ const statusText = m.is_online ? 'Online' : 'Offline';
|
||||
705→ const connectDisabled = m.is_online ? '' : 'disabled';
|
||||
706→ const connectTitle = m.is_online ? '' : 'title="Agent is offline"';
|
||||
707→
|
||||
708→ container.innerHTML =
|
||||
709→ '<div class="detail-section">' +
|
||||
710→ '<div class="detail-section-title">Machine Info</div>' +
|
||||
711→ '<div class="detail-row"><span class="detail-label">Status</span><span class="detail-value" style="color: ' + statusColor + ';">' + statusText + '</span></div>' +
|
||||
712→ '<div class="detail-row"><span class="detail-label">Agent ID</span><span class="detail-value">' + m.agent_id.slice(0,8) + '...</span></div>' +
|
||||
713→ '<div class="detail-row"><span class="detail-label">Session ID</span><span class="detail-value">' + m.id.slice(0,8) + '...</span></div>' +
|
||||
714→ '<div class="detail-row"><span class="detail-label">Connected</span><span class="detail-value">' + started + '</span></div>' +
|
||||
715→ '<div class="detail-row"><span class="detail-label">Viewers</span><span class="detail-value">' + m.viewer_count + '</span></div>' +
|
||||
716→ '</div>' +
|
||||
717→ '<div class="detail-section">' +
|
||||
718→ '<div class="detail-section-title">Actions</div>' +
|
||||
719→ '<button class="btn btn-primary" style="width: 100%; margin-bottom: 8px;" onclick="connectToMachine(\'' + m.id + '\')" ' + connectDisabled + ' ' + connectTitle + '>Connect</button>' +
|
||||
720→ '<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" onclick="openChat(\'' + m.id + '\', \'' + (m.agent_name || 'Client').replace(/'/g, "\\'") + '\')" ' + connectDisabled + '>Chat</button>' +
|
||||
721→ '<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" disabled>Transfer Files</button>' +
|
||||
722→ '<button class="btn btn-outline" style="width: 100%; color: hsl(0, 62.8%, 50%);" onclick="disconnectMachine(\'' + m.id + '\', \'' + (m.agent_name || m.agent_id).replace(/'/g, "\\'") + '\')">Disconnect</button>' +
|
||||
723→ '</div>';
|
||||
724→ }
|
||||
725→
|
||||
726→ function connectToMachine(sessionId) {
|
||||
727→ // Open viewer in new window
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 127
|
||||
bash: line 1: cargo: command not found
|
||||
@@ -0,0 +1,28 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\src\api\mod.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
26→ pub uptime_secs: i64,
|
||||
27→ pub display_count: i32,
|
||||
28→}
|
||||
29→
|
||||
30→impl From<crate::session::Session> for SessionInfo {
|
||||
31→ fn from(s: crate::session::Session) -> Self {
|
||||
32→ Self {
|
||||
33→ id: s.id.to_string(),
|
||||
34→ agent_id: s.agent_id,
|
||||
35→ agent_name: s.agent_name,
|
||||
36→ started_at: s.started_at.to_rfc3339(),
|
||||
37→ viewer_count: s.viewer_count,
|
||||
38→ is_streaming: s.is_streaming,
|
||||
39→ is_online: s.is_online,
|
||||
40→ is_persistent: s.is_persistent,
|
||||
41→ last_heartbeat: s.last_heartbeat.to_rfc3339(),
|
||||
42→ os_version: s.os_version,
|
||||
43→ is_elevated: s.is_elevated,
|
||||
44→ uptime_secs: s.uptime_secs,
|
||||
45→ display_count: s.display_count,
|
||||
46→ }
|
||||
47→ }
|
||||
48→}
|
||||
49→
|
||||
50→/// List all active sessions
|
||||
51→pub async fn list_sessions(
|
||||
52→ State(sessions): State<SessionManager>,
|
||||
@@ -0,0 +1,328 @@
|
||||
1→//! WebSocket relay handlers
|
||||
2→//!
|
||||
3→//! Handles WebSocket connections from agents and viewers,
|
||||
4→//! relaying video frames and input events between them.
|
||||
5→
|
||||
6→use axum::{
|
||||
7→ extract::{
|
||||
8→ ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
9→ Query, State,
|
||||
10→ },
|
||||
11→ response::IntoResponse,
|
||||
12→};
|
||||
13→use futures_util::{SinkExt, StreamExt};
|
||||
14→use prost::Message as ProstMessage;
|
||||
15→use serde::Deserialize;
|
||||
16→use tracing::{error, info, warn};
|
||||
17→use uuid::Uuid;
|
||||
18→
|
||||
19→use crate::proto;
|
||||
20→use crate::session::SessionManager;
|
||||
21→use crate::AppState;
|
||||
22→
|
||||
23→#[derive(Debug, Deserialize)]
|
||||
24→pub struct AgentParams {
|
||||
25→ agent_id: String,
|
||||
26→ #[serde(default)]
|
||||
27→ agent_name: Option<String>,
|
||||
28→ #[serde(default)]
|
||||
29→ support_code: Option<String>,
|
||||
30→ #[serde(default)]
|
||||
31→ hostname: Option<String>,
|
||||
32→}
|
||||
33→
|
||||
34→#[derive(Debug, Deserialize)]
|
||||
35→pub struct ViewerParams {
|
||||
36→ session_id: String,
|
||||
37→}
|
||||
38→
|
||||
39→/// WebSocket handler for agent connections
|
||||
40→pub async fn agent_ws_handler(
|
||||
41→ ws: WebSocketUpgrade,
|
||||
42→ State(state): State<AppState>,
|
||||
43→ Query(params): Query<AgentParams>,
|
||||
44→) -> impl IntoResponse {
|
||||
45→ let agent_id = params.agent_id;
|
||||
46→ let agent_name = params.hostname.or(params.agent_name).unwrap_or_else(|| agent_id.clone());
|
||||
47→ let support_code = params.support_code;
|
||||
48→ let sessions = state.sessions.clone();
|
||||
49→ let support_codes = state.support_codes.clone();
|
||||
50→
|
||||
51→ ws.on_upgrade(move |socket| handle_agent_connection(socket, sessions, support_codes, agent_id, agent_name, support_code))
|
||||
52→}
|
||||
53→
|
||||
54→/// WebSocket handler for viewer connections
|
||||
55→pub async fn viewer_ws_handler(
|
||||
56→ ws: WebSocketUpgrade,
|
||||
57→ State(state): State<AppState>,
|
||||
58→ Query(params): Query<ViewerParams>,
|
||||
59→) -> impl IntoResponse {
|
||||
60→ let session_id = params.session_id;
|
||||
61→ let sessions = state.sessions.clone();
|
||||
62→
|
||||
63→ ws.on_upgrade(move |socket| handle_viewer_connection(socket, sessions, session_id))
|
||||
64→}
|
||||
65→
|
||||
66→/// Handle an agent WebSocket connection
|
||||
67→async fn handle_agent_connection(
|
||||
68→ socket: WebSocket,
|
||||
69→ sessions: SessionManager,
|
||||
70→ support_codes: crate::support_codes::SupportCodeManager,
|
||||
71→ agent_id: String,
|
||||
72→ agent_name: String,
|
||||
73→ support_code: Option<String>,
|
||||
74→) {
|
||||
75→ info!("Agent connected: {} ({})", agent_name, agent_id);
|
||||
76→
|
||||
77→ let (mut ws_sender, mut ws_receiver) = socket.split();
|
||||
78→
|
||||
79→ // If a support code was provided, check if it's valid
|
||||
80→ if let Some(ref code) = support_code {
|
||||
81→ // Check if the code is cancelled or invalid
|
||||
82→ if support_codes.is_cancelled(code).await {
|
||||
83→ warn!("Agent tried to connect with cancelled code: {}", code);
|
||||
84→ // Send disconnect message to agent
|
||||
85→ let disconnect_msg = proto::Message {
|
||||
86→ payload: Some(proto::message::Payload::Disconnect(proto::Disconnect {
|
||||
87→ reason: "Support session was cancelled by technician".to_string(),
|
||||
88→ })),
|
||||
89→ };
|
||||
90→ let mut buf = Vec::new();
|
||||
91→ if prost::Message::encode(&disconnect_msg, &mut buf).is_ok() {
|
||||
92→ let _ = ws_sender.send(Message::Binary(buf.into())).await;
|
||||
93→ }
|
||||
94→ let _ = ws_sender.close().await;
|
||||
95→ return;
|
||||
96→ }
|
||||
97→ }
|
||||
98→
|
||||
99→ // Register the agent and get channels
|
||||
100→ let (session_id, frame_tx, mut input_rx) = sessions.register_agent(agent_id.clone(), agent_name.clone()).await;
|
||||
101→
|
||||
102→ info!("Session created: {} (agent in idle mode)", session_id);
|
||||
103→
|
||||
104→ // If a support code was provided, mark it as connected
|
||||
105→ if let Some(ref code) = support_code {
|
||||
106→ info!("Linking support code {} to session {}", code, session_id);
|
||||
107→ support_codes.mark_connected(code, Some(agent_name.clone()), Some(agent_id.clone())).await;
|
||||
108→ support_codes.link_session(code, session_id).await;
|
||||
109→ }
|
||||
110→
|
||||
111→ // Use Arc<Mutex> for sender so we can use it from multiple places
|
||||
112→ let ws_sender = std::sync::Arc::new(tokio::sync::Mutex::new(ws_sender));
|
||||
113→ let ws_sender_input = ws_sender.clone();
|
||||
114→ let ws_sender_cancel = ws_sender.clone();
|
||||
115→
|
||||
116→ // Task to forward input events from viewers to agent
|
||||
117→ let input_forward = tokio::spawn(async move {
|
||||
118→ while let Some(input_data) = input_rx.recv().await {
|
||||
119→ let mut sender = ws_sender_input.lock().await;
|
||||
120→ if sender.send(Message::Binary(input_data.into())).await.is_err() {
|
||||
121→ break;
|
||||
122→ }
|
||||
123→ }
|
||||
124→ });
|
||||
125→
|
||||
126→ let sessions_cleanup = sessions.clone();
|
||||
127→ let sessions_status = sessions.clone();
|
||||
128→ let support_codes_cleanup = support_codes.clone();
|
||||
129→ let support_code_cleanup = support_code.clone();
|
||||
130→ let support_code_check = support_code.clone();
|
||||
131→ let support_codes_check = support_codes.clone();
|
||||
132→
|
||||
133→ // Task to check for cancellation every 2 seconds
|
||||
134→ let cancel_check = tokio::spawn(async move {
|
||||
135→ let mut interval = tokio::time::interval(std::time::Duration::from_secs(2));
|
||||
136→ loop {
|
||||
137→ interval.tick().await;
|
||||
138→ if let Some(ref code) = support_code_check {
|
||||
139→ if support_codes_check.is_cancelled(code).await {
|
||||
140→ info!("Support code {} was cancelled, disconnecting agent", code);
|
||||
141→ // Send disconnect message
|
||||
142→ let disconnect_msg = proto::Message {
|
||||
143→ payload: Some(proto::message::Payload::Disconnect(proto::Disconnect {
|
||||
144→ reason: "Support session was cancelled by technician".to_string(),
|
||||
145→ })),
|
||||
146→ };
|
||||
147→ let mut buf = Vec::new();
|
||||
148→ if prost::Message::encode(&disconnect_msg, &mut buf).is_ok() {
|
||||
149→ let mut sender = ws_sender_cancel.lock().await;
|
||||
150→ let _ = sender.send(Message::Binary(buf.into())).await;
|
||||
151→ let _ = sender.close().await;
|
||||
152→ }
|
||||
153→ break;
|
||||
154→ }
|
||||
155→ }
|
||||
156→ }
|
||||
157→ });
|
||||
158→
|
||||
159→ // Main loop: receive messages from agent
|
||||
160→ while let Some(msg) = ws_receiver.next().await {
|
||||
161→ match msg {
|
||||
162→ Ok(Message::Binary(data)) => {
|
||||
163→ // Try to decode as protobuf message
|
||||
164→ match proto::Message::decode(data.as_ref()) {
|
||||
165→ Ok(proto_msg) => {
|
||||
166→ match &proto_msg.payload {
|
||||
167→ Some(proto::message::Payload::VideoFrame(_)) => {
|
||||
168→ // Broadcast frame to all viewers (only sent when streaming)
|
||||
169→ let _ = frame_tx.send(data.to_vec());
|
||||
170→ }
|
||||
171→ Some(proto::message::Payload::ChatMessage(chat)) => {
|
||||
172→ // Broadcast chat message to all viewers
|
||||
173→ info!("Chat from client: {}", chat.content);
|
||||
174→ let _ = frame_tx.send(data.to_vec());
|
||||
175→ }
|
||||
176→ Some(proto::message::Payload::AgentStatus(status)) => {
|
||||
177→ // Update session with agent status
|
||||
178→ sessions_status.update_agent_status(
|
||||
179→ session_id,
|
||||
180→ Some(status.os_version.clone()),
|
||||
181→ status.is_elevated,
|
||||
182→ status.uptime_secs,
|
||||
183→ status.display_count,
|
||||
184→ status.is_streaming,
|
||||
185→ ).await;
|
||||
186→ info!("Agent status update: {} - streaming={}, uptime={}s",
|
||||
187→ status.hostname, status.is_streaming, status.uptime_secs);
|
||||
188→ }
|
||||
189→ Some(proto::message::Payload::Heartbeat(_)) => {
|
||||
190→ // Update heartbeat timestamp
|
||||
191→ sessions_status.update_heartbeat(session_id).await;
|
||||
192→ }
|
||||
193→ Some(proto::message::Payload::HeartbeatAck(_)) => {
|
||||
194→ // Agent acknowledged our heartbeat
|
||||
195→ sessions_status.update_heartbeat(session_id).await;
|
||||
196→ }
|
||||
197→ _ => {}
|
||||
198→ }
|
||||
199→ }
|
||||
200→ Err(e) => {
|
||||
201→ warn!("Failed to decode agent message: {}", e);
|
||||
202→ }
|
||||
203→ }
|
||||
204→ }
|
||||
205→ Ok(Message::Close(_)) => {
|
||||
206→ info!("Agent disconnected: {}", agent_id);
|
||||
207→ break;
|
||||
208→ }
|
||||
209→ Ok(Message::Ping(data)) => {
|
||||
210→ // Pong is handled automatically by axum
|
||||
211→ let _ = data;
|
||||
212→ }
|
||||
213→ Ok(_) => {}
|
||||
214→ Err(e) => {
|
||||
215→ error!("WebSocket error from agent {}: {}", agent_id, e);
|
||||
216→ break;
|
||||
217→ }
|
||||
218→ }
|
||||
219→ }
|
||||
220→
|
||||
221→ // Cleanup
|
||||
222→ input_forward.abort();
|
||||
223→ cancel_check.abort();
|
||||
224→ sessions_cleanup.remove_session(session_id).await;
|
||||
225→
|
||||
226→ // Mark support code as completed if one was used (unless cancelled)
|
||||
227→ if let Some(ref code) = support_code_cleanup {
|
||||
228→ if !support_codes_cleanup.is_cancelled(code).await {
|
||||
229→ support_codes_cleanup.mark_completed(code).await;
|
||||
230→ info!("Support code {} marked as completed", code);
|
||||
231→ }
|
||||
232→ }
|
||||
233→
|
||||
234→ info!("Session {} ended", session_id);
|
||||
235→}
|
||||
236→
|
||||
237→/// Handle a viewer WebSocket connection
|
||||
238→async fn handle_viewer_connection(
|
||||
239→ socket: WebSocket,
|
||||
240→ sessions: SessionManager,
|
||||
241→ session_id_str: String,
|
||||
242→) {
|
||||
243→ // Parse session ID
|
||||
244→ let session_id = match uuid::Uuid::parse_str(&session_id_str) {
|
||||
245→ Ok(id) => id,
|
||||
246→ Err(_) => {
|
||||
247→ warn!("Invalid session ID: {}", session_id_str);
|
||||
248→ return;
|
||||
249→ }
|
||||
250→ };
|
||||
251→
|
||||
252→ // Generate unique viewer ID
|
||||
253→ let viewer_id = Uuid::new_v4().to_string();
|
||||
254→
|
||||
255→ // Join the session (this sends StartStream to agent if first viewer)
|
||||
256→ let (mut frame_rx, input_tx) = match sessions.join_session(session_id, viewer_id.clone()).await {
|
||||
257→ Some(channels) => channels,
|
||||
258→ None => {
|
||||
259→ warn!("Session not found: {}", session_id);
|
||||
260→ return;
|
||||
261→ }
|
||||
262→ };
|
||||
263→
|
||||
264→ info!("Viewer {} joined session: {}", viewer_id, session_id);
|
||||
265→
|
||||
266→ let (mut ws_sender, mut ws_receiver) = socket.split();
|
||||
267→
|
||||
268→ // Task to forward frames from agent to this viewer
|
||||
269→ let frame_forward = tokio::spawn(async move {
|
||||
270→ while let Ok(frame_data) = frame_rx.recv().await {
|
||||
271→ if ws_sender.send(Message::Binary(frame_data.into())).await.is_err() {
|
||||
272→ break;
|
||||
273→ }
|
||||
274→ }
|
||||
275→ });
|
||||
276→
|
||||
277→ let sessions_cleanup = sessions.clone();
|
||||
278→ let viewer_id_cleanup = viewer_id.clone();
|
||||
279→
|
||||
280→ // Main loop: receive input from viewer and forward to agent
|
||||
281→ while let Some(msg) = ws_receiver.next().await {
|
||||
282→ match msg {
|
||||
283→ Ok(Message::Binary(data)) => {
|
||||
284→ // Try to decode as protobuf message
|
||||
285→ match proto::Message::decode(data.as_ref()) {
|
||||
286→ Ok(proto_msg) => {
|
||||
287→ match &proto_msg.payload {
|
||||
288→ Some(proto::message::Payload::MouseEvent(_)) |
|
||||
289→ Some(proto::message::Payload::KeyEvent(_)) |
|
||||
290→ Some(proto::message::Payload::SpecialKey(_)) => {
|
||||
291→ // Forward input to agent
|
||||
292→ let _ = input_tx.send(data.to_vec()).await;
|
||||
293→ }
|
||||
294→ Some(proto::message::Payload::ChatMessage(chat)) => {
|
||||
295→ // Forward chat message to agent
|
||||
296→ info!("Chat from technician: {}", chat.content);
|
||||
297→ let _ = input_tx.send(data.to_vec()).await;
|
||||
298→ }
|
||||
299→ _ => {}
|
||||
300→ }
|
||||
301→ }
|
||||
302→ Err(e) => {
|
||||
303→ warn!("Failed to decode viewer message: {}", e);
|
||||
304→ }
|
||||
305→ }
|
||||
306→ }
|
||||
307→ Ok(Message::Close(_)) => {
|
||||
308→ info!("Viewer {} disconnected from session: {}", viewer_id, session_id);
|
||||
309→ break;
|
||||
310→ }
|
||||
311→ Ok(_) => {}
|
||||
312→ Err(e) => {
|
||||
313→ error!("WebSocket error from viewer {}: {}", viewer_id, e);
|
||||
314→ break;
|
||||
315→ }
|
||||
316→ }
|
||||
317→ }
|
||||
318→
|
||||
319→ // Cleanup (this sends StopStream to agent if last viewer)
|
||||
320→ frame_forward.abort();
|
||||
321→ sessions_cleanup.leave_session(session_id, &viewer_id_cleanup).await;
|
||||
322→ info!("Viewer {} left session: {}", viewer_id_cleanup, session_id);
|
||||
323→}
|
||||
324→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1,16 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\src\main.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
206→ .route("/api/sessions", get(list_sessions))
|
||||
207→ .route("/api/sessions/:id", get(get_session))
|
||||
208→ .route("/api/sessions/:id", delete(disconnect_session))
|
||||
209→
|
||||
210→ // REST API - Machines
|
||||
211→ .route("/api/machines", get(list_machines))
|
||||
212→ .route("/api/machines/:agent_id", get(get_machine))
|
||||
213→ .route("/api/machines/:agent_id", delete(delete_machine))
|
||||
214→ .route("/api/machines/:agent_id/history", get(get_machine_history))
|
||||
215→
|
||||
216→ // HTML page routes (clean URLs)
|
||||
217→ .route("/login", get(serve_login))
|
||||
218→ .route("/dashboard", get(serve_dashboard))
|
||||
219→ .route("/users", get(serve_users))
|
||||
220→
|
||||
@@ -0,0 +1 @@
|
||||
Created start-server.sh
|
||||
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIABnQjolTxDtfqOwdDjamK1oyFPiQnaNT/tAgsIHH1Zo claude-code
|
||||
@@ -0,0 +1,19 @@
|
||||
Get-Content : Cannot find path 'C:\Users\MikeSwanson\Claude\-' because it does not exist.
|
||||
At line:1 char:18
|
||||
+ & { \:SSHPASS = (Get-Content -Raw -Path '-'); ssh root@172.16.3.20 'e ...
|
||||
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
+ CategoryInfo : ObjectNotFound: (C:\Users\MikeSwanson\Claude\-:String) [Get-Content], ItemNotFoundExcept
|
||||
ion
|
||||
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
|
||||
|
||||
\:SSHPASS : The term '\:SSHPASS' is not recognized as the name of a cmdlet, function, script file, or operable
|
||||
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
|
||||
At line:1 char:5
|
||||
+ & { \:SSHPASS = (Get-Content -Raw -Path '-'); ssh root@172.16.3.20 'e ...
|
||||
+ ~~~~~~~~~
|
||||
+ CategoryInfo : ObjectNotFound: (\:SSHPASS:String) [], CommandNotFoundException
|
||||
+ FullyQualifiedErrorId : CommandNotFoundException
|
||||
|
||||
Permission denied, please try again.
|
||||
Permission denied, please try again.
|
||||
root@172.16.3.20: Permission denied (publickey,password,keyboard-interactive).
|
||||
@@ -0,0 +1,44 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\src\session\mod.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
286→ let _ = session_data.input_tx.send(buf).await;
|
||||
287→ }
|
||||
288→ }
|
||||
289→
|
||||
290→ /// Mark agent as disconnected
|
||||
291→ /// For persistent agents: keep session but mark as offline
|
||||
292→ /// For support sessions: remove session entirely
|
||||
293→ pub async fn mark_agent_disconnected(&self, session_id: SessionId) {
|
||||
294→ let mut sessions = self.sessions.write().await;
|
||||
295→ if let Some(session_data) = sessions.get_mut(&session_id) {
|
||||
296→ if session_data.info.is_persistent {
|
||||
297→ // Persistent agent - keep session but mark as offline
|
||||
298→ tracing::info!("Persistent agent {} marked offline (session {} preserved)",
|
||||
299→ session_data.info.agent_id, session_id);
|
||||
300→ session_data.info.is_online = false;
|
||||
301→ session_data.info.is_streaming = false;
|
||||
302→ session_data.info.viewer_count = 0;
|
||||
303→ session_data.viewers.clear();
|
||||
304→ } else {
|
||||
305→ // Support session - remove entirely
|
||||
306→ let agent_id = session_data.info.agent_id.clone();
|
||||
307→ sessions.remove(&session_id);
|
||||
308→ drop(sessions); // Release sessions lock before acquiring agents lock
|
||||
309→ let mut agents = self.agents.write().await;
|
||||
310→ agents.remove(&agent_id);
|
||||
311→ tracing::info!("Support session {} removed", session_id);
|
||||
312→ }
|
||||
313→ }
|
||||
314→ }
|
||||
315→
|
||||
316→ /// Remove a session entirely (for cleanup)
|
||||
317→ pub async fn remove_session(&self, session_id: SessionId) {
|
||||
318→ let mut sessions = self.sessions.write().await;
|
||||
319→ if let Some(session_data) = sessions.remove(&session_id) {
|
||||
320→ drop(sessions);
|
||||
321→ let mut agents = self.agents.write().await;
|
||||
322→ agents.remove(&session_data.info.agent_id);
|
||||
323→ }
|
||||
324→ }
|
||||
325→
|
||||
326→ /// Disconnect a session by sending a disconnect message to the agent
|
||||
327→ /// Returns true if the message was sent successfully
|
||||
328→ pub async fn disconnect_session(&self, session_id: SessionId, reason: &str) -> bool {
|
||||
@@ -0,0 +1 @@
|
||||
Command running in background with ID: b65271c. Output is being written to: C:\Users\MIKESW~1\AppData\Local\Temp\claude\C--Users-MikeSwanson-Claude\tasks\b65271c.output
|
||||
@@ -0,0 +1,13 @@
|
||||
Starting GuruConnect Server...
|
||||
Server log:
|
||||
[2m2025-12-30T04:07:27.976024Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Connecting to database...
|
||||
[2m2025-12-30T04:07:27.988213Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Database connection established
|
||||
[2m2025-12-30T04:07:27.988233Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Running database migrations...
|
||||
[2m2025-12-30T04:07:27.990753Z[0m [32m INFO[0m [2msqlx::postgres::notice[0m[2m:[0m relation "_sqlx_migrations" already exists, skipping
|
||||
[2m2025-12-30T04:07:27.994265Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Migrations complete
|
||||
[2m2025-12-30T04:07:28.006350Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m 1 user(s) in database
|
||||
[2m2025-12-30T04:07:28.008802Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m Restoring 2 persistent machines from database
|
||||
[2m2025-12-30T04:07:28.008863Z[0m [32m INFO[0m [2mguruconnect_server::session[0m[2m:[0m Restored offline machine: DESKTOP-N9MIFGD (62feaeb4-42b6-47a9-8d2c-a4629a50e053)
|
||||
[2m2025-12-30T04:07:28.008877Z[0m [32m INFO[0m [2mguruconnect_server::session[0m[2m:[0m Restored offline machine: DESKTOP-N9MIFGD (99432392-c89c-4f11-8547-fa38527c75ab)
|
||||
[2m2025-12-30T04:07:28.009332Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m Server listening on 0.0.0.0:3002
|
||||
Server started. PID: 47901
|
||||
@@ -0,0 +1,23 @@
|
||||
Exit code 101
|
||||
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
|
||||
package: /tmp/guru-connect/agent/Cargo.toml
|
||||
workspace: /tmp/guru-connect/Cargo.toml
|
||||
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
|
||||
package: /tmp/guru-connect/server/Cargo.toml
|
||||
workspace: /tmp/guru-connect/Cargo.toml
|
||||
Compiling proc-macro2 v1.0.103
|
||||
Compiling quote v1.0.42
|
||||
Compiling unicode-ident v1.0.22
|
||||
Compiling serde_core v1.0.228
|
||||
Compiling serde v1.0.228
|
||||
Compiling cfg-if v1.0.4
|
||||
Compiling typenum v1.19.0
|
||||
Compiling version_check v0.9.5
|
||||
error[E0463]: can't find crate for `core`
|
||||
|
|
||||
= note: the `x86_64-pc-windows-msvc` target may not be installed
|
||||
= help: consider downloading the target with `rustup target add x86_64-pc-windows-msvc`
|
||||
|
||||
For more information about this error, try `rustc --explain E0463`.
|
||||
error: could not compile `cfg-if` (lib) due to 1 previous error
|
||||
warning: build failed, waiting for other jobs to finish...
|
||||
@@ -0,0 +1,595 @@
|
||||
1→# Session Log: 2025-12-29
|
||||
2→
|
||||
3→## Session Summary
|
||||
4→
|
||||
5→### Work Completed
|
||||
6→
|
||||
7→1. **Fixed settings.local.json**
|
||||
8→ - Removed hardcoded credentials from permission entries (exposed passwords)
|
||||
9→ - Removed redundant WebFetch domain entries (domain:* covers all)
|
||||
10→ - Consolidated specific commands to wildcard patterns
|
||||
11→ - Cleaned up invalid/obsolete entries
|
||||
12→
|
||||
13→2. **Installed GuruRMM Agent on Scileppi RS2212+ NAS**
|
||||
14→ - Agent binary already present at `/volume1/gururmm/gururmm-agent`
|
||||
15→ - Fixed config format (was `server_url`, needed `[server] url`)
|
||||
16→ - Renamed `config.toml` to `agent.toml` (agent default)
|
||||
17→ - Cleared 199MB log file (caused by interactive mode prompt loop)
|
||||
18→ - Agent successfully connected and registered
|
||||
19→
|
||||
20→3. **Network Connectivity Troubleshooting**
|
||||
21→ - External URL `wss://rmm-api.azcomputerguru.com/ws` failed from NAS
|
||||
22→ - Cause: NAT hairpin - NAS currently on same local network as server
|
||||
23→ - Tested internal URL `ws://172.16.3.30:3001/ws` - works
|
||||
24→ - Final config uses external URL for when NAS moves to Scileppi location
|
||||
25→
|
||||
26→4. **Agent Registration Confirmed**
|
||||
27→ - Agent ID: `2585f6d5-3887-412e-a586-1dec030f0a40`
|
||||
28→ - Hostname: SL-SERVER
|
||||
29→ - Client: Scileppi Law Firm
|
||||
30→ - Site: Main Office
|
||||
31→ - Status: Online (when using internal URL)
|
||||
32→
|
||||
33→---
|
||||
34→
|
||||
35→## Credentials
|
||||
36→
|
||||
37→### Scileppi RS2212+ NAS
|
||||
38→- **IP:** 172.16.1.59
|
||||
39→- **Hostname:** SL-SERVER
|
||||
40→- **SSH User:** sysadmin
|
||||
41→- **Password:** Gptf*77ttb123!@#-sl-server
|
||||
42→- **Storage:** 25TB total, 6.9TB used (28%)
|
||||
43→
|
||||
44→### Scileppi DS214se (Source - Migration Complete)
|
||||
45→- **IP:** 172.16.1.54
|
||||
46→- **SSH User:** admin
|
||||
47→- **Password:** Th1nk3r^99
|
||||
48→
|
||||
49→### Scileppi Unraid (Source - Migration Complete)
|
||||
50→- **IP:** 172.16.1.21
|
||||
51→- **SSH User:** root
|
||||
52→- **Password:** Th1nk3r^99
|
||||
53→
|
||||
54→### GuruRMM Agent on RS2212+
|
||||
55→- **API Key:** grmm_YlqtkrCpEe0Fxfc7lipqqFO_JwUUvojH
|
||||
56→- **Server URL:** wss://rmm-api.azcomputerguru.com/ws
|
||||
57→- **Agent ID:** 2585f6d5-3887-412e-a586-1dec030f0a40
|
||||
58→
|
||||
59→### Build Server (172.16.3.30)
|
||||
60→- **User:** guru
|
||||
61→- **Password:** Gptf*77ttb123!@#-rmm
|
||||
62→- **Root SSH:** Key-based auth configured
|
||||
63→
|
||||
64→### GuruRMM Server
|
||||
65→- **Internal URL:** http://172.16.3.30:3001
|
||||
66→- **External URL:** https://rmm-api.azcomputerguru.com
|
||||
67→- **WebSocket:** wss://rmm-api.azcomputerguru.com/ws
|
||||
68→- **DNS:** Resolves to 72.194.62.4
|
||||
69→
|
||||
70→---
|
||||
71→
|
||||
72→## Infrastructure
|
||||
73→
|
||||
74→### SSH Access to RS2212+ (via jump host)
|
||||
75→```bash
|
||||
76→ssh guru@172.16.3.30 << 'ENDSSH'
|
||||
77→cat > /tmp/synopass << 'PASSEOF'
|
||||
78→Gptf*77ttb123!@#-sl-server
|
||||
79→PASSEOF
|
||||
80→sshpass -f /tmp/synopass ssh -o StrictHostKeyChecking=no sysadmin@172.16.1.59 "command"
|
||||
81→rm -f /tmp/synopass
|
||||
82→ENDSSH
|
||||
83→```
|
||||
84→
|
||||
85→### Files on RS2212+
|
||||
86→```
|
||||
87→/volume1/gururmm/
|
||||
88→├── gururmm-agent (4.7 MB binary, v0.5.1)
|
||||
89→├── agent.toml (config file)
|
||||
90→├── agent.log (log file)
|
||||
91→└── start.sh (startup script)
|
||||
92→```
|
||||
93→
|
||||
94→### Agent Config (/volume1/gururmm/agent.toml)
|
||||
95→```toml
|
||||
96→[server]
|
||||
97→url = "wss://rmm-api.azcomputerguru.com/ws"
|
||||
98→api_key = "grmm_YlqtkrCpEe0Fxfc7lipqqFO_JwUUvojH"
|
||||
99→
|
||||
100→[metrics]
|
||||
101→interval_seconds = 60
|
||||
102→collect_cpu = true
|
||||
103→collect_memory = true
|
||||
104→collect_disk = true
|
||||
105→collect_network = true
|
||||
106→
|
||||
107→[watchdog]
|
||||
108→enabled = false
|
||||
109→check_interval_seconds = 30
|
||||
110→```
|
||||
111→
|
||||
112→### Startup Script (/volume1/gururmm/start.sh)
|
||||
113→```bash
|
||||
114→#!/bin/bash
|
||||
115→cd /volume1/gururmm
|
||||
116→nohup ./gururmm-agent run >> agent.log 2>&1 &
|
||||
117→```
|
||||
118→
|
||||
119→---
|
||||
120→
|
||||
121→## Commands Reference
|
||||
122→
|
||||
123→### Agent CLI
|
||||
124→```bash
|
||||
125→gururmm-agent run # Run the agent
|
||||
126→gururmm-agent setup # Interactive setup
|
||||
127→gururmm-agent install # Install as system service
|
||||
128→gururmm-agent status # Show agent status
|
||||
129→gururmm-agent generate-config # Generate sample config
|
||||
130→gururmm-agent -c config.toml run # Run with specific config
|
||||
131→```
|
||||
132→
|
||||
133→### Check Agent Status
|
||||
134→```bash
|
||||
135→# View log
|
||||
136→tail -f /volume1/gururmm/agent.log
|
||||
137→
|
||||
138→# Check if running
|
||||
139→ps aux | grep gururmm-agent
|
||||
140→
|
||||
141→# Kill agent
|
||||
142→pkill gururmm-agent
|
||||
143→
|
||||
144→# Start agent
|
||||
145→cd /volume1/gururmm && nohup ./gururmm-agent run >> agent.log 2>&1 &
|
||||
146→```
|
||||
147→
|
||||
148→### Check RMM Dashboard Agents
|
||||
149→```bash
|
||||
150→curl -s http://172.16.3.30:3001/api/agents | python3 -m json.tool
|
||||
151→```
|
||||
152→
|
||||
153→---
|
||||
154→
|
||||
155→## Problems Encountered & Solutions
|
||||
156→
|
||||
157→### 1. Config Format Wrong
|
||||
158→- **Problem:** Agent expected `[server] url = ...` format, had `server_url = ...`
|
||||
159→- **Solution:** Generated sample config with `gururmm-agent generate-config`, matched format
|
||||
160→
|
||||
161→### 2. 199MB Log File
|
||||
162→- **Problem:** Agent ran in interactive mode, filled log with "Enter API Key" prompts
|
||||
163→- **Solution:** Renamed config.toml to agent.toml (default name), ran with `run` subcommand
|
||||
164→
|
||||
165→### 3. NAT Hairpin Issue
|
||||
166→- **Problem:** NAS couldn't reach `wss://rmm-api.azcomputerguru.com/ws`
|
||||
167→- **Cause:** NAS temporarily on same local network as server
|
||||
168→- **Solution:** Config set to external URL, will work when NAS moved to Scileppi
|
||||
169→
|
||||
170→### 4. No Sudo on Synology
|
||||
171→- **Problem:** sysadmin user can't create systemd service or rc.d script
|
||||
172→- **Solution:** Created start.sh script, manual Task Scheduler setup required in DSM
|
||||
173→
|
||||
174→---
|
||||
175→
|
||||
176→## Pending Tasks
|
||||
177→
|
||||
178→### Scileppi NAS
|
||||
179→1. **Move NAS to Scileppi location** - Agent will auto-connect via external URL
|
||||
180→2. **Set up Task Scheduler in DSM** for startup persistence:
|
||||
181→ - Control Panel → Task Scheduler → Triggered Task
|
||||
182→ - User: `sysadmin`, Event: Boot-up
|
||||
183→ - Script: `/volume1/gururmm/start.sh`
|
||||
184→
|
||||
185→### Scileppi Data Restructure (from previous session)
|
||||
186→1. Create "Data" shared folder on RS2212+
|
||||
187→2. Create user accounts (Chris, Andrew, Sylvia, Rose, +1 TBD)
|
||||
188→3. Move data from /volume1/homes/ to /volume1/Data/
|
||||
189→4. Configure SMB for Mac clients
|
||||
190→
|
||||
191→---
|
||||
192→
|
||||
193→## Reference
|
||||
194→
|
||||
195→### Current RS2212+ /volume1 Contents
|
||||
196→| Folder | Size | Source |
|
||||
197→|--------|------|--------|
|
||||
198→| Data | new | Created for restructure |
|
||||
199→| homes | 6.7TB | Contains migrated data |
|
||||
200→| gururmm | ~5MB | RMM agent |
|
||||
201→| Test | - | Test share |
|
||||
202→
|
||||
203→### Agents in GuruRMM Dashboard
|
||||
204→| Hostname | Status | Client | Agent ID |
|
||||
205→|----------|--------|--------|----------|
|
||||
206→| ACG-M-L5090 | offline | AZ Computer Guru | 97f63c3b-... |
|
||||
207→| gururmm | online | AZ Computer Guru | 8cd0440f-... |
|
||||
208→| SL-SERVER | online* | Scileppi Law Firm | 2585f6d5-... |
|
||||
209→
|
||||
210→*SL-SERVER will show offline until moved to Scileppi network (NAT hairpin)
|
||||
211→
|
||||
212→---
|
||||
213→
|
||||
214→## Update: 08:30 - NAS Prep and DNS Fix
|
||||
215→
|
||||
216→### Work Completed
|
||||
217→
|
||||
218→5. **Changed RS2212+ Bond Mode**
|
||||
219→ - Changed from LACP (mode 4) to active-backup (mode 1)
|
||||
220→ - LACP requires switch configuration; active-backup works with any switch
|
||||
221→ - Config: `/etc/sysconfig/network-scripts/ifcfg-bond0`
|
||||
222→ - New BONDING_OPTS: `mode=1 miimon=100 primary=eth0`
|
||||
223→
|
||||
224→6. **Shutdown RS2212+ for Site Move**
|
||||
225→ - NAS shut down for physical move to Scileppi location
|
||||
226→ - Will get new DHCP IP at Scileppi network
|
||||
227→ - Agent will auto-connect via external URL once powered on
|
||||
228→
|
||||
229→7. **Fixed cascadestucson.com DMARC**
|
||||
230→ - **Problem:** Duplicate DMARC records causing validation failures
|
||||
231→ - Deleted: `v=DMARC1; p=none;` (line 21)
|
||||
232→ - Kept: `v=DMARC1;p=none;pct=100;rua=mailto:info@cascadestucson.com;ruf=mailto:info@cascadestucson.com;ri=86400;fo=1;`
|
||||
233→
|
||||
234→8. **Set cascadestucson.com Zone TTL to 5 Minutes**
|
||||
235→ - Updated SOA minimum TTL to 300
|
||||
236→ - Updated all 31 records to 300 second TTL
|
||||
237→ - Faster propagation for any future changes
|
||||
238→
|
||||
239→### DNS Status - cascadestucson.com
|
||||
240→
|
||||
241→| Record | Status | Value |
|
||||
242→|--------|--------|-------|
|
||||
243→| DMARC | FIXED | Single record with reporting |
|
||||
244→| SPF | OK | `v=spf1 include:spf.protection.outlook.com -all` |
|
||||
245→| MX | OK | `cascadestucson-com.mail.protection.outlook.com` |
|
||||
246→| DKIM | OK | selector1/selector2 CNAMEs to Microsoft 365 |
|
||||
247→| TTL | 300s | All records now 5 minute TTL |
|
||||
248→
|
||||
249→### WHM API Used
|
||||
250→
|
||||
251→```bash
|
||||
252→# Delete DMARC record
|
||||
253→curl -s "https://websvr.acghosting.com:2087/json-api/removezonerecord?domain=cascadestucson.com&zone=cascadestucson.com&line=21" \
|
||||
254→ -H "Authorization: whm root:8ZPYVM6R0RGOHII7EFF533MX6EQ17M7O"
|
||||
255→
|
||||
256→# Edit record TTL
|
||||
257→curl -s "https://websvr.acghosting.com:2087/json-api/editzonerecord?domain=cascadestucson.com&line=X&type=TXT&name=record.&ttl=300&txtdata=value" \
|
||||
258→ -H "Authorization: whm root:8ZPYVM6R0RGOHII7EFF533MX6EQ17M7O"
|
||||
259→```
|
||||
260→
|
||||
261→---
|
||||
262→
|
||||
263→## Credentials Added
|
||||
264→
|
||||
265→### WebSvr (WHM/cPanel)
|
||||
266→- **Host:** websvr.acghosting.com
|
||||
267→- **API Token:** 8ZPYVM6R0RGOHII7EFF533MX6EQ17M7O
|
||||
268→- **SSH User:** root
|
||||
269→- **SSH Password:** r3tr0gradE99#
|
||||
270→
|
||||
271→---
|
||||
272→
|
||||
273→## Update: 09:30 - Scileppi NAS Deployment Complete
|
||||
274→
|
||||
275→### Work Completed
|
||||
276→
|
||||
277→9. **NAS Online at Scileppi Location**
|
||||
278→ - New IP: 192.168.242.5 (via VPN)
|
||||
279→ - RMM agent connected successfully to `wss://rmm-api.azcomputerguru.com/ws`
|
||||
280→ - Agent status: **online** in dashboard
|
||||
281→
|
||||
282→10. **Fixed Filename Colon Issue**
|
||||
283→ - 6,505 files and 122 folders had ":" in names (Mac "/" → ":" translation)
|
||||
284→ - Renamed all to use "-" instead for Windows compatibility
|
||||
285→ - Command: `find /volume1/Data -name '*:*' | while read f; do mv "$f" "$(echo $f | sed 's/:/-/g')"; done`
|
||||
286→
|
||||
287→11. **Configured Agent Startup Persistence**
|
||||
288→ - Created `/usr/local/etc/rc.d/S99gururmm.sh`
|
||||
289→ - Agent will auto-start on boot
|
||||
290→ - SSH as root works: `root@192.168.242.5` with same password
|
||||
291→
|
||||
292→### Scileppi NAS Final Configuration
|
||||
293→
|
||||
294→| Setting | Value |
|
||||
295→|---------|-------|
|
||||
296→| IP | 192.168.242.5 |
|
||||
297→| Hostname | SL-SERVER |
|
||||
298→| SSH User | sysadmin / root |
|
||||
299→| Password | Gptf*77ttb123!@#-sl-server |
|
||||
300→| Bond Mode | active-backup (mode 1) |
|
||||
301→| RMM Agent | /volume1/gururmm/gururmm-agent |
|
||||
302→| Startup Script | /usr/local/etc/rc.d/S99gururmm.sh |
|
||||
303→| Agent Status | Online |
|
||||
304→
|
||||
305→### Startup Script (/usr/local/etc/rc.d/S99gururmm.sh)
|
||||
306→```bash
|
||||
307→#!/bin/sh
|
||||
308→case "$1" in
|
||||
309→ start)
|
||||
310→ if [ -x /volume1/gururmm/gururmm-agent ]; then
|
||||
311→ cd /volume1/gururmm
|
||||
312→ /volume1/gururmm/gururmm-agent run >> /volume1/gururmm/agent.log 2>&1 &
|
||||
313→ echo "GuruRMM Agent started"
|
||||
314→ fi
|
||||
315→ ;;
|
||||
316→ stop)
|
||||
317→ pkill -f gururmm-agent
|
||||
318→ echo "GuruRMM Agent stopped"
|
||||
319→ ;;
|
||||
320→esac
|
||||
321→exit 0
|
||||
322→```
|
||||
323→
|
||||
324→---
|
||||
325→
|
||||
326→## Future Feature: RMM Agent Tunnel/Proxy
|
||||
327→
|
||||
328→**Concept:** Create direct TCP tunnels through RMM agents to access remote services without VPN.
|
||||
329→
|
||||
330→**Use case:** Access Synology DSM (port 5000) at Scileppi via tunnel through RMM agents.
|
||||
331→
|
||||
332→**Architecture:**
|
||||
333→```
|
||||
334→Browser → localhost:15000 → Local Agent → RMM Server → Remote Agent → DSM:5000
|
||||
335→```
|
||||
336→
|
||||
337→**Implementation approach:** WebSocket relay (uses existing agent connections)
|
||||
338→
|
||||
339→**Status:** Pending - return to this later
|
||||
340→
|
||||
341→---
|
||||
342→
|
||||
343→## Git Status
|
||||
344→- Modified: `.claude/settings.local.json` (cleaned up)
|
||||
345→- Added: `session-logs/2025-12-29-session.md`
|
||||
346→
|
||||
347→---
|
||||
348→
|
||||
349→## Update: 12:45 - M365 Investigation Tools & DNS Fixes
|
||||
350→
|
||||
351→### Work Completed
|
||||
352→
|
||||
353→1. **Created Claude-MSP-Access Multi-Tenant App**
|
||||
354→ - Full Graph API access for M365 investigations and remediation
|
||||
355→ - Permissions: User.ReadWrite.All, Directory.ReadWrite.All, Mail.ReadWrite, MailboxSettings.ReadWrite, AuditLog.Read.All, Application.ReadWrite.All, DelegatedPermissionGrant.ReadWrite.All, Group.ReadWrite.All, SecurityEvents.ReadWrite.All, AppRoleAssignment.ReadWrite.All, UserAuthenticationMethod.ReadWrite.All
|
||||
356→ - Admin consent URL for onboarding new tenants
|
||||
357→
|
||||
358→2. **Tested on martylryan.com Tenant**
|
||||
359→ - Successfully authenticated and queried tenant
|
||||
360→ - Pulled users, sign-in logs, OAuth grants, service principals
|
||||
361→ - Found suspicious sign-in failures from VPN IP (195.210.125.x - GSL Networks)
|
||||
362→ - User had already remediated account
|
||||
363→
|
||||
364→3. **Checked CIPP-SAM Permissions**
|
||||
365→ - CIPP-SAM has 54 Graph permissions
|
||||
366→ - Missing 4 for full remediation: AppRoleAssignment.ReadWrite.All, DelegatedPermissionGrant.ReadWrite.All, Mail.ReadWrite, SecurityEvents.ReadWrite.All
|
||||
367→ - User to add missing permissions via Entra portal
|
||||
368→
|
||||
369→4. **Fixed SPF Records**
|
||||
370→ - **acepickupparts.com**: Added IX IP (72.194.62.5) to SPF
|
||||
371→ - Before: `v=spf1 include:spf.us.emailservice.io -all`
|
||||
372→ - After: `v=spf1 ip4:72.194.62.5 include:spf.us.emailservice.io -all`
|
||||
373→ - **devconllc.com**: Added IX IP (72.194.62.5) to SPF
|
||||
374→ - Before: `v=spf1 +a +mx +ip4:162.248.93.233 +ip4:72.194.62.7 +include:mail.acghosting.com +include:spf.us.emailservice.io -all`
|
||||
375→ - After: `v=spf1 +a +mx +ip4:162.248.93.233 +ip4:72.194.62.7 +ip4:72.194.62.5 +include:mail.acghosting.com +include:spf.us.emailservice.io -all`
|
||||
376→
|
||||
377→5. **Checked woodenbucketcreative.com Email Records**
|
||||
378→ - Domain on Wix DNS, MX points to Google Workspace
|
||||
379→ - User fixed missing SPF and DMARC via Wix dashboard
|
||||
380→
|
||||
381→6. **Identified Glue Record Issue**
|
||||
382→ - ns1/ns2/ns3.acghosting.com had no glue records at GoDaddy
|
||||
383→ - All three were resolving to same IP (52.52.94.202 - DNS cluster)
|
||||
384→ - User added glue records at GoDaddy:
|
||||
385→ - ns1 → 162.248.93.233 (WEBSVR)
|
||||
386→ - ns2 → 72.194.62.5 (IX)
|
||||
387→ - ns3 → 52.52.94.202 (DNS Cluster)
|
||||
388→ - Propagation in progress
|
||||
389→
|
||||
390→7. **Started GuruConnect Native Viewer**
|
||||
391→ - Created viewer crate in guru-connect workspace
|
||||
392→ - Implemented WebSocket client, window rendering, low-level keyboard hooks
|
||||
393→ - Purpose: Full keyboard capture including Win key, Alt+Tab, etc.
|
||||
394→ - Files created: viewer/Cargo.toml, viewer/src/main.rs, proto.rs, transport.rs, render.rs, input.rs
|
||||
395→
|
||||
396→---
|
||||
397→
|
||||
398→### Credentials
|
||||
399→
|
||||
400→#### Claude-MSP-Access (Multi-Tenant Graph API)
|
||||
401→- **Tenant ID:** ce61461e-81a0-4c84-bb4a-7b354a9a356d
|
||||
402→- **App ID (Client ID):** fabb3421-8b34-484b-bc17-e46de9703418
|
||||
403→- **Client Secret:** ~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO
|
||||
404→- **Secret Expires:** 2026-12 (24 months)
|
||||
405→- **Sign-in Audience:** Multi-tenant (any Entra ID org)
|
||||
406→- **Admin Consent URL:** https://login.microsoftonline.com/common/adminconsent?client_id=fabb3421-8b34-484b-bc17-e46de9703418&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient
|
||||
407→
|
||||
408→#### AZ Computer Guru M365
|
||||
409→- **Admin User:** mike@azcomputerguru.com
|
||||
410→- **Password:** Window123!@#
|
||||
411→- **Note:** MFA enabled, ROPC flow blocked
|
||||
412→
|
||||
413→#### Marty Ryan Tenant (Tested)
|
||||
414→- **Tenant ID:** 48581923-2153-48b9-82b3-6a3587813041
|
||||
415→- **Domain:** martylryan.com
|
||||
416→- **Admin:** admin@martylryan.onmicrosoft.com
|
||||
417→- **Status:** Claude-MSP-Access consented, Graph API working
|
||||
418→
|
||||
419→---
|
||||
420→
|
||||
421→### Infrastructure
|
||||
422→
|
||||
423→#### Server IPs Verified
|
||||
424→| Server | IP |
|
||||
425→|--------|-----|
|
||||
426→| websvr.acghosting.com | 162.248.93.233 |
|
||||
427→| ix.azcomputerguru.com | 72.194.62.5 |
|
||||
428→| ns1.acghosting.com | Currently 52.52.94.202 (glue updating to 162.248.93.233) |
|
||||
429→| ns2.acghosting.com | Currently 52.52.94.202 (glue updating to 72.194.62.5) |
|
||||
430→| ns3.acghosting.com | 52.52.94.202 (DNS Cluster) |
|
||||
431→
|
||||
432→#### Glue Records at GoDaddy (for acghosting.com)
|
||||
433→| Hostname | IP | Server |
|
||||
434→|----------|-----|--------|
|
||||
435→| ns1 | 162.248.93.233 | WEBSVR |
|
||||
436→| ns2 | 72.194.62.5 | IX |
|
||||
437→| ns3 | 52.52.94.202 | DNS Cluster |
|
||||
438→
|
||||
439→---
|
||||
440→
|
||||
441→### Commands Reference
|
||||
442→
|
||||
443→#### Claude-MSP-Access Token & Query
|
||||
444→```python
|
||||
445→import requests
|
||||
446→
|
||||
447→tenant_id = "CUSTOMER_TENANT_ID" # After admin consent
|
||||
448→client_id = "fabb3421-8b34-484b-bc17-e46de9703418"
|
||||
449→client_secret = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
|
||||
450→
|
||||
451→# Get token
|
||||
452→token_resp = requests.post(
|
||||
453→ f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token",
|
||||
454→ data={
|
||||
455→ "client_id": client_id,
|
||||
456→ "client_secret": client_secret,
|
||||
457→ "scope": "https://graph.microsoft.com/.default",
|
||||
458→ "grant_type": "client_credentials"
|
||||
459→ }
|
||||
460→)
|
||||
461→access_token = token_resp.json()["access_token"]
|
||||
462→
|
||||
463→# Query Graph API
|
||||
464→headers = {"Authorization": f"Bearer {access_token}"}
|
||||
465→users = requests.get("https://graph.microsoft.com/v1.0/users", headers=headers)
|
||||
466→signins = requests.get("https://graph.microsoft.com/v1.0/auditLogs/signIns?$top=10", headers=headers)
|
||||
467→```
|
||||
468→
|
||||
469→#### WHM API for DNS Updates
|
||||
470→```bash
|
||||
471→# Edit SPF record
|
||||
472→curl -s "https://websvr.acghosting.com:2087/json-api/editzonerecord?domain=DOMAIN&line=LINE&type=TXT&name=DOMAIN.&ttl=14400&txtdata=ENCODED_SPF" \
|
||||
473→ -H "Authorization: whm root:8ZPYVM6R0RGOHII7EFF533MX6EQ17M7O"
|
||||
474→```
|
||||
475→
|
||||
476→---
|
||||
477→
|
||||
478→### Files Created/Modified
|
||||
479→
|
||||
480→#### GuruConnect Viewer (new crate)
|
||||
481→- `viewer/Cargo.toml` - Dependencies for native viewer
|
||||
482→- `viewer/build.rs` - Protobuf compilation
|
||||
483→- `viewer/src/main.rs` - Entry point, CLI args, main loop
|
||||
484→- `viewer/src/proto.rs` - Protobuf module
|
||||
485→- `viewer/src/transport.rs` - WebSocket client
|
||||
486→- `viewer/src/render.rs` - Window and frame rendering with softbuffer
|
||||
487→- `viewer/src/input.rs` - Low-level keyboard hooks (Win key capture)
|
||||
488→
|
||||
489→#### Workspace Updated
|
||||
490→- `Cargo.toml` - Added viewer to workspace members
|
||||
491→
|
||||
492→---
|
||||
493→
|
||||
494→### Pending Tasks
|
||||
495→
|
||||
496→#### GuruConnect Native Viewer
|
||||
497→- [ ] Build and test viewer on Windows
|
||||
498→- [ ] Test low-level keyboard hook (Win key, Alt+Tab capture)
|
||||
499→- [ ] Test mouse input forwarding
|
||||
500→- [ ] Integrate Ctrl+Alt+Del special key support
|
||||
501→- [ ] Add fullscreen toggle (F11)
|
||||
502→
|
||||
503→#### CIPP Permissions
|
||||
504→- [ ] Add missing permissions to CIPP-SAM app in Entra:
|
||||
505→ - AppRoleAssignment.ReadWrite.All
|
||||
506→ - DelegatedPermissionGrant.ReadWrite.All
|
||||
507→ - Mail.ReadWrite
|
||||
508→ - SecurityEvents.ReadWrite.All
|
||||
509→- [ ] Run SAM refresh to push to all tenants
|
||||
510→
|
||||
511→#### DNS Glue Records
|
||||
512→- [ ] Verify glue record propagation for ns1/ns2/ns3.acghosting.com
|
||||
513→- [ ] Update affected domains to use all three nameservers
|
||||
514→
|
||||
515→---
|
||||
516→
|
||||
517→### Reference
|
||||
518→
|
||||
519→#### GuruConnect Viewer Modes (Planned)
|
||||
520→| Mode | Description | Use Case |
|
||||
521→|------|-------------|----------|
|
||||
522→| Web Viewer | Browser-based, quick access | Simple support, most keys work |
|
||||
523→| Native Viewer | Full keyboard capture | Full control, Win+R, Alt+Tab |
|
||||
524→| Backstage | Admin console | CMD, file transfer, registry |
|
||||
525→
|
||||
526→#### Admin Consent URL (for new M365 tenants)
|
||||
527→```
|
||||
528→https://login.microsoftonline.com/common/adminconsent?client_id=fabb3421-8b34-484b-bc17-e46de9703418&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient
|
||||
529→```
|
||||
530→
|
||||
531→After customer admin clicks and approves, use their tenant ID or domain with Claude-MSP-Access credentials to query Graph API.
|
||||
532→
|
||||
533→---
|
||||
534→
|
||||
535→## Update: 17:50 - GuruConnect Native Viewer Built
|
||||
536→
|
||||
537→### Work Completed
|
||||
538→
|
||||
539→8. **Set Up Windows Development Environment**
|
||||
540→ - Installed Rust on Windows (`rustup-init.exe` with stable-x86_64-pc-windows-msvc)
|
||||
541→ - Installed Visual Studio Build Tools 2022 with C++ workload
|
||||
542→ - Installed protoc (Protocol Buffers compiler) for protobuf code generation
|
||||
543→ - Location: `C:\Users\localadmin\protoc\bin\protoc.exe`
|
||||
544→
|
||||
545→9. **Built GuruConnect Native Viewer**
|
||||
546→ - Successfully compiled for Windows with full keyboard hook support
|
||||
547→ - Output: `C:\Users\localadmin\claude-projects\guru-connect\target\x86_64-pc-windows-msvc\release\guruconnect-viewer.exe` (2.8MB)
|
||||
548→ - Features:
|
||||
549→ - Low-level keyboard hooks for Win key, Alt+Tab capture
|
||||
550→ - WebSocket client for server connection
|
||||
551→ - softbuffer rendering for frame display
|
||||
552→ - Zstd decompression
|
||||
553→ - Mouse and keyboard input forwarding
|
||||
554→
|
||||
555→### Windows Build Environment
|
||||
556→
|
||||
557→```
|
||||
558→Rust: 1.92.0 (stable-x86_64-pc-windows-msvc)
|
||||
559→Visual Studio: Build Tools 2022 with C++ workload
|
||||
560→protoc: v29.3 at C:\Users\localadmin\protoc\bin\protoc.exe
|
||||
561→```
|
||||
562→
|
||||
563→### Build Command (from PowerShell)
|
||||
564→```powershell
|
||||
565→$env:PROTOC = 'C:\Users\localadmin\protoc\bin\protoc.exe'
|
||||
566→cd 'C:\Users\localadmin\claude-projects\guru-connect'
|
||||
567→cargo build -p guruconnect-viewer --release
|
||||
568→```
|
||||
569→
|
||||
570→### Viewer Files Created
|
||||
571→| File | Purpose |
|
||||
572→|------|---------|
|
||||
573→| `viewer/Cargo.toml` | Dependencies (winit, softbuffer, tokio, windows API) |
|
||||
574→| `viewer/build.rs` | Protobuf compilation |
|
||||
575→| `viewer/src/main.rs` | Entry point, CLI args, async runtime |
|
||||
576→| `viewer/src/proto.rs` | Protobuf module |
|
||||
577→| `viewer/src/transport.rs` | WebSocket client |
|
||||
578→| `viewer/src/render.rs` | Window rendering with softbuffer |
|
||||
579→| `viewer/src/input.rs` | Low-level keyboard hooks (Win key capture) |
|
||||
580→
|
||||
581→### Viewer CLI Usage
|
||||
582→```bash
|
||||
583→guruconnect-viewer.exe -i <session_id> -s wss://connect.azcomputerguru.com/ws/viewer -a <api_key>
|
||||
584→```
|
||||
585→
|
||||
586→### Next Steps for Viewer
|
||||
587→- [ ] Test with running GuruConnect server
|
||||
588→- [ ] Add fullscreen toggle (F11)
|
||||
589→- [ ] Add Ctrl+Alt+Del special key button
|
||||
590→- [ ] Test Win key and Alt+Tab capture
|
||||
591→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1 @@
|
||||
Command running in background with ID: bc4f361. Output is being written to: C:\Users\MIKESW~1\AppData\Local\Temp\claude\C--Users-MikeSwanson-Claude\tasks\bc4f361.output
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 1
|
||||
bash: line 1: cd: /tmp/guru-connect: No such file or directory
|
||||
@@ -0,0 +1 @@
|
||||
test
|
||||
@@ -0,0 +1,14 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\src\relay\mod.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
219→ }
|
||||
220→ }
|
||||
221→ }
|
||||
222→
|
||||
223→ // Cleanup
|
||||
224→ input_forward.abort();
|
||||
225→ cancel_check.abort();
|
||||
226→ // Mark agent as disconnected (persistent agents stay in list as offline)
|
||||
227→ sessions_cleanup.mark_agent_disconnected(session_id).await;
|
||||
228→
|
||||
229→ // Mark support code as completed if one was used (unless cancelled)
|
||||
230→ if let Some(ref code) = support_code_cleanup {
|
||||
231→ if !support_codes_cleanup.is_cancelled(code).await {
|
||||
@@ -0,0 +1,24 @@
|
||||
The file C:\Users\MikeSwanson\Claude\shared-data\credentials.md has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
109→---
|
||||
110→
|
||||
111→## Projects - GuruConnect
|
||||
112→
|
||||
113→### Dashboard Login
|
||||
114→- **URL:** https://gc.azcomputerguru.com/login
|
||||
115→- **Username:** admin
|
||||
116→- **Password:** uwYmX6aygmJ@ZGqv
|
||||
117→- **Role:** admin
|
||||
118→- **Created:** 2025-12-29
|
||||
119→
|
||||
120→### Database (PostgreSQL on build server)
|
||||
121→- **Host:** localhost (172.16.3.30)
|
||||
122→- **Port:** 5432
|
||||
123→- **Database:** guruconnect
|
||||
124→- **User:** guruconnect
|
||||
125→- **Password:** gc_a7f82d1e4b9c3f60
|
||||
126→- **DATABASE_URL:** `postgres://guruconnect:gc_a7f82d1e4b9c3f60@localhost:5432/guruconnect`
|
||||
127→- **Created:** 2025-12-28
|
||||
128→
|
||||
129→---
|
||||
130→
|
||||
131→## Projects - GuruRMM (continued)
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 255
|
||||
ssh: Could not resolve hostname jupiter: No such host is known.
|
||||
@@ -0,0 +1,335 @@
|
||||
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
|
||||
package: /home/guru/guru-connect/server/Cargo.toml
|
||||
workspace: /home/guru/guru-connect/Cargo.toml
|
||||
Compiling guruconnect-server v0.1.0 (/home/guru/guru-connect/server)
|
||||
warning: unused import: `JwtConfig`
|
||||
--> server/src/api/auth.rs:11:41
|
||||
|
|
||||
11 | verify_password, AuthenticatedUser, JwtConfig,
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: unused import: `Query`
|
||||
--> server/src/api/mod.rs:7:28
|
||||
|
|
||||
7 | extract::{Path, State, Query},
|
||||
| ^^^^^
|
||||
|
||||
warning: unused import: `machines::*`
|
||||
--> server/src/db/mod.rs:17:9
|
||||
|
|
||||
17 | pub use machines::*;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: unused import: `sessions::*`
|
||||
--> server/src/db/mod.rs:18:9
|
||||
|
|
||||
18 | pub use sessions::*;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: unused import: `events::*`
|
||||
--> server/src/db/mod.rs:19:9
|
||||
|
|
||||
19 | pub use events::*;
|
||||
| ^^^^^^^^^
|
||||
|
||||
warning: unused import: `support_codes::*`
|
||||
--> server/src/db/mod.rs:20:9
|
||||
|
|
||||
20 | pub use support_codes::*;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: unused variable: `machine_id`
|
||||
--> server/src/relay/mod.rs:118:9
|
||||
|
|
||||
118 | let machine_id = if let Some(ref db) = db {
|
||||
| ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_machine_id`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: struct `ValidateParams` is never constructed
|
||||
--> server/src/main.rs:270:8
|
||||
|
|
||||
270 | struct ValidateParams {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: fields `listen_addr`, `jwt_secret`, and `debug` are never read
|
||||
--> server/src/config.rs:10:9
|
||||
|
|
||||
8 | pub struct Config {
|
||||
| ------ fields in this struct
|
||||
9 | /// Address to listen on (e.g., "0.0.0.0:8080")
|
||||
10 | pub listen_addr: String,
|
||||
| ^^^^^^^^^^^
|
||||
...
|
||||
19 | pub jwt_secret: Option<String>,
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
22 | pub debug: bool,
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `Config` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
|
||||
|
||||
warning: constant `HEARTBEAT_TIMEOUT_SECS` is never used
|
||||
--> server/src/session/mod.rs:30:7
|
||||
|
|
||||
30 | const HEARTBEAT_TIMEOUT_SECS: u64 = 90;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `input_rx` is never read
|
||||
--> server/src/session/mod.rs:67:5
|
||||
|
|
||||
61 | struct SessionData {
|
||||
| ----------- field in this struct
|
||||
...
|
||||
67 | input_rx: Option<InputReceiver>,
|
||||
| ^^^^^^^^
|
||||
|
||||
warning: methods `is_session_timed_out` and `get_timed_out_sessions` are never used
|
||||
--> server/src/session/mod.rs:195:18
|
||||
|
|
||||
81 | impl SessionManager {
|
||||
| ------------------- methods in this implementation
|
||||
...
|
||||
195 | pub async fn is_session_timed_out(&self, session_id: SessionId) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
205 | pub async fn get_timed_out_sessions(&self) -> Vec<SessionId> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `permissions` is never read
|
||||
--> server/src/auth/mod.rs:24:9
|
||||
|
|
||||
20 | pub struct AuthenticatedUser {
|
||||
| ----------------- field in this struct
|
||||
...
|
||||
24 | pub permissions: Vec<String>,
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: `AuthenticatedUser` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
|
||||
|
||||
warning: method `has_permission` is never used
|
||||
--> server/src/auth/mod.rs:29:12
|
||||
|
|
||||
27 | impl AuthenticatedUser {
|
||||
| ---------------------- method in this implementation
|
||||
28 | /// Check if user has a specific permission
|
||||
29 | pub fn has_permission(&self, permission: &str) -> bool {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
warning: struct `AuthenticatedAgent` is never constructed
|
||||
--> server/src/auth/mod.rs:55:12
|
||||
|
|
||||
55 | pub struct AuthenticatedAgent {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: struct `AuthState` is never constructed
|
||||
--> server/src/auth/mod.rs:62:12
|
||||
|
|
||||
62 | pub struct AuthState {
|
||||
| ^^^^^^^^^
|
||||
|
||||
warning: associated function `new` is never used
|
||||
--> server/src/auth/mod.rs:67:12
|
||||
|
|
||||
66 | impl AuthState {
|
||||
| -------------- associated function in this implementation
|
||||
67 | pub fn new(jwt_secret: String, expiry_hours: i64) -> Self {
|
||||
| ^^^
|
||||
|
||||
warning: struct `OptionalUser` is never constructed
|
||||
--> server/src/auth/mod.rs:112:12
|
||||
|
|
||||
112 | pub struct OptionalUser(pub Option<AuthenticatedUser>);
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: function `validate_agent_key` is never used
|
||||
--> server/src/auth/mod.rs:151:8
|
||||
|
|
||||
151 | pub fn validate_agent_key(_api_key: &str) -> Option<AuthenticatedAgent> {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `default_jwt_secret` is never used
|
||||
--> server/src/auth/jwt.rs:104:8
|
||||
|
|
||||
104 | pub fn default_jwt_secret() -> String {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `list_sessions` is never used
|
||||
--> server/src/api/mod.rs:75:14
|
||||
|
|
||||
75 | pub async fn list_sessions(
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_session` is never used
|
||||
--> server/src/api/mod.rs:83:14
|
||||
|
|
||||
83 | pub async fn get_session(
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: function `update_machine_status` is never used
|
||||
--> server/src/db/machines.rs:51:14
|
||||
|
|
||||
51 | pub async fn update_machine_status(
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_session` is never used
|
||||
--> server/src/db/sessions.rs:67:14
|
||||
|
|
||||
67 | pub async fn get_session(pool: &PgPool, session_id: Uuid) -> Result<Option<DbSession>, sqlx::Error> {
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: function `get_active_sessions_for_machine` is never used
|
||||
--> server/src/db/sessions.rs:75:14
|
||||
|
|
||||
75 | pub async fn get_active_sessions_for_machine(
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_recent_sessions` is never used
|
||||
--> server/src/db/sessions.rs:88:14
|
||||
|
|
||||
88 | pub async fn get_recent_sessions(
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: associated constants `SESSION_TIMEOUT`, `STREAMING_STARTED`, and `STREAMING_STOPPED` are never used
|
||||
--> server/src/db/events.rs:29:15
|
||||
|
|
||||
26 | impl EventTypes {
|
||||
| --------------- associated constants in this implementation
|
||||
...
|
||||
29 | pub const SESSION_TIMEOUT: &'static str = "session_timeout";
|
||||
| ^^^^^^^^^^^^^^^
|
||||
...
|
||||
32 | pub const STREAMING_STARTED: &'static str = "streaming_started";
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
33 | pub const STREAMING_STOPPED: &'static str = "streaming_stopped";
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_session_events` is never used
|
||||
--> server/src/db/events.rs:69:14
|
||||
|
|
||||
69 | pub async fn get_session_events(
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_recent_events` is never used
|
||||
--> server/src/db/events.rs:82:14
|
||||
|
|
||||
82 | pub async fn get_recent_events(
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_events_by_type` is never used
|
||||
--> server/src/db/events.rs:95:14
|
||||
|
|
||||
95 | pub async fn get_events_by_type(
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: struct `DbSupportCode` is never constructed
|
||||
--> server/src/db/support_codes.rs:10:12
|
||||
|
|
||||
10 | pub struct DbSupportCode {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
warning: function `create_support_code` is never used
|
||||
--> server/src/db/support_codes.rs:24:14
|
||||
|
|
||||
24 | pub async fn create_support_code(
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_support_code` is never used
|
||||
--> server/src/db/support_codes.rs:43:14
|
||||
|
|
||||
43 | pub async fn get_support_code(pool: &PgPool, code: &str) -> Result<Option<DbSupportCode>, sqlx::Error> {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `mark_code_cancelled` is never used
|
||||
--> server/src/db/support_codes.rs:90:14
|
||||
|
|
||||
90 | pub async fn mark_code_cancelled(pool: &PgPool, code: &str) -> Result<(), sqlx::Error> {
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_active_support_codes` is never used
|
||||
--> server/src/db/support_codes.rs:99:14
|
||||
|
|
||||
99 | pub async fn get_active_support_codes(pool: &PgPool) -> Result<Vec<DbSupportCode>, sqlx::Error> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `is_code_valid` is never used
|
||||
--> server/src/db/support_codes.rs:108:14
|
||||
|
|
||||
108 | pub async fn is_code_valid(pool: &PgPool, code: &str) -> Result<bool, sqlx::Error> {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
warning: function `is_code_cancelled` is never used
|
||||
--> server/src/db/support_codes.rs:119:14
|
||||
|
|
||||
119 | pub async fn is_code_cancelled(pool: &PgPool, code: &str) -> Result<bool, sqlx::Error> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `link_session_to_code` is never used
|
||||
--> server/src/db/support_codes.rs:130:14
|
||||
|
|
||||
130 | pub async fn link_session_to_code(
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `updated_at` is never read
|
||||
--> server/src/db/users.rs:18:9
|
||||
|
|
||||
10 | pub struct User {
|
||||
| ---- field in this struct
|
||||
...
|
||||
18 | pub updated_at: DateTime<Utc>,
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: `User` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
|
||||
|
||||
warning: struct `UserInfo` is never constructed
|
||||
--> server/src/db/users.rs:24:12
|
||||
|
|
||||
24 | pub struct UserInfo {
|
||||
| ^^^^^^^^
|
||||
|
||||
warning: function `get_user_client_access` is never used
|
||||
--> server/src/db/users.rs:210:14
|
||||
|
|
||||
210 | pub async fn get_user_client_access(pool: &PgPool, user_id: Uuid) -> Result<Vec<Uuid>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `user_has_client_access` is never used
|
||||
--> server/src/db/users.rs:246:14
|
||||
|
|
||||
246 | pub async fn user_has_client_access(
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `technician_id` is never read
|
||||
--> server/src/support_codes.rs:39:9
|
||||
|
|
||||
38 | pub struct CreateCodeRequest {
|
||||
| ----------------- field in this struct
|
||||
39 | pub technician_id: Option<String>,
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `CreateCodeRequest` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
|
||||
|
||||
warning: methods `get_code`, `is_valid_for_connection`, `list_codes`, and `get_by_session` are never used
|
||||
--> server/src/support_codes.rs:166:18
|
||||
|
|
||||
60 | impl SupportCodeManager {
|
||||
| ----------------------- methods in this implementation
|
||||
...
|
||||
166 | pub async fn get_code(&self, code: &str) -> Option<SupportCode> {
|
||||
| ^^^^^^^^
|
||||
...
|
||||
198 | pub async fn is_valid_for_connection(&self, code: &str) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
204 | pub async fn list_codes(&self) -> Vec<SupportCode> {
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
219 | pub async fn get_by_session(&self, session_id: Uuid) -> Option<SupportCode> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
warning: `guruconnect-server` (bin "guruconnect-server") generated 44 warnings (run `cargo fix --bin "guruconnect-server" -p guruconnect-server` to apply 7 suggestions)
|
||||
Finished `release` profile [optimized] target(s) in 15.62s
|
||||
@@ -0,0 +1,487 @@
|
||||
1→//! GuruConnect Server - WebSocket Relay Server
|
||||
2→//!
|
||||
3→//! Handles connections from both agents and dashboard viewers,
|
||||
4→//! relaying video frames and input events between them.
|
||||
5→
|
||||
6→mod config;
|
||||
7→mod relay;
|
||||
8→mod session;
|
||||
9→mod auth;
|
||||
10→mod api;
|
||||
11→mod db;
|
||||
12→mod support_codes;
|
||||
13→
|
||||
14→pub mod proto {
|
||||
15→ include!(concat!(env!("OUT_DIR"), "/guruconnect.rs"));
|
||||
16→}
|
||||
17→
|
||||
18→use anyhow::Result;
|
||||
19→use axum::{
|
||||
20→ Router,
|
||||
21→<<<<<<< HEAD
|
||||
22→ routing::{get, post, delete},
|
||||
23→ extract::{Path, State, Json, Query},
|
||||
24→=======
|
||||
25→ routing::{get, post, put, delete},
|
||||
26→ extract::{Path, State, Json, Request},
|
||||
27→>>>>>>> b861cb1 (Add user management system with JWT authentication)
|
||||
28→ response::{Html, IntoResponse},
|
||||
29→ http::StatusCode,
|
||||
30→ middleware::{self, Next},
|
||||
31→};
|
||||
32→use std::net::SocketAddr;
|
||||
33→use std::sync::Arc;
|
||||
34→use tower_http::cors::{Any, CorsLayer};
|
||||
35→use tower_http::trace::TraceLayer;
|
||||
36→use tower_http::services::ServeDir;
|
||||
37→use tracing::{info, Level};
|
||||
38→use tracing_subscriber::FmtSubscriber;
|
||||
39→use serde::Deserialize;
|
||||
40→
|
||||
41→use support_codes::{SupportCodeManager, CreateCodeRequest, SupportCode, CodeValidation};
|
||||
42→use auth::{JwtConfig, hash_password, generate_random_password};
|
||||
43→
|
||||
44→/// Application state
|
||||
45→#[derive(Clone)]
|
||||
46→pub struct AppState {
|
||||
47→ sessions: session::SessionManager,
|
||||
48→ support_codes: SupportCodeManager,
|
||||
49→ db: Option<db::Database>,
|
||||
50→ pub jwt_config: Arc<JwtConfig>,
|
||||
51→}
|
||||
52→
|
||||
53→/// Middleware to inject JWT config into request extensions
|
||||
54→async fn auth_layer(
|
||||
55→ State(state): State<AppState>,
|
||||
56→ mut request: Request,
|
||||
57→ next: Next,
|
||||
58→) -> impl IntoResponse {
|
||||
59→ request.extensions_mut().insert(state.jwt_config.clone());
|
||||
60→ next.run(request).await
|
||||
61→}
|
||||
62→
|
||||
63→#[tokio::main]
|
||||
64→async fn main() -> Result<()> {
|
||||
65→ // Initialize logging
|
||||
66→ let _subscriber = FmtSubscriber::builder()
|
||||
67→ .with_max_level(Level::INFO)
|
||||
68→ .with_target(true)
|
||||
69→ .init();
|
||||
70→
|
||||
71→ info!("GuruConnect Server v{}", env!("CARGO_PKG_VERSION"));
|
||||
72→
|
||||
73→ // Load configuration
|
||||
74→ let config = config::Config::load()?;
|
||||
75→
|
||||
76→ // Use port 3002 for GuruConnect
|
||||
77→ let listen_addr = std::env::var("LISTEN_ADDR").unwrap_or_else(|_| "0.0.0.0:3002".to_string());
|
||||
78→ info!("Loaded configuration, listening on {}", listen_addr);
|
||||
79→
|
||||
80→ // JWT configuration
|
||||
81→ let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| {
|
||||
82→ tracing::warn!("JWT_SECRET not set, using default (INSECURE for production!)");
|
||||
83→ "guruconnect-dev-secret-change-me-in-production".to_string()
|
||||
84→ });
|
||||
85→ let jwt_expiry_hours = std::env::var("JWT_EXPIRY_HOURS")
|
||||
86→ .ok()
|
||||
87→ .and_then(|s| s.parse().ok())
|
||||
88→ .unwrap_or(24i64);
|
||||
89→ let jwt_config = Arc::new(JwtConfig::new(jwt_secret, jwt_expiry_hours));
|
||||
90→
|
||||
91→ // Initialize database if configured
|
||||
92→ let database = if let Some(ref db_url) = config.database_url {
|
||||
93→ match db::Database::connect(db_url, config.database_max_connections).await {
|
||||
94→ Ok(db) => {
|
||||
95→ // Run migrations
|
||||
96→ if let Err(e) = db.migrate().await {
|
||||
97→ tracing::error!("Failed to run migrations: {}", e);
|
||||
98→ return Err(e);
|
||||
99→ }
|
||||
100→ Some(db)
|
||||
101→ }
|
||||
102→ Err(e) => {
|
||||
103→ tracing::warn!("Failed to connect to database: {}. Running without persistence.", e);
|
||||
104→ None
|
||||
105→ }
|
||||
106→ }
|
||||
107→ } else {
|
||||
108→ info!("No DATABASE_URL set, running without persistence");
|
||||
109→ None
|
||||
110→ };
|
||||
111→
|
||||
112→ // Create initial admin user if no users exist
|
||||
113→ if let Some(ref db) = database {
|
||||
114→ match db::count_users(db.pool()).await {
|
||||
115→ Ok(0) => {
|
||||
116→ info!("No users found, creating initial admin user...");
|
||||
117→ let password = generate_random_password(16);
|
||||
118→ let password_hash = hash_password(&password)?;
|
||||
119→
|
||||
120→ match db::create_user(db.pool(), "admin", &password_hash, None, "admin").await {
|
||||
121→ Ok(user) => {
|
||||
122→ // Set admin permissions
|
||||
123→ let perms = vec![
|
||||
124→ "view".to_string(),
|
||||
125→ "control".to_string(),
|
||||
126→ "transfer".to_string(),
|
||||
127→ "manage_users".to_string(),
|
||||
128→ "manage_clients".to_string(),
|
||||
129→ ];
|
||||
130→ let _ = db::set_user_permissions(db.pool(), user.id, &perms).await;
|
||||
131→
|
||||
132→ info!("========================================");
|
||||
133→ info!(" INITIAL ADMIN USER CREATED");
|
||||
134→ info!(" Username: admin");
|
||||
135→ info!(" Password: {}", password);
|
||||
136→ info!(" (Change this password after first login!)");
|
||||
137→ info!("========================================");
|
||||
138→ }
|
||||
139→ Err(e) => {
|
||||
140→ tracing::error!("Failed to create initial admin user: {}", e);
|
||||
141→ }
|
||||
142→ }
|
||||
143→ }
|
||||
144→ Ok(count) => {
|
||||
145→ info!("{} user(s) in database", count);
|
||||
146→ }
|
||||
147→ Err(e) => {
|
||||
148→ tracing::warn!("Could not check user count: {}", e);
|
||||
149→ }
|
||||
150→ }
|
||||
151→ }
|
||||
152→
|
||||
153→ // Create session manager
|
||||
154→ let sessions = session::SessionManager::new();
|
||||
155→
|
||||
156→ // Restore persistent machines from database
|
||||
157→ if let Some(ref db) = database {
|
||||
158→ match db::machines::get_all_machines(db.pool()).await {
|
||||
159→ Ok(machines) => {
|
||||
160→ info!("Restoring {} persistent machines from database", machines.len());
|
||||
161→ for machine in machines {
|
||||
162→ sessions.restore_offline_machine(&machine.agent_id, &machine.hostname).await;
|
||||
163→ }
|
||||
164→ }
|
||||
165→ Err(e) => {
|
||||
166→ tracing::warn!("Failed to restore machines: {}", e);
|
||||
167→ }
|
||||
168→ }
|
||||
169→ }
|
||||
170→
|
||||
171→ // Create application state
|
||||
172→ let state = AppState {
|
||||
173→ sessions,
|
||||
174→ support_codes: SupportCodeManager::new(),
|
||||
175→ db: database,
|
||||
176→ jwt_config,
|
||||
177→ };
|
||||
178→
|
||||
179→ // Build router
|
||||
180→ let app = Router::new()
|
||||
181→ // Health check (no auth required)
|
||||
182→ .route("/health", get(health))
|
||||
183→
|
||||
184→ // Auth endpoints (no auth required for login)
|
||||
185→ .route("/api/auth/login", post(api::auth::login))
|
||||
186→
|
||||
187→ // Auth endpoints (auth required)
|
||||
188→ .route("/api/auth/me", get(api::auth::get_me))
|
||||
189→ .route("/api/auth/change-password", post(api::auth::change_password))
|
||||
190→
|
||||
191→ // User management (admin only)
|
||||
192→ .route("/api/users", get(api::users::list_users))
|
||||
193→ .route("/api/users", post(api::users::create_user))
|
||||
194→ .route("/api/users/:id", get(api::users::get_user))
|
||||
195→ .route("/api/users/:id", put(api::users::update_user))
|
||||
196→ .route("/api/users/:id", delete(api::users::delete_user))
|
||||
197→ .route("/api/users/:id/permissions", put(api::users::set_permissions))
|
||||
198→ .route("/api/users/:id/clients", put(api::users::set_client_access))
|
||||
199→
|
||||
200→ // Portal API - Support codes
|
||||
201→ .route("/api/codes", post(create_code))
|
||||
202→ .route("/api/codes", get(list_codes))
|
||||
203→ .route("/api/codes/:code/validate", get(validate_code))
|
||||
204→ .route("/api/codes/:code/cancel", post(cancel_code))
|
||||
205→
|
||||
206→ // WebSocket endpoints
|
||||
207→ .route("/ws/agent", get(relay::agent_ws_handler))
|
||||
208→ .route("/ws/viewer", get(relay::viewer_ws_handler))
|
||||
209→
|
||||
210→ // REST API - Sessions
|
||||
211→ .route("/api/sessions", get(list_sessions))
|
||||
212→ .route("/api/sessions/:id", get(get_session))
|
||||
213→ .route("/api/sessions/:id", delete(disconnect_session))
|
||||
214→
|
||||
215→<<<<<<< HEAD
|
||||
216→ // REST API - Machines
|
||||
217→ .route("/api/machines", get(list_machines))
|
||||
218→ .route("/api/machines/:agent_id", get(get_machine))
|
||||
219→ .route("/api/machines/:agent_id", delete(delete_machine))
|
||||
220→ .route("/api/machines/:agent_id/history", get(get_machine_history))
|
||||
221→
|
||||
222→=======
|
||||
223→>>>>>>> b861cb1 (Add user management system with JWT authentication)
|
||||
224→ // HTML page routes (clean URLs)
|
||||
225→ .route("/login", get(serve_login))
|
||||
226→ .route("/dashboard", get(serve_dashboard))
|
||||
227→ .route("/users", get(serve_users))
|
||||
228→
|
||||
229→ // State and middleware
|
||||
230→ .with_state(state.clone())
|
||||
231→ .layer(middleware::from_fn_with_state(state, auth_layer))
|
||||
232→
|
||||
233→ // Serve static files for portal (fallback)
|
||||
234→ .fallback_service(ServeDir::new("static").append_index_html_on_directories(true))
|
||||
235→
|
||||
236→ // Middleware
|
||||
237→ .layer(TraceLayer::new_for_http())
|
||||
238→ .layer(
|
||||
239→ CorsLayer::new()
|
||||
240→ .allow_origin(Any)
|
||||
241→ .allow_methods(Any)
|
||||
242→ .allow_headers(Any),
|
||||
243→ );
|
||||
244→
|
||||
245→ // Start server
|
||||
246→ let addr: SocketAddr = listen_addr.parse()?;
|
||||
247→ let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
248→
|
||||
249→ info!("Server listening on {}", addr);
|
||||
250→
|
||||
251→ axum::serve(listener, app).await?;
|
||||
252→
|
||||
253→ Ok(())
|
||||
254→}
|
||||
255→
|
||||
256→async fn health() -> &'static str {
|
||||
257→ "OK"
|
||||
258→}
|
||||
259→
|
||||
260→// Support code API handlers
|
||||
261→
|
||||
262→async fn create_code(
|
||||
263→ State(state): State<AppState>,
|
||||
264→ Json(request): Json<CreateCodeRequest>,
|
||||
265→) -> Json<SupportCode> {
|
||||
266→ let code = state.support_codes.create_code(request).await;
|
||||
267→ info!("Created support code: {}", code.code);
|
||||
268→ Json(code)
|
||||
269→}
|
||||
270→
|
||||
271→async fn list_codes(
|
||||
272→ State(state): State<AppState>,
|
||||
273→) -> Json<Vec<SupportCode>> {
|
||||
274→ Json(state.support_codes.list_active_codes().await)
|
||||
275→}
|
||||
276→
|
||||
277→#[derive(Deserialize)]
|
||||
278→struct ValidateParams {
|
||||
279→ code: String,
|
||||
280→}
|
||||
281→
|
||||
282→async fn validate_code(
|
||||
283→ State(state): State<AppState>,
|
||||
284→ Path(code): Path<String>,
|
||||
285→) -> Json<CodeValidation> {
|
||||
286→ Json(state.support_codes.validate_code(&code).await)
|
||||
287→}
|
||||
288→
|
||||
289→async fn cancel_code(
|
||||
290→ State(state): State<AppState>,
|
||||
291→ Path(code): Path<String>,
|
||||
292→) -> impl IntoResponse {
|
||||
293→ if state.support_codes.cancel_code(&code).await {
|
||||
294→ (StatusCode::OK, "Code cancelled")
|
||||
295→ } else {
|
||||
296→ (StatusCode::BAD_REQUEST, "Cannot cancel code")
|
||||
297→ }
|
||||
298→}
|
||||
299→
|
||||
300→// Session API handlers (updated to use AppState)
|
||||
301→
|
||||
302→async fn list_sessions(
|
||||
303→ State(state): State<AppState>,
|
||||
304→) -> Json<Vec<api::SessionInfo>> {
|
||||
305→ let sessions = state.sessions.list_sessions().await;
|
||||
306→ Json(sessions.into_iter().map(api::SessionInfo::from).collect())
|
||||
307→}
|
||||
308→
|
||||
309→async fn get_session(
|
||||
310→ State(state): State<AppState>,
|
||||
311→ Path(id): Path<String>,
|
||||
312→) -> Result<Json<api::SessionInfo>, (StatusCode, &'static str)> {
|
||||
313→ let session_id = uuid::Uuid::parse_str(&id)
|
||||
314→ .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid session ID"))?;
|
||||
315→
|
||||
316→ let session = state.sessions.get_session(session_id).await
|
||||
317→ .ok_or((StatusCode::NOT_FOUND, "Session not found"))?;
|
||||
318→
|
||||
319→ Ok(Json(api::SessionInfo::from(session)))
|
||||
320→}
|
||||
321→
|
||||
322→async fn disconnect_session(
|
||||
323→ State(state): State<AppState>,
|
||||
324→ Path(id): Path<String>,
|
||||
325→) -> impl IntoResponse {
|
||||
326→ let session_id = match uuid::Uuid::parse_str(&id) {
|
||||
327→ Ok(id) => id,
|
||||
328→ Err(_) => return (StatusCode::BAD_REQUEST, "Invalid session ID"),
|
||||
329→ };
|
||||
330→
|
||||
331→ if state.sessions.disconnect_session(session_id, "Disconnected by administrator").await {
|
||||
332→ info!("Session {} disconnected by admin", session_id);
|
||||
333→ (StatusCode::OK, "Session disconnected")
|
||||
334→ } else {
|
||||
335→ (StatusCode::NOT_FOUND, "Session not found")
|
||||
336→ }
|
||||
337→}
|
||||
338→
|
||||
339→// Machine API handlers
|
||||
340→
|
||||
341→async fn list_machines(
|
||||
342→ State(state): State<AppState>,
|
||||
343→) -> Result<Json<Vec<api::MachineInfo>>, (StatusCode, &'static str)> {
|
||||
344→ let db = state.db.as_ref()
|
||||
345→ .ok_or((StatusCode::SERVICE_UNAVAILABLE, "Database not available"))?;
|
||||
346→
|
||||
347→ let machines = db::machines::get_all_machines(db.pool()).await
|
||||
348→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?;
|
||||
349→
|
||||
350→ Ok(Json(machines.into_iter().map(api::MachineInfo::from).collect()))
|
||||
351→}
|
||||
352→
|
||||
353→async fn get_machine(
|
||||
354→ State(state): State<AppState>,
|
||||
355→ Path(agent_id): Path<String>,
|
||||
356→) -> Result<Json<api::MachineInfo>, (StatusCode, &'static str)> {
|
||||
357→ let db = state.db.as_ref()
|
||||
358→ .ok_or((StatusCode::SERVICE_UNAVAILABLE, "Database not available"))?;
|
||||
359→
|
||||
360→ let machine = db::machines::get_machine_by_agent_id(db.pool(), &agent_id).await
|
||||
361→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?
|
||||
362→ .ok_or((StatusCode::NOT_FOUND, "Machine not found"))?;
|
||||
363→
|
||||
364→ Ok(Json(api::MachineInfo::from(machine)))
|
||||
365→}
|
||||
366→
|
||||
367→async fn get_machine_history(
|
||||
368→ State(state): State<AppState>,
|
||||
369→ Path(agent_id): Path<String>,
|
||||
370→) -> Result<Json<api::MachineHistory>, (StatusCode, &'static str)> {
|
||||
371→ let db = state.db.as_ref()
|
||||
372→ .ok_or((StatusCode::SERVICE_UNAVAILABLE, "Database not available"))?;
|
||||
373→
|
||||
374→ // Get machine
|
||||
375→ let machine = db::machines::get_machine_by_agent_id(db.pool(), &agent_id).await
|
||||
376→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?
|
||||
377→ .ok_or((StatusCode::NOT_FOUND, "Machine not found"))?;
|
||||
378→
|
||||
379→ // Get sessions for this machine
|
||||
380→ let sessions = db::sessions::get_sessions_for_machine(db.pool(), machine.id).await
|
||||
381→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?;
|
||||
382→
|
||||
383→ // Get events for this machine
|
||||
384→ let events = db::events::get_events_for_machine(db.pool(), machine.id).await
|
||||
385→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?;
|
||||
386→
|
||||
387→ let history = api::MachineHistory {
|
||||
388→ machine: api::MachineInfo::from(machine),
|
||||
389→ sessions: sessions.into_iter().map(api::SessionRecord::from).collect(),
|
||||
390→ events: events.into_iter().map(api::EventRecord::from).collect(),
|
||||
391→ exported_at: chrono::Utc::now().to_rfc3339(),
|
||||
392→ };
|
||||
393→
|
||||
394→ Ok(Json(history))
|
||||
395→}
|
||||
396→
|
||||
397→async fn delete_machine(
|
||||
398→ State(state): State<AppState>,
|
||||
399→ Path(agent_id): Path<String>,
|
||||
400→ Query(params): Query<api::DeleteMachineParams>,
|
||||
401→) -> Result<Json<api::DeleteMachineResponse>, (StatusCode, &'static str)> {
|
||||
402→ let db = state.db.as_ref()
|
||||
403→ .ok_or((StatusCode::SERVICE_UNAVAILABLE, "Database not available"))?;
|
||||
404→
|
||||
405→ // Get machine first
|
||||
406→ let machine = db::machines::get_machine_by_agent_id(db.pool(), &agent_id).await
|
||||
407→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?
|
||||
408→ .ok_or((StatusCode::NOT_FOUND, "Machine not found"))?;
|
||||
409→
|
||||
410→ // Export history if requested
|
||||
411→ let history = if params.export {
|
||||
412→ let sessions = db::sessions::get_sessions_for_machine(db.pool(), machine.id).await
|
||||
413→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?;
|
||||
414→ let events = db::events::get_events_for_machine(db.pool(), machine.id).await
|
||||
415→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error"))?;
|
||||
416→
|
||||
417→ Some(api::MachineHistory {
|
||||
418→ machine: api::MachineInfo::from(machine.clone()),
|
||||
419→ sessions: sessions.into_iter().map(api::SessionRecord::from).collect(),
|
||||
420→ events: events.into_iter().map(api::EventRecord::from).collect(),
|
||||
421→ exported_at: chrono::Utc::now().to_rfc3339(),
|
||||
422→ })
|
||||
423→ } else {
|
||||
424→ None
|
||||
425→ };
|
||||
426→
|
||||
427→ // Send uninstall command if requested and agent is online
|
||||
428→ let mut uninstall_sent = false;
|
||||
429→ if params.uninstall {
|
||||
430→ // Find session for this agent
|
||||
431→ if let Some(session) = state.sessions.get_session_by_agent(&agent_id).await {
|
||||
432→ if session.is_online {
|
||||
433→ uninstall_sent = state.sessions.send_admin_command(
|
||||
434→ session.id,
|
||||
435→ proto::AdminCommandType::AdminUninstall,
|
||||
436→ "Deleted by administrator",
|
||||
437→ ).await;
|
||||
438→ if uninstall_sent {
|
||||
439→ info!("Sent uninstall command to agent {}", agent_id);
|
||||
440→ }
|
||||
441→ }
|
||||
442→ }
|
||||
443→ }
|
||||
444→
|
||||
445→ // Remove from session manager
|
||||
446→ state.sessions.remove_agent(&agent_id).await;
|
||||
447→
|
||||
448→ // Delete from database (cascades to sessions and events)
|
||||
449→ db::machines::delete_machine(db.pool(), &agent_id).await
|
||||
450→ .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Failed to delete machine"))?;
|
||||
451→
|
||||
452→ info!("Deleted machine {} (uninstall_sent: {})", agent_id, uninstall_sent);
|
||||
453→
|
||||
454→ Ok(Json(api::DeleteMachineResponse {
|
||||
455→ success: true,
|
||||
456→ message: format!("Machine {} deleted", machine.hostname),
|
||||
457→ uninstall_sent,
|
||||
458→ history,
|
||||
459→ }))
|
||||
460→}
|
||||
461→
|
||||
462→// Static page handlers
|
||||
463→async fn serve_login() -> impl IntoResponse {
|
||||
464→ match tokio::fs::read_to_string("static/login.html").await {
|
||||
465→ Ok(content) => Html(content).into_response(),
|
||||
466→ Err(_) => (StatusCode::NOT_FOUND, "Page not found").into_response(),
|
||||
467→ }
|
||||
468→}
|
||||
469→
|
||||
470→async fn serve_dashboard() -> impl IntoResponse {
|
||||
471→ match tokio::fs::read_to_string("static/dashboard.html").await {
|
||||
472→ Ok(content) => Html(content).into_response(),
|
||||
473→ Err(_) => (StatusCode::NOT_FOUND, "Page not found").into_response(),
|
||||
474→ }
|
||||
475→}
|
||||
476→
|
||||
477→async fn serve_users() -> impl IntoResponse {
|
||||
478→ match tokio::fs::read_to_string("static/users.html").await {
|
||||
479→ Ok(content) => Html(content).into_response(),
|
||||
480→ Err(_) => (StatusCode::NOT_FOUND, "Page not found").into_response(),
|
||||
481→ }
|
||||
482→}
|
||||
483→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1,3 @@
|
||||
Exit code 1
|
||||
Access denied
|
||||
FATAL ERROR: Configured password was not accepted
|
||||
@@ -0,0 +1,556 @@
|
||||
1→# Credentials & Authorization Reference
|
||||
2→**Last Updated:** 2025-12-16
|
||||
3→**Purpose:** Centralized credentials for Claude Code context recovery across all machines
|
||||
4→
|
||||
5→---
|
||||
6→
|
||||
7→## Infrastructure - SSH Access
|
||||
8→
|
||||
9→### Jupiter (Unraid Primary)
|
||||
10→- **Host:** 172.16.3.20
|
||||
11→- **User:** root
|
||||
12→- **Port:** 22
|
||||
13→- **Password:** Th1nk3r^99##
|
||||
14→- **WebUI Password:** Th1nk3r^99##
|
||||
15→- **Role:** Primary container host (Gitea, NPM, GuruRMM, media)
|
||||
16→- **iDRAC IP:** 172.16.1.73 (DHCP)
|
||||
17→- **iDRAC User:** root
|
||||
18→- **iDRAC Password:** Window123!@#-idrac
|
||||
19→- **iDRAC SSH:** Enabled (port 22)
|
||||
20→- **IPMI Key:** All zeros
|
||||
21→
|
||||
22→### Saturn (Unraid Secondary)
|
||||
23→- **Host:** 172.16.3.21
|
||||
24→- **User:** root
|
||||
25→- **Port:** 22
|
||||
26→- **Password:** r3tr0gradE99
|
||||
27→- **Role:** Migration source, being consolidated to Jupiter
|
||||
28→
|
||||
29→### pfSense (Firewall)
|
||||
30→- **Host:** 172.16.0.1
|
||||
31→- **User:** admin
|
||||
32→- **Port:** 2248
|
||||
33→- **Password:** r3tr0gradE99!!
|
||||
34→- **Role:** Firewall, Tailscale gateway
|
||||
35→- **Tailscale IP:** 100.79.69.82 (pfsense-1)
|
||||
36→
|
||||
37→### OwnCloud VM (on Jupiter)
|
||||
38→- **Host:** 172.16.3.22
|
||||
39→- **Hostname:** cloud.acghosting.com
|
||||
40→- **User:** root
|
||||
41→- **Port:** 22
|
||||
42→- **Password:** Paper123!@#-unifi!
|
||||
43→- **OS:** Rocky Linux 9.6
|
||||
44→- **Role:** OwnCloud file sync server
|
||||
45→- **Services:** Apache, MariaDB, PHP-FPM, Redis, Datto RMM agents
|
||||
46→- **Storage:** SMB mount from Jupiter (/mnt/user/OwnCloud)
|
||||
47→- **Note:** Jupiter has SSH key auth configured
|
||||
48→
|
||||
49→### GuruRMM Build Server
|
||||
50→- **Host:** 172.16.3.30
|
||||
51→- **Hostname:** gururmm
|
||||
52→- **User:** guru
|
||||
53→- **Port:** 22
|
||||
54→- **Password:** Gptf*77ttb123!@#-rmm
|
||||
55→- **Sudo Password:** Gptf*77ttb123!@#-rmm
|
||||
56→- **OS:** Ubuntu 22.04
|
||||
57→- **Role:** GuruRMM dedicated server (API, DB, Dashboard, Downloads)
|
||||
58→- **Services:** nginx, PostgreSQL, gururmm-server, gururmm-agent
|
||||
59→- **Note:** WSL has SSH key auth configured; sudo requires heredoc for password with special chars
|
||||
60→
|
||||
61→---
|
||||
62→
|
||||
63→## Services - Web Applications
|
||||
64→
|
||||
65→### Gitea (Git Server)
|
||||
66→- **URL:** https://git.azcomputerguru.com/
|
||||
67→- **Internal:** http://172.16.3.20:3000
|
||||
68→- **SSH:** ssh://git@172.16.3.20:2222
|
||||
69→- **User:** mike@azcomputerguru.com
|
||||
70→- **Password:** Window123!@#-git
|
||||
71→- **API Token:** 9b1da4b79a38ef782268341d25a4b6880572063f
|
||||
72→
|
||||
73→### NPM (Nginx Proxy Manager)
|
||||
74→- **Admin URL:** http://172.16.3.20:7818
|
||||
75→- **HTTP Port:** 1880
|
||||
76→- **HTTPS Port:** 18443
|
||||
77→- **User:** mike@azcomputerguru.com
|
||||
78→- **Password:** Paper123!@#-unifi
|
||||
79→
|
||||
80→### Cloudflare
|
||||
81→- **API Token (Full DNS):** DRRGkHS33pxAUjQfRDzDeVPtt6wwUU6FwtXqOzNj
|
||||
82→- **API Token (Legacy/Limited):** U1UTbBOWA4a69eWEBiqIbYh0etCGzrpTU4XaKp7w
|
||||
83→- **Permissions:** Zone:Read, Zone:Edit, DNS:Read, DNS:Edit
|
||||
84→- **Used for:** DNS management, WHM plugin, cf-dns CLI
|
||||
85→- **Domain:** azcomputerguru.com
|
||||
86→- **Notes:** New full-access token added 2025-12-19
|
||||
87→
|
||||
88→---
|
||||
89→
|
||||
90→## Projects - GuruRMM
|
||||
91→
|
||||
92→### Dashboard/API Login
|
||||
93→- **Email:** admin@azcomputerguru.com
|
||||
94→- **Password:** GuruRMM2025
|
||||
95→- **Role:** admin
|
||||
96→
|
||||
97→### Database (PostgreSQL)
|
||||
98→- **Host:** gururmm-db container (172.16.3.20)
|
||||
99→- **Database:** gururmm
|
||||
100→- **User:** gururmm
|
||||
101→- **Password:** 43617ebf7eb242e814ca9988cc4df5ad
|
||||
102→
|
||||
103→### API Server
|
||||
104→- **External URL:** https://rmm-api.azcomputerguru.com
|
||||
105→- **Internal URL:** http://172.16.3.20:3001
|
||||
106→- **JWT Secret:** ZNzGxghru2XUdBVlaf2G2L1YUBVcl5xH0lr/Gpf/QmE=
|
||||
107→
|
||||
108→### Microsoft Entra ID (SSO)
|
||||
109→- **App Name:** GuruRMM Dashboard
|
||||
110→- **App ID (Client ID):** 18a15f5d-7ab8-46f4-8566-d7b5436b84b6
|
||||
111→- **Object ID:** 34c80aa8-385a-4bea-af85-f8bf67decc8f
|
||||
112→- **Client Secret:** gOz8Q~J.oz7KnUIEpzmHOyJ6GEzYNecGRl-Pbc9w
|
||||
113→- **Secret Expires:** 2026-12-21
|
||||
114→- **Sign-in Audience:** Multi-tenant (any Azure AD org)
|
||||
115→- **Redirect URIs:** https://rmm.azcomputerguru.com/auth/callback, http://localhost:5173/auth/callback
|
||||
116→- **API Permissions:** openid, email, profile
|
||||
117→- **Notes:** Created 2025-12-21 for GuruRMM SSO
|
||||
118→
|
||||
119→### CI/CD (Build Automation)
|
||||
120→- **Webhook URL:** http://172.16.3.30/webhook/build
|
||||
121→- **Webhook Secret:** gururmm-build-secret
|
||||
122→- **Build Script:** /opt/gururmm/build-agents.sh
|
||||
123→- **Build Log:** /var/log/gururmm-build.log
|
||||
124→- **Gitea Webhook ID:** 1
|
||||
125→- **Trigger:** Push to main branch
|
||||
126→- **Builds:** Linux (x86_64) and Windows (x86_64) agents
|
||||
127→- **Deploy Path:** /var/www/gururmm/downloads/
|
||||
128→
|
||||
129→### Build Server SSH Key (for Gitea)
|
||||
130→- **Key Name:** gururmm-build-server
|
||||
131→- **Public Key:**
|
||||
132→```
|
||||
133→ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKSqf2/phEXUK8vd5GhMIDTEGSk0LvYk92sRdNiRrjKi guru@gururmm-build
|
||||
134→```
|
||||
135→- **Added to:** Gitea (azcomputerguru account)
|
||||
136→
|
||||
137→### Clients & Sites
|
||||
138→#### Glaztech Industries (GLAZ)
|
||||
139→- **Client ID:** d857708c-5713-4ee5-a314-679f86d2f9f9
|
||||
140→- **Site:** SLC - Salt Lake City
|
||||
141→- **Site ID:** 290bd2ea-4af5-49c6-8863-c6d58c5a55de
|
||||
142→- **Site Code:** DARK-GROVE-7839
|
||||
143→- **API Key:** grmm_Qw64eawPBjnMdwN5UmDGWoPlqwvjM7lI
|
||||
144→- **Created:** 2025-12-18
|
||||
145→
|
||||
146→---
|
||||
147→
|
||||
148→## Client Sites - WHM/cPanel
|
||||
149→
|
||||
150→### IX Server (ix.azcomputerguru.com)
|
||||
151→- **SSH Host:** ix.azcomputerguru.com
|
||||
152→- **Internal IP:** 172.16.3.10 (VPN required)
|
||||
153→- **SSH User:** root
|
||||
154→- **SSH Password:** Gptf*77ttb!@#!@#
|
||||
155→- **SSH Key:** guru@wsl key added to authorized_keys
|
||||
156→- **Role:** cPanel/WHM server hosting client sites
|
||||
157→
|
||||
158→### WebSvr (websvr.acghosting.com)
|
||||
159→- **Host:** websvr.acghosting.com
|
||||
160→- **SSH User:** root
|
||||
161→- **SSH Password:** r3tr0gradE99#
|
||||
162→- **API Token:** 8ZPYVM6R0RGOHII7EFF533MX6EQ17M7O
|
||||
163→- **Access Level:** Full access
|
||||
164→- **Role:** Legacy cPanel/WHM server (migration source to IX)
|
||||
165→
|
||||
166→### data.grabbanddurando.com
|
||||
167→- **Server:** IX (ix.azcomputerguru.com)
|
||||
168→- **cPanel Account:** grabblaw
|
||||
169→- **Site Path:** /home/grabblaw/public_html/data_grabbanddurando
|
||||
170→- **Site Admin User:** admin
|
||||
171→- **Site Admin Password:** GND-Paper123!@#-datasite
|
||||
172→- **Database:** grabblaw_gdapp_data
|
||||
173→- **DB User:** grabblaw_gddata
|
||||
174→- **DB Password:** GrabbData2025
|
||||
175→- **Config File:** /home/grabblaw/public_html/data_grabbanddurando/connection.php
|
||||
176→- **Backups:** /home/grabblaw/public_html/data_grabbanddurando/backups_mariadb_fix/
|
||||
177→
|
||||
178→### GoDaddy VPS (Legacy)
|
||||
179→- **IP:** 208.109.235.224
|
||||
180→- **Hostname:** 224.235.109.208.host.secureserver.net
|
||||
181→- **Auth:** SSH key
|
||||
182→- **Database:** grabblaw_gdapp
|
||||
183→- **Note:** Old server, data migrated to IX
|
||||
184→
|
||||
185→---
|
||||
186→
|
||||
187→## Seafile (on Jupiter - Migrated 2025-12-27)
|
||||
188→
|
||||
189→### Container
|
||||
190→- **Host:** Jupiter (172.16.3.20)
|
||||
191→- **URL:** https://sync.azcomputerguru.com
|
||||
192→- **Port:** 8082 (internal), proxied via NPM
|
||||
193→- **Containers:** seafile, seafile-mysql, seafile-memcached, seafile-elasticsearch
|
||||
194→- **Docker Compose:** /mnt/user0/SeaFile/DockerCompose/docker-compose.yml
|
||||
195→- **Data Path:** /mnt/user0/SeaFile/seafile-data/
|
||||
196→
|
||||
197→### Seafile Admin
|
||||
198→- **Email:** mike@azcomputerguru.com
|
||||
199→- **Password:** r3tr0gradE99#
|
||||
200→
|
||||
201→### Database (MariaDB)
|
||||
202→- **Container:** seafile-mysql
|
||||
203→- **Image:** mariadb:10.6
|
||||
204→- **Root Password:** db_dev
|
||||
205→- **Seafile User:** seafile
|
||||
206→- **Seafile Password:** 64f2db5e-6831-48ed-a243-d4066fe428f9
|
||||
207→- **Databases:** ccnet_db (users), seafile_db (data), seahub_db (web)
|
||||
208→
|
||||
209→### Elasticsearch
|
||||
210→- **Container:** seafile-elasticsearch
|
||||
211→- **Image:** elasticsearch:7.17.26
|
||||
212→- **Note:** Upgraded from 7.16.2 for kernel 6.12 compatibility
|
||||
213→
|
||||
214→### Microsoft Graph API (Email)
|
||||
215→- **Tenant ID:** ce61461e-81a0-4c84-bb4a-7b354a9a356d
|
||||
216→- **Client ID:** 15b0fafb-ab51-4cc9-adc7-f6334c805c22
|
||||
217→- **Client Secret:** rRN8Q~FPfSL8O24iZthi_LVJTjGOCZG.DnxGHaSk
|
||||
218→- **Sender Email:** noreply@azcomputerguru.com
|
||||
219→- **Used for:** Seafile email notifications via Graph API
|
||||
220→
|
||||
221→### Migration Notes
|
||||
222→- **Migrated from:** Saturn (172.16.3.21) on 2025-12-27
|
||||
223→- **Saturn Status:** Seafile stopped, data intact for rollback (keep 1 week)
|
||||
224→
|
||||
225→---
|
||||
226→
|
||||
227→## NPM Proxy Hosts Reference
|
||||
228→
|
||||
229→| ID | Domain | Backend | SSL Cert |
|
||||
230→|----|--------|---------|----------|
|
||||
231→| 1 | emby.azcomputerguru.com | 172.16.2.99:8096 | npm-1 |
|
||||
232→| 2 | git.azcomputerguru.com | 172.16.3.20:3000 | npm-2 |
|
||||
233→| 4 | plexrequest.azcomputerguru.com | 172.16.3.31:5055 | npm-4 |
|
||||
234→| 5 | rmm-api.azcomputerguru.com | 172.16.3.20:3001 | npm-6 |
|
||||
235→| - | unifi.azcomputerguru.com | 172.16.3.28:8443 | npm-5 |
|
||||
236→| 8 | sync.azcomputerguru.com | 172.16.3.20:8082 | npm-8 |
|
||||
237→
|
||||
238→---
|
||||
239→
|
||||
240→## Tailscale Network
|
||||
241→
|
||||
242→| Tailscale IP | Hostname | Owner | OS |
|
||||
243→|--------------|----------|-------|-----|
|
||||
244→| 100.79.69.82 (pfsense-1) | pfsense | mike@ | freebsd |
|
||||
245→| 100.125.36.6 | acg-m-l5090 | mike@ | windows |
|
||||
246→| 100.92.230.111 | acg-tech-01l | mike@ | windows |
|
||||
247→| 100.96.135.117 | acg-tech-02l | mike@ | windows |
|
||||
248→| 100.113.45.7 | acg-tech03l | howard@ | windows |
|
||||
249→| 100.77.166.22 | desktop-hjfjtep | mike@ | windows |
|
||||
250→| 100.101.145.100 | guru-legion9 | mike@ | windows |
|
||||
251→| 100.119.194.51 | guru-surface8 | howard@ | windows |
|
||||
252→| 100.66.103.110 | magus-desktop | rob@ | windows |
|
||||
253→| 100.66.167.120 | magus-pc | rob@ | windows |
|
||||
254→
|
||||
255→---
|
||||
256→
|
||||
257→## SSH Public Keys
|
||||
258→
|
||||
259→### guru@wsl (Windows/WSL)
|
||||
260→- **User:** guru
|
||||
261→- **Sudo Password:** Window123!@#-wsl
|
||||
262→- **SSH Key:**
|
||||
263→```
|
||||
264→ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAWY+SdqMHJP5JOe3qpWENQZhXJA4tzI2d7ZVNAwA/1u guru@wsl
|
||||
265→```
|
||||
266→
|
||||
267→### azcomputerguru@local (Mac)
|
||||
268→- **User:** azcomputerguru
|
||||
269→- **SSH Key:**
|
||||
270→```
|
||||
271→ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDrGbr4EwvQ4P3ZtyZW3ZKkuDQOMbqyAQUul2+JE4K4S azcomputerguru@local
|
||||
272→```
|
||||
273→
|
||||
274→---
|
||||
275→
|
||||
276→## Quick Reference Commands
|
||||
277→
|
||||
278→### NPM API Auth
|
||||
279→```bash
|
||||
280→curl -s -X POST http://172.16.3.20:7818/api/tokens \
|
||||
281→ -H "Content-Type: application/json" \
|
||||
282→ -d '{"identity":"mike@azcomputerguru.com","secret":"Paper123!@#-unifi"}'
|
||||
283→```
|
||||
284→
|
||||
285→### Gitea API
|
||||
286→```bash
|
||||
287→curl -H "Authorization: token 9b1da4b79a38ef782268341d25a4b6880572063f" \
|
||||
288→ https://git.azcomputerguru.com/api/v1/repos/search
|
||||
289→```
|
||||
290→
|
||||
291→### GuruRMM Health Check
|
||||
292→```bash
|
||||
293→curl http://172.16.3.20:3001/health
|
||||
294→```
|
||||
295→
|
||||
296→---
|
||||
297→
|
||||
298→## MSP Tools
|
||||
299→
|
||||
300→### Syncro (PSA/RMM) - AZ Computer Guru
|
||||
301→- **API Key:** T259810e5c9917386b-52c2aeea7cdb5ff41c6685a73cebbeb3
|
||||
302→- **Subdomain:** computerguru
|
||||
303→- **API Base URL:** https://computerguru.syncromsp.com/api/v1
|
||||
304→- **API Docs:** https://api-docs.syncromsp.com/
|
||||
305→- **Account:** AZ Computer Guru MSP
|
||||
306→- **Notes:** Added 2025-12-18
|
||||
307→
|
||||
308→### Autotask (PSA) - AZ Computer Guru
|
||||
309→- **API Username:** dguyqap2nucge6r@azcomputerguru.com
|
||||
310→- **API Password:** z*6G4fT#oM~8@9Hxy$2Y7K$ma
|
||||
311→- **API Integration Code:** HYTYYZ6LA5HB5XK7IGNA7OAHQLH
|
||||
312→- **Integration Name:** ClaudeAPI
|
||||
313→- **API Zone:** webservices5.autotask.net
|
||||
314→- **API Docs:** https://autotask.net/help/developerhelp/Content/APIs/REST/REST_API_Home.htm
|
||||
315→- **Account:** AZ Computer Guru MSP
|
||||
316→- **Notes:** Added 2025-12-18, new API user "Claude API"
|
||||
317→
|
||||
318→### CIPP (CyberDrain Improved Partner Portal)
|
||||
319→- **URL:** https://cippcanvb.azurewebsites.net
|
||||
320→- **Tenant ID:** ce61461e-81a0-4c84-bb4a-7b354a9a356d
|
||||
321→- **API Client Name:** ClaudeCipp2 (working)
|
||||
322→- **App ID (Client ID):** 420cb849-542d-4374-9cb2-3d8ae0e1835b
|
||||
323→- **Client Secret:** MOn8Q~otmxJPLvmL~_aCVTV8Va4t4~SrYrukGbJT
|
||||
324→- **Scope:** api://420cb849-542d-4374-9cb2-3d8ae0e1835b/.default
|
||||
325→- **CIPP-SAM App ID:** 91b9102d-bafd-43f8-b17a-f99479149b07
|
||||
326→- **IP Range:** 0.0.0.0/0 (all IPs allowed)
|
||||
327→- **Auth Method:** OAuth 2.0 Client Credentials
|
||||
328→- **Notes:** Updated 2025-12-23, working API client
|
||||
329→
|
||||
330→#### CIPP API Usage (Bash)
|
||||
331→```bash
|
||||
332→# Get token
|
||||
333→ACCESS_TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/ce61461e-81a0-4c84-bb4a-7b354a9a356d/oauth2/v2.0/token" \
|
||||
334→ -d "client_id=420cb849-542d-4374-9cb2-3d8ae0e1835b" \
|
||||
335→ -d "client_secret=MOn8Q~otmxJPLvmL~_aCVTV8Va4t4~SrYrukGbJT" \
|
||||
336→ -d "scope=api://420cb849-542d-4374-9cb2-3d8ae0e1835b/.default" \
|
||||
337→ -d "grant_type=client_credentials" | python3 -c "import sys, json; print(json.load(sys.stdin).get('access_token', ''))")
|
||||
338→
|
||||
339→# Query endpoints (use tenant domain or tenant ID as TenantFilter)
|
||||
340→curl -s "https://cippcanvb.azurewebsites.net/api/ListLicenses?TenantFilter=sonorangreenllc.com" \
|
||||
341→ -H "Authorization: Bearer ${ACCESS_TOKEN}"
|
||||
342→
|
||||
343→# Other useful endpoints:
|
||||
344→# ListTenants?AllTenants=true - List all managed tenants
|
||||
345→# ListUsers?TenantFilter={tenant} - List users
|
||||
346→# ListMailboxRules?TenantFilter={tenant} - Check mailbox rules
|
||||
347→# BECCheck?TenantFilter={tenant}&UserID={userid} - BEC investigation
|
||||
348→```
|
||||
349→
|
||||
350→#### Old API Client (403 errors - do not use)
|
||||
351→- **App ID:** d545a836-7118-44f6-8852-d9dd64fb7bb9
|
||||
352→- **Status:** Authenticated but all endpoints returned 403
|
||||
353→
|
||||
354→---
|
||||
355→
|
||||
356→## Client - MVAN Inc
|
||||
357→
|
||||
358→### Microsoft 365 Tenant 1
|
||||
359→- **Tenant:** mvan.onmicrosoft.com
|
||||
360→- **Admin User:** sysadmin@mvaninc.com
|
||||
361→- **Password:** r3tr0gradE99#
|
||||
362→- **Notes:** Global admin, project to merge/trust with T2
|
||||
363→
|
||||
364→---
|
||||
365→
|
||||
366→## Client - BG Builders LLC
|
||||
367→
|
||||
368→### Microsoft 365 Tenant
|
||||
369→- **Tenant:** bgbuildersllc.com
|
||||
370→- **CIPP Name:** sonorangreenllc.com
|
||||
371→- **Tenant ID:** ededa4fb-f6eb-4398-851d-5eb3e11fab27
|
||||
372→- **Admin User:** sysadmin@bgbuildersllc.com
|
||||
373→- **Password:** Window123!@#-bgb
|
||||
374→- **Notes:** Added 2025-12-19
|
||||
375→
|
||||
376→### Security Investigation (2025-12-22)
|
||||
377→- **Compromised User:** Shelly@bgbuildersllc.com (Shelly Dooley)
|
||||
378→- **Symptoms:** Suspicious sent items reported by user
|
||||
379→- **Findings:**
|
||||
380→ - Gmail OAuth app with EAS.AccessAsUser.All (REMOVED)
|
||||
381→ - "P2P Server" app registration backdoor (DELETED by admin)
|
||||
382→ - No malicious mailbox rules or forwarding
|
||||
383→ - Sign-in logs unavailable (no Entra P1 license)
|
||||
384→- **Remediation:**
|
||||
385→ - Password reset: `5ecwyHv6&dP7` (must change on login)
|
||||
386→ - All sessions revoked
|
||||
387→ - Gmail OAuth consent removed
|
||||
388→ - P2P Server backdoor deleted
|
||||
389→- **Status:** RESOLVED
|
||||
390→
|
||||
391→---
|
||||
392→
|
||||
393→## Client - Dataforth
|
||||
394→
|
||||
395→### Network
|
||||
396→- **Subnet:** 192.168.0.0/24
|
||||
397→- **Domain:** INTRANET (intranet.dataforth.com)
|
||||
398→
|
||||
399→### UDM (Unifi Dream Machine)
|
||||
400→- **IP:** 192.168.0.254
|
||||
401→- **SSH User:** root
|
||||
402→- **SSH Password:** Paper123!@#-unifi
|
||||
403→- **Web User:** azcomputerguru
|
||||
404→- **Web Password:** Paper123!@#-unifi
|
||||
405→- **2FA:** Push notification enabled
|
||||
406→- **Notes:** Gateway/firewall, OpenVPN server
|
||||
407→
|
||||
408→### AD1 (Domain Controller)
|
||||
409→- **IP:** 192.168.0.27
|
||||
410→- **Hostname:** AD1.intranet.dataforth.com
|
||||
411→- **User:** INTRANET\sysadmin
|
||||
412→- **Password:** Paper123!@#
|
||||
413→- **Role:** Primary DC, NPS/RADIUS server
|
||||
414→- **NPS Ports:** 1812/1813 (auth/accounting)
|
||||
415→
|
||||
416→### AD2 (Domain Controller)
|
||||
417→- **IP:** 192.168.0.6
|
||||
418→- **Hostname:** AD2.intranet.dataforth.com
|
||||
419→- **User:** INTRANET\sysadmin
|
||||
420→- **Password:** Paper123!@#
|
||||
421→- **Role:** Secondary DC, file server
|
||||
422→
|
||||
423→### NPS RADIUS Configuration
|
||||
424→- **Client Name:** unifi
|
||||
425→- **Client IP:** 192.168.0.254
|
||||
426→- **Shared Secret:** Gptf*77ttb!@#!@#
|
||||
427→- **Policy:** "Unifi" - allows Domain Users
|
||||
428→
|
||||
429→### D2TESTNAS (SMB1 Proxy)
|
||||
430→- **IP:** 192.168.0.9
|
||||
431→- **Web/SSH User:** admin
|
||||
432→- **Web/SSH Password:** Paper123!@#-nas
|
||||
433→- **Role:** DOS machine SMB1 proxy
|
||||
434→- **Notes:** Added 2025-12-14
|
||||
435→
|
||||
436→---
|
||||
437→
|
||||
438→## Client - Valley Wide Plastering
|
||||
439→
|
||||
440→### Network
|
||||
441→- **Subnet:** 172.16.9.0/24
|
||||
442→
|
||||
443→### UDM (UniFi Dream Machine)
|
||||
444→- **IP:** 172.16.9.1
|
||||
445→- **SSH User:** root
|
||||
446→- **SSH Password:** Gptf*77ttb123!@#-vwp
|
||||
447→- **Notes:** Gateway/firewall, VPN server, RADIUS client
|
||||
448→
|
||||
449→### VWP-DC1 (Domain Controller)
|
||||
450→- **IP:** 172.16.9.2
|
||||
451→- **Hostname:** VWP-DC1
|
||||
452→- **User:** sysadmin
|
||||
453→- **Password:** r3tr0gradE99#
|
||||
454→- **Role:** Primary DC, NPS/RADIUS server
|
||||
455→- **Notes:** Added 2025-12-22
|
||||
456→
|
||||
457→### NPS RADIUS Configuration
|
||||
458→- **RADIUS Server:** 172.16.9.2
|
||||
459→- **RADIUS Ports:** 1812 (auth), 1813 (accounting)
|
||||
460→- **Clients:** UDM (172.16.9.1), VWP-Subnet (172.16.9.0/24)
|
||||
461→- **Shared Secret:** Gptf*77ttb123!@#-radius
|
||||
462→- **Policy:** "VPN-Access" - allows all authenticated users (24/7)
|
||||
463→- **Auth Methods:** All (PAP, CHAP, MS-CHAP, MS-CHAPv2, EAP)
|
||||
464→- **User Dial-in:** All VWP_Users set to Allow
|
||||
465→- **AuthAttributeRequired:** Disabled on clients
|
||||
466→- **Tested:** 2025-12-22, user cguerrero authenticated successfully
|
||||
467→
|
||||
468→### Dataforth - Entra App Registration (Claude-Code-M365)
|
||||
469→- **Tenant ID:** 7dfa3ce8-c496-4b51-ab8d-bd3dcd78b584
|
||||
470→- **App ID (Client ID):** 7a8c0b2e-57fb-4d79-9b5a-4b88d21b1f29
|
||||
471→- **Client Secret:** tXo8Q~ZNG9zoBpbK9HwJTkzx.YEigZ9AynoSrca3
|
||||
472→- **Permissions:** Calendars.ReadWrite, Contacts.ReadWrite, User.ReadWrite.All, Mail.ReadWrite, Directory.ReadWrite.All, Group.ReadWrite.All
|
||||
473→- **Created:** 2025-12-22
|
||||
474→- **Use:** Silent Graph API access to Dataforth tenant
|
||||
475→
|
||||
476→---
|
||||
477→
|
||||
478→## Client - CW Concrete LLC
|
||||
479→
|
||||
480→### Microsoft 365 Tenant
|
||||
481→- **Tenant:** cwconcretellc.com
|
||||
482→- **CIPP Name:** cwconcretellc.com
|
||||
483→- **Tenant ID:** dfee2224-93cd-4291-9b09-6c6ce9bb8711
|
||||
484→- **Default Domain:** NETORGFT11452752.onmicrosoft.com
|
||||
485→- **Notes:** De-federated from GoDaddy 2025-12, domain needs re-verification
|
||||
486→
|
||||
487→### Security Investigation (2025-12-22)
|
||||
488→- **Findings:**
|
||||
489→ - Graph Command Line Tools OAuth consent with high privileges (REMOVED)
|
||||
490→ - "test" backdoor app registration with multi-tenant access (DELETED)
|
||||
491→ - Apple Internet Accounts OAuth (left - likely iOS device)
|
||||
492→ - No malicious mailbox rules or forwarding
|
||||
493→- **Remediation:**
|
||||
494→ - All sessions revoked for all 4 users
|
||||
495→ - Backdoor apps removed
|
||||
496→- **Status:** RESOLVED
|
||||
497→
|
||||
498→---
|
||||
499→
|
||||
500→## Client - Khalsa
|
||||
501→
|
||||
502→### Network
|
||||
503→- **Subnet:** 172.16.50.0/24
|
||||
504→
|
||||
505→### UCG (UniFi Cloud Gateway)
|
||||
506→- **IP:** 172.16.50.1
|
||||
507→- **SSH User:** azcomputerguru
|
||||
508→- **SSH Password:** Paper123!@#-camden (reset 2025-12-22)
|
||||
509→- **Notes:** Gateway/firewall, VPN server, SSH key added but not working
|
||||
510→
|
||||
511→### Switch
|
||||
512→- **User:** 8WfY8
|
||||
513→- **Password:** tI3evTNBZMlnngtBc
|
||||
514→
|
||||
515→### Accountant Machine
|
||||
516→- **IP:** 172.16.50.168
|
||||
517→- **User:** accountant
|
||||
518→- **Password:** Paper123!@#-accountant
|
||||
519→- **Notes:** Added 2025-12-22, VPN routing issue
|
||||
520→
|
||||
521→---
|
||||
522→
|
||||
523→## Client - Scileppi Law Firm
|
||||
524→
|
||||
525→### DS214se (Source NAS - being migrated)
|
||||
526→- **IP:** 172.16.1.54
|
||||
527→- **SSH User:** admin
|
||||
528→- **Password:** Th1nk3r^99
|
||||
529→- **Storage:** 1.8TB (1.6TB used)
|
||||
530→- **Data:** User home folders (admin, Andrew Ross, Chris Scileppi, Samantha Nunez, etc.)
|
||||
531→
|
||||
532→### Unraid (Source - Migration)
|
||||
533→- **IP:** 172.16.1.21
|
||||
534→- **SSH User:** root
|
||||
535→- **Password:** Th1nk3r^99
|
||||
536→- **Role:** Data source for migration to RS2212+
|
||||
537→
|
||||
538→### RS2212+ (Destination NAS)
|
||||
539→- **IP:** 172.16.1.59
|
||||
540→- **Hostname:** SL-SERVER
|
||||
541→- **SSH User:** sysadmin
|
||||
542→- **Password:** Gptf*77ttb123!@#-sl-server
|
||||
543→- **SSH Key:** claude-code@localadmin added to authorized_keys
|
||||
544→- **Storage:** 25TB available
|
||||
545→- **Notes:** User home service enabled, migration in progress 2025-12-23
|
||||
546→
|
||||
547→### Migration Status
|
||||
548→- **Started:** 2025-12-23
|
||||
549→- **Method:** rsync over SSH (RS2212+ pulling from DS214se)
|
||||
550→- **Command:** `rsync -avz --progress -e 'ssh -i ~/.ssh/id_ed25519' admin@172.16.1.54:/volume1/homes/ /volume1/homes/`
|
||||
551→- **Estimated time:** 10-20 hours for 1.6TB
|
||||
552→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1,17 @@
|
||||
674- '<div style="display: flex; align-items: center; gap: 12px;">' +
|
||||
675- '<div style="width: 10px; height: 10px; border-radius: 50%; background: hsl(142, 76%, 50%);"></div>' +
|
||||
676- '<div>' +
|
||||
677: '<div style="font-weight: 500;">' + (m.agent_name || m.agent_id.slice(0,8)) + '</div>' +
|
||||
678- '<div style="font-size: 12px; color: hsl(var(--muted-foreground));">Connected ' + started + '</div>' +
|
||||
679- '</div>' +
|
||||
680- '</div>' +
|
||||
--
|
||||
710- '<div class="detail-section">' +
|
||||
711- '<div class="detail-section-title">Actions</div>' +
|
||||
712- '<button class="btn btn-primary" style="width: 100%; margin-bottom: 8px;" onclick="connectToMachine(\'' + m.id + '\')">Connect</button>' +
|
||||
713: '<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" onclick="openChat(\'' + m.id + '\', \'' + (m.agent_name || 'Client').replace(/'/g, "\\'") + '\')">Chat</button>' +
|
||||
714- '<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" disabled>Transfer Files</button>' +
|
||||
715: '<button class="btn btn-outline" style="width: 100%; color: hsl(0, 62.8%, 50%);" onclick="disconnectMachine(\'' + m.id + '\', \'' + (m.agent_name || m.agent_id).replace(/'/g, "\\'") + '\')">Disconnect</button>' +
|
||||
716- '</div>';
|
||||
717- }
|
||||
718-
|
||||
@@ -0,0 +1,389 @@
|
||||
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
|
||||
package: /tmp/guru-connect/agent/Cargo.toml
|
||||
workspace: /tmp/guru-connect/Cargo.toml
|
||||
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
|
||||
package: /tmp/guru-connect/server/Cargo.toml
|
||||
workspace: /tmp/guru-connect/Cargo.toml
|
||||
Compiling proc-macro2 v1.0.103
|
||||
Compiling libc v0.2.178
|
||||
Compiling cfg-if v1.0.4
|
||||
Compiling quote v1.0.42
|
||||
Compiling serde_core v1.0.228
|
||||
Compiling serde v1.0.228
|
||||
Compiling generic-array v0.14.7
|
||||
Compiling parking_lot_core v0.9.12
|
||||
Compiling autocfg v1.5.0
|
||||
Compiling bytes v1.11.0
|
||||
Compiling icu_normalizer_data v2.1.1
|
||||
Compiling itoa v1.0.16
|
||||
Compiling icu_properties_data v2.1.2
|
||||
Compiling pin-project-lite v0.2.16
|
||||
Compiling typenum v1.19.0
|
||||
Compiling zerocopy v0.8.31
|
||||
Compiling stable_deref_trait v1.2.1
|
||||
Compiling syn v2.0.111
|
||||
Compiling futures-core v0.3.31
|
||||
Compiling crossbeam-utils v0.8.21
|
||||
Compiling scopeguard v1.2.0
|
||||
Compiling lock_api v0.4.14
|
||||
Compiling subtle v2.6.1
|
||||
Compiling futures-sink v0.3.31
|
||||
Compiling thiserror v2.0.17
|
||||
Compiling serde_json v1.0.145
|
||||
Compiling signal-hook-registry v1.4.7
|
||||
Compiling socket2 v0.6.1
|
||||
Compiling mio v1.1.1
|
||||
Compiling block-buffer v0.10.4
|
||||
Compiling crypto-common v0.1.7
|
||||
Compiling digest v0.10.7
|
||||
Compiling memchr v2.7.6
|
||||
Compiling getrandom v0.2.16
|
||||
Compiling getrandom v0.3.4
|
||||
Compiling writeable v0.6.2
|
||||
Compiling litemap v0.8.1
|
||||
Compiling anyhow v1.0.100
|
||||
Compiling log v0.4.29
|
||||
Compiling equivalent v1.0.2
|
||||
Compiling num-traits v0.2.19
|
||||
Compiling once_cell v1.21.3
|
||||
Compiling pin-utils v0.1.0
|
||||
Compiling tracing-core v0.1.36
|
||||
Compiling hashbrown v0.16.1
|
||||
Compiling rand_core v0.6.4
|
||||
Compiling http v1.4.0
|
||||
Compiling futures-io v0.3.31
|
||||
Compiling indexmap v2.12.1
|
||||
Compiling slab v0.4.11
|
||||
Compiling bitflags v2.10.0
|
||||
Compiling futures-task v0.3.31
|
||||
Compiling cpufeatures v0.2.17
|
||||
Compiling percent-encoding v2.3.2
|
||||
Compiling utf8_iter v1.0.4
|
||||
Compiling tinyvec_macros v0.1.1
|
||||
Compiling httparse v1.10.1
|
||||
Compiling foldhash v0.1.5
|
||||
Compiling ryu v1.0.21
|
||||
Compiling allocator-api2 v0.2.21
|
||||
Compiling tinyvec v1.10.0
|
||||
Compiling synstructure v0.13.2
|
||||
Compiling hashbrown v0.15.5
|
||||
Compiling form_urlencoded v1.2.2
|
||||
Compiling tokio v1.48.0
|
||||
Compiling http-body v1.0.1
|
||||
Compiling ppv-lite86 v0.2.21
|
||||
Compiling concurrent-queue v2.5.0
|
||||
Compiling shlex v1.3.0
|
||||
Compiling rand_chacha v0.3.1
|
||||
Compiling base64 v0.22.1
|
||||
Compiling find-msvc-tools v0.1.5
|
||||
Compiling crc32fast v1.5.0
|
||||
Compiling parking v2.2.1
|
||||
Compiling heck v0.5.0
|
||||
Compiling rustix v1.1.2
|
||||
Compiling iana-time-zone v0.1.64
|
||||
Compiling crc-catalog v2.4.0
|
||||
Compiling crc v3.4.0
|
||||
Compiling futures-util v0.3.31
|
||||
Compiling chrono v0.4.42
|
||||
Compiling serde_derive v1.0.228
|
||||
Compiling zerofrom-derive v0.1.6
|
||||
Compiling yoke-derive v0.8.1
|
||||
Compiling zerovec-derive v0.11.2
|
||||
Compiling displaydoc v0.2.5
|
||||
Compiling tracing-attributes v0.1.31
|
||||
Compiling tokio-macros v2.6.0
|
||||
Compiling thiserror-impl v2.0.17
|
||||
Compiling zerofrom v0.1.6
|
||||
Compiling yoke v0.8.1
|
||||
Compiling zerovec v0.11.5
|
||||
Compiling zerotrie v0.2.3
|
||||
Compiling tinystr v0.8.2
|
||||
Compiling icu_locale_core v2.1.1
|
||||
Compiling potential_utf v0.1.4
|
||||
Compiling icu_collections v2.1.1
|
||||
Compiling futures-macro v0.3.31
|
||||
Compiling tracing v0.1.44
|
||||
Compiling icu_provider v2.1.1
|
||||
Compiling tokio-stream v0.1.17
|
||||
Compiling icu_properties v2.1.2
|
||||
Compiling either v1.15.0
|
||||
Compiling smallvec v1.15.1
|
||||
Compiling itertools v0.14.0
|
||||
Compiling icu_normalizer v2.1.1
|
||||
Compiling parking_lot v0.12.5
|
||||
Compiling idna_adapter v1.2.1
|
||||
Compiling idna v1.1.0
|
||||
Compiling url v2.5.7
|
||||
Compiling futures-intrusive v0.5.0
|
||||
Compiling sha2 v0.10.9
|
||||
Compiling event-listener v5.4.1
|
||||
Compiling prost-derive v0.13.5
|
||||
Compiling rand v0.8.5
|
||||
Compiling cc v1.2.50
|
||||
Compiling hashlink v0.10.0
|
||||
Compiling unicode-normalization v0.1.25
|
||||
Compiling crossbeam-queue v0.3.12
|
||||
Compiling hmac v0.12.1
|
||||
Compiling futures-channel v0.3.31
|
||||
Compiling byteorder v1.5.0
|
||||
Compiling linux-raw-sys v0.11.0
|
||||
Compiling regex-syntax v0.8.8
|
||||
Compiling unicode-bidi v0.3.18
|
||||
Compiling tower-service v0.3.3
|
||||
Compiling thiserror v1.0.69
|
||||
Compiling prettyplease v0.2.37
|
||||
Compiling uuid v1.19.0
|
||||
Compiling simd-adler32 v0.3.8
|
||||
Compiling unicode-properties v0.1.4
|
||||
Compiling adler2 v2.0.1
|
||||
Compiling stringprep v0.1.5
|
||||
Compiling miniz_oxide v0.8.9
|
||||
Compiling sqlx-core v0.8.6
|
||||
Compiling ring v0.17.14
|
||||
Compiling regex-automata v0.4.13
|
||||
Compiling prost v0.13.5
|
||||
Compiling hkdf v0.12.4
|
||||
Compiling thiserror-impl v1.0.69
|
||||
Compiling atoi v2.0.0
|
||||
Compiling md-5 v0.10.6
|
||||
Compiling rustversion v1.0.22
|
||||
Compiling num-conv v0.1.0
|
||||
Compiling home v0.5.12
|
||||
Compiling unicase v2.8.1
|
||||
Compiling fixedbitset v0.5.7
|
||||
Compiling hex v0.4.3
|
||||
Compiling time-core v0.1.6
|
||||
Compiling powerfmt v0.2.0
|
||||
Compiling fastrand v2.3.0
|
||||
Compiling whoami v1.6.1
|
||||
Compiling dotenvy v0.15.7
|
||||
Compiling mime v0.3.17
|
||||
Compiling httpdate v1.0.3
|
||||
Compiling tower-layer v0.3.3
|
||||
Compiling sqlx-postgres v0.8.6
|
||||
Compiling tempfile v3.23.0
|
||||
Compiling deranged v0.5.5
|
||||
Compiling time-macros v0.2.24
|
||||
Compiling petgraph v0.7.1
|
||||
Compiling mime_guess v2.0.5
|
||||
Compiling regex v1.12.2
|
||||
Compiling flate2 v1.1.5
|
||||
Compiling prost-types v0.13.5
|
||||
Compiling http-body-util v0.1.3
|
||||
Compiling num-integer v0.1.46
|
||||
Compiling sha1 v0.10.6
|
||||
Compiling sync_wrapper v1.0.2
|
||||
Compiling atomic-waker v1.1.2
|
||||
Compiling multimap v0.10.1
|
||||
Compiling utf-8 v0.7.6
|
||||
Compiling compression-core v0.4.31
|
||||
Compiling data-encoding v2.9.0
|
||||
Compiling compression-codecs v0.4.35
|
||||
Compiling tungstenite v0.24.0
|
||||
Compiling time v0.3.44
|
||||
Compiling sqlx-macros-core v0.8.6
|
||||
Compiling prost-build v0.13.5
|
||||
Compiling hyper v1.8.1
|
||||
Compiling num-bigint v0.4.6
|
||||
Compiling serde_spanned v0.6.9
|
||||
Compiling toml_datetime v0.6.3
|
||||
Compiling async-trait v0.1.89
|
||||
Compiling winnow v0.5.40
|
||||
Compiling lazy_static v1.5.0
|
||||
Compiling untrusted v0.9.0
|
||||
Compiling base64ct v1.8.1
|
||||
Compiling password-hash v0.5.0
|
||||
Compiling toml_edit v0.20.2
|
||||
Compiling sharded-slab v0.1.7
|
||||
Compiling axum-core v0.4.5
|
||||
Compiling simple_asn1 v0.6.3
|
||||
Compiling sqlx-macros v0.8.6
|
||||
Compiling guruconnect-server v0.1.0 (/tmp/guru-connect/server)
|
||||
Compiling hyper-util v0.1.19
|
||||
Compiling matchers v0.2.0
|
||||
Compiling tokio-tungstenite v0.24.0
|
||||
Compiling async-compression v0.4.36
|
||||
Compiling tower v0.5.2
|
||||
Compiling tokio-util v0.7.17
|
||||
Compiling serde_urlencoded v0.7.1
|
||||
Compiling axum-macros v0.4.2
|
||||
Compiling pem v3.0.6
|
||||
Compiling tracing-log v0.2.0
|
||||
Compiling serde_path_to_error v0.1.20
|
||||
Compiling blake2 v0.10.6
|
||||
Compiling thread_local v1.1.9
|
||||
Compiling matchit v0.7.3
|
||||
Compiling http-range-header v0.4.2
|
||||
Compiling nu-ansi-term v0.50.3
|
||||
Compiling tracing-subscriber v0.3.22
|
||||
Compiling tower-http v0.6.8
|
||||
Compiling axum v0.7.9
|
||||
Compiling argon2 v0.5.3
|
||||
Compiling jsonwebtoken v9.3.1
|
||||
Compiling sqlx v0.8.6
|
||||
Compiling toml v0.8.2
|
||||
warning: unused variable: `config`
|
||||
--> server/src/main.rs:54:9
|
||||
|
|
||||
54 | let config = config::Config::load()?;
|
||||
| ^^^^^^ help: if this is intentional, prefix it with an underscore: `_config`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: struct `ValidateParams` is never constructed
|
||||
--> server/src/main.rs:138:8
|
||||
|
|
||||
138 | struct ValidateParams {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
|
||||
|
||||
warning: fields `listen_addr`, `database_url`, `jwt_secret`, and `debug` are never read
|
||||
--> server/src/config.rs:10:9
|
||||
|
|
||||
8 | pub struct Config {
|
||||
| ------ fields in this struct
|
||||
9 | /// Address to listen on (e.g., "0.0.0.0:8080")
|
||||
10 | pub listen_addr: String,
|
||||
| ^^^^^^^^^^^
|
||||
...
|
||||
13 | pub database_url: Option<String>,
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
16 | pub jwt_secret: Option<String>,
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
19 | pub debug: bool,
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `Config` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
|
||||
|
||||
warning: constant `HEARTBEAT_TIMEOUT_SECS` is never used
|
||||
--> server/src/session/mod.rs:22:7
|
||||
|
|
||||
22 | const HEARTBEAT_TIMEOUT_SECS: u64 = 90;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `input_rx` is never read
|
||||
--> server/src/session/mod.rs:58:5
|
||||
|
|
||||
52 | struct SessionData {
|
||||
| ----------- field in this struct
|
||||
...
|
||||
58 | input_rx: Option<InputReceiver>,
|
||||
| ^^^^^^^^
|
||||
|
||||
warning: methods `is_session_timed_out`, `get_timed_out_sessions`, `get_session_by_agent`, and `remove_session` are never used
|
||||
--> server/src/session/mod.rs:185:18
|
||||
|
|
||||
72 | impl SessionManager {
|
||||
| ------------------- methods in this implementation
|
||||
...
|
||||
185 | pub async fn is_session_timed_out(&self, session_id: SessionId) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
195 | pub async fn get_timed_out_sessions(&self) -> Vec<SessionId> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
205 | pub async fn get_session_by_agent(&self, agent_id: &str) -> Option<Session> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
317 | pub async fn remove_session(&self, session_id: SessionId) {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
warning: struct `AuthenticatedUser` is never constructed
|
||||
--> server/src/auth/mod.rs:13:12
|
||||
|
|
||||
13 | pub struct AuthenticatedUser {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: struct `AuthenticatedAgent` is never constructed
|
||||
--> server/src/auth/mod.rs:21:12
|
||||
|
|
||||
21 | pub struct AuthenticatedAgent {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `validate_agent_key` is never used
|
||||
--> server/src/auth/mod.rs:54:8
|
||||
|
|
||||
54 | pub fn validate_agent_key(_api_key: &str) -> Option<AuthenticatedAgent> {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: function `list_sessions` is never used
|
||||
--> server/src/api/mod.rs:51:14
|
||||
|
|
||||
51 | pub async fn list_sessions(
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
warning: function `get_session` is never used
|
||||
--> server/src/api/mod.rs:59:14
|
||||
|
|
||||
59 | pub async fn get_session(
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: struct `Database` is never constructed
|
||||
--> server/src/db/mod.rs:10:12
|
||||
|
|
||||
10 | pub struct Database {
|
||||
| ^^^^^^^^
|
||||
|
||||
warning: associated function `init` is never used
|
||||
--> server/src/db/mod.rs:17:18
|
||||
|
|
||||
15 | impl Database {
|
||||
| ------------- associated function in this implementation
|
||||
16 | /// Initialize database connection
|
||||
17 | pub async fn init(_database_url: &str) -> Result<Self> {
|
||||
| ^^^^
|
||||
|
||||
warning: struct `SessionEvent` is never constructed
|
||||
--> server/src/db/mod.rs:25:12
|
||||
|
|
||||
25 | pub struct SessionEvent {
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
warning: enum `SessionEventType` is never used
|
||||
--> server/src/db/mod.rs:32:10
|
||||
|
|
||||
32 | pub enum SessionEventType {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: method `log_session_event` is never used
|
||||
--> server/src/db/mod.rs:41:18
|
||||
|
|
||||
39 | impl Database {
|
||||
| ------------- method in this implementation
|
||||
40 | /// Log a session event (placeholder)
|
||||
41 | pub async fn log_session_event(&self, _event: SessionEvent) -> Result<()> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: field `technician_id` is never read
|
||||
--> server/src/support_codes.rs:39:9
|
||||
|
|
||||
38 | pub struct CreateCodeRequest {
|
||||
| ----------------- field in this struct
|
||||
39 | pub technician_id: Option<String>,
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `CreateCodeRequest` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
|
||||
|
||||
warning: methods `get_code`, `is_valid_for_connection`, `list_codes`, and `get_by_session` are never used
|
||||
--> server/src/support_codes.rs:166:18
|
||||
|
|
||||
60 | impl SupportCodeManager {
|
||||
| ----------------------- methods in this implementation
|
||||
...
|
||||
166 | pub async fn get_code(&self, code: &str) -> Option<SupportCode> {
|
||||
| ^^^^^^^^
|
||||
...
|
||||
198 | pub async fn is_valid_for_connection(&self, code: &str) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
204 | pub async fn list_codes(&self) -> Vec<SupportCode> {
|
||||
| ^^^^^^^^^^
|
||||
...
|
||||
219 | pub async fn get_by_session(&self, session_id: Uuid) -> Option<SupportCode> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
warning: `guruconnect-server` (bin "guruconnect-server") generated 18 warnings (run `cargo fix --bin "guruconnect-server" -p guruconnect-server` to apply 1 suggestion)
|
||||
Finished `release` profile [optimized] target(s) in 57.14s
|
||||
@@ -0,0 +1,5 @@
|
||||
No binary found, checking target structure:
|
||||
build/
|
||||
deps/
|
||||
examples/
|
||||
incremental/
|
||||
@@ -0,0 +1,12 @@
|
||||
100.125.36.6 acg-m-l5090 mike@ windows -
|
||||
100.92.230.111 acg-tech-01l mike@ windows offline, last seen 6d ago
|
||||
100.96.135.117 acg-tech-02l mike@ windows offline, last seen 6d ago
|
||||
100.113.45.7 acg-tech03l howard@ windows -
|
||||
100.77.166.22 desktop-hjfjtep mike@ windows -
|
||||
100.101.145.100 guru-legion9 mike@ windows offline, last seen 50d ago
|
||||
100.119.194.51 guru-surface8 howard@ windows offline, last seen 81d ago
|
||||
100.66.103.110 magus-desktop rob@ windows offline, last seen 58d ago
|
||||
100.66.167.120 magus-pc rob@ windows offline, last seen 146d ago
|
||||
100.65.158.123 mikes-macbook-air mike@ macOS offline, last seen 12m ago
|
||||
100.119.153.74 pfsense-2 mike@ freebsd active; direct 98.181.90.163:12101, tx 13175936 rx 69340004
|
||||
100.122.110.39 pfsense mike@ freebsd offline, last seen 2d ago
|
||||
@@ -0,0 +1,4 @@
|
||||
username | role | enabled
|
||||
----------+-------+---------
|
||||
admin | admin | t
|
||||
(1 row)
|
||||
@@ -0,0 +1 @@
|
||||
No matches found
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 1
|
||||
tail: cannot open 'server.log' for reading: No such file or directory
|
||||
@@ -0,0 +1,26 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\src\api\mod.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
8→use uuid::Uuid;
|
||||
9→
|
||||
10→use crate::session::SessionManager;
|
||||
11→
|
||||
12→/// Session info returned by API
|
||||
13→#[derive(Debug, Serialize)]
|
||||
14→pub struct SessionInfo {
|
||||
15→ pub id: String,
|
||||
16→ pub agent_id: String,
|
||||
17→ pub agent_name: String,
|
||||
18→ pub started_at: String,
|
||||
19→ pub viewer_count: usize,
|
||||
20→ pub is_streaming: bool,
|
||||
21→ pub is_online: bool,
|
||||
22→ pub is_persistent: bool,
|
||||
23→ pub last_heartbeat: String,
|
||||
24→ pub os_version: Option<String>,
|
||||
25→ pub is_elevated: bool,
|
||||
26→ pub uptime_secs: i64,
|
||||
27→ pub display_count: i32,
|
||||
28→}
|
||||
29→
|
||||
30→impl From<crate::session::Session> for SessionInfo {
|
||||
31→ fn from(s: crate::session::Session) -> Self {
|
||||
32→ Self {
|
||||
@@ -0,0 +1,104 @@
|
||||
660→ }
|
||||
661→
|
||||
662→ function renderMachinesList() {
|
||||
663→ const container = document.getElementById("machinesList");
|
||||
664→
|
||||
665→ if (machines.length === 0) {
|
||||
666→ container.innerHTML = '<div class="empty-state"><h3>No machines</h3><p>Install the agent on a machine to see it here</p></div>';
|
||||
667→ return;
|
||||
668→ }
|
||||
669→
|
||||
670→ container.innerHTML = '<div style="padding: 12px;">' + machines.map(m => {
|
||||
671→ const started = new Date(m.started_at).toLocaleString();
|
||||
672→ const isSelected = selectedMachine?.id === m.id;
|
||||
673→ return '<div class="sidebar-item' + (isSelected ? ' active' : '') + '" onclick="selectMachine(\'' + m.id + '\')" style="margin-bottom: 8px; padding: 12px;">' +
|
||||
674→ '<div style="display: flex; align-items: center; gap: 12px;">' +
|
||||
675→ '<div style="width: 10px; height: 10px; border-radius: 50%; background: hsl(142, 76%, 50%);"></div>' +
|
||||
676→ '<div>' +
|
||||
677→ '<div style="font-weight: 500;">' + (m.agent_name || m.agent_id.slice(0,8)) + '</div>' +
|
||||
678→ '<div style="font-size: 12px; color: hsl(var(--muted-foreground));">Connected ' + started + '</div>' +
|
||||
679→ '</div>' +
|
||||
680→ '</div>' +
|
||||
681→ '</div>';
|
||||
682→ }).join("") + '</div>';
|
||||
683→ }
|
||||
684→
|
||||
685→ function selectMachine(id) {
|
||||
686→ selectedMachine = machines.find(m => m.id === id);
|
||||
687→ renderMachinesList();
|
||||
688→ renderMachineDetail();
|
||||
689→ }
|
||||
690→
|
||||
691→ function renderMachineDetail() {
|
||||
692→ const container = document.getElementById("machineDetail");
|
||||
693→
|
||||
694→ if (!selectedMachine) {
|
||||
695→ container.innerHTML = '<div class="empty-state"><h3>Select a machine</h3><p>Click a machine to view details</p></div>';
|
||||
696→ return;
|
||||
697→ }
|
||||
698→
|
||||
699→ const m = selectedMachine;
|
||||
700→ const started = new Date(m.started_at).toLocaleString();
|
||||
701→
|
||||
702→ container.innerHTML =
|
||||
703→ '<div class="detail-section">' +
|
||||
704→ '<div class="detail-section-title">Machine Info</div>' +
|
||||
705→ '<div class="detail-row"><span class="detail-label">Agent ID</span><span class="detail-value">' + m.agent_id.slice(0,8) + '...</span></div>' +
|
||||
706→ '<div class="detail-row"><span class="detail-label">Session ID</span><span class="detail-value">' + m.id.slice(0,8) + '...</span></div>' +
|
||||
707→ '<div class="detail-row"><span class="detail-label">Connected</span><span class="detail-value">' + started + '</span></div>' +
|
||||
708→ '<div class="detail-row"><span class="detail-label">Viewers</span><span class="detail-value">' + m.viewer_count + '</span></div>' +
|
||||
709→ '</div>' +
|
||||
710→ '<div class="detail-section">' +
|
||||
711→ '<div class="detail-section-title">Actions</div>' +
|
||||
712→ '<button class="btn btn-primary" style="width: 100%; margin-bottom: 8px;" onclick="connectToMachine(\'' + m.id + '\')">Connect</button>' +
|
||||
713→ '<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" onclick="openChat(\'' + m.id + '\', \'' + (m.agent_name || 'Client').replace(/'/g, "\\'") + '\')">Chat</button>' +
|
||||
714→ '<button class="btn btn-outline" style="width: 100%; margin-bottom: 8px;" disabled>Transfer Files</button>' +
|
||||
715→ '<button class="btn btn-outline" style="width: 100%; color: hsl(0, 62.8%, 50%);" onclick="disconnectMachine(\'' + m.id + '\', \'' + (m.agent_name || m.agent_id).replace(/'/g, "\\'") + '\')">Disconnect</button>' +
|
||||
716→ '</div>';
|
||||
717→ }
|
||||
718→
|
||||
719→ function connectToMachine(sessionId) {
|
||||
720→ // Open viewer in new window
|
||||
721→ const viewerUrl = "/viewer.html?session_id=" + sessionId;
|
||||
722→ window.open(viewerUrl, "viewer_" + sessionId, "width=1280,height=800,menubar=no,toolbar=no,location=no,status=no");
|
||||
723→ }
|
||||
724→
|
||||
725→ async function disconnectMachine(sessionId, machineName) {
|
||||
726→ if (!confirm("Disconnect " + machineName + "?\\n\\nThis will end the remote session.")) return;
|
||||
727→ try {
|
||||
728→ const response = await fetch("/api/sessions/" + sessionId, { method: "DELETE" });
|
||||
729→ if (response.ok) {
|
||||
730→ selectedMachine = null;
|
||||
731→ renderMachineDetail();
|
||||
732→ loadMachines();
|
||||
733→ } else {
|
||||
734→ alert("Failed to disconnect: " + await response.text());
|
||||
735→ }
|
||||
736→ } catch (err) {
|
||||
737→ alert("Error disconnecting machine");
|
||||
738→ }
|
||||
739→ }
|
||||
740→
|
||||
741→ // Refresh machines every 5 seconds
|
||||
742→ loadMachines();
|
||||
743→ setInterval(loadMachines, 5000);
|
||||
744→
|
||||
745→ // ========== Chat Functions ==========
|
||||
746→
|
||||
747→ // Chat modal elements
|
||||
748→ const chatModal = document.getElementById("chatModal");
|
||||
749→ const chatMessagesEl = document.getElementById("chatMessages");
|
||||
750→ const chatInput = document.getElementById("chatInput");
|
||||
751→ const chatSend = document.getElementById("chatSend");
|
||||
752→ const chatClose = document.getElementById("chatClose");
|
||||
753→ const chatClientName = document.getElementById("chatClientName");
|
||||
754→
|
||||
755→ // Close chat modal
|
||||
756→ chatClose.addEventListener("click", () => {
|
||||
757→ closeChat();
|
||||
758→ });
|
||||
759→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 255
|
||||
root@172.16.3.20: Permission denied (publickey,password,keyboard-interactive).
|
||||
@@ -0,0 +1,5 @@
|
||||
Host ix.azcomputerguru.com
|
||||
User root
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
IdentitiesOnly yes
|
||||
BatchMode yes
|
||||
@@ -0,0 +1,24 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\static\dashboard.html has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
666→ container.innerHTML = '<div class="empty-state"><h3>No machines</h3><p>Install the agent on a machine to see it here</p></div>';
|
||||
667→ return;
|
||||
668→ }
|
||||
669→
|
||||
670→ container.innerHTML = '<div style="padding: 12px;">' + machines.map(m => {
|
||||
671→ const started = new Date(m.started_at).toLocaleString();
|
||||
672→ const isSelected = selectedMachine?.id === m.id;
|
||||
673→ const statusColor = m.is_online ? 'hsl(142, 76%, 50%)' : 'hsl(0, 0%, 50%)';
|
||||
674→ const statusText = m.is_online ? 'Online' : 'Offline';
|
||||
675→ return '<div class="sidebar-item' + (isSelected ? ' active' : '') + '" onclick="selectMachine(\'' + m.id + '\')" style="margin-bottom: 8px; padding: 12px;">' +
|
||||
676→ '<div style="display: flex; align-items: center; gap: 12px;">' +
|
||||
677→ '<div style="width: 10px; height: 10px; border-radius: 50%; background: ' + statusColor + ';"></div>' +
|
||||
678→ '<div>' +
|
||||
679→ '<div style="font-weight: 500;">' + (m.agent_name || m.agent_id.slice(0,8)) + '</div>' +
|
||||
680→ '<div style="font-size: 12px; color: hsl(var(--muted-foreground));">' + statusText + ' • ' + started + '</div>' +
|
||||
681→ '</div>' +
|
||||
682→ '</div>' +
|
||||
683→ '</div>';
|
||||
684→ }).join("") + '</div>';
|
||||
685→ }
|
||||
686→
|
||||
687→ function selectMachine(id) {
|
||||
688→ selectedMachine = machines.find(m => m.id === id);
|
||||
@@ -0,0 +1 @@
|
||||
46841 /home/guru/guru-connect/target/release/guruconnect-server
|
||||
@@ -0,0 +1 @@
|
||||
Command running in background with ID: b5e79f0. Output is being written to: C:\Users\MIKESW~1\AppData\Local\Temp\claude\C--Users-MikeSwanson-Claude\tasks\b5e79f0.output
|
||||
@@ -0,0 +1,26 @@
|
||||
Updating 743b73d..3fc4e1f
|
||||
Fast-forward
|
||||
server/migrations/002_user_management.sql | 44 +++
|
||||
server/src/api/auth.rs | 317 ++++++++++++++++
|
||||
server/src/api/mod.rs | 3 +
|
||||
server/src/api/users.rs | 592 +++++++++++++++++++++++++++++
|
||||
server/src/auth/jwt.rs | 140 +++++++
|
||||
server/src/auth/mod.rs | 133 ++++++-
|
||||
server/src/auth/password.rs | 57 +++
|
||||
server/src/db/mod.rs | 2 +
|
||||
server/src/db/users.rs | 283 ++++++++++++++
|
||||
server/src/main.rs | 114 +++++-
|
||||
server/static/dashboard.html | 72 +++-
|
||||
server/static/login.html | 67 ++--
|
||||
server/static/users.html | 602 ++++++++++++++++++++++++++++++
|
||||
13 files changed, 2355 insertions(+), 71 deletions(-)
|
||||
create mode 100644 server/migrations/002_user_management.sql
|
||||
create mode 100644 server/src/api/auth.rs
|
||||
create mode 100644 server/src/api/users.rs
|
||||
create mode 100644 server/src/auth/jwt.rs
|
||||
create mode 100644 server/src/auth/password.rs
|
||||
create mode 100644 server/src/db/users.rs
|
||||
create mode 100644 server/static/users.html
|
||||
From https://git.azcomputerguru.com/azcomputerguru/guru-connect
|
||||
* branch main -> FETCH_HEAD
|
||||
743b73d..3fc4e1f main -> origin/main
|
||||
@@ -0,0 +1,4 @@
|
||||
remote: . Processing 1 references
|
||||
remote: Processed 1 references in total
|
||||
To https://git.azcomputerguru.com/azcomputerguru/guru-connect.git
|
||||
743b73d..3fc4e1f main -> main
|
||||
@@ -0,0 +1,62 @@
|
||||
280→ viewer_id: viewer_id.to_string(),
|
||||
281→ })),
|
||||
282→ };
|
||||
283→
|
||||
284→ let mut buf = Vec::new();
|
||||
285→ if stop_stream.encode(&mut buf).is_ok() {
|
||||
286→ let _ = session_data.input_tx.send(buf).await;
|
||||
287→ }
|
||||
288→ }
|
||||
289→
|
||||
290→ /// Remove a session (when agent disconnects)
|
||||
291→ pub async fn remove_session(&self, session_id: SessionId) {
|
||||
292→ let mut sessions = self.sessions.write().await;
|
||||
293→ if let Some(session_data) = sessions.remove(&session_id) {
|
||||
294→ let mut agents = self.agents.write().await;
|
||||
295→ agents.remove(&session_data.info.agent_id);
|
||||
296→ }
|
||||
297→ }
|
||||
298→
|
||||
299→ /// Disconnect a session by sending a disconnect message to the agent
|
||||
300→ /// Returns true if the message was sent successfully
|
||||
301→ pub async fn disconnect_session(&self, session_id: SessionId, reason: &str) -> bool {
|
||||
302→ let sessions = self.sessions.read().await;
|
||||
303→ if let Some(session_data) = sessions.get(&session_id) {
|
||||
304→ // Create disconnect message
|
||||
305→ use crate::proto;
|
||||
306→ use prost::Message;
|
||||
307→
|
||||
308→ let disconnect_msg = proto::Message {
|
||||
309→ payload: Some(proto::message::Payload::Disconnect(proto::Disconnect {
|
||||
310→ reason: reason.to_string(),
|
||||
311→ })),
|
||||
312→ };
|
||||
313→
|
||||
314→ let mut buf = Vec::new();
|
||||
315→ if disconnect_msg.encode(&mut buf).is_ok() {
|
||||
316→ // Send via input channel (will be forwarded to agent's WebSocket)
|
||||
317→ if session_data.input_tx.send(buf).await.is_ok() {
|
||||
318→ return true;
|
||||
319→ }
|
||||
320→ }
|
||||
321→ }
|
||||
322→ false
|
||||
323→ }
|
||||
324→
|
||||
325→ /// List all active sessions
|
||||
326→ pub async fn list_sessions(&self) -> Vec<Session> {
|
||||
327→ let sessions = self.sessions.read().await;
|
||||
328→ sessions.values().map(|s| s.info.clone()).collect()
|
||||
329→ }
|
||||
330→}
|
||||
331→
|
||||
332→impl Default for SessionManager {
|
||||
333→ fn default() -> Self {
|
||||
334→ Self::new()
|
||||
335→ }
|
||||
336→}
|
||||
337→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 1
|
||||
FATAL ERROR: Cannot answer interactive prompts in batch mode
|
||||
@@ -0,0 +1,16 @@
|
||||
1→[2m2025-12-30T04:02:27.326062Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m GuruConnect Server v0.1.0
|
||||
2→[2m2025-12-30T04:02:27.326153Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m Loaded configuration, listening on 0.0.0.0:3002
|
||||
3→[2m2025-12-30T04:02:27.326163Z[0m [33m WARN[0m [2mguruconnect_server[0m[2m:[0m JWT_SECRET not set, using default (INSECURE for production!)
|
||||
4→[2m2025-12-30T04:02:27.326169Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Connecting to database...
|
||||
5→[2m2025-12-30T04:02:27.341136Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Database connection established
|
||||
6→[2m2025-12-30T04:02:27.341174Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Running database migrations...
|
||||
7→[2m2025-12-30T04:02:27.346248Z[0m [32m INFO[0m [2msqlx::postgres::notice[0m[2m:[0m relation "_sqlx_migrations" already exists, skipping
|
||||
8→[2m2025-12-30T04:02:27.356332Z[0m [32m INFO[0m [2mguruconnect_server::db[0m[2m:[0m Migrations complete
|
||||
9→[2m2025-12-30T04:02:27.380581Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m 1 user(s) in database
|
||||
10→[2m2025-12-30T04:02:27.387175Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m Restoring 0 persistent machines from database
|
||||
11→[2m2025-12-30T04:02:27.388002Z[0m [32m INFO[0m [2mguruconnect_server[0m[2m:[0m Server listening on 0.0.0.0:3002
|
||||
12→
|
||||
|
||||
<system-reminder>
|
||||
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
|
||||
</system-reminder>
|
||||
@@ -0,0 +1,2 @@
|
||||
/home/guru/.cargo/bin/cargo
|
||||
cargo 1.92.0 (344c4567c 2025-10-21)
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 255
|
||||
scp: stat local "C:/Users/MikeSwanson/Claude/guru-connect/target/release/guruconnect-server.exe": No such file or directory
|
||||
@@ -0,0 +1,2 @@
|
||||
Exit code 127
|
||||
bash: line 1: cargo: command not found
|
||||
@@ -0,0 +1,2 @@
|
||||
sshpass not found, trying chocolatey install...
|
||||
/usr/bin/bash: line 1: choco: command not found
|
||||
@@ -0,0 +1 @@
|
||||
<system-reminder>Warning: the file exists but is shorter than the provided offset (1). The file has 1 lines.</system-reminder>
|
||||
@@ -0,0 +1,13 @@
|
||||
The file C:\Users\MikeSwanson\Claude\guru-connect\server\src\relay\mod.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
|
||||
95→ return;
|
||||
96→ }
|
||||
97→ }
|
||||
98→
|
||||
99→ // Register the agent and get channels
|
||||
100→ // Persistent agents (no support code) keep their session when disconnected
|
||||
101→ let is_persistent = support_code.is_none();
|
||||
102→ let (session_id, frame_tx, mut input_rx) = sessions.register_agent(agent_id.clone(), agent_name.clone(), is_persistent).await;
|
||||
103→
|
||||
104→ info!("Session created: {} (agent in idle mode)", session_id);
|
||||
105→
|
||||
106→ // If a support code was provided, mark it as connected
|
||||
Reference in New Issue
Block a user