@wopr-network/platform-core 0.1.0

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 (694) hide show
  1. package/biome.json +61 -0
  2. package/dist/admin/admin-audit-log-repository.d.ts +33 -0
  3. package/dist/admin/admin-audit-log-repository.js +102 -0
  4. package/dist/admin/audit-log.d.ts +49 -0
  5. package/dist/admin/audit-log.js +63 -0
  6. package/dist/admin/index.d.ts +6 -0
  7. package/dist/admin/index.js +3 -0
  8. package/dist/admin/role-store.d.ts +37 -0
  9. package/dist/admin/role-store.js +106 -0
  10. package/dist/auth/api-key-repository.d.ts +11 -0
  11. package/dist/auth/api-key-repository.js +33 -0
  12. package/dist/auth/api-key-repository.test.d.ts +1 -0
  13. package/dist/auth/api-key-repository.test.js +46 -0
  14. package/dist/auth/auth.test.d.ts +1 -0
  15. package/dist/auth/auth.test.js +140 -0
  16. package/dist/auth/better-auth.d.ts +42 -0
  17. package/dist/auth/better-auth.js +196 -0
  18. package/dist/auth/index.d.ts +186 -0
  19. package/dist/auth/index.js +422 -0
  20. package/dist/auth/login-history-repository.d.ts +14 -0
  21. package/dist/auth/login-history-repository.js +15 -0
  22. package/dist/auth/login-history-repository.test.d.ts +1 -0
  23. package/dist/auth/login-history-repository.test.js +47 -0
  24. package/dist/auth/middleware.d.ts +55 -0
  25. package/dist/auth/middleware.js +101 -0
  26. package/dist/auth/middleware.test.d.ts +1 -0
  27. package/dist/auth/middleware.test.js +213 -0
  28. package/dist/auth/scoped-tokens.test.d.ts +1 -0
  29. package/dist/auth/scoped-tokens.test.js +306 -0
  30. package/dist/auth/tenant-access.test.d.ts +1 -0
  31. package/dist/auth/tenant-access.test.js +62 -0
  32. package/dist/auth/user-creator.d.ts +9 -0
  33. package/dist/auth/user-creator.js +47 -0
  34. package/dist/auth/user-creator.test.d.ts +1 -0
  35. package/dist/auth/user-creator.test.js +78 -0
  36. package/dist/auth/user-role-repository.d.ts +31 -0
  37. package/dist/auth/user-role-repository.js +53 -0
  38. package/dist/auth/user-role-repository.test.d.ts +1 -0
  39. package/dist/auth/user-role-repository.test.js +122 -0
  40. package/dist/billing/drizzle-webhook-seen-repository.d.ts +10 -0
  41. package/dist/billing/drizzle-webhook-seen-repository.js +28 -0
  42. package/dist/billing/index.d.ts +7 -0
  43. package/dist/billing/index.js +7 -0
  44. package/dist/billing/payment-processor.d.ts +127 -0
  45. package/dist/billing/payment-processor.js +8 -0
  46. package/dist/billing/payment-processor.test.d.ts +1 -0
  47. package/dist/billing/payment-processor.test.js +71 -0
  48. package/dist/billing/payram/cents-credits-boundary.test.d.ts +1 -0
  49. package/dist/billing/payram/cents-credits-boundary.test.js +75 -0
  50. package/dist/billing/payram/charge-store.d.ts +41 -0
  51. package/dist/billing/payram/charge-store.js +72 -0
  52. package/dist/billing/payram/charge-store.test.d.ts +1 -0
  53. package/dist/billing/payram/charge-store.test.js +64 -0
  54. package/dist/billing/payram/checkout.d.ts +15 -0
  55. package/dist/billing/payram/checkout.js +24 -0
  56. package/dist/billing/payram/checkout.test.d.ts +1 -0
  57. package/dist/billing/payram/checkout.test.js +74 -0
  58. package/dist/billing/payram/client.d.ts +7 -0
  59. package/dist/billing/payram/client.js +15 -0
  60. package/dist/billing/payram/client.test.d.ts +1 -0
  61. package/dist/billing/payram/client.test.js +52 -0
  62. package/dist/billing/payram/index.d.ts +8 -0
  63. package/dist/billing/payram/index.js +4 -0
  64. package/dist/billing/payram/types.d.ts +40 -0
  65. package/dist/billing/payram/types.js +1 -0
  66. package/dist/billing/payram/webhook.d.ts +19 -0
  67. package/dist/billing/payram/webhook.js +67 -0
  68. package/dist/billing/payram/webhook.test.d.ts +7 -0
  69. package/dist/billing/payram/webhook.test.js +248 -0
  70. package/dist/billing/stripe/cents-credits-boundary.test.d.ts +1 -0
  71. package/dist/billing/stripe/cents-credits-boundary.test.js +62 -0
  72. package/dist/billing/stripe/checkout.d.ts +20 -0
  73. package/dist/billing/stripe/checkout.js +63 -0
  74. package/dist/billing/stripe/checkout.test.d.ts +1 -0
  75. package/dist/billing/stripe/checkout.test.js +148 -0
  76. package/dist/billing/stripe/client.d.ts +14 -0
  77. package/dist/billing/stripe/client.js +33 -0
  78. package/dist/billing/stripe/client.test.d.ts +1 -0
  79. package/dist/billing/stripe/client.test.js +58 -0
  80. package/dist/billing/stripe/credit-prices.d.ts +63 -0
  81. package/dist/billing/stripe/credit-prices.js +81 -0
  82. package/dist/billing/stripe/credit-prices.test.d.ts +1 -0
  83. package/dist/billing/stripe/credit-prices.test.js +87 -0
  84. package/dist/billing/stripe/index.d.ts +14 -0
  85. package/dist/billing/stripe/index.js +8 -0
  86. package/dist/billing/stripe/payment-methods-detach-all.test.d.ts +1 -0
  87. package/dist/billing/stripe/payment-methods-detach-all.test.js +40 -0
  88. package/dist/billing/stripe/payment-methods.d.ts +25 -0
  89. package/dist/billing/stripe/payment-methods.js +53 -0
  90. package/dist/billing/stripe/payment-methods.test.d.ts +1 -0
  91. package/dist/billing/stripe/payment-methods.test.js +122 -0
  92. package/dist/billing/stripe/portal.d.ts +10 -0
  93. package/dist/billing/stripe/portal.js +16 -0
  94. package/dist/billing/stripe/portal.test.d.ts +1 -0
  95. package/dist/billing/stripe/portal.test.js +48 -0
  96. package/dist/billing/stripe/setup-intent.d.ts +16 -0
  97. package/dist/billing/stripe/setup-intent.js +22 -0
  98. package/dist/billing/stripe/setup-intent.test.d.ts +1 -0
  99. package/dist/billing/stripe/setup-intent.test.js +58 -0
  100. package/dist/billing/stripe/stripe-payment-processor.d.ts +49 -0
  101. package/dist/billing/stripe/stripe-payment-processor.js +166 -0
  102. package/dist/billing/stripe/stripe-payment-processor.test.d.ts +1 -0
  103. package/dist/billing/stripe/stripe-payment-processor.test.js +413 -0
  104. package/dist/billing/stripe/tenant-store.d.ts +56 -0
  105. package/dist/billing/stripe/tenant-store.js +119 -0
  106. package/dist/billing/stripe/tenant-store.test.d.ts +1 -0
  107. package/dist/billing/stripe/tenant-store.test.js +97 -0
  108. package/dist/billing/stripe/types.d.ts +49 -0
  109. package/dist/billing/stripe/types.js +1 -0
  110. package/dist/billing/webhook-seen-repository.d.ts +14 -0
  111. package/dist/billing/webhook-seen-repository.js +13 -0
  112. package/dist/config/billing-env.test.d.ts +1 -0
  113. package/dist/config/billing-env.test.js +48 -0
  114. package/dist/config/index.d.ts +46 -0
  115. package/dist/config/index.js +38 -0
  116. package/dist/config/logger.d.ts +2 -0
  117. package/dist/config/logger.js +11 -0
  118. package/dist/config/provider-endpoints.d.ts +6 -0
  119. package/dist/config/provider-endpoints.js +12 -0
  120. package/dist/credits/auto-topup-charge.d.ts +27 -0
  121. package/dist/credits/auto-topup-charge.js +139 -0
  122. package/dist/credits/auto-topup-charge.test.d.ts +1 -0
  123. package/dist/credits/auto-topup-charge.test.js +242 -0
  124. package/dist/credits/auto-topup-event-log-repository.d.ts +16 -0
  125. package/dist/credits/auto-topup-event-log-repository.js +18 -0
  126. package/dist/credits/auto-topup-event-log-repository.test.d.ts +1 -0
  127. package/dist/credits/auto-topup-event-log-repository.test.js +83 -0
  128. package/dist/credits/auto-topup-schedule.d.ts +27 -0
  129. package/dist/credits/auto-topup-schedule.js +66 -0
  130. package/dist/credits/auto-topup-schedule.test.d.ts +1 -0
  131. package/dist/credits/auto-topup-schedule.test.js +145 -0
  132. package/dist/credits/auto-topup-settings-repository.d.ts +54 -0
  133. package/dist/credits/auto-topup-settings-repository.js +184 -0
  134. package/dist/credits/auto-topup-settings-repository.test.d.ts +1 -0
  135. package/dist/credits/auto-topup-settings-repository.test.js +104 -0
  136. package/dist/credits/auto-topup-usage.d.ts +22 -0
  137. package/dist/credits/auto-topup-usage.js +56 -0
  138. package/dist/credits/auto-topup-usage.test.d.ts +1 -0
  139. package/dist/credits/auto-topup-usage.test.js +181 -0
  140. package/dist/credits/credit-expiry-cron.d.ts +19 -0
  141. package/dist/credits/credit-expiry-cron.js +50 -0
  142. package/dist/credits/credit-expiry-cron.test.d.ts +1 -0
  143. package/dist/credits/credit-expiry-cron.test.js +67 -0
  144. package/dist/credits/credit-ledger-extra.test.d.ts +1 -0
  145. package/dist/credits/credit-ledger-extra.test.js +40 -0
  146. package/dist/credits/credit-ledger.bench.d.ts +1 -0
  147. package/dist/credits/credit-ledger.bench.js +33 -0
  148. package/dist/credits/credit-ledger.d.ts +130 -0
  149. package/dist/credits/credit-ledger.js +293 -0
  150. package/dist/credits/credit-ledger.test.d.ts +4 -0
  151. package/dist/credits/credit-ledger.test.js +203 -0
  152. package/dist/credits/credit-transaction-repository.d.ts +17 -0
  153. package/dist/credits/credit-transaction-repository.js +35 -0
  154. package/dist/credits/credit-transaction-repository.test.d.ts +1 -0
  155. package/dist/credits/credit-transaction-repository.test.js +232 -0
  156. package/dist/credits/credit.d.ts +75 -0
  157. package/dist/credits/credit.js +139 -0
  158. package/dist/credits/credit.test.d.ts +1 -0
  159. package/dist/credits/credit.test.js +196 -0
  160. package/dist/credits/dividend-cron.d.ts +29 -0
  161. package/dist/credits/dividend-cron.js +88 -0
  162. package/dist/credits/dividend-cron.test.d.ts +1 -0
  163. package/dist/credits/dividend-cron.test.js +128 -0
  164. package/dist/credits/dividend-repository.d.ts +29 -0
  165. package/dist/credits/dividend-repository.js +126 -0
  166. package/dist/credits/dividend-repository.test.d.ts +1 -0
  167. package/dist/credits/dividend-repository.test.js +176 -0
  168. package/dist/credits/index.d.ts +9 -0
  169. package/dist/credits/index.js +5 -0
  170. package/dist/credits/repository-types.d.ts +29 -0
  171. package/dist/credits/repository-types.js +1 -0
  172. package/dist/credits/signup-grant.d.ts +12 -0
  173. package/dist/credits/signup-grant.js +35 -0
  174. package/dist/credits/signup-grant.test.d.ts +1 -0
  175. package/dist/credits/signup-grant.test.js +51 -0
  176. package/dist/credits/tenant-customer-repository.d.ts +30 -0
  177. package/dist/credits/tenant-customer-repository.js +5 -0
  178. package/dist/db/auth-user-repository.d.ts +46 -0
  179. package/dist/db/auth-user-repository.js +90 -0
  180. package/dist/db/credit-column.d.ts +27 -0
  181. package/dist/db/credit-column.js +13 -0
  182. package/dist/db/index.d.ts +14 -0
  183. package/dist/db/index.js +8 -0
  184. package/dist/db/schema/account-deletion-requests.d.ts +203 -0
  185. package/dist/db/schema/account-deletion-requests.js +36 -0
  186. package/dist/db/schema/account-export-requests.d.ts +148 -0
  187. package/dist/db/schema/account-export-requests.js +19 -0
  188. package/dist/db/schema/admin-audit.d.ts +194 -0
  189. package/dist/db/schema/admin-audit.js +21 -0
  190. package/dist/db/schema/admin-users.d.ts +177 -0
  191. package/dist/db/schema/admin-users.js +23 -0
  192. package/dist/db/schema/affiliate-fraud.d.ts +160 -0
  193. package/dist/db/schema/affiliate-fraud.js +18 -0
  194. package/dist/db/schema/affiliate.d.ts +277 -0
  195. package/dist/db/schema/affiliate.js +32 -0
  196. package/dist/db/schema/coupon-codes.d.ts +143 -0
  197. package/dist/db/schema/coupon-codes.js +17 -0
  198. package/dist/db/schema/credit-auto-topup-settings.d.ts +232 -0
  199. package/dist/db/schema/credit-auto-topup-settings.js +27 -0
  200. package/dist/db/schema/credit-auto-topup.d.ts +130 -0
  201. package/dist/db/schema/credit-auto-topup.js +21 -0
  202. package/dist/db/schema/credits.d.ts +283 -0
  203. package/dist/db/schema/credits.js +38 -0
  204. package/dist/db/schema/dividend-distributions.d.ts +130 -0
  205. package/dist/db/schema/dividend-distributions.js +19 -0
  206. package/dist/db/schema/email-notifications.d.ts +99 -0
  207. package/dist/db/schema/email-notifications.js +21 -0
  208. package/dist/db/schema/index.d.ts +33 -0
  209. package/dist/db/schema/index.js +33 -0
  210. package/dist/db/schema/meter-events.d.ts +599 -0
  211. package/dist/db/schema/meter-events.js +55 -0
  212. package/dist/db/schema/notification-preferences.d.ts +165 -0
  213. package/dist/db/schema/notification-preferences.js +18 -0
  214. package/dist/db/schema/notification-queue.d.ts +236 -0
  215. package/dist/db/schema/notification-queue.js +40 -0
  216. package/dist/db/schema/org-memberships.d.ts +63 -0
  217. package/dist/db/schema/org-memberships.js +15 -0
  218. package/dist/db/schema/organization-members.d.ts +235 -0
  219. package/dist/db/schema/organization-members.js +27 -0
  220. package/dist/db/schema/payram.d.ts +164 -0
  221. package/dist/db/schema/payram.js +21 -0
  222. package/dist/db/schema/platform-api-keys.d.ts +143 -0
  223. package/dist/db/schema/platform-api-keys.js +20 -0
  224. package/dist/db/schema/promotion-redemptions.d.ts +143 -0
  225. package/dist/db/schema/promotion-redemptions.js +18 -0
  226. package/dist/db/schema/promotions.d.ts +445 -0
  227. package/dist/db/schema/promotions.js +48 -0
  228. package/dist/db/schema/provider-credentials.d.ts +201 -0
  229. package/dist/db/schema/provider-credentials.js +36 -0
  230. package/dist/db/schema/rate-limit-entries.d.ts +75 -0
  231. package/dist/db/schema/rate-limit-entries.js +7 -0
  232. package/dist/db/schema/secret-audit-log.d.ts +109 -0
  233. package/dist/db/schema/secret-audit-log.js +15 -0
  234. package/dist/db/schema/session-usage.d.ts +194 -0
  235. package/dist/db/schema/session-usage.js +19 -0
  236. package/dist/db/schema/spending-limits.d.ts +92 -0
  237. package/dist/db/schema/spending-limits.js +8 -0
  238. package/dist/db/schema/tenant-addons.d.ts +58 -0
  239. package/dist/db/schema/tenant-addons.js +9 -0
  240. package/dist/db/schema/tenant-api-keys.d.ts +131 -0
  241. package/dist/db/schema/tenant-api-keys.js +21 -0
  242. package/dist/db/schema/tenant-capability-settings.d.ts +79 -0
  243. package/dist/db/schema/tenant-capability-settings.js +12 -0
  244. package/dist/db/schema/tenant-customers.d.ts +303 -0
  245. package/dist/db/schema/tenant-customers.js +25 -0
  246. package/dist/db/schema/tenants.d.ts +126 -0
  247. package/dist/db/schema/tenants.js +18 -0
  248. package/dist/db/schema/user-roles.d.ts +98 -0
  249. package/dist/db/schema/user-roles.js +18 -0
  250. package/dist/db/schema/webhook-seen-events.d.ts +58 -0
  251. package/dist/db/schema/webhook-seen-events.js +9 -0
  252. package/dist/email/billing-emails.d.ts +51 -0
  253. package/dist/email/billing-emails.js +163 -0
  254. package/dist/email/billing-emails.test.d.ts +1 -0
  255. package/dist/email/billing-emails.test.js +162 -0
  256. package/dist/email/client.d.ts +51 -0
  257. package/dist/email/client.js +102 -0
  258. package/dist/email/client.test.d.ts +1 -0
  259. package/dist/email/client.test.js +120 -0
  260. package/dist/email/drizzle-billing-email-repository.d.ts +21 -0
  261. package/dist/email/drizzle-billing-email-repository.js +36 -0
  262. package/dist/email/drizzle-billing-email-repository.test.d.ts +1 -0
  263. package/dist/email/drizzle-billing-email-repository.test.js +42 -0
  264. package/dist/email/index.d.ts +33 -0
  265. package/dist/email/index.js +22 -0
  266. package/dist/email/notification-preferences-store.d.ts +12 -0
  267. package/dist/email/notification-preferences-store.js +82 -0
  268. package/dist/email/notification-preferences-store.test.d.ts +1 -0
  269. package/dist/email/notification-preferences-store.test.js +86 -0
  270. package/dist/email/notification-queue-store.d.ts +25 -0
  271. package/dist/email/notification-queue-store.js +97 -0
  272. package/dist/email/notification-queue-store.test.d.ts +1 -0
  273. package/dist/email/notification-queue-store.test.js +177 -0
  274. package/dist/email/notification-repository-types.d.ts +70 -0
  275. package/dist/email/notification-repository-types.js +6 -0
  276. package/dist/email/notification-service.d.ts +41 -0
  277. package/dist/email/notification-service.js +196 -0
  278. package/dist/email/notification-service.test.d.ts +1 -0
  279. package/dist/email/notification-service.test.js +160 -0
  280. package/dist/email/notification-templates.d.ts +18 -0
  281. package/dist/email/notification-templates.js +574 -0
  282. package/dist/email/notification-templates.test.d.ts +1 -0
  283. package/dist/email/notification-templates.test.js +238 -0
  284. package/dist/email/notification-worker.d.ts +24 -0
  285. package/dist/email/notification-worker.js +109 -0
  286. package/dist/email/notification-worker.test.d.ts +1 -0
  287. package/dist/email/notification-worker.test.js +153 -0
  288. package/dist/email/require-verified.d.ts +25 -0
  289. package/dist/email/require-verified.js +52 -0
  290. package/dist/email/require-verified.test.d.ts +1 -0
  291. package/dist/email/require-verified.test.js +62 -0
  292. package/dist/email/resend-adapter.d.ts +47 -0
  293. package/dist/email/resend-adapter.js +137 -0
  294. package/dist/email/resend-adapter.test.d.ts +1 -0
  295. package/dist/email/resend-adapter.test.js +190 -0
  296. package/dist/email/templates.d.ts +22 -0
  297. package/dist/email/templates.js +359 -0
  298. package/dist/email/templates.test.d.ts +1 -0
  299. package/dist/email/templates.test.js +170 -0
  300. package/dist/email/verification.d.ts +42 -0
  301. package/dist/email/verification.js +83 -0
  302. package/dist/email/verification.test.d.ts +1 -0
  303. package/dist/email/verification.test.js +141 -0
  304. package/dist/index.d.ts +13 -0
  305. package/dist/index.js +23 -0
  306. package/dist/metering/aggregator.d.ts +54 -0
  307. package/dist/metering/aggregator.js +123 -0
  308. package/dist/metering/aggregator.test.d.ts +1 -0
  309. package/dist/metering/aggregator.test.js +179 -0
  310. package/dist/metering/dlq.d.ts +31 -0
  311. package/dist/metering/dlq.js +82 -0
  312. package/dist/metering/dlq.test.d.ts +1 -0
  313. package/dist/metering/dlq.test.js +117 -0
  314. package/dist/metering/drizzle-usage-summary-repository.d.ts +67 -0
  315. package/dist/metering/drizzle-usage-summary-repository.js +98 -0
  316. package/dist/metering/emitter.d.ts +66 -0
  317. package/dist/metering/emitter.js +185 -0
  318. package/dist/metering/emitter.test.d.ts +1 -0
  319. package/dist/metering/emitter.test.js +171 -0
  320. package/dist/metering/index.d.ts +11 -0
  321. package/dist/metering/index.js +5 -0
  322. package/dist/metering/load-test.bench.d.ts +1 -0
  323. package/dist/metering/load-test.bench.js +103 -0
  324. package/dist/metering/meter-event-repository.d.ts +33 -0
  325. package/dist/metering/meter-event-repository.js +58 -0
  326. package/dist/metering/meter-repositories.test.d.ts +1 -0
  327. package/dist/metering/meter-repositories.test.js +419 -0
  328. package/dist/metering/metering.test.d.ts +1 -0
  329. package/dist/metering/metering.test.js +1046 -0
  330. package/dist/metering/reconciliation-cron.d.ts +37 -0
  331. package/dist/metering/reconciliation-cron.js +85 -0
  332. package/dist/metering/reconciliation-cron.test.d.ts +1 -0
  333. package/dist/metering/reconciliation-cron.test.js +162 -0
  334. package/dist/metering/reconciliation-repository.d.ts +27 -0
  335. package/dist/metering/reconciliation-repository.js +43 -0
  336. package/dist/metering/reconciliation-repository.test.d.ts +1 -0
  337. package/dist/metering/reconciliation-repository.test.js +160 -0
  338. package/dist/metering/types.d.ts +88 -0
  339. package/dist/metering/types.js +1 -0
  340. package/dist/metering/wal.d.ts +49 -0
  341. package/dist/metering/wal.js +124 -0
  342. package/dist/metering/wal.test.d.ts +1 -0
  343. package/dist/metering/wal.test.js +175 -0
  344. package/dist/middleware/csrf.d.ts +24 -0
  345. package/dist/middleware/csrf.js +80 -0
  346. package/dist/middleware/csrf.test.d.ts +1 -0
  347. package/dist/middleware/csrf.test.js +152 -0
  348. package/dist/middleware/drizzle-rate-limit-repository.d.ts +9 -0
  349. package/dist/middleware/drizzle-rate-limit-repository.js +52 -0
  350. package/dist/middleware/drizzle-rate-limit-repository.test.d.ts +1 -0
  351. package/dist/middleware/drizzle-rate-limit-repository.test.js +74 -0
  352. package/dist/middleware/get-client-ip.d.ts +22 -0
  353. package/dist/middleware/get-client-ip.js +51 -0
  354. package/dist/middleware/get-client-ip.test.d.ts +1 -0
  355. package/dist/middleware/get-client-ip.test.js +40 -0
  356. package/dist/middleware/index.d.ts +5 -0
  357. package/dist/middleware/index.js +4 -0
  358. package/dist/middleware/rate-limit-repository.d.ts +19 -0
  359. package/dist/middleware/rate-limit-repository.js +1 -0
  360. package/dist/middleware/rate-limit.d.ts +57 -0
  361. package/dist/middleware/rate-limit.js +109 -0
  362. package/dist/middleware/rate-limit.test.d.ts +1 -0
  363. package/dist/middleware/rate-limit.test.js +247 -0
  364. package/dist/security/credential-vault/audit-repository.d.ts +27 -0
  365. package/dist/security/credential-vault/audit-repository.js +42 -0
  366. package/dist/security/credential-vault/audit-repository.test.d.ts +1 -0
  367. package/dist/security/credential-vault/audit-repository.test.js +78 -0
  368. package/dist/security/credential-vault/credential-repository.d.ts +94 -0
  369. package/dist/security/credential-vault/credential-repository.js +145 -0
  370. package/dist/security/credential-vault/credential-repository.test.d.ts +1 -0
  371. package/dist/security/credential-vault/credential-repository.test.js +206 -0
  372. package/dist/security/credential-vault/index.d.ts +12 -0
  373. package/dist/security/credential-vault/index.js +6 -0
  374. package/dist/security/credential-vault/key-rotation.d.ts +18 -0
  375. package/dist/security/credential-vault/key-rotation.js +52 -0
  376. package/dist/security/credential-vault/key-rotation.test.d.ts +1 -0
  377. package/dist/security/credential-vault/key-rotation.test.js +95 -0
  378. package/dist/security/credential-vault/migrate-plaintext.d.ts +15 -0
  379. package/dist/security/credential-vault/migrate-plaintext.js +80 -0
  380. package/dist/security/credential-vault/migrate-plaintext.test.d.ts +1 -0
  381. package/dist/security/credential-vault/migrate-plaintext.test.js +111 -0
  382. package/dist/security/credential-vault/migration-check.d.ts +15 -0
  383. package/dist/security/credential-vault/migration-check.js +71 -0
  384. package/dist/security/credential-vault/migration-check.test.d.ts +1 -0
  385. package/dist/security/credential-vault/migration-check.test.js +457 -0
  386. package/dist/security/credential-vault/store.d.ts +106 -0
  387. package/dist/security/credential-vault/store.js +181 -0
  388. package/dist/security/credential-vault/store.test.d.ts +1 -0
  389. package/dist/security/credential-vault/store.test.js +482 -0
  390. package/dist/security/encryption.d.ts +22 -0
  391. package/dist/security/encryption.js +53 -0
  392. package/dist/security/encryption.test.d.ts +1 -0
  393. package/dist/security/encryption.test.js +95 -0
  394. package/dist/security/host-validation.d.ts +11 -0
  395. package/dist/security/host-validation.js +108 -0
  396. package/dist/security/host-validation.test.d.ts +1 -0
  397. package/dist/security/host-validation.test.js +106 -0
  398. package/dist/security/index.d.ts +11 -0
  399. package/dist/security/index.js +11 -0
  400. package/dist/security/key-audit.d.ts +16 -0
  401. package/dist/security/key-audit.js +35 -0
  402. package/dist/security/key-audit.test.d.ts +1 -0
  403. package/dist/security/key-audit.test.js +50 -0
  404. package/dist/security/key-injection.d.ts +28 -0
  405. package/dist/security/key-injection.js +57 -0
  406. package/dist/security/key-injection.test.d.ts +1 -0
  407. package/dist/security/key-injection.test.js +97 -0
  408. package/dist/security/key-validation.d.ts +16 -0
  409. package/dist/security/key-validation.js +78 -0
  410. package/dist/security/key-validation.test.d.ts +1 -0
  411. package/dist/security/key-validation.test.js +87 -0
  412. package/dist/security/redirect-allowlist.d.ts +6 -0
  413. package/dist/security/redirect-allowlist.js +36 -0
  414. package/dist/security/redirect-allowlist.test.d.ts +1 -0
  415. package/dist/security/redirect-allowlist.test.js +55 -0
  416. package/dist/security/tenant-keys/capability-settings-store.d.ts +22 -0
  417. package/dist/security/tenant-keys/capability-settings-store.js +33 -0
  418. package/dist/security/tenant-keys/capability-settings-store.test.d.ts +1 -0
  419. package/dist/security/tenant-keys/capability-settings-store.test.js +77 -0
  420. package/dist/security/tenant-keys/index.d.ts +10 -0
  421. package/dist/security/tenant-keys/index.js +5 -0
  422. package/dist/security/tenant-keys/key-resolution-repository.d.ts +15 -0
  423. package/dist/security/tenant-keys/key-resolution-repository.js +18 -0
  424. package/dist/security/tenant-keys/key-resolution-repository.test.d.ts +1 -0
  425. package/dist/security/tenant-keys/key-resolution-repository.test.js +72 -0
  426. package/dist/security/tenant-keys/key-resolution.d.ts +39 -0
  427. package/dist/security/tenant-keys/key-resolution.js +59 -0
  428. package/dist/security/tenant-keys/key-resolution.test.d.ts +1 -0
  429. package/dist/security/tenant-keys/key-resolution.test.js +97 -0
  430. package/dist/security/tenant-keys/org-key-resolution.d.ts +30 -0
  431. package/dist/security/tenant-keys/org-key-resolution.js +50 -0
  432. package/dist/security/tenant-keys/org-key-resolution.test.d.ts +1 -0
  433. package/dist/security/tenant-keys/org-key-resolution.test.js +103 -0
  434. package/dist/security/tenant-keys/tenant-key-repository.d.ts +36 -0
  435. package/dist/security/tenant-keys/tenant-key-repository.js +96 -0
  436. package/dist/security/tenant-keys/tenant-key-repository.test.d.ts +1 -0
  437. package/dist/security/tenant-keys/tenant-key-repository.test.js +114 -0
  438. package/dist/security/types.d.ts +35 -0
  439. package/dist/security/types.js +15 -0
  440. package/dist/tenancy/drizzle-org-repository.d.ts +40 -0
  441. package/dist/tenancy/drizzle-org-repository.js +126 -0
  442. package/dist/tenancy/index.d.ts +6 -0
  443. package/dist/tenancy/index.js +3 -0
  444. package/dist/tenancy/org-member-repository.d.ts +57 -0
  445. package/dist/tenancy/org-member-repository.js +99 -0
  446. package/dist/tenancy/org-repository.test.d.ts +1 -0
  447. package/dist/tenancy/org-repository.test.js +143 -0
  448. package/dist/tenancy/org-service.d.ts +70 -0
  449. package/dist/tenancy/org-service.js +223 -0
  450. package/dist/tenancy/org-service.test.d.ts +1 -0
  451. package/dist/tenancy/org-service.test.js +550 -0
  452. package/dist/test/db.d.ts +33 -0
  453. package/dist/test/db.js +65 -0
  454. package/dist/trpc/index.d.ts +1 -0
  455. package/dist/trpc/index.js +1 -0
  456. package/dist/trpc/init.d.ts +49 -0
  457. package/dist/trpc/init.js +108 -0
  458. package/dist/trpc/init.test.d.ts +1 -0
  459. package/dist/trpc/init.test.js +154 -0
  460. package/drizzle/migrations/0000_slippery_mandrill.sql +559 -0
  461. package/drizzle/migrations/meta/0000_snapshot.json +4374 -0
  462. package/drizzle/migrations/meta/_journal.json +13 -0
  463. package/drizzle.config.ts +41 -0
  464. package/package.json +64 -0
  465. package/src/admin/admin-audit-log-repository.ts +135 -0
  466. package/src/admin/audit-log.ts +111 -0
  467. package/src/admin/index.ts +6 -0
  468. package/src/admin/role-store.ts +134 -0
  469. package/src/auth/api-key-repository.test.ts +63 -0
  470. package/src/auth/api-key-repository.ts +46 -0
  471. package/src/auth/auth.test.ts +166 -0
  472. package/src/auth/better-auth.ts +216 -0
  473. package/src/auth/index.ts +520 -0
  474. package/src/auth/login-history-repository.test.ts +54 -0
  475. package/src/auth/login-history-repository.ts +28 -0
  476. package/src/auth/middleware.test.ts +264 -0
  477. package/src/auth/middleware.ts +117 -0
  478. package/src/auth/scoped-tokens.test.ts +362 -0
  479. package/src/auth/tenant-access.test.ts +69 -0
  480. package/src/auth/user-creator.test.ts +98 -0
  481. package/src/auth/user-creator.ts +54 -0
  482. package/src/auth/user-role-repository.test.ts +149 -0
  483. package/src/auth/user-role-repository.ts +67 -0
  484. package/src/billing/drizzle-webhook-seen-repository.ts +34 -0
  485. package/src/billing/index.ts +22 -0
  486. package/src/billing/payment-processor.test.ts +93 -0
  487. package/src/billing/payment-processor.ts +150 -0
  488. package/src/billing/payram/cents-credits-boundary.test.ts +84 -0
  489. package/src/billing/payram/charge-store.test.ts +84 -0
  490. package/src/billing/payram/charge-store.ts +109 -0
  491. package/src/billing/payram/checkout.test.ts +99 -0
  492. package/src/billing/payram/checkout.ts +40 -0
  493. package/src/billing/payram/client.test.ts +62 -0
  494. package/src/billing/payram/client.ts +21 -0
  495. package/src/billing/payram/index.ts +14 -0
  496. package/src/billing/payram/types.ts +44 -0
  497. package/src/billing/payram/webhook.test.ts +318 -0
  498. package/src/billing/payram/webhook.ts +97 -0
  499. package/src/billing/stripe/cents-credits-boundary.test.ts +70 -0
  500. package/src/billing/stripe/checkout.test.ts +186 -0
  501. package/src/billing/stripe/checkout.ts +82 -0
  502. package/src/billing/stripe/client.test.ts +64 -0
  503. package/src/billing/stripe/client.ts +39 -0
  504. package/src/billing/stripe/credit-prices.test.ts +114 -0
  505. package/src/billing/stripe/credit-prices.ts +113 -0
  506. package/src/billing/stripe/index.ts +14 -0
  507. package/src/billing/stripe/payment-methods-detach-all.test.ts +53 -0
  508. package/src/billing/stripe/payment-methods.test.ts +157 -0
  509. package/src/billing/stripe/payment-methods.ts +76 -0
  510. package/src/billing/stripe/portal.test.ts +63 -0
  511. package/src/billing/stripe/portal.ts +25 -0
  512. package/src/billing/stripe/setup-intent.test.ts +78 -0
  513. package/src/billing/stripe/setup-intent.ts +34 -0
  514. package/src/billing/stripe/stripe-payment-processor.test.ts +517 -0
  515. package/src/billing/stripe/stripe-payment-processor.ts +255 -0
  516. package/src/billing/stripe/tenant-store.test.ts +124 -0
  517. package/src/billing/stripe/tenant-store.ts +151 -0
  518. package/src/billing/stripe/types.ts +53 -0
  519. package/src/billing/webhook-seen-repository.ts +24 -0
  520. package/src/config/billing-env.test.ts +54 -0
  521. package/src/config/index.ts +44 -0
  522. package/src/config/logger.ts +12 -0
  523. package/src/config/provider-endpoints.ts +14 -0
  524. package/src/credits/auto-topup-charge.test.ts +292 -0
  525. package/src/credits/auto-topup-charge.ts +171 -0
  526. package/src/credits/auto-topup-event-log-repository.test.ts +99 -0
  527. package/src/credits/auto-topup-event-log-repository.ts +30 -0
  528. package/src/credits/auto-topup-schedule.test.ts +179 -0
  529. package/src/credits/auto-topup-schedule.ts +93 -0
  530. package/src/credits/auto-topup-settings-repository.test.ts +123 -0
  531. package/src/credits/auto-topup-settings-repository.ts +245 -0
  532. package/src/credits/auto-topup-usage.test.ts +220 -0
  533. package/src/credits/auto-topup-usage.ts +68 -0
  534. package/src/credits/credit-expiry-cron.test.ts +125 -0
  535. package/src/credits/credit-expiry-cron.ts +76 -0
  536. package/src/credits/credit-ledger-extra.test.ts +57 -0
  537. package/src/credits/credit-ledger.bench.ts +56 -0
  538. package/src/credits/credit-ledger.test.ts +276 -0
  539. package/src/credits/credit-ledger.ts +450 -0
  540. package/src/credits/credit-transaction-repository.test.ts +274 -0
  541. package/src/credits/credit-transaction-repository.ts +62 -0
  542. package/src/credits/credit.test.ts +234 -0
  543. package/src/credits/credit.ts +160 -0
  544. package/src/credits/dividend-cron.test.ts +158 -0
  545. package/src/credits/dividend-cron.ts +127 -0
  546. package/src/credits/dividend-repository.test.ts +223 -0
  547. package/src/credits/dividend-repository.ts +182 -0
  548. package/src/credits/index.ts +25 -0
  549. package/src/credits/repository-types.ts +33 -0
  550. package/src/credits/signup-grant.test.ts +63 -0
  551. package/src/credits/signup-grant.ts +44 -0
  552. package/src/credits/tenant-customer-repository.ts +28 -0
  553. package/src/db/auth-user-repository.ts +124 -0
  554. package/src/db/credit-column.ts +17 -0
  555. package/src/db/index.ts +21 -0
  556. package/src/db/schema/account-deletion-requests.ts +41 -0
  557. package/src/db/schema/account-export-requests.ts +24 -0
  558. package/src/db/schema/admin-audit.ts +26 -0
  559. package/src/db/schema/admin-users.ts +31 -0
  560. package/src/db/schema/affiliate-fraud.ts +23 -0
  561. package/src/db/schema/affiliate.ts +38 -0
  562. package/src/db/schema/coupon-codes.ts +22 -0
  563. package/src/db/schema/credit-auto-topup-settings.ts +32 -0
  564. package/src/db/schema/credit-auto-topup.ts +26 -0
  565. package/src/db/schema/credits.ts +44 -0
  566. package/src/db/schema/dividend-distributions.ts +24 -0
  567. package/src/db/schema/email-notifications.ts +26 -0
  568. package/src/db/schema/index.ts +33 -0
  569. package/src/db/schema/meter-events.ts +70 -0
  570. package/src/db/schema/notification-preferences.ts +19 -0
  571. package/src/db/schema/notification-queue.ts +45 -0
  572. package/src/db/schema/org-memberships.ts +20 -0
  573. package/src/db/schema/organization-members.ts +37 -0
  574. package/src/db/schema/payram.ts +26 -0
  575. package/src/db/schema/platform-api-keys.ts +25 -0
  576. package/src/db/schema/promotion-redemptions.ts +23 -0
  577. package/src/db/schema/promotions.ts +57 -0
  578. package/src/db/schema/provider-credentials.ts +41 -0
  579. package/src/db/schema/rate-limit-entries.ts +12 -0
  580. package/src/db/schema/secret-audit-log.ts +20 -0
  581. package/src/db/schema/session-usage.ts +24 -0
  582. package/src/db/schema/spending-limits.ts +9 -0
  583. package/src/db/schema/tenant-addons.ts +14 -0
  584. package/src/db/schema/tenant-api-keys.ts +26 -0
  585. package/src/db/schema/tenant-capability-settings.ts +17 -0
  586. package/src/db/schema/tenant-customers.ts +35 -0
  587. package/src/db/schema/tenants.ts +23 -0
  588. package/src/db/schema/user-roles.ts +23 -0
  589. package/src/db/schema/webhook-seen-events.ts +14 -0
  590. package/src/email/billing-emails.test.ts +198 -0
  591. package/src/email/billing-emails.ts +211 -0
  592. package/src/email/client.test.ts +149 -0
  593. package/src/email/client.ts +137 -0
  594. package/src/email/drizzle-billing-email-repository.test.ts +52 -0
  595. package/src/email/drizzle-billing-email-repository.ts +59 -0
  596. package/src/email/index.ts +57 -0
  597. package/src/email/notification-preferences-store.test.ts +102 -0
  598. package/src/email/notification-preferences-store.ts +90 -0
  599. package/src/email/notification-queue-store.test.ts +215 -0
  600. package/src/email/notification-queue-store.ts +127 -0
  601. package/src/email/notification-repository-types.ts +101 -0
  602. package/src/email/notification-service.test.ts +178 -0
  603. package/src/email/notification-service.ts +265 -0
  604. package/src/email/notification-templates.test.ts +261 -0
  605. package/src/email/notification-templates.ts +727 -0
  606. package/src/email/notification-worker.test.ts +189 -0
  607. package/src/email/notification-worker.ts +133 -0
  608. package/src/email/require-verified.ts +65 -0
  609. package/src/email/resend-adapter.test.ts +253 -0
  610. package/src/email/resend-adapter.ts +157 -0
  611. package/src/email/templates.test.ts +217 -0
  612. package/src/email/templates.ts +469 -0
  613. package/src/email/verification.test.ts +185 -0
  614. package/src/email/verification.ts +110 -0
  615. package/src/index.ts +51 -0
  616. package/src/metering/aggregator.test.ts +239 -0
  617. package/src/metering/aggregator.ts +160 -0
  618. package/src/metering/dlq.test.ts +134 -0
  619. package/src/metering/dlq.ts +102 -0
  620. package/src/metering/drizzle-usage-summary-repository.ts +167 -0
  621. package/src/metering/emitter.test.ts +202 -0
  622. package/src/metering/emitter.ts +227 -0
  623. package/src/metering/index.ts +21 -0
  624. package/src/metering/load-test.bench.ts +130 -0
  625. package/src/metering/meter-event-repository.ts +87 -0
  626. package/src/metering/meter-repositories.test.ts +491 -0
  627. package/src/metering/metering.test.ts +1317 -0
  628. package/src/metering/reconciliation-cron.test.ts +202 -0
  629. package/src/metering/reconciliation-cron.ts +134 -0
  630. package/src/metering/reconciliation-repository.test.ts +196 -0
  631. package/src/metering/reconciliation-repository.ts +83 -0
  632. package/src/metering/types.ts +93 -0
  633. package/src/metering/wal.test.ts +222 -0
  634. package/src/metering/wal.ts +139 -0
  635. package/src/middleware/csrf.test.ts +178 -0
  636. package/src/middleware/csrf.ts +101 -0
  637. package/src/middleware/drizzle-rate-limit-repository.test.ts +97 -0
  638. package/src/middleware/drizzle-rate-limit-repository.ts +57 -0
  639. package/src/middleware/get-client-ip.test.ts +49 -0
  640. package/src/middleware/get-client-ip.ts +62 -0
  641. package/src/middleware/index.ts +12 -0
  642. package/src/middleware/rate-limit-repository.ts +22 -0
  643. package/src/middleware/rate-limit.test.ts +338 -0
  644. package/src/middleware/rate-limit.ts +169 -0
  645. package/src/security/credential-vault/audit-repository.test.ts +91 -0
  646. package/src/security/credential-vault/audit-repository.ts +64 -0
  647. package/src/security/credential-vault/credential-repository.test.ts +264 -0
  648. package/src/security/credential-vault/credential-repository.ts +233 -0
  649. package/src/security/credential-vault/index.ts +26 -0
  650. package/src/security/credential-vault/key-rotation.test.ts +139 -0
  651. package/src/security/credential-vault/key-rotation.ts +70 -0
  652. package/src/security/credential-vault/migrate-plaintext.test.ts +138 -0
  653. package/src/security/credential-vault/migrate-plaintext.ts +101 -0
  654. package/src/security/credential-vault/migration-check.test.ts +533 -0
  655. package/src/security/credential-vault/migration-check.ts +88 -0
  656. package/src/security/credential-vault/store.test.ts +569 -0
  657. package/src/security/credential-vault/store.ts +284 -0
  658. package/src/security/encryption.test.ts +114 -0
  659. package/src/security/encryption.ts +65 -0
  660. package/src/security/host-validation.test.ts +136 -0
  661. package/src/security/host-validation.ts +116 -0
  662. package/src/security/index.ts +59 -0
  663. package/src/security/key-audit.test.ts +57 -0
  664. package/src/security/key-audit.ts +45 -0
  665. package/src/security/key-injection.test.ts +131 -0
  666. package/src/security/key-injection.ts +71 -0
  667. package/src/security/key-validation.test.ts +111 -0
  668. package/src/security/key-validation.ts +84 -0
  669. package/src/security/redirect-allowlist.test.ts +70 -0
  670. package/src/security/redirect-allowlist.ts +35 -0
  671. package/src/security/tenant-keys/capability-settings-store.test.ts +98 -0
  672. package/src/security/tenant-keys/capability-settings-store.ts +53 -0
  673. package/src/security/tenant-keys/index.ts +10 -0
  674. package/src/security/tenant-keys/key-resolution-repository.test.ts +95 -0
  675. package/src/security/tenant-keys/key-resolution-repository.ts +31 -0
  676. package/src/security/tenant-keys/key-resolution.test.ts +173 -0
  677. package/src/security/tenant-keys/key-resolution.ts +87 -0
  678. package/src/security/tenant-keys/org-key-resolution.test.ts +217 -0
  679. package/src/security/tenant-keys/org-key-resolution.ts +76 -0
  680. package/src/security/tenant-keys/tenant-key-repository.test.ts +143 -0
  681. package/src/security/tenant-keys/tenant-key-repository.ts +130 -0
  682. package/src/security/types.ts +43 -0
  683. package/src/tenancy/drizzle-org-repository.ts +169 -0
  684. package/src/tenancy/index.ts +6 -0
  685. package/src/tenancy/org-member-repository.ts +159 -0
  686. package/src/tenancy/org-repository.test.ts +172 -0
  687. package/src/tenancy/org-service.test.ts +634 -0
  688. package/src/tenancy/org-service.ts +290 -0
  689. package/src/test/db.ts +97 -0
  690. package/src/trpc/index.ts +11 -0
  691. package/src/trpc/init.test.ts +196 -0
  692. package/src/trpc/init.ts +138 -0
  693. package/tsconfig.json +20 -0
  694. package/vitest.config.ts +8 -0
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Auth — Session management, token verification, and middleware.
3
+ *
4
+ * Provides:
5
+ * - `requireRole` middleware for role-based access control
6
+ * - `scopedBearerAuth` middleware for operation-scoped API tokens
7
+ */
8
+ import { timingSafeEqual } from "node:crypto";
9
+ /**
10
+ * Extract the bearer token from an Authorization header value.
11
+ * Returns `null` if the header is missing, empty, or not a Bearer scheme.
12
+ */
13
+ export function extractBearerToken(header) {
14
+ if (!header)
15
+ return null;
16
+ const trimmed = header.trim();
17
+ if (!trimmed.toLowerCase().startsWith("bearer "))
18
+ return null;
19
+ const token = trimmed.slice(7).trim();
20
+ return token || null;
21
+ }
22
+ /**
23
+ * Timing-safe lookup of a string key in a Map.
24
+ *
25
+ * Iterates all entries and compares each key using `crypto.timingSafeEqual`
26
+ * to prevent timing side-channel attacks. Returns the value of the first
27
+ * matching key, or `undefined` if no key matches.
28
+ */
29
+ export function timingSafeMapLookup(map, candidate) {
30
+ const candidateBuf = Buffer.from(candidate);
31
+ let found;
32
+ for (const [key, value] of map) {
33
+ const keyBuf = Buffer.from(key);
34
+ if (candidateBuf.length === keyBuf.length && timingSafeEqual(candidateBuf, keyBuf)) {
35
+ found = value;
36
+ }
37
+ }
38
+ return found;
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Middleware
42
+ // ---------------------------------------------------------------------------
43
+ /**
44
+ * Create a `requireRole` middleware that rejects users without the specified role.
45
+ * Must be used after `requireAuth`.
46
+ */
47
+ export function requireRole(role) {
48
+ return async (c, next) => {
49
+ let user;
50
+ try {
51
+ user = c.get("user");
52
+ }
53
+ catch {
54
+ return c.json({ error: "Authentication required" }, 401);
55
+ }
56
+ if (!user) {
57
+ return c.json({ error: "Authentication required" }, 401);
58
+ }
59
+ if (!user.roles.includes(role)) {
60
+ return c.json({ error: "Insufficient permissions", required: role }, 403);
61
+ }
62
+ return next();
63
+ };
64
+ }
65
+ /** Privilege hierarchy: admin > write > read. */
66
+ const SCOPE_LEVEL = {
67
+ read: 0,
68
+ write: 1,
69
+ admin: 2,
70
+ };
71
+ const VALID_SCOPES = new Set(["read", "write", "admin"]);
72
+ /**
73
+ * Parse a token's scope from the `wopr_<scope>_<random>` format.
74
+ *
75
+ * - `wopr_read_abc123` -> `"read"`
76
+ * - `wopr_write_abc123` -> `"write"`
77
+ * - `wopr_admin_abc123` -> `"admin"`
78
+ * - Any other format -> `null` (not a scoped token)
79
+ */
80
+ export function parseTokenScope(token) {
81
+ if (!token.startsWith("wopr_"))
82
+ return null;
83
+ const parts = token.split("_");
84
+ // Must be at least 3 parts: "wopr", scope, random
85
+ if (parts.length < 3)
86
+ return null;
87
+ const scope = parts[1];
88
+ if (!VALID_SCOPES.has(scope))
89
+ return null;
90
+ // The random portion (everything after wopr_<scope>_) must be non-empty
91
+ const random = parts.slice(2).join("_");
92
+ if (!random)
93
+ return null;
94
+ return scope;
95
+ }
96
+ /**
97
+ * Check whether a token's scope satisfies the required minimum scope.
98
+ * admin >= write >= read.
99
+ */
100
+ export function scopeSatisfies(tokenScope, requiredScope) {
101
+ return SCOPE_LEVEL[tokenScope] >= SCOPE_LEVEL[requiredScope];
102
+ }
103
+ /**
104
+ * Build a token-to-scope map from environment variables.
105
+ *
106
+ * Accepts:
107
+ * - `FLEET_API_TOKEN` (legacy single token, treated as admin)
108
+ * - `FLEET_API_TOKEN_READ` (read-scoped token)
109
+ * - `FLEET_API_TOKEN_WRITE` (write-scoped token)
110
+ * - `FLEET_API_TOKEN_ADMIN` (admin-scoped token)
111
+ * - Any `wopr_<scope>_<random>` formatted token has its scope inferred.
112
+ */
113
+ export function buildTokenMap(env = process.env) {
114
+ const tokens = new Map();
115
+ // Scoped env vars
116
+ const scopedVars = [
117
+ ["FLEET_API_TOKEN_READ", "read"],
118
+ ["FLEET_API_TOKEN_WRITE", "write"],
119
+ ["FLEET_API_TOKEN_ADMIN", "admin"],
120
+ ];
121
+ for (const [envVar, scope] of scopedVars) {
122
+ const val = env[envVar]?.trim();
123
+ if (val)
124
+ tokens.set(val, scope);
125
+ }
126
+ // Legacy single token -- admin scope for backwards compatibility
127
+ const legacyToken = env.FLEET_API_TOKEN?.trim();
128
+ if (legacyToken && !tokens.has(legacyToken)) {
129
+ // If the token is in wopr_ format, parse its actual scope
130
+ const parsed = parseTokenScope(legacyToken);
131
+ tokens.set(legacyToken, parsed ?? "admin");
132
+ }
133
+ return tokens;
134
+ }
135
+ /**
136
+ * Build a token-to-metadata map from environment variables.
137
+ *
138
+ * Accepts:
139
+ * - `FLEET_TOKEN_<TENANT>=<scope>:<token>` (tenant-scoped tokens)
140
+ * - Fallback to legacy token map for backwards compatibility (no tenant constraint)
141
+ *
142
+ * Example: `FLEET_TOKEN_ACME=write:wopr_write_abc123`
143
+ */
144
+ export function buildTokenMetadataMap(env = process.env) {
145
+ const metadataMap = new Map();
146
+ // Parse FLEET_TOKEN_<TENANT>=<scope>:<token> format
147
+ for (const [key, value] of Object.entries(env)) {
148
+ if (key.startsWith("FLEET_TOKEN_") && value) {
149
+ const tenantId = key.slice("FLEET_TOKEN_".length);
150
+ if (!tenantId)
151
+ continue;
152
+ const trimmed = value.trim();
153
+ const colonIdx = trimmed.indexOf(":");
154
+ if (colonIdx === -1)
155
+ continue;
156
+ const scopeStr = trimmed.slice(0, colonIdx);
157
+ const token = trimmed.slice(colonIdx + 1);
158
+ if (!VALID_SCOPES.has(scopeStr) || !token)
159
+ continue;
160
+ metadataMap.set(token, {
161
+ scope: scopeStr,
162
+ tenantId,
163
+ });
164
+ }
165
+ }
166
+ // Fallback: add legacy tokens without tenant constraint
167
+ const legacyTokenMap = buildTokenMap(env);
168
+ for (const [token, scope] of legacyTokenMap) {
169
+ if (!metadataMap.has(token)) {
170
+ metadataMap.set(token, { scope });
171
+ }
172
+ }
173
+ return metadataMap;
174
+ }
175
+ /**
176
+ * Create a scoped bearer auth middleware.
177
+ *
178
+ * Validates the bearer token against the token map and checks that the
179
+ * token's scope satisfies the required minimum scope for the route.
180
+ *
181
+ * @param tokenMap - Map of token -> scope (use `buildTokenMap()` to create)
182
+ * @param requiredScope - Minimum scope required for this route/group
183
+ */
184
+ export function scopedBearerAuth(tokenMap, requiredScope) {
185
+ return async (c, next) => {
186
+ const authHeader = c.req.header("Authorization");
187
+ const token = extractBearerToken(authHeader);
188
+ if (!token) {
189
+ return c.json({ error: "Authentication required" }, 401);
190
+ }
191
+ // Look up the token in the map
192
+ const scope = timingSafeMapLookup(tokenMap, token);
193
+ // If not in map, try to parse scope from token format for dynamic tokens
194
+ if (scope === undefined) {
195
+ return c.json({ error: "Invalid or expired token" }, 401);
196
+ }
197
+ // Check scope hierarchy
198
+ if (!scopeSatisfies(scope, requiredScope)) {
199
+ return c.json({ error: "Insufficient scope", required: requiredScope, provided: scope }, 403);
200
+ }
201
+ // Set user context for downstream middleware (audit, etc.)
202
+ c.set("user", { id: `token:${scope}`, roles: [scope] });
203
+ c.set("authMethod", "api_key");
204
+ return next();
205
+ };
206
+ }
207
+ /**
208
+ * Create a tenant-aware scoped bearer auth middleware.
209
+ *
210
+ * Validates the bearer token against the metadata map, checks scope,
211
+ * and stores the token's associated tenantId for downstream ownership checks.
212
+ *
213
+ * @param metadataMap - Map of token -> metadata (use `buildTokenMetadataMap()` to create)
214
+ * @param requiredScope - Minimum scope required for this route/group
215
+ */
216
+ export function scopedBearerAuthWithTenant(metadataMap, requiredScope) {
217
+ return async (c, next) => {
218
+ const authHeader = c.req.header("Authorization");
219
+ const token = extractBearerToken(authHeader);
220
+ if (!token) {
221
+ return c.json({ error: "Authentication required" }, 401);
222
+ }
223
+ // Look up the token in the metadata map
224
+ const metadata = timingSafeMapLookup(metadataMap, token);
225
+ if (!metadata) {
226
+ return c.json({ error: "Invalid or expired token" }, 401);
227
+ }
228
+ // Check scope hierarchy
229
+ if (!scopeSatisfies(metadata.scope, requiredScope)) {
230
+ return c.json({ error: "Insufficient scope", required: requiredScope, provided: metadata.scope }, 403);
231
+ }
232
+ // Set user context for downstream middleware (audit, etc.)
233
+ c.set("user", { id: `token:${metadata.scope}`, roles: [metadata.scope] });
234
+ c.set("authMethod", "api_key");
235
+ // Store tenant constraint if present; absence is allowed here — admin/legacy
236
+ // tokens have no tenantId and must still reach admin routes.
237
+ // Tenant-scoped routes enforce ownership via requireTenantOwnership /
238
+ // validateTenantOwnership (WOP-1264).
239
+ if (metadata.tenantId) {
240
+ c.set("tokenTenantId", metadata.tenantId);
241
+ }
242
+ else {
243
+ // Operator/admin tokens have no tenant scope — mark them so ownership
244
+ // middlewares can pass them through without a tenantId check.
245
+ c.set("isOperatorToken", true);
246
+ }
247
+ return next();
248
+ };
249
+ }
250
+ // ---------------------------------------------------------------------------
251
+ // Session Resolution (better-auth)
252
+ // ---------------------------------------------------------------------------
253
+ /**
254
+ * Middleware that resolves the current user from a better-auth session cookie.
255
+ *
256
+ * On success, sets:
257
+ * - `c.set("user", { id, roles })`
258
+ * - `c.set("authMethod", "session")`
259
+ *
260
+ * If no session cookie is present (or session is invalid), the request
261
+ * continues without a user — downstream middleware like `scopedBearerAuth`
262
+ * or `requireAuth` will handle enforcement.
263
+ *
264
+ * Uses lazy `getAuth()` to avoid initializing the DB at import time.
265
+ */
266
+ export function resolveSessionUser() {
267
+ return async (c, next) => {
268
+ // Skip if user is already set (e.g., by scopedBearerAuth)
269
+ try {
270
+ if (c.get("user"))
271
+ return next();
272
+ }
273
+ catch {
274
+ // c.get throws if variable not set — that's fine, continue
275
+ }
276
+ try {
277
+ const { getAuth } = await import("./better-auth.js");
278
+ const auth = getAuth();
279
+ const session = await auth.api.getSession({ headers: c.req.raw.headers });
280
+ if (session?.user) {
281
+ const user = session.user;
282
+ const roles = [];
283
+ if (user.role)
284
+ roles.push(user.role);
285
+ c.set("user", { id: user.id, roles });
286
+ c.set("authMethod", "session");
287
+ }
288
+ }
289
+ catch {
290
+ // Session resolution failed — continue without user
291
+ }
292
+ return next();
293
+ };
294
+ }
295
+ /**
296
+ * Middleware that requires either a valid session or a scoped API token.
297
+ *
298
+ * Tries session first, then falls back to scoped bearer auth.
299
+ * Returns 401 if neither is present.
300
+ */
301
+ export function requireSessionOrToken(tokenMap, requiredScope) {
302
+ return async (c, next) => {
303
+ // Check if user was already resolved by resolveSessionUser
304
+ try {
305
+ if (c.get("user"))
306
+ return next();
307
+ }
308
+ catch {
309
+ // Not set — continue to check bearer token
310
+ }
311
+ // Fall back to scoped bearer auth
312
+ const authHeader = c.req.header("Authorization");
313
+ const token = extractBearerToken(authHeader);
314
+ if (!token) {
315
+ return c.json({ error: "Authentication required" }, 401);
316
+ }
317
+ const scope = timingSafeMapLookup(tokenMap, token);
318
+ if (scope === undefined) {
319
+ return c.json({ error: "Invalid or expired token" }, 401);
320
+ }
321
+ if (!scopeSatisfies(scope, requiredScope)) {
322
+ return c.json({ error: "Insufficient scope", required: requiredScope, provided: scope }, 403);
323
+ }
324
+ c.set("user", { id: `token:${scope}`, roles: [scope] });
325
+ c.set("authMethod", "api_key");
326
+ return next();
327
+ };
328
+ }
329
+ // ---------------------------------------------------------------------------
330
+ // Tenant Ownership Validation
331
+ // ---------------------------------------------------------------------------
332
+ /**
333
+ * Middleware that enforces tenant ownership on tenant-scoped resources.
334
+ *
335
+ * Must be used after `scopedBearerAuthWithTenant` middleware.
336
+ * Compares the resource's tenantId against the token's tenantId.
337
+ * - If token has no tenantId (legacy/admin tokens), returns 403 Forbidden.
338
+ * - If resource tenantId matches token tenantId, passes through.
339
+ * - Otherwise, returns 403 Forbidden.
340
+ *
341
+ * @param _getResourceTenantId - Function that extracts tenantId from the resource (reserved for future use)
342
+ */
343
+ export function requireTenantOwnership(_getResourceTenantId) {
344
+ return async (c, next) => {
345
+ // Operator/admin tokens (fleet env var tokens) have no tenantId but must
346
+ // still access tenant-scoped routes — they are trusted at the operator level.
347
+ const isOperatorToken = c.get("isOperatorToken");
348
+ if (isOperatorToken) {
349
+ return next();
350
+ }
351
+ const tokenTenantId = c.get("tokenTenantId");
352
+ // If token has no tenant constraint and is not an operator token, reject.
353
+ if (!tokenTenantId) {
354
+ return c.json({ error: "Token lacks tenant scope" }, 403);
355
+ }
356
+ // Resource tenantId will be validated by route handler
357
+ // Store tokenTenantId for route to check
358
+ return next();
359
+ };
360
+ }
361
+ /**
362
+ * Validate that a resource belongs to the authenticated tenant.
363
+ * Returns a response if validation fails (404 for not found or tenant mismatch).
364
+ *
365
+ * @param c - Hono context
366
+ * @param resource - The resource to check (null/undefined = not found)
367
+ * @param resourceTenantId - The resource's tenantId
368
+ * @returns Response if validation fails, undefined if validation passes
369
+ */
370
+ export function validateTenantOwnership(c, resource, resourceTenantId) {
371
+ // Resource not found
372
+ if (resource == null) {
373
+ return c.json({ error: "Resource not found" }, 404);
374
+ }
375
+ // Get token's tenant constraint
376
+ let tokenTenantId;
377
+ try {
378
+ tokenTenantId = c.get("tokenTenantId");
379
+ }
380
+ catch {
381
+ // No tokenTenantId set — this is a legacy/admin token
382
+ tokenTenantId = undefined;
383
+ }
384
+ // Operator/admin tokens (fleet env var tokens) have no tenantId but must
385
+ // still access tenant-scoped resources — they are trusted at the operator level.
386
+ let isOperatorToken;
387
+ try {
388
+ isOperatorToken = c.get("isOperatorToken");
389
+ }
390
+ catch {
391
+ isOperatorToken = undefined;
392
+ }
393
+ if (isOperatorToken) {
394
+ return undefined;
395
+ }
396
+ // No tenant constraint and not an operator token — reject (WOP-1264)
397
+ if (!tokenTenantId) {
398
+ return c.json({ error: "Token lacks tenant scope" }, 403);
399
+ }
400
+ // Validate tenant match
401
+ if (resourceTenantId !== tokenTenantId) {
402
+ return c.json({ error: "Resource not found" }, 404);
403
+ }
404
+ return undefined;
405
+ }
406
+ /**
407
+ * Validate that a user has access to the requested tenant.
408
+ *
409
+ * - If requestedTenantId is falsy or matches userId, access is allowed (personal tenant).
410
+ * - Otherwise, checks org membership via IOrgMemberRepository.
411
+ *
412
+ * @returns true if the user has access, false otherwise.
413
+ */
414
+ export async function validateTenantAccess(userId, requestedTenantId, orgMemberRepo) {
415
+ // No tenant requested or personal tenant — always allowed
416
+ if (!requestedTenantId || requestedTenantId === userId) {
417
+ return true;
418
+ }
419
+ // Check org membership
420
+ const member = await orgMemberRepo.findMember(requestedTenantId, userId);
421
+ return member !== null;
422
+ }
@@ -0,0 +1,14 @@
1
+ import type { Pool } from "pg";
2
+ export interface LoginHistoryEntry {
3
+ ip: string | null;
4
+ userAgent: string | null;
5
+ createdAt: string;
6
+ }
7
+ export interface ILoginHistoryRepository {
8
+ findByUserId(userId: string, limit?: number): Promise<LoginHistoryEntry[]>;
9
+ }
10
+ export declare class BetterAuthLoginHistoryRepository implements ILoginHistoryRepository {
11
+ private readonly pool;
12
+ constructor(pool: Pool);
13
+ findByUserId(userId: string, limit?: number): Promise<LoginHistoryEntry[]>;
14
+ }
@@ -0,0 +1,15 @@
1
+ export class BetterAuthLoginHistoryRepository {
2
+ pool;
3
+ constructor(pool) {
4
+ this.pool = pool;
5
+ }
6
+ async findByUserId(userId, limit = 20) {
7
+ // raw SQL: better-auth manages its own schema outside Drizzle
8
+ const { rows } = await this.pool.query(`SELECT "ipAddress", "userAgent", "createdAt" FROM "session" WHERE "userId" = $1 ORDER BY "createdAt" DESC LIMIT $2`, [userId, Math.min(Math.max(1, limit), 100)]);
9
+ return rows.map((r) => ({
10
+ ip: r.ipAddress,
11
+ userAgent: r.userAgent,
12
+ createdAt: r.createdAt instanceof Date ? r.createdAt.toISOString() : String(r.createdAt),
13
+ }));
14
+ }
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { BetterAuthLoginHistoryRepository } from "./login-history-repository.js";
3
+ function makePool(rows) {
4
+ return {
5
+ query: vi.fn().mockResolvedValue({ rows }),
6
+ };
7
+ }
8
+ describe("BetterAuthLoginHistoryRepository", () => {
9
+ beforeEach(() => {
10
+ vi.clearAllMocks();
11
+ });
12
+ it("returns empty array when user has no sessions", async () => {
13
+ const pool = makePool([]);
14
+ const repo = new BetterAuthLoginHistoryRepository(pool);
15
+ const result = await repo.findByUserId("no-such-user");
16
+ expect(result).toEqual([]);
17
+ });
18
+ it("returns sessions ordered by createdAt DESC", async () => {
19
+ const pool = makePool([
20
+ { ipAddress: "5.6.7.8", userAgent: "Chrome/120", createdAt: new Date("2026-01-02") },
21
+ { ipAddress: "1.2.3.4", userAgent: "Mozilla/5.0", createdAt: new Date("2026-01-01") },
22
+ ]);
23
+ const repo = new BetterAuthLoginHistoryRepository(pool);
24
+ const result = await repo.findByUserId("user-1");
25
+ expect(result).toHaveLength(2);
26
+ expect(result[0].ip).toBe("5.6.7.8");
27
+ expect(result[1].ip).toBe("1.2.3.4");
28
+ });
29
+ it("respects the limit parameter", async () => {
30
+ const pool = makePool([
31
+ { ipAddress: "1.1.1.1", userAgent: null, createdAt: new Date() },
32
+ { ipAddress: "2.2.2.2", userAgent: null, createdAt: new Date() },
33
+ { ipAddress: "3.3.3.3", userAgent: null, createdAt: new Date() },
34
+ ]);
35
+ const repo = new BetterAuthLoginHistoryRepository(pool);
36
+ const result = await repo.findByUserId("user-1", 3);
37
+ expect(pool.query).toHaveBeenCalledWith(expect.any(String), ["user-1", 3]);
38
+ expect(result).toHaveLength(3);
39
+ });
40
+ it("does not return sessions for other users", async () => {
41
+ const pool = makePool([]);
42
+ const repo = new BetterAuthLoginHistoryRepository(pool);
43
+ const result = await repo.findByUserId("user-1");
44
+ expect(result).toEqual([]);
45
+ expect(pool.query).toHaveBeenCalledWith(expect.any(String), ["user-1", 20]);
46
+ });
47
+ });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Session Auth Middleware — Resolves better-auth sessions for Hono routes.
3
+ *
4
+ * Reads the better-auth session cookie from the request, resolves the user,
5
+ * and sets `c.set("user", ...)` and `c.set("authMethod", "session")` for
6
+ * downstream route handlers.
7
+ *
8
+ * This middleware supports a dual-auth model:
9
+ * - **Session auth** (cookie-based): For browser/UI clients via better-auth
10
+ * - **Bearer token auth** (header-based): For machine-to-machine / API key access
11
+ *
12
+ * If neither is present, the request is rejected with 401.
13
+ */
14
+ import type { Context, Next } from "hono";
15
+ import type { IApiKeyRepository } from "./api-key-repository.js";
16
+ import type { Auth } from "./better-auth.js";
17
+ import type { AuthUser } from "./index.js";
18
+ export interface SessionAuthEnv {
19
+ Variables: {
20
+ user: AuthUser;
21
+ authMethod: "session" | "api_key";
22
+ };
23
+ }
24
+ /**
25
+ * Create middleware that authenticates requests via better-auth session cookies.
26
+ *
27
+ * On success, sets:
28
+ * - `c.set("user", { id, roles })` — user context for downstream routes
29
+ * - `c.set("authMethod", "session")` — indicates session-based auth
30
+ *
31
+ * If no valid session cookie is found, returns 401.
32
+ *
33
+ * @param auth - The better-auth instance (use `getAuth()`)
34
+ */
35
+ export declare function sessionAuth(auth: Auth): (c: Context<SessionAuthEnv>, next: Next) => Promise<void | (Response & import("hono").TypedResponse<{
36
+ error: string;
37
+ }, 401, "json">) | (Response & import("hono").TypedResponse<{
38
+ error: string;
39
+ }, 503, "json">)>;
40
+ /**
41
+ * Create dual-auth middleware that accepts EITHER a better-auth session cookie
42
+ * OR a bearer token. Session cookies are checked first; if absent, falls back
43
+ * to bearer token validation via DB-backed API key lookup.
44
+ *
45
+ * Bearer tokens are hashed with SHA-256 before lookup — raw tokens are never
46
+ * stored or compared in plaintext.
47
+ *
48
+ * @param auth - The better-auth instance
49
+ * @param apiKeyRepo - Optional repository for DB-backed API key lookup
50
+ */
51
+ export declare function dualAuth(auth: Auth, apiKeyRepo?: IApiKeyRepository): (c: Context<SessionAuthEnv>, next: Next) => Promise<void | (Response & import("hono").TypedResponse<{
52
+ error: string;
53
+ }, 503, "json">) | (Response & import("hono").TypedResponse<{
54
+ error: string;
55
+ }, 401, "json">)>;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Session Auth Middleware — Resolves better-auth sessions for Hono routes.
3
+ *
4
+ * Reads the better-auth session cookie from the request, resolves the user,
5
+ * and sets `c.set("user", ...)` and `c.set("authMethod", "session")` for
6
+ * downstream route handlers.
7
+ *
8
+ * This middleware supports a dual-auth model:
9
+ * - **Session auth** (cookie-based): For browser/UI clients via better-auth
10
+ * - **Bearer token auth** (header-based): For machine-to-machine / API key access
11
+ *
12
+ * If neither is present, the request is rejected with 401.
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ /**
16
+ * Create middleware that authenticates requests via better-auth session cookies.
17
+ *
18
+ * On success, sets:
19
+ * - `c.set("user", { id, roles })` — user context for downstream routes
20
+ * - `c.set("authMethod", "session")` — indicates session-based auth
21
+ *
22
+ * If no valid session cookie is found, returns 401.
23
+ *
24
+ * @param auth - The better-auth instance (use `getAuth()`)
25
+ */
26
+ export function sessionAuth(auth) {
27
+ return async (c, next) => {
28
+ try {
29
+ const session = await auth.api.getSession({ headers: c.req.raw.headers });
30
+ if (!session?.user) {
31
+ return c.json({ error: "Authentication required" }, 401);
32
+ }
33
+ const sessionUser = session.user;
34
+ const user = {
35
+ id: sessionUser.id,
36
+ roles: sessionUser.role === "admin" ? ["admin", "user"] : ["user"],
37
+ };
38
+ c.set("user", user);
39
+ c.set("authMethod", "session");
40
+ return next();
41
+ }
42
+ catch {
43
+ return c.json({ error: "Service unavailable" }, 503);
44
+ }
45
+ };
46
+ }
47
+ /**
48
+ * Create dual-auth middleware that accepts EITHER a better-auth session cookie
49
+ * OR a bearer token. Session cookies are checked first; if absent, falls back
50
+ * to bearer token validation via DB-backed API key lookup.
51
+ *
52
+ * Bearer tokens are hashed with SHA-256 before lookup — raw tokens are never
53
+ * stored or compared in plaintext.
54
+ *
55
+ * @param auth - The better-auth instance
56
+ * @param apiKeyRepo - Optional repository for DB-backed API key lookup
57
+ */
58
+ export function dualAuth(auth, apiKeyRepo) {
59
+ return async (c, next) => {
60
+ // 1. Try session cookie first
61
+ try {
62
+ const session = await auth.api.getSession({ headers: c.req.raw.headers });
63
+ if (session?.user) {
64
+ const sessionUser = session.user;
65
+ const user = {
66
+ id: sessionUser.id,
67
+ roles: sessionUser.role === "admin" ? ["admin", "user"] : ["user"],
68
+ };
69
+ c.set("user", user);
70
+ c.set("authMethod", "session");
71
+ return next();
72
+ }
73
+ }
74
+ catch {
75
+ return c.json({ error: "Service unavailable" }, 503);
76
+ }
77
+ // 2. Fall back to bearer token (DB-backed lookup)
78
+ const authHeader = c.req.header("Authorization");
79
+ if (authHeader && apiKeyRepo) {
80
+ const trimmed = authHeader.trim();
81
+ if (trimmed.toLowerCase().startsWith("bearer ")) {
82
+ const token = trimmed.slice(7).trim();
83
+ if (token) {
84
+ const keyHash = createHash("sha256").update(token).digest("hex");
85
+ try {
86
+ const apiUser = await apiKeyRepo.findByHash(keyHash);
87
+ if (apiUser) {
88
+ c.set("user", { ...apiUser, roles: [...apiUser.roles] });
89
+ c.set("authMethod", "api_key");
90
+ return next();
91
+ }
92
+ }
93
+ catch {
94
+ return c.json({ error: "Service unavailable" }, 503);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return c.json({ error: "Authentication required" }, 401);
100
+ };
101
+ }
@@ -0,0 +1 @@
1
+ export {};