@wizeworks/lens-cloud 0.3.0-alpha.1 → 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.
Files changed (275) hide show
  1. package/README.md +57 -5
  2. package/dist/alerts/evaluate.d.ts.map +1 -1
  3. package/dist/alerts/evaluate.js +17 -2
  4. package/dist/alerts/evaluate.js.map +1 -1
  5. package/dist/alerts/notify.d.ts +13 -2
  6. package/dist/alerts/notify.d.ts.map +1 -1
  7. package/dist/alerts/notify.js +22 -16
  8. package/dist/alerts/notify.js.map +1 -1
  9. package/dist/app.d.ts +10 -2
  10. package/dist/app.d.ts.map +1 -1
  11. package/dist/auth/auth.d.ts +51 -0
  12. package/dist/auth/auth.d.ts.map +1 -0
  13. package/dist/auth/auth.js +63 -0
  14. package/dist/auth/auth.js.map +1 -0
  15. package/dist/auth/bridge.d.ts +7 -0
  16. package/dist/auth/bridge.d.ts.map +1 -0
  17. package/dist/auth/bridge.js +39 -0
  18. package/dist/auth/bridge.js.map +1 -0
  19. package/dist/auth/crypto.d.ts +0 -5
  20. package/dist/auth/crypto.d.ts.map +1 -1
  21. package/dist/auth/crypto.js +3 -20
  22. package/dist/auth/crypto.js.map +1 -1
  23. package/dist/auth/delete-user.d.ts +16 -0
  24. package/dist/auth/delete-user.d.ts.map +1 -0
  25. package/dist/auth/delete-user.js +21 -0
  26. package/dist/auth/delete-user.js.map +1 -0
  27. package/dist/auth/deps.d.ts +27 -0
  28. package/dist/auth/deps.d.ts.map +1 -0
  29. package/dist/auth/deps.js +3 -0
  30. package/dist/auth/deps.js.map +1 -0
  31. package/dist/auth/email-token.d.ts +8 -0
  32. package/dist/auth/email-token.d.ts.map +1 -0
  33. package/dist/auth/email-token.js +15 -0
  34. package/dist/auth/email-token.js.map +1 -0
  35. package/dist/auth/hooks.d.ts +4 -0
  36. package/dist/auth/hooks.d.ts.map +1 -0
  37. package/dist/auth/hooks.js +143 -0
  38. package/dist/auth/hooks.js.map +1 -0
  39. package/dist/auth/options.d.ts +17 -0
  40. package/dist/auth/options.d.ts.map +1 -0
  41. package/dist/auth/options.js +98 -0
  42. package/dist/auth/options.js.map +1 -0
  43. package/dist/auth/password.d.ts +10 -0
  44. package/dist/auth/password.d.ts.map +1 -0
  45. package/dist/auth/password.js +22 -0
  46. package/dist/auth/password.js.map +1 -0
  47. package/dist/auth/plugins.d.ts +4 -0
  48. package/dist/auth/plugins.d.ts.map +1 -0
  49. package/dist/auth/plugins.js +45 -0
  50. package/dist/auth/plugins.js.map +1 -0
  51. package/dist/auth/principal.d.ts +8 -3
  52. package/dist/auth/principal.d.ts.map +1 -1
  53. package/dist/auth/principal.js +13 -8
  54. package/dist/auth/principal.js.map +1 -1
  55. package/dist/auth/roles.d.ts +43 -3
  56. package/dist/auth/roles.d.ts.map +1 -1
  57. package/dist/auth/roles.js +15 -6
  58. package/dist/auth/roles.js.map +1 -1
  59. package/dist/auth/schema-names.d.ts +130 -0
  60. package/dist/auth/schema-names.d.ts.map +1 -0
  61. package/dist/auth/schema-names.js +53 -0
  62. package/dist/auth/schema-names.js.map +1 -0
  63. package/dist/auth/secret.d.ts +12 -0
  64. package/dist/auth/secret.d.ts.map +1 -0
  65. package/dist/auth/secret.js +32 -0
  66. package/dist/auth/secret.js.map +1 -0
  67. package/dist/auth/security-notices.d.ts +34 -0
  68. package/dist/auth/security-notices.d.ts.map +1 -0
  69. package/dist/auth/security-notices.js +81 -0
  70. package/dist/auth/security-notices.js.map +1 -0
  71. package/dist/auth/signup.d.ts +23 -0
  72. package/dist/auth/signup.d.ts.map +1 -0
  73. package/dist/auth/signup.js +61 -0
  74. package/dist/auth/signup.js.map +1 -0
  75. package/dist/auth/sso.d.ts +21 -0
  76. package/dist/auth/sso.d.ts.map +1 -0
  77. package/dist/auth/sso.js +35 -0
  78. package/dist/auth/sso.js.map +1 -0
  79. package/dist/auth/wire.d.ts +16 -0
  80. package/dist/auth/wire.d.ts.map +1 -0
  81. package/dist/auth/wire.js +47 -0
  82. package/dist/auth/wire.js.map +1 -0
  83. package/dist/cli-args.d.ts +1 -1
  84. package/dist/cli-args.d.ts.map +1 -1
  85. package/dist/cli-args.js +55 -14
  86. package/dist/cli-args.js.map +1 -1
  87. package/dist/cli.js +18 -2
  88. package/dist/cli.js.map +1 -1
  89. package/dist/config.d.ts +26 -4
  90. package/dist/config.d.ts.map +1 -1
  91. package/dist/config.js +35 -0
  92. package/dist/config.js.map +1 -1
  93. package/dist/db/db.d.ts +5 -2
  94. package/dist/db/db.d.ts.map +1 -1
  95. package/dist/db/db.js +15 -8
  96. package/dist/db/db.js.map +1 -1
  97. package/dist/db/migrate.d.ts +2 -2
  98. package/dist/db/migrate.d.ts.map +1 -1
  99. package/dist/db/migrate.js +15 -5
  100. package/dist/db/migrate.js.map +1 -1
  101. package/dist/db/schema-auth.d.ts +2 -0
  102. package/dist/db/schema-auth.d.ts.map +1 -0
  103. package/dist/db/schema-auth.js +93 -0
  104. package/dist/db/schema-auth.js.map +1 -0
  105. package/dist/db/schema-mail.d.ts +2 -0
  106. package/dist/db/schema-mail.d.ts.map +1 -0
  107. package/dist/db/schema-mail.js +8 -0
  108. package/dist/db/schema-mail.js.map +1 -0
  109. package/dist/db/schema-notify.d.ts +2 -0
  110. package/dist/db/schema-notify.d.ts.map +1 -0
  111. package/dist/db/schema-notify.js +26 -0
  112. package/dist/db/schema-notify.js.map +1 -0
  113. package/dist/db/schema-sso.d.ts +2 -0
  114. package/dist/db/schema-sso.d.ts.map +1 -0
  115. package/dist/db/schema-sso.js +17 -0
  116. package/dist/db/schema-sso.js.map +1 -0
  117. package/dist/db/schema.d.ts.map +1 -1
  118. package/dist/db/schema.js +8 -0
  119. package/dist/db/schema.js.map +1 -1
  120. package/dist/http/http.d.ts +2 -0
  121. package/dist/http/http.d.ts.map +1 -1
  122. package/dist/http/http.js +7 -3
  123. package/dist/http/http.js.map +1 -1
  124. package/dist/jobs.d.ts +1 -1
  125. package/dist/jobs.d.ts.map +1 -1
  126. package/dist/jobs.js +14 -3
  127. package/dist/jobs.js.map +1 -1
  128. package/dist/mail/blocks.d.ts +14 -0
  129. package/dist/mail/blocks.d.ts.map +1 -0
  130. package/dist/mail/blocks.js +35 -0
  131. package/dist/mail/blocks.js.map +1 -0
  132. package/dist/mail/content.d.ts +68 -0
  133. package/dist/mail/content.d.ts.map +1 -0
  134. package/dist/mail/content.js +6 -0
  135. package/dist/mail/content.js.map +1 -0
  136. package/dist/mail/emails-digest.d.ts +10 -0
  137. package/dist/mail/emails-digest.d.ts.map +1 -0
  138. package/dist/mail/emails-digest.js +55 -0
  139. package/dist/mail/emails-digest.js.map +1 -0
  140. package/dist/mail/emails-health.d.ts +6 -0
  141. package/dist/mail/emails-health.d.ts.map +1 -0
  142. package/dist/mail/emails-health.js +36 -0
  143. package/dist/mail/emails-health.js.map +1 -0
  144. package/dist/mail/emails-security.d.ts +14 -0
  145. package/dist/mail/emails-security.d.ts.map +1 -0
  146. package/dist/mail/emails-security.js +27 -0
  147. package/dist/mail/emails-security.js.map +1 -0
  148. package/dist/mail/emails-team.d.ts +11 -0
  149. package/dist/mail/emails-team.d.ts.map +1 -0
  150. package/dist/mail/emails-team.js +45 -0
  151. package/dist/mail/emails-team.js.map +1 -0
  152. package/dist/mail/emails.d.ts +9 -0
  153. package/dist/mail/emails.d.ts.map +1 -0
  154. package/dist/mail/emails.js +49 -0
  155. package/dist/mail/emails.js.map +1 -0
  156. package/dist/mail/layout.d.ts +6 -0
  157. package/dist/mail/layout.d.ts.map +1 -0
  158. package/dist/mail/layout.js +10 -0
  159. package/dist/mail/layout.js.map +1 -0
  160. package/dist/mail/mailer.d.ts +25 -0
  161. package/dist/mail/mailer.d.ts.map +1 -0
  162. package/dist/mail/mailer.js +49 -0
  163. package/dist/mail/mailer.js.map +1 -0
  164. package/dist/mail/preview.d.ts +6 -0
  165. package/dist/mail/preview.d.ts.map +1 -0
  166. package/dist/mail/preview.js +26 -0
  167. package/dist/mail/preview.js.map +1 -0
  168. package/dist/mail/resend.d.ts +11 -0
  169. package/dist/mail/resend.d.ts.map +1 -0
  170. package/dist/mail/resend.js +34 -0
  171. package/dist/mail/resend.js.map +1 -0
  172. package/dist/mail/samples.d.ts +3 -0
  173. package/dist/mail/samples.d.ts.map +1 -0
  174. package/dist/mail/samples.js +63 -0
  175. package/dist/mail/samples.js.map +1 -0
  176. package/dist/mail/stop-route.d.ts +4 -0
  177. package/dist/mail/stop-route.d.ts.map +1 -0
  178. package/dist/mail/stop-route.js +41 -0
  179. package/dist/mail/stop-route.js.map +1 -0
  180. package/dist/mail/svix.d.ts +8 -0
  181. package/dist/mail/svix.d.ts.map +1 -0
  182. package/dist/mail/svix.js +17 -0
  183. package/dist/mail/svix.js.map +1 -0
  184. package/dist/mail/templates.d.ts +12 -0
  185. package/dist/mail/templates.d.ts.map +1 -0
  186. package/dist/mail/templates.js +18 -0
  187. package/dist/mail/templates.js.map +1 -0
  188. package/dist/mail/text.d.ts +4 -0
  189. package/dist/mail/text.d.ts.map +1 -0
  190. package/dist/mail/text.js +17 -0
  191. package/dist/mail/text.js.map +1 -0
  192. package/dist/mail/theme.d.ts +26 -0
  193. package/dist/mail/theme.d.ts.map +1 -0
  194. package/dist/mail/theme.js +21 -0
  195. package/dist/mail/theme.js.map +1 -0
  196. package/dist/mail/unsubscribe.d.ts +14 -0
  197. package/dist/mail/unsubscribe.d.ts.map +1 -0
  198. package/dist/mail/unsubscribe.js +39 -0
  199. package/dist/mail/unsubscribe.js.map +1 -0
  200. package/dist/mail/webhook.d.ts +5 -0
  201. package/dist/mail/webhook.d.ts.map +1 -0
  202. package/dist/mail/webhook.js +41 -0
  203. package/dist/mail/webhook.js.map +1 -0
  204. package/dist/notify/digest-data.d.ts +31 -0
  205. package/dist/notify/digest-data.d.ts.map +1 -0
  206. package/dist/notify/digest-data.js +37 -0
  207. package/dist/notify/digest-data.js.map +1 -0
  208. package/dist/notify/digest.d.ts +17 -0
  209. package/dist/notify/digest.d.ts.map +1 -0
  210. package/dist/notify/digest.js +76 -0
  211. package/dist/notify/digest.js.map +1 -0
  212. package/dist/notify/health.d.ts +6 -0
  213. package/dist/notify/health.d.ts.map +1 -0
  214. package/dist/notify/health.js +43 -0
  215. package/dist/notify/health.js.map +1 -0
  216. package/dist/notify/team-describe.d.ts +22 -0
  217. package/dist/notify/team-describe.d.ts.map +1 -0
  218. package/dist/notify/team-describe.js +67 -0
  219. package/dist/notify/team-describe.js.map +1 -0
  220. package/dist/notify/team.d.ts +6 -0
  221. package/dist/notify/team.d.ts.map +1 -0
  222. package/dist/notify/team.js +56 -0
  223. package/dist/notify/team.js.map +1 -0
  224. package/dist/repo/alerts.d.ts +6 -2
  225. package/dist/repo/alerts.d.ts.map +1 -1
  226. package/dist/repo/alerts.js.map +1 -1
  227. package/dist/repo/auth-prune.d.ts +4 -0
  228. package/dist/repo/auth-prune.d.ts.map +1 -0
  229. package/dist/repo/auth-prune.js +8 -0
  230. package/dist/repo/auth-prune.js.map +1 -0
  231. package/dist/repo/mail-prefs.d.ts +35 -0
  232. package/dist/repo/mail-prefs.d.ts.map +1 -0
  233. package/dist/repo/mail-prefs.js +34 -0
  234. package/dist/repo/mail-prefs.js.map +1 -0
  235. package/dist/repo/orgs.d.ts +15 -25
  236. package/dist/repo/orgs.d.ts.map +1 -1
  237. package/dist/repo/orgs.js +18 -60
  238. package/dist/repo/orgs.js.map +1 -1
  239. package/dist/repo/suppressions.d.ts +8 -0
  240. package/dist/repo/suppressions.d.ts.map +1 -0
  241. package/dist/repo/suppressions.js +16 -0
  242. package/dist/repo/suppressions.js.map +1 -0
  243. package/dist/repo/users.d.ts +5 -16
  244. package/dist/repo/users.d.ts.map +1 -1
  245. package/dist/repo/users.js +2 -60
  246. package/dist/repo/users.js.map +1 -1
  247. package/dist/routes/alerts.d.ts.map +1 -1
  248. package/dist/routes/alerts.js +23 -6
  249. package/dist/routes/alerts.js.map +1 -1
  250. package/dist/routes/index.d.ts.map +1 -1
  251. package/dist/routes/index.js +4 -2
  252. package/dist/routes/index.js.map +1 -1
  253. package/dist/routes/me.d.ts +4 -0
  254. package/dist/routes/me.d.ts.map +1 -0
  255. package/dist/routes/me.js +37 -0
  256. package/dist/routes/me.js.map +1 -0
  257. package/dist/routes/notify.d.ts +4 -0
  258. package/dist/routes/notify.d.ts.map +1 -0
  259. package/dist/routes/notify.js +55 -0
  260. package/dist/routes/notify.js.map +1 -0
  261. package/dist/routes/orgs.d.ts.map +1 -1
  262. package/dist/routes/orgs.js +30 -53
  263. package/dist/routes/orgs.js.map +1 -1
  264. package/dist/routes/projects.d.ts.map +1 -1
  265. package/dist/routes/projects.js +4 -3
  266. package/dist/routes/projects.js.map +1 -1
  267. package/dist/server.d.ts +2 -0
  268. package/dist/server.d.ts.map +1 -1
  269. package/dist/server.js +45 -14
  270. package/dist/server.js.map +1 -1
  271. package/package.json +19 -6
  272. package/dist/routes/auth.d.ts +0 -4
  273. package/dist/routes/auth.d.ts.map +0 -1
  274. package/dist/routes/auth.js +0 -106
  275. package/dist/routes/auth.js.map +0 -1
