fix(relay): audit fixes — abort signal, model selector guard, SSE CRLF, SQL format
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

Addresses findings from deep code audit:

H-1: Pass abortController.signal to saasClient.chatCompletion() so
     user-cancelled streams actually abort the HTTP connection (was only
     stopping the read loop, leaving server-side SSE connection open).

H-2: ModelSelector now shows only when (!isTauriRuntime() || isLoggedIn).
     Prevents decorative model list in Tauri local kernel mode where model
     selection has no effect (violates CLAUDE.md §5.2).

M-1: Normalize CRLF to LF before SSE event boundary parsing (\n\n).
     Prevents buffer overflow when behind nginx/CDN with CRLF line endings.

M-2: SQL window_minute comparison uses to_char(NOW()-interval, format)
     instead of (NOW()-interval)::TEXT, matching the stored format exactly.

M-3: sort_candidates_by_quota uses same sliding 60s window as select_best_key.

LOW: Fix misleading invalidate_cache doc comment.
This commit is contained in:
iven
2026-04-09 19:51:34 +08:00
parent e6eb97dcaa
commit a081a97678
4 changed files with 10 additions and 6 deletions

View File

@@ -28,7 +28,7 @@ fn get_cache() -> &'static DashMap<String, CachedSelection> {
KEY_SELECTION_CACHE.get_or_init(DashMap::new)
}
/// Invalidate cached selection for a provider (called on usage record and 429 marking)
/// Invalidate cached selection for a provider (called on 429 marking)
fn invalidate_cache(provider_id: &str) {
let cache = get_cache();
cache.remove(provider_id);
@@ -86,7 +86,7 @@ pub async fn select_best_key(db: &PgPool, provider_id: &str, enc_key: &[u8; 32])
COALESCE(SUM(uw.token_count), 0)
FROM provider_keys pk
LEFT JOIN key_usage_window uw ON pk.id = uw.key_id
AND uw.window_minute >= (NOW() - INTERVAL '1 minute')::TEXT
AND uw.window_minute >= to_char(NOW() - INTERVAL '1 minute', 'YYYY-MM-DDTHH24:MI')
WHERE pk.provider_id = $1 AND pk.is_active = TRUE
AND (pk.cooldown_until IS NULL OR pk.cooldown_until::timestamptz <= $2)
GROUP BY pk.id, pk.key_value, pk.priority, pk.max_rpm, pk.max_tpm
@@ -200,7 +200,7 @@ pub async fn record_key_usage(
// 3. 清理过期的滑动窗口行(保留最近 2 分钟即可)
let _ = sqlx::query(
"DELETE FROM key_usage_window WHERE window_minute < (NOW() - INTERVAL '2 minutes')::TEXT"
"DELETE FROM key_usage_window WHERE window_minute < to_char(NOW() - INTERVAL '2 minutes', 'YYYY-MM-DDTHH24:MI')"
)
.execute(db).await; // 忽略错误,非关键操作

View File

@@ -651,7 +651,7 @@ pub async fn sort_candidates_by_quota(
SUM(COALESCE(pk.max_rpm, 999999) - COALESCE(uw.request_count, 0)) AS remaining_rpm
FROM provider_keys pk
LEFT JOIN key_usage_window uw ON pk.id = uw.key_id
AND uw.window_minute = to_char(date_trunc('minute', NOW()), 'YYYY-MM-DDTHH24:MI')
AND uw.window_minute >= to_char(NOW() - INTERVAL '1 minute', 'YYYY-MM-DDTHH24:MI')
WHERE pk.provider_id = ANY($1)
AND pk.is_active = TRUE
AND (pk.cooldown_until IS NULL OR pk.cooldown_until::timestamptz <= NOW())