Files
zclaw_openfang/crates/zclaw-saas/src/workers/cleanup_refresh_tokens.rs
iven 88cac9557b
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
fix(saas): P0-2/P0-3 — usage endpoint + refresh token type mismatch
P0-2: GET /usage 500 "text >= timestamptz" — usage_records.created_at
is TEXT in actual DB despite migration declaring TIMESTAMPTZ. Fixed by
using dynamic SQL with ::timestamptz explicit casts for all date
comparisons, avoiding sqlx NULL-without-type-OID binding issues.

P0-3: POST /auth/refresh 500 — refresh_tokens.expires_at/used_at are
TEXT columns. Added ::timestamptz cast to SQL queries in auth handlers
and cleanup worker.
2026-04-10 16:25:52 +08:00

37 lines
955 B
Rust

//! 清理过期 Refresh Token Worker
use async_trait::async_trait;
use sqlx::PgPool;
use serde::{Serialize, Deserialize};
use crate::error::SaasResult;
use super::Worker;
#[derive(Debug, Serialize, Deserialize)]
pub struct CleanupRefreshTokensArgs;
pub struct CleanupRefreshTokensWorker;
#[async_trait]
impl Worker for CleanupRefreshTokensWorker {
type Args = CleanupRefreshTokensArgs;
fn name(&self) -> &str {
"cleanup_refresh_tokens"
}
async fn perform(&self, db: &PgPool, _args: Self::Args) -> SaasResult<()> {
let now = chrono::Utc::now();
let result = sqlx::query(
"DELETE FROM refresh_tokens WHERE expires_at::timestamptz < $1 OR used_at IS NOT NULL"
)
.bind(&now)
.execute(db)
.await?;
if result.rows_affected() > 0 {
tracing::info!("Cleaned up {} expired/used refresh tokens", result.rows_affected());
}
Ok(())
}
}