@wizeworks/lens-cloud 0.0.0-stage → 0.3.0-alpha.2
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 +115 -2
- package/dist/alerts/evaluate.d.ts +10 -0
- package/dist/alerts/evaluate.d.ts.map +1 -0
- package/dist/alerts/evaluate.js +81 -0
- package/dist/alerts/evaluate.js.map +1 -0
- package/dist/alerts/notify.d.ts +28 -0
- package/dist/alerts/notify.d.ts.map +1 -0
- package/dist/alerts/notify.js +52 -0
- package/dist/alerts/notify.js.map +1 -0
- package/dist/app.d.ts +26 -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/auth.d.ts +51 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +63 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/bridge.d.ts +7 -0
- package/dist/auth/bridge.d.ts.map +1 -0
- package/dist/auth/bridge.js +39 -0
- package/dist/auth/bridge.js.map +1 -0
- package/dist/auth/crypto.d.ts +14 -0
- package/dist/auth/crypto.d.ts.map +1 -0
- package/dist/auth/crypto.js +16 -0
- package/dist/auth/crypto.js.map +1 -0
- package/dist/auth/delete-user.d.ts +16 -0
- package/dist/auth/delete-user.d.ts.map +1 -0
- package/dist/auth/delete-user.js +21 -0
- package/dist/auth/delete-user.js.map +1 -0
- package/dist/auth/deps.d.ts +27 -0
- package/dist/auth/deps.d.ts.map +1 -0
- package/dist/auth/deps.js +3 -0
- package/dist/auth/deps.js.map +1 -0
- package/dist/auth/email-token.d.ts +8 -0
- package/dist/auth/email-token.d.ts.map +1 -0
- package/dist/auth/email-token.js +15 -0
- package/dist/auth/email-token.js.map +1 -0
- package/dist/auth/hooks.d.ts +4 -0
- package/dist/auth/hooks.d.ts.map +1 -0
- package/dist/auth/hooks.js +143 -0
- package/dist/auth/hooks.js.map +1 -0
- package/dist/auth/options.d.ts +17 -0
- package/dist/auth/options.d.ts.map +1 -0
- package/dist/auth/options.js +98 -0
- package/dist/auth/options.js.map +1 -0
- package/dist/auth/password.d.ts +10 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +22 -0
- package/dist/auth/password.js.map +1 -0
- package/dist/auth/plugins.d.ts +4 -0
- package/dist/auth/plugins.d.ts.map +1 -0
- package/dist/auth/plugins.js +45 -0
- package/dist/auth/plugins.js.map +1 -0
- package/dist/auth/principal.d.ts +38 -0
- package/dist/auth/principal.d.ts.map +1 -0
- package/dist/auth/principal.js +69 -0
- package/dist/auth/principal.js.map +1 -0
- package/dist/auth/roles.d.ts +50 -0
- package/dist/auth/roles.d.ts.map +1 -0
- package/dist/auth/roles.js +18 -0
- package/dist/auth/roles.js.map +1 -0
- package/dist/auth/schema-names.d.ts +130 -0
- package/dist/auth/schema-names.d.ts.map +1 -0
- package/dist/auth/schema-names.js +53 -0
- package/dist/auth/schema-names.js.map +1 -0
- package/dist/auth/secret.d.ts +12 -0
- package/dist/auth/secret.d.ts.map +1 -0
- package/dist/auth/secret.js +32 -0
- package/dist/auth/secret.js.map +1 -0
- package/dist/auth/security-notices.d.ts +34 -0
- package/dist/auth/security-notices.d.ts.map +1 -0
- package/dist/auth/security-notices.js +81 -0
- package/dist/auth/security-notices.js.map +1 -0
- package/dist/auth/signup.d.ts +23 -0
- package/dist/auth/signup.d.ts.map +1 -0
- package/dist/auth/signup.js +61 -0
- package/dist/auth/signup.js.map +1 -0
- package/dist/auth/sso.d.ts +21 -0
- package/dist/auth/sso.d.ts.map +1 -0
- package/dist/auth/sso.js +35 -0
- package/dist/auth/sso.js.map +1 -0
- package/dist/auth/wire.d.ts +16 -0
- package/dist/auth/wire.d.ts.map +1 -0
- package/dist/auth/wire.js +47 -0
- package/dist/auth/wire.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 +86 -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 +44 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/db/db.d.ts +22 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +64 -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 +35 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema-auth.d.ts +2 -0
- package/dist/db/schema-auth.d.ts.map +1 -0
- package/dist/db/schema-auth.js +93 -0
- package/dist/db/schema-auth.js.map +1 -0
- package/dist/db/schema-mail.d.ts +2 -0
- package/dist/db/schema-mail.d.ts.map +1 -0
- package/dist/db/schema-mail.js +8 -0
- package/dist/db/schema-mail.js.map +1 -0
- package/dist/db/schema-notify.d.ts +2 -0
- package/dist/db/schema-notify.d.ts.map +1 -0
- package/dist/db/schema-notify.js +26 -0
- package/dist/db/schema-notify.js.map +1 -0
- package/dist/db/schema-sso.d.ts +2 -0
- package/dist/db/schema-sso.d.ts.map +1 -0
- package/dist/db/schema-sso.js +17 -0
- package/dist/db/schema-sso.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 +208 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/http/http.d.ts +19 -0
- package/dist/http/http.d.ts.map +1 -0
- package/dist/http/http.js +66 -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 +53 -0
- package/dist/jobs.js.map +1 -0
- package/dist/mail/blocks.d.ts +14 -0
- package/dist/mail/blocks.d.ts.map +1 -0
- package/dist/mail/blocks.js +35 -0
- package/dist/mail/blocks.js.map +1 -0
- package/dist/mail/content.d.ts +68 -0
- package/dist/mail/content.d.ts.map +1 -0
- package/dist/mail/content.js +6 -0
- package/dist/mail/content.js.map +1 -0
- package/dist/mail/emails-digest.d.ts +10 -0
- package/dist/mail/emails-digest.d.ts.map +1 -0
- package/dist/mail/emails-digest.js +55 -0
- package/dist/mail/emails-digest.js.map +1 -0
- package/dist/mail/emails-health.d.ts +6 -0
- package/dist/mail/emails-health.d.ts.map +1 -0
- package/dist/mail/emails-health.js +36 -0
- package/dist/mail/emails-health.js.map +1 -0
- package/dist/mail/emails-security.d.ts +14 -0
- package/dist/mail/emails-security.d.ts.map +1 -0
- package/dist/mail/emails-security.js +27 -0
- package/dist/mail/emails-security.js.map +1 -0
- package/dist/mail/emails-team.d.ts +11 -0
- package/dist/mail/emails-team.d.ts.map +1 -0
- package/dist/mail/emails-team.js +45 -0
- package/dist/mail/emails-team.js.map +1 -0
- package/dist/mail/emails.d.ts +9 -0
- package/dist/mail/emails.d.ts.map +1 -0
- package/dist/mail/emails.js +49 -0
- package/dist/mail/emails.js.map +1 -0
- package/dist/mail/layout.d.ts +6 -0
- package/dist/mail/layout.d.ts.map +1 -0
- package/dist/mail/layout.js +10 -0
- package/dist/mail/layout.js.map +1 -0
- package/dist/mail/mailer.d.ts +25 -0
- package/dist/mail/mailer.d.ts.map +1 -0
- package/dist/mail/mailer.js +49 -0
- package/dist/mail/mailer.js.map +1 -0
- package/dist/mail/preview.d.ts +6 -0
- package/dist/mail/preview.d.ts.map +1 -0
- package/dist/mail/preview.js +26 -0
- package/dist/mail/preview.js.map +1 -0
- package/dist/mail/resend.d.ts +11 -0
- package/dist/mail/resend.d.ts.map +1 -0
- package/dist/mail/resend.js +34 -0
- package/dist/mail/resend.js.map +1 -0
- package/dist/mail/samples.d.ts +3 -0
- package/dist/mail/samples.d.ts.map +1 -0
- package/dist/mail/samples.js +63 -0
- package/dist/mail/samples.js.map +1 -0
- package/dist/mail/stop-route.d.ts +4 -0
- package/dist/mail/stop-route.d.ts.map +1 -0
- package/dist/mail/stop-route.js +41 -0
- package/dist/mail/stop-route.js.map +1 -0
- package/dist/mail/svix.d.ts +8 -0
- package/dist/mail/svix.d.ts.map +1 -0
- package/dist/mail/svix.js +17 -0
- package/dist/mail/svix.js.map +1 -0
- package/dist/mail/templates.d.ts +12 -0
- package/dist/mail/templates.d.ts.map +1 -0
- package/dist/mail/templates.js +18 -0
- package/dist/mail/templates.js.map +1 -0
- package/dist/mail/text.d.ts +4 -0
- package/dist/mail/text.d.ts.map +1 -0
- package/dist/mail/text.js +17 -0
- package/dist/mail/text.js.map +1 -0
- package/dist/mail/theme.d.ts +26 -0
- package/dist/mail/theme.d.ts.map +1 -0
- package/dist/mail/theme.js +21 -0
- package/dist/mail/theme.js.map +1 -0
- package/dist/mail/unsubscribe.d.ts +14 -0
- package/dist/mail/unsubscribe.d.ts.map +1 -0
- package/dist/mail/unsubscribe.js +39 -0
- package/dist/mail/unsubscribe.js.map +1 -0
- package/dist/mail/webhook.d.ts +5 -0
- package/dist/mail/webhook.d.ts.map +1 -0
- package/dist/mail/webhook.js +41 -0
- package/dist/mail/webhook.js.map +1 -0
- package/dist/notify/digest-data.d.ts +31 -0
- package/dist/notify/digest-data.d.ts.map +1 -0
- package/dist/notify/digest-data.js +37 -0
- package/dist/notify/digest-data.js.map +1 -0
- package/dist/notify/digest.d.ts +17 -0
- package/dist/notify/digest.d.ts.map +1 -0
- package/dist/notify/digest.js +76 -0
- package/dist/notify/digest.js.map +1 -0
- package/dist/notify/health.d.ts +6 -0
- package/dist/notify/health.d.ts.map +1 -0
- package/dist/notify/health.js +43 -0
- package/dist/notify/health.js.map +1 -0
- package/dist/notify/team-describe.d.ts +22 -0
- package/dist/notify/team-describe.d.ts.map +1 -0
- package/dist/notify/team-describe.js +67 -0
- package/dist/notify/team-describe.js.map +1 -0
- package/dist/notify/team.d.ts +6 -0
- package/dist/notify/team.d.ts.map +1 -0
- package/dist/notify/team.js +56 -0
- package/dist/notify/team.js.map +1 -0
- package/dist/repo/alerts.d.ts +52 -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/auth-prune.d.ts +4 -0
- package/dist/repo/auth-prune.d.ts.map +1 -0
- package/dist/repo/auth-prune.js +8 -0
- package/dist/repo/auth-prune.js.map +1 -0
- package/dist/repo/mail-prefs.d.ts +35 -0
- package/dist/repo/mail-prefs.d.ts.map +1 -0
- package/dist/repo/mail-prefs.js +34 -0
- package/dist/repo/mail-prefs.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 +37 -0
- package/dist/repo/orgs.d.ts.map +1 -0
- package/dist/repo/orgs.js +33 -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/suppressions.d.ts +8 -0
- package/dist/repo/suppressions.d.ts.map +1 -0
- package/dist/repo/suppressions.js +16 -0
- package/dist/repo/suppressions.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 +13 -0
- package/dist/repo/users.d.ts.map +1 -0
- package/dist/repo/users.js +7 -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 +116 -0
- package/dist/routes/alerts.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 +25 -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/me.d.ts +4 -0
- package/dist/routes/me.d.ts.map +1 -0
- package/dist/routes/me.js +37 -0
- package/dist/routes/me.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/notify.d.ts +4 -0
- package/dist/routes/notify.d.ts.map +1 -0
- package/dist/routes/notify.js +55 -0
- package/dist/routes/notify.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 +73 -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 +55 -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 +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +126 -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 +76 -3
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AlertMetric, AlertOp, MetricAlertStat } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Queryable } from "../db/db.js";
|
|
3
|
+
/** Where a notice goes: a webhook, a Slack incoming webhook, or email to members of the organization. */
|
|
4
|
+
export type Channel = {
|
|
5
|
+
kind: "webhook" | "slack";
|
|
6
|
+
url: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "email";
|
|
9
|
+
to: string[];
|
|
10
|
+
};
|
|
11
|
+
export interface AlertRule {
|
|
12
|
+
id: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
name: string;
|
|
15
|
+
/** A trace metric, or "metric" for one OpenTelemetry metric named by `metricName`. */
|
|
16
|
+
metric: AlertMetric | "metric";
|
|
17
|
+
/** For "metric" rules: the metric, what to read from it, and optionally one service. Empty otherwise. */
|
|
18
|
+
metricName: string;
|
|
19
|
+
metricStat: MetricAlertStat | "";
|
|
20
|
+
metricService: string;
|
|
21
|
+
op: AlertOp;
|
|
22
|
+
threshold: number;
|
|
23
|
+
windowMs: number;
|
|
24
|
+
env: string;
|
|
25
|
+
channels: Channel[];
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
state: "ok" | "firing";
|
|
28
|
+
lastValue: number | null;
|
|
29
|
+
lastEvalMs: number | null;
|
|
30
|
+
}
|
|
31
|
+
export interface AlertEvent {
|
|
32
|
+
id: string;
|
|
33
|
+
ruleId: string;
|
|
34
|
+
ruleName: string;
|
|
35
|
+
kind: "fired" | "resolved" | "test";
|
|
36
|
+
value: number | null;
|
|
37
|
+
threshold: number;
|
|
38
|
+
message: string;
|
|
39
|
+
timeMs: number;
|
|
40
|
+
delivery: string;
|
|
41
|
+
}
|
|
42
|
+
export type RuleInput = Omit<AlertRule, "id" | "projectId" | "state" | "lastValue" | "lastEvalMs">;
|
|
43
|
+
export declare function listRules(q: Queryable, projectId: string): Promise<AlertRule[]>;
|
|
44
|
+
export declare function enabledRules(q: Queryable): Promise<AlertRule[]>;
|
|
45
|
+
export declare function ruleById(q: Queryable, projectId: string, id: string): Promise<AlertRule | undefined>;
|
|
46
|
+
export declare function createRule(q: Queryable, projectId: string, r: RuleInput, by: string, now: number): Promise<string>;
|
|
47
|
+
export declare function updateRule(q: Queryable, projectId: string, id: string, r: RuleInput): Promise<void>;
|
|
48
|
+
export declare function deleteRule(q: Queryable, projectId: string, id: string): Promise<boolean>;
|
|
49
|
+
export declare function saveEvaluation(q: Queryable, id: string, state: AlertRule["state"], value: number | null, now: number): Promise<void>;
|
|
50
|
+
export declare function addEvent(q: Queryable, rule: AlertRule, e: Omit<AlertEvent, "id" | "ruleId" | "ruleName" | "threshold">): Promise<void>;
|
|
51
|
+
export declare const listEvents: (q: Queryable, projectId: string, limit: number) => Promise<AlertEvent[]>;
|
|
52
|
+
//# 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":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAyC1C,MAAM,YAAY,GAAG;;0DAEqC,CAAC;AAG3D,MAAM,MAAM,GAAG,CAAC,CAAU,EAAa,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAc,EAAE,CAAC,CAAC;AAEpG,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,CAAY,EAAE,SAAiB;IAC7D,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAU,UAAU,YAAY,uDAAuD,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC1I,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,CAAY;IAC7C,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAU,UAAU,YAAY,iCAAiC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACvG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,CAAY,EAAE,SAAiB,EAAE,EAAU;IACxE,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAU,UAAU,YAAY,qDAAqD,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IACnI,OAAO,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAY,EAAE,SAAiB,EAAE,CAAY,EAAE,EAAU,EAAE,GAAW;IACrG,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,CAAC,CAAC,KAAK,CACX;;+EAE2E,EAC3E,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,aAAa,CAAC,CACrK,CAAC;IACF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAY,EAAE,SAAiB,EAAE,EAAU,EAAE,CAAY;IACxF,MAAM,CAAC,CAAC,KAAK,CACX;oJACgJ,EAChJ,CAAC,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,aAAa,CAAC,CAC5J,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAY,EAAE,SAAiB,EAAE,EAAU;IAC1E,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,wEAAwE,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/H,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,CAAY,EAAE,EAAU,EAAE,KAAyB,EAAE,KAAoB,EAAE,GAAW;IACzH,MAAM,CAAC,CAAC,KAAK,CAAC,qFAAqF,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAChI,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,CAAY,EAAE,IAAe,EAAE,CAA+D;IAC3H,MAAM,CAAC,CAAC,KAAK,CACX,oJAAoJ,EACpJ,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CACrG,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAY,EAAE,SAAiB,EAAE,KAAa,EAAyB,EAAE,CAClG,CAAC,CAAC,KAAK,CACL;yHACqH,EACrH,CAAC,SAAS,EAAE,KAAK,CAAC,CACnB,CAAC"}
|
|
@@ -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,4 @@
|
|
|
1
|
+
import type { Queryable } from "../db/db.js";
|
|
2
|
+
/** Remove expired sessions and links, and sign-in counters older than a day. Better Auth ignores them already; this keeps the tables small. */
|
|
3
|
+
export declare function pruneAuthRows(q: Queryable): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=auth-prune.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-prune.d.ts","sourceRoot":"","sources":["../../src/repo/auth-prune.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,+IAA+I;AAC/I,wBAAsB,aAAa,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAK/D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Remove expired sessions and links, and sign-in counters older than a day. Better Auth ignores them already; this keeps the tables small. */
|
|
2
|
+
export async function pruneAuthRows(q) {
|
|
3
|
+
await q.query("DELETE FROM sessions WHERE expires_at < now()");
|
|
4
|
+
await q.query("DELETE FROM verifications WHERE expires_at < now()");
|
|
5
|
+
await q.query("DELETE FROM rate_limits WHERE last_request < $1", [Date.now() - 86_400_000]);
|
|
6
|
+
await q.query("DELETE FROM invites WHERE status <> 'pending' AND created_at < now() - interval '90 days'");
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=auth-prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-prune.js","sourceRoot":"","sources":["../../src/repo/auth-prune.ts"],"names":[],"mappings":"AAEA,+IAA+I;AAC/I,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,CAAY;IAC9C,MAAM,CAAC,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAC/D,MAAM,CAAC,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACpE,MAAM,CAAC,CAAC,KAAK,CAAC,iDAAiD,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;IAC5F,MAAM,CAAC,CAAC,KAAK,CAAC,2FAA2F,CAAC,CAAC;AAC7G,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Queryable } from "../db/db.js";
|
|
2
|
+
export type DigestPeriod = "daily" | "weekly" | "monthly";
|
|
3
|
+
export declare const DIGEST_PERIODS: readonly DigestPeriod[];
|
|
4
|
+
/** A project with no saved choice gets the weekly digest only. */
|
|
5
|
+
export declare const DEFAULT_DIGEST: Record<DigestPeriod, boolean>;
|
|
6
|
+
export interface MailPrefs {
|
|
7
|
+
team: boolean;
|
|
8
|
+
health: boolean;
|
|
9
|
+
/** Off stops every digest, for projects made later too. */
|
|
10
|
+
digests: boolean;
|
|
11
|
+
timeZone: string;
|
|
12
|
+
/** Local hour digests go out, 0 to 23. */
|
|
13
|
+
digestHour: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const DEFAULT_PREFS: MailPrefs;
|
|
16
|
+
/** What a stop link turns off: team or health notices, one digest of one project, or every digest. */
|
|
17
|
+
export type StopScope = "team" | "health" | "digests" | {
|
|
18
|
+
projectId: string;
|
|
19
|
+
period: DigestPeriod;
|
|
20
|
+
};
|
|
21
|
+
export declare function prefsOf(q: Queryable, userId: string): Promise<MailPrefs>;
|
|
22
|
+
export declare function savePrefs(q: Queryable, userId: string, patch: Partial<MailPrefs>): Promise<MailPrefs>;
|
|
23
|
+
/** Saved digest choices by project. Projects not in the map use DEFAULT_DIGEST. */
|
|
24
|
+
export declare function digestPrefsOf(q: Queryable, userId: string): Promise<Map<string, Record<DigestPeriod, boolean>>>;
|
|
25
|
+
export declare function saveDigestPref(q: Queryable, userId: string, projectId: string, patch: Partial<Record<DigestPeriod, boolean>>): Promise<void>;
|
|
26
|
+
/** Turn off what a stop link names. */
|
|
27
|
+
export declare function applyStop(q: Queryable, userId: string, scope: StopScope): Promise<void>;
|
|
28
|
+
export interface Recipient {
|
|
29
|
+
id: string;
|
|
30
|
+
email: string;
|
|
31
|
+
name: string;
|
|
32
|
+
}
|
|
33
|
+
/** Owners and admins of an organization who keep this kind of notice on. With real mail, only confirmed addresses. */
|
|
34
|
+
export declare const managersOf: (q: Queryable, orgId: string, pref: "team" | "health", confirmedOnly: boolean) => Promise<Recipient[]>;
|
|
35
|
+
//# sourceMappingURL=mail-prefs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-prefs.d.ts","sourceRoot":"","sources":["../../src/repo/mail-prefs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAC1D,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAAmC,CAAC;AACtF,kEAAkE;AAClE,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAkD,CAAC;AAE5G,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,EAAE,SAAuF,CAAC;AAEpH,sGAAsG;AACtG,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AAIpG,wBAAsB,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAG9E;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAQ3G;AAED,mFAAmF;AACnF,wBAAsB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,CAGrH;AAED,wBAAsB,cAAc,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAOlJ;AAED,uCAAuC;AACvC,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7F;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sHAAsH;AACtH,eAAO,MAAM,UAAU,MAAa,SAAS,SAAS,MAAM,QAAQ,MAAM,GAAG,QAAQ,iBAAiB,OAAO,KAAG,OAAO,CAAC,SAAS,EAAE,CAKhI,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const DIGEST_PERIODS = ["daily", "weekly", "monthly"];
|
|
2
|
+
/** A project with no saved choice gets the weekly digest only. */
|
|
3
|
+
export const DEFAULT_DIGEST = { daily: false, weekly: true, monthly: false };
|
|
4
|
+
export const DEFAULT_PREFS = { team: true, health: true, digests: true, timeZone: "UTC", digestHour: 8 };
|
|
5
|
+
export async function prefsOf(q, userId) {
|
|
6
|
+
const [r] = await q.query("SELECT team, health, digests, time_zone, digest_hour FROM mail_prefs WHERE user_id = $1", [userId]);
|
|
7
|
+
return r ? { team: r.team, health: r.health, digests: r.digests, timeZone: r.time_zone, digestHour: r.digest_hour } : { ...DEFAULT_PREFS };
|
|
8
|
+
}
|
|
9
|
+
export async function savePrefs(q, userId, patch) {
|
|
10
|
+
const p = { ...(await prefsOf(q, userId)), ...patch };
|
|
11
|
+
await q.query(`INSERT INTO mail_prefs (user_id, team, health, digests, time_zone, digest_hour) VALUES ($1, $2, $3, $4, $5, $6)
|
|
12
|
+
ON CONFLICT (user_id) DO UPDATE SET team = $2, health = $3, digests = $4, time_zone = $5, digest_hour = $6`, [userId, p.team, p.health, p.digests, p.timeZone, p.digestHour]);
|
|
13
|
+
return p;
|
|
14
|
+
}
|
|
15
|
+
/** Saved digest choices by project. Projects not in the map use DEFAULT_DIGEST. */
|
|
16
|
+
export async function digestPrefsOf(q, userId) {
|
|
17
|
+
const rows = await q.query("SELECT project_id, daily, weekly, monthly FROM digest_prefs WHERE user_id = $1", [userId]);
|
|
18
|
+
return new Map(rows.map((r) => [r.project_id, { daily: r.daily, weekly: r.weekly, monthly: r.monthly }]));
|
|
19
|
+
}
|
|
20
|
+
export async function saveDigestPref(q, userId, projectId, patch) {
|
|
21
|
+
const p = { ...((await digestPrefsOf(q, userId)).get(projectId) ?? DEFAULT_DIGEST), ...patch };
|
|
22
|
+
await q.query(`INSERT INTO digest_prefs (user_id, project_id, daily, weekly, monthly) VALUES ($1, $2, $3, $4, $5)
|
|
23
|
+
ON CONFLICT (user_id, project_id) DO UPDATE SET daily = $3, weekly = $4, monthly = $5`, [userId, projectId, p.daily, p.weekly, p.monthly]);
|
|
24
|
+
}
|
|
25
|
+
/** Turn off what a stop link names. */
|
|
26
|
+
export async function applyStop(q, userId, scope) {
|
|
27
|
+
if (typeof scope === "object")
|
|
28
|
+
return saveDigestPref(q, userId, scope.projectId, { [scope.period]: false });
|
|
29
|
+
await savePrefs(q, userId, { [scope]: false });
|
|
30
|
+
}
|
|
31
|
+
/** Owners and admins of an organization who keep this kind of notice on. With real mail, only confirmed addresses. */
|
|
32
|
+
export const managersOf = async (q, orgId, pref, confirmedOnly) => q.query(`SELECT u.id, u.email, u.name FROM members m JOIN users u ON u.id = m.user_id LEFT JOIN mail_prefs p ON p.user_id = u.id
|
|
33
|
+
WHERE m.org_id = $1 AND m.role IN ('owner', 'admin') AND COALESCE(p.${pref}, TRUE) AND (u.email_verified OR NOT $2) ORDER BY u.email`, [orgId, confirmedOnly]);
|
|
34
|
+
//# sourceMappingURL=mail-prefs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-prefs.js","sourceRoot":"","sources":["../../src/repo/mail-prefs.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,37 @@
|
|
|
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
|
+
/** The membership id Better Auth uses to change a role. */
|
|
14
|
+
memberId: string;
|
|
15
|
+
userId: string;
|
|
16
|
+
email: string;
|
|
17
|
+
name: string;
|
|
18
|
+
role: Role;
|
|
19
|
+
sinceMs: number;
|
|
20
|
+
}
|
|
21
|
+
export interface PendingInvite {
|
|
22
|
+
id: string;
|
|
23
|
+
email: string;
|
|
24
|
+
role: Role;
|
|
25
|
+
expiresMs: number;
|
|
26
|
+
}
|
|
27
|
+
/** A slug no organization has yet, from the name. */
|
|
28
|
+
export declare function freeSlug(q: Queryable, name: string): Promise<string>;
|
|
29
|
+
export declare const orgsOfUser: (q: Queryable, userId: string) => Promise<Membership[]>;
|
|
30
|
+
export declare function membership(q: Queryable, userId: string, orgId: string): Promise<Membership | undefined>;
|
|
31
|
+
export declare function setRetention(q: Queryable, orgId: string, retentionDays: number): Promise<void>;
|
|
32
|
+
export declare const listMembers: (q: Queryable, orgId: string) => Promise<Member[]>;
|
|
33
|
+
/** Open invites. Their ids are the secret part of the link, so only managers see them. */
|
|
34
|
+
export declare const pendingInvites: (q: Queryable, orgId: string) => Promise<PendingInvite[]>;
|
|
35
|
+
/** Delete an organization: every project's data first, then the row (members, invites, projects, keys, alerts, and shares go with it). */
|
|
36
|
+
export declare function deleteOrgEverything(q: Queryable, orgId: string): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=orgs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../src/repo/orgs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAW,SAAQ,GAAG;IACrC,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,MAAM;IACrB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,qDAAqD;AACrD,wBAAsB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM1E;AAED,eAAO,MAAM,UAAU,MAAO,SAAS,UAAU,MAAM,KAAG,OAAO,CAAC,UAAU,EAAE,CACqE,CAAC;AAEpJ,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAG7G;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpG;AAED,eAAO,MAAM,WAAW,MAAO,SAAS,SAAS,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CAKvE,CAAC;AAEJ,0FAA0F;AAC1F,eAAO,MAAM,cAAc,MAAO,SAAS,SAAS,MAAM,KAAG,OAAO,CAAC,aAAa,EAAE,CAIjF,CAAC;AAEJ,0IAA0I;AAC1I,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { slugOf } from "../auth/crypto.js";
|
|
2
|
+
import { deleteProjectData } from "./projects.js";
|
|
3
|
+
const ORG_COLUMNS = `o.id, o.name, o.slug, o.retention_days AS "retentionDays"`;
|
|
4
|
+
const ms = (column) => `(extract(epoch FROM ${column}) * 1000)::double precision`;
|
|
5
|
+
/** A slug no organization has yet, from the name. */
|
|
6
|
+
export async function freeSlug(q, name) {
|
|
7
|
+
const base = slugOf(name);
|
|
8
|
+
const taken = new Set((await q.query("SELECT slug FROM orgs WHERE slug LIKE $1", [`${base}%`])).map((r) => r.slug));
|
|
9
|
+
let slug = base;
|
|
10
|
+
for (let i = 2; taken.has(slug); i++)
|
|
11
|
+
slug = `${base}-${i}`;
|
|
12
|
+
return slug;
|
|
13
|
+
}
|
|
14
|
+
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]);
|
|
15
|
+
export async function membership(q, userId, orgId) {
|
|
16
|
+
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`, [userId, orgId]);
|
|
17
|
+
return row;
|
|
18
|
+
}
|
|
19
|
+
export async function setRetention(q, orgId, retentionDays) {
|
|
20
|
+
await q.query("UPDATE orgs SET retention_days = $1 WHERE id = $2", [retentionDays, orgId]);
|
|
21
|
+
}
|
|
22
|
+
export const listMembers = (q, orgId) => q.query(`SELECT m.id AS "memberId", u.id AS "userId", u.email, u.name, m.role, ${ms("m.created_at")} AS "sinceMs" FROM members m JOIN users u ON u.id = m.user_id
|
|
23
|
+
WHERE m.org_id = $1 ORDER BY u.name`, [orgId]);
|
|
24
|
+
/** Open invites. Their ids are the secret part of the link, so only managers see them. */
|
|
25
|
+
export const pendingInvites = (q, orgId) => q.query(`SELECT id, email, role, ${ms("expires_at")} AS "expiresMs" FROM invites WHERE org_id = $1 AND status = 'pending' AND expires_at > now() ORDER BY created_at DESC`, [orgId]);
|
|
26
|
+
/** Delete an organization: every project's data first, then the row (members, invites, projects, keys, alerts, and shares go with it). */
|
|
27
|
+
export async function deleteOrgEverything(q, orgId) {
|
|
28
|
+
for (const { id } of await q.query("SELECT id FROM projects WHERE org_id = $1", [orgId]))
|
|
29
|
+
await deleteProjectData(q, id);
|
|
30
|
+
await q.query("DELETE FROM audit WHERE org_id = $1", [orgId]);
|
|
31
|
+
await q.query("DELETE FROM orgs WHERE id = $1", [orgId]);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=orgs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../src/repo/orgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAgClD,MAAM,WAAW,GAAG,2DAA2D,CAAC;AAChF,MAAM,EAAE,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,uBAAuB,MAAM,6BAA6B,CAAC;AAE1F,qDAAqD;AACrD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,CAAY,EAAE,IAAY;IACvD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAmB,0CAA0C,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACtI,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAY,EAAE,MAAc,EAAyB,EAAE,CAChF,CAAC,CAAC,KAAK,CAAa,UAAU,WAAW,6FAA6F,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AAEpJ,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAY,EAAE,MAAc,EAAE,KAAa;IAC1E,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAa,UAAU,WAAW,+FAA+F,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/K,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,CAAY,EAAE,KAAa,EAAE,aAAqB;IACnF,MAAM,CAAC,CAAC,KAAK,CAAC,mDAAmD,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAY,EAAE,KAAa,EAAqB,EAAE,CAC5E,CAAC,CAAC,KAAK,CACL,yEAAyE,EAAE,CAAC,cAAc,CAAC;yCACtD,EACrC,CAAC,KAAK,CAAC,CACR,CAAC;AAEJ,0FAA0F;AAC1F,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAY,EAAE,KAAa,EAA4B,EAAE,CACtF,CAAC,CAAC,KAAK,CACL,2BAA2B,EAAE,CAAC,YAAY,CAAC,uHAAuH,EAClK,CAAC,KAAK,CAAC,CACR,CAAC;AAEJ,0IAA0I;AAC1I,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,CAAY,EAAE,KAAa;IACnE,KAAK,MAAM,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,CAAC,KAAK,CAAiB,2CAA2C,EAAE,CAAC,KAAK,CAAC,CAAC;QAAE,MAAM,iBAAiB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzI,MAAM,CAAC,CAAC,KAAK,CAAC,qCAAqC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9D,MAAM,CAAC,CAAC,KAAK,CAAC,gCAAgC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -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,8 @@
|
|
|
1
|
+
import type { Queryable } from "../db/db.js";
|
|
2
|
+
export type SuppressReason = "bounce" | "complaint";
|
|
3
|
+
/** Skip this address from now on. A bounce wins over a complaint, since nothing can reach the address. */
|
|
4
|
+
export declare function suppress(q: Queryable, email: string, reason: SuppressReason, detail: string, now: number): Promise<void>;
|
|
5
|
+
export declare function suppressionOf(q: Queryable, email: string): Promise<SuppressReason | null>;
|
|
6
|
+
/** The person proved the address works (they opened a link sent to it), so mail may go there again. */
|
|
7
|
+
export declare function unsuppress(q: Queryable, email: string): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=suppressions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppressions.d.ts","sourceRoot":"","sources":["../../src/repo/suppressions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,WAAW,CAAC;AAIpD,0GAA0G;AAC1G,wBAAsB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9H;AAED,wBAAsB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAG/F;AAED,uGAAuG;AACvG,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const norm = (email) => email.trim().toLowerCase();
|
|
2
|
+
/** Skip this address from now on. A bounce wins over a complaint, since nothing can reach the address. */
|
|
3
|
+
export async function suppress(q, email, reason, detail, now) {
|
|
4
|
+
await q.query(`INSERT INTO mail_suppressions (email, reason, detail, created_ms) VALUES ($1, $2, $3, $4)
|
|
5
|
+
ON CONFLICT (email) DO UPDATE SET reason = CASE WHEN mail_suppressions.reason = 'bounce' THEN 'bounce' ELSE EXCLUDED.reason END,
|
|
6
|
+
detail = EXCLUDED.detail, created_ms = EXCLUDED.created_ms`, [norm(email), reason, detail.slice(0, 500), now]);
|
|
7
|
+
}
|
|
8
|
+
export async function suppressionOf(q, email) {
|
|
9
|
+
const rows = await q.query("SELECT reason FROM mail_suppressions WHERE email = $1", [norm(email)]);
|
|
10
|
+
return rows[0]?.reason ?? null;
|
|
11
|
+
}
|
|
12
|
+
/** The person proved the address works (they opened a link sent to it), so mail may go there again. */
|
|
13
|
+
export async function unsuppress(q, email) {
|
|
14
|
+
await q.query("DELETE FROM mail_suppressions WHERE email = $1", [norm(email)]);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=suppressions.js.map
|