use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ScanResponse {
pub scan_id: String,
pub status: ScanStatus,
pub target: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub websocket_url: Option<String>,
pub queued_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_completion: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ScanStatusResponse {
pub scan_id: String,
pub status: ScanStatus,
pub progress: u8,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_stage: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub eta_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub started_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completed_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub results_url: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum ScanStatus {
Queued,
Running,
Completed,
Failed,
Cancelled,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct HealthResponse {
pub status: String,
pub version: String,
pub uptime_seconds: u64,
pub active_scans: usize,
pub queued_scans: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub database: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct StatsResponse {
pub total_scans: u64,
pub completed_scans: u64,
pub failed_scans: u64,
pub avg_scan_duration_seconds: f64,
pub scans_last_24h: u64,
pub scans_last_7d: u64,
pub top_domains: Vec<DomainStats>,
pub api_usage: ApiUsageStats,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct DomainStats {
pub domain: String,
pub scan_count: u64,
pub last_scan: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ApiUsageStats {
pub requests_last_hour: u64,
pub requests_last_day: u64,
pub avg_response_time_ms: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CertificateListResponse {
pub total: usize,
pub offset: usize,
pub limit: usize,
pub certificates: Vec<CertificateSummary>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CertificateSummary {
pub fingerprint: String,
pub common_name: String,
pub san: Vec<String>,
pub issuer: String,
pub valid_from: DateTime<Utc>,
pub valid_until: DateTime<Utc>,
pub days_until_expiry: i64,
pub is_expired: bool,
pub is_expiring_soon: bool,
pub hostnames: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PolicyResponse {
pub id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub rules: String,
pub enabled: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PolicyEvaluationResponse {
pub policy_id: String,
pub policy_name: String,
pub target: String,
pub compliant: bool,
pub checks: Vec<PolicyCheckResult>,
pub evaluated_at: DateTime<Utc>,
pub scan_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PolicyCheckResult {
pub check: String,
pub passed: bool,
pub severity: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expected: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub actual: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ScanHistoryResponse {
pub domain: String,
pub port: u16,
pub total_scans: usize,
pub scans: Vec<ScanHistoryItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ScanHistoryItem {
pub scan_id: u64,
pub timestamp: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub grade: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub score: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct HistoricalScan {
pub scan_id: String,
pub timestamp: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub grade: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub score: Option<u32>,
pub duration_ms: u64,
pub vulnerability_count: usize,
pub results_url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProgressMessage {
pub msg_type: String,
pub scan_id: String,
pub progress: u8,
pub stage: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<String>,
pub timestamp: DateTime<Utc>,
}
impl ProgressMessage {
pub fn new(scan_id: impl Into<String>, progress: u8, stage: impl Into<String>) -> Self {
Self {
msg_type: "progress".to_string(),
scan_id: scan_id.into(),
progress,
stage: stage.into(),
details: None,
timestamp: Utc::now(),
}
}
pub fn completed(scan_id: impl Into<String>) -> Self {
Self {
msg_type: "completed".to_string(),
scan_id: scan_id.into(),
progress: 100,
stage: "completed".to_string(),
details: None,
timestamp: Utc::now(),
}
}
pub fn failed(scan_id: impl Into<String>, error: impl Into<String>) -> Self {
Self {
msg_type: "failed".to_string(),
scan_id: scan_id.into(),
progress: 0,
stage: "failed".to_string(),
details: Some(error.into()),
timestamp: Utc::now(),
}
}
}