- Split library (db/crypto/service) and MCP/Web/OAuth binary - Add deploy examples and CI/docs updates Made-with: Cursor
56 lines
1.7 KiB
Rust
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))
|
|
}
|