ci: enforce clippy -D warnings and cargo audit as hard gates
All checks were successful
All checks were successful
Flip both CI gates from informational to hard-fail (SPEC-001 quality gates): - clippy: `-- -D warnings` on the server crate. Cleared the debt via clippy --fix (unused imports/style), targeted #[allow(dead_code)] on native-remote-control future API, and #[allow(clippy::too_many_arguments)] on 3 protocol-mirroring fns. - cargo audit: hard-fail with documented per-ID --ignore flags (rsa RUSTSEC-2023-0071 unfixable/unreachable in active tree; gtk-rs + glib Linux-only tray backend not compiled into the Windows agent; proc-macro-error build-time). New advisories fail. - Move [profile.release] to the workspace root (it was silently ignored in the server member), activating lto/codegen-units/strip. No behavioral changes. Reviewed and gates verified passing on the build host. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,8 +60,3 @@ prometheus-client = "0.22"
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = "0.13"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
strip = true
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
//! Authentication API endpoints
|
||||
|
||||
use axum::{
|
||||
extract::{Request, State},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use axum::{extract::State, http::StatusCode, Json};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::auth::{verify_password, AuthenticatedUser, JwtConfig};
|
||||
use crate::auth::{verify_password, AuthenticatedUser};
|
||||
use crate::db;
|
||||
use crate::AppState;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Logout and token revocation endpoints
|
||||
|
||||
use axum::{
|
||||
extract::{Path, Request, State},
|
||||
extract::{Request, State},
|
||||
http::{HeaderMap, StatusCode},
|
||||
Json,
|
||||
};
|
||||
@@ -97,7 +97,7 @@ pub struct RevokeUserRequest {
|
||||
///
|
||||
/// For MVP, we're implementing the foundation but not the full user tracking.
|
||||
pub async fn revoke_user_tokens(
|
||||
State(state): State<AppState>,
|
||||
State(_state): State<AppState>,
|
||||
admin: AuthenticatedUser,
|
||||
Json(req): Json<RevokeUserRequest>,
|
||||
) -> Result<Json<LogoutResponse>, (StatusCode, Json<ErrorResponse>)> {
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{Path, Query, State},
|
||||
extract::Query,
|
||||
http::{header, StatusCode},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use tracing::{error, info, warn};
|
||||
use tracing::{error, info};
|
||||
|
||||
/// Magic marker for embedded configuration (must match agent)
|
||||
const MAGIC_MARKER: &[u8] = b"GURUCONFIG";
|
||||
|
||||
@@ -8,7 +8,7 @@ pub mod releases;
|
||||
pub mod users;
|
||||
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
extract::{Path, State},
|
||||
Json,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -78,12 +78,14 @@ impl From<crate::session::Session> for SessionInfo {
|
||||
}
|
||||
|
||||
/// List all active sessions
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn list_sessions(State(sessions): State<SessionManager>) -> Json<Vec<SessionInfo>> {
|
||||
let sessions = sessions.list_sessions().await;
|
||||
Json(sessions.into_iter().map(SessionInfo::from).collect())
|
||||
}
|
||||
|
||||
/// Get a specific session by ID
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_session(
|
||||
State(sessions): State<SessionManager>,
|
||||
Path(id): Path<String>,
|
||||
|
||||
@@ -23,11 +23,14 @@ pub struct AuthenticatedUser {
|
||||
pub user_id: String,
|
||||
pub username: String,
|
||||
pub role: String,
|
||||
#[allow(dead_code)]
|
||||
// TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub permissions: Vec<String>,
|
||||
}
|
||||
|
||||
impl AuthenticatedUser {
|
||||
/// Check if user has a specific permission
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub fn has_permission(&self, permission: &str) -> bool {
|
||||
if self.role == "admin" {
|
||||
return true;
|
||||
@@ -54,6 +57,7 @@ impl From<Claims> for AuthenticatedUser {
|
||||
|
||||
/// Authenticated agent from API key
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub struct AuthenticatedAgent {
|
||||
pub agent_id: String,
|
||||
pub org_id: String,
|
||||
@@ -61,11 +65,13 @@ pub struct AuthenticatedAgent {
|
||||
|
||||
/// JWT configuration stored in app state
|
||||
#[derive(Clone)]
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub struct AuthState {
|
||||
pub jwt_config: Arc<JwtConfig>,
|
||||
}
|
||||
|
||||
impl AuthState {
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub fn new(jwt_secret: String, expiry_hours: i64) -> Self {
|
||||
Self {
|
||||
jwt_config: Arc::new(JwtConfig::new(jwt_secret, expiry_hours)),
|
||||
@@ -122,6 +128,7 @@ where
|
||||
|
||||
/// Optional authenticated user (doesn't reject if not authenticated)
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub struct OptionalUser(pub Option<AuthenticatedUser>);
|
||||
|
||||
#[axum::async_trait]
|
||||
@@ -161,6 +168,7 @@ where
|
||||
}
|
||||
|
||||
/// Validate an agent API key (placeholder for MVP)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub fn validate_agent_key(_api_key: &str) -> Option<AuthenticatedAgent> {
|
||||
// TODO: Implement actual API key validation against database
|
||||
// For now, accept any key for agent connections
|
||||
|
||||
@@ -5,6 +5,7 @@ use serde::Deserialize;
|
||||
use std::env;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub struct Config {
|
||||
/// Address to listen on (e.g., "0.0.0.0:8080")
|
||||
pub listen_addr: String,
|
||||
|
||||
@@ -26,10 +26,13 @@ pub struct EventTypes;
|
||||
impl EventTypes {
|
||||
pub const SESSION_STARTED: &'static str = "session_started";
|
||||
pub const SESSION_ENDED: &'static str = "session_ended";
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub const SESSION_TIMEOUT: &'static str = "session_timeout";
|
||||
pub const VIEWER_JOINED: &'static str = "viewer_joined";
|
||||
pub const VIEWER_LEFT: &'static str = "viewer_left";
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub const STREAMING_STARTED: &'static str = "streaming_started";
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub const STREAMING_STOPPED: &'static str = "streaming_stopped";
|
||||
|
||||
// Failed connection events (security audit trail)
|
||||
@@ -75,6 +78,7 @@ pub async fn log_event(
|
||||
}
|
||||
|
||||
/// Get events for a session
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_session_events(
|
||||
pool: &PgPool,
|
||||
session_id: Uuid,
|
||||
@@ -88,6 +92,7 @@ pub async fn get_session_events(
|
||||
}
|
||||
|
||||
/// Get recent events (for dashboard)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_recent_events(
|
||||
pool: &PgPool,
|
||||
limit: i64,
|
||||
@@ -101,6 +106,7 @@ pub async fn get_recent_events(
|
||||
}
|
||||
|
||||
/// Get events by type
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_events_by_type(
|
||||
pool: &PgPool,
|
||||
event_type: &str,
|
||||
|
||||
@@ -48,6 +48,7 @@ pub async fn upsert_machine(
|
||||
}
|
||||
|
||||
/// Update machine status and info
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn update_machine_status(
|
||||
pool: &PgPool,
|
||||
agent_id: &str,
|
||||
|
||||
@@ -15,11 +15,7 @@ use sqlx::postgres::PgPoolOptions;
|
||||
use sqlx::PgPool;
|
||||
use tracing::info;
|
||||
|
||||
pub use events::*;
|
||||
pub use machines::*;
|
||||
pub use releases::*;
|
||||
pub use sessions::*;
|
||||
pub use support_codes::*;
|
||||
pub use users::*;
|
||||
|
||||
/// Database connection pool wrapper
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct Release {
|
||||
}
|
||||
|
||||
/// Create a new release
|
||||
#[allow(clippy::too_many_arguments)] // signature mirrors the relay/session protocol contract; refactor into a params struct tracked in docs/specs/native-remote-control/
|
||||
pub async fn create_release(
|
||||
pool: &PgPool,
|
||||
version: &str,
|
||||
@@ -157,6 +158,7 @@ pub async fn update_machine_update_status(
|
||||
}
|
||||
|
||||
/// Get machines that need updates (version < latest stable)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_machines_needing_update(
|
||||
pool: &PgPool,
|
||||
latest_version: &str,
|
||||
|
||||
@@ -64,6 +64,7 @@ pub async fn end_session(
|
||||
}
|
||||
|
||||
/// Get session by ID
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_session(
|
||||
pool: &PgPool,
|
||||
session_id: Uuid,
|
||||
@@ -75,6 +76,7 @@ pub async fn get_session(
|
||||
}
|
||||
|
||||
/// Get active sessions for a machine
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_active_sessions_for_machine(
|
||||
pool: &PgPool,
|
||||
machine_id: Uuid,
|
||||
@@ -88,6 +90,7 @@ pub async fn get_active_sessions_for_machine(
|
||||
}
|
||||
|
||||
/// Get recent sessions (for dashboard)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_recent_sessions(pool: &PgPool, limit: i64) -> Result<Vec<DbSession>, sqlx::Error> {
|
||||
sqlx::query_as::<_, DbSession>(
|
||||
"SELECT * FROM connect_sessions ORDER BY started_at DESC LIMIT $1",
|
||||
|
||||
@@ -7,6 +7,7 @@ use uuid::Uuid;
|
||||
|
||||
/// Support code record from database
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub struct DbSupportCode {
|
||||
pub id: Uuid,
|
||||
pub code: String,
|
||||
@@ -21,6 +22,7 @@ pub struct DbSupportCode {
|
||||
}
|
||||
|
||||
/// Create a new support code
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn create_support_code(
|
||||
pool: &PgPool,
|
||||
code: &str,
|
||||
@@ -40,6 +42,7 @@ pub async fn create_support_code(
|
||||
}
|
||||
|
||||
/// Get support code by code string
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_support_code(
|
||||
pool: &PgPool,
|
||||
code: &str,
|
||||
@@ -88,6 +91,7 @@ pub async fn mark_code_completed(pool: &PgPool, code: &str) -> Result<(), sqlx::
|
||||
}
|
||||
|
||||
/// Mark support code as cancelled
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn mark_code_cancelled(pool: &PgPool, code: &str) -> Result<(), sqlx::Error> {
|
||||
sqlx::query("UPDATE connect_support_codes SET status = 'cancelled' WHERE code = $1")
|
||||
.bind(code)
|
||||
@@ -97,6 +101,7 @@ pub async fn mark_code_cancelled(pool: &PgPool, code: &str) -> Result<(), sqlx::
|
||||
}
|
||||
|
||||
/// Get active support codes (pending or connected)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_active_support_codes(pool: &PgPool) -> Result<Vec<DbSupportCode>, sqlx::Error> {
|
||||
sqlx::query_as::<_, DbSupportCode>(
|
||||
"SELECT * FROM connect_support_codes WHERE status IN ('pending', 'connected') ORDER BY created_at DESC"
|
||||
@@ -106,6 +111,7 @@ pub async fn get_active_support_codes(pool: &PgPool) -> Result<Vec<DbSupportCode
|
||||
}
|
||||
|
||||
/// Check if code exists and is valid for connection
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn is_code_valid(pool: &PgPool, code: &str) -> Result<bool, sqlx::Error> {
|
||||
let result = sqlx::query_scalar::<_, bool>(
|
||||
"SELECT EXISTS(SELECT 1 FROM connect_support_codes WHERE code = $1 AND status = 'pending')",
|
||||
@@ -117,6 +123,7 @@ pub async fn is_code_valid(pool: &PgPool, code: &str) -> Result<bool, sqlx::Erro
|
||||
}
|
||||
|
||||
/// Check if code is cancelled
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn is_code_cancelled(pool: &PgPool, code: &str) -> Result<bool, sqlx::Error> {
|
||||
let result = sqlx::query_scalar::<_, bool>(
|
||||
"SELECT EXISTS(SELECT 1 FROM connect_support_codes WHERE code = $1 AND status = 'cancelled')"
|
||||
@@ -128,6 +135,7 @@ pub async fn is_code_cancelled(pool: &PgPool, code: &str) -> Result<bool, sqlx::
|
||||
}
|
||||
|
||||
/// Link session to support code
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn link_session_to_code(
|
||||
pool: &PgPool,
|
||||
code: &str,
|
||||
|
||||
@@ -15,12 +15,15 @@ pub struct User {
|
||||
pub role: String,
|
||||
pub enabled: bool,
|
||||
pub created_at: DateTime<Utc>,
|
||||
#[allow(dead_code)]
|
||||
// TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub last_login: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// User without password hash (for API responses)
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub struct UserInfo {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
@@ -193,6 +196,7 @@ pub async fn set_user_permissions(
|
||||
}
|
||||
|
||||
/// Get user's accessible client IDs (empty = all access)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_user_client_access(pool: &PgPool, user_id: Uuid) -> Result<Vec<Uuid>> {
|
||||
let clients: Vec<(Uuid,)> =
|
||||
sqlx::query_as("SELECT client_id FROM user_client_access WHERE user_id = $1")
|
||||
@@ -226,6 +230,7 @@ pub async fn set_user_client_access(
|
||||
}
|
||||
|
||||
/// Check if user has access to a specific client
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn user_has_client_access(pool: &PgPool, user_id: Uuid, client_id: Uuid) -> Result<bool> {
|
||||
// Admins have access to all
|
||||
let user = get_user_by_id(pool, user_id).await?;
|
||||
|
||||
@@ -31,7 +31,7 @@ use axum::{
|
||||
use serde::Deserialize;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use tower_http::cors::{AllowOrigin, Any, CorsLayer};
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_http::trace::TraceLayer;
|
||||
use tracing::{info, Level};
|
||||
@@ -76,7 +76,7 @@ async fn auth_layer(
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Initialize logging
|
||||
let _subscriber = FmtSubscriber::builder()
|
||||
FmtSubscriber::builder()
|
||||
.with_max_level(Level::INFO)
|
||||
.with_target(true)
|
||||
.init();
|
||||
@@ -359,7 +359,7 @@ async fn main() -> Result<()> {
|
||||
.layer(TraceLayer::new_for_http())
|
||||
// SEC-11: Restricted CORS configuration
|
||||
.layer({
|
||||
let cors = CorsLayer::new()
|
||||
CorsLayer::new()
|
||||
// Allow requests from the production domain and localhost (for development)
|
||||
.allow_origin([
|
||||
"https://connect.azcomputerguru.com"
|
||||
@@ -383,8 +383,7 @@ async fn main() -> Result<()> {
|
||||
axum::http::header::ACCEPT,
|
||||
])
|
||||
// Allow credentials (cookies, auth headers)
|
||||
.allow_credentials(true);
|
||||
cors
|
||||
.allow_credentials(true)
|
||||
});
|
||||
|
||||
// Start server
|
||||
@@ -437,6 +436,7 @@ async fn list_codes(
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
struct ValidateParams {
|
||||
code: String,
|
||||
}
|
||||
|
||||
@@ -288,6 +288,7 @@ pub async fn viewer_ws_handler(
|
||||
}
|
||||
|
||||
/// Handle an agent WebSocket connection
|
||||
#[allow(clippy::too_many_arguments)] // signature mirrors the relay/session protocol contract; refactor into a params struct tracked in docs/specs/native-remote-control/
|
||||
async fn handle_agent_connection(
|
||||
socket: WebSocket,
|
||||
sessions: SessionManager,
|
||||
@@ -318,7 +319,7 @@ async fn handle_agent_connection(
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
if prost::Message::encode(&disconnect_msg, &mut buf).is_ok() {
|
||||
let _ = ws_sender.send(Message::Binary(buf.into())).await;
|
||||
let _ = ws_sender.send(Message::Binary(buf)).await;
|
||||
}
|
||||
let _ = ws_sender.close().await;
|
||||
return;
|
||||
@@ -335,7 +336,7 @@ async fn handle_agent_connection(
|
||||
info!("Session created: {} (agent in idle mode)", session_id);
|
||||
|
||||
// Database: upsert machine and create session record
|
||||
let machine_id = if let Some(ref db) = db {
|
||||
let _machine_id = if let Some(ref db) = db {
|
||||
match db::machines::upsert_machine(db.pool(), &agent_id, &agent_name, is_persistent).await {
|
||||
Ok(machine) => {
|
||||
// Create session record
|
||||
@@ -401,11 +402,7 @@ async fn handle_agent_connection(
|
||||
let input_forward = tokio::spawn(async move {
|
||||
while let Some(input_data) = input_rx.recv().await {
|
||||
let mut sender = ws_sender_input.lock().await;
|
||||
if sender
|
||||
.send(Message::Binary(input_data.into()))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
if sender.send(Message::Binary(input_data)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -435,7 +432,7 @@ async fn handle_agent_connection(
|
||||
let mut buf = Vec::new();
|
||||
if prost::Message::encode(&disconnect_msg, &mut buf).is_ok() {
|
||||
let mut sender = ws_sender_cancel.lock().await;
|
||||
let _ = sender.send(Message::Binary(buf.into())).await;
|
||||
let _ = sender.send(Message::Binary(buf)).await;
|
||||
let _ = sender.close().await;
|
||||
}
|
||||
break;
|
||||
@@ -651,11 +648,7 @@ async fn handle_viewer_connection(
|
||||
// Task to forward frames from agent to this viewer
|
||||
let frame_forward = tokio::spawn(async move {
|
||||
while let Ok(frame_data) = frame_rx.recv().await {
|
||||
if ws_sender
|
||||
.send(Message::Binary(frame_data.into()))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
if ws_sender.send(Message::Binary(frame_data)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ pub struct ViewerInfo {
|
||||
}
|
||||
|
||||
/// Heartbeat timeout (90 seconds - 3x the agent's 30 second interval)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
const HEARTBEAT_TIMEOUT_SECS: u64 = 90;
|
||||
|
||||
/// Session state
|
||||
@@ -68,6 +69,8 @@ struct SessionData {
|
||||
frame_tx: FrameSender,
|
||||
/// Channel for input events (viewer -> agent)
|
||||
input_tx: InputSender,
|
||||
#[allow(dead_code)]
|
||||
// TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
input_rx: Option<InputReceiver>,
|
||||
/// Map of connected viewers (id -> info)
|
||||
viewers: HashMap<ViewerId, ViewerInfo>,
|
||||
@@ -176,6 +179,7 @@ impl SessionManager {
|
||||
}
|
||||
|
||||
/// Update agent status from heartbeat or status message
|
||||
#[allow(clippy::too_many_arguments)] // signature mirrors the relay/session protocol contract; refactor into a params struct tracked in docs/specs/native-remote-control/
|
||||
pub async fn update_agent_status(
|
||||
&self,
|
||||
session_id: SessionId,
|
||||
@@ -225,6 +229,7 @@ impl SessionManager {
|
||||
}
|
||||
|
||||
/// Check if a session has timed out (no heartbeat for too long)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn is_session_timed_out(&self, session_id: SessionId) -> bool {
|
||||
let sessions = self.sessions.read().await;
|
||||
if let Some(session_data) = sessions.get(&session_id) {
|
||||
@@ -235,6 +240,7 @@ impl SessionManager {
|
||||
}
|
||||
|
||||
/// Get sessions that have timed out
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_timed_out_sessions(&self) -> Vec<SessionId> {
|
||||
let sessions = self.sessions.read().await;
|
||||
sessions
|
||||
@@ -471,11 +477,9 @@ impl SessionManager {
|
||||
};
|
||||
|
||||
let mut buf = Vec::new();
|
||||
if admin_cmd.encode(&mut buf).is_ok() {
|
||||
if session_data.input_tx.send(buf).await.is_ok() {
|
||||
tracing::info!("Sent admin command {:?} to session {}", command, session_id);
|
||||
return true;
|
||||
}
|
||||
if admin_cmd.encode(&mut buf).is_ok() && session_data.input_tx.send(buf).await.is_ok() {
|
||||
tracing::info!("Sent admin command {:?} to session {}", command, session_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
||||
@@ -36,6 +36,8 @@ pub enum CodeStatus {
|
||||
/// Request to create a new support code
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateCodeRequest {
|
||||
#[allow(dead_code)]
|
||||
// TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub technician_id: Option<String>,
|
||||
pub technician_name: Option<String>,
|
||||
}
|
||||
@@ -172,6 +174,7 @@ impl SupportCodeManager {
|
||||
}
|
||||
|
||||
/// Get code by its code string
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_code(&self, code: &str) -> Option<SupportCode> {
|
||||
let codes = self.codes.read().await;
|
||||
codes.get(code).cloned()
|
||||
@@ -209,6 +212,7 @@ impl SupportCodeManager {
|
||||
}
|
||||
|
||||
/// Check if a code is valid for connection (exists and is pending)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn is_valid_for_connection(&self, code: &str) -> bool {
|
||||
let codes = self.codes.read().await;
|
||||
codes
|
||||
@@ -218,6 +222,7 @@ impl SupportCodeManager {
|
||||
}
|
||||
|
||||
/// List all codes (for dashboard)
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn list_codes(&self) -> Vec<SupportCode> {
|
||||
let codes = self.codes.read().await;
|
||||
codes.values().cloned().collect()
|
||||
@@ -234,6 +239,7 @@ impl SupportCodeManager {
|
||||
}
|
||||
|
||||
/// Get code by session ID
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub async fn get_by_session(&self, session_id: Uuid) -> Option<SupportCode> {
|
||||
let session_to_code = self.session_to_code.read().await;
|
||||
let code = session_to_code.get(&session_id)?;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! IP address extraction from WebSocket connections
|
||||
|
||||
use axum::extract::ConnectInfo;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
/// Extract IP address from Axum ConnectInfo
|
||||
@@ -12,11 +11,13 @@ use std::net::{IpAddr, SocketAddr};
|
||||
/// // Use ip for logging
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub fn extract_ip(addr: &SocketAddr) -> IpAddr {
|
||||
addr.ip()
|
||||
}
|
||||
|
||||
/// Extract IP address as string
|
||||
#[allow(dead_code)] // TODO(native-remote-control): consumed by the integration API; see docs/specs/native-remote-control/
|
||||
pub fn extract_ip_string(addr: &SocketAddr) -> String {
|
||||
addr.ip().to_string()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user