@opentrust/db 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +51 -0
- package/dist/dialect.d.ts +3 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +12 -0
- package/dist/generate.d.ts +2 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +20 -0
- package/dist/helpers.d.ts +11 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +32 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/migrate.d.ts +2 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +61 -0
- package/dist/queries/agents.d.ts +25 -0
- package/dist/queries/agents.d.ts.map +1 -0
- package/dist/queries/agents.js +46 -0
- package/dist/queries/auth.d.ts +18 -0
- package/dist/queries/auth.d.ts.map +1 -0
- package/dist/queries/auth.js +77 -0
- package/dist/queries/detection-results.d.ts +24 -0
- package/dist/queries/detection-results.d.ts.map +1 -0
- package/dist/queries/detection-results.js +43 -0
- package/dist/queries/observations.d.ts +58 -0
- package/dist/queries/observations.d.ts.map +1 -0
- package/dist/queries/observations.js +212 -0
- package/dist/queries/policies.d.ts +25 -0
- package/dist/queries/policies.d.ts.map +1 -0
- package/dist/queries/policies.js +38 -0
- package/dist/queries/scanners.d.ts +25 -0
- package/dist/queries/scanners.d.ts.map +1 -0
- package/dist/queries/scanners.js +56 -0
- package/dist/queries/settings.d.ts +8 -0
- package/dist/queries/settings.d.ts.map +1 -0
- package/dist/queries/settings.js +30 -0
- package/dist/queries/usage.d.ts +18 -0
- package/dist/queries/usage.d.ts.map +1 -0
- package/dist/queries/usage.js +54 -0
- package/dist/schema/index.d.ts +4415 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +19 -0
- package/dist/schema/mysql.d.ts +1479 -0
- package/dist/schema/mysql.d.ts.map +1 -0
- package/dist/schema/mysql.js +151 -0
- package/dist/schema/pg.d.ts +1479 -0
- package/dist/schema/pg.d.ts.map +1 -0
- package/dist/schema/pg.js +151 -0
- package/dist/schema/sqlite.d.ts +1479 -0
- package/dist/schema/sqlite.d.ts.map +1 -0
- package/dist/schema/sqlite.js +153 -0
- package/dist/seed.d.ts +2 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +49 -0
- package/drizzle/sqlite/0000_serious_martin_li.sql +143 -0
- package/drizzle/sqlite/meta/0000_snapshot.json +945 -0
- package/drizzle/sqlite/meta/_journal.json +13 -0
- package/drizzle.config.mysql.ts +10 -0
- package/drizzle.config.pg.ts +10 -0
- package/drizzle.config.sqlite.ts +10 -0
- package/package.json +55 -0
- package/src/client.ts +66 -0
- package/src/dialect.ts +13 -0
- package/src/generate.ts +26 -0
- package/src/helpers.ts +47 -0
- package/src/index.ts +12 -0
- package/src/migrate.ts +74 -0
- package/src/queries/agents.ts +68 -0
- package/src/queries/auth.ts +94 -0
- package/src/queries/detection-results.ts +58 -0
- package/src/queries/observations.ts +275 -0
- package/src/queries/policies.ts +59 -0
- package/src/queries/scanners.ts +74 -0
- package/src/queries/settings.ts +34 -0
- package/src/queries/usage.ts +69 -0
- package/src/schema/index.ts +22 -0
- package/src/schema/mysql.ts +207 -0
- package/src/schema/pg.ts +208 -0
- package/src/schema/sqlite.ts +199 -0
- package/src/seed.ts +56 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { eq, and, desc, count, sql } from "drizzle-orm";
|
|
2
|
+
import { toolCallObservations, agentPermissions } from "../schema/index.js";
|
|
3
|
+
import { DEFAULT_TENANT_ID } from "@opentrust/shared";
|
|
4
|
+
// ─── Tool name → category / access pattern inference ────────────
|
|
5
|
+
const ACCESS_READ_PREFIXES = ["list", "get", "search", "read", "fetch", "find", "query", "check", "view"];
|
|
6
|
+
const ACCESS_WRITE_PREFIXES = ["create", "send", "write", "update", "edit", "post", "put", "add", "set"];
|
|
7
|
+
const ACCESS_ADMIN_PREFIXES = ["delete", "remove", "execute", "run", "admin", "destroy", "revoke", "drop"];
|
|
8
|
+
export function inferCategory(toolName) {
|
|
9
|
+
// "github_create_issue" → "github"
|
|
10
|
+
// "slack_send_message" → "slack"
|
|
11
|
+
// "read_file" → "filesystem"
|
|
12
|
+
const lower = toolName.toLowerCase();
|
|
13
|
+
if (lower.startsWith("read_file") || lower.startsWith("write_file") || lower.startsWith("list_dir")) {
|
|
14
|
+
return "filesystem";
|
|
15
|
+
}
|
|
16
|
+
if (lower.startsWith("execute_command") || lower.startsWith("run_command") || lower === "bash") {
|
|
17
|
+
return "shell";
|
|
18
|
+
}
|
|
19
|
+
const underscoreIdx = lower.indexOf("_");
|
|
20
|
+
if (underscoreIdx > 0) {
|
|
21
|
+
return lower.slice(0, underscoreIdx);
|
|
22
|
+
}
|
|
23
|
+
return lower;
|
|
24
|
+
}
|
|
25
|
+
export function inferAccessPattern(toolName) {
|
|
26
|
+
const lower = toolName.toLowerCase();
|
|
27
|
+
// Strip category prefix: "github_create_issue" → "create_issue"
|
|
28
|
+
const underscoreIdx = lower.indexOf("_");
|
|
29
|
+
const action = underscoreIdx > 0 ? lower.slice(underscoreIdx + 1) : lower;
|
|
30
|
+
for (const prefix of ACCESS_ADMIN_PREFIXES) {
|
|
31
|
+
if (action.startsWith(prefix))
|
|
32
|
+
return "admin";
|
|
33
|
+
}
|
|
34
|
+
for (const prefix of ACCESS_WRITE_PREFIXES) {
|
|
35
|
+
if (action.startsWith(prefix))
|
|
36
|
+
return "write";
|
|
37
|
+
}
|
|
38
|
+
for (const prefix of ACCESS_READ_PREFIXES) {
|
|
39
|
+
if (action.startsWith(prefix))
|
|
40
|
+
return "read";
|
|
41
|
+
}
|
|
42
|
+
return "unknown";
|
|
43
|
+
}
|
|
44
|
+
// ─── Query Functions ────────────────────────────────────────────
|
|
45
|
+
export function observationQueries(db) {
|
|
46
|
+
return {
|
|
47
|
+
/**
|
|
48
|
+
* Record a tool call observation.
|
|
49
|
+
*/
|
|
50
|
+
async record(data) {
|
|
51
|
+
const category = inferCategory(data.toolName);
|
|
52
|
+
const accessPattern = inferAccessPattern(data.toolName);
|
|
53
|
+
const tenantId = data.tenantId ?? DEFAULT_TENANT_ID;
|
|
54
|
+
await db.insert(toolCallObservations).values({
|
|
55
|
+
agentId: data.agentId,
|
|
56
|
+
sessionKey: data.sessionKey ?? null,
|
|
57
|
+
toolName: data.toolName,
|
|
58
|
+
category,
|
|
59
|
+
accessPattern,
|
|
60
|
+
paramsJson: data.params ?? null,
|
|
61
|
+
phase: data.phase,
|
|
62
|
+
resultJson: data.result ?? null,
|
|
63
|
+
error: data.error ?? null,
|
|
64
|
+
durationMs: data.durationMs ?? null,
|
|
65
|
+
blocked: data.blocked ?? false,
|
|
66
|
+
blockReason: data.blockReason ?? null,
|
|
67
|
+
tenantId,
|
|
68
|
+
});
|
|
69
|
+
// Upsert permission on "after" phase (or "before" if blocked)
|
|
70
|
+
if (data.phase === "after" || data.blocked) {
|
|
71
|
+
await this.upsertPermission({
|
|
72
|
+
agentId: data.agentId,
|
|
73
|
+
toolName: data.toolName,
|
|
74
|
+
category,
|
|
75
|
+
accessPattern,
|
|
76
|
+
params: data.params,
|
|
77
|
+
hasError: !!data.error,
|
|
78
|
+
tenantId,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Upsert an agent permission entry based on observed tool call.
|
|
84
|
+
*/
|
|
85
|
+
async upsertPermission(data) {
|
|
86
|
+
const now = new Date().toISOString();
|
|
87
|
+
// Check if permission already exists
|
|
88
|
+
const existing = await db
|
|
89
|
+
.select()
|
|
90
|
+
.from(agentPermissions)
|
|
91
|
+
.where(and(eq(agentPermissions.tenantId, data.tenantId), eq(agentPermissions.agentId, data.agentId), eq(agentPermissions.toolName, data.toolName)))
|
|
92
|
+
.limit(1);
|
|
93
|
+
if (existing.length > 0) {
|
|
94
|
+
const perm = existing[0];
|
|
95
|
+
const targets = perm.targetsJson || [];
|
|
96
|
+
const newTargets = extractTargets(data.params);
|
|
97
|
+
const mergedTargets = mergeTargets(targets, newTargets);
|
|
98
|
+
await db
|
|
99
|
+
.update(agentPermissions)
|
|
100
|
+
.set({
|
|
101
|
+
callCount: (perm.callCount ?? 0) + 1,
|
|
102
|
+
errorCount: (perm.errorCount ?? 0) + (data.hasError ? 1 : 0),
|
|
103
|
+
lastSeen: now,
|
|
104
|
+
targetsJson: mergedTargets,
|
|
105
|
+
})
|
|
106
|
+
.where(eq(agentPermissions.id, perm.id));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const targets = extractTargets(data.params);
|
|
110
|
+
await db.insert(agentPermissions).values({
|
|
111
|
+
tenantId: data.tenantId,
|
|
112
|
+
agentId: data.agentId,
|
|
113
|
+
toolName: data.toolName,
|
|
114
|
+
category: data.category,
|
|
115
|
+
accessPattern: data.accessPattern,
|
|
116
|
+
targetsJson: targets,
|
|
117
|
+
callCount: 1,
|
|
118
|
+
errorCount: data.hasError ? 1 : 0,
|
|
119
|
+
firstSeen: now,
|
|
120
|
+
lastSeen: now,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Get recent observations, optionally filtered by agentId.
|
|
126
|
+
*/
|
|
127
|
+
async findRecent(opts = {}) {
|
|
128
|
+
const tenantId = opts.tenantId ?? DEFAULT_TENANT_ID;
|
|
129
|
+
const limit = opts.limit ?? 50;
|
|
130
|
+
const conditions = [eq(toolCallObservations.tenantId, tenantId)];
|
|
131
|
+
if (opts.agentId) {
|
|
132
|
+
conditions.push(eq(toolCallObservations.agentId, opts.agentId));
|
|
133
|
+
}
|
|
134
|
+
return db
|
|
135
|
+
.select()
|
|
136
|
+
.from(toolCallObservations)
|
|
137
|
+
.where(and(...conditions))
|
|
138
|
+
.orderBy(desc(toolCallObservations.timestamp))
|
|
139
|
+
.limit(limit);
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Get the aggregated permission profile for an agent.
|
|
143
|
+
*/
|
|
144
|
+
async getPermissions(agentId, tenantId = DEFAULT_TENANT_ID) {
|
|
145
|
+
return db
|
|
146
|
+
.select()
|
|
147
|
+
.from(agentPermissions)
|
|
148
|
+
.where(and(eq(agentPermissions.tenantId, tenantId), eq(agentPermissions.agentId, agentId)))
|
|
149
|
+
.orderBy(desc(agentPermissions.callCount));
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Get permissions for all agents (overview).
|
|
153
|
+
*/
|
|
154
|
+
async getAllPermissions(tenantId = DEFAULT_TENANT_ID) {
|
|
155
|
+
return db
|
|
156
|
+
.select()
|
|
157
|
+
.from(agentPermissions)
|
|
158
|
+
.where(eq(agentPermissions.tenantId, tenantId))
|
|
159
|
+
.orderBy(agentPermissions.agentId, desc(agentPermissions.callCount));
|
|
160
|
+
},
|
|
161
|
+
/**
|
|
162
|
+
* Find first-seen tool calls (anomalies) — permissions with callCount = 1.
|
|
163
|
+
*/
|
|
164
|
+
async findAnomalies(tenantId = DEFAULT_TENANT_ID, limit = 20) {
|
|
165
|
+
return db
|
|
166
|
+
.select()
|
|
167
|
+
.from(agentPermissions)
|
|
168
|
+
.where(and(eq(agentPermissions.tenantId, tenantId), eq(agentPermissions.callCount, 1)))
|
|
169
|
+
.orderBy(desc(agentPermissions.firstSeen))
|
|
170
|
+
.limit(limit);
|
|
171
|
+
},
|
|
172
|
+
/**
|
|
173
|
+
* Get observation count summary per agent.
|
|
174
|
+
*/
|
|
175
|
+
async summary(tenantId = DEFAULT_TENANT_ID) {
|
|
176
|
+
return db
|
|
177
|
+
.select({
|
|
178
|
+
agentId: toolCallObservations.agentId,
|
|
179
|
+
totalCalls: count(),
|
|
180
|
+
blockedCalls: sql `sum(case when ${toolCallObservations.blocked} = true then 1 else 0 end)`,
|
|
181
|
+
uniqueTools: sql `count(distinct ${toolCallObservations.toolName})`,
|
|
182
|
+
})
|
|
183
|
+
.from(toolCallObservations)
|
|
184
|
+
.where(eq(toolCallObservations.tenantId, tenantId))
|
|
185
|
+
.groupBy(toolCallObservations.agentId);
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
190
|
+
/** Extract likely target identifiers from tool call params. */
|
|
191
|
+
function extractTargets(params) {
|
|
192
|
+
if (!params)
|
|
193
|
+
return [];
|
|
194
|
+
const targets = [];
|
|
195
|
+
const targetKeys = ["repo", "repository", "channel", "to", "email", "path", "file", "url", "owner", "user", "org"];
|
|
196
|
+
for (const key of targetKeys) {
|
|
197
|
+
const val = params[key];
|
|
198
|
+
if (typeof val === "string" && val.length > 0 && val.length < 200) {
|
|
199
|
+
targets.push(val);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return targets;
|
|
203
|
+
}
|
|
204
|
+
/** Merge new targets into existing list, capped at 50 entries. */
|
|
205
|
+
function mergeTargets(existing, incoming) {
|
|
206
|
+
const set = new Set(existing);
|
|
207
|
+
for (const t of incoming) {
|
|
208
|
+
set.add(t);
|
|
209
|
+
}
|
|
210
|
+
const merged = [...set];
|
|
211
|
+
return merged.length > 50 ? merged.slice(-50) : merged;
|
|
212
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Database } from "../client.js";
|
|
2
|
+
export declare function policyQueries(db: Database): {
|
|
3
|
+
findAll(tenantId?: string): Promise<any>;
|
|
4
|
+
findById(id: string, tenantId?: string): Promise<any>;
|
|
5
|
+
create(data: {
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string | null;
|
|
8
|
+
scannerIds: string[];
|
|
9
|
+
action: string;
|
|
10
|
+
sensitivityThreshold?: number;
|
|
11
|
+
tenantId?: string;
|
|
12
|
+
}): Promise<unknown>;
|
|
13
|
+
update(id: string, data: Partial<{
|
|
14
|
+
name: string;
|
|
15
|
+
description: string | null;
|
|
16
|
+
scannerIds: string[];
|
|
17
|
+
action: string;
|
|
18
|
+
sensitivityThreshold: number;
|
|
19
|
+
isEnabled: boolean;
|
|
20
|
+
}>, tenantId?: string): Promise<unknown>;
|
|
21
|
+
delete(id: string, tenantId?: string): Promise<void>;
|
|
22
|
+
/** Get all enabled policies for detection flow */
|
|
23
|
+
getEnabled(tenantId?: string): Promise<any>;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=policies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../../src/queries/policies.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ;uBAEd,MAAM;iBAIX,MAAM,aAAY,MAAM;iBAKxB;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;eAQgB,MAAM,QAAQ,OAAO,CAAC;QACrC,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,oBAAoB,EAAE,MAAM,CAAC;QAC7B,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC,aAAY,MAAM;eAOH,MAAM,aAAY,MAAM;IAIzC,kDAAkD;0BACvB,MAAM;EAOpC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { eq, and } from "drizzle-orm";
|
|
2
|
+
import { policies } from "../schema/index.js";
|
|
3
|
+
import { insertReturning, updateReturning } from "../helpers.js";
|
|
4
|
+
import { DEFAULT_TENANT_ID } from "@opentrust/shared";
|
|
5
|
+
export function policyQueries(db) {
|
|
6
|
+
return {
|
|
7
|
+
async findAll(tenantId = DEFAULT_TENANT_ID) {
|
|
8
|
+
return db.select().from(policies).where(eq(policies.tenantId, tenantId)).orderBy(policies.createdAt);
|
|
9
|
+
},
|
|
10
|
+
async findById(id, tenantId = DEFAULT_TENANT_ID) {
|
|
11
|
+
const result = await db.select().from(policies).where(and(eq(policies.id, id), eq(policies.tenantId, tenantId))).limit(1);
|
|
12
|
+
return result[0] ?? null;
|
|
13
|
+
},
|
|
14
|
+
async create(data) {
|
|
15
|
+
return insertReturning(db, policies, {
|
|
16
|
+
...data,
|
|
17
|
+
sensitivityThreshold: data.sensitivityThreshold ?? 0.5,
|
|
18
|
+
tenantId: data.tenantId ?? DEFAULT_TENANT_ID,
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
async update(id, data, tenantId = DEFAULT_TENANT_ID) {
|
|
22
|
+
return updateReturning(db, policies, and(eq(policies.id, id), eq(policies.tenantId, tenantId)), {
|
|
23
|
+
...data,
|
|
24
|
+
updatedAt: new Date().toISOString(),
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
async delete(id, tenantId = DEFAULT_TENANT_ID) {
|
|
28
|
+
await db.delete(policies).where(and(eq(policies.id, id), eq(policies.tenantId, tenantId)));
|
|
29
|
+
},
|
|
30
|
+
/** Get all enabled policies for detection flow */
|
|
31
|
+
async getEnabled(tenantId = DEFAULT_TENANT_ID) {
|
|
32
|
+
return db
|
|
33
|
+
.select()
|
|
34
|
+
.from(policies)
|
|
35
|
+
.where(and(eq(policies.isEnabled, true), eq(policies.tenantId, tenantId)));
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Database } from "../client.js";
|
|
2
|
+
export declare function scannerQueries(db: Database): {
|
|
3
|
+
/** Get all scanners (defaults + overrides) */
|
|
4
|
+
getAll(tenantId?: string): Promise<any>;
|
|
5
|
+
/** Get all system default scanners */
|
|
6
|
+
getDefaults(tenantId?: string): Promise<any>;
|
|
7
|
+
/** Get enabled scanners for detection */
|
|
8
|
+
getEnabled(tenantId?: string): Promise<any>;
|
|
9
|
+
/** Upsert a scanner override */
|
|
10
|
+
upsert(data: {
|
|
11
|
+
scannerId: string;
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
isEnabled: boolean;
|
|
15
|
+
tenantId?: string;
|
|
16
|
+
}): Promise<unknown>;
|
|
17
|
+
/** Create a system default scanner */
|
|
18
|
+
createDefault(data: {
|
|
19
|
+
scannerId: string;
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
tenantId?: string;
|
|
23
|
+
}): Promise<unknown>;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=scanners.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanners.d.ts","sourceRoot":"","sources":["../../src/queries/scanners.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ;IAEvC,8CAA8C;sBACvB,MAAM;IAQ7B,sCAAsC;2BACV,MAAM;IAQlC,yCAAyC;0BACd,MAAM;IAQjC,gCAAgC;iBACb;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAsBD,sCAAsC;wBACZ;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;EAQxG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { eq, and } from "drizzle-orm";
|
|
2
|
+
import { scannerDefinitions } from "../schema/index.js";
|
|
3
|
+
import { insertReturning } from "../helpers.js";
|
|
4
|
+
import { DEFAULT_TENANT_ID } from "@opentrust/shared";
|
|
5
|
+
export function scannerQueries(db) {
|
|
6
|
+
return {
|
|
7
|
+
/** Get all scanners (defaults + overrides) */
|
|
8
|
+
async getAll(tenantId = DEFAULT_TENANT_ID) {
|
|
9
|
+
return db
|
|
10
|
+
.select()
|
|
11
|
+
.from(scannerDefinitions)
|
|
12
|
+
.where(eq(scannerDefinitions.tenantId, tenantId))
|
|
13
|
+
.orderBy(scannerDefinitions.scannerId);
|
|
14
|
+
},
|
|
15
|
+
/** Get all system default scanners */
|
|
16
|
+
async getDefaults(tenantId = DEFAULT_TENANT_ID) {
|
|
17
|
+
return db
|
|
18
|
+
.select()
|
|
19
|
+
.from(scannerDefinitions)
|
|
20
|
+
.where(and(eq(scannerDefinitions.isDefault, true), eq(scannerDefinitions.tenantId, tenantId)))
|
|
21
|
+
.orderBy(scannerDefinitions.scannerId);
|
|
22
|
+
},
|
|
23
|
+
/** Get enabled scanners for detection */
|
|
24
|
+
async getEnabled(tenantId = DEFAULT_TENANT_ID) {
|
|
25
|
+
return db
|
|
26
|
+
.select()
|
|
27
|
+
.from(scannerDefinitions)
|
|
28
|
+
.where(and(eq(scannerDefinitions.isEnabled, true), eq(scannerDefinitions.tenantId, tenantId)))
|
|
29
|
+
.orderBy(scannerDefinitions.scannerId);
|
|
30
|
+
},
|
|
31
|
+
/** Upsert a scanner override */
|
|
32
|
+
async upsert(data) {
|
|
33
|
+
const tid = data.tenantId ?? DEFAULT_TENANT_ID;
|
|
34
|
+
// Delete existing non-default with same scannerId for this tenant
|
|
35
|
+
await db
|
|
36
|
+
.delete(scannerDefinitions)
|
|
37
|
+
.where(and(eq(scannerDefinitions.scannerId, data.scannerId), eq(scannerDefinitions.isDefault, false), eq(scannerDefinitions.tenantId, tid)));
|
|
38
|
+
return insertReturning(db, scannerDefinitions, {
|
|
39
|
+
scannerId: data.scannerId,
|
|
40
|
+
name: data.name,
|
|
41
|
+
description: data.description,
|
|
42
|
+
isEnabled: data.isEnabled,
|
|
43
|
+
isDefault: false,
|
|
44
|
+
tenantId: tid,
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
/** Create a system default scanner */
|
|
48
|
+
async createDefault(data) {
|
|
49
|
+
return insertReturning(db, scannerDefinitions, {
|
|
50
|
+
...data,
|
|
51
|
+
isDefault: true,
|
|
52
|
+
tenantId: data.tenantId ?? DEFAULT_TENANT_ID,
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Database } from "../client.js";
|
|
2
|
+
export declare function settingsQueries(db: Database): {
|
|
3
|
+
get(key: string): Promise<string | null>;
|
|
4
|
+
set(key: string, value: string): Promise<void>;
|
|
5
|
+
getAll(): Promise<Record<string, string>>;
|
|
6
|
+
delete(key: string): Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/queries/settings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG7C,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ;aAEzB,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;aAK/B,MAAM,SAAS,MAAM;cASpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAS7B,MAAM;EAI3B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { settings } from "../schema/index.js";
|
|
3
|
+
export function settingsQueries(db) {
|
|
4
|
+
return {
|
|
5
|
+
async get(key) {
|
|
6
|
+
const result = await db.select().from(settings).where(eq(settings.key, key)).limit(1);
|
|
7
|
+
return result[0]?.value ?? null;
|
|
8
|
+
},
|
|
9
|
+
async set(key, value) {
|
|
10
|
+
const existing = await this.get(key);
|
|
11
|
+
if (existing !== null) {
|
|
12
|
+
await db.update(settings).set({ value, updatedAt: new Date().toISOString() }).where(eq(settings.key, key));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
await db.insert(settings).values({ key, value });
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
async getAll() {
|
|
19
|
+
const rows = await db.select().from(settings);
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const row of rows) {
|
|
22
|
+
result[row.key] = row.value;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
},
|
|
26
|
+
async delete(key) {
|
|
27
|
+
await db.delete(settings).where(eq(settings.key, key));
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Database } from "../client.js";
|
|
2
|
+
export declare function usageQueries(db: Database): {
|
|
3
|
+
log(data: {
|
|
4
|
+
agentId?: string | null;
|
|
5
|
+
endpoint: string;
|
|
6
|
+
statusCode: number;
|
|
7
|
+
responseSafe: boolean | null;
|
|
8
|
+
categories?: string[];
|
|
9
|
+
latencyMs: number;
|
|
10
|
+
requestId: string;
|
|
11
|
+
tenantId?: string;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
countInPeriod(start: Date | string, end: Date | string, tenantId?: string): Promise<any>;
|
|
14
|
+
summary(start: Date | string, end: Date | string, tenantId?: string): Promise<any>;
|
|
15
|
+
daily(start: Date | string, end: Date | string, tenantId?: string): Promise<any>;
|
|
16
|
+
countRecent(minutes?: number, tenantId?: string): Promise<any>;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/queries/usage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAI7C,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ;cAErB;QACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;yBAQ0B,IAAI,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,aAAY,MAAM;mBAQzD,IAAI,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,aAAY,MAAM;iBAYrD,IAAI,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,aAAY,MAAM;0BAe3C,MAAM,aAAgB,MAAM;EAS1D"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { eq, gte, lte, sql, count, and } from "drizzle-orm";
|
|
2
|
+
import { usageLogs } from "../schema/index.js";
|
|
3
|
+
import { DEFAULT_TENANT_ID } from "@opentrust/shared";
|
|
4
|
+
export function usageQueries(db) {
|
|
5
|
+
return {
|
|
6
|
+
async log(data) {
|
|
7
|
+
await db.insert(usageLogs).values({
|
|
8
|
+
...data,
|
|
9
|
+
categories: data.categories ?? [],
|
|
10
|
+
tenantId: data.tenantId ?? DEFAULT_TENANT_ID,
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
async countInPeriod(start, end, tenantId = DEFAULT_TENANT_ID) {
|
|
14
|
+
const result = await db
|
|
15
|
+
.select({ count: count() })
|
|
16
|
+
.from(usageLogs)
|
|
17
|
+
.where(and(gte(usageLogs.createdAt, start), lte(usageLogs.createdAt, end), eq(usageLogs.tenantId, tenantId)));
|
|
18
|
+
return result[0]?.count ?? 0;
|
|
19
|
+
},
|
|
20
|
+
async summary(start, end, tenantId = DEFAULT_TENANT_ID) {
|
|
21
|
+
const result = await db
|
|
22
|
+
.select({
|
|
23
|
+
totalCalls: count(),
|
|
24
|
+
safeCount: sql `sum(case when ${usageLogs.responseSafe} = true then 1 else 0 end)`,
|
|
25
|
+
unsafeCount: sql `sum(case when ${usageLogs.responseSafe} = false then 1 else 0 end)`,
|
|
26
|
+
})
|
|
27
|
+
.from(usageLogs)
|
|
28
|
+
.where(and(gte(usageLogs.createdAt, start), lte(usageLogs.createdAt, end), eq(usageLogs.tenantId, tenantId)));
|
|
29
|
+
return result[0] ?? { totalCalls: 0, safeCount: 0, unsafeCount: 0 };
|
|
30
|
+
},
|
|
31
|
+
async daily(start, end, tenantId = DEFAULT_TENANT_ID) {
|
|
32
|
+
const result = await db
|
|
33
|
+
.select({
|
|
34
|
+
date: sql `date(${usageLogs.createdAt})`,
|
|
35
|
+
count: count(),
|
|
36
|
+
safeCount: sql `sum(case when ${usageLogs.responseSafe} = true then 1 else 0 end)`,
|
|
37
|
+
unsafeCount: sql `sum(case when ${usageLogs.responseSafe} = false then 1 else 0 end)`,
|
|
38
|
+
})
|
|
39
|
+
.from(usageLogs)
|
|
40
|
+
.where(and(gte(usageLogs.createdAt, start), lte(usageLogs.createdAt, end), eq(usageLogs.tenantId, tenantId)))
|
|
41
|
+
.groupBy(sql `date(${usageLogs.createdAt})`)
|
|
42
|
+
.orderBy(sql `date(${usageLogs.createdAt})`);
|
|
43
|
+
return result;
|
|
44
|
+
},
|
|
45
|
+
async countRecent(minutes = 1, tenantId = DEFAULT_TENANT_ID) {
|
|
46
|
+
const since = new Date(Date.now() - minutes * 60_000).toISOString();
|
|
47
|
+
const result = await db
|
|
48
|
+
.select({ count: count() })
|
|
49
|
+
.from(usageLogs)
|
|
50
|
+
.where(and(gte(usageLogs.createdAt, since), eq(usageLogs.tenantId, tenantId)));
|
|
51
|
+
return result[0]?.count ?? 0;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|