@@ -0,0 +1,35 @@
1
+ import { sso } from "@better-auth/sso";
2
+ import { audit } from "../repo/audit.js";
3
+ import { ssoSchema } from "./schema-names.js";
4
+ /** The DNS record that proves a domain: `_wizelens-sso-<providerId>.<domain>` TXT `<token>`. */
5
+ export const SSO_TOKEN_PREFIX = "wizelens-sso";
6
+ /** The SSO provider behind a new account, when Better Auth makes it from a single sign-on. */
7
+ export function ssoProviderOf(source) {
8
+ const s = source;
9
+ return s?.method?.startsWith("sso-") && typeof s.sso?.providerId === "string" ? s.sso.providerId : undefined;
10
+ }
11
+ /** True when an organization's provider has a proven domain and the email is at exactly that domain. */
12
+ export async function ssoTrusts(db, providerId, email) {
13
+ const p = await db.findOne({ model: "ssoProvider", where: [{ field: "providerId", value: providerId }] });
14
+ const at = email.trim().toLowerCase().split("@")[1] ?? "";
15
+ const domains = (p?.domain ?? "").toLowerCase().split(",").map((d) => d.trim());
16
+ return !!p?.organizationId && p.domainVerified === true && !!at && domains.includes(at);
17
+ }
18
+ /** Organization SSO with OIDC or SAML. Sign-in works once the domain is proven in DNS; new people join that organization as members. */
19
+ export function ssoPlugin(deps) {
20
+ return sso({
21
+ schema: ssoSchema,
22
+ providersLimit: 20,
23
+ domainVerification: { enabled: true, tokenPrefix: SSO_TOKEN_PREFIX },
24
+ organizationProvisioning: {
25
+ defaultRole: "member",
26
+ getRole: async ({ user, provider }) => {
27
+ if (provider.organizationId)
28
+ await audit(deps.db, provider.organizationId, `user:${user.id}`, "member.join", { via: "sso", providerId: provider.providerId }, Date.now());
29
+ return "member";
30
+ },
31
+ },
32
+ saml: { enableInResponseToValidation: true, allowIdpInitiated: false, requireTimestamps: true, clockSkew: 2 * 60_000, algorithms: { onDeprecated: "reject" } },
33
+ });
34
+ }
35
+ //# sourceMappingURL=sso.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.js","sourceRoot":"","sources":["../../src/auth/sso.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,gGAAgG;AAChG,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAY/C,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,MAAe;IAC3C,MAAM,CAAC,GAAG,MAAyE,CAAC;IACpF,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/G,CAAC;AAED,wGAAwG;AACxG,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAU,EAAE,UAAkB,EAAE,KAAa;IAC3E,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,OAAO,CAAc,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IACvH,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAChF,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,IAAI,CAAC,CAAC,cAAc,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,wIAAwI;AACxI,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,OAAO,GAAG,CAAC;QACT,MAAM,EAAE,SAAS;QACjB,cAAc,EAAE,EAAE;QAClB,kBAAkB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE;QACpE,wBAAwB,EAAE;YACxB,WAAW,EAAE,QAAQ;YACrB,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;gBACpC,IAAI,QAAQ,CAAC,cAAc;oBAAE,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,cAAc,EAAE,QAAQ,IAAI,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC1K,OAAO,QAAQ,CAAC;YAClB,CAAC;SACF;QACD,IAAI,EAAE,EAAE,4BAA4B,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE;KAC/J,CAAgC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { CloudConfig } from "../config.js";
2
+ import type { Db } from "../db/db.js";
3
+ import type { Mailer } from "../mail/mailer.js";
4
+ import { type WizeAuth } from "./auth.js";
5
+ import { type AuthSecret } from "./secret.js";
6
+ export interface AuthWiring {
7
+ /** Build Better Auth for the address the cloud listens on. */
8
+ start(listenUrl: string): WizeAuth;
9
+ /** The first account's link while no account exists, else null. */
10
+ setupLink(): string | null;
11
+ /** The auth secrets, newest first. Unsubscribe links are signed with them too. */
12
+ secrets: AuthSecret[];
13
+ }
14
+ /** Secrets, the setup gate, and the rest Better Auth needs; Better Auth itself starts once the address is known. */
15
+ export declare function wireAuth(db: Db, config: CloudConfig, mailer: Mailer): Promise<AuthWiring>;
16
+ //# sourceMappingURL=wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/auth/wire.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EAAE,KAAK,UAAU,EAAgC,MAAM,aAAa,CAAC;AAE5E,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnC,mEAAmE;IACnE,SAAS,IAAI,MAAM,GAAG,IAAI,CAAC;IAC3B,kFAAkF;IAClF,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAgBD,oHAAoH;AACpH,wBAAsB,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA2B/F"}
@@ -0,0 +1,47 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { userCount } from "../repo/users.js";
3
+ import { createAuth } from "./auth.js";
4
+ import { clearAuthEnv, resolveSecrets } from "./secret.js";
5
+ /** The one-time setup token: from the config, or new. Gone once the first account exists. */
6
+ async function setupGate(db, configured) {
7
+ let open = (await userCount(db)) === 0;
8
+ const token = configured ?? randomBytes(24).toString("base64url");
9
+ return { token: () => (open ? token : null), done: () => void (open = false) };
10
+ }
11
+ /** The public origin, plus 127.0.0.1 next to localhost (or the reverse) when the cloud runs on its default local address. */
12
+ function originsOf(publicUrl, local) {
13
+ const origin = new URL(publicUrl).origin;
14
+ if (!local)
15
+ return [origin];
16
+ return [origin, origin.includes("//localhost") ? origin.replace("//localhost", "//127.0.0.1") : origin.replace("//127.0.0.1", "//localhost")];
17
+ }
18
+ /** Secrets, the setup gate, and the rest Better Auth needs; Better Auth itself starts once the address is known. */
19
+ export async function wireAuth(db, config, mailer) {
20
+ clearAuthEnv();
21
+ const [secrets, setup] = await Promise.all([resolveSecrets(db, config.authSecret), setupGate(db, config.setupToken)]);
22
+ let base = config.publicUrl;
23
+ return {
24
+ secrets,
25
+ start: (listenUrl) => {
26
+ base = config.publicUrl ?? listenUrl.replace("//127.0.0.1", "//localhost");
27
+ const deps = {
28
+ db,
29
+ publicUrl: new URL(base).origin,
30
+ trustedOrigins: originsOf(base, !config.publicUrl),
31
+ ssoOrigins: config.ssoOrigins,
32
+ secrets,
33
+ signup: config.signup,
34
+ mailer,
35
+ setup,
36
+ checkPwned: config.checkPwnedPasswords ?? db.kind === "postgres",
37
+ retentionDays: config.retentionDays,
38
+ };
39
+ return createAuth(deps);
40
+ },
41
+ setupLink: () => {
42
+ const token = setup.token();
43
+ return token && base ? `${base}/_wizelens/#/setup?token=${token}` : null;
44
+ },
45
+ };
46
+ }
47
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../src/auth/wire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAiB,MAAM,WAAW,CAAC;AAEtD,OAAO,EAAmB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAW5E,6FAA6F;AAC7F,KAAK,UAAU,SAAS,CAAC,EAAM,EAAE,UAA8B;IAC7D,IAAI,IAAI,GAAG,CAAC,MAAM,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,UAAU,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAClE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;AACjF,CAAC;AAED,6HAA6H;AAC7H,SAAS,SAAS,CAAC,SAAiB,EAAE,KAAc;IAClD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC;AAChJ,CAAC;AAED,oHAAoH;AACpH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAM,EAAE,MAAmB,EAAE,MAAc;IACxE,YAAY,EAAE,CAAC;IACf,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACtH,IAAI,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;IAC5B,OAAO;QACL,OAAO;QACP,KAAK,EAAE,CAAC,SAAS,EAAE,EAAE;YACnB,IAAI,GAAG,MAAM,CAAC,SAAS,IAAI,SAAS,CAAC,OAAO,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;YAC3E,MAAM,IAAI,GAAa;gBACrB,EAAE;gBACF,SAAS,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM;gBAC/B,cAAc,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC;gBAClD,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO;gBACP,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,MAAM;gBACN,KAAK;gBACL,UAAU,EAAE,MAAM,CAAC,mBAAmB,IAAI,EAAE,CAAC,IAAI,KAAK,UAAU;gBAChE,aAAa,EAAE,MAAM,CAAC,aAAa;aACpC,CAAC;YACF,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,SAAS,EAAE,GAAG,EAAE;YACd,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YAC5B,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,4BAA4B,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3E,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { CloudOptions } from "./config.js";
2
- export declare const USAGE = "wizelens-cloud: the WizeLens service for many teams.\n\nUsage: wizelens-cloud [options]\n\n --database <url> postgres://..., pglite:<folder>, or memory.\n Default pglite:./.wizelens-cloud. (DATABASE_URL)\n --port <n> Default 5970. (PORT)\n --host <addr> Default 0.0.0.0. (WIZELENS_CLOUD_HOST)\n --public-url <url> The address people use, for links. (WIZELENS_CLOUD_PUBLIC_URL)\n --signup <invite|open> Who can make an account. Default invite. (WIZELENS_CLOUD_SIGNUP)\n --retention-days <n> Default for new organizations. Default 30. (WIZELENS_CLOUD_RETENTION_DAYS)\n --allow-private-webhooks Let alert webhooks reach private addresses. (WIZELENS_CLOUD_ALLOW_PRIVATE_WEBHOOKS=1)\n --help Show this help.\n";
2
+ export declare const USAGE = "wizelens-cloud: the WizeLens service for many teams.\n\nUsage: wizelens-cloud [options]\n\n --database <url> postgres://..., pglite:<folder>, or memory.\n Default pglite:./.wizelens-cloud. (DATABASE_URL)\n --port <n> Default 5970. (PORT)\n --host <addr> Default 0.0.0.0. (WIZELENS_CLOUD_HOST)\n --public-url <url> The address people use, for links. (WIZELENS_CLOUD_PUBLIC_URL)\n --signup <invite|open> Who can make an account. Default invite. (WIZELENS_CLOUD_SIGNUP)\n --retention-days <n> Default for new organizations. Default 30. (WIZELENS_CLOUD_RETENTION_DAYS)\n --allow-private-webhooks Let alert webhooks reach private addresses. (WIZELENS_CLOUD_ALLOW_PRIVATE_WEBHOOKS=1)\n --auth-secret <text> Signs sessions, encrypts 2FA secrets. 32+ (WIZELENS_CLOUD_AUTH_SECRET)\n characters; \"2:new,1:old\" rotates.\n Required with Postgres.\n --resend-api-key <key> Outgoing mail through Resend (re_...). (WIZELENS_CLOUD_RESEND_API_KEY)\n Needs --public-url.\n --resend-webhook-secret <s> The Resend webhook's whsec_ secret: (WIZELENS_CLOUD_RESEND_WEBHOOK_SECRET)\n bounces and complaints stop later mail.\n Webhook URL: <public-url>/_wizelens/api/mail/resend\n --smtp-url <url> Or mail over SMTP: smtp://user:pass@host:587 (WIZELENS_CLOUD_SMTP_URL)\n or smtps://...:465. Needs --public-url.\n --mail-from <address> Sender, e.g. \"WizeLens <a@b.com>\". (WIZELENS_CLOUD_MAIL_FROM)\n --trust-proxy Read the client address from the last (WIZELENS_CLOUD_TRUST_PROXY=1)\n X-Forwarded-For entry (your own proxy only).\n --setup-token <text> The first account's token. Default: random, (WIZELENS_CLOUD_SETUP_TOKEN)\n printed at start while no account exists.\n --check-pwned-passwords Refuse breached passwords. Default on with (WIZELENS_CLOUD_CHECK_PWNED=1|0)\n --no-check-pwned-passwords Postgres, off with PGlite.\n --sso-origins <list> Identity provider origins for OIDC single (WIZELENS_CLOUD_SSO_ORIGINS)\n sign-on, comma separated, for example\n https://acme.okta.com. SAML needs none.\n --help Show this help.\n";
3
3
  /** Options from the command line, then from the environment. */
