AkurAI Build
Menu

Public repository / 1f985ee57c4d7762e35f4e0bd2fbbfe716bf42ee

AkurAI-DNS / src/models.rs

Latest change 1f985ee57c4d7762e35f4e0bd2fbbfe716bf42ee - feat(domains): audited domain lifecycle - certificate-ready state, transitions, events, customer-safe status (AKURAI-DNS-4) by agent

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DomainState {
    Requested,
    Verifying,
    Provisioned,
    CertificateReady,
    Renewing,
    Failed,
    Retired,
}

impl DomainState {
    pub fn as_str(&self) -> &'static str {
        match self {
            DomainState::Requested => "requested",
            DomainState::Verifying => "verifying",
            DomainState::Provisioned => "provisioned",
            DomainState::CertificateReady => "certificate_ready",
            DomainState::Renewing => "renewing",
            DomainState::Failed => "failed",
            DomainState::Retired => "retired",
        }
    }

    pub fn from_str(s: &str) -> Option<Self> {
        match s {
            "requested" => Some(DomainState::Requested),
            "verifying" => Some(DomainState::Verifying),
            "provisioned" => Some(DomainState::Provisioned),
            "certificate_ready" => Some(DomainState::CertificateReady),
            "renewing" => Some(DomainState::Renewing),
            "failed" => Some(DomainState::Failed),
            "retired" => Some(DomainState::Retired),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DomainOwnership {
    pub id: i64,
    pub org_id: String,
    pub workspace_id: String,
    pub domain: String,
    pub state: DomainState,
    pub verification_token: Option<String>,
    pub verified_at: Option<String>,
    pub provisioned_at: Option<String>,
    pub cert_ready_at: Option<String>,
    pub expires_at: Option<String>,
    pub last_error: Option<String>,
    pub created_at: String,
    pub updated_at: String,
}

/// JSON shape for POST /api/domains — only the domain is caller-supplied.
#[derive(Debug, Deserialize)]
pub struct CreateDomainRequest {
    pub domain: String,
}

/// JSON shape for POST /api/domains/{id}/verify.
#[derive(Debug, Deserialize)]
pub struct VerifyDomainRequest {
    pub token: String,
}

/// JSON shape for POST /api/domains/{id}/fail.
#[derive(Debug, Deserialize)]
pub struct FailDomainRequest {
    pub reason: String,
}

/// JSON shape for returning a created or listed domain — the caller's own
/// full tenant-scoped view. Includes the verification token (needed by the
/// caller to prove ownership) but never any other tenant's data.
#[derive(Debug, Serialize)]
pub struct DomainResponse {
    pub id: i64,
    pub org_id: String,
    pub workspace_id: String,
    pub domain: String,
    pub state: String,
    pub verification_token: Option<String>,
    pub verified_at: Option<String>,
    pub provisioned_at: Option<String>,
    pub cert_ready_at: Option<String>,
    pub expires_at: Option<String>,
    pub last_error: Option<String>,
    pub created_at: String,
    pub updated_at: String,
}

impl From<DomainOwnership> for DomainResponse {
    fn from(d: DomainOwnership) -> Self {
        DomainResponse {
            id: d.id,
            org_id: d.org_id,
            workspace_id: d.workspace_id,
            domain: d.domain,
            state: d.state.as_str().to_string(),
            verification_token: d.verification_token,
            verified_at: d.verified_at,
            provisioned_at: d.provisioned_at,
            cert_ready_at: d.cert_ready_at,
            expires_at: d.expires_at,
            last_error: d.last_error,
            created_at: d.created_at,
            updated_at: d.updated_at,
        }
    }
}

/// Customer-safe status view for Platform — never carries the verification
/// token, timestamps beyond `updated_at`, or any zone/DNS-internal detail.
#[derive(Debug, Serialize)]
pub struct DomainStatusResponse {
    pub id: i64,
    pub domain: String,
    pub state: String,
    pub updated_at: String,
    pub last_error: Option<String>,
}

impl From<DomainOwnership> for DomainStatusResponse {
    fn from(d: DomainOwnership) -> Self {
        DomainStatusResponse {
            id: d.id,
            domain: d.domain,
            state: d.state.as_str().to_string(),
            updated_at: d.updated_at,
            last_error: d.last_error,
        }
    }
}