fix(saas): P0-2/P0-3 — usage endpoint + refresh token type mismatch
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

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.
This commit is contained in:
iven
2026-04-10 16:25:52 +08:00
parent 12a018cc74
commit 88cac9557b
3 changed files with 59 additions and 34 deletions

View File

@@ -331,7 +331,7 @@ pub async fn refresh(
// 3. 从 DB 查找 refresh token确保未被使用
let row: Option<(String,)> = sqlx::query_as(
"SELECT account_id FROM refresh_tokens WHERE jti = $1 AND used_at IS NULL AND expires_at > $2"
"SELECT account_id FROM refresh_tokens WHERE jti = $1 AND used_at IS NULL AND expires_at::timestamptz > $2"
)
.bind(jti)
.bind(&chrono::Utc::now())
@@ -567,7 +567,7 @@ async fn cleanup_expired_refresh_tokens(db: &sqlx::PgPool) -> SaasResult<()> {
let now = chrono::Utc::now();
// 删除过期超过 30 天的已使用 token (减少 DB 膨胀)
sqlx::query(
"DELETE FROM refresh_tokens WHERE (used_at IS NOT NULL AND used_at < $1) OR (expires_at < $1)"
"DELETE FROM refresh_tokens WHERE (used_at IS NOT NULL AND used_at::timestamptz < $1) OR (expires_at::timestamptz < $1)"
)
.bind(&now)
.execute(db).await?;