4
4
  export declare function optionsFromArgs(argv: string[], env: NodeJS.ProcessEnv): CloudOptions | "help";
5
5
  //# sourceMappingURL=cli-args.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AAE5D,eAAO,MAAM,KAAK,83BAajB,CAAC;AAKF,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,GAAG,MAAM,CAwB7F"}
1
+ {"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AAE5D,eAAO,MAAM,KAAK,y/EAiCjB,CAAC;AAgCF,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,GAAG,MAAM,CAqB7F"}
package/dist/cli-args.js CHANGED
@@ -11,35 +11,76 @@ Usage: wizelens-cloud [options]
11
11
  --signup <invite|open> Who can make an account. Default invite. (WIZELENS_CLOUD_SIGNUP)
12
12
  --retention-days <n> Default for new organizations. Default 30. (WIZELENS_CLOUD_RETENTION_DAYS)
13
13
  --allow-private-webhooks Let alert webhooks reach private addresses. (WIZELENS_CLOUD_ALLOW_PRIVATE_WEBHOOKS=1)
14
+ --auth-secret <text> Signs sessions, encrypts 2FA secrets. 32+ (WIZELENS_CLOUD_AUTH_SECRET)
15
+ characters; "2:new,1:old" rotates.
16
+ Required with Postgres.
17
+ --resend-api-key <key> Outgoing mail through Resend (re_...). (WIZELENS_CLOUD_RESEND_API_KEY)
18
+ Needs --public-url.
19
+ --resend-webhook-secret <s> The Resend webhook's whsec_ secret: (WIZELENS_CLOUD_RESEND_WEBHOOK_SECRET)
20
+ bounces and complaints stop later mail.
21
+ Webhook URL: <public-url>/_wizelens/api/mail/resend
22
+ --smtp-url <url> Or mail over SMTP: smtp://user:pass@host:587 (WIZELENS_CLOUD_SMTP_URL)
23
+ or smtps://...:465. Needs --public-url.
24
+ --mail-from <address> Sender, e.g. "WizeLens <a@b.com>". (WIZELENS_CLOUD_MAIL_FROM)
25
+ --trust-proxy Read the client address from the last (WIZELENS_CLOUD_TRUST_PROXY=1)
26
+ X-Forwarded-For entry (your own proxy only).
27
+ --setup-token <text> The first account's token. Default: random, (WIZELENS_CLOUD_SETUP_TOKEN)
28
+ printed at start while no account exists.
29
+ --check-pwned-passwords Refuse breached passwords. Default on with (WIZELENS_CLOUD_CHECK_PWNED=1|0)
30
+ --no-check-pwned-passwords Postgres, off with PGlite.
31
+ --sso-origins <list> Identity provider origins for OIDC single (WIZELENS_CLOUD_SSO_ORIGINS)
32
+ sign-on, comma separated, for example
33
+ https://acme.okta.com. SAML needs none.
14
34
  --help Show this help.
