use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::entities::api_key_scope::ApiKeyScope;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiKey {
pub id: Uuid,
pub user_id: Uuid,
pub name: String,
#[serde(skip_serializing)]
pub key_hash: String,
pub key_prefix: String,
pub scopes: Vec<ApiKeyScope>,
pub is_active: bool,
pub expires_at: Option<DateTime<Utc>>,
pub last_used_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone)]
pub struct NewApiKey {
pub user_id: Uuid,
pub name: String,
pub key_hash: String,
pub key_prefix: String,
pub scopes: Vec<ApiKeyScope>,
pub expires_at: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Default)]
pub struct ApiKeyUpdate {
pub name: Option<String>,
pub scopes: Option<Vec<ApiKeyScope>>,
pub is_active: Option<bool>,
pub expires_at: Option<Option<DateTime<Utc>>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn api_key_serde_excludes_hash() {
let key = ApiKey {
id: Uuid::now_v7(),
user_id: Uuid::now_v7(),
name: "test-key".to_string(),
key_hash: "secret_hash".to_string(),
key_prefix: "irfl_abc".to_string(),
scopes: vec![ApiKeyScope::RunsRead],
is_active: true,
expires_at: None,
last_used_at: None,
created_at: Utc::now(),
updated_at: Utc::now(),
};
let json = serde_json::to_string(&key).expect("serialize");
assert!(!json.contains("secret_hash"));
assert!(json.contains("test-key"));
assert!(json.contains("irfl_abc"));
}
#[test]
fn api_key_update_default_is_empty() {
let update = ApiKeyUpdate::default();
assert!(update.name.is_none());
assert!(update.scopes.is_none());
assert!(update.is_active.is_none());
assert!(update.expires_at.is_none());
}
}