@wizeworks/lens-cloud 0.3.0-alpha.1 → 0.3.0-alpha.3
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/README.md +58 -5
- package/dist/alerts/evaluate.d.ts.map +1 -1
- package/dist/alerts/evaluate.js +20 -6
- package/dist/alerts/evaluate.js.map +1 -1
- package/dist/alerts/notify.d.ts +13 -2
- package/dist/alerts/notify.d.ts.map +1 -1
- package/dist/alerts/notify.js +22 -16
- package/dist/alerts/notify.js.map +1 -1
- package/dist/app.d.ts +16 -2
- package/dist/app.d.ts.map +1 -1
- 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 +0 -5
- package/dist/auth/crypto.d.ts.map +1 -1
- package/dist/auth/crypto.js +3 -20
- package/dist/auth/crypto.js.map +1 -1
- 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/join-scope.d.ts +7 -0
- package/dist/auth/join-scope.d.ts.map +1 -0
- package/dist/auth/join-scope.js +17 -0
- package/dist/auth/join-scope.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 +8 -3
- package/dist/auth/principal.d.ts.map +1 -1
- package/dist/auth/principal.js +13 -8
- package/dist/auth/principal.js.map +1 -1
- package/dist/auth/roles.d.ts +43 -3
- package/dist/auth/roles.d.ts.map +1 -1
- package/dist/auth/roles.js +15 -6
- package/dist/auth/roles.js.map +1 -1
- 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/changes.d.ts +15 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +45 -0
- package/dist/changes.js.map +1 -0
- package/dist/cli-args.d.ts +1 -1
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +66 -14
- package/dist/cli-args.js.map +1 -1
- package/dist/cli.js +22 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +30 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +37 -0
- package/dist/config.js.map +1 -1
- package/dist/copy-cli.d.ts +3 -0
- package/dist/copy-cli.d.ts.map +1 -0
- package/dist/copy-cli.js +24 -0
- package/dist/copy-cli.js.map +1 -0
- package/dist/db/db.d.ts +7 -2
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +28 -10
- package/dist/db/db.js.map +1 -1
- package/dist/db/migrate.d.ts +2 -2
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +15 -5
- package/dist/db/migrate.js.map +1 -1
- 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-search.d.ts +2 -0
- package/dist/db/schema-search.d.ts.map +1 -0
- package/dist/db/schema-search.js +16 -0
- package/dist/db/schema-search.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.map +1 -1
- package/dist/db/schema.js +10 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/db/trigram.d.ts +4 -0
- package/dist/db/trigram.d.ts.map +1 -0
- package/dist/db/trigram.js +34 -0
- package/dist/db/trigram.js.map +1 -0
- package/dist/http/http.d.ts +2 -0
- package/dist/http/http.d.ts.map +1 -1
- package/dist/http/http.js +7 -3
- package/dist/http/http.js.map +1 -1
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.d.ts.map +1 -1
- package/dist/jobs.js +23 -11
- package/dist/jobs.js.map +1 -1
- 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 +50 -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 +13 -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 +32 -0
- package/dist/mail/theme.d.ts.map +1 -0
- package/dist/mail/theme.js +27 -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 +32 -0
- package/dist/notify/digest-data.d.ts.map +1 -0
- package/dist/notify/digest-data.js +31 -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 +42 -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 +6 -2
- package/dist/repo/alerts.d.ts.map +1 -1
- package/dist/repo/alerts.js.map +1 -1
- 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/orgs.d.ts +15 -25
- package/dist/repo/orgs.d.ts.map +1 -1
- package/dist/repo/orgs.js +18 -60
- package/dist/repo/orgs.js.map +1 -1
- package/dist/repo/services.d.ts +6 -0
- package/dist/repo/services.d.ts.map +1 -0
- package/dist/repo/services.js +16 -0
- package/dist/repo/services.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-join.d.ts +9 -0
- package/dist/repo/trace-join.d.ts.map +1 -0
- package/dist/repo/trace-join.js +35 -0
- package/dist/repo/trace-join.js.map +1 -0
- package/dist/repo/trace-read.d.ts +6 -13
- package/dist/repo/trace-read.d.ts.map +1 -1
- package/dist/repo/trace-read.js +8 -3
- package/dist/repo/trace-read.js.map +1 -1
- package/dist/repo/trace-write.d.ts +5 -1
- package/dist/repo/trace-write.d.ts.map +1 -1
- package/dist/repo/trace-write.js +11 -2
- package/dist/repo/trace-write.js.map +1 -1
- package/dist/repo/users.d.ts +5 -16
- package/dist/repo/users.d.ts.map +1 -1
- package/dist/repo/users.js +2 -60
- package/dist/repo/users.js.map +1 -1
- package/dist/routes/alerts.d.ts.map +1 -1
- package/dist/routes/alerts.js +23 -6
- package/dist/routes/alerts.js.map +1 -1
- package/dist/routes/changes.d.ts +4 -0
- package/dist/routes/changes.d.ts.map +1 -0
- package/dist/routes/changes.js +19 -0
- package/dist/routes/changes.js.map +1 -0
- package/dist/routes/data.d.ts.map +1 -1
- package/dist/routes/data.js +24 -9
- package/dist/routes/data.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +8 -2
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/ingest.d.ts.map +1 -1
- package/dist/routes/ingest.js +7 -2
- package/dist/routes/ingest.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +42 -25
- package/dist/routes/mcp.js.map +1 -1
- 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 +2 -2
- package/dist/routes/metrics.d.ts.map +1 -1
- package/dist/routes/metrics.js +2 -3
- package/dist/routes/metrics.js.map +1 -1
- package/dist/routes/models.d.ts +2 -0
- package/dist/routes/models.d.ts.map +1 -1
- package/dist/routes/models.js +10 -5
- package/dist/routes/models.js.map +1 -1
- 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.map +1 -1
- package/dist/routes/orgs.js +34 -53
- package/dist/routes/orgs.js.map +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +5 -3
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/search.d.ts +4 -0
- package/dist/routes/search.d.ts.map +1 -0
- package/dist/routes/search.js +33 -0
- package/dist/routes/search.js.map +1 -0
- package/dist/routes/shares.d.ts.map +1 -1
- package/dist/routes/shares.js +8 -5
- package/dist/routes/shares.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +59 -16
- package/dist/server.js.map +1 -1
- package/dist/settle.d.ts +7 -6
- package/dist/settle.d.ts.map +1 -1
- package/dist/settle.js +30 -13
- package/dist/settle.js.map +1 -1
- package/dist/telemetry/cached.d.ts +11 -0
- package/dist/telemetry/cached.d.ts.map +1 -0
- package/dist/telemetry/cached.js +30 -0
- package/dist/telemetry/cached.js.map +1 -0
- package/dist/telemetry/ch/admin.d.ts +10 -0
- package/dist/telemetry/ch/admin.d.ts.map +1 -0
- package/dist/telemetry/ch/admin.js +65 -0
- package/dist/telemetry/ch/admin.js.map +1 -0
- package/dist/telemetry/ch/http.d.ts +19 -0
- package/dist/telemetry/ch/http.d.ts.map +1 -0
- package/dist/telemetry/ch/http.js +91 -0
- package/dist/telemetry/ch/http.js.map +1 -0
- package/dist/telemetry/ch/metrics.d.ts +10 -0
- package/dist/telemetry/ch/metrics.d.ts.map +1 -0
- package/dist/telemetry/ch/metrics.js +61 -0
- package/dist/telemetry/ch/metrics.js.map +1 -0
- package/dist/telemetry/ch/queries.d.ts +19 -0
- package/dist/telemetry/ch/queries.d.ts.map +1 -0
- package/dist/telemetry/ch/queries.js +30 -0
- package/dist/telemetry/ch/queries.js.map +1 -0
- package/dist/telemetry/ch/queue.d.ts +34 -0
- package/dist/telemetry/ch/queue.d.ts.map +1 -0
- package/dist/telemetry/ch/queue.js +79 -0
- package/dist/telemetry/ch/queue.js.map +1 -0
- package/dist/telemetry/ch/read.d.ts +12 -0
- package/dist/telemetry/ch/read.d.ts.map +1 -0
- package/dist/telemetry/ch/read.js +72 -0
- package/dist/telemetry/ch/read.js.map +1 -0
- package/dist/telemetry/ch/schema-facts.d.ts +6 -0
- package/dist/telemetry/ch/schema-facts.d.ts.map +1 -0
- package/dist/telemetry/ch/schema-facts.js +49 -0
- package/dist/telemetry/ch/schema-facts.js.map +1 -0
- package/dist/telemetry/ch/schema-raw.d.ts +8 -0
- package/dist/telemetry/ch/schema-raw.d.ts.map +1 -0
- package/dist/telemetry/ch/schema-raw.js +35 -0
- package/dist/telemetry/ch/schema-raw.js.map +1 -0
- package/dist/telemetry/ch/schema-rollups.d.ts +2 -0
- package/dist/telemetry/ch/schema-rollups.d.ts.map +1 -0
- package/dist/telemetry/ch/schema-rollups.js +61 -0
- package/dist/telemetry/ch/schema-rollups.js.map +1 -0
- package/dist/telemetry/ch/schema.d.ts +12 -0
- package/dist/telemetry/ch/schema.d.ts.map +1 -0
- package/dist/telemetry/ch/schema.js +25 -0
- package/dist/telemetry/ch/schema.js.map +1 -0
- package/dist/telemetry/ch/settle-rows.d.ts +13 -0
- package/dist/telemetry/ch/settle-rows.d.ts.map +1 -0
- package/dist/telemetry/ch/settle-rows.js +67 -0
- package/dist/telemetry/ch/settle-rows.js.map +1 -0
- package/dist/telemetry/ch/settle.d.ts +7 -0
- package/dist/telemetry/ch/settle.d.ts.map +1 -0
- package/dist/telemetry/ch/settle.js +96 -0
- package/dist/telemetry/ch/settle.js.map +1 -0
- package/dist/telemetry/ch/store.d.ts +16 -0
- package/dist/telemetry/ch/store.d.ts.map +1 -0
- package/dist/telemetry/ch/store.js +65 -0
- package/dist/telemetry/ch/store.js.map +1 -0
- package/dist/telemetry/ch/util.d.ts +20 -0
- package/dist/telemetry/ch/util.d.ts.map +1 -0
- package/dist/telemetry/ch/util.js +45 -0
- package/dist/telemetry/ch/util.js.map +1 -0
- package/dist/telemetry/ch/write.d.ts +11 -0
- package/dist/telemetry/ch/write.d.ts.map +1 -0
- package/dist/telemetry/ch/write.js +59 -0
- package/dist/telemetry/ch/write.js.map +1 -0
- package/dist/telemetry/copy.d.ts +9 -0
- package/dist/telemetry/copy.d.ts.map +1 -0
- package/dist/telemetry/copy.js +60 -0
- package/dist/telemetry/copy.js.map +1 -0
- package/dist/telemetry/index.d.ts +11 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +21 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/pg-store.d.ts +5 -0
- package/dist/telemetry/pg-store.d.ts.map +1 -0
- package/dist/telemetry/pg-store.js +78 -0
- package/dist/telemetry/pg-store.js.map +1 -0
- package/dist/telemetry/store.d.ts +74 -0
- package/dist/telemetry/store.d.ts.map +1 -0
- package/dist/telemetry/store.js +2 -0
- package/dist/telemetry/store.js.map +1 -0
- package/package.json +20 -6
- package/dist/routes/auth.d.ts +0 -4
- package/dist/routes/auth.d.ts.map +0 -1
- package/dist/routes/auth.js +0 -106
- package/dist/routes/auth.js.map +0 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { fail, readRawBody } from "../http/http.js";
|
|
2
|
+
import { suppress } from "../repo/suppressions.js";
|
|
3
|
+
import { verifySvix } from "./svix.js";
|
|
4
|
+
export const RESEND_WEBHOOK_PATH = "api/mail/resend";
|
|
5
|
+
const header = (ctx, name) => {
|
|
6
|
+
const v = ctx.req.headers[name];
|
|
7
|
+
return Array.isArray(v) ? v[0] : v;
|
|
8
|
+
};
|
|
9
|
+
/** What to do with an event: only hard bounces and spam complaints change anything. */
|
|
10
|
+
function suppressionFor(e) {
|
|
11
|
+
if (e.type === "email.complained")
|
|
12
|
+
return { reason: "complaint", detail: "Marked as spam." };
|
|
13
|
+
if (e.type !== "email.bounced" || e.data?.bounce?.type !== "Permanent")
|
|
14
|
+
return null;
|
|
15
|
+
const b = e.data.bounce;
|
|
16
|
+
return { reason: "bounce", detail: [b.subType, b.message].filter((x) => typeof x === "string" && x).join(": ") || "Hard bounce." };
|
|
17
|
+
}
|
|
18
|
+
/** POST from Resend's webhook, signed with the endpoint's secret. Hard bounces and complaints go on the suppression list. */
|
|
19
|
+
export async function resendWebhook(ctx) {
|
|
20
|
+
const secret = ctx.app.config.resendWebhookSecret;
|
|
21
|
+
if (!secret)
|
|
22
|
+
fail(404, "Not found.");
|
|
23
|
+
const raw = await readRawBody(ctx.req);
|
|
24
|
+
const signed = { id: header(ctx, "svix-id"), timestamp: header(ctx, "svix-timestamp"), signature: header(ctx, "svix-signature") };
|
|
25
|
+
if (!verifySvix(secret, signed, raw, ctx.app.config.now()))
|
|
26
|
+
fail(401, "The webhook signature does not match.");
|
|
27
|
+
let event;
|
|
28
|
+
try {
|
|
29
|
+
event = JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return fail(400, "The body is not valid JSON.");
|
|
33
|
+
}
|
|
34
|
+
const action = suppressionFor(event);
|
|
35
|
+
const to = Array.isArray(event.data?.to) ? event.data.to.filter((x) => typeof x === "string") : [];
|
|
36
|
+
if (action)
|
|
37
|
+
for (const email of to)
|
|
38
|
+
await suppress(ctx.app.db, email, action.reason, action.detail, ctx.app.config.now());
|
|
39
|
+
ctx.res.writeHead(204).end();
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/mail/webhook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAuB,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAOrD,MAAM,MAAM,GAAG,CAAC,GAAQ,EAAE,IAAY,EAAE,EAAE;IACxC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF,uFAAuF;AACvF,SAAS,cAAc,CAAC,CAAc;IACpC,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB;QAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC7F,IAAI,CAAC,CAAC,IAAI,KAAK,eAAe,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACpF,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,cAAc,EAAE,CAAC;AACrI,CAAC;AAED,6HAA6H;AAC7H,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAQ;IAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,mBAAmB,CAAC;IAClD,IAAI,CAAC,MAAM;QAAE,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,CAAC;IAClI,IAAI,CAAC,UAAU,CAAC,MAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAAE,IAAI,CAAC,GAAG,EAAE,uCAAuC,CAAC,CAAC;IAChH,IAAI,KAAkB,CAAC;IACvB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,GAAG,EAAE,6BAA6B,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChH,IAAI,MAAM;QAAE,KAAK,MAAM,KAAK,IAAI,EAAE;YAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1H,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Headline, type TimeRange } from "@wizeworks/lens-core";
|
|
2
|
+
import type { Queryable } from "../db/db.js";
|
|
3
|
+
import type { DigestPeriod } from "../repo/mail-prefs.js";
|
|
4
|
+
import type { TelemetryStore } from "../telemetry/store.js";
|
|
5
|
+
export declare const RANGE_OF: Record<DigestPeriod, TimeRange>;
|
|
6
|
+
/** One project's numbers for one digest: the same models the dashboard pages use, plus the slowest endpoints and alerts. */
|
|
7
|
+
export interface DigestData {
|
|
8
|
+
empty: boolean;
|
|
9
|
+
requests: Headline;
|
|
10
|
+
errorRate: Headline;
|
|
11
|
+
avgMs: Headline;
|
|
12
|
+
sqlQueries: Headline;
|
|
13
|
+
p95Ms: number | null;
|
|
14
|
+
errors: {
|
|
15
|
+
type: string;
|
|
16
|
+
message: string;
|
|
17
|
+
count: number;
|
|
18
|
+
}[];
|
|
19
|
+
slowest: {
|
|
20
|
+
endpoint: string;
|
|
21
|
+
p95Ms: number;
|
|
22
|
+
count: number;
|
|
23
|
+
}[];
|
|
24
|
+
insights: {
|
|
25
|
+
title: string;
|
|
26
|
+
occurrences: number;
|
|
27
|
+
}[];
|
|
28
|
+
alertsFired: number;
|
|
29
|
+
}
|
|
30
|
+
/** The digest's numbers, or null when the organization keeps data for less time than the digest covers. */
|
|
31
|
+
export declare function digestData(q: Queryable, t: TelemetryStore, projectId: string, retentionDays: number, period: DigestPeriod, now: number): Promise<DigestData | null>;
|
|
32
|
+
//# sourceMappingURL=digest-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest-data.d.ts","sourceRoot":"","sources":["../../src/notify/digest-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,QAAQ,EAAqC,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtI,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,YAAY,EAAE,SAAS,CAAkD,CAAC;AAExG,4HAA4H;AAC5H,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,QAAQ,CAAC;IACpB,KAAK,EAAE,QAAQ,CAAC;IAChB,UAAU,EAAE,QAAQ,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3D,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACnD,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD,2GAA2G;AAC3G,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAoBzK"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { alignNow, FACT_SLOW_QUERY_MS, pageContext, runPage, TIME_RANGES } from "@wizeworks/lens-core";
|
|
2
|
+
const DAY_MS = 86_400_000;
|
|
3
|
+
export const RANGE_OF = { daily: "24h", weekly: "7d", monthly: "30d" };
|
|
4
|
+
async function alertsFired(q, projectId, from, to) {
|
|
5
|
+
const [row] = await q.query("SELECT count(*)::int AS n FROM alert_events WHERE project_id = $1 AND kind = 'fired' AND time_ms >= $2 AND time_ms < $3", [projectId, from, to]);
|
|
6
|
+
return row?.n ?? 0;
|
|
7
|
+
}
|
|
8
|
+
/** The digest's numbers, or null when the organization keeps data for less time than the digest covers. */
|
|
9
|
+
export async function digestData(q, t, projectId, retentionDays, period, now) {
|
|
10
|
+
const range = RANGE_OF[period];
|
|
11
|
+
if (TIME_RANGES[range].ms > retentionDays * DAY_MS)
|
|
12
|
+
return null;
|
|
13
|
+
const pc = pageContext(range, alignNow(now), {}, { slowQueryMs: FACT_SLOW_QUERY_MS }, await t.settling(projectId));
|
|
14
|
+
const ex = t.executor(projectId, retentionDays * DAY_MS);
|
|
15
|
+
const [overview, errors] = await Promise.all([runPage("overview", pc, ex), runPage("errors", pc, ex)]);
|
|
16
|
+
const { from, to } = pc.window;
|
|
17
|
+
const h = overview.headlines;
|
|
18
|
+
return {
|
|
19
|
+
empty: !h.total.value && !h.total.previous,
|
|
20
|
+
requests: h.total,
|
|
21
|
+
errorRate: h.errorRate,
|
|
22
|
+
avgMs: h.avgDuration,
|
|
23
|
+
sqlQueries: h.sqlQueries,
|
|
24
|
+
p95Ms: overview.timing.find((t) => t.key === "request")?.p95 ?? null,
|
|
25
|
+
errors: errors.groups.slice(0, 5).map((g) => ({ type: g.type, message: g.message, count: g.count })),
|
|
26
|
+
slowest: await t.slowestEndpoints(projectId, { from, to }, 5, 5),
|
|
27
|
+
insights: overview.insights.map((i) => ({ title: i.title, occurrences: i.occurrences })),
|
|
28
|
+
alertsFired: await alertsFired(q, projectId, from, to),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=digest-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest-data.js","sourceRoot":"","sources":["../../src/notify/digest-data.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CloudApp } from "../app.js";
|
|
2
|
+
import { type DigestPeriod } from "../repo/mail-prefs.js";
|
|
3
|
+
interface Local {
|
|
4
|
+
date: string;
|
|
5
|
+
month: string;
|
|
6
|
+
weekday: number;
|
|
7
|
+
day: number;
|
|
8
|
+
hour: number;
|
|
9
|
+
}
|
|
10
|
+
/** The date and hour in a time zone. Unknown zones count as UTC. */
|
|
11
|
+
export declare function localOf(ms: number, timeZone: string): Local;
|
|
12
|
+
/** Digests due at this local time, each with the key that makes it go out once: every day, on Mondays, on the 1st. */
|
|
13
|
+
export declare function dueKeys(l: Local, hour: number): [DigestPeriod, string][];
|
|
14
|
+
/** Send the digests that are due now. People with digests off, and unconfirmed addresses when mail is real, get none. */
|
|
15
|
+
export declare function sendDigests(app: CloudApp): Promise<void>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=digest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/notify/digest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAI1C,OAAO,EAAkB,KAAK,YAAY,EAAiB,MAAM,uBAAuB,CAAC;AAGzF,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oEAAoE;AACpE,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAY3D;AAED,sHAAsH;AACtH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,CAMxE;AAwDD,yHAAyH;AACzH,wBAAsB,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ9D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { digest } from "../mail/emails-digest.js";
|
|
2
|
+
import { renderMail } from "../mail/templates.js";
|
|
3
|
+
import { stopUrl } from "../mail/unsubscribe.js";
|
|
4
|
+
import { DEFAULT_DIGEST, digestPrefsOf } from "../repo/mail-prefs.js";
|
|
5
|
+
import { digestData } from "./digest-data.js";
|
|
6
|
+
/** The date and hour in a time zone. Unknown zones count as UTC. */
|
|
7
|
+
export function localOf(ms, timeZone) {
|
|
8
|
+
const fmt = (tz) => new Intl.DateTimeFormat("en-CA", { timeZone: tz, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", hourCycle: "h23", weekday: "short" }).formatToParts(new Date(ms));
|
|
9
|
+
let parts;
|
|
10
|
+
try {
|
|
11
|
+
parts = fmt(timeZone);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
parts = fmt("UTC");
|
|
15
|
+
}
|
|
16
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
17
|
+
const weekday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].indexOf(get("weekday")) + 1;
|
|
18
|
+
return { date: `${get("year")}-${get("month")}-${get("day")}`, month: `${get("year")}-${get("month")}`, weekday, day: Number(get("day")), hour: Number(get("hour")) };
|
|
19
|
+
}
|
|
20
|
+
/** Digests due at this local time, each with the key that makes it go out once: every day, on Mondays, on the 1st. */
|
|
21
|
+
export function dueKeys(l, hour) {
|
|
22
|
+
if (l.hour < hour)
|
|
23
|
+
return [];
|
|
24
|
+
const due = [["daily", `D:${l.date}`]];
|
|
25
|
+
if (l.weekday === 1)
|
|
26
|
+
due.push(["weekly", `W:${l.date}`]);
|
|
27
|
+
if (l.day === 1)
|
|
28
|
+
due.push(["monthly", `M:${l.month}`]);
|
|
29
|
+
return due;
|
|
30
|
+
}
|
|
31
|
+
/** Claim one send. Only the instance whose row went in sends, so a digest goes out once. */
|
|
32
|
+
const claim = async (app, userId, projectId, period, key) => (await app.db.query("INSERT INTO digest_sends (user_id, project_id, period, period_key, sent_ms) VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING RETURNING period", [
|
|
33
|
+
userId,
|
|
34
|
+
projectId,
|
|
35
|
+
period,
|
|
36
|
+
key,
|
|
37
|
+
app.config.now(),
|
|
38
|
+
])).length > 0;
|
|
39
|
+
async function sendOne(app, who, p, period, key, cache) {
|
|
40
|
+
if (!(await claim(app, who.id, p.id, period, key)))
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
const id = `${p.id}:${period}`;
|
|
44
|
+
if (!cache.has(id))
|
|
45
|
+
cache.set(id, digestData(app.db, app.telemetry, p.id, p.retention_days, period, app.config.now()));
|
|
46
|
+
const data = await cache.get(id);
|
|
47
|
+
if (!data || data.empty)
|
|
48
|
+
return;
|
|
49
|
+
const links = { project: `${app.baseUrl()}/_wizelens/#/?project=${encodeURIComponent(p.id)}`, stop: stopUrl(app, who.id, { projectId: p.id, period }) };
|
|
50
|
+
app.mailer.send(renderMail(who.email, digest(p.name, p.org, period, data, links)));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
await app.db.query("DELETE FROM digest_sends WHERE user_id = $1 AND project_id = $2 AND period = $3 AND period_key = $4", [who.id, p.id, period, key]);
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function digestsFor(app, who, cache) {
|
|
58
|
+
const due = dueKeys(localOf(app.config.now(), who.time_zone ?? "UTC"), who.digest_hour ?? 8);
|
|
59
|
+
if (due.length === 0)
|
|
60
|
+
return;
|
|
61
|
+
const projects = await app.db.query("SELECT p.id, p.name, o.name AS org, o.retention_days FROM members m JOIN orgs o ON o.id = m.org_id JOIN projects p ON p.org_id = o.id WHERE m.user_id = $1 ORDER BY p.name", [who.id]);
|
|
62
|
+
const prefs = await digestPrefsOf(app.db, who.id);
|
|
63
|
+
for (const p of projects)
|
|
64
|
+
for (const [period, key] of due)
|
|
65
|
+
if ((prefs.get(p.id) ?? DEFAULT_DIGEST)[period])
|
|
66
|
+
await sendOne(app, who, p, period, key, cache);
|
|
67
|
+
}
|
|
68
|
+
/** Send the digests that are due now. People with digests off, and unconfirmed addresses when mail is real, get none. */
|
|
69
|
+
export async function sendDigests(app) {
|
|
70
|
+
const people = await app.db.query(`SELECT u.id, u.email, p.time_zone, p.digest_hour FROM users u LEFT JOIN mail_prefs p ON p.user_id = u.id
|
|
71
|
+
WHERE COALESCE(p.digests, TRUE) AND (u.email_verified OR NOT $1)`, [app.mailer.live]);
|
|
72
|
+
const cache = new Map();
|
|
73
|
+
for (const who of people)
|
|
74
|
+
await digestsFor(app, who, cache).catch((err) => console.error(`[wizelens-cloud] digest for ${who.id} failed:`, err));
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=digest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.js","sourceRoot":"","sources":["../../src/notify/digest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CloudApp } from "../app.js";
|
|
2
|
+
/** Silence this long counts as quiet, for a project that sends around the clock. */
|
|
3
|
+
export declare const QUIET_MS: number;
|
|
4
|
+
/** Tell owners and admins when a busy project stops sending data, and when it starts again. */
|
|
5
|
+
export declare function checkHealth(app: CloudApp): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/notify/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAQ1C,oFAAoF;AACpF,eAAO,MAAM,QAAQ,QAAW,CAAC;AAsCjC,+FAA+F;AAC/F,wBAAsB,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { projectBack, projectQuiet } from "../mail/emails-health.js";
|
|
2
|
+
import { renderMail } from "../mail/templates.js";
|
|
3
|
+
import { stopUrl } from "../mail/unsubscribe.js";
|
|
4
|
+
import { managersOf } from "../repo/mail-prefs.js";
|
|
5
|
+
const HOUR = 3_600_000;
|
|
6
|
+
/** Silence this long counts as quiet, for a project that sends around the clock. */
|
|
7
|
+
export const QUIET_MS = 2 * HOUR;
|
|
8
|
+
/** "Around the clock": data in at least 58 of the 72 hours before the last data. */
|
|
9
|
+
const BUSY_HOURS = 58;
|
|
10
|
+
async function busy(app, projectId, lastMs) {
|
|
11
|
+
return (await app.telemetry.activeHours(projectId, lastMs - 72 * HOUR, lastMs)) >= BUSY_HOURS;
|
|
12
|
+
}
|
|
13
|
+
async function tell(app, p, make) {
|
|
14
|
+
for (const m of await managersOf(app.db, p.org_id, "health", app.mailer.live))
|
|
15
|
+
app.mailer.send(renderMail(m.email, make(stopUrl(app, m.id, "health"))));
|
|
16
|
+
}
|
|
17
|
+
/** Check one project. The first look only records its state, so an upgrade sends nothing for projects already quiet. */
|
|
18
|
+
async function checkProject(app, p, now) {
|
|
19
|
+
if (p.last_ms === null)
|
|
20
|
+
return;
|
|
21
|
+
const quiet = now - p.last_ms > QUIET_MS && (await busy(app, p.id, p.last_ms));
|
|
22
|
+
if (p.quiet === quiet)
|
|
23
|
+
return;
|
|
24
|
+
const saved = await app.db.query(`INSERT INTO project_health (project_id, quiet, changed_ms) VALUES ($1, $2, $3)
|
|
25
|
+
ON CONFLICT (project_id) DO UPDATE SET quiet = $2, changed_ms = $3 WHERE project_health.quiet <> $2 RETURNING project_id`, [p.id, quiet, now]);
|
|
26
|
+
if (p.quiet === null || saved.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
const url = `${app.baseUrl()}/_wizelens/#/?project=${encodeURIComponent(p.id)}`;
|
|
29
|
+
if (quiet)
|
|
30
|
+
await tell(app, p, (stop) => projectQuiet(p.name, p.org, p.last_ms, now, url, stop));
|
|
31
|
+
else
|
|
32
|
+
await tell(app, p, (stop) => projectBack(p.name, p.org, now - (p.changed_ms ?? now) + QUIET_MS, url, stop));
|
|
33
|
+
}
|
|
34
|
+
/** Tell owners and admins when a busy project stops sending data, and when it starts again. */
|
|
35
|
+
export async function checkHealth(app) {
|
|
36
|
+
const now = app.config.now();
|
|
37
|
+
const projects = await app.db.query("SELECT p.id, p.org_id, p.name, o.name AS org, h.quiet, h.changed_ms FROM projects p JOIN orgs o ON o.id = p.org_id LEFT JOIN project_health h ON h.project_id = p.id");
|
|
38
|
+
const last = await app.telemetry.lastTraceMs();
|
|
39
|
+
for (const p of projects)
|
|
40
|
+
await checkProject(app, { ...p, last_ms: last.get(p.id) ?? null }, now);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/notify/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Queryable } from "../db/db.js";
|
|
2
|
+
/** Audit actions that become team notices. MCP calls and the first organization are not news. */
|
|
3
|
+
export declare const NOTICED: Set<string>;
|
|
4
|
+
export interface AuditRow {
|
|
5
|
+
id: string;
|
|
6
|
+
org_id: string;
|
|
7
|
+
actor: string;
|
|
8
|
+
action: string;
|
|
9
|
+
detail: string;
|
|
10
|
+
time_ms: number;
|
|
11
|
+
}
|
|
12
|
+
/** Names by id, each looked up once per run. */
|
|
13
|
+
export declare function namesOf(q: Queryable): {
|
|
14
|
+
user: (id: string) => Promise<string>;
|
|
15
|
+
member: (id: string) => Promise<string>;
|
|
16
|
+
project: (id: string) => Promise<string>;
|
|
17
|
+
key: (id: string) => Promise<string>;
|
|
18
|
+
};
|
|
19
|
+
export type Names = ReturnType<typeof namesOf>;
|
|
20
|
+
/** One sentence per change, naming who did it. */
|
|
21
|
+
export declare function describe(row: AuditRow, names: Names): Promise<string>;
|
|
22
|
+
//# sourceMappingURL=team-describe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-describe.d.ts","sourceRoot":"","sources":["../../src/notify/team-describe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,iGAAiG;AACjG,eAAO,MAAM,OAAO,aAmBlB,CAAC;AAEH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,gDAAgD;AAChD,wBAAgB,OAAO,CAAC,CAAC,EAAE,SAAS;IAKhC,IAAI,OAAO,MAAM;IACjB,MAAM,OAAO,MAAM;IACnB,OAAO,OAAO,MAAM;IACpB,GAAG,OAAO,MAAM;EAEnB;AAED,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAS/C,kDAAkD;AAClD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B3E"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Audit actions that become team notices. MCP calls and the first organization are not news. */
|
|
2
|
+
export const NOTICED = new Set([
|
|
3
|
+
"member.invite",
|
|
4
|
+
"invite.cancel",
|
|
5
|
+
"member.join",
|
|
6
|
+
"member.leave",
|
|
7
|
+
"member.remove",
|
|
8
|
+
"member.role",
|
|
9
|
+
"key.create",
|
|
10
|
+
"key.revoke",
|
|
11
|
+
"sso.create",
|
|
12
|
+
"sso.update",
|
|
13
|
+
"sso.delete",
|
|
14
|
+
"sso.verify",
|
|
15
|
+
"project.create",
|
|
16
|
+
"project.delete",
|
|
17
|
+
"project.clear",
|
|
18
|
+
"share.create",
|
|
19
|
+
"share.revoke",
|
|
20
|
+
"org.update",
|
|
21
|
+
]);
|
|
22
|
+
/** Names by id, each looked up once per run. */
|
|
23
|
+
export function namesOf(q) {
|
|
24
|
+
const cache = new Map();
|
|
25
|
+
const once = (key, load) => cache.get(key) ?? (cache.set(key, load()), cache.get(key));
|
|
26
|
+
const one = async (sql, id, fallback) => (await q.query(sql, [id]))[0]?.v ?? fallback;
|
|
27
|
+
return {
|
|
28
|
+
user: (id) => once(`u:${id}`, () => one("SELECT name || ' (' || email || ')' AS v FROM users WHERE id = $1", id, "Someone")),
|
|
29
|
+
member: (id) => once(`m:${id}`, () => one("SELECT u.email AS v FROM members m JOIN users u ON u.id = m.user_id WHERE m.id = $1", id, "a member")),
|
|
30
|
+
project: (id) => once(`p:${id}`, () => one("SELECT name AS v FROM projects WHERE id = $1", id, "a project")),
|
|
31
|
+
key: (id) => once(`k:${id}`, () => one("SELECT name AS v FROM api_keys WHERE id = $1", id, "")),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const s = (v) => (typeof v === "string" ? v : "");
|
|
35
|
+
async function projectsText(names, ids) {
|
|
36
|
+
const list = Array.isArray(ids) ? await Promise.all(ids.filter((x) => typeof x === "string").map(names.project)) : [];
|
|
37
|
+
return list.length ? list.join(", ") : "no project";
|
|
38
|
+
}
|
|
39
|
+
/** One sentence per change, naming who did it. */
|
|
40
|
+
export async function describe(row, names) {
|
|
41
|
+
const d = JSON.parse(row.detail);
|
|
42
|
+
const who = row.actor.startsWith("user:") ? await names.user(row.actor.slice(5)) : "WizeLens";
|
|
43
|
+
const sso = `${s(d.type).toUpperCase() || "single sign-on"} for ${s(d.domain) || s(d.providerId)}`;
|
|
44
|
+
const project = async () => s(d.name) || (await names.project(s(d.projectId)));
|
|
45
|
+
const say = {
|
|
46
|
+
"member.invite": () => `${who} invited ${s(d.email)} as ${s(d.role) || "member"}.`,
|
|
47
|
+
"invite.cancel": () => `${who} canceled an invite.`,
|
|
48
|
+
"member.join": () => `${who} joined${d.via === "sso" ? " through single sign-on" : ""}.`,
|
|
49
|
+
"member.leave": () => `${who} left.`,
|
|
50
|
+
"member.remove": () => `${who} removed ${s(d.memberEmail) || s(d.memberIdOrEmail) || "a member"}.`,
|
|
51
|
+
"member.role": async () => `${who} made ${await names.member(s(d.memberId))} ${s(d.role)}.`,
|
|
52
|
+
"key.create": async () => `${who} made ${d.kind === "read" ? "a read key" : "an ingest key"} for ${await projectsText(names, d.projectIds)}.`,
|
|
53
|
+
"key.revoke": async () => `${who} revoked the key ${(await names.key(s(d.keyId))) || s(d.keyId)}.`,
|
|
54
|
+
"sso.create": () => `${who} added ${sso}.`,
|
|
55
|
+
"sso.update": () => `${who} changed ${sso}.`,
|
|
56
|
+
"sso.delete": () => `${who} removed ${sso}.`,
|
|
57
|
+
"sso.verify": () => `${who} proved the domain for ${sso}.`,
|
|
58
|
+
"project.create": async () => `${who} made the project ${await project()}.`,
|
|
59
|
+
"project.delete": async () => `${who} deleted the project ${await project()}.`,
|
|
60
|
+
"project.clear": async () => `${who} deleted all data of ${await project()}.`,
|
|
61
|
+
"share.create": async () => `${who} shared a trace from ${await project()} for ${Number(d.days) || 7} days.`,
|
|
62
|
+
"share.revoke": () => `${who} revoked a share link.`,
|
|
63
|
+
"org.update": () => `${who} changed the organization settings${typeof d.retentionDays === "number" ? ` (keep data ${d.retentionDays} days)` : ""}.`,
|
|
64
|
+
};
|
|
65
|
+
return (await say[row.action]?.()) ?? `${who}: ${row.action}.`;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=team-describe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-describe.js","sourceRoot":"","sources":["../../src/notify/team-describe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CloudApp } from "../app.js";
|
|
2
|
+
/** Send team notices for new audit lines, grouped by organization. Returns how many lines it took. */
|
|
3
|
+
export declare function sendTeamNotices(app: CloudApp): Promise<number>;
|
|
4
|
+
/** Before an organization is deleted: who to tell, since members are gone after. */
|
|
5
|
+
export declare function orgDeletionNotice(app: CloudApp, orgId: string, actorId: string): Promise<() => void>;
|
|
6
|
+
//# sourceMappingURL=team.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.d.ts","sourceRoot":"","sources":["../../src/notify/team.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AA0C1C,sGAAsG;AACtG,wBAAsB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAOpE;AAED,oFAAoF;AACpF,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAO1G"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { orgDeleted, roleChanged, teamChanges } from "../mail/emails-team.js";
|
|
2
|
+
import { renderMail } from "../mail/templates.js";
|
|
3
|
+
import { stopUrl } from "../mail/unsubscribe.js";
|
|
4
|
+
import { managersOf } from "../repo/mail-prefs.js";
|
|
5
|
+
import { describe, NOTICED, namesOf } from "./team-describe.js";
|
|
6
|
+
const BATCH = 200;
|
|
7
|
+
/** Take audit lines nobody was told about yet. SKIP LOCKED lets several instances share the work without sending twice. */
|
|
8
|
+
async function claim(app) {
|
|
9
|
+
return app.db.query(`UPDATE audit SET noticed = TRUE WHERE id IN (SELECT id FROM audit WHERE NOT noticed ORDER BY time_ms LIMIT ${BATCH} FOR UPDATE SKIP LOCKED)
|
|
10
|
+
RETURNING id, org_id, actor, action, detail, time_ms`);
|
|
11
|
+
}
|
|
12
|
+
const auditLink = (app, orgId) => `${app.baseUrl()}/_wizelens/#/team?org=${encodeURIComponent(orgId)}`;
|
|
13
|
+
/** A member whose role changed hears it from us too, even when they are not a manager. */
|
|
14
|
+
async function tellRoleChange(app, row, org) {
|
|
15
|
+
const d = JSON.parse(row.detail);
|
|
16
|
+
const [m] = await app.db.query("SELECT m.user_id, u.email, p.team FROM members m JOIN users u ON u.id = m.user_id LEFT JOIN mail_prefs p ON p.user_id = u.id WHERE m.id = $1", [d.memberId ?? ""]);
|
|
17
|
+
if (!m || m.team === false || row.actor === `user:${m.user_id}`)
|
|
18
|
+
return;
|
|
19
|
+
app.mailer.send(renderMail(m.email, roleChanged(org, d.role ?? "member", auditLink(app, row.org_id), stopUrl(app, m.user_id, "team"))));
|
|
20
|
+
}
|
|
21
|
+
/** One email per manager with the changes others made in that organization. */
|
|
22
|
+
async function noticeOrg(app, orgId, rows, names) {
|
|
23
|
+
const [o] = await app.db.query("SELECT name FROM orgs WHERE id = $1", [orgId]);
|
|
24
|
+
if (!o)
|
|
25
|
+
return;
|
|
26
|
+
for (const row of rows.filter((r) => r.action === "member.role"))
|
|
27
|
+
await tellRoleChange(app, row, o.name);
|
|
28
|
+
const changes = await Promise.all(rows.map(async (r) => ({ actor: r.actor, text: await describe(r, names), timeMs: r.time_ms })));
|
|
29
|
+
for (const m of await managersOf(app.db, orgId, "team", app.mailer.live)) {
|
|
30
|
+
const theirs = changes.filter((c) => c.actor !== `user:${m.id}`);
|
|
31
|
+
if (theirs.length)
|
|
32
|
+
app.mailer.send(renderMail(m.email, teamChanges(o.name, theirs, auditLink(app, orgId), stopUrl(app, m.id, "team"))));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Send team notices for new audit lines, grouped by organization. Returns how many lines it took. */
|
|
36
|
+
export async function sendTeamNotices(app) {
|
|
37
|
+
const rows = (await claim(app)).sort((a, b) => a.time_ms - b.time_ms);
|
|
38
|
+
const byOrg = new Map();
|
|
39
|
+
for (const r of rows.filter((r) => NOTICED.has(r.action)))
|
|
40
|
+
byOrg.set(r.org_id, [...(byOrg.get(r.org_id) ?? []), r]);
|
|
41
|
+
const names = namesOf(app.db);
|
|
42
|
+
for (const [orgId, list] of byOrg)
|
|
43
|
+
await noticeOrg(app, orgId, list, names);
|
|
44
|
+
return rows.length;
|
|
45
|
+
}
|
|
46
|
+
/** Before an organization is deleted: who to tell, since members are gone after. */
|
|
47
|
+
export async function orgDeletionNotice(app, orgId, actorId) {
|
|
48
|
+
const [o] = await app.db.query("SELECT name FROM orgs WHERE id = $1", [orgId]);
|
|
49
|
+
const people = await app.db.query("SELECT u.email FROM members m JOIN users u ON u.id = m.user_id WHERE m.org_id = $1 AND u.id <> $2", [orgId, actorId]);
|
|
50
|
+
const by = await namesOf(app.db).user(actorId);
|
|
51
|
+
return () => {
|
|
52
|
+
for (const p of people)
|
|
53
|
+
app.mailer.send(renderMail(p.email, orgDeleted(o?.name ?? "An organization", by, `${app.baseUrl()}/_wizelens/`)));
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=team.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/notify/team.ts"],"names":[],"mappings":"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"}
|
package/dist/repo/alerts.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { AlertMetric, AlertOp, MetricAlertStat } from "@wizeworks/lens-core";
|
|
2
2
|
import type { Queryable } from "../db/db.js";
|
|
3
|
-
|
|
3
|
+
/** Where a notice goes: a webhook, a Slack incoming webhook, or email to members of the organization. */
|
|
4
|
+
export type Channel = {
|
|
4
5
|
kind: "webhook" | "slack";
|
|
5
6
|
url: string;
|
|
6
|
-
}
|
|
7
|
+
} | {
|
|
8
|
+
kind: "email";
|
|
9
|
+
to: string[];
|
|
10
|
+
};
|
|
7
11
|
export interface AlertRule {
|
|
8
12
|
id: string;
|
|
9
13
|
projectId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/repo/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAElF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,
|
|
1
|
+
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/repo/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAElF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,yGAAyG;AACzG,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEnG,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,yGAAyG;IACzG,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,eAAe,GAAG,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,GAAG,WAAW,GAAG,YAAY,CAAC,CAAC;AASnG,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAErF;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAErE;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAG1G;AAED,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASxH;AAED,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzG;AAED,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9F;AAED,wBAAsB,cAAc,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1I;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5I;AAED,eAAO,MAAM,UAAU,MAAO,SAAS,aAAa,MAAM,SAAS,MAAM,KAAG,OAAO,CAAC,UAAU,EAAE,CAK7F,CAAC"}
|
package/dist/repo/alerts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/repo/alerts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/repo/alerts.ts"],"names":[],"mappings":"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"}
|
|
@@ -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"}
|