@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,74 @@
|
|
|
1
|
+
import { principalOf, requireProject } from "../auth/principal.js";
|
|
2
|
+
import { fail, sendJson } from "../http/http.js";
|
|
3
|
+
import { countRows, environmentsOf, getTrace, listServices, queryTraces } from "../repo/trace-read.js";
|
|
4
|
+
const DAY_MS = 86_400_000;
|
|
5
|
+
const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
|
|
6
|
+
const num = (p, k) => (p.has(k) && Number.isFinite(Number(p.get(k))) ? Number(p.get(k)) : undefined);
|
|
7
|
+
/** The project named by `?project=`, with access checked. */
|
|
8
|
+
export async function projectOf(ctx) {
|
|
9
|
+
const id = ctx.url.searchParams.get("project");
|
|
10
|
+
if (!id)
|
|
11
|
+
return fail(400, "Add ?project=<project id>.");
|
|
12
|
+
return requireProject(ctx, id);
|
|
13
|
+
}
|
|
14
|
+
async function retentionMs(ctx) {
|
|
15
|
+
const id = ctx.url.searchParams.get("project");
|
|
16
|
+
const p = id ? await principalOf(ctx) : undefined;
|
|
17
|
+
if (!id || !p)
|
|
18
|
+
return ctx.app.config.retentionDays * DAY_MS;
|
|
19
|
+
const access = await requireProject(ctx, id).catch(() => undefined);
|
|
20
|
+
if (!access)
|
|
21
|
+
return ctx.app.config.retentionDays * DAY_MS;
|
|
22
|
+
const [row] = await ctx.app.db.query("SELECT retention_days AS d FROM orgs WHERE id = $1", [access.project.orgId]);
|
|
23
|
+
return (row?.d ?? ctx.app.config.retentionDays) * DAY_MS;
|
|
24
|
+
}
|
|
25
|
+
async function info(ctx) {
|
|
26
|
+
const value = {
|
|
27
|
+
mode: "cloud",
|
|
28
|
+
capabilities: ["environments", "registry", "accounts", "alerts", "sharing", "serverStats", "metrics", "serverMetrics"],
|
|
29
|
+
service: "WizeLens Cloud",
|
|
30
|
+
version: ctx.app.version,
|
|
31
|
+
runtime: `node ${process.version}`,
|
|
32
|
+
mcp: true,
|
|
33
|
+
startedMs: ctx.app.startedMs,
|
|
34
|
+
storage: { kind: ctx.app.db.kind === "postgres" ? "postgres" : "sqlite", maxAgeMs: await retentionMs(ctx) },
|
|
35
|
+
redactKeys: [...ctx.app.config.redactKeys],
|
|
36
|
+
};
|
|
37
|
+
sendJson(ctx.res, 200, value);
|
|
38
|
+
}
|
|
39
|
+
async function traces(ctx) {
|
|
40
|
+
const { project } = await projectOf(ctx);
|
|
41
|
+
const p = ctx.url.searchParams;
|
|
42
|
+
const full = p.get("full") === "true";
|
|
43
|
+
const q = { projectId: project.id, limit: clamp(num(p, "limit") ?? (full ? 1000 : 50), 1, full ? 5000 : 1000) };
|
|
44
|
+
q.since = num(p, "since");
|
|
45
|
+
q.before = num(p, "before");
|
|
46
|
+
q.env = p.get("env") || undefined;
|
|
47
|
+
q.session = p.get("session") || undefined;
|
|
48
|
+
const list = await queryTraces(ctx.app.db, q);
|
|
49
|
+
sendJson(ctx.res, 200, full ? list : list.map(({ spans: _s, logs: _l, remotes: _r, ...summary }) => summary));
|
|
50
|
+
}
|
|
51
|
+
async function trace(ctx) {
|
|
52
|
+
const { project } = await projectOf(ctx);
|
|
53
|
+
const t = await getTrace(ctx.app.db, project.id, ctx.params.id);
|
|
54
|
+
if (!t)
|
|
55
|
+
fail(404, "Trace not found.");
|
|
56
|
+
sendJson(ctx.res, 200, t);
|
|
57
|
+
}
|
|
58
|
+
async function services(ctx) {
|
|
59
|
+
const { project } = await projectOf(ctx);
|
|
60
|
+
const db = ctx.app.db;
|
|
61
|
+
sendJson(ctx.res, 200, {
|
|
62
|
+
services: await listServices(db, project.id, ctx.app.config.now()),
|
|
63
|
+
environments: await environmentsOf(db, project.id),
|
|
64
|
+
projects: [],
|
|
65
|
+
...(await countRows(db, project.id)),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export function dataRoutes(r) {
|
|
69
|
+
r.on("GET", "api/info", info)
|
|
70
|
+
.on("GET", "api/traces", traces)
|
|
71
|
+
.on("GET", "api/traces/:id", trace)
|
|
72
|
+
.on("GET", "api/services", services);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/routes/data.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Ctx } from "../app.js";
|
|
2
|
+
import { Router } from "../http/router.js";
|
|
3
|
+
/** Every route below `/_wizelens/`, except ingest and OTLP (they use ingest keys and are matched first). */
|
|
4
|
+
export declare function allRoutes(): Router<Ctx>;
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAU3C,4GAA4G;AAC5G,wBAAgB,SAAS,IAAI,MAAM,CAAC,GAAG,CAAC,CAWvC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Router } from "../http/router.js";
|
|
2
|
+
import { alertRoutes } from "./alerts.js";
|
|
3
|
+
import { authRoutes } from "./auth.js";
|
|
4
|
+
import { mcpRoutes } from "./mcp.js";
|
|
5
|
+
import { shareRoutes } from "./shares.js";
|
|
6
|
+
import { dataRoutes } from "./data.js";
|
|
7
|
+
import { modelRoutes } from "./models.js";
|
|
8
|
+
import { orgRoutes } from "./orgs.js";
|
|
9
|
+
import { projectRoutes } from "./projects.js";
|
|
10
|
+
/** Every route below `/_wizelens/`, except ingest and OTLP (they use ingest keys and are matched first). */
|
|
11
|
+
export function allRoutes() {
|
|
12
|
+
const r = new Router();
|
|
13
|
+
authRoutes(r);
|
|
14
|
+
orgRoutes(r);
|
|
15
|
+
projectRoutes(r);
|
|
16
|
+
dataRoutes(r);
|
|
17
|
+
modelRoutes(r);
|
|
18
|
+
alertRoutes(r);
|
|
19
|
+
shareRoutes(r);
|
|
20
|
+
mcpRoutes(r);
|
|
21
|
+
return r;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,4GAA4G;AAC5G,MAAM,UAAU,SAAS;IACvB,MAAM,CAAC,GAAG,IAAI,MAAM,EAAO,CAAC;IAC5B,UAAU,CAAC,CAAC,CAAC,CAAC;IACd,SAAS,CAAC,CAAC,CAAC,CAAC;IACb,aAAa,CAAC,CAAC,CAAC,CAAC;IACjB,UAAU,CAAC,CAAC,CAAC,CAAC;IACd,WAAW,CAAC,CAAC,CAAC,CAAC;IACf,WAAW,CAAC,CAAC,CAAC,CAAC;IACf,WAAW,CAAC,CAAC,CAAC,CAAC;IACf,SAAS,CAAC,CAAC,CAAC,CAAC;IACb,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Ctx } from "../app.js";
|
|
2
|
+
import type { RateLimiter } from "../http/limiter.js";
|
|
3
|
+
import { type OtlpSignal } from "@wizeworks/lens-hub/kit";
|
|
4
|
+
/** `POST api/ingest`: a WizeLens add-on batch. */
|
|
5
|
+
export declare function ingestBatch(ctx: Ctx, limiter: RateLimiter): Promise<void>;
|
|
6
|
+
/** `POST /v1/traces` and `/v1/logs`: OpenTelemetry apps. */
|
|
7
|
+
export declare function ingestOtlp(ctx: Ctx, signal: OtlpSignal, limiter: RateLimiter): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=ingest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/routes/ingest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,EAA6B,KAAK,UAAU,EAAoC,MAAM,yBAAyB,CAAC;AAevH,kDAAkD;AAClD,wBAAsB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAW/E;AAED,4DAA4D;AAC5D,wBAAsB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlG"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { bearerOf, fail } from "../http/http.js";
|
|
2
|
+
import { keyByToken } from "../repo/projects.js";
|
|
3
|
+
import { writeBatch } from "../repo/trace-write.js";
|
|
4
|
+
import { handleOtlp, parseIngest, parseJson, readBody } from "@wizeworks/lens-hub/kit";
|
|
5
|
+
/** The project an ingest key writes to. Add-ons send it as WIZELENS_HUB_TOKEN; OTLP apps as a bearer header. */
|
|
6
|
+
async function ingestProject(ctx, limiter) {
|
|
7
|
+
const token = bearerOf(ctx.req);
|
|
8
|
+
if (!token?.startsWith("wl_ik_"))
|
|
9
|
+
return fail(401, "Send an ingest key: Authorization: Bearer wl_ik_...");
|
|
10
|
+
const key = await keyByToken(ctx.app.db, token, ctx.app.config.now());
|
|
11
|
+
if (!key || key.kind !== "ingest")
|
|
12
|
+
return fail(401, "This ingest key is not valid.");
|
|
13
|
+
if (!limiter.take(key.id))
|
|
14
|
+
fail(429, "Too many calls for this key. Slow down and retry.");
|
|
15
|
+
return key.projectIds[0];
|
|
16
|
+
}
|
|
17
|
+
const save = (ctx, projectId) => (w) => ctx.app.db.tx((q) => writeBatch(q, projectId, w, ctx.app.config.now()));
|
|
18
|
+
/** `POST api/ingest`: a WizeLens add-on batch. */
|
|
19
|
+
export async function ingestBatch(ctx, limiter) {
|
|
20
|
+
const projectId = await ingestProject(ctx, limiter);
|
|
21
|
+
let w;
|
|
22
|
+
try {
|
|
23
|
+
w = parseIngest(parseJson(await readBody(ctx.req)), ctx.app.config.redactKeys);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return fail(400, "The body is not valid JSON.");
|
|
27
|
+
}
|
|
28
|
+
if (!w)
|
|
29
|
+
fail(400, "sender.service is required.");
|
|
30
|
+
await save(ctx, projectId)(w);
|
|
31
|
+
ctx.res.writeHead(204).end();
|
|
32
|
+
}
|
|
33
|
+
/** `POST /v1/traces` and `/v1/logs`: OpenTelemetry apps. */
|
|
34
|
+
export async function ingestOtlp(ctx, signal, limiter) {
|
|
35
|
+
const projectId = await ingestProject(ctx, limiter);
|
|
36
|
+
await handleOtlp(signal, ctx.req, ctx.res, save(ctx, projectId), ctx.app.config.redactKeys);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/routes/ingest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAkC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEvH,gHAAgH;AAChH,KAAK,UAAU,aAAa,CAAC,GAAQ,EAAE,OAAoB;IACzD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,qDAAqD,CAAC,CAAC;IAC1G,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;IACtE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,+BAA+B,CAAC,CAAC;IACrF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,IAAI,CAAC,GAAG,EAAE,mDAAmD,CAAC,CAAC;IAC1F,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;AAC5B,CAAC;AAED,MAAM,IAAI,GAAG,CAAC,GAAQ,EAAE,SAAiB,EAAE,EAAE,CAAC,CAAC,CAAW,EAAE,EAAE,CAC5D,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAE1E,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAQ,EAAE,OAAoB;IAC9D,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACpD,IAAI,CAAkB,CAAC;IACvB,IAAI,CAAC;QACH,CAAC,GAAG,WAAW,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,GAAG,EAAE,6BAA6B,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,CAAC,CAAC;QAAE,IAAI,CAAC,GAAG,EAAE,6BAA6B,CAAC,CAAC;IACjD,MAAM,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC;IAC/B,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;AAC/B,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAQ,EAAE,MAAkB,EAAE,OAAoB;IACjF,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACpD,MAAM,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAC9F,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA2ChD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAE9C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { toSummary } from "@wizeworks/lens-core";
|
|
2
|
+
import { parseJson, readBody } from "@wizeworks/lens-hub/kit";
|
|
3
|
+
import { DEFAULT_N_PLUS_ONE, DEFAULT_SLOW_QUERY_MS, handleMcp } from "@wizeworks/lens-node/kit";
|
|
4
|
+
import { principalOf, requireProject } from "../auth/principal.js";
|
|
5
|
+
import { fail } from "../http/http.js";
|
|
6
|
+
import { audit } from "../repo/audit.js";
|
|
7
|
+
import { getTrace, queryTraces } from "../repo/trace-read.js";
|
|
8
|
+
import { projectMetrics } from "./metrics.js";
|
|
9
|
+
/** Traces the MCP tools search through, newest first. */
|
|
10
|
+
const MCP_RECENT = 500;
|
|
11
|
+
/** The project to read: `?project=`, or the only project of the read key. */
|
|
12
|
+
async function projectId(ctx) {
|
|
13
|
+
const id = ctx.url.searchParams.get("project");
|
|
14
|
+
if (id)
|
|
15
|
+
return id;
|
|
16
|
+
const p = await principalOf(ctx);
|
|
17
|
+
if (p?.kind === "key" && p.key.projectIds.length === 1)
|
|
18
|
+
return p.key.projectIds[0];
|
|
19
|
+
return fail(400, "Add ?project=<project id>. Read keys for one project do not need it.");
|
|
20
|
+
}
|
|
21
|
+
/** `POST /_wizelens/mcp`: the same read-only tools as the add-ons, for one project. Needs a read key: agents get only what it allows. */
|
|
22
|
+
async function mcp(ctx) {
|
|
23
|
+
if ((await principalOf(ctx))?.kind !== "key")
|
|
24
|
+
fail(401, "Send a read key: Authorization: Bearer wl_rk_...");
|
|
25
|
+
const access = await requireProject(ctx, await projectId(ctx));
|
|
26
|
+
const { project, actor } = access;
|
|
27
|
+
const db = ctx.app.db;
|
|
28
|
+
const recent = await queryTraces(db, { projectId: project.id, limit: MCP_RECENT });
|
|
29
|
+
const writes = [];
|
|
30
|
+
const data = {
|
|
31
|
+
service: project.name,
|
|
32
|
+
version: ctx.app.version,
|
|
33
|
+
slowQueryMs: DEFAULT_SLOW_QUERY_MS,
|
|
34
|
+
nPlusOneThreshold: DEFAULT_N_PLUS_ONE,
|
|
35
|
+
list: (limit) => recent.slice(0, limit).map(toSummary),
|
|
36
|
+
all: () => recent,
|
|
37
|
+
get: (traceId) => getTrace(db, project.id, traceId),
|
|
38
|
+
metrics: (range, params) => projectMetrics(db, project.id, range, params, { now: ctx.app.config.now() }),
|
|
39
|
+
// Every tool call is kept in the organization's audit log.
|
|
40
|
+
audit: (line) => void writes.push(audit(db, project.orgId, actor, "mcp.call", { projectId: project.id, line }, ctx.app.config.now())),
|
|
41
|
+
};
|
|
42
|
+
const body = ctx.req.method === "POST" ? parseJson(await readBody(ctx.req)) : undefined;
|
|
43
|
+
await handleMcp(data, ctx.req, ctx.res, body);
|
|
44
|
+
// The audit lines are written before this request is done; a failed write is logged, never lost silently.
|
|
45
|
+
for (const r of await Promise.allSettled(writes))
|
|
46
|
+
if (r.status === "rejected")
|
|
47
|
+
console.error("[wizelens-cloud] audit write failed:", r.reason);
|
|
48
|
+
}
|
|
49
|
+
export function mcpRoutes(r) {
|
|
50
|
+
r.on("POST", "mcp", mcp).on("GET", "mcp", mcp).on("DELETE", "mcp", mcp);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type MetricsModel, type TimeRange } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Ctx } from "../app.js";
|
|
3
|
+
import type { Queryable } from "../db/db.js";
|
|
4
|
+
/** Model parameters from the query: the metric, service, grouping, stat, and search. */
|
|
5
|
+
export declare function metricParamsOf(p: URLSearchParams): Record<string, string>;
|
|
6
|
+
/** The metrics page for one project, from the metric rollup tiers. */
|
|
7
|
+
export declare function projectMetrics(db: Queryable, projectId: string, range: TimeRange, params: Record<string, string>, o: {
|
|
8
|
+
now: number;
|
|
9
|
+
env?: string;
|
|
10
|
+
}): Promise<MetricsModel>;
|
|
11
|
+
/** The range from the query, checked against the organization's retention. */
|
|
12
|
+
export declare function metricRange(ctx: Ctx, retentionDays: number): TimeRange;
|
|
13
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/routes/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,YAAY,EAAkB,KAAK,SAAS,EAAe,MAAM,sBAAsB,CAAC;AACnH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOzE;AAED,sEAAsE;AACtE,wBAAsB,cAAc,CAClC,EAAE,EAAE,SAAS,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,CAAC,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,GAAG,SAAS,CAKtE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { isTimeRange, metricsModelOf, TIME_RANGES } from "@wizeworks/lens-core";
|
|
2
|
+
import { fail } from "../http/http.js";
|
|
3
|
+
import { loadMetricData } from "../repo/metric-read.js";
|
|
4
|
+
const DAY_MS = 86_400_000;
|
|
5
|
+
const PARAMS = ["metric", "service", "group", "stat", "q"];
|
|
6
|
+
/** Model parameters from the query: the metric, service, grouping, stat, and search. */
|
|
7
|
+
export function metricParamsOf(p) {
|
|
8
|
+
const out = {};
|
|
9
|
+
for (const k of PARAMS) {
|
|
10
|
+
const v = p.get(k);
|
|
11
|
+
if (v)
|
|
12
|
+
out[k] = v.slice(0, 200);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
/** The metrics page for one project, from the metric rollup tiers. */
|
|
17
|
+
export async function projectMetrics(db, projectId, range, params, o) {
|
|
18
|
+
return metricsModelOf(await loadMetricData(db, { projectId, env: o.env }, range, params, o.now));
|
|
19
|
+
}
|
|
20
|
+
/** The range from the query, checked against the organization's retention. */
|
|
21
|
+
export function metricRange(ctx, retentionDays) {
|
|
22
|
+
const range = ctx.url.searchParams.get("range") ?? "1h";
|
|
23
|
+
if (!isTimeRange(range))
|
|
24
|
+
return fail(400, `Unknown range "${range}".`);
|
|
25
|
+
if (TIME_RANGES[range].ms > retentionDays * DAY_MS)
|
|
26
|
+
fail(400, "This range is longer than the organization keeps data.");
|
|
27
|
+
return range;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/routes/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAqB,cAAc,EAAkB,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnH,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,MAAM,GAAG,UAAU,CAAC;AAC1B,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;AAE3D,wFAAwF;AACxF,MAAM,UAAU,cAAc,CAAC,CAAkB;IAC/C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,EAAa,EACb,SAAiB,EACjB,KAAgB,EAChB,MAA8B,EAC9B,CAAgC;IAEhC,OAAO,cAAc,CAAC,MAAM,cAAc,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,GAAQ,EAAE,aAAqB;IACzD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACxD,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAC,CAAC;IACvE,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,aAAa,GAAG,MAAM;QAAE,IAAI,CAAC,GAAG,EAAE,wDAAwD,CAAC,CAAC;IACxH,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/routes/models.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAmEhD,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAEhD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { alignNow, FACT_SLOW_QUERY_MS, isPageName, isTimeRange, pageContext, runPage, TIME_RANGES } from "@wizeworks/lens-core";
|
|
2
|
+
import { fail, sendJson } from "../http/http.js";
|
|
3
|
+
import { cloudExecutor, cloudSettling } from "../scale-exec.js";
|
|
4
|
+
import { projectOf } from "./data.js";
|
|
5
|
+
import { metricParamsOf, metricRange, projectMetrics } from "./metrics.js";
|
|
6
|
+
const CACHE_MS = 15_000;
|
|
7
|
+
const CACHE_MAX = 500;
|
|
8
|
+
const DAY_MS = 86_400_000;
|
|
9
|
+
const RESERVED = new Set(["project", "range", "env"]);
|
|
10
|
+
const cache = new Map();
|
|
11
|
+
function cached(key, now) {
|
|
12
|
+
const hit = cache.get(key);
|
|
13
|
+
return hit && now - hit.atMs < CACHE_MS ? hit.value : undefined;
|
|
14
|
+
}
|
|
15
|
+
function remember(key, value, now) {
|
|
16
|
+
if (cache.size >= CACHE_MAX)
|
|
17
|
+
cache.delete(cache.keys().next().value);
|
|
18
|
+
cache.set(key, { atMs: now, value });
|
|
19
|
+
}
|
|
20
|
+
/** Model parameters: every query value except the ones that pick the data. */
|
|
21
|
+
const paramsOf = (url) => Object.fromEntries([...url.searchParams].filter(([k]) => !RESERVED.has(k)).sort());
|
|
22
|
+
async function retentionDays(ctx, orgId) {
|
|
23
|
+
const [row] = await ctx.app.db.query("SELECT retention_days AS d FROM orgs WHERE id = $1", [orgId]);
|
|
24
|
+
return row?.d ?? ctx.app.config.retentionDays;
|
|
25
|
+
}
|
|
26
|
+
/** `GET api/models/metrics?project=&range=&env=&metric=&service=&group=&stat=&q=`: the metrics page, from the newest points. */
|
|
27
|
+
async function metrics(ctx) {
|
|
28
|
+
const { project } = await projectOf(ctx);
|
|
29
|
+
const range = metricRange(ctx, await retentionDays(ctx, project.orgId));
|
|
30
|
+
const now = alignNow(ctx.app.config.now());
|
|
31
|
+
const env = ctx.url.searchParams.get("env") || undefined;
|
|
32
|
+
const params = metricParamsOf(ctx.url.searchParams);
|
|
33
|
+
const key = JSON.stringify([project.id, "metrics", range, env, params, now]);
|
|
34
|
+
const hit = cached(key, ctx.app.config.now());
|
|
35
|
+
if (hit)
|
|
36
|
+
return sendJson(ctx.res, 200, hit);
|
|
37
|
+
const value = await projectMetrics(ctx.app.db, project.id, range, params, { now, env });
|
|
38
|
+
remember(key, value, ctx.app.config.now());
|
|
39
|
+
sendJson(ctx.res, 200, value);
|
|
40
|
+
}
|
|
41
|
+
/** `GET api/models/<page>?project=&range=&env=&...`: one page's numbers, from the project's stats tables. */
|
|
42
|
+
async function model(ctx) {
|
|
43
|
+
const page = ctx.params.page;
|
|
44
|
+
if (page === "metrics")
|
|
45
|
+
return metrics(ctx);
|
|
46
|
+
if (!isPageName(page))
|
|
47
|
+
return fail(404, `No model named "${page}".`);
|
|
48
|
+
const { project } = await projectOf(ctx);
|
|
49
|
+
const range = ctx.url.searchParams.get("range") ?? "24h";
|
|
50
|
+
if (!isTimeRange(range))
|
|
51
|
+
return fail(400, `Unknown range "${range}".`);
|
|
52
|
+
const days = await retentionDays(ctx, project.orgId);
|
|
53
|
+
if (TIME_RANGES[range].ms > days * DAY_MS)
|
|
54
|
+
fail(400, "This range is longer than the organization keeps data.");
|
|
55
|
+
const now = alignNow(ctx.app.config.now());
|
|
56
|
+
const env = ctx.url.searchParams.get("env") || undefined;
|
|
57
|
+
const params = paramsOf(ctx.url);
|
|
58
|
+
const key = JSON.stringify([project.id, page, range, env, params, now]);
|
|
59
|
+
const hit = cached(key, ctx.app.config.now());
|
|
60
|
+
if (hit)
|
|
61
|
+
return sendJson(ctx.res, 200, hit);
|
|
62
|
+
const db = ctx.app.db;
|
|
63
|
+
const pc = pageContext(range, now, params, { slowQueryMs: FACT_SLOW_QUERY_MS }, await cloudSettling(db, project.id, env));
|
|
64
|
+
const value = await runPage(page, pc, cloudExecutor(db, project.id, days * DAY_MS, env));
|
|
65
|
+
remember(key, value, ctx.app.config.now());
|
|
66
|
+
sendJson(ctx.res, 200, value);
|
|
67
|
+
}
|
|
68
|
+
export function modelRoutes(r) {
|
|
69
|
+
r.on("GET", "api/models/:page", model);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/routes/models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../src/routes/orgs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAIrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAqFhD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAQ9C"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { requireOrg, requireUser } from "../auth/principal.js";
|
|
2
|
+
import { canGrant, isRole } from "../auth/roles.js";
|
|
3
|
+
import { fail, readJsonBody, sendJson, str } from "../http/http.js";
|
|
4
|
+
import { audit, listAudit } from "../repo/audit.js";
|
|
5
|
+
import { createInvite, createOrg, listMembers, ownerCount, pendingInvites, removeMember, setRole, updateOrg } from "../repo/orgs.js";
|
|
6
|
+
import { createProject, listKeys, listProjects } from "../repo/projects.js";
|
|
7
|
+
const now = (ctx) => ctx.app.config.now();
|
|
8
|
+
async function overview(ctx) {
|
|
9
|
+
const { role } = await requireOrg(ctx, ctx.params.org, "read");
|
|
10
|
+
const db = ctx.app.db;
|
|
11
|
+
const id = ctx.params.org;
|
|
12
|
+
const manage = role === "owner" || role === "admin";
|
|
13
|
+
sendJson(ctx.res, 200, {
|
|
14
|
+
role,
|
|
15
|
+
members: await listMembers(db, id),
|
|
16
|
+
projects: await listProjects(db, id),
|
|
17
|
+
invites: manage ? await pendingInvites(db, id, now(ctx)) : [],
|
|
18
|
+
keys: manage ? await listKeys(db, id) : [],
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function newOrg(ctx) {
|
|
22
|
+
const user = await requireUser(ctx);
|
|
23
|
+
const body = await readJsonBody(ctx.req);
|
|
24
|
+
const org = await ctx.app.db.tx(async (q) => {
|
|
25
|
+
const o = await createOrg(q, str(body, "name"), user.id, now(ctx), ctx.app.config.retentionDays);
|
|
26
|
+
await createProject(q, o.id, "My app", now(ctx));
|
|
27
|
+
await audit(q, o.id, `user:${user.id}`, "org.create", { name: o.name }, now(ctx));
|
|
28
|
+
return o;
|
|
29
|
+
});
|
|
30
|
+
sendJson(ctx.res, 201, org);
|
|
31
|
+
}
|
|
32
|
+
async function changeOrg(ctx) {
|
|
33
|
+
const { user } = await requireOrg(ctx, ctx.params.org, "manage");
|
|
34
|
+
const body = await readJsonBody(ctx.req);
|
|
35
|
+
const days = body.retentionDays;
|
|
36
|
+
if (days !== undefined && (!Number.isInteger(days) || days < 1 || days > 400))
|
|
37
|
+
fail(400, "Retention is 1 to 400 days.");
|
|
38
|
+
const name = typeof body.name === "string" && body.name.trim() ? body.name : undefined;
|
|
39
|
+
await updateOrg(ctx.app.db, ctx.params.org, { name, retentionDays: days });
|
|
40
|
+
await audit(ctx.app.db, ctx.params.org, `user:${user.id}`, "org.update", { name, retentionDays: days }, now(ctx));
|
|
41
|
+
sendJson(ctx.res, 200, { ok: true });
|
|
42
|
+
}
|
|
43
|
+
async function invite(ctx) {
|
|
44
|
+
const { user, role } = await requireOrg(ctx, ctx.params.org, "manage");
|
|
45
|
+
const body = await readJsonBody(ctx.req);
|
|
46
|
+
const target = body.role ?? "member";
|
|
47
|
+
if (!isRole(target) || !canGrant(role, target))
|
|
48
|
+
return fail(400, "Pick a role at or below your own.");
|
|
49
|
+
const email = str(body, "email");
|
|
50
|
+
const token = await createInvite(ctx.app.db, ctx.params.org, email, target, user.id, now(ctx));
|
|
51
|
+
await audit(ctx.app.db, ctx.params.org, `user:${user.id}`, "member.invite", { email, role: target }, now(ctx));
|
|
52
|
+
sendJson(ctx.res, 201, { url: `${ctx.app.baseUrl(ctx.req)}/_wizelens/#/join/${token}`, expiresInDays: 7 });
|
|
53
|
+
}
|
|
54
|
+
async function applyMemberChange(ctx, actorRole, targetRole, lastOwner) {
|
|
55
|
+
const { org, userId } = ctx.params;
|
|
56
|
+
if (ctx.req.method === "DELETE") {
|
|
57
|
+
if (lastOwner)
|
|
58
|
+
fail(409, "An organization needs at least one owner.");
|
|
59
|
+
return removeMember(ctx.app.db, org, userId);
|
|
60
|
+
}
|
|
61
|
+
const next = (await readJsonBody(ctx.req)).role;
|
|
62
|
+
if (!isRole(next) || !canGrant(actorRole, next))
|
|
63
|
+
return fail(400, "Pick a role at or below your own.");
|
|
64
|
+
if (lastOwner && next !== "owner")
|
|
65
|
+
fail(409, "An organization needs at least one owner.");
|
|
66
|
+
if (targetRole !== next)
|
|
67
|
+
await setRole(ctx.app.db, org, userId, next);
|
|
68
|
+
}
|
|
69
|
+
async function changeMember(ctx) {
|
|
70
|
+
const { user, role } = await requireOrg(ctx, ctx.params.org, "manage");
|
|
71
|
+
const { org, userId } = ctx.params;
|
|
72
|
+
const target = (await listMembers(ctx.app.db, org)).find((m) => m.userId === userId);
|
|
73
|
+
if (!target)
|
|
74
|
+
return fail(404, "Member not found.");
|
|
75
|
+
if (!canGrant(role, target.role))
|
|
76
|
+
fail(403, "You cannot change someone above your role.");
|
|
77
|
+
const lastOwner = target.role === "owner" && (await ownerCount(ctx.app.db, org)) === 1;
|
|
78
|
+
await applyMemberChange(ctx, role, target.role, lastOwner);
|
|
79
|
+
await audit(ctx.app.db, org, `user:${user.id}`, ctx.req.method === "DELETE" ? "member.remove" : "member.role", { userId }, now(ctx));
|
|
80
|
+
sendJson(ctx.res, 200, { ok: true });
|
|
81
|
+
}
|
|
82
|
+
async function auditLog(ctx) {
|
|
83
|
+
await requireOrg(ctx, ctx.params.org, "manage");
|
|
84
|
+
const before = Number(ctx.url.searchParams.get("before")) || undefined;
|
|
85
|
+
sendJson(ctx.res, 200, await listAudit(ctx.app.db, ctx.params.org, 100, before));
|
|
86
|
+
}
|
|
87
|
+
export function orgRoutes(r) {
|
|
88
|
+
r.on("POST", "api/orgs", newOrg)
|
|
89
|
+
.on("GET", "api/orgs/:org", overview)
|
|
90
|
+
.on("PATCH", "api/orgs/:org", changeOrg)
|
|
91
|
+
.on("POST", "api/orgs/:org/invites", invite)
|
|
92
|
+
.on("PATCH", "api/orgs/:org/members/:userId", changeMember)
|
|
93
|
+
.on("DELETE", "api/orgs/:org/members/:userId", changeMember)
|
|
94
|
+
.on("GET", "api/orgs/:org/audit", auditLog);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=orgs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../src/routes/orgs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA+ChD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAMlD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { requireOrg } from "../auth/principal.js";
|
|
2
|
+
import { fail, readJsonBody, sendJson, str } from "../http/http.js";
|
|
3
|
+
import { audit } from "../repo/audit.js";
|
|
4
|
+
import { createKey, createProject, deleteProject, deleteProjectData, listProjects, projectInOrg, revokeKey } from "../repo/projects.js";
|
|
5
|
+
const now = (ctx) => ctx.app.config.now();
|
|
6
|
+
async function newProject(ctx) {
|
|
7
|
+
const { user } = await requireOrg(ctx, ctx.params.org, "manage");
|
|
8
|
+
const p = await createProject(ctx.app.db, ctx.params.org, str(await readJsonBody(ctx.req), "name"), now(ctx));
|
|
9
|
+
await audit(ctx.app.db, ctx.params.org, `user:${user.id}`, "project.create", { projectId: p.id, name: p.name }, now(ctx));
|
|
10
|
+
sendJson(ctx.res, 201, p);
|
|
11
|
+
}
|
|
12
|
+
/** DELETE removes the project. POST .../clear removes its data and keeps the project. */
|
|
13
|
+
async function dropProject(ctx) {
|
|
14
|
+
const { org, project } = ctx.params;
|
|
15
|
+
const { user } = await requireOrg(ctx, org, "manage");
|
|
16
|
+
if (!(await projectInOrg(ctx.app.db, org, project)))
|
|
17
|
+
fail(404, "Project not found.");
|
|
18
|
+
const clearOnly = ctx.req.method === "POST";
|
|
19
|
+
await ctx.app.db.tx((q) => (clearOnly ? deleteProjectData(q, project) : deleteProject(q, project)));
|
|
20
|
+
await audit(ctx.app.db, org, `user:${user.id}`, clearOnly ? "project.clear" : "project.delete", { projectId: project }, now(ctx));
|
|
21
|
+
sendJson(ctx.res, 200, { ok: true });
|
|
22
|
+
}
|
|
23
|
+
/** Ingest keys belong to one project. Read keys name the projects they may read. */
|
|
24
|
+
async function newKey(ctx) {
|
|
25
|
+
const org = ctx.params.org;
|
|
26
|
+
const { user } = await requireOrg(ctx, org, "manage");
|
|
27
|
+
const body = await readJsonBody(ctx.req);
|
|
28
|
+
const kind = body.kind;
|
|
29
|
+
if (kind !== "ingest" && kind !== "read")
|
|
30
|
+
fail(400, 'Set kind to "ingest" or "read".');
|
|
31
|
+
const known = new Set((await listProjects(ctx.app.db, org)).map((p) => p.id));
|
|
32
|
+
const ids = Array.isArray(body.projectIds) ? body.projectIds.filter((id) => typeof id === "string" && known.has(id)) : [];
|
|
33
|
+
if (ids.length === 0 || (kind === "ingest" && ids.length !== 1))
|
|
34
|
+
fail(400, "Pick one project for an ingest key, or one or more for a read key.");
|
|
35
|
+
const key = await createKey(ctx.app.db, org, kind, str(body, "name", false), ids, user.id, now(ctx));
|
|
36
|
+
await audit(ctx.app.db, org, `user:${user.id}`, "key.create", { keyId: key.id, kind, projectIds: ids }, now(ctx));
|
|
37
|
+
sendJson(ctx.res, 201, key);
|
|
38
|
+
}
|
|
39
|
+
async function dropKey(ctx) {
|
|
40
|
+
const { org, key } = ctx.params;
|
|
41
|
+
const { user } = await requireOrg(ctx, org, "manage");
|
|
42
|
+
if (!(await revokeKey(ctx.app.db, org, key, now(ctx))))
|
|
43
|
+
fail(404, "Key not found.");
|
|
44
|
+
await audit(ctx.app.db, org, `user:${user.id}`, "key.revoke", { keyId: key }, now(ctx));
|
|
45
|
+
sendJson(ctx.res, 200, { ok: true });
|
|
46
|
+
}
|
|
47
|
+
export function projectRoutes(r) {
|
|
48
|
+
r.on("POST", "api/orgs/:org/projects", newProject)
|
|
49
|
+
.on("DELETE", "api/orgs/:org/projects/:project", dropProject)
|
|
50
|
+
.on("POST", "api/orgs/:org/projects/:project/clear", dropProject)
|
|
51
|
+
.on("POST", "api/orgs/:org/keys", newKey)
|
|
52
|
+
.on("DELETE", "api/orgs/:org/keys/:key", dropKey);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Ctx } from "../app.js";
|
|
2
|
+
import type { Router } from "../http/router.js";
|
|
3
|
+
export interface Share {
|
|
4
|
+
id: string;
|
|
5
|
+
traceId: string;
|
|
6
|
+
createdMs: number;
|
|
7
|
+
expiresMs: number;
|
|
8
|
+
views: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function shareRoutes(r: Router<Ctx>): void;
|
|
11
|
+
//# sourceMappingURL=shares.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shares.d.ts","sourceRoot":"","sources":["../../src/routes/shares.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAMhD,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAwDD,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAKhD"}
|