15
35
  `;
16
36
  const int = (v) => (v && /^\d+$/.test(v) ? Number(v) : undefined);
17
37
  const signupOf = (v) => (v === "open" || v === "invite" ? v : undefined);
38
+ const text = (...v) => v.find((x) => x !== undefined && x !== "");
39
+ const list = (v) => v?.split(",").map((s) => s.trim()).filter(Boolean);
40
+ const flag = (v) => (v === "1" || v === "true" ? true : v === "0" || v === "false" ? false : undefined);
41
+ const OPTIONS = {
42
+ database: { type: "string" },
43
+ port: { type: "string" },
44
+ host: { type: "string" },
45
+ "public-url": { type: "string" },
46
+ signup: { type: "string" },
47
+ "retention-days": { type: "string" },
48
+ "allow-private-webhooks": { type: "boolean" },
49
+ "auth-secret": { type: "string" },
50
+ "smtp-url": { type: "string" },
51
+ "resend-api-key": { type: "string" },
52
+ "resend-webhook-secret": { type: "string" },
53
+ "mail-from": { type: "string" },
54
+ "trust-proxy": { type: "boolean" },
55
+ "setup-token": { type: "string" },
56
+ "check-pwned-passwords": { type: "boolean" },
57
+ "no-check-pwned-passwords": { type: "boolean" },
58
+ "sso-origins": { type: "string" },
59
+ help: { type: "boolean", short: "h" },
60
+ };
61
+ const pwnedOf = (a, env) => a["no-check-pwned-passwords"] ? false : (a["check-pwned-passwords"] ?? flag(env.WIZELENS_CLOUD_CHECK_PWNED));
18
62
  /** Options from the command line, then from the environment. */
19
63
  export function optionsFromArgs(argv, env) {
20
- const { values: a } = parseArgs({
21
- args: argv,
22
- options: {
23
- database: { type: "string" },
24
- port: { type: "string" },
25
- host: { type: "string" },
26
- "public-url": { type: "string" },
27
- signup: { type: "string" },
28
- "retention-days": { type: "string" },
29
- "allow-private-webhooks": { type: "boolean" },
30
- help: { type: "boolean", short: "h" },
31
- },
32
- });
64
+ const { values: a } = parseArgs({ args: argv, options: OPTIONS });
33
65
  if (a.help)
34
66
  return "help";
35
67
  return {
36
68
  database: a.database ?? env.DATABASE_URL,
37
69
  port: int(a.port ?? env.PORT),
38
70
  host: a.host ?? env.WIZELENS_CLOUD_HOST,
39
- publicUrl: a["public-url"] ?? env.WIZELENS_CLOUD_PUBLIC_URL,
71
+ publicUrl: text(a["public-url"], env.WIZELENS_CLOUD_PUBLIC_URL),
40
72
  signup: signupOf(a.signup ?? env.WIZELENS_CLOUD_SIGNUP),
41
73
  retentionDays: int(a["retention-days"] ?? env.WIZELENS_CLOUD_RETENTION_DAYS),
42
74
  allowPrivateWebhooks: a["allow-private-webhooks"] ?? env.WIZELENS_CLOUD_ALLOW_PRIVATE_WEBHOOKS === "1",
75
+ authSecret: text(a["auth-secret"], env.WIZELENS_CLOUD_AUTH_SECRET),
76
+ smtpUrl: text(a["smtp-url"], env.WIZELENS_CLOUD_SMTP_URL),
77
+ resendApiKey: text(a["resend-api-key"], env.WIZELENS_CLOUD_RESEND_API_KEY),
78
+ resendWebhookSecret: text(a["resend-webhook-secret"], env.WIZELENS_CLOUD_RESEND_WEBHOOK_SECRET),
79
+ mailFrom: text(a["mail-from"], env.WIZELENS_CLOUD_MAIL_FROM),
80
+ trustProxy: a["trust-proxy"] ?? env.WIZELENS_CLOUD_TRUST_PROXY === "1",
81
+ setupToken: text(a["setup-token"], env.WIZELENS_CLOUD_SETUP_TOKEN),
82
+ checkPwnedPasswords: pwnedOf(a, env),
83
+ ssoOrigins: list(text(a["sso-origins"], env.WIZELENS_CLOUD_SSO_ORIGINS)),
43
84
  };
44
85
  }
45
86
  //# sourceMappingURL=cli-args.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;;;;CAapB,CAAC;AAEF,MAAM,GAAG,GAAG,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACtF,MAAM,QAAQ,GAAG,CAAC,CAAqB,EAA0B,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAErH,gEAAgE;AAChE,MAAM,UAAU,eAAe,CAAC,IAAc,EAAE,GAAsB;IACpE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC;QAC9B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACP,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC5B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAChC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACpC,wBAAwB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC7C,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACtC;KACF,CAAC,CAAC;IACH,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,MAAM,CAAC;IAC1B,OAAO;QACL,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,YAAY;QACxC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC;QAC7B,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,mBAAmB;QACvC,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,yBAAyB;QAC3D,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,qBAAqB,CAAC;QACvD,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,6BAA6B,CAAC;QAC5E,oBAAoB,EAAE,CAAC,CAAC,wBAAwB,CAAC,IAAI,GAAG,CAAC,qCAAqC,KAAK,GAAG;KACvG,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,6 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import { optionsFromArgs, USAGE } from "./cli-args.js";
3
3
  import { CLOUD_VERSION, createCloud } from "./server.js";
4
+ function notes(cloud) {
5
+ const { app } = cloud;
6
+ const out = [];
7
+ if (app.db.kind === "pglite")
8
+ out.push(" Note: PGlite is for trials and tests. Use Postgres for real teams.");
9
+ if (!app.mailer.live)
10
+ out.push(" Note: no --resend-api-key or --smtp-url, so emails are written to this log and email addresses are not confirmed.");
11
+ if (app.mailer.via === "resend" && !app.config.resendWebhookSecret)
12
+ out.push(" Note: no --resend-webhook-secret, so bounced addresses keep getting mail.");
13
+ if (app.db.kind === "postgres" && !app.config.publicUrl?.startsWith("https://"))
14
+ out.push(" Note: set --public-url to an https:// address, so sign-in cookies are Secure.");
15
+ const setup = cloud.setupLink();
16
+ if (setup)
17
+ out.push("", " No account yet. Open this link to make the first one (it works once):", ` ${setup}`);
18
+ return out;
19
+ }
4
20
  async function main() {
5
21
  const options = optionsFromArgs(process.argv.slice(2), process.env);
6
22
  if (options === "help")
@@ -14,9 +30,9 @@ async function main() {
14
30
  ` Add-ons WIZELENS_HUB=${url} WIZELENS_HUB_TOKEN=<ingest key>`,
15
31
  ` OTLP/HTTP OTEL_EXPORTER_OTLP_ENDPOINT=${url} (header Authorization=Bearer <ingest key>)`,
16
32
  ` Database ${db}`,
33
+ ` Mail ${{ resend: "Resend", smtp: "SMTP", test: "test sender", log: "log only" }[cloud.app.mailer.via]}`,
34
+ ...notes(cloud),
17
35
  ].join("\n"));
18
- if (cloud.app.db.kind === "pglite")
19
- console.info(" Note: PGlite is for trials and tests. Use Postgres for real teams.");
20
36
  const stop = () => void cloud.close().then(() => process.exit(0));
21
37
  process.once("SIGINT", stop);
22
38
  process.once("SIGTERM", stop);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEzD,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACpE,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;IACjC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC;IACnG,OAAO,CAAC,IAAI,CACV;QACE,kBAAkB,aAAa,EAAE;QACjC,iBAAiB,GAAG,aAAa;QACjC,8BAA8B,GAAG,mCAAmC;QACpE,6CAA6C,GAAG,8CAA8C;QAC9F,iBAAiB,EAAE,EAAE;KACtB,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IACF,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;IACzH,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Mail } from "./mail/mailer.js";
1
2
  export type SignupMode = "open" | "invite";
2
3
  export interface CloudOptions {
3
4
  /** "postgres://...", "pglite:<folder>", or "memory". Default "pglite:./.wizelens-cloud". */
@@ -18,16 +19,37 @@ export interface CloudOptions {
18
19
  alertIntervalMs?: number;
19
20
  /** Let alert webhooks call private and local addresses, and plain http. For self-hosting on a private network. Default false. */
20
21
  allowPrivateWebhooks?: boolean;
22
+ /** Outgoing mail: "smtp://user:pass@host:587" (STARTTLS) or "smtps://user:pass@host:465". Without it, mail is written to the log. */
23
+ smtpUrl?: string;
24
+ /** Outgoing mail through the Resend API (a `re_` key). Used in place of `smtpUrl`. */
25
+ resendApiKey?: string;
26
+ /** The `whsec_` signing secret of the Resend webhook, so bounces and spam complaints reach the suppression list. */
27
+ resendWebhookSecret?: string;
28
+ /** The sender of every email, for example "WizeLens <alerts@example.com>". */
29
+ mailFrom?: string;
30
+ /** Take the client address from X-Forwarded-For (behind a proxy you run). Default false. */
31
+ trustProxy?: boolean;
32
+ /** Signs sessions and encrypts 2FA secrets: 32+ random characters, or "2:new,1:old" to rotate. Required with Postgres. */
33
+ authSecret?: string;
34
+ /** The one-time token the first account needs. Default: a new random one, printed at start while no account exists. */
35
+ setupToken?: string;
36
+ /** Refuse passwords found in known breaches (sends a 5-character hash prefix to api.pwnedpasswords.com). Default: on with Postgres. */
37
+ checkPwnedPasswords?: boolean;
38
+ /** Identity provider origins for OIDC single sign-on, for example https://acme.okta.com. The cloud fetches OIDC settings only from these. */
39
+ ssoOrigins?: string[];
21
40
  redactKeys?: string[];
22
41
  uiDir?: string;
23
- /** For tests: the clock and the HTTP client alerts use. */
42
+ /** For tests: the clock, the HTTP client alerts use, and a mail sender in place of SMTP. */
24
43
  now?: () => number;
25
44
  fetch?: typeof fetch;
45
+ sendMail?: (mail: Mail) => Promise<void>;
46
+ /** For tests: false keeps background jobs off, so a test runs them itself. Default true. */
47
+ jobs?: boolean;
26
48
  }
27
- export type CloudConfig = Required<Omit<CloudOptions, "uiDir" | "publicUrl">> & {
28
- uiDir: string | undefined;
29
- publicUrl: string | undefined;
49
+ type Optional = "uiDir" | "publicUrl" | "smtpUrl" | "resendApiKey" | "resendWebhookSecret" | "sendMail" | "authSecret" | "setupToken" | "checkPwnedPasswords";
50
+ export type CloudConfig = Required<Omit<CloudOptions, Optional>> & Pick<CloudOptions, Optional> & {
30
51
  secureCookies: boolean;
31
52
  };
32
53
  export declare function resolveConfig(o?: CloudOptions): CloudConfig;
54
+ export {};
33
55
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6GAA6G;IAC7G,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iIAAiI;IACjI,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,WAAW,CAAC,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC;AAErK,wBAAgB,aAAa,CAAC,CAAC,GAAE,YAAiB,GAAG,WAAW,CAiB/D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6GAA6G;IAC7G,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iIAAiI;IACjI,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qIAAqI;IACrI,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oHAAoH;IACpH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0HAA0H;IAC1H,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uHAAuH;IACvH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uIAAuI;IACvI,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,6IAA6I;IAC7I,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,4FAA4F;IAC5F,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,KAAK,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,cAAc,GAAG,qBAAqB,GAAG,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,qBAAqB,CAAC;AAC9J,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC;AAqB7H,wBAAgB,aAAa,CAAC,CAAC,GAAE,YAAiB,GAAG,WAAW,CA6B/D"}
package/dist/config.js CHANGED
@@ -1,5 +1,29 @@
1
1
  import { DEFAULT_REDACT_KEYS } from "@wizeworks/lens-core";
2
+ /** Mail links must use the public address: a link built from the request's Host header could point anywhere. */
3
+ function checkMail(o) {
4
+ if ((o.smtpUrl || o.resendApiKey) && !o.publicUrl)
5
+ throw new Error("Sending mail needs --public-url, so links in emails point at this cloud.");
6
+ if (o.smtpUrl && o.resendApiKey)
7
+ throw new Error("Pick one mail service: --resend-api-key or --smtp-url, not both.");
8
+ if (o.smtpUrl && !/^smtps?:\/\//.test(o.smtpUrl))
9
+ throw new Error("--smtp-url must start with smtp:// or smtps://.");
10
+ if (o.resendApiKey && !o.resendApiKey.startsWith("re_"))
11
+ throw new Error("--resend-api-key must be a Resend API key (it starts with re_).");
12
+ if (o.resendWebhookSecret && !o.resendWebhookSecret.startsWith("whsec_"))
13
+ throw new Error("--resend-webhook-secret must start with whsec_.");
14
+ }
15
+ /** Each SSO origin as a bare origin: https, or http on this machine only. */
16
+ function ssoOriginsOf(list) {
17
+ return list.map((raw) => {
18
+ const url = URL.canParse(raw) ? new URL(raw) : null;
19
+ const local = url?.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
20
+ if (!url || (url.protocol !== "https:" && !local) || raw.includes("*"))
21
+ throw new Error(`--sso-origins: "${raw}" must be an https:// origin, like https://acme.okta.com.`);
22
+ return url.origin;
23
+ });
24
+ }
2
25
  export function resolveConfig(o = {}) {
26
+ checkMail(o);
3
27
  return {
4
28
  database: o.database ?? "pglite:./.wizelens-cloud",
5
29
  port: o.port ?? 5970,
@@ -10,6 +34,17 @@ export function resolveConfig(o = {}) {
10
34
  ingestPerMinute: o.ingestPerMinute ?? 1200,
11
35
  alertIntervalMs: o.alertIntervalMs ?? 60_000,
12
36
  allowPrivateWebhooks: o.allowPrivateWebhooks ?? false,
37
+ smtpUrl: o.smtpUrl,
38
+ resendApiKey: o.resendApiKey,
39
+ resendWebhookSecret: o.resendWebhookSecret,
40
+ mailFrom: o.mailFrom ?? `WizeLens <no-reply@${o.publicUrl ? new URL(o.publicUrl).hostname : "localhost"}>`,
41
+ trustProxy: o.trustProxy ?? false,
42
+ sendMail: o.sendMail,
43
+ jobs: o.jobs ?? true,
44
+ authSecret: o.authSecret,
45
+ setupToken: o.setupToken,
46
+ checkPwnedPasswords: o.checkPwnedPasswords,
47
+ ssoOrigins: ssoOriginsOf(o.ssoOrigins ?? []),
13
48
  redactKeys: o.redactKeys ?? [...DEFAULT_REDACT_KEYS],
14
49
  uiDir: o.uiDir,
15
50
  now: o.now ?? Date.now,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAgC3D,MAAM,UAAU,aAAa,CAAC,CAAC,GAAiB,EAAE;IAChD,OAAO;QACL,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,0BAA0B;QAClD,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI;QACpB,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,SAAS;QACzB,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3C,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,QAAQ;QAC5B,aAAa,EAAE,CAAC,CAAC,aAAa,IAAI,EAAE;QACpC,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,IAAI;QAC1C,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,MAAM;QAC5C,oBAAoB,EAAE,CAAC,CAAC,oBAAoB,IAAI,KAAK;QACrD,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,CAAC,GAAG,mBAAmB,CAAC;QACpD,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;QACtB,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK;QACvB,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,UAAU,CAAC;KACrD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/db/db.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { BetterAuthOptions } from "better-auth";
1
2
  /** Something that runs SQL: the database, or one transaction. Postgres syntax, `$1` parameters. */
2
3
  export interface Queryable {
3
4
  query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
@@ -5,14 +6,16 @@ export interface Queryable {
5
6
  export interface Db extends Queryable {
6
7
  /** Run `fn` in one transaction. It rolls back when `fn` throws. */
7
8
  tx<T>(fn: (q: Queryable) => Promise<T>): Promise<T>;
8
- /** Run several statements without parameters (migrations). */
9
+ /** Run several statements without parameters (migrations, before anything else uses the database). */
9
10
  exec(sql: string): Promise<void>;
10
11
  close(): Promise<void>;
11
12
  readonly kind: "postgres" | "pglite";
13
+ /** The same database, for Better Auth. */
14
+ readonly authDatabase: NonNullable<BetterAuthOptions["database"]>;
12
15
  }
13
16
  /** A managed Postgres, from a `postgres://` URL. */
14
17
  export declare function openPostgres(url: string): Promise<Db>;
15
- /** Postgres in this process (PGlite): a folder on disk, or memory when `dir` is undefined. For tests and trials. */
18
+ /** PGlite (a folder, or memory), for tests and trials. All queries share one Kysely queue with Better Auth: PGlite has one connection. */
16
19
  export declare function openPglite(dir?: string): Promise<Db>;
17
20
  /** "postgres://..." opens Postgres; "pglite:<folder>" or "memory" opens PGlite. */
18
21
  export declare function openDb(url: string): Promise<Db>;
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAGA,mGAAmG;AACnG,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,EAAG,SAAQ,SAAS;IACnC,mEAAmE;IACnE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpD,8DAA8D;IAC9D,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;CACtC;AAMD,oDAAoD;AACpD,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAwB3D;AAED,oHAAoH;AACpH,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAa1D;AAED,mFAAmF;AACnF,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAK/C"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,mGAAmG;AACnG,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,EAAG,SAAQ,SAAS;IACnC,mEAAmE;IACnE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpD,sGAAsG;IACtG,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IACrC,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;CACnE;AAMD,oDAAoD;AACpD,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAyB3D;AAED,0IAA0I;AAC1I,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAmB1D;AAED,mFAAmF;AACnF,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAK/C"}
package/dist/db/db.js CHANGED
@@ -8,6 +8,7 @@ export async function openPostgres(url) {
8
8
  const pool = new pg.Pool({ connectionString: url, max: 10 });
9
9
  return {
10
10
  kind: "postgres",
11
+ authDatabase: pool,
11
12
  ...wrap(pool),
12
13
  async tx(fn) {
13
14
  const client = await pool.connect();
@@ -29,19 +30,25 @@ export async function openPostgres(url) {
29
30
  close: () => pool.end(),
30
31
  };
31
32
  }
32
- /** Postgres in this process (PGlite): a folder on disk, or memory when `dir` is undefined. For tests and trials. */
33
+ /** PGlite (a folder, or memory), for tests and trials. All queries share one Kysely queue with Better Auth: PGlite has one connection. */
33
34
  export async function openPglite(dir) {
34
- const { PGlite: Lite } = await import("@electric-sql/pglite");
35
- const lite = await Lite.create(dir);
36
- const q = (runner) => ({
37
- query: async (sql, params = []) => (await runner.query(sql, params)).rows,
35
+ const [{ PGlite }, { Kysely, CompiledQuery }, { PGliteDialect }] = await Promise.all([
36
+ import("@electric-sql/pglite"),
37
+ import("kysely"),
38
+ import("kysely-pglite-dialect"),
39
+ ]);
40
+ const lite = await PGlite.create(dir);
41
+ const kysely = new Kysely({ dialect: new PGliteDialect(lite) });
42
+ const run = (k) => ({
43
+ query: async (sql, params = []) => (await k.executeQuery(CompiledQuery.raw(sql, params))).rows,
38
44
  });
39
45
  return {
40
46
  kind: "pglite",
41
- ...q(lite),
42
- tx: (fn) => lite.transaction((t) => fn(q(t))),
47
+ authDatabase: { db: kysely, type: "postgres", transaction: true },
48
+ ...run(kysely),
49
+ tx: (fn) => kysely.transaction().execute((t) => fn(run(t))),
43
50
  exec: async (sql) => void (await lite.exec(sql)),
44
- close: () => lite.close(),
51
+ close: () => kysely.destroy(),
45
52
  };
46
53
  }
47
54
  /** "postgres://..." opens Postgres; "pglite:<folder>" or "memory" opens PGlite. */
package/dist/db/db.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { Db } from "./db.js";
2
2
  /** Apply every migration not applied yet, each in its own transaction. Returns how many ran. */
3
3
  export declare function migrate(db: Db): Promise<number>;
4
- /** One migration as single statements, so it runs inside a transaction on every driver. */
5
- export declare const splitStatements: (sql: string) => string[];
4
+ /** One migration as single statements, so it runs inside a transaction on every driver. A `$$ … $$` body (a function) stays whole. */
5
+ export declare function splitStatements(sql: string): string[];
6
6
  //# sourceMappingURL=migrate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAGlC,gGAAgG;AAChG,wBAAsB,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAcrD;AAED,2FAA2F;AAC3F,eAAO,MAAM,eAAe,QAAS,MAAM,KAAG,MAAM,EAIhC,CAAC"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAGlC,gGAAgG;AAChG,wBAAsB,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAcrD;AAED,sIAAsI;AACtI,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAWrD"}
@@ -17,9 +17,19 @@ export async function migrate(db) {
17
17
  }
18
18
  return ran;
19
19
  }
20
- /** One migration as single statements, so it runs inside a transaction on every driver. */
21
- export const splitStatements = (sql) => sql
22
- .split(/;\s*\n/)
23
- .map((s) => s.trim().replace(/;$/, ""))
24
- .filter(Boolean);
20
+ /** One migration as single statements, so it runs inside a transaction on every driver. A `$$ … $$` body (a function) stays whole. */
21
+ export function splitStatements(sql) {
22
+ const out = [];
23
+ let current = "";
24
+ let quoted = false;
25
+ for (const line of sql.split("\n")) {
26
+ current += `${line}\n`;
27
+ if ((line.match(/\$\$/g)?.length ?? 0) % 2 === 1)
28
+ quoted = !quoted;
29
+ if (!quoted && /;\s*$/.test(line))
30
+ (out.push(current), (current = ""));
31
+ }
32
+ out.push(current);
33
+ return out.map((s) => s.trim().replace(/;$/, "")).filter(Boolean);
34
+ }
25
35
  //# sourceMappingURL=migrate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3D,gGAAgG;AAChG,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAAM;IAClC,MAAM,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAsB,yCAAyC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7H,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QAChC,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACtB,KAAK,MAAM,SAAS,IAAI,eAAe,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;gBAAE,MAAM,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClF,MAAM,CAAC,CAAC,KAAK,CAAC,uEAAuE,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAChH,CAAC,CAAC,CAAC;QACH,GAAG,EAAE,CAAC;IACR,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAW,EAAY,EAAE,CACvD,GAAG;KACA,KAAK,CAAC,QAAQ,CAAC;KACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;KACtC,MAAM,CAAC,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3D,gGAAgG;AAChG,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAAM;IAClC,MAAM,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAsB,yCAAyC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7H,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QAChC,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACtB,KAAK,MAAM,SAAS,IAAI,eAAe,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;gBAAE,MAAM,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClF,MAAM,CAAC,CAAC,KAAK,CAAC,uEAAuE,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAChH,CAAC,CAAC,CAAC;QACH,GAAG,EAAE,CAAC;IACR,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sIAAsI;AACtI,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,GAAG,IAAI,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,MAAM,GAAG,CAAC,MAAM,CAAC;QACnE,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const AUTH_MIGRATION = "\nALTER TABLE users ADD COLUMN email_verified BOOLEAN NOT NULL DEFAULT FALSE;\nALTER TABLE users ADD COLUMN image TEXT;\nALTER TABLE users ADD COLUMN created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP;\nALTER TABLE users ADD COLUMN updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP;\nALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN DEFAULT FALSE;\nUPDATE users SET email = lower(email), email_verified = TRUE, created_at = to_timestamp(created_ms / 1000.0), updated_at = to_timestamp(created_ms / 1000.0);\nCREATE TABLE accounts (\n id TEXT NOT NULL PRIMARY KEY, account_id TEXT NOT NULL, provider_id TEXT NOT NULL,\n user_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE, access_token TEXT, refresh_token TEXT, id_token TEXT,\n access_token_expires_at TIMESTAMPTZ, refresh_token_expires_at TIMESTAMPTZ, scope TEXT, password TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL\n);\nCREATE INDEX accounts_user_id_idx ON accounts (user_id);\nCREATE UNIQUE INDEX ux_accounts_provider_account ON accounts (provider_id, account_id);\nINSERT INTO accounts (id, account_id, provider_id, user_id, password, created_at, updated_at)\n SELECT gen_random_uuid()::text, id, 'credential', id, password_hash, created_at, CURRENT_TIMESTAMP FROM users;\nALTER TABLE users DROP COLUMN password_hash;\nALTER TABLE users DROP COLUMN created_ms;\nDROP TABLE sessions;\nCREATE TABLE sessions (\n id TEXT NOT NULL PRIMARY KEY, expires_at TIMESTAMPTZ NOT NULL, token TEXT NOT NULL UNIQUE,\n created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL, ip_address TEXT, user_agent TEXT,\n user_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE, active_org_id TEXT\n);\nCREATE INDEX sessions_user_id_idx ON sessions (user_id);\nALTER TABLE orgs ADD COLUMN logo TEXT;\nALTER TABLE orgs ADD COLUMN metadata TEXT;\nALTER TABLE orgs ADD COLUMN created_at TIMESTAMPTZ;\nUPDATE orgs SET created_at = to_timestamp(created_ms / 1000.0);\nALTER TABLE orgs ALTER COLUMN created_at SET NOT NULL;\nALTER TABLE orgs DROP COLUMN created_ms;\nALTER TABLE members ADD COLUMN id TEXT;\nALTER TABLE members ADD COLUMN created_at TIMESTAMPTZ;\nUPDATE members SET id = gen_random_uuid()::text, created_at = to_timestamp(created_ms / 1000.0);\nALTER TABLE members DROP CONSTRAINT members_pkey;\nALTER TABLE members ALTER COLUMN id SET NOT NULL;\nALTER TABLE members ALTER COLUMN created_at SET NOT NULL;\nALTER TABLE members DROP COLUMN created_ms;\nALTER TABLE members ADD PRIMARY KEY (id);\nCREATE UNIQUE INDEX ux_members_org_user ON members (org_id, user_id);\nCREATE INDEX members_org_id_idx ON members (org_id);\nCREATE INDEX members_user_id_idx ON members (user_id);\nDROP INDEX ix_members_user;\nALTER TABLE invites RENAME TO legacy_invites;\nCREATE TABLE invites (\n id TEXT NOT NULL PRIMARY KEY, org_id TEXT NOT NULL REFERENCES orgs (id) ON DELETE CASCADE, email TEXT NOT NULL, role TEXT,\n status TEXT NOT NULL, expires_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,\n inviter_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE\n);\nCREATE INDEX invites_org_id_idx ON invites (org_id);\nCREATE INDEX invites_email_idx ON invites (email);\nINSERT INTO invites (id, org_id, email, role, status, expires_at, created_at, inviter_id)\n SELECT gen_random_uuid()::text, i.org_id, lower(i.email), i.role, 'pending', to_timestamp(i.expires_ms / 1000.0), to_timestamp(i.created_ms / 1000.0),\n COALESCE((SELECT u.id FROM users u WHERE u.id = i.created_by),\n (SELECT m.user_id FROM members m WHERE m.org_id = i.org_id AND m.role = 'owner' ORDER BY m.created_at LIMIT 1))\n FROM legacy_invites i\n WHERE i.accepted_ms IS NULL AND i.expires_ms > extract(epoch FROM CURRENT_TIMESTAMP) * 1000\n AND EXISTS (SELECT 1 FROM members m WHERE m.org_id = i.org_id AND m.role = 'owner');\nDROP TABLE legacy_invites;\nCREATE TABLE verifications (\n id TEXT NOT NULL PRIMARY KEY, identifier TEXT NOT NULL, value TEXT NOT NULL, expires_at TIMESTAMPTZ NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\nCREATE INDEX verifications_identifier_idx ON verifications (identifier);\nCREATE TABLE two_factors (\n id TEXT NOT NULL PRIMARY KEY, secret TEXT NOT NULL, backup_codes TEXT NOT NULL, user_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE,\n verified BOOLEAN, failed_verification_count INTEGER, locked_until TIMESTAMPTZ\n);\nCREATE INDEX two_factors_secret_idx ON two_factors (secret);\nCREATE INDEX two_factors_user_id_idx ON two_factors (user_id);\nCREATE TABLE passkeys (\n id TEXT NOT NULL PRIMARY KEY, name TEXT, public_key TEXT NOT NULL, user_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE,\n credential_id TEXT NOT NULL, counter INTEGER NOT NULL, device_type TEXT NOT NULL, backed_up BOOLEAN NOT NULL, transports TEXT,\n created_at TIMESTAMPTZ, aaguid TEXT\n);\nCREATE INDEX passkeys_user_id_idx ON passkeys (user_id);\nCREATE INDEX passkeys_credential_id_idx ON passkeys (credential_id);\nCREATE TABLE rate_limits (id TEXT NOT NULL PRIMARY KEY, key TEXT NOT NULL UNIQUE, count INTEGER NOT NULL, last_request BIGINT NOT NULL);\nCREATE TABLE cloud_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);\nCREATE FUNCTION wl_org_keeps_owner() RETURNS trigger LANGUAGE plpgsql AS $$\nDECLARE oid TEXT := COALESCE(OLD.org_id, NEW.org_id);\nBEGIN\n IF EXISTS (SELECT 1 FROM orgs WHERE id = oid) AND NOT EXISTS (SELECT 1 FROM members WHERE org_id = oid AND role = 'owner') THEN\n RAISE EXCEPTION 'organization % must keep at least one owner', oid USING ERRCODE = 'check_violation';\n END IF;\n RETURN NULL;\nEND $$;\nCREATE CONSTRAINT TRIGGER members_keep_owner AFTER UPDATE OR DELETE ON members\n DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION wl_org_keeps_owner();\n";
2
+ //# sourceMappingURL=schema-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-auth.d.ts","sourceRoot":"","sources":["../../src/db/schema-auth.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc,8xLA0F1B,CAAC"}