Skip to main content

mini_apm/models/
api_key.rs

1use crate::DbPool;
2use chrono::Utc;
3use rand::Rng;
4use sha2::{Digest, Sha256};
5
6const PREFIX: &str = "mini_apm_k_";
7
8#[derive(Debug, Clone)]
9pub struct ApiKey {
10    pub id: i64,
11    pub name: String,
12    pub created_at: String,
13    pub last_used_at: Option<String>,
14}
15
16pub fn create(pool: &DbPool, name: &str) -> anyhow::Result<String> {
17    let conn = pool.get()?;
18
19    // Generate random key
20    let random_bytes: [u8; 24] = rand::thread_rng().r#gen();
21    let raw_key = format!("{}{}", PREFIX, hex::encode(random_bytes));
22    let key_hash = hash_key(&raw_key);
23
24    conn.execute(
25        "INSERT INTO api_keys (name, key_hash, created_at) VALUES (?1, ?2, ?3)",
26        (&name, &key_hash, Utc::now().to_rfc3339()),
27    )?;
28
29    Ok(raw_key)
30}
31
32pub fn verify(pool: &DbPool, raw_key: &str) -> anyhow::Result<bool> {
33    if raw_key.is_empty() {
34        return Ok(false);
35    }
36
37    let conn = pool.get()?;
38    let key_hash = hash_key(raw_key);
39
40    let exists: bool = conn
41        .query_row(
42            "SELECT EXISTS(SELECT 1 FROM api_keys WHERE key_hash = ?1)",
43            [&key_hash],
44            |row| row.get(0),
45        )
46        .unwrap_or(false);
47
48    if exists {
49        // Update last_used_at
50        let _ = conn.execute(
51            "UPDATE api_keys SET last_used_at = ?1 WHERE key_hash = ?2",
52            (Utc::now().to_rfc3339(), &key_hash),
53        );
54    }
55
56    Ok(exists)
57}
58
59pub fn list(pool: &DbPool) -> anyhow::Result<Vec<ApiKey>> {
60    let conn = pool.get()?;
61    let mut stmt = conn
62        .prepare("SELECT id, name, created_at, last_used_at FROM api_keys ORDER BY created_at")?;
63
64    let keys = stmt
65        .query_map([], |row| {
66            Ok(ApiKey {
67                id: row.get(0)?,
68                name: row.get(1)?,
69                created_at: row.get(2)?,
70                last_used_at: row.get(3)?,
71            })
72        })?
73        .collect::<Result<Vec<_>, _>>()?;
74
75    Ok(keys)
76}
77
78fn hash_key(raw_key: &str) -> String {
79    let mut hasher = Sha256::new();
80    hasher.update(raw_key.as_bytes());
81    hex::encode(hasher.finalize())
82}