@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
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { hashToken, newId, newToken } from "../auth/crypto.js";
|
|
2
|
+
import { requireProject } from "../auth/principal.js";
|
|
3
|
+
import { fail, readJsonBody, sendJson, str } from "../http/http.js";
|
|
4
|
+
import { audit } from "../repo/audit.js";
|
|
5
|
+
import { getTrace } from "../repo/trace-read.js";
|
|
6
|
+
const DAY_MS = 86_400_000;
|
|
7
|
+
/** A read-only link to one trace, valid for 1 to 30 days. Only the token's hash is kept. */
|
|
8
|
+
async function create(ctx) {
|
|
9
|
+
const { project, actor } = await requireProject(ctx, ctx.params.project, "write");
|
|
10
|
+
const body = await readJsonBody(ctx.req);
|
|
11
|
+
const traceId = str(body, "traceId");
|
|
12
|
+
const days = Number(body.days ?? 7);
|
|
13
|
+
if (!Number.isInteger(days) || days < 1 || days > 30)
|
|
14
|
+
fail(400, "A link lasts 1 to 30 days.");
|
|
15
|
+
if (!(await getTrace(ctx.app.db, project.id, traceId)))
|
|
16
|
+
fail(404, "Trace not found.");
|
|
17
|
+
const now = ctx.app.config.now();
|
|
18
|
+
const token = newToken("share");
|
|
19
|
+
await ctx.app.db.query("INSERT INTO shares (id, project_id, trace_id, token_hash, created_by, created_ms, expires_ms) VALUES ($1, $2, $3, $4, $5, $6, $7)", [
|
|
20
|
+
newId(),
|
|
21
|
+
project.id,
|
|
22
|
+
traceId,
|
|
23
|
+
hashToken(token),
|
|
24
|
+
actor,
|
|
25
|
+
now,
|
|
26
|
+
now + days * DAY_MS,
|
|
27
|
+
]);
|
|
28
|
+
await audit(ctx.app.db, project.orgId, actor, "share.create", { projectId: project.id, traceId, days }, now);
|
|
29
|
+
sendJson(ctx.res, 201, { url: `${ctx.app.baseUrl(ctx.req)}/_wizelens/#/shared/${token}`, expiresMs: now + days * DAY_MS });
|
|
30
|
+
}
|
|
31
|
+
async function list(ctx) {
|
|
32
|
+
const { project } = await requireProject(ctx, ctx.params.project);
|
|
33
|
+
const rows = await ctx.app.db.query(`SELECT id, trace_id AS "traceId", created_ms AS "createdMs", expires_ms AS "expiresMs", views FROM shares
|
|
34
|
+
WHERE project_id = $1 AND revoked_ms IS NULL AND expires_ms > $2 ORDER BY created_ms DESC`, [project.id, ctx.app.config.now()]);
|
|
35
|
+
sendJson(ctx.res, 200, rows);
|
|
36
|
+
}
|
|
37
|
+
async function revoke(ctx) {
|
|
38
|
+
const { project, actor } = await requireProject(ctx, ctx.params.project, "write");
|
|
39
|
+
const now = ctx.app.config.now();
|
|
40
|
+
const rows = await ctx.app.db.query("UPDATE shares SET revoked_ms = $1 WHERE project_id = $2 AND id = $3 AND revoked_ms IS NULL RETURNING id", [now, project.id, ctx.params.id]);
|
|
41
|
+
if (rows.length === 0)
|
|
42
|
+
fail(404, "Link not found.");
|
|
43
|
+
await audit(ctx.app.db, project.orgId, actor, "share.revoke", { projectId: project.id, shareId: ctx.params.id }, now);
|
|
44
|
+
sendJson(ctx.res, 200, { ok: true });
|
|
45
|
+
}
|
|
46
|
+
/** Anyone with the link can read the trace while the link is valid. Nothing else is reachable with it. */
|
|
47
|
+
async function open(ctx) {
|
|
48
|
+
const now = ctx.app.config.now();
|
|
49
|
+
const [row] = await ctx.app.db.query("UPDATE shares SET views = views + 1 WHERE token_hash = $1 AND revoked_ms IS NULL AND expires_ms > $2 RETURNING project_id, trace_id, expires_ms", [hashToken(ctx.params.token), now]);
|
|
50
|
+
const trace = row ? await getTrace(ctx.app.db, row.project_id, row.trace_id) : undefined;
|
|
51
|
+
if (!row || !trace)
|
|
52
|
+
return fail(404, "This link is not valid any more.");
|
|
53
|
+
sendJson(ctx.res, 200, { trace, expiresMs: row.expires_ms });
|
|
54
|
+
}
|
|
55
|
+
export function shareRoutes(r) {
|
|
56
|
+
r.on("POST", "api/projects/:project/shares", create)
|
|
57
|
+
.on("GET", "api/projects/:project/shares", list)
|
|
58
|
+
.on("DELETE", "api/projects/:project/shares/:id", revoke)
|
|
59
|
+
.on("GET", "api/shared/:token", open);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=shares.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shares.js","sourceRoot":"","sources":["../../src/routes/shares.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Executor, type SqlRunner } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Queryable } from "./db/db.js";
|
|
3
|
+
/** Reads for the SQL executor, on the cloud database. */
|
|
4
|
+
export declare const pgRunner: (q: Queryable) => SqlRunner;
|
|
5
|
+
/** How long a project's organization keeps data, in ms; `fallbackDays` when the project has no organization. */
|
|
6
|
+
export declare function projectRetentionMs(q: Queryable, projectId: string, fallbackDays: number): Promise<number>;
|
|
7
|
+
/** Page needs from a project's stats tables, optionally one environment. The 1 h tier keeps the organization's retention. */
|
|
8
|
+
export declare function cloudExecutor(q: Queryable, projectId: string, retentionMs: number, env?: string): Executor;
|
|
9
|
+
/** Traces of a project still waiting for their stats, for `pageContext(..., settling)`. Null when none. */
|
|
10
|
+
export declare function cloudSettling(q: Queryable, projectId: string, env?: string): Promise<number | null>;
|
|
11
|
+
//# sourceMappingURL=scale-exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scale-exec.d.ts","sourceRoot":"","sources":["../src/scale-exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAc,KAAK,SAAS,EAAe,MAAM,sBAAsB,CAAC;AAC9F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,yDAAyD;AACzD,eAAO,MAAM,QAAQ,MAAO,SAAS,KAAG,SAA2F,CAAC;AAEpI,gHAAgH;AAChH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG/G;AAED,6HAA6H;AAC7H,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,CAQ1G;AAED,2GAA2G;AAC3G,wBAAsB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMzG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PG_DIALECT, sqlExecutor } from "@wizeworks/lens-core";
|
|
2
|
+
import { getTrace } from "./repo/trace-read.js";
|
|
3
|
+
const DAY_MS = 86_400_000;
|
|
4
|
+
/** Reads for the SQL executor, on the cloud database. */
|
|
5
|
+
export const pgRunner = (q) => ({ all: (sql, params) => q.query(sql, [...params]) });
|
|
6
|
+
/** How long a project's organization keeps data, in ms; `fallbackDays` when the project has no organization. */
|
|
7
|
+
export async function projectRetentionMs(q, projectId, fallbackDays) {
|
|
8
|
+
const [row] = await q.query("SELECT o.retention_days AS d FROM projects p JOIN orgs o ON o.id = p.org_id WHERE p.id = $1", [projectId]);
|
|
9
|
+
return (row?.d ?? fallbackDays) * DAY_MS;
|
|
10
|
+
}
|
|
11
|
+
/** Page needs from a project's stats tables, optionally one environment. The 1 h tier keeps the organization's retention. */
|
|
12
|
+
export function cloudExecutor(q, projectId, retentionMs, env) {
|
|
13
|
+
return sqlExecutor({
|
|
14
|
+
d: PG_DIALECT,
|
|
15
|
+
run: pgRunner(q),
|
|
16
|
+
scope: { project: projectId, env },
|
|
17
|
+
retentionMs,
|
|
18
|
+
loadTrace: async (id) => (await getTrace(q, projectId, id)) ?? null,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Traces of a project still waiting for their stats, for `pageContext(..., settling)`. Null when none. */
|
|
22
|
+
export async function cloudSettling(q, projectId, env) {
|
|
23
|
+
const [row] = await q.query("SELECT count(*)::int AS n FROM traces WHERE project_id = $1 AND settled_ms < updated_ms AND ($2::text = '' OR env = $2::text)", [projectId, env ?? ""]);
|
|
24
|
+
return row?.n || null;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=scale-exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scale-exec.js","sourceRoot":"","sources":["../src/scale-exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,UAAU,EAAkB,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE9F,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,MAAM,MAAM,GAAG,UAAU,CAAC;AAE1B,yDAAyD;AACzD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAY,EAAa,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAA0B,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;AAEpI,gHAAgH;AAChH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,CAAY,EAAE,SAAiB,EAAE,YAAoB;IAC5F,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAgB,6FAA6F,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IACvJ,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,GAAG,MAAM,CAAC;AAC3C,CAAC;AAED,6HAA6H;AAC7H,MAAM,UAAU,aAAa,CAAC,CAAY,EAAE,SAAiB,EAAE,WAAmB,EAAE,GAAY;IAC9F,OAAO,WAAW,CAAC;QACjB,CAAC,EAAE,UAAU;QACb,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;QAChB,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE;QAClC,WAAW;QACX,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI;KACpE,CAAC,CAAC;AACL,CAAC;AAED,2GAA2G;AAC3G,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,CAAY,EAAE,SAAiB,EAAE,GAAY;IAC/E,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CACzB,+HAA+H,EAC/H,CAAC,SAAS,EAAE,GAAG,IAAI,EAAE,CAAC,CACvB,CAAC;IACF,OAAO,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC;AACxB,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import type { CloudApp } from "./app.js";
|
|
3
|
+
import { type CloudOptions } from "./config.js";
|
|
4
|
+
export declare const CLOUD_VERSION: string;
|
|
5
|
+
export interface Cloud {
|
|
6
|
+
readonly server: Server;
|
|
7
|
+
readonly app: CloudApp;
|
|
8
|
+
listen(): Promise<string>;
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
/** Open the database, bring its schema up to date, and build the server. Call `listen()` to start. */
|
|
12
|
+
export declare function createCloud(options?: CloudOptions): Promise<Cloud>;
|
|
13
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAIjG,OAAO,KAAK,EAAE,QAAQ,EAAO,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,aAAa,CAAC;AAY/D,eAAO,MAAM,aAAa,EAAE,MAAoE,CAAC;AAGjG,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAsDD,sGAAsG;AACtG,wBAAsB,WAAW,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,CA0B5E"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { otlpSignalOf } from "@wizeworks/lens-hub/kit";
|
|
4
|
+
import { resolveUiDir, serveUi } from "@wizeworks/lens-node/kit";
|
|
5
|
+
import { resolveConfig } from "./config.js";
|
|
6
|
+
import { openDb } from "./db/db.js";
|
|
7
|
+
import { migrate } from "./db/migrate.js";
|
|
8
|
+
import { HttpError, sendJson } from "./http/http.js";
|
|
9
|
+
import { RateLimiter } from "./http/limiter.js";
|
|
10
|
+
import { Router } from "./http/router.js";
|
|
11
|
+
import { startJobs } from "./jobs.js";
|
|
12
|
+
import { checkFactsVersion } from "./settle.js";
|
|
13
|
+
import { allRoutes } from "./routes/index.js";
|
|
14
|
+
import { ingestBatch, ingestOtlp } from "./routes/ingest.js";
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
export const CLOUD_VERSION = require("../package.json").version;
|
|
17
|
+
const BASE = "/_wizelens";
|
|
18
|
+
/** Changes from a browser must carry the dashboard header, so a page on another site cannot send them. */
|
|
19
|
+
const needsClientHeader = (req, sub) => req.method !== "GET" && req.method !== "HEAD" && !req.headers.authorization && sub.startsWith("api/") && sub !== "api/ingest";
|
|
20
|
+
async function routeBase(p, ctx, sub) {
|
|
21
|
+
const { req, res } = ctx;
|
|
22
|
+
if (sub === "api/ingest" && req.method === "POST")
|
|
23
|
+
return ingestBatch(ctx, p.ingest);
|
|
24
|
+
if (needsClientHeader(req, sub) && req.headers["x-wizelens-client"] !== "dashboard")
|
|
25
|
+
throw new HttpError(403, "Missing the X-WizeLens-Client header.");
|
|
26
|
+
const m = p.router.match(req.method ?? "GET", sub);
|
|
27
|
+
if (m)
|
|
28
|
+
return m.handler({ ...ctx, params: m.params });
|
|
29
|
+
if (sub.startsWith("api/") || sub === "mcp")
|
|
30
|
+
throw new HttpError(p.router.hasPath(sub) ? 405 : 404, "Not found.");
|
|
31
|
+
if (req.method !== "GET" && req.method !== "HEAD")
|
|
32
|
+
return void res.writeHead(405).end();
|
|
33
|
+
await serveUi(p.uiDir, sub, res);
|
|
34
|
+
}
|
|
35
|
+
async function route(p, req, res) {
|
|
36
|
+
const url = new URL(req.url ?? "/", "http://cloud.local");
|
|
37
|
+
const path = url.pathname;
|
|
38
|
+
const ctx = { req, res, url, params: {}, app: p.app };
|
|
39
|
+
if (path === "/healthz")
|
|
40
|
+
return void res.writeHead(200, { "Content-Type": "text/plain" }).end("ok");
|
|
41
|
+
if (path === "/" || path === BASE)
|
|
42
|
+
return void res.writeHead(302, { Location: `${BASE}/${url.search}` }).end();
|
|
43
|
+
const signal = otlpSignalOf(path);
|
|
44
|
+
if (signal && req.method === "POST")
|
|
45
|
+
return ingestOtlp(ctx, signal, p.ingest);
|
|
46
|
+
if (path.startsWith(`${BASE}/`))
|
|
47
|
+
return routeBase(p, ctx, path.slice(BASE.length + 1));
|
|
48
|
+
sendJson(res, 404, { error: "Not found." });
|
|
49
|
+
}
|
|
50
|
+
function onError(err, res) {
|
|
51
|
+
if (res.headersSent)
|
|
52
|
+
return void res.end();
|
|
53
|
+
if (err instanceof HttpError)
|
|
54
|
+
return sendJson(res, err.status, { error: err.message });
|
|
55
|
+
console.error("[wizelens-cloud]", err);
|
|
56
|
+
sendJson(res, 500, { error: "Something went wrong on the server." });
|
|
57
|
+
}
|
|
58
|
+
function makeApp(db, options) {
|
|
59
|
+
const config = resolveConfig(options);
|
|
60
|
+
return {
|
|
61
|
+
db,
|
|
62
|
+
config,
|
|
63
|
+
version: CLOUD_VERSION,
|
|
64
|
+
startedMs: config.now(),
|
|
65
|
+
baseUrl: (req) => config.publicUrl ?? `${req.headers["x-forwarded-proto"] === "https" ? "https" : "http"}://${req.headers.host ?? "localhost"}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Open the database, bring its schema up to date, and build the server. Call `listen()` to start. */
|
|
69
|
+
export async function createCloud(options = {}) {
|
|
70
|
+
const db = await openDb(resolveConfig(options).database);
|
|
71
|
+
await migrate(db);
|
|
72
|
+
await checkFactsVersion(db);
|
|
73
|
+
const app = makeApp(db, options);
|
|
74
|
+
const parts = { app, router: allRoutes(), ingest: new RateLimiter(app.config.ingestPerMinute, 60_000, app.config.now), uiDir: resolveUiDir(app.config.uiDir) };
|
|
75
|
+
const server = createServer((req, res) => void route(parts, req, res).catch((err) => onError(err, res)));
|
|
76
|
+
const stopJobs = startJobs(app);
|
|
77
|
+
return {
|
|
78
|
+
server,
|
|
79
|
+
app,
|
|
80
|
+
listen: () => new Promise((resolve, reject) => {
|
|
81
|
+
server.once("error", reject);
|
|
82
|
+
server.listen(app.config.port, app.config.host, () => {
|
|
83
|
+
const { port } = server.address();
|
|
84
|
+
resolve(app.config.publicUrl ?? `http://localhost:${port}`);
|
|
85
|
+
});
|
|
86
|
+
}),
|
|
87
|
+
close: async () => {
|
|
88
|
+
stopJobs();
|
|
89
|
+
server.closeAllConnections?.();
|
|
90
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
91
|
+
await db.close();
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/settle.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Statement, type TraceState } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Db, Queryable } from "./db/db.js";
|
|
3
|
+
/** Fact, rollup, and hist tables: everything settling builds. */
|
|
4
|
+
export declare const STATS_TABLES: string[];
|
|
5
|
+
export interface TraceStateRow {
|
|
6
|
+
project_id: string;
|
|
7
|
+
trace_id: string;
|
|
8
|
+
env: string;
|
|
9
|
+
updated_ms?: number;
|
|
10
|
+
contrib: string | null;
|
|
11
|
+
retention_days: number | null;
|
|
12
|
+
}
|
|
13
|
+
/** Run statements one by one, in the caller's transaction. */
|
|
14
|
+
export declare function runStatements(q: Queryable, statements: readonly Statement[]): Promise<void>;
|
|
15
|
+
export declare const stateOf: (r: TraceStateRow, defaultDays: number) => TraceState;
|
|
16
|
+
/** Settle dirty traces in batches until none are left or the budget is spent. Returns how many settled. */
|
|
17
|
+
export declare function settleCloud(db: Db, now: number, o?: {
|
|
18
|
+
defaultRetentionDays?: number;
|
|
19
|
+
batch?: number;
|
|
20
|
+
budgetMs?: number;
|
|
21
|
+
}): Promise<number>;
|
|
22
|
+
/** Age out rollup buckets: 10 s and 5 min by their keep time, 1 h by each organization's retention. */
|
|
23
|
+
export declare function pruneRollups(q: Queryable, now: number, empties?: boolean): Promise<void>;
|
|
24
|
+
/** On startup: when the facts version changed, clear the stats so settling rebuilds them. Returns true when it did. */
|
|
25
|
+
export declare function checkFactsVersion(db: Db): Promise<boolean>;
|
|
26
|
+
//# sourceMappingURL=settle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settle.d.ts","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4D,KAAK,SAAS,EAAuB,KAAK,UAAU,EAAc,MAAM,sBAAsB,CAAC;AAClK,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAUhD,iEAAiE;AACjE,eAAO,MAAM,YAAY,UAAwF,CAAC;AAElH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,8DAA8D;AAC9D,wBAAsB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjG;AAED,eAAO,MAAM,OAAO,MAAO,aAAa,eAAe,MAAM,KAAG,UAO9D,CAAC;AA6BH,2GAA2G;AAC3G,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CASpJ;AAED,uGAAuG;AACvG,wBAAsB,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5F;AAED,uHAAuH;AACvH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAShE"}
|
package/dist/settle.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { FACT_TABLES, FACTS_VERSION, PG_DIALECT, settleStatements, tierPruneStatements } from "@wizeworks/lens-core";
|
|
2
|
+
import { loadTraces } from "./repo/trace-read.js";
|
|
3
|
+
// Settling: traces quiet for a while get their facts and rollups rebuilt from the stored spans and logs (docs/CONTRACT.md).
|
|
4
|
+
const BATCH = 500;
|
|
5
|
+
const BUDGET_MS = 2_000;
|
|
6
|
+
const QUIET_MS = 5_000;
|
|
7
|
+
const BUSY_MS = 30_000;
|
|
8
|
+
const LOCK = 4711;
|
|
9
|
+
const DAY_MS = 86_400_000;
|
|
10
|
+
/** Fact, rollup, and hist tables: everything settling builds. */
|
|
11
|
+
export const STATS_TABLES = [...FACT_TABLES, ...["10s", "5m", "1h"].flatMap((t) => [`rollup_${t}`, `hist_${t}`])];
|
|
12
|
+
/** Run statements one by one, in the caller's transaction. */
|
|
13
|
+
export async function runStatements(q, statements) {
|
|
14
|
+
for (const s of statements)
|
|
15
|
+
await q.query(s.sql, s.params);
|
|
16
|
+
}
|
|
17
|
+
export const stateOf = (r, defaultDays) => ({
|
|
18
|
+
traceId: r.trace_id,
|
|
19
|
+
project: r.project_id,
|
|
20
|
+
env: r.env,
|
|
21
|
+
updatedMs: r.updated_ms ?? 0,
|
|
22
|
+
contrib: r.contrib,
|
|
23
|
+
retentionMs: (r.retention_days ?? defaultDays) * DAY_MS,
|
|
24
|
+
});
|
|
25
|
+
async function loadAll(q, rows) {
|
|
26
|
+
const byProject = new Map();
|
|
27
|
+
for (const r of rows)
|
|
28
|
+
byProject.set(r.project_id, [...(byProject.get(r.project_id) ?? []), r.trace_id]);
|
|
29
|
+
const out = new Map();
|
|
30
|
+
for (const [project, ids] of byProject)
|
|
31
|
+
for (const t of await loadTraces(q, project, ids))
|
|
32
|
+
out.set(`${project} ${t.traceId}`, t);
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
/** Settle one batch in one transaction. -1 when another instance holds the lock. */
|
|
36
|
+
async function settleBatch(db, now, batch, defaultDays) {
|
|
37
|
+
return db.tx(async (q) => {
|
|
38
|
+
const [lock] = await q.query("SELECT pg_try_advisory_xact_lock($1) AS ok", [LOCK]);
|
|
39
|
+
if (!lock?.ok)
|
|
40
|
+
return -1;
|
|
41
|
+
const rows = await q.query(`SELECT t.project_id, t.trace_id, t.env, t.updated_ms, t.contrib, o.retention_days FROM traces t
|
|
42
|
+
LEFT JOIN projects p ON p.id = t.project_id LEFT JOIN orgs o ON o.id = p.org_id
|
|
43
|
+
WHERE t.settled_ms < t.updated_ms AND (t.updated_ms <= $1 OR t.settled_ms <= $2) ORDER BY t.start_ms DESC LIMIT $3`, [now - QUIET_MS, now - BUSY_MS, batch]);
|
|
44
|
+
if (!rows.length)
|
|
45
|
+
return 0;
|
|
46
|
+
const traces = await loadAll(q, rows);
|
|
47
|
+
const work = rows.map((r) => ({ state: stateOf(r, defaultDays), trace: traces.get(`${r.project_id} ${r.trace_id}`) }));
|
|
48
|
+
await runStatements(q, settleStatements(PG_DIALECT, work, now, true));
|
|
49
|
+
return rows.length;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** Settle dirty traces in batches until none are left or the budget is spent. Returns how many settled. */
|
|
53
|
+
export async function settleCloud(db, now, o = {}) {
|
|
54
|
+
const start = Date.now();
|
|
55
|
+
const batch = o.batch ?? BATCH;
|
|
56
|
+
let total = 0;
|
|
57
|
+
for (;;) {
|
|
58
|
+
const n = await settleBatch(db, now, batch, o.defaultRetentionDays ?? 30);
|
|
59
|
+
if (n > 0)
|
|
60
|
+
total += n;
|
|
61
|
+
if (n < batch || Date.now() - start >= (o.budgetMs ?? BUDGET_MS))
|
|
62
|
+
return total;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Age out rollup buckets: 10 s and 5 min by their keep time, 1 h by each organization's retention. */
|
|
66
|
+
export async function pruneRollups(q, now, empties = false) {
|
|
67
|
+
await runStatements(q, tierPruneStatements(now, empties));
|
|
68
|
+
for (const t of ["rollup_1h", "hist_1h"]) {
|
|
69
|
+
await q.query(`DELETE FROM ${t} r USING projects p, orgs o WHERE p.id = r.project_id AND o.id = p.org_id AND r.bucket_ms <= $1 - o.retention_days * 86400000.0`, [now - 3_600_000]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** On startup: when the facts version changed, clear the stats so settling rebuilds them. Returns true when it did. */
|
|
73
|
+
export async function checkFactsVersion(db) {
|
|
74
|
+
const [row] = await db.query("SELECT value FROM scale_meta WHERE key = 'facts'");
|
|
75
|
+
if (row?.value === String(FACTS_VERSION))
|
|
76
|
+
return false;
|
|
77
|
+
await db.tx(async (q) => {
|
|
78
|
+
for (const t of STATS_TABLES)
|
|
79
|
+
await q.query(`DELETE FROM ${t}`);
|
|
80
|
+
await q.query("UPDATE traces SET settled_ms = 0, contrib = NULL");
|
|
81
|
+
await q.query("INSERT INTO scale_meta (key, value) VALUES ('facts', $1) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value", [String(FACTS_VERSION)]);
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=settle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settle.js","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wizeworks/lens-cloud",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.3.0-alpha.1",
|
|
4
|
+
"description": "The WizeLens cloud service: many organizations, accounts and teams, server-side stats, alerts, sharing, and scoped AI access, on Postgres.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "WizeWorks LLC",
|
|
7
|
+
"homepage": "https://github.com/brandonkorous/wizelens#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/brandonkorous/wizelens.git",
|
|
11
|
+
"directory": "js/packages/cloud"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/brandonkorous/wizelens/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"wizelens",
|
|
18
|
+
"cloud",
|
|
19
|
+
"tracing",
|
|
20
|
+
"opentelemetry",
|
|
21
|
+
"otlp",
|
|
22
|
+
"profiler",
|
|
23
|
+
"postgres"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"bin": {
|
|
29
|
+
"wizelens-cloud": "./dist/cli.js"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22.13"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@electric-sql/pglite": "^0.5.10",
|
|
46
|
+
"pg": "^8.23.1",
|
|
47
|
+
"@wizeworks/lens-core": "^0.3.0-alpha.1",
|
|
48
|
+
"@wizeworks/lens-hub": "^0.3.0-alpha.1",
|
|
49
|
+
"@wizeworks/lens-node": "^0.3.0-alpha.1",
|
|
50
|
+
"@wizeworks/lens-ui": "^0.3.0-alpha.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^24.19.2",
|
|
54
|
+
"@types/pg": "^8.23.1",
|
|
55
|
+
"typescript": "^7.0.2",
|
|
56
|
+
"vitest": "^5.0.5"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsc -p tsconfig.build.json",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"start": "node dist/cli.js"
|
|
65
|
+
}
|
|
6
66
|
}
|