Files
secrets/crates/secrets-core/src/service/api_key.rs
voson 49fb7430a8 refactor: workspace secrets-core + secrets-mcp MCP SaaS
- Split library (db/crypto/service) and MCP/Web/OAuth binary
- Add deploy examples and CI/docs updates

Made-with: Cursor
2026-03-20 17:36:00 +08:00

56 lines
1.7 KiB
Rust

use anyhow::Result;
use sqlx::PgPool;
use uuid::Uuid;
const KEY_PREFIX: &str = "sk_";
/// Generate a new API key: `sk_<64 hex chars>` = 67 characters total.
pub fn generate_api_key() -> String {
use rand::RngExt;
let mut bytes = [0u8; 32];
rand::rng().fill(&mut bytes);
let hex: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
format!("{}{}", KEY_PREFIX, hex)
}
/// Return the user's existing API key, or generate and store a new one if NULL.
pub async fn ensure_api_key(pool: &PgPool, user_id: Uuid) -> Result<String> {
let existing: Option<(Option<String>,)> =
sqlx::query_as("SELECT api_key FROM users WHERE id = $1")
.bind(user_id)
.fetch_optional(pool)
.await?;
if let Some((Some(key),)) = existing {
return Ok(key);
}
let new_key = generate_api_key();
sqlx::query("UPDATE users SET api_key = $1 WHERE id = $2")
.bind(&new_key)
.bind(user_id)
.execute(pool)
.await?;
Ok(new_key)
}
/// Generate a fresh API key for the user, replacing the old one.
pub async fn regenerate_api_key(pool: &PgPool, user_id: Uuid) -> Result<String> {
let new_key = generate_api_key();
sqlx::query("UPDATE users SET api_key = $1 WHERE id = $2")
.bind(&new_key)
.bind(user_id)
.execute(pool)
.await?;
Ok(new_key)
}
/// Validate a Bearer token. Returns the `user_id` if the key matches.
pub async fn validate_api_key(pool: &PgPool, raw_key: &str) -> Result<Option<Uuid>> {
let row: Option<(Uuid,)> = sqlx::query_as("SELECT id FROM users WHERE api_key = $1")
.bind(raw_key)
.fetch_optional(pool)
.await?;
Ok(row.map(|(id,)| id))
}