use mnm_core::types::TokenLimitOverride;
use sqlx::PgPool;
use time::OffsetDateTime;
use uuid::Uuid;
use crate::error::{Result, StoreError};
const COLS: &str =
"id, subject_kind, subject, hourly, daily, expires_at, note, created_by, created_at";
#[allow(clippy::too_many_arguments)]
pub async fn insert(
pool: &PgPool,
subject_kind: &str,
subject: &str,
hourly: i64,
daily: i64,
expires_at: OffsetDateTime,
note: Option<&str>,
created_by: &str,
) -> Result<TokenLimitOverride> {
let normalised = if subject_kind == "cidr" {
"network($2::inet)::text"
} else {
"$2"
};
let sql = format!(
"INSERT INTO token_limit_override \
(subject_kind, subject, hourly, daily, expires_at, note, created_by) \
VALUES ($1, {normalised}, $3, $4, $5, $6, $7) \
RETURNING {COLS}"
);
let row: Row = sqlx::query_as(&sql)
.bind(subject_kind)
.bind(subject)
.bind(hourly)
.bind(daily)
.bind(expires_at)
.bind(note)
.bind(created_by)
.fetch_one(pool)
.await?;
Ok(row.into())
}
pub async fn list_active(pool: &PgPool) -> Result<Vec<TokenLimitOverride>> {
let rows = sqlx::query_as::<_, Row>(&format!(
"SELECT {COLS} FROM token_limit_override \
WHERE expires_at > now() ORDER BY created_at DESC"
))
.fetch_all(pool)
.await?;
Ok(rows.into_iter().map(Into::into).collect())
}
pub async fn get_by_id(pool: &PgPool, id: Uuid) -> Result<TokenLimitOverride> {
let row =
sqlx::query_as::<_, Row>(&format!("SELECT {COLS} FROM token_limit_override WHERE id = $1"))
.bind(id)
.fetch_one(pool)
.await?;
Ok(row.into())
}
#[derive(Debug, Default, Clone)]
pub struct Patch {
pub expires_at: Option<OffsetDateTime>,
pub hourly: Option<i64>,
pub daily: Option<i64>,
pub note: Option<String>,
}
pub async fn update(pool: &PgPool, id: Uuid, patch: Patch) -> Result<TokenLimitOverride> {
let row = sqlx::query_as::<_, Row>(
"UPDATE token_limit_override SET \
expires_at = COALESCE($2, expires_at), \
hourly = COALESCE($3, hourly), \
daily = COALESCE($4, daily), \
note = COALESCE($5, note) \
WHERE id = $1 \
RETURNING id, subject_kind, subject, hourly, daily, expires_at, note, created_by, created_at",
)
.bind(id)
.bind(patch.expires_at)
.bind(patch.hourly)
.bind(patch.daily)
.bind(patch.note)
.fetch_optional(pool)
.await?;
row.map_or(Err(StoreError::NotFound), |r| Ok(r.into()))
}
pub async fn delete(pool: &PgPool, id: Uuid) -> Result<TokenLimitOverride> {
let row = sqlx::query_as::<_, Row>(
"DELETE FROM token_limit_override WHERE id = $1 \
RETURNING id, subject_kind, subject, hourly, daily, expires_at, note, created_by, created_at",
)
.bind(id)
.fetch_optional(pool)
.await?;
row.map_or(Err(StoreError::NotFound), |r| Ok(r.into()))
}
#[derive(sqlx::FromRow)]
struct Row {
id: Uuid,
subject_kind: String,
subject: String,
hourly: i64,
daily: i64,
expires_at: OffsetDateTime,
note: Option<String>,
created_by: String,
created_at: OffsetDateTime,
}
impl From<Row> for TokenLimitOverride {
fn from(r: Row) -> Self {
Self {
id: r.id,
subject_kind: r.subject_kind,
subject: r.subject,
hourly: r.hourly,
daily: r.daily,
expires_at: r.expires_at,
note: r.note,
created_by: r.created_by,
created_at: r.created_at,
}
}
}