mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
Resynced onto the release tip after #12051/#12052/#12053 landed. Same LKGP-clear conflict as #12053 (kept the current clearStaleLKGP() helper at both call sites). One additional issue this final phase's combined-worktree validation surfaced: clearStaleLKGP() itself (added by #12013, which none of the 4 phase PRs could have seen since it landed after they were authored) still had a dynamic `await import("@/lib/localDb")` — a real break once this PR deletes the barrel. Fixed to `await import("@/lib/db/settings")`, matching the direct-import pattern used at every other call site. typecheck:core, check-db-rules, check:cycles, and the eslint-import-boundaries regression test (3/3, including "G14 rejects localDb barrel imports") all green after resync — zero barrel-importing production files remain. Nice clean 5-phase migration, and thanks for taking on the full #11795 cleanup.
97 lines
2.9 KiB
TypeScript
97 lines
2.9 KiB
TypeScript
/**
|
|
* db/quotaModelCaps.ts — CRUD for quota_allocation_model_caps table.
|
|
*
|
|
* Per-(pool_id, api_key_id, model) budget caps for the Quota Share Engine.
|
|
* Closes the "one key drains the pool on a single model" attack (Fase 3 #7).
|
|
*
|
|
* cap_unit aligns with QuotaUnit: "requests" | "tokens" | "usd" | "percent".
|
|
* cap_value of ≤ Number.EPSILON is treated as a placeholder by the enforce
|
|
* layer (not enforced), consistent with the planRegistry EPSILON convention.
|
|
*
|
|
* All SQL goes through prepared statements — never raw string interpolation
|
|
* (Hard Rule #5).
|
|
*/
|
|
|
|
import { getDbInstance } from "./core";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type QuotaUnit = "percent" | "requests" | "tokens" | "usd";
|
|
|
|
export interface ModelCap {
|
|
poolId: string;
|
|
apiKeyId: string;
|
|
model: string;
|
|
capValue: number;
|
|
capUnit: QuotaUnit;
|
|
}
|
|
|
|
interface ModelCapRow {
|
|
pool_id: string;
|
|
api_key_id: string;
|
|
model: string;
|
|
cap_value: number;
|
|
cap_unit: string;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Internal helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
interface StatementLike<TRow = unknown> {
|
|
all: (...params: unknown[]) => TRow[];
|
|
get: (...params: unknown[]) => TRow | undefined;
|
|
run: (...params: unknown[]) => { changes: number };
|
|
}
|
|
|
|
interface DbLike {
|
|
prepare: <TRow = unknown>(sql: string) => StatementLike<TRow>;
|
|
}
|
|
|
|
function rowToModelCap(row: ModelCapRow): ModelCap {
|
|
return {
|
|
poolId: row.pool_id,
|
|
apiKeyId: row.api_key_id,
|
|
model: row.model,
|
|
capValue: row.cap_value,
|
|
capUnit: row.cap_unit as QuotaUnit,
|
|
};
|
|
}
|
|
|
|
function getDb(): DbLike {
|
|
return getDbInstance() as unknown as DbLike;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public API
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Retrieve the cap for a specific (pool, key, model) triple.
|
|
* Returns null if no cap is configured.
|
|
*/
|
|
export function getModelCap(poolId: string, apiKeyId: string, model: string): ModelCap | null {
|
|
const row = getDb()
|
|
.prepare<ModelCapRow>(
|
|
`SELECT pool_id, api_key_id, model, cap_value, cap_unit
|
|
FROM quota_allocation_model_caps
|
|
WHERE pool_id = ? AND api_key_id = ? AND model = ?`
|
|
)
|
|
.get(poolId, apiKeyId, model);
|
|
return row ? rowToModelCap(row) : null;
|
|
}
|
|
export function setModelCap(cap: ModelCap): void {
|
|
getDb()
|
|
.prepare(
|
|
`INSERT INTO quota_allocation_model_caps
|
|
(pool_id, api_key_id, model, cap_value, cap_unit)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
ON CONFLICT(pool_id, api_key_id, model) DO UPDATE SET
|
|
cap_value = excluded.cap_value,
|
|
cap_unit = excluded.cap_unit`
|
|
)
|
|
.run(cap.poolId, cap.apiKeyId, cap.model, cap.capValue, cap.capUnit);
|
|
}
|