Created comprehensive VPN setup tooling for Peaceful Spirit L2TP/IPsec connection and enhanced agent documentation framework. VPN Configuration (PST-NW-VPN): - Setup-PST-L2TP-VPN.ps1: Automated L2TP/IPsec setup with split-tunnel and DNS - Connect-PST-VPN.ps1: Connection helper with PPP adapter detection, DNS (192.168.0.2), and route config (192.168.0.0/24) - Connect-PST-VPN-Standalone.ps1: Self-contained connection script for remote deployment - Fix-PST-VPN-Auth.ps1: Authentication troubleshooting for CHAP/MSChapv2 - Diagnose-VPN-Interface.ps1: Comprehensive VPN interface and routing diagnostic - Quick-Test-VPN.ps1: Fast connectivity verification (DNS/router/routes) - Add-PST-VPN-Route-Manual.ps1: Manual route configuration helper - vpn-connect.bat, vpn-disconnect.bat: Simple batch file shortcuts - OpenVPN config files (Windows-compatible, abandoned for L2TP) Key VPN Implementation Details: - L2TP creates PPP adapter with connection name as interface description - UniFi auto-configures DNS (192.168.0.2) but requires manual route to 192.168.0.0/24 - Split-tunnel enabled (only remote traffic through VPN) - All-user connection for pre-login auto-connect via scheduled task - Authentication: CHAP + MSChapv2 for UniFi compatibility Agent Documentation: - AGENT_QUICK_REFERENCE.md: Quick reference for all specialized agents - documentation-squire.md: Documentation and task management specialist agent - Updated all agent markdown files with standardized formatting Project Organization: - Moved conversation logs to dedicated directories (guru-connect-conversation-logs, guru-rmm-conversation-logs) - Cleaned up old session JSONL files from projects/msp-tools/ - Added guru-connect infrastructure (agent, dashboard, proto, scripts, .gitea workflows) - Added guru-rmm server components and deployment configs Technical Notes: - VPN IP pool: 192.168.4.x (client gets 192.168.4.6) - Remote network: 192.168.0.0/24 (router at 192.168.0.10) - PSK: rrClvnmUeXEFo90Ol+z7tfsAZHeSK6w7 - Credentials: pst-admin / 24Hearts$ Files: 15 VPN scripts, 2 agent docs, conversation log reorganization, guru-connect/guru-rmm infrastructure additions Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
164 lines
3.9 KiB
Rust
164 lines
3.9 KiB
Rust
//! Commands database operations
|
|
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use sqlx::PgPool;
|
|
use uuid::Uuid;
|
|
|
|
/// Command record from database
|
|
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
|
|
pub struct Command {
|
|
pub id: Uuid,
|
|
pub agent_id: Uuid,
|
|
pub command_type: String,
|
|
pub command_text: String,
|
|
pub status: String,
|
|
pub exit_code: Option<i32>,
|
|
pub stdout: Option<String>,
|
|
pub stderr: Option<String>,
|
|
pub created_at: DateTime<Utc>,
|
|
pub started_at: Option<DateTime<Utc>>,
|
|
pub completed_at: Option<DateTime<Utc>>,
|
|
pub created_by: Option<Uuid>,
|
|
}
|
|
|
|
/// Create a new command
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CreateCommand {
|
|
pub agent_id: Uuid,
|
|
pub command_type: String,
|
|
pub command_text: String,
|
|
pub created_by: Option<Uuid>,
|
|
}
|
|
|
|
/// Insert a new command
|
|
pub async fn create_command(pool: &PgPool, cmd: CreateCommand) -> Result<Command, sqlx::Error> {
|
|
sqlx::query_as::<_, Command>(
|
|
r#"
|
|
INSERT INTO commands (agent_id, command_type, command_text, status, created_by)
|
|
VALUES ($1, $2, $3, 'pending', $4)
|
|
RETURNING *
|
|
"#,
|
|
)
|
|
.bind(cmd.agent_id)
|
|
.bind(&cmd.command_type)
|
|
.bind(&cmd.command_text)
|
|
.bind(cmd.created_by)
|
|
.fetch_one(pool)
|
|
.await
|
|
}
|
|
|
|
/// Get a command by ID
|
|
pub async fn get_command_by_id(pool: &PgPool, id: Uuid) -> Result<Option<Command>, sqlx::Error> {
|
|
sqlx::query_as::<_, Command>("SELECT * FROM commands WHERE id = $1")
|
|
.bind(id)
|
|
.fetch_optional(pool)
|
|
.await
|
|
}
|
|
|
|
/// Get pending commands for an agent
|
|
pub async fn get_pending_commands(
|
|
pool: &PgPool,
|
|
agent_id: Uuid,
|
|
) -> Result<Vec<Command>, sqlx::Error> {
|
|
sqlx::query_as::<_, Command>(
|
|
r#"
|
|
SELECT * FROM commands
|
|
WHERE agent_id = $1 AND status = 'pending'
|
|
ORDER BY created_at ASC
|
|
"#,
|
|
)
|
|
.bind(agent_id)
|
|
.fetch_all(pool)
|
|
.await
|
|
}
|
|
|
|
/// Get command history for an agent
|
|
pub async fn get_agent_commands(
|
|
pool: &PgPool,
|
|
agent_id: Uuid,
|
|
limit: i64,
|
|
) -> Result<Vec<Command>, sqlx::Error> {
|
|
sqlx::query_as::<_, Command>(
|
|
r#"
|
|
SELECT * FROM commands
|
|
WHERE agent_id = $1
|
|
ORDER BY created_at DESC
|
|
LIMIT $2
|
|
"#,
|
|
)
|
|
.bind(agent_id)
|
|
.bind(limit)
|
|
.fetch_all(pool)
|
|
.await
|
|
}
|
|
|
|
/// Get all recent commands
|
|
pub async fn get_recent_commands(pool: &PgPool, limit: i64) -> Result<Vec<Command>, sqlx::Error> {
|
|
sqlx::query_as::<_, Command>(
|
|
r#"
|
|
SELECT * FROM commands
|
|
ORDER BY created_at DESC
|
|
LIMIT $1
|
|
"#,
|
|
)
|
|
.bind(limit)
|
|
.fetch_all(pool)
|
|
.await
|
|
}
|
|
|
|
/// Update command status to running
|
|
pub async fn mark_command_running(pool: &PgPool, id: Uuid) -> Result<(), sqlx::Error> {
|
|
sqlx::query("UPDATE commands SET status = 'running', started_at = NOW() WHERE id = $1")
|
|
.bind(id)
|
|
.execute(pool)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Update command result
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommandResult {
|
|
pub exit_code: i32,
|
|
pub stdout: String,
|
|
pub stderr: String,
|
|
}
|
|
|
|
pub async fn update_command_result(
|
|
pool: &PgPool,
|
|
id: Uuid,
|
|
result: CommandResult,
|
|
) -> Result<(), sqlx::Error> {
|
|
let status = if result.exit_code == 0 {
|
|
"completed"
|
|
} else {
|
|
"failed"
|
|
};
|
|
|
|
sqlx::query(
|
|
r#"
|
|
UPDATE commands
|
|
SET status = $1, exit_code = $2, stdout = $3, stderr = $4, completed_at = NOW()
|
|
WHERE id = $5
|
|
"#,
|
|
)
|
|
.bind(status)
|
|
.bind(result.exit_code)
|
|
.bind(&result.stdout)
|
|
.bind(&result.stderr)
|
|
.bind(id)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Delete a command
|
|
pub async fn delete_command(pool: &PgPool, id: Uuid) -> Result<bool, sqlx::Error> {
|
|
let result = sqlx::query("DELETE FROM commands WHERE id = $1")
|
|
.bind(id)
|
|
.execute(pool)
|
|
.await?;
|
|
Ok(result.rows_affected() > 0)
|
|
}
|