fix: update the tracking usage filter for the usage page

This commit is contained in:
2026-07-11 18:36:55 +07:00
parent 40606ce39f
commit e1232ed1bd
11 changed files with 422 additions and 241 deletions
+15
View File
@@ -30,6 +30,21 @@ describe("usage access scope SQL predicates", () => {
expect(params).toEqual(["connection-a", "connection-b", "key-a"]);
});
it("uses persisted actor attribution when a user id is available", () => {
const conditions = [];
const params = [];
appendUsageAccessClause(conditions, params, {
isAdmin: false,
userId: "user-a",
connectionIds: ["connection-a"],
apiKeys: ["key-a"],
});
expect(conditions).toEqual(["userId = ?"]);
expect(params).toEqual(["user-a"]);
});
it("denies users with no attributable resources", () => {
const conditions = [];
const params = [];
+64
View File
@@ -0,0 +1,64 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
const originalDataDir = process.env.DATA_DIR;
let tempDir;
let db;
beforeAll(async () => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "9router-usage-roles-"));
process.env.DATA_DIR = tempDir;
vi.resetModules();
db = await import("@/lib/db/index.js");
await db.initDb();
});
afterAll(() => {
if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
if (originalDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = originalDataDir;
});
describe("usage role breakdowns", () => {
it("attributes requests to the API key owner and exposes breakdowns by role", async () => {
const keyOwner = await db.createUser({ username: "usage-key-owner", password: "password", role: "user" });
const connectionOwner = await db.createUser({ username: "usage-connection-owner", password: "password", role: "user" });
const admin = await db.createUser({ username: "usage-admin", password: "password", role: "admin" });
const apiKey = await db.createApiKey("usage-owned-key", "usage-machine", keyOwner.id);
const connection = await db.createProviderConnection({
provider: "usage-test",
authType: "apikey",
name: "usage connection",
apiKey: "upstream-key",
ownerId: connectionOwner.id,
});
await db.saveRequestUsage({
timestamp: new Date().toISOString(),
provider: "usage-test",
model: "test-model",
connectionId: connection.id,
apiKey: apiKey.key,
endpoint: "/v1/chat/completions",
tokens: { prompt_tokens: 10, completion_tokens: 5 },
});
const keyOwnerStats = await db.getUsageStats("24h", keyOwner);
expect(keyOwnerStats.totalRequests).toBe(1);
expect(keyOwnerStats.availableTableViews).toEqual(["model", "endpoint"]);
expect(keyOwnerStats).not.toHaveProperty("byUser");
expect(keyOwnerStats).not.toHaveProperty("byApiKey");
expect(keyOwnerStats).not.toHaveProperty("byAccount");
const connectionOwnerStats = await db.getUsageStats("24h", connectionOwner);
expect(connectionOwnerStats.totalRequests).toBe(0);
const adminStats = await db.getUsageStats("24h", admin);
expect(adminStats.availableTableViews).toEqual(["model", "user", "apiKey", "endpoint"]);
expect(Object.values(adminStats.byUser)).toEqual(expect.arrayContaining([
expect.objectContaining({ username: keyOwner.username, requests: 1 }),
]));
});
});