@wizeworks/lens-cloud 0.0.0-stage → 0.3.0-alpha.1
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/LICENSE +21 -0
- package/README.md +63 -2
- package/dist/alerts/evaluate.d.ts +10 -0
- package/dist/alerts/evaluate.d.ts.map +1 -0
- package/dist/alerts/evaluate.js +66 -0
- package/dist/alerts/evaluate.js.map +1 -0
- package/dist/alerts/notify.d.ts +17 -0
- package/dist/alerts/notify.d.ts.map +1 -0
- package/dist/alerts/notify.js +46 -0
- package/dist/alerts/notify.js.map +1 -0
- package/dist/app.d.ts +18 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +2 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/crypto.d.ts +19 -0
- package/dist/auth/crypto.d.ts.map +1 -0
- package/dist/auth/crypto.js +33 -0
- package/dist/auth/crypto.js.map +1 -0
- package/dist/auth/principal.d.ts +33 -0
- package/dist/auth/principal.d.ts.map +1 -0
- package/dist/auth/principal.js +64 -0
- package/dist/auth/principal.js.map +1 -0
- package/dist/auth/roles.d.ts +10 -0
- package/dist/auth/roles.d.ts.map +1 -0
- package/dist/auth/roles.js +9 -0
- package/dist/auth/roles.js.map +1 -0
- package/dist/cli-args.d.ts +5 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +45 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +28 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +20 -0
- package/dist/config.js.map +1 -0
- package/dist/db/db.d.ts +19 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +57 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/migrate.d.ts +6 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +25 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema.d.ts +3 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +200 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/http/http.d.ts +17 -0
- package/dist/http/http.d.ts.map +1 -0
- package/dist/http/http.js +62 -0
- package/dist/http/http.js.map +1 -0
- package/dist/http/limiter.d.ts +11 -0
- package/dist/http/limiter.d.ts.map +1 -0
- package/dist/http/limiter.js +31 -0
- package/dist/http/limiter.js.map +1 -0
- package/dist/http/router.d.ts +21 -0
- package/dist/http/router.d.ts.map +1 -0
- package/dist/http/router.js +28 -0
- package/dist/http/router.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs.d.ts +4 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +42 -0
- package/dist/jobs.js.map +1 -0
- package/dist/repo/alerts.d.ts +48 -0
- package/dist/repo/alerts.d.ts.map +1 -0
- package/dist/repo/alerts.js +38 -0
- package/dist/repo/alerts.js.map +1 -0
- package/dist/repo/audit.d.ts +11 -0
- package/dist/repo/audit.d.ts.map +1 -0
- package/dist/repo/audit.js +17 -0
- package/dist/repo/audit.js.map +1 -0
- package/dist/repo/metric-read.d.ts +12 -0
- package/dist/repo/metric-read.d.ts.map +1 -0
- package/dist/repo/metric-read.js +11 -0
- package/dist/repo/metric-read.js.map +1 -0
- package/dist/repo/metric-write.d.ts +9 -0
- package/dist/repo/metric-write.d.ts.map +1 -0
- package/dist/repo/metric-write.js +53 -0
- package/dist/repo/metric-write.js.map +1 -0
- package/dist/repo/orgs.d.ts +47 -0
- package/dist/repo/orgs.d.ts.map +1 -0
- package/dist/repo/orgs.js +75 -0
- package/dist/repo/orgs.js.map +1 -0
- package/dist/repo/projects.d.ts +37 -0
- package/dist/repo/projects.d.ts.map +1 -0
- package/dist/repo/projects.js +57 -0
- package/dist/repo/projects.js.map +1 -0
- package/dist/repo/trace-read.d.ts +28 -0
- package/dist/repo/trace-read.d.ts.map +1 -0
- package/dist/repo/trace-read.js +76 -0
- package/dist/repo/trace-read.js.map +1 -0
- package/dist/repo/trace-write.d.ts +9 -0
- package/dist/repo/trace-write.d.ts.map +1 -0
- package/dist/repo/trace-write.js +82 -0
- package/dist/repo/trace-write.js.map +1 -0
- package/dist/repo/users.d.ts +24 -0
- package/dist/repo/users.d.ts.map +1 -0
- package/dist/repo/users.js +65 -0
- package/dist/repo/users.js.map +1 -0
- package/dist/routes/alerts.d.ts +4 -0
- package/dist/routes/alerts.d.ts.map +1 -0
- package/dist/routes/alerts.js +99 -0
- package/dist/routes/alerts.js.map +1 -0
- package/dist/routes/auth.d.ts +4 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +106 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/data.d.ts +7 -0
- package/dist/routes/data.d.ts.map +1 -0
- package/dist/routes/data.js +74 -0
- package/dist/routes/data.js.map +1 -0
- package/dist/routes/index.d.ts +5 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +23 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/ingest.d.ts +8 -0
- package/dist/routes/ingest.d.ts.map +1 -0
- package/dist/routes/ingest.js +38 -0
- package/dist/routes/ingest.js.map +1 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +52 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/metrics.d.ts +13 -0
- package/dist/routes/metrics.d.ts.map +1 -0
- package/dist/routes/metrics.js +29 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/models.d.ts +4 -0
- package/dist/routes/models.d.ts.map +1 -0
- package/dist/routes/models.js +71 -0
- package/dist/routes/models.js.map +1 -0
- package/dist/routes/orgs.d.ts +4 -0
- package/dist/routes/orgs.d.ts.map +1 -0
- package/dist/routes/orgs.js +96 -0
- package/dist/routes/orgs.js.map +1 -0
- package/dist/routes/projects.d.ts +4 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +54 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/shares.d.ts +11 -0
- package/dist/routes/shares.d.ts.map +1 -0
- package/dist/routes/shares.js +61 -0
- package/dist/routes/shares.js.map +1 -0
- package/dist/scale-exec.d.ts +11 -0
- package/dist/scale-exec.d.ts.map +1 -0
- package/dist/scale-exec.js +26 -0
- package/dist/scale-exec.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +95 -0
- package/dist/server.js.map +1 -0
- package/dist/settle.d.ts +26 -0
- package/dist/settle.d.ts.map +1 -0
- package/dist/settle.js +85 -0
- package/dist/settle.js.map +1 -0
- package/package.json +63 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/jobs.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CloudApp } from "./app.js";
|
|
2
|
+
/** Background work: retention, settling stats, rollup aging, expired sessions, and alert rules. Returns a function that stops it. */
|
|
3
|
+
export declare function startJobs(app: CloudApp): () => void;
|
|
4
|
+
//# sourceMappingURL=jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AA0BzC,qIAAqI;AACrI,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,IAAI,CAcnD"}
|
package/dist/jobs.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { evaluateAll } from "./alerts/evaluate.js";
|
|
2
|
+
import { pruneByRetention, pruneMetricsByRetention } from "./repo/trace-write.js";
|
|
3
|
+
import { pruneSessions } from "./repo/users.js";
|
|
4
|
+
import { pruneRollups, settleCloud } from "./settle.js";
|
|
5
|
+
const HOUSEKEEPING_MS = 10 * 60_000;
|
|
6
|
+
const SETTLE_MS = 3_000;
|
|
7
|
+
const ROLLUP_PRUNE_MS = 60_000;
|
|
8
|
+
/** Run `job` now and then every `everyMs`. A failed run logs and waits for the next one. */
|
|
9
|
+
function every(name, everyMs, job) {
|
|
10
|
+
let running = false;
|
|
11
|
+
const run = () => {
|
|
12
|
+
if (running)
|
|
13
|
+
return;
|
|
14
|
+
running = true;
|
|
15
|
+
job()
|
|
16
|
+
.catch((err) => console.error(`[wizelens-cloud] ${name} failed:`, err))
|
|
17
|
+
.finally(() => (running = false));
|
|
18
|
+
};
|
|
19
|
+
const timer = setInterval(run, everyMs);
|
|
20
|
+
const first = setTimeout(run, 1000);
|
|
21
|
+
timer.unref();
|
|
22
|
+
first.unref();
|
|
23
|
+
return () => (clearInterval(timer), clearTimeout(first));
|
|
24
|
+
}
|
|
25
|
+
/** Background work: retention, settling stats, rollup aging, expired sessions, and alert rules. Returns a function that stops it. */
|
|
26
|
+
export function startJobs(app) {
|
|
27
|
+
const now = app.config.now;
|
|
28
|
+
const stops = [
|
|
29
|
+
every("retention", HOUSEKEEPING_MS, async () => {
|
|
30
|
+
while ((await pruneByRetention(app.db, now())) > 0)
|
|
31
|
+
;
|
|
32
|
+
await pruneMetricsByRetention(app.db, now());
|
|
33
|
+
await pruneRollups(app.db, now(), true);
|
|
34
|
+
}),
|
|
35
|
+
every("settle", SETTLE_MS, () => settleCloud(app.db, now(), { defaultRetentionDays: app.config.retentionDays })),
|
|
36
|
+
every("rollups", ROLLUP_PRUNE_MS, () => pruneRollups(app.db, now())),
|
|
37
|
+
every("sessions", HOUSEKEEPING_MS, () => pruneSessions(app.db, now())),
|
|
38
|
+
every("alerts", app.config.alertIntervalMs, () => evaluateAll(app)),
|
|
39
|
+
];
|
|
40
|
+
return () => stops.forEach((stop) => stop());
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=jobs.js.map
|
package/dist/jobs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,eAAe,GAAG,EAAE,GAAG,MAAM,CAAC;AACpC,MAAM,SAAS,GAAG,KAAK,CAAC;AACxB,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B,4FAA4F;AAC5F,SAAS,KAAK,CAAC,IAAY,EAAE,OAAe,EAAE,GAA2B;IACvE,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,GAAG,GAAG,GAAG,EAAE;QACf,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,GAAG,EAAE;aACF,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,IAAI,UAAU,EAAE,GAAG,CAAC,CAAC;aAC/E,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACpC,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,OAAO,GAAG,EAAE,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,qIAAqI;AACrI,MAAM,UAAU,SAAS,CAAC,GAAa;IACrC,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;IAC3B,MAAM,KAAK,GAAG;QACZ,KAAK,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,IAAI,EAAE;YAC7C,OAAO,CAAC,MAAM,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;gBAAC,CAAC;YACpD,MAAM,uBAAuB,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAC7C,MAAM,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC,CAAC;QACF,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;QAChH,KAAK,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACpE,KAAK,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACtE,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;KACpE,CAAC;IACF,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { AlertMetric, AlertOp, MetricAlertStat } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Queryable } from "../db/db.js";
|
|
3
|
+
export interface Channel {
|
|
4
|
+
kind: "webhook" | "slack";
|
|
5
|
+
url: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AlertRule {
|
|
8
|
+
id: string;
|
|
9
|
+
projectId: string;
|
|
10
|
+
name: string;
|
|
11
|
+
/** A trace metric, or "metric" for one OpenTelemetry metric named by `metricName`. */
|
|
12
|
+
metric: AlertMetric | "metric";
|
|
13
|
+
/** For "metric" rules: the metric, what to read from it, and optionally one service. Empty otherwise. */
|
|
14
|
+
metricName: string;
|
|
15
|
+
metricStat: MetricAlertStat | "";
|
|
16
|
+
metricService: string;
|
|
17
|
+
op: AlertOp;
|
|
18
|
+
threshold: number;
|
|
19
|
+
windowMs: number;
|
|
20
|
+
env: string;
|
|
21
|
+
channels: Channel[];
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
state: "ok" | "firing";
|
|
24
|
+
lastValue: number | null;
|
|
25
|
+
lastEvalMs: number | null;
|
|
26
|
+
}
|
|
27
|
+
export interface AlertEvent {
|
|
28
|
+
id: string;
|
|
29
|
+
ruleId: string;
|
|
30
|
+
ruleName: string;
|
|
31
|
+
kind: "fired" | "resolved" | "test";
|
|
32
|
+
value: number | null;
|
|
33
|
+
threshold: number;
|
|
34
|
+
message: string;
|
|
35
|
+
timeMs: number;
|
|
36
|
+
delivery: string;
|
|
37
|
+
}
|
|
38
|
+
export type RuleInput = Omit<AlertRule, "id" | "projectId" | "state" | "lastValue" | "lastEvalMs">;
|
|
39
|
+
export declare function listRules(q: Queryable, projectId: string): Promise<AlertRule[]>;
|
|
40
|
+
export declare function enabledRules(q: Queryable): Promise<AlertRule[]>;
|
|
41
|
+
export declare function ruleById(q: Queryable, projectId: string, id: string): Promise<AlertRule | undefined>;
|
|
42
|
+
export declare function createRule(q: Queryable, projectId: string, r: RuleInput, by: string, now: number): Promise<string>;
|
|
43
|
+
export declare function updateRule(q: Queryable, projectId: string, id: string, r: RuleInput): Promise<void>;
|
|
44
|
+
export declare function deleteRule(q: Queryable, projectId: string, id: string): Promise<boolean>;
|
|
45
|
+
export declare function saveEvaluation(q: Queryable, id: string, state: AlertRule["state"], value: number | null, now: number): Promise<void>;
|
|
46
|
+
export declare function addEvent(q: Queryable, rule: AlertRule, e: Omit<AlertEvent, "id" | "ruleId" | "ruleName" | "threshold">): Promise<void>;
|
|
47
|
+
export declare const listEvents: (q: Queryable, projectId: string, limit: number) => Promise<AlertEvent[]>;
|
|
48
|
+
//# sourceMappingURL=alerts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/repo/alerts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { newId } from "../auth/crypto.js";
|
|
2
|
+
const RULE_COLUMNS = `id, project_id AS "projectId", name, metric, metric_name AS "metricName", metric_stat AS "metricStat",
|
|
3
|
+
metric_service AS "metricService", op, threshold, window_ms AS "windowMs", env, channels, enabled, state,
|
|
4
|
+
last_value AS "lastValue", last_eval_ms AS "lastEvalMs"`;
|
|
5
|
+
const toRule = (r) => ({ ...r, channels: JSON.parse(r.channels) });
|
|
6
|
+
export async function listRules(q, projectId) {
|
|
7
|
+
return (await q.query(`SELECT ${RULE_COLUMNS} FROM alert_rules WHERE project_id = $1 ORDER BY name`, [projectId])).map(toRule);
|
|
8
|
+
}
|
|
9
|
+
export async function enabledRules(q) {
|
|
10
|
+
return (await q.query(`SELECT ${RULE_COLUMNS} FROM alert_rules WHERE enabled`)).map(toRule);
|
|
11
|
+
}
|
|
12
|
+
export async function ruleById(q, projectId, id) {
|
|
13
|
+
const [row] = await q.query(`SELECT ${RULE_COLUMNS} FROM alert_rules WHERE project_id = $1 AND id = $2`, [projectId, id]);
|
|
14
|
+
return row && toRule(row);
|
|
15
|
+
}
|
|
16
|
+
export async function createRule(q, projectId, r, by, now) {
|
|
17
|
+
const id = newId();
|
|
18
|
+
await q.query(`INSERT INTO alert_rules (id, project_id, name, metric, op, threshold, window_ms, env, channels, enabled, created_by, created_ms,
|
|
19
|
+
metric_name, metric_stat, metric_service)
|
|
20
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)`, [id, projectId, r.name, r.metric, r.op, r.threshold, r.windowMs, r.env, JSON.stringify(r.channels), r.enabled, by, now, r.metricName, r.metricStat, r.metricService]);
|
|
21
|
+
return id;
|
|
22
|
+
}
|
|
23
|
+
export async function updateRule(q, projectId, id, r) {
|
|
24
|
+
await q.query(`UPDATE alert_rules SET name = $3, metric = $4, op = $5, threshold = $6, window_ms = $7, env = $8, channels = $9, enabled = $10,
|
|
25
|
+
metric_name = $11, metric_stat = $12, metric_service = $13, state = CASE WHEN $10 THEN state ELSE 'ok' END WHERE project_id = $1 AND id = $2`, [projectId, id, r.name, r.metric, r.op, r.threshold, r.windowMs, r.env, JSON.stringify(r.channels), r.enabled, r.metricName, r.metricStat, r.metricService]);
|
|
26
|
+
}
|
|
27
|
+
export async function deleteRule(q, projectId, id) {
|
|
28
|
+
return (await q.query("DELETE FROM alert_rules WHERE project_id = $1 AND id = $2 RETURNING id", [projectId, id])).length > 0;
|
|
29
|
+
}
|
|
30
|
+
export async function saveEvaluation(q, id, state, value, now) {
|
|
31
|
+
await q.query("UPDATE alert_rules SET state = $2, last_value = $3, last_eval_ms = $4 WHERE id = $1", [id, state, value, now]);
|
|
32
|
+
}
|
|
33
|
+
export async function addEvent(q, rule, e) {
|
|
34
|
+
await q.query(`INSERT INTO alert_events (id, rule_id, project_id, kind, value, threshold, message, time_ms, delivery) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [newId(), rule.id, rule.projectId, e.kind, e.value, rule.threshold, e.message, e.timeMs, e.delivery]);
|
|
35
|
+
}
|
|
36
|
+
export const listEvents = (q, projectId, limit) => q.query(`SELECT e.id, e.rule_id AS "ruleId", r.name AS "ruleName", e.kind, e.value, e.threshold, e.message, e.time_ms AS "timeMs", e.delivery
|
|
37
|
+
FROM alert_events e JOIN alert_rules r ON r.id = e.rule_id WHERE e.project_id = $1 ORDER BY e.time_ms DESC LIMIT $2`, [projectId, limit]);
|
|
38
|
+
//# sourceMappingURL=alerts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/repo/alerts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Queryable } from "../db/db.js";
|
|
2
|
+
export interface AuditEntry {
|
|
3
|
+
actor: string;
|
|
4
|
+
action: string;
|
|
5
|
+
detail: Record<string, unknown>;
|
|
6
|
+
timeMs: number;
|
|
7
|
+
}
|
|
8
|
+
/** Keep one line of who did what. Every MCP call and every change to members, keys, and projects writes one. */
|
|
9
|
+
export declare function audit(q: Queryable, orgId: string, actor: string, action: string, detail: Record<string, unknown>, now: number): Promise<void>;
|
|
10
|
+
export declare function listAudit(q: Queryable, orgId: string, limit: number, before?: number): Promise<AuditEntry[]>;
|
|
11
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/repo/audit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gHAAgH;AAChH,wBAAsB,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASnJ;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAMlH"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { newId } from "../auth/crypto.js";
|
|
2
|
+
/** Keep one line of who did what. Every MCP call and every change to members, keys, and projects writes one. */
|
|
3
|
+
export async function audit(q, orgId, actor, action, detail, now) {
|
|
4
|
+
await q.query("INSERT INTO audit (id, org_id, actor, action, detail, time_ms) VALUES ($1, $2, $3, $4, $5, $6)", [
|
|
5
|
+
newId(),
|
|
6
|
+
orgId,
|
|
7
|
+
actor,
|
|
8
|
+
action,
|
|
9
|
+
JSON.stringify(detail),
|
|
10
|
+
now,
|
|
11
|
+
]);
|
|
12
|
+
}
|
|
13
|
+
export async function listAudit(q, orgId, limit, before) {
|
|
14
|
+
const rows = await q.query(`SELECT actor, action, detail, time_ms FROM audit WHERE org_id = $1 AND time_ms < $2 ORDER BY time_ms DESC LIMIT $3`, [orgId, before ?? Number.MAX_SAFE_INTEGER, limit]);
|
|
15
|
+
return rows.map((r) => ({ actor: r.actor, action: r.action, detail: JSON.parse(r.detail), timeMs: r.time_ms }));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/repo/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAU1C,gHAAgH;AAChH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,CAAY,EAAE,KAAa,EAAE,KAAa,EAAE,MAAc,EAAE,MAA+B,EAAE,GAAW;IAClI,MAAM,CAAC,CAAC,KAAK,CAAC,gGAAgG,EAAE;QAC9G,KAAK,EAAE;QACP,KAAK;QACL,KAAK;QACL,MAAM;QACN,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;QACtB,GAAG;KACJ,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,CAAY,EAAE,KAAa,EAAE,KAAa,EAAE,MAAe;IACzF,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,KAAK,CACxB,oHAAoH,EACpH,CAAC,KAAK,EAAE,MAAM,IAAI,MAAM,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAClD,CAAC;IACF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAA4B,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC7I,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type MetricAlertData, type MetricData, type TimeRange, type TimeWindow } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Queryable } from "../db/db.js";
|
|
3
|
+
export interface MetricQuery {
|
|
4
|
+
projectId: string;
|
|
5
|
+
env?: string;
|
|
6
|
+
service?: string;
|
|
7
|
+
}
|
|
8
|
+
/** The metrics page data for one project, from the metric rollup tiers. `now` is rounded down to 10 s. */
|
|
9
|
+
export declare function loadMetricData(q: Queryable, m: MetricQuery, range: TimeRange, params: Readonly<Record<string, string>>, now: number): Promise<MetricData>;
|
|
10
|
+
/** One metric over an alert rule's window, from the metric rollup tiers. */
|
|
11
|
+
export declare function loadMetricAlertData(q: Queryable, m: MetricQuery, name: string, w: TimeWindow, now: number): Promise<MetricAlertData>;
|
|
12
|
+
//# sourceMappingURL=metric-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric-read.d.ts","sourceRoot":"","sources":["../../src/repo/metric-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EAEpB,KAAK,UAAU,EAKf,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,0GAA0G;AAC1G,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEzJ;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAEpI"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { alignNow, metricAlertSteps, metricPageSteps, runMetricAsync, } from "@wizeworks/lens-core";
|
|
2
|
+
const run = (q) => (s) => q.query(s.sql, s.args);
|
|
3
|
+
/** The metrics page data for one project, from the metric rollup tiers. `now` is rounded down to 10 s. */
|
|
4
|
+
export function loadMetricData(q, m, range, params, now) {
|
|
5
|
+
return runMetricAsync(metricPageSteps("pg", { project: m.projectId, env: m.env }, range, alignNow(now), params), run(q));
|
|
6
|
+
}
|
|
7
|
+
/** One metric over an alert rule's window, from the metric rollup tiers. */
|
|
8
|
+
export function loadMetricAlertData(q, m, name, w, now) {
|
|
9
|
+
return runMetricAsync(metricAlertSteps("pg", { project: m.projectId, env: m.env, service: m.service }, name, w, now), run(q));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=metric-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric-read.js","sourceRoot":"","sources":["../../src/repo/metric-read.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAER,gBAAgB,EAEhB,eAAe,EAGf,cAAc,GAGf,MAAM,sBAAsB,CAAC;AAS9B,MAAM,GAAG,GAAG,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAA0B,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAwB,CAAC;AAEvH,0GAA0G;AAC1G,MAAM,UAAU,cAAc,CAAC,CAAY,EAAE,CAAc,EAAE,KAAgB,EAAE,MAAwC,EAAE,GAAW;IAClI,OAAO,cAAc,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3H,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,mBAAmB,CAAC,CAAY,EAAE,CAAc,EAAE,IAAY,EAAE,CAAa,EAAE,GAAW;IACxG,OAAO,cAAc,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAChI,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type MetricPoint } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Queryable } from "../db/db.js";
|
|
3
|
+
/** The metric rollup tables, for clearing a project. */
|
|
4
|
+
export declare const METRIC_TABLES: string[];
|
|
5
|
+
/** Add one batch's points to the metric tiers and series state. Series rows are locked first, in key order. */
|
|
6
|
+
export declare function writeMetricRollups(q: Queryable, projectId: string, env: string, points: readonly MetricPoint[], now: number): Promise<void>;
|
|
7
|
+
/** Drop tier buckets past their keep (10 s and 5 min tiers) or the organization's retention (1 h tier and series state). */
|
|
8
|
+
export declare function pruneMetricRollups(q: Queryable, now: number): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=metric-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric-write.d.ts","sourceRoot":"","sources":["../../src/repo/metric-write.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,WAAW,EAYjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C,wDAAwD;AACxD,eAAO,MAAM,aAAa,UAA6D,CAAC;AAsBxF,+GAA+G;AAC/G,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjJ;AAED,4HAA4H;AAC5H,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { groupMetricSeries, mergeStoredHists, metricBatchWrites, metricHistOfColumns, metricHistStateSql, metricPlaceholderValues, metricSeriesInsertSql, metricSeriesUpsertSql, metricSeriesValues, metricStateOfColumns, tierCutoff, metricStateSql, metricTierUpsertSql, metricTierValues, seriesKeyOf, TIERS, } from "@wizeworks/lens-core";
|
|
3
|
+
// Metric rollups at ingest (docs/CONTRACT.md, "Cloud storage"): written with the raw points, in the same transaction.
|
|
4
|
+
const CHUNK = 200;
|
|
5
|
+
const sha1 = (text) => createHash("sha1").update(text).digest("hex");
|
|
6
|
+
/** The metric rollup tables, for clearing a project. */
|
|
7
|
+
export const METRIC_TABLES = ["metric_series", ...TIERS.map((t) => `metric_${t.name}`)];
|
|
8
|
+
/** Multi-row statements, `CHUNK` rows each; `sqlOf(n)` gives the text for n rows. */
|
|
9
|
+
async function insertRows(q, sqlOf, rows) {
|
|
10
|
+
for (let i = 0; i < rows.length; i += CHUNK) {
|
|
11
|
+
const part = rows.slice(i, i + CHUNK);
|
|
12
|
+
await q.query(sqlOf(part.length), part.flat());
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** The stored histograms of the tier buckets this batch adds counts to, locked until the transaction ends. */
|
|
16
|
+
async function storedHists(q, projectId, env, tiers) {
|
|
17
|
+
const stored = new Map();
|
|
18
|
+
for (const tier of TIERS) {
|
|
19
|
+
const mine = tiers.filter((t) => t.tier === tier && t.row.hist);
|
|
20
|
+
if (mine.length === 0)
|
|
21
|
+
continue;
|
|
22
|
+
const s = metricHistStateSql("pg", tier, projectId, env, [...new Set(mine.map((t) => t.key))], [...new Set(mine.map((t) => t.bucket))]);
|
|
23
|
+
for (const r of await q.query(s.sql, s.args))
|
|
24
|
+
stored.set(`${tier.name} ${r.series_key} ${Number(r.bucket_ms)}`, metricHistOfColumns(r));
|
|
25
|
+
}
|
|
26
|
+
return (t) => stored.get(`${t.tier.name} ${t.key} ${t.bucket}`) ?? null;
|
|
27
|
+
}
|
|
28
|
+
/** Add one batch's points to the metric tiers and series state. Series rows are locked first, in key order. */
|
|
29
|
+
export async function writeMetricRollups(q, projectId, env, points, now) {
|
|
30
|
+
if (points.length === 0)
|
|
31
|
+
return;
|
|
32
|
+
const groups = groupMetricSeries(points, (p) => sha1(seriesKeyOf(p)));
|
|
33
|
+
const keys = [...groups.keys()];
|
|
34
|
+
await insertRows(q, (n) => metricSeriesInsertSql("pg", n), keys.map((k) => metricPlaceholderValues(projectId, env, k, groups.get(k).at(-1))));
|
|
35
|
+
const st = metricStateSql("pg", projectId, env, keys);
|
|
36
|
+
const states = new Map((await q.query(st.sql, st.args)).map((r) => [String(r.series_key), metricStateOfColumns(r)]));
|
|
37
|
+
const { series, tiers } = metricBatchWrites(groups, states, now);
|
|
38
|
+
mergeStoredHists(tiers, await storedHists(q, projectId, env, tiers));
|
|
39
|
+
for (const tier of TIERS) {
|
|
40
|
+
const rows = tiers.filter((t) => t.tier === tier).map((t) => metricTierValues("pg", projectId, env, t));
|
|
41
|
+
await insertRows(q, (n) => metricTierUpsertSql("pg", tier, n), rows);
|
|
42
|
+
}
|
|
43
|
+
await insertRows(q, (n) => metricSeriesUpsertSql("pg", n), series.map((s) => metricSeriesValues("pg", projectId, env, s)));
|
|
44
|
+
}
|
|
45
|
+
/** Drop tier buckets past their keep (10 s and 5 min tiers) or the organization's retention (1 h tier and series state). */
|
|
46
|
+
export async function pruneMetricRollups(q, now) {
|
|
47
|
+
for (const t of TIERS.filter((x) => x.keepMs !== null))
|
|
48
|
+
await q.query(`DELETE FROM metric_${t.name} WHERE bucket_ms <= $1`, [tierCutoff(t, now, t.keepMs)]);
|
|
49
|
+
const scope = "USING projects p, orgs o WHERE p.id = m.project_id AND o.id = p.org_id";
|
|
50
|
+
await q.query(`DELETE FROM metric_1h m ${scope} AND m.bucket_ms <= $1 - o.retention_days * 86400000.0`, [now - 3_600_000]);
|
|
51
|
+
await q.query(`DELETE FROM metric_series m ${scope} AND m.last_ms < $1 - o.retention_days * 86400000.0`, [now]);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=metric-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric-write.js","sourceRoot":"","sources":["../../src/repo/metric-write.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Role } from "../auth/roles.js";
|
|
2
|
+
import type { Queryable } from "../db/db.js";
|
|
3
|
+
export interface Org {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
slug: string;
|
|
7
|
+
retentionDays: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Membership extends Org {
|
|
10
|
+
role: Role;
|
|
11
|
+
}
|
|
12
|
+
export interface Member {
|
|
13
|
+
userId: string;
|
|
14
|
+
email: string;
|
|
15
|
+
name: string;
|
|
16
|
+
role: Role;
|
|
17
|
+
sinceMs: number;
|
|
18
|
+
}
|
|
19
|
+
/** A new organization with `ownerId` as its owner. */
|
|
20
|
+
export declare function createOrg(q: Queryable, name: string, ownerId: string, now: number, retentionDays?: number): Promise<Org>;
|
|
21
|
+
export declare const orgsOfUser: (q: Queryable, userId: string) => Promise<Membership[]>;
|
|
22
|
+
export declare function membership(q: Queryable, userId: string, orgId: string): Promise<Membership | undefined>;
|
|
23
|
+
export declare function updateOrg(q: Queryable, orgId: string, change: {
|
|
24
|
+
name?: string;
|
|
25
|
+
retentionDays?: number;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
export declare const listMembers: (q: Queryable, orgId: string) => Promise<Member[]>;
|
|
28
|
+
export declare function setRole(q: Queryable, orgId: string, userId: string, role: Role): Promise<void>;
|
|
29
|
+
export declare function removeMember(q: Queryable, orgId: string, userId: string): Promise<void>;
|
|
30
|
+
export declare function ownerCount(q: Queryable, orgId: string): Promise<number>;
|
|
31
|
+
/** A new invite link token for one email address. Valid for 7 days. */
|
|
32
|
+
export declare function createInvite(q: Queryable, orgId: string, email: string, role: Role, by: string, now: number): Promise<string>;
|
|
33
|
+
export interface Invite {
|
|
34
|
+
orgId: string;
|
|
35
|
+
orgName: string;
|
|
36
|
+
email: string;
|
|
37
|
+
role: Role;
|
|
38
|
+
}
|
|
39
|
+
export declare function openInvite(q: Queryable, token: string, now: number): Promise<Invite | undefined>;
|
|
40
|
+
/** Join the organization of an open invite. The invite then cannot be used again. */
|
|
41
|
+
export declare function acceptInvite(q: Queryable, token: string, userId: string, now: number): Promise<Invite | undefined>;
|
|
42
|
+
export declare const pendingInvites: (q: Queryable, orgId: string, now: number) => Promise<{
|
|
43
|
+
email: string;
|
|
44
|
+
role: Role;
|
|
45
|
+
expiresMs: number;
|
|
46
|
+
}[]>;
|
|
47
|
+
//# sourceMappingURL=orgs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../src/repo/orgs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { hashToken, newId, newToken, slugOf } from "../auth/crypto.js";
|
|
2
|
+
import { normalizeEmail } from "./users.js";
|
|
3
|
+
const INVITE_MS = 7 * 24 * 60 * 60_000;
|
|
4
|
+
const ORG_COLUMNS = "o.id, o.name, o.slug, o.retention_days AS \"retentionDays\"";
|
|
5
|
+
async function freeSlug(q, name) {
|
|
6
|
+
const base = slugOf(name);
|
|
7
|
+
const taken = new Set((await q.query("SELECT slug FROM orgs WHERE slug LIKE $1", [`${base}%`])).map((r) => r.slug));
|
|
8
|
+
let slug = base;
|
|
9
|
+
for (let i = 2; taken.has(slug); i++)
|
|
10
|
+
slug = `${base}-${i}`;
|
|
11
|
+
return slug;
|
|
12
|
+
}
|
|
13
|
+
/** A new organization with `ownerId` as its owner. */
|
|
14
|
+
export async function createOrg(q, name, ownerId, now, retentionDays = 30) {
|
|
15
|
+
const org = { id: newId(), name: name.trim().slice(0, 100) || "My organization", slug: await freeSlug(q, name), retentionDays };
|
|
16
|
+
await q.query("INSERT INTO orgs (id, name, slug, retention_days, created_ms) VALUES ($1, $2, $3, $4, $5)", [org.id, org.name, org.slug, retentionDays, now]);
|
|
17
|
+
await q.query("INSERT INTO members (org_id, user_id, role, created_ms) VALUES ($1, $2, 'owner', $3)", [org.id, ownerId, now]);
|
|
18
|
+
return org;
|
|
19
|
+
}
|
|
20
|
+
export const orgsOfUser = (q, userId) => q.query(`SELECT ${ORG_COLUMNS}, m.role FROM members m JOIN orgs o ON o.id = m.org_id WHERE m.user_id = $1 ORDER BY o.name`, [userId]);
|
|
21
|
+
export async function membership(q, userId, orgId) {
|
|
22
|
+
const [row] = await q.query(`SELECT ${ORG_COLUMNS}, m.role FROM members m JOIN orgs o ON o.id = m.org_id WHERE m.user_id = $1 AND m.org_id = $2`, [
|
|
23
|
+
userId,
|
|
24
|
+
orgId,
|
|
25
|
+
]);
|
|
26
|
+
return row;
|
|
27
|
+
}
|
|
28
|
+
export async function updateOrg(q, orgId, change) {
|
|
29
|
+
if (change.name !== undefined)
|
|
30
|
+
await q.query("UPDATE orgs SET name = $1 WHERE id = $2", [change.name.trim().slice(0, 100), orgId]);
|
|
31
|
+
if (change.retentionDays !== undefined)
|
|
32
|
+
await q.query("UPDATE orgs SET retention_days = $1 WHERE id = $2", [change.retentionDays, orgId]);
|
|
33
|
+
}
|
|
34
|
+
export const listMembers = (q, orgId) => q.query(`SELECT u.id AS "userId", u.email, u.name, m.role, m.created_ms AS "sinceMs" FROM members m JOIN users u ON u.id = m.user_id
|
|
35
|
+
WHERE m.org_id = $1 ORDER BY u.name`, [orgId]);
|
|
36
|
+
export async function setRole(q, orgId, userId, role) {
|
|
37
|
+
await q.query("UPDATE members SET role = $1 WHERE org_id = $2 AND user_id = $3", [role, orgId, userId]);
|
|
38
|
+
}
|
|
39
|
+
export async function removeMember(q, orgId, userId) {
|
|
40
|
+
await q.query("DELETE FROM members WHERE org_id = $1 AND user_id = $2", [orgId, userId]);
|
|
41
|
+
}
|
|
42
|
+
export async function ownerCount(q, orgId) {
|
|
43
|
+
const [row] = await q.query("SELECT count(*)::int AS n FROM members WHERE org_id = $1 AND role = 'owner'", [orgId]);
|
|
44
|
+
return row?.n ?? 0;
|
|
45
|
+
}
|
|
46
|
+
/** A new invite link token for one email address. Valid for 7 days. */
|
|
47
|
+
export async function createInvite(q, orgId, email, role, by, now) {
|
|
48
|
+
const token = newToken("invite");
|
|
49
|
+
await q.query("INSERT INTO invites (token_hash, org_id, email, role, created_by, created_ms, expires_ms) VALUES ($1, $2, $3, $4, $5, $6, $7)", [
|
|
50
|
+
hashToken(token),
|
|
51
|
+
orgId,
|
|
52
|
+
normalizeEmail(email),
|
|
53
|
+
role,
|
|
54
|
+
by,
|
|
55
|
+
now,
|
|
56
|
+
now + INVITE_MS,
|
|
57
|
+
]);
|
|
58
|
+
return token;
|
|
59
|
+
}
|
|
60
|
+
export async function openInvite(q, token, now) {
|
|
61
|
+
const [row] = await q.query(`SELECT i.org_id AS "orgId", o.name AS "orgName", i.email, i.role FROM invites i JOIN orgs o ON o.id = i.org_id
|
|
62
|
+
WHERE i.token_hash = $1 AND i.accepted_ms IS NULL AND i.expires_ms > $2`, [hashToken(token), now]);
|
|
63
|
+
return row;
|
|
64
|
+
}
|
|
65
|
+
/** Join the organization of an open invite. The invite then cannot be used again. */
|
|
66
|
+
export async function acceptInvite(q, token, userId, now) {
|
|
67
|
+
const invite = await openInvite(q, token, now);
|
|
68
|
+
if (!invite)
|
|
69
|
+
return undefined;
|
|
70
|
+
await q.query("UPDATE invites SET accepted_ms = $1 WHERE token_hash = $2", [now, hashToken(token)]);
|
|
71
|
+
await q.query("INSERT INTO members (org_id, user_id, role, created_ms) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING", [invite.orgId, userId, invite.role, now]);
|
|
72
|
+
return invite;
|
|
73
|
+
}
|
|
74
|
+
export const pendingInvites = (q, orgId, now) => q.query(`SELECT email, role, expires_ms AS "expiresMs" FROM invites WHERE org_id = $1 AND accepted_ms IS NULL AND expires_ms > $2 ORDER BY created_ms DESC`, [orgId, now]);
|
|
75
|
+
//# sourceMappingURL=orgs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../src/repo/orgs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Queryable } from "../db/db.js";
|
|
2
|
+
export interface Project {
|
|
3
|
+
id: string;
|
|
4
|
+
orgId: string;
|
|
5
|
+
name: string;
|
|
6
|
+
slug: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function createProject(q: Queryable, orgId: string, name: string, now: number): Promise<Project>;
|
|
9
|
+
export declare const listProjects: (q: Queryable, orgId: string) => Promise<Project[]>;
|
|
10
|
+
export declare function projectInOrg(q: Queryable, orgId: string, projectId: string): Promise<Project | undefined>;
|
|
11
|
+
/** Delete a project and all of its data. */
|
|
12
|
+
export declare function deleteProject(q: Queryable, projectId: string): Promise<void>;
|
|
13
|
+
/** Delete every trace, span, log, service, metric, and stat of a project. The project and its settings stay. */
|
|
14
|
+
export declare function deleteProjectData(q: Queryable, projectId: string): Promise<void>;
|
|
15
|
+
export type KeyKind = "ingest" | "read";
|
|
16
|
+
export interface ApiKey {
|
|
17
|
+
id: string;
|
|
18
|
+
kind: KeyKind;
|
|
19
|
+
name: string;
|
|
20
|
+
prefix: string;
|
|
21
|
+
/** Ingest keys: exactly one project. Read keys: the projects they may read. */
|
|
22
|
+
projectIds: string[];
|
|
23
|
+
createdMs: number;
|
|
24
|
+
lastUsedMs: number | null;
|
|
25
|
+
}
|
|
26
|
+
/** A new key. Returns the secret once; only its hash is kept. */
|
|
27
|
+
export declare function createKey(q: Queryable, orgId: string, kind: KeyKind, name: string, projectIds: string[], by: string, now: number): Promise<{
|
|
28
|
+
id: string;
|
|
29
|
+
token: string;
|
|
30
|
+
}>;
|
|
31
|
+
export declare function listKeys(q: Queryable, orgId: string): Promise<ApiKey[]>;
|
|
32
|
+
export declare function revokeKey(q: Queryable, orgId: string, keyId: string, now: number): Promise<boolean>;
|
|
33
|
+
/** The key for a secret, when it is valid. Marks it used (at most once a minute). */
|
|
34
|
+
export declare function keyByToken(q: Queryable, token: string, now: number): Promise<(ApiKey & {
|
|
35
|
+
orgId: string;
|
|
36
|
+
}) | undefined>;
|
|
37
|
+
//# sourceMappingURL=projects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/repo/projects.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAID,wBAAsB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ5G;AAED,eAAO,MAAM,YAAY,MAAO,SAAS,SAAS,MAAM,KAAG,OAAO,CAAC,OAAO,EAAE,CACkB,CAAC;AAE/F,wBAAsB,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAG/G;AAED,4CAA4C;AAC5C,wBAAsB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlF;AAED,gHAAgH;AAChH,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtF;AAED,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAExC,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAUD,iEAAiE;AACjE,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;;;GAQtI;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAG7E;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGzG;AAED,qFAAqF;AACrF,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAAC,CAK5H"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { hashToken, newId, newToken, slugOf } from "../auth/crypto.js";
|
|
2
|
+
import { METRIC_TABLES } from "./metric-write.js";
|
|
3
|
+
import { STATS_TABLES } from "../settle.js";
|
|
4
|
+
const COLUMNS = `id, org_id AS "orgId", name, slug`;
|
|
5
|
+
export async function createProject(q, orgId, name, now) {
|
|
6
|
+
const base = slugOf(name);
|
|
7
|
+
const taken = new Set((await q.query("SELECT slug FROM projects WHERE org_id = $1", [orgId])).map((r) => r.slug));
|
|
8
|
+
let slug = base;
|
|
9
|
+
for (let i = 2; taken.has(slug); i++)
|
|
10
|
+
slug = `${base}-${i}`;
|
|
11
|
+
const p = { id: newId(), orgId, name: name.trim().slice(0, 100) || slug, slug };
|
|
12
|
+
await q.query("INSERT INTO projects (id, org_id, name, slug, created_ms) VALUES ($1, $2, $3, $4, $5)", [p.id, orgId, p.name, slug, now]);
|
|
13
|
+
return p;
|
|
14
|
+
}
|
|
15
|
+
export const listProjects = (q, orgId) => q.query(`SELECT ${COLUMNS} FROM projects WHERE org_id = $1 ORDER BY name`, [orgId]);
|
|
16
|
+
export async function projectInOrg(q, orgId, projectId) {
|
|
17
|
+
const [row] = await q.query(`SELECT ${COLUMNS} FROM projects WHERE org_id = $1 AND id = $2`, [orgId, projectId]);
|
|
18
|
+
return row;
|
|
19
|
+
}
|
|
20
|
+
/** Delete a project and all of its data. */
|
|
21
|
+
export async function deleteProject(q, projectId) {
|
|
22
|
+
await deleteProjectData(q, projectId);
|
|
23
|
+
await q.query("DELETE FROM api_keys WHERE project_ids = $1", [JSON.stringify([projectId])]);
|
|
24
|
+
await q.query("DELETE FROM projects WHERE id = $1", [projectId]);
|
|
25
|
+
}
|
|
26
|
+
/** Delete every trace, span, log, service, metric, and stat of a project. The project and its settings stay. */
|
|
27
|
+
export async function deleteProjectData(q, projectId) {
|
|
28
|
+
for (const table of ["spans", "logs", "traces", "services", "metric_points", ...METRIC_TABLES, ...STATS_TABLES])
|
|
29
|
+
await q.query(`DELETE FROM ${table} WHERE project_id = $1`, [projectId]);
|
|
30
|
+
}
|
|
31
|
+
const KEY_COLUMNS = `id, org_id, kind, name, prefix, project_ids, created_ms AS "createdMs", last_used_ms AS "lastUsedMs"`;
|
|
32
|
+
const toKey = ({ project_ids, org_id: _org, ...k }) => ({ ...k, projectIds: JSON.parse(project_ids) });
|
|
33
|
+
/** A new key. Returns the secret once; only its hash is kept. */
|
|
34
|
+
export async function createKey(q, orgId, kind, name, projectIds, by, now) {
|
|
35
|
+
const token = newToken(kind);
|
|
36
|
+
const id = newId();
|
|
37
|
+
await q.query(`INSERT INTO api_keys (id, org_id, kind, project_ids, name, prefix, token_hash, created_by, created_ms) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [id, orgId, kind, JSON.stringify(projectIds), name.trim().slice(0, 100) || kind, token.slice(0, 12), hashToken(token), by, now]);
|
|
38
|
+
return { id, token };
|
|
39
|
+
}
|
|
40
|
+
export async function listKeys(q, orgId) {
|
|
41
|
+
const rows = await q.query(`SELECT ${KEY_COLUMNS} FROM api_keys WHERE org_id = $1 AND revoked_ms IS NULL ORDER BY created_ms DESC`, [orgId]);
|
|
42
|
+
return rows.map(toKey);
|
|
43
|
+
}
|
|
44
|
+
export async function revokeKey(q, orgId, keyId, now) {
|
|
45
|
+
const rows = await q.query("UPDATE api_keys SET revoked_ms = $1 WHERE org_id = $2 AND id = $3 AND revoked_ms IS NULL RETURNING id", [now, orgId, keyId]);
|
|
46
|
+
return rows.length > 0;
|
|
47
|
+
}
|
|
48
|
+
/** The key for a secret, when it is valid. Marks it used (at most once a minute). */
|
|
49
|
+
export async function keyByToken(q, token, now) {
|
|
50
|
+
const [row] = await q.query(`SELECT ${KEY_COLUMNS} FROM api_keys WHERE token_hash = $1 AND revoked_ms IS NULL`, [hashToken(token)]);
|
|
51
|
+
if (!row)
|
|
52
|
+
return undefined;
|
|
53
|
+
if (!row.lastUsedMs || now - row.lastUsedMs > 60_000)
|
|
54
|
+
await q.query("UPDATE api_keys SET last_used_ms = $1 WHERE id = $2", [now, row.id]);
|
|
55
|
+
return { ...toKey(row), orgId: row.org_id };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/repo/projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type HubService, type Trace } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Queryable } from "../db/db.js";
|
|
3
|
+
export interface TraceQuery {
|
|
4
|
+
projectId: string;
|
|
5
|
+
/** Changed at or after (server clock). */
|
|
6
|
+
since?: number;
|
|
7
|
+
/** Started before. For paging back. */
|
|
8
|
+
before?: number;
|
|
9
|
+
/** Started at or after. For time windows. */
|
|
10
|
+
from?: number;
|
|
11
|
+
env?: string;
|
|
12
|
+
session?: string;
|
|
13
|
+
limit: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function findTraceIds(q: Queryable, t: TraceQuery): Promise<string[]>;
|
|
16
|
+
/** Full traces, in the order of `ids`. */
|
|
17
|
+
export declare function loadTraces(q: Queryable, projectId: string, ids: readonly string[]): Promise<Trace[]>;
|
|
18
|
+
export declare const queryTraces: (q: Queryable, t: TraceQuery) => Promise<Trace[]>;
|
|
19
|
+
export declare function getTrace(q: Queryable, projectId: string, traceId: string): Promise<Trace | undefined>;
|
|
20
|
+
/** How many traces started in [from, to). */
|
|
21
|
+
export declare function countTraces(q: Queryable, projectId: string, from: number, to: number, env?: string): Promise<number>;
|
|
22
|
+
export declare function listServices(q: Queryable, projectId: string, now: number): Promise<HubService[]>;
|
|
23
|
+
export declare function environmentsOf(q: Queryable, projectId: string): Promise<string[]>;
|
|
24
|
+
export declare function countRows(q: Queryable, projectId: string): Promise<{
|
|
25
|
+
traces: number;
|
|
26
|
+
spans: number;
|
|
27
|
+
}>;
|
|
28
|
+
//# sourceMappingURL=trace-read.d.ts.map
|