@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.
- package/biome.json +61 -0
- package/dist/admin/admin-audit-log-repository.d.ts +33 -0
- package/dist/admin/admin-audit-log-repository.js +102 -0
- package/dist/admin/audit-log.d.ts +49 -0
- package/dist/admin/audit-log.js +63 -0
- package/dist/admin/index.d.ts +6 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/role-store.d.ts +37 -0
- package/dist/admin/role-store.js +106 -0
- package/dist/auth/api-key-repository.d.ts +11 -0
- package/dist/auth/api-key-repository.js +33 -0
- package/dist/auth/api-key-repository.test.d.ts +1 -0
- package/dist/auth/api-key-repository.test.js +46 -0
- package/dist/auth/auth.test.d.ts +1 -0
- package/dist/auth/auth.test.js +140 -0
- package/dist/auth/better-auth.d.ts +42 -0
- package/dist/auth/better-auth.js +196 -0
- package/dist/auth/index.d.ts +186 -0
- package/dist/auth/index.js +422 -0
- package/dist/auth/login-history-repository.d.ts +14 -0
- package/dist/auth/login-history-repository.js +15 -0
- package/dist/auth/login-history-repository.test.d.ts +1 -0
- package/dist/auth/login-history-repository.test.js +47 -0
- package/dist/auth/middleware.d.ts +55 -0
- package/dist/auth/middleware.js +101 -0
- package/dist/auth/middleware.test.d.ts +1 -0
- package/dist/auth/middleware.test.js +213 -0
- package/dist/auth/scoped-tokens.test.d.ts +1 -0
- package/dist/auth/scoped-tokens.test.js +306 -0
- package/dist/auth/tenant-access.test.d.ts +1 -0
- package/dist/auth/tenant-access.test.js +62 -0
- package/dist/auth/user-creator.d.ts +9 -0
- package/dist/auth/user-creator.js +47 -0
- package/dist/auth/user-creator.test.d.ts +1 -0
- package/dist/auth/user-creator.test.js +78 -0
- package/dist/auth/user-role-repository.d.ts +31 -0
- package/dist/auth/user-role-repository.js +53 -0
- package/dist/auth/user-role-repository.test.d.ts +1 -0
- package/dist/auth/user-role-repository.test.js +122 -0
- package/dist/billing/drizzle-webhook-seen-repository.d.ts +10 -0
- package/dist/billing/drizzle-webhook-seen-repository.js +28 -0
- package/dist/billing/index.d.ts +7 -0
- package/dist/billing/index.js +7 -0
- package/dist/billing/payment-processor.d.ts +127 -0
- package/dist/billing/payment-processor.js +8 -0
- package/dist/billing/payment-processor.test.d.ts +1 -0
- package/dist/billing/payment-processor.test.js +71 -0
- package/dist/billing/payram/cents-credits-boundary.test.d.ts +1 -0
- package/dist/billing/payram/cents-credits-boundary.test.js +75 -0
- package/dist/billing/payram/charge-store.d.ts +41 -0
- package/dist/billing/payram/charge-store.js +72 -0
- package/dist/billing/payram/charge-store.test.d.ts +1 -0
- package/dist/billing/payram/charge-store.test.js +64 -0
- package/dist/billing/payram/checkout.d.ts +15 -0
- package/dist/billing/payram/checkout.js +24 -0
- package/dist/billing/payram/checkout.test.d.ts +1 -0
- package/dist/billing/payram/checkout.test.js +74 -0
- package/dist/billing/payram/client.d.ts +7 -0
- package/dist/billing/payram/client.js +15 -0
- package/dist/billing/payram/client.test.d.ts +1 -0
- package/dist/billing/payram/client.test.js +52 -0
- package/dist/billing/payram/index.d.ts +8 -0
- package/dist/billing/payram/index.js +4 -0
- package/dist/billing/payram/types.d.ts +40 -0
- package/dist/billing/payram/types.js +1 -0
- package/dist/billing/payram/webhook.d.ts +19 -0
- package/dist/billing/payram/webhook.js +67 -0
- package/dist/billing/payram/webhook.test.d.ts +7 -0
- package/dist/billing/payram/webhook.test.js +248 -0
- package/dist/billing/stripe/cents-credits-boundary.test.d.ts +1 -0
- package/dist/billing/stripe/cents-credits-boundary.test.js +62 -0
- package/dist/billing/stripe/checkout.d.ts +20 -0
- package/dist/billing/stripe/checkout.js +63 -0
- package/dist/billing/stripe/checkout.test.d.ts +1 -0
- package/dist/billing/stripe/checkout.test.js +148 -0
- package/dist/billing/stripe/client.d.ts +14 -0
- package/dist/billing/stripe/client.js +33 -0
- package/dist/billing/stripe/client.test.d.ts +1 -0
- package/dist/billing/stripe/client.test.js +58 -0
- package/dist/billing/stripe/credit-prices.d.ts +63 -0
- package/dist/billing/stripe/credit-prices.js +81 -0
- package/dist/billing/stripe/credit-prices.test.d.ts +1 -0
- package/dist/billing/stripe/credit-prices.test.js +87 -0
- package/dist/billing/stripe/index.d.ts +14 -0
- package/dist/billing/stripe/index.js +8 -0
- package/dist/billing/stripe/payment-methods-detach-all.test.d.ts +1 -0
- package/dist/billing/stripe/payment-methods-detach-all.test.js +40 -0
- package/dist/billing/stripe/payment-methods.d.ts +25 -0
- package/dist/billing/stripe/payment-methods.js +53 -0
- package/dist/billing/stripe/payment-methods.test.d.ts +1 -0
- package/dist/billing/stripe/payment-methods.test.js +122 -0
- package/dist/billing/stripe/portal.d.ts +10 -0
- package/dist/billing/stripe/portal.js +16 -0
- package/dist/billing/stripe/portal.test.d.ts +1 -0
- package/dist/billing/stripe/portal.test.js +48 -0
- package/dist/billing/stripe/setup-intent.d.ts +16 -0
- package/dist/billing/stripe/setup-intent.js +22 -0
- package/dist/billing/stripe/setup-intent.test.d.ts +1 -0
- package/dist/billing/stripe/setup-intent.test.js +58 -0
- package/dist/billing/stripe/stripe-payment-processor.d.ts +49 -0
- package/dist/billing/stripe/stripe-payment-processor.js +166 -0
- package/dist/billing/stripe/stripe-payment-processor.test.d.ts +1 -0
- package/dist/billing/stripe/stripe-payment-processor.test.js +413 -0
- package/dist/billing/stripe/tenant-store.d.ts +56 -0
- package/dist/billing/stripe/tenant-store.js +119 -0
- package/dist/billing/stripe/tenant-store.test.d.ts +1 -0
- package/dist/billing/stripe/tenant-store.test.js +97 -0
- package/dist/billing/stripe/types.d.ts +49 -0
- package/dist/billing/stripe/types.js +1 -0
- package/dist/billing/webhook-seen-repository.d.ts +14 -0
- package/dist/billing/webhook-seen-repository.js +13 -0
- package/dist/config/billing-env.test.d.ts +1 -0
- package/dist/config/billing-env.test.js +48 -0
- package/dist/config/index.d.ts +46 -0
- package/dist/config/index.js +38 -0
- package/dist/config/logger.d.ts +2 -0
- package/dist/config/logger.js +11 -0
- package/dist/config/provider-endpoints.d.ts +6 -0
- package/dist/config/provider-endpoints.js +12 -0
- package/dist/credits/auto-topup-charge.d.ts +27 -0
- package/dist/credits/auto-topup-charge.js +139 -0
- package/dist/credits/auto-topup-charge.test.d.ts +1 -0
- package/dist/credits/auto-topup-charge.test.js +242 -0
- package/dist/credits/auto-topup-event-log-repository.d.ts +16 -0
- package/dist/credits/auto-topup-event-log-repository.js +18 -0
- package/dist/credits/auto-topup-event-log-repository.test.d.ts +1 -0
- package/dist/credits/auto-topup-event-log-repository.test.js +83 -0
- package/dist/credits/auto-topup-schedule.d.ts +27 -0
- package/dist/credits/auto-topup-schedule.js +66 -0
- package/dist/credits/auto-topup-schedule.test.d.ts +1 -0
- package/dist/credits/auto-topup-schedule.test.js +145 -0
- package/dist/credits/auto-topup-settings-repository.d.ts +54 -0
- package/dist/credits/auto-topup-settings-repository.js +184 -0
- package/dist/credits/auto-topup-settings-repository.test.d.ts +1 -0
- package/dist/credits/auto-topup-settings-repository.test.js +104 -0
- package/dist/credits/auto-topup-usage.d.ts +22 -0
- package/dist/credits/auto-topup-usage.js +56 -0
- package/dist/credits/auto-topup-usage.test.d.ts +1 -0
- package/dist/credits/auto-topup-usage.test.js +181 -0
- package/dist/credits/credit-expiry-cron.d.ts +19 -0
- package/dist/credits/credit-expiry-cron.js +50 -0
- package/dist/credits/credit-expiry-cron.test.d.ts +1 -0
- package/dist/credits/credit-expiry-cron.test.js +67 -0
- package/dist/credits/credit-ledger-extra.test.d.ts +1 -0
- package/dist/credits/credit-ledger-extra.test.js +40 -0
- package/dist/credits/credit-ledger.bench.d.ts +1 -0
- package/dist/credits/credit-ledger.bench.js +33 -0
- package/dist/credits/credit-ledger.d.ts +130 -0
- package/dist/credits/credit-ledger.js +293 -0
- package/dist/credits/credit-ledger.test.d.ts +4 -0
- package/dist/credits/credit-ledger.test.js +203 -0
- package/dist/credits/credit-transaction-repository.d.ts +17 -0
- package/dist/credits/credit-transaction-repository.js +35 -0
- package/dist/credits/credit-transaction-repository.test.d.ts +1 -0
- package/dist/credits/credit-transaction-repository.test.js +232 -0
- package/dist/credits/credit.d.ts +75 -0
- package/dist/credits/credit.js +139 -0
- package/dist/credits/credit.test.d.ts +1 -0
- package/dist/credits/credit.test.js +196 -0
- package/dist/credits/dividend-cron.d.ts +29 -0
- package/dist/credits/dividend-cron.js +88 -0
- package/dist/credits/dividend-cron.test.d.ts +1 -0
- package/dist/credits/dividend-cron.test.js +128 -0
- package/dist/credits/dividend-repository.d.ts +29 -0
- package/dist/credits/dividend-repository.js +126 -0
- package/dist/credits/dividend-repository.test.d.ts +1 -0
- package/dist/credits/dividend-repository.test.js +176 -0
- package/dist/credits/index.d.ts +9 -0
- package/dist/credits/index.js +5 -0
- package/dist/credits/repository-types.d.ts +29 -0
- package/dist/credits/repository-types.js +1 -0
- package/dist/credits/signup-grant.d.ts +12 -0
- package/dist/credits/signup-grant.js +35 -0
- package/dist/credits/signup-grant.test.d.ts +1 -0
- package/dist/credits/signup-grant.test.js +51 -0
- package/dist/credits/tenant-customer-repository.d.ts +30 -0
- package/dist/credits/tenant-customer-repository.js +5 -0
- package/dist/db/auth-user-repository.d.ts +46 -0
- package/dist/db/auth-user-repository.js +90 -0
- package/dist/db/credit-column.d.ts +27 -0
- package/dist/db/credit-column.js +13 -0
- package/dist/db/index.d.ts +14 -0
- package/dist/db/index.js +8 -0
- package/dist/db/schema/account-deletion-requests.d.ts +203 -0
- package/dist/db/schema/account-deletion-requests.js +36 -0
- package/dist/db/schema/account-export-requests.d.ts +148 -0
- package/dist/db/schema/account-export-requests.js +19 -0
- package/dist/db/schema/admin-audit.d.ts +194 -0
- package/dist/db/schema/admin-audit.js +21 -0
- package/dist/db/schema/admin-users.d.ts +177 -0
- package/dist/db/schema/admin-users.js +23 -0
- package/dist/db/schema/affiliate-fraud.d.ts +160 -0
- package/dist/db/schema/affiliate-fraud.js +18 -0
- package/dist/db/schema/affiliate.d.ts +277 -0
- package/dist/db/schema/affiliate.js +32 -0
- package/dist/db/schema/coupon-codes.d.ts +143 -0
- package/dist/db/schema/coupon-codes.js +17 -0
- package/dist/db/schema/credit-auto-topup-settings.d.ts +232 -0
- package/dist/db/schema/credit-auto-topup-settings.js +27 -0
- package/dist/db/schema/credit-auto-topup.d.ts +130 -0
- package/dist/db/schema/credit-auto-topup.js +21 -0
- package/dist/db/schema/credits.d.ts +283 -0
- package/dist/db/schema/credits.js +38 -0
- package/dist/db/schema/dividend-distributions.d.ts +130 -0
- package/dist/db/schema/dividend-distributions.js +19 -0
- package/dist/db/schema/email-notifications.d.ts +99 -0
- package/dist/db/schema/email-notifications.js +21 -0
- package/dist/db/schema/index.d.ts +33 -0
- package/dist/db/schema/index.js +33 -0
- package/dist/db/schema/meter-events.d.ts +599 -0
- package/dist/db/schema/meter-events.js +55 -0
- package/dist/db/schema/notification-preferences.d.ts +165 -0
- package/dist/db/schema/notification-preferences.js +18 -0
- package/dist/db/schema/notification-queue.d.ts +236 -0
- package/dist/db/schema/notification-queue.js +40 -0
- package/dist/db/schema/org-memberships.d.ts +63 -0
- package/dist/db/schema/org-memberships.js +15 -0
- package/dist/db/schema/organization-members.d.ts +235 -0
- package/dist/db/schema/organization-members.js +27 -0
- package/dist/db/schema/payram.d.ts +164 -0
- package/dist/db/schema/payram.js +21 -0
- package/dist/db/schema/platform-api-keys.d.ts +143 -0
- package/dist/db/schema/platform-api-keys.js +20 -0
- package/dist/db/schema/promotion-redemptions.d.ts +143 -0
- package/dist/db/schema/promotion-redemptions.js +18 -0
- package/dist/db/schema/promotions.d.ts +445 -0
- package/dist/db/schema/promotions.js +48 -0
- package/dist/db/schema/provider-credentials.d.ts +201 -0
- package/dist/db/schema/provider-credentials.js +36 -0
- package/dist/db/schema/rate-limit-entries.d.ts +75 -0
- package/dist/db/schema/rate-limit-entries.js +7 -0
- package/dist/db/schema/secret-audit-log.d.ts +109 -0
- package/dist/db/schema/secret-audit-log.js +15 -0
- package/dist/db/schema/session-usage.d.ts +194 -0
- package/dist/db/schema/session-usage.js +19 -0
- package/dist/db/schema/spending-limits.d.ts +92 -0
- package/dist/db/schema/spending-limits.js +8 -0
- package/dist/db/schema/tenant-addons.d.ts +58 -0
- package/dist/db/schema/tenant-addons.js +9 -0
- package/dist/db/schema/tenant-api-keys.d.ts +131 -0
- package/dist/db/schema/tenant-api-keys.js +21 -0
- package/dist/db/schema/tenant-capability-settings.d.ts +79 -0
- package/dist/db/schema/tenant-capability-settings.js +12 -0
- package/dist/db/schema/tenant-customers.d.ts +303 -0
- package/dist/db/schema/tenant-customers.js +25 -0
- package/dist/db/schema/tenants.d.ts +126 -0
- package/dist/db/schema/tenants.js +18 -0
- package/dist/db/schema/user-roles.d.ts +98 -0
- package/dist/db/schema/user-roles.js +18 -0
- package/dist/db/schema/webhook-seen-events.d.ts +58 -0
- package/dist/db/schema/webhook-seen-events.js +9 -0
- package/dist/email/billing-emails.d.ts +51 -0
- package/dist/email/billing-emails.js +163 -0
- package/dist/email/billing-emails.test.d.ts +1 -0
- package/dist/email/billing-emails.test.js +162 -0
- package/dist/email/client.d.ts +51 -0
- package/dist/email/client.js +102 -0
- package/dist/email/client.test.d.ts +1 -0
- package/dist/email/client.test.js +120 -0
- package/dist/email/drizzle-billing-email-repository.d.ts +21 -0
- package/dist/email/drizzle-billing-email-repository.js +36 -0
- package/dist/email/drizzle-billing-email-repository.test.d.ts +1 -0
- package/dist/email/drizzle-billing-email-repository.test.js +42 -0
- package/dist/email/index.d.ts +33 -0
- package/dist/email/index.js +22 -0
- package/dist/email/notification-preferences-store.d.ts +12 -0
- package/dist/email/notification-preferences-store.js +82 -0
- package/dist/email/notification-preferences-store.test.d.ts +1 -0
- package/dist/email/notification-preferences-store.test.js +86 -0
- package/dist/email/notification-queue-store.d.ts +25 -0
- package/dist/email/notification-queue-store.js +97 -0
- package/dist/email/notification-queue-store.test.d.ts +1 -0
- package/dist/email/notification-queue-store.test.js +177 -0
- package/dist/email/notification-repository-types.d.ts +70 -0
- package/dist/email/notification-repository-types.js +6 -0
- package/dist/email/notification-service.d.ts +41 -0
- package/dist/email/notification-service.js +196 -0
- package/dist/email/notification-service.test.d.ts +1 -0
- package/dist/email/notification-service.test.js +160 -0
- package/dist/email/notification-templates.d.ts +18 -0
- package/dist/email/notification-templates.js +574 -0
- package/dist/email/notification-templates.test.d.ts +1 -0
- package/dist/email/notification-templates.test.js +238 -0
- package/dist/email/notification-worker.d.ts +24 -0
- package/dist/email/notification-worker.js +109 -0
- package/dist/email/notification-worker.test.d.ts +1 -0
- package/dist/email/notification-worker.test.js +153 -0
- package/dist/email/require-verified.d.ts +25 -0
- package/dist/email/require-verified.js +52 -0
- package/dist/email/require-verified.test.d.ts +1 -0
- package/dist/email/require-verified.test.js +62 -0
- package/dist/email/resend-adapter.d.ts +47 -0
- package/dist/email/resend-adapter.js +137 -0
- package/dist/email/resend-adapter.test.d.ts +1 -0
- package/dist/email/resend-adapter.test.js +190 -0
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/templates.js +359 -0
- package/dist/email/templates.test.d.ts +1 -0
- package/dist/email/templates.test.js +170 -0
- package/dist/email/verification.d.ts +42 -0
- package/dist/email/verification.js +83 -0
- package/dist/email/verification.test.d.ts +1 -0
- package/dist/email/verification.test.js +141 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +23 -0
- package/dist/metering/aggregator.d.ts +54 -0
- package/dist/metering/aggregator.js +123 -0
- package/dist/metering/aggregator.test.d.ts +1 -0
- package/dist/metering/aggregator.test.js +179 -0
- package/dist/metering/dlq.d.ts +31 -0
- package/dist/metering/dlq.js +82 -0
- package/dist/metering/dlq.test.d.ts +1 -0
- package/dist/metering/dlq.test.js +117 -0
- package/dist/metering/drizzle-usage-summary-repository.d.ts +67 -0
- package/dist/metering/drizzle-usage-summary-repository.js +98 -0
- package/dist/metering/emitter.d.ts +66 -0
- package/dist/metering/emitter.js +185 -0
- package/dist/metering/emitter.test.d.ts +1 -0
- package/dist/metering/emitter.test.js +171 -0
- package/dist/metering/index.d.ts +11 -0
- package/dist/metering/index.js +5 -0
- package/dist/metering/load-test.bench.d.ts +1 -0
- package/dist/metering/load-test.bench.js +103 -0
- package/dist/metering/meter-event-repository.d.ts +33 -0
- package/dist/metering/meter-event-repository.js +58 -0
- package/dist/metering/meter-repositories.test.d.ts +1 -0
- package/dist/metering/meter-repositories.test.js +419 -0
- package/dist/metering/metering.test.d.ts +1 -0
- package/dist/metering/metering.test.js +1046 -0
- package/dist/metering/reconciliation-cron.d.ts +37 -0
- package/dist/metering/reconciliation-cron.js +85 -0
- package/dist/metering/reconciliation-cron.test.d.ts +1 -0
- package/dist/metering/reconciliation-cron.test.js +162 -0
- package/dist/metering/reconciliation-repository.d.ts +27 -0
- package/dist/metering/reconciliation-repository.js +43 -0
- package/dist/metering/reconciliation-repository.test.d.ts +1 -0
- package/dist/metering/reconciliation-repository.test.js +160 -0
- package/dist/metering/types.d.ts +88 -0
- package/dist/metering/types.js +1 -0
- package/dist/metering/wal.d.ts +49 -0
- package/dist/metering/wal.js +124 -0
- package/dist/metering/wal.test.d.ts +1 -0
- package/dist/metering/wal.test.js +175 -0
- package/dist/middleware/csrf.d.ts +24 -0
- package/dist/middleware/csrf.js +80 -0
- package/dist/middleware/csrf.test.d.ts +1 -0
- package/dist/middleware/csrf.test.js +152 -0
- package/dist/middleware/drizzle-rate-limit-repository.d.ts +9 -0
- package/dist/middleware/drizzle-rate-limit-repository.js +52 -0
- package/dist/middleware/drizzle-rate-limit-repository.test.d.ts +1 -0
- package/dist/middleware/drizzle-rate-limit-repository.test.js +74 -0
- package/dist/middleware/get-client-ip.d.ts +22 -0
- package/dist/middleware/get-client-ip.js +51 -0
- package/dist/middleware/get-client-ip.test.d.ts +1 -0
- package/dist/middleware/get-client-ip.test.js +40 -0
- package/dist/middleware/index.d.ts +5 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/rate-limit-repository.d.ts +19 -0
- package/dist/middleware/rate-limit-repository.js +1 -0
- package/dist/middleware/rate-limit.d.ts +57 -0
- package/dist/middleware/rate-limit.js +109 -0
- package/dist/middleware/rate-limit.test.d.ts +1 -0
- package/dist/middleware/rate-limit.test.js +247 -0
- package/dist/security/credential-vault/audit-repository.d.ts +27 -0
- package/dist/security/credential-vault/audit-repository.js +42 -0
- package/dist/security/credential-vault/audit-repository.test.d.ts +1 -0
- package/dist/security/credential-vault/audit-repository.test.js +78 -0
- package/dist/security/credential-vault/credential-repository.d.ts +94 -0
- package/dist/security/credential-vault/credential-repository.js +145 -0
- package/dist/security/credential-vault/credential-repository.test.d.ts +1 -0
- package/dist/security/credential-vault/credential-repository.test.js +206 -0
- package/dist/security/credential-vault/index.d.ts +12 -0
- package/dist/security/credential-vault/index.js +6 -0
- package/dist/security/credential-vault/key-rotation.d.ts +18 -0
- package/dist/security/credential-vault/key-rotation.js +52 -0
- package/dist/security/credential-vault/key-rotation.test.d.ts +1 -0
- package/dist/security/credential-vault/key-rotation.test.js +95 -0
- package/dist/security/credential-vault/migrate-plaintext.d.ts +15 -0
- package/dist/security/credential-vault/migrate-plaintext.js +80 -0
- package/dist/security/credential-vault/migrate-plaintext.test.d.ts +1 -0
- package/dist/security/credential-vault/migrate-plaintext.test.js +111 -0
- package/dist/security/credential-vault/migration-check.d.ts +15 -0
- package/dist/security/credential-vault/migration-check.js +71 -0
- package/dist/security/credential-vault/migration-check.test.d.ts +1 -0
- package/dist/security/credential-vault/migration-check.test.js +457 -0
- package/dist/security/credential-vault/store.d.ts +106 -0
- package/dist/security/credential-vault/store.js +181 -0
- package/dist/security/credential-vault/store.test.d.ts +1 -0
- package/dist/security/credential-vault/store.test.js +482 -0
- package/dist/security/encryption.d.ts +22 -0
- package/dist/security/encryption.js +53 -0
- package/dist/security/encryption.test.d.ts +1 -0
- package/dist/security/encryption.test.js +95 -0
- package/dist/security/host-validation.d.ts +11 -0
- package/dist/security/host-validation.js +108 -0
- package/dist/security/host-validation.test.d.ts +1 -0
- package/dist/security/host-validation.test.js +106 -0
- package/dist/security/index.d.ts +11 -0
- package/dist/security/index.js +11 -0
- package/dist/security/key-audit.d.ts +16 -0
- package/dist/security/key-audit.js +35 -0
- package/dist/security/key-audit.test.d.ts +1 -0
- package/dist/security/key-audit.test.js +50 -0
- package/dist/security/key-injection.d.ts +28 -0
- package/dist/security/key-injection.js +57 -0
- package/dist/security/key-injection.test.d.ts +1 -0
- package/dist/security/key-injection.test.js +97 -0
- package/dist/security/key-validation.d.ts +16 -0
- package/dist/security/key-validation.js +78 -0
- package/dist/security/key-validation.test.d.ts +1 -0
- package/dist/security/key-validation.test.js +87 -0
- package/dist/security/redirect-allowlist.d.ts +6 -0
- package/dist/security/redirect-allowlist.js +36 -0
- package/dist/security/redirect-allowlist.test.d.ts +1 -0
- package/dist/security/redirect-allowlist.test.js +55 -0
- package/dist/security/tenant-keys/capability-settings-store.d.ts +22 -0
- package/dist/security/tenant-keys/capability-settings-store.js +33 -0
- package/dist/security/tenant-keys/capability-settings-store.test.d.ts +1 -0
- package/dist/security/tenant-keys/capability-settings-store.test.js +77 -0
- package/dist/security/tenant-keys/index.d.ts +10 -0
- package/dist/security/tenant-keys/index.js +5 -0
- package/dist/security/tenant-keys/key-resolution-repository.d.ts +15 -0
- package/dist/security/tenant-keys/key-resolution-repository.js +18 -0
- package/dist/security/tenant-keys/key-resolution-repository.test.d.ts +1 -0
- package/dist/security/tenant-keys/key-resolution-repository.test.js +72 -0
- package/dist/security/tenant-keys/key-resolution.d.ts +39 -0
- package/dist/security/tenant-keys/key-resolution.js +59 -0
- package/dist/security/tenant-keys/key-resolution.test.d.ts +1 -0
- package/dist/security/tenant-keys/key-resolution.test.js +97 -0
- package/dist/security/tenant-keys/org-key-resolution.d.ts +30 -0
- package/dist/security/tenant-keys/org-key-resolution.js +50 -0
- package/dist/security/tenant-keys/org-key-resolution.test.d.ts +1 -0
- package/dist/security/tenant-keys/org-key-resolution.test.js +103 -0
- package/dist/security/tenant-keys/tenant-key-repository.d.ts +36 -0
- package/dist/security/tenant-keys/tenant-key-repository.js +96 -0
- package/dist/security/tenant-keys/tenant-key-repository.test.d.ts +1 -0
- package/dist/security/tenant-keys/tenant-key-repository.test.js +114 -0
- package/dist/security/types.d.ts +35 -0
- package/dist/security/types.js +15 -0
- package/dist/tenancy/drizzle-org-repository.d.ts +40 -0
- package/dist/tenancy/drizzle-org-repository.js +126 -0
- package/dist/tenancy/index.d.ts +6 -0
- package/dist/tenancy/index.js +3 -0
- package/dist/tenancy/org-member-repository.d.ts +57 -0
- package/dist/tenancy/org-member-repository.js +99 -0
- package/dist/tenancy/org-repository.test.d.ts +1 -0
- package/dist/tenancy/org-repository.test.js +143 -0
- package/dist/tenancy/org-service.d.ts +70 -0
- package/dist/tenancy/org-service.js +223 -0
- package/dist/tenancy/org-service.test.d.ts +1 -0
- package/dist/tenancy/org-service.test.js +550 -0
- package/dist/test/db.d.ts +33 -0
- package/dist/test/db.js +65 -0
- package/dist/trpc/index.d.ts +1 -0
- package/dist/trpc/index.js +1 -0
- package/dist/trpc/init.d.ts +49 -0
- package/dist/trpc/init.js +108 -0
- package/dist/trpc/init.test.d.ts +1 -0
- package/dist/trpc/init.test.js +154 -0
- package/drizzle/migrations/0000_slippery_mandrill.sql +559 -0
- package/drizzle/migrations/meta/0000_snapshot.json +4374 -0
- package/drizzle/migrations/meta/_journal.json +13 -0
- package/drizzle.config.ts +41 -0
- package/package.json +64 -0
- package/src/admin/admin-audit-log-repository.ts +135 -0
- package/src/admin/audit-log.ts +111 -0
- package/src/admin/index.ts +6 -0
- package/src/admin/role-store.ts +134 -0
- package/src/auth/api-key-repository.test.ts +63 -0
- package/src/auth/api-key-repository.ts +46 -0
- package/src/auth/auth.test.ts +166 -0
- package/src/auth/better-auth.ts +216 -0
- package/src/auth/index.ts +520 -0
- package/src/auth/login-history-repository.test.ts +54 -0
- package/src/auth/login-history-repository.ts +28 -0
- package/src/auth/middleware.test.ts +264 -0
- package/src/auth/middleware.ts +117 -0
- package/src/auth/scoped-tokens.test.ts +362 -0
- package/src/auth/tenant-access.test.ts +69 -0
- package/src/auth/user-creator.test.ts +98 -0
- package/src/auth/user-creator.ts +54 -0
- package/src/auth/user-role-repository.test.ts +149 -0
- package/src/auth/user-role-repository.ts +67 -0
- package/src/billing/drizzle-webhook-seen-repository.ts +34 -0
- package/src/billing/index.ts +22 -0
- package/src/billing/payment-processor.test.ts +93 -0
- package/src/billing/payment-processor.ts +150 -0
- package/src/billing/payram/cents-credits-boundary.test.ts +84 -0
- package/src/billing/payram/charge-store.test.ts +84 -0
- package/src/billing/payram/charge-store.ts +109 -0
- package/src/billing/payram/checkout.test.ts +99 -0
- package/src/billing/payram/checkout.ts +40 -0
- package/src/billing/payram/client.test.ts +62 -0
- package/src/billing/payram/client.ts +21 -0
- package/src/billing/payram/index.ts +14 -0
- package/src/billing/payram/types.ts +44 -0
- package/src/billing/payram/webhook.test.ts +318 -0
- package/src/billing/payram/webhook.ts +97 -0
- package/src/billing/stripe/cents-credits-boundary.test.ts +70 -0
- package/src/billing/stripe/checkout.test.ts +186 -0
- package/src/billing/stripe/checkout.ts +82 -0
- package/src/billing/stripe/client.test.ts +64 -0
- package/src/billing/stripe/client.ts +39 -0
- package/src/billing/stripe/credit-prices.test.ts +114 -0
- package/src/billing/stripe/credit-prices.ts +113 -0
- package/src/billing/stripe/index.ts +14 -0
- package/src/billing/stripe/payment-methods-detach-all.test.ts +53 -0
- package/src/billing/stripe/payment-methods.test.ts +157 -0
- package/src/billing/stripe/payment-methods.ts +76 -0
- package/src/billing/stripe/portal.test.ts +63 -0
- package/src/billing/stripe/portal.ts +25 -0
- package/src/billing/stripe/setup-intent.test.ts +78 -0
- package/src/billing/stripe/setup-intent.ts +34 -0
- package/src/billing/stripe/stripe-payment-processor.test.ts +517 -0
- package/src/billing/stripe/stripe-payment-processor.ts +255 -0
- package/src/billing/stripe/tenant-store.test.ts +124 -0
- package/src/billing/stripe/tenant-store.ts +151 -0
- package/src/billing/stripe/types.ts +53 -0
- package/src/billing/webhook-seen-repository.ts +24 -0
- package/src/config/billing-env.test.ts +54 -0
- package/src/config/index.ts +44 -0
- package/src/config/logger.ts +12 -0
- package/src/config/provider-endpoints.ts +14 -0
- package/src/credits/auto-topup-charge.test.ts +292 -0
- package/src/credits/auto-topup-charge.ts +171 -0
- package/src/credits/auto-topup-event-log-repository.test.ts +99 -0
- package/src/credits/auto-topup-event-log-repository.ts +30 -0
- package/src/credits/auto-topup-schedule.test.ts +179 -0
- package/src/credits/auto-topup-schedule.ts +93 -0
- package/src/credits/auto-topup-settings-repository.test.ts +123 -0
- package/src/credits/auto-topup-settings-repository.ts +245 -0
- package/src/credits/auto-topup-usage.test.ts +220 -0
- package/src/credits/auto-topup-usage.ts +68 -0
- package/src/credits/credit-expiry-cron.test.ts +125 -0
- package/src/credits/credit-expiry-cron.ts +76 -0
- package/src/credits/credit-ledger-extra.test.ts +57 -0
- package/src/credits/credit-ledger.bench.ts +56 -0
- package/src/credits/credit-ledger.test.ts +276 -0
- package/src/credits/credit-ledger.ts +450 -0
- package/src/credits/credit-transaction-repository.test.ts +274 -0
- package/src/credits/credit-transaction-repository.ts +62 -0
- package/src/credits/credit.test.ts +234 -0
- package/src/credits/credit.ts +160 -0
- package/src/credits/dividend-cron.test.ts +158 -0
- package/src/credits/dividend-cron.ts +127 -0
- package/src/credits/dividend-repository.test.ts +223 -0
- package/src/credits/dividend-repository.ts +182 -0
- package/src/credits/index.ts +25 -0
- package/src/credits/repository-types.ts +33 -0
- package/src/credits/signup-grant.test.ts +63 -0
- package/src/credits/signup-grant.ts +44 -0
- package/src/credits/tenant-customer-repository.ts +28 -0
- package/src/db/auth-user-repository.ts +124 -0
- package/src/db/credit-column.ts +17 -0
- package/src/db/index.ts +21 -0
- package/src/db/schema/account-deletion-requests.ts +41 -0
- package/src/db/schema/account-export-requests.ts +24 -0
- package/src/db/schema/admin-audit.ts +26 -0
- package/src/db/schema/admin-users.ts +31 -0
- package/src/db/schema/affiliate-fraud.ts +23 -0
- package/src/db/schema/affiliate.ts +38 -0
- package/src/db/schema/coupon-codes.ts +22 -0
- package/src/db/schema/credit-auto-topup-settings.ts +32 -0
- package/src/db/schema/credit-auto-topup.ts +26 -0
- package/src/db/schema/credits.ts +44 -0
- package/src/db/schema/dividend-distributions.ts +24 -0
- package/src/db/schema/email-notifications.ts +26 -0
- package/src/db/schema/index.ts +33 -0
- package/src/db/schema/meter-events.ts +70 -0
- package/src/db/schema/notification-preferences.ts +19 -0
- package/src/db/schema/notification-queue.ts +45 -0
- package/src/db/schema/org-memberships.ts +20 -0
- package/src/db/schema/organization-members.ts +37 -0
- package/src/db/schema/payram.ts +26 -0
- package/src/db/schema/platform-api-keys.ts +25 -0
- package/src/db/schema/promotion-redemptions.ts +23 -0
- package/src/db/schema/promotions.ts +57 -0
- package/src/db/schema/provider-credentials.ts +41 -0
- package/src/db/schema/rate-limit-entries.ts +12 -0
- package/src/db/schema/secret-audit-log.ts +20 -0
- package/src/db/schema/session-usage.ts +24 -0
- package/src/db/schema/spending-limits.ts +9 -0
- package/src/db/schema/tenant-addons.ts +14 -0
- package/src/db/schema/tenant-api-keys.ts +26 -0
- package/src/db/schema/tenant-capability-settings.ts +17 -0
- package/src/db/schema/tenant-customers.ts +35 -0
- package/src/db/schema/tenants.ts +23 -0
- package/src/db/schema/user-roles.ts +23 -0
- package/src/db/schema/webhook-seen-events.ts +14 -0
- package/src/email/billing-emails.test.ts +198 -0
- package/src/email/billing-emails.ts +211 -0
- package/src/email/client.test.ts +149 -0
- package/src/email/client.ts +137 -0
- package/src/email/drizzle-billing-email-repository.test.ts +52 -0
- package/src/email/drizzle-billing-email-repository.ts +59 -0
- package/src/email/index.ts +57 -0
- package/src/email/notification-preferences-store.test.ts +102 -0
- package/src/email/notification-preferences-store.ts +90 -0
- package/src/email/notification-queue-store.test.ts +215 -0
- package/src/email/notification-queue-store.ts +127 -0
- package/src/email/notification-repository-types.ts +101 -0
- package/src/email/notification-service.test.ts +178 -0
- package/src/email/notification-service.ts +265 -0
- package/src/email/notification-templates.test.ts +261 -0
- package/src/email/notification-templates.ts +727 -0
- package/src/email/notification-worker.test.ts +189 -0
- package/src/email/notification-worker.ts +133 -0
- package/src/email/require-verified.ts +65 -0
- package/src/email/resend-adapter.test.ts +253 -0
- package/src/email/resend-adapter.ts +157 -0
- package/src/email/templates.test.ts +217 -0
- package/src/email/templates.ts +469 -0
- package/src/email/verification.test.ts +185 -0
- package/src/email/verification.ts +110 -0
- package/src/index.ts +51 -0
- package/src/metering/aggregator.test.ts +239 -0
- package/src/metering/aggregator.ts +160 -0
- package/src/metering/dlq.test.ts +134 -0
- package/src/metering/dlq.ts +102 -0
- package/src/metering/drizzle-usage-summary-repository.ts +167 -0
- package/src/metering/emitter.test.ts +202 -0
- package/src/metering/emitter.ts +227 -0
- package/src/metering/index.ts +21 -0
- package/src/metering/load-test.bench.ts +130 -0
- package/src/metering/meter-event-repository.ts +87 -0
- package/src/metering/meter-repositories.test.ts +491 -0
- package/src/metering/metering.test.ts +1317 -0
- package/src/metering/reconciliation-cron.test.ts +202 -0
- package/src/metering/reconciliation-cron.ts +134 -0
- package/src/metering/reconciliation-repository.test.ts +196 -0
- package/src/metering/reconciliation-repository.ts +83 -0
- package/src/metering/types.ts +93 -0
- package/src/metering/wal.test.ts +222 -0
- package/src/metering/wal.ts +139 -0
- package/src/middleware/csrf.test.ts +178 -0
- package/src/middleware/csrf.ts +101 -0
- package/src/middleware/drizzle-rate-limit-repository.test.ts +97 -0
- package/src/middleware/drizzle-rate-limit-repository.ts +57 -0
- package/src/middleware/get-client-ip.test.ts +49 -0
- package/src/middleware/get-client-ip.ts +62 -0
- package/src/middleware/index.ts +12 -0
- package/src/middleware/rate-limit-repository.ts +22 -0
- package/src/middleware/rate-limit.test.ts +338 -0
- package/src/middleware/rate-limit.ts +169 -0
- package/src/security/credential-vault/audit-repository.test.ts +91 -0
- package/src/security/credential-vault/audit-repository.ts +64 -0
- package/src/security/credential-vault/credential-repository.test.ts +264 -0
- package/src/security/credential-vault/credential-repository.ts +233 -0
- package/src/security/credential-vault/index.ts +26 -0
- package/src/security/credential-vault/key-rotation.test.ts +139 -0
- package/src/security/credential-vault/key-rotation.ts +70 -0
- package/src/security/credential-vault/migrate-plaintext.test.ts +138 -0
- package/src/security/credential-vault/migrate-plaintext.ts +101 -0
- package/src/security/credential-vault/migration-check.test.ts +533 -0
- package/src/security/credential-vault/migration-check.ts +88 -0
- package/src/security/credential-vault/store.test.ts +569 -0
- package/src/security/credential-vault/store.ts +284 -0
- package/src/security/encryption.test.ts +114 -0
- package/src/security/encryption.ts +65 -0
- package/src/security/host-validation.test.ts +136 -0
- package/src/security/host-validation.ts +116 -0
- package/src/security/index.ts +59 -0
- package/src/security/key-audit.test.ts +57 -0
- package/src/security/key-audit.ts +45 -0
- package/src/security/key-injection.test.ts +131 -0
- package/src/security/key-injection.ts +71 -0
- package/src/security/key-validation.test.ts +111 -0
- package/src/security/key-validation.ts +84 -0
- package/src/security/redirect-allowlist.test.ts +70 -0
- package/src/security/redirect-allowlist.ts +35 -0
- package/src/security/tenant-keys/capability-settings-store.test.ts +98 -0
- package/src/security/tenant-keys/capability-settings-store.ts +53 -0
- package/src/security/tenant-keys/index.ts +10 -0
- package/src/security/tenant-keys/key-resolution-repository.test.ts +95 -0
- package/src/security/tenant-keys/key-resolution-repository.ts +31 -0
- package/src/security/tenant-keys/key-resolution.test.ts +173 -0
- package/src/security/tenant-keys/key-resolution.ts +87 -0
- package/src/security/tenant-keys/org-key-resolution.test.ts +217 -0
- package/src/security/tenant-keys/org-key-resolution.ts +76 -0
- package/src/security/tenant-keys/tenant-key-repository.test.ts +143 -0
- package/src/security/tenant-keys/tenant-key-repository.ts +130 -0
- package/src/security/types.ts +43 -0
- package/src/tenancy/drizzle-org-repository.ts +169 -0
- package/src/tenancy/index.ts +6 -0
- package/src/tenancy/org-member-repository.ts +159 -0
- package/src/tenancy/org-repository.test.ts +172 -0
- package/src/tenancy/org-service.test.ts +634 -0
- package/src/tenancy/org-service.ts +290 -0
- package/src/test/db.ts +97 -0
- package/src/trpc/index.ts +11 -0
- package/src/trpc/init.test.ts +196 -0
- package/src/trpc/init.ts +138 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { and, desc, eq, gte, lt, sql } from "drizzle-orm";
|
|
2
|
+
import type { PlatformDb } from "../db/index.js";
|
|
3
|
+
import { adminUsers } from "../db/schema/admin-users.js";
|
|
4
|
+
import { creditTransactions } from "../db/schema/credits.js";
|
|
5
|
+
import { dividendDistributions } from "../db/schema/dividend-distributions.js";
|
|
6
|
+
import { Credit } from "./credit.js";
|
|
7
|
+
import type { DividendHistoryEntry, DividendStats } from "./repository-types.js";
|
|
8
|
+
|
|
9
|
+
export type { DividendHistoryEntry, DividendStats };
|
|
10
|
+
|
|
11
|
+
export interface DigestTenantRow {
|
|
12
|
+
tenantId: string;
|
|
13
|
+
total: Credit;
|
|
14
|
+
distributionCount: number;
|
|
15
|
+
avgPool: Credit;
|
|
16
|
+
avgActiveUsers: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IDividendRepository {
|
|
20
|
+
getStats(tenantId: string): Promise<DividendStats>;
|
|
21
|
+
getHistory(tenantId: string, limit: number, offset: number): Promise<DividendHistoryEntry[]>;
|
|
22
|
+
getLifetimeTotal(tenantId: string): Promise<Credit>;
|
|
23
|
+
/** Aggregate dividend distributions per tenant for a date window [windowStart, windowEnd). */
|
|
24
|
+
getDigestTenantAggregates(windowStart: string, windowEnd: string): Promise<DigestTenantRow[]>;
|
|
25
|
+
/** Resolve email for a tenant from admin_users. Returns undefined if no row exists. */
|
|
26
|
+
getTenantEmail(tenantId: string): Promise<string | undefined>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class DrizzleDividendRepository implements IDividendRepository {
|
|
30
|
+
constructor(private readonly db: PlatformDb) {}
|
|
31
|
+
|
|
32
|
+
async getStats(tenantId: string): Promise<DividendStats> {
|
|
33
|
+
// 1. Pool = sum of purchase amounts from yesterday UTC
|
|
34
|
+
const poolRow = (
|
|
35
|
+
await this.db
|
|
36
|
+
// raw SQL: Drizzle cannot express COALESCE(SUM(...), 0) aggregate
|
|
37
|
+
.select({ total: sql<number>`COALESCE(SUM(${creditTransactions.amount}), 0)` })
|
|
38
|
+
.from(creditTransactions)
|
|
39
|
+
.where(
|
|
40
|
+
and(
|
|
41
|
+
eq(creditTransactions.type, "purchase"),
|
|
42
|
+
// raw SQL: Drizzle cannot express date_trunc with interval arithmetic
|
|
43
|
+
sql`${creditTransactions.createdAt}::timestamp >= date_trunc('day', timezone('UTC', now())) - INTERVAL '1 day'`,
|
|
44
|
+
sql`${creditTransactions.createdAt}::timestamp < date_trunc('day', timezone('UTC', now()))`,
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
)[0];
|
|
48
|
+
const poolCents = poolRow?.total ?? 0;
|
|
49
|
+
const pool = Credit.fromCents(poolCents);
|
|
50
|
+
|
|
51
|
+
// 2. Active users = distinct tenants with a purchase in the last 7 days
|
|
52
|
+
const activeRow = (
|
|
53
|
+
await this.db
|
|
54
|
+
// raw SQL: Drizzle cannot express COUNT(DISTINCT col)
|
|
55
|
+
.select({ count: sql<number>`COUNT(DISTINCT ${creditTransactions.tenantId})` })
|
|
56
|
+
.from(creditTransactions)
|
|
57
|
+
.where(
|
|
58
|
+
and(
|
|
59
|
+
eq(creditTransactions.type, "purchase"),
|
|
60
|
+
// raw SQL: Drizzle cannot express timestamp comparison with interval arithmetic
|
|
61
|
+
sql`${creditTransactions.createdAt}::timestamp >= timezone('UTC', now()) - INTERVAL '7 days'`,
|
|
62
|
+
),
|
|
63
|
+
)
|
|
64
|
+
)[0];
|
|
65
|
+
const activeUsers = activeRow?.count ?? 0;
|
|
66
|
+
|
|
67
|
+
// 3. Per-user projection (avoid division by zero)
|
|
68
|
+
const perUser = activeUsers > 0 ? Credit.fromRaw(Math.floor(pool.toRaw() / activeUsers)) : Credit.ZERO;
|
|
69
|
+
|
|
70
|
+
// 4. Next distribution = midnight UTC tonight
|
|
71
|
+
const now = new Date();
|
|
72
|
+
const nextMidnight = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 0, 0));
|
|
73
|
+
const nextDistributionAt = nextMidnight.toISOString();
|
|
74
|
+
|
|
75
|
+
// 5. User eligibility — last purchase within 7 days
|
|
76
|
+
const userPurchaseRow = (
|
|
77
|
+
await this.db
|
|
78
|
+
.select({ createdAt: creditTransactions.createdAt })
|
|
79
|
+
.from(creditTransactions)
|
|
80
|
+
.where(and(eq(creditTransactions.tenantId, tenantId), eq(creditTransactions.type, "purchase")))
|
|
81
|
+
.orderBy(desc(creditTransactions.createdAt))
|
|
82
|
+
.limit(1)
|
|
83
|
+
)[0];
|
|
84
|
+
|
|
85
|
+
let userEligible = false;
|
|
86
|
+
let userLastPurchaseAt: string | null = null;
|
|
87
|
+
let userWindowExpiresAt: string | null = null;
|
|
88
|
+
|
|
89
|
+
if (userPurchaseRow) {
|
|
90
|
+
const rawTs = userPurchaseRow.createdAt;
|
|
91
|
+
// Parse the timestamp directly. PGlite may return ISO strings with or without
|
|
92
|
+
// timezone suffix. JavaScript's Date constructor handles ISO 8601 strings natively.
|
|
93
|
+
const lastPurchase = new Date(rawTs);
|
|
94
|
+
userLastPurchaseAt = lastPurchase.toISOString();
|
|
95
|
+
|
|
96
|
+
const windowExpiry = new Date(lastPurchase.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
97
|
+
userWindowExpiresAt = windowExpiry.toISOString();
|
|
98
|
+
|
|
99
|
+
userEligible = windowExpiry.getTime() > Date.now();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
pool,
|
|
104
|
+
activeUsers,
|
|
105
|
+
perUser,
|
|
106
|
+
nextDistributionAt,
|
|
107
|
+
userEligible,
|
|
108
|
+
userLastPurchaseAt,
|
|
109
|
+
userWindowExpiresAt,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getHistory(tenantId: string, limit: number, offset: number): Promise<DividendHistoryEntry[]> {
|
|
114
|
+
const safeLimit = Math.min(Math.max(1, limit), 250);
|
|
115
|
+
const safeOffset = Math.max(0, offset);
|
|
116
|
+
|
|
117
|
+
const rows = await this.db
|
|
118
|
+
.select({
|
|
119
|
+
date: dividendDistributions.date,
|
|
120
|
+
amountCents: dividendDistributions.amountCents,
|
|
121
|
+
poolCents: dividendDistributions.poolCents,
|
|
122
|
+
activeUsers: dividendDistributions.activeUsers,
|
|
123
|
+
})
|
|
124
|
+
.from(dividendDistributions)
|
|
125
|
+
.where(eq(dividendDistributions.tenantId, tenantId))
|
|
126
|
+
.orderBy(desc(dividendDistributions.date))
|
|
127
|
+
.limit(safeLimit)
|
|
128
|
+
.offset(safeOffset);
|
|
129
|
+
|
|
130
|
+
return rows.map((row) => ({
|
|
131
|
+
date: row.date,
|
|
132
|
+
amount: Credit.fromCents(row.amountCents),
|
|
133
|
+
pool: Credit.fromCents(row.poolCents),
|
|
134
|
+
activeUsers: row.activeUsers,
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getLifetimeTotal(tenantId: string): Promise<Credit> {
|
|
139
|
+
const row = (
|
|
140
|
+
await this.db
|
|
141
|
+
// raw SQL: Drizzle cannot express COALESCE(SUM(...), 0) aggregate
|
|
142
|
+
.select({ total: sql<number>`COALESCE(SUM(${dividendDistributions.amountCents}), 0)` })
|
|
143
|
+
.from(dividendDistributions)
|
|
144
|
+
.where(eq(dividendDistributions.tenantId, tenantId))
|
|
145
|
+
)[0];
|
|
146
|
+
return Credit.fromCents(row?.total ?? 0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async getDigestTenantAggregates(windowStart: string, windowEnd: string): Promise<DigestTenantRow[]> {
|
|
150
|
+
const rows = await this.db
|
|
151
|
+
.select({
|
|
152
|
+
tenantId: dividendDistributions.tenantId,
|
|
153
|
+
// raw SQL: Drizzle cannot express SUM/COUNT(DISTINCT)/AVG with CAST aggregates
|
|
154
|
+
totalCents: sql<number>`SUM(${dividendDistributions.amountCents})`,
|
|
155
|
+
distributionCount: sql<number>`COUNT(DISTINCT ${dividendDistributions.date})`,
|
|
156
|
+
avgPoolCents: sql<number>`CAST(AVG(${dividendDistributions.poolCents}) AS INTEGER)`,
|
|
157
|
+
avgActiveUsers: sql<number>`CAST(AVG(${dividendDistributions.activeUsers}) AS INTEGER)`,
|
|
158
|
+
})
|
|
159
|
+
.from(dividendDistributions)
|
|
160
|
+
.where(and(gte(dividendDistributions.date, windowStart), lt(dividendDistributions.date, windowEnd)))
|
|
161
|
+
.groupBy(dividendDistributions.tenantId);
|
|
162
|
+
|
|
163
|
+
return rows.map((row) => ({
|
|
164
|
+
tenantId: row.tenantId,
|
|
165
|
+
total: Credit.fromCents(row.totalCents),
|
|
166
|
+
distributionCount: row.distributionCount,
|
|
167
|
+
avgPool: Credit.fromCents(row.avgPoolCents),
|
|
168
|
+
avgActiveUsers: row.avgActiveUsers,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async getTenantEmail(tenantId: string): Promise<string | undefined> {
|
|
173
|
+
const row = (
|
|
174
|
+
await this.db
|
|
175
|
+
.select({ email: adminUsers.email })
|
|
176
|
+
.from(adminUsers)
|
|
177
|
+
.where(eq(adminUsers.tenantId, tenantId))
|
|
178
|
+
.limit(1)
|
|
179
|
+
)[0];
|
|
180
|
+
return row?.email;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
AutoTopupSettings,
|
|
3
|
+
IAutoTopupSettingsRepository,
|
|
4
|
+
} from "./auto-topup-settings-repository.js";
|
|
5
|
+
export {
|
|
6
|
+
ALLOWED_SCHEDULE_INTERVALS,
|
|
7
|
+
ALLOWED_THRESHOLDS,
|
|
8
|
+
ALLOWED_TOPUP_AMOUNTS,
|
|
9
|
+
computeNextScheduleAt,
|
|
10
|
+
DrizzleAutoTopupSettingsRepository,
|
|
11
|
+
} from "./auto-topup-settings-repository.js";
|
|
12
|
+
export type { CreditExpiryCronConfig, CreditExpiryCronResult } from "./credit-expiry-cron.js";
|
|
13
|
+
export { runCreditExpiryCron } from "./credit-expiry-cron.js";
|
|
14
|
+
export type {
|
|
15
|
+
CreditTransaction,
|
|
16
|
+
CreditType,
|
|
17
|
+
DebitType,
|
|
18
|
+
HistoryOptions,
|
|
19
|
+
ICreditLedger,
|
|
20
|
+
TransactionType,
|
|
21
|
+
} from "./credit-ledger.js";
|
|
22
|
+
export { CreditLedger, DrizzleCreditLedger, InsufficientBalanceError } from "./credit-ledger.js";
|
|
23
|
+
export { grantSignupCredits, SIGNUP_GRANT } from "./signup-grant.js";
|
|
24
|
+
export { Credit } from "./credit.js";
|
|
25
|
+
export type { ITenantCustomerRepository, TenantCustomerRow } from "./tenant-customer-repository.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Credit } from "./credit.js";
|
|
2
|
+
|
|
3
|
+
/** Domain type for a provisioned phone number tracked for monthly billing. */
|
|
4
|
+
export interface ProvisionedPhoneNumber {
|
|
5
|
+
sid: string;
|
|
6
|
+
tenantId: string;
|
|
7
|
+
phoneNumber: string;
|
|
8
|
+
provisionedAt: string;
|
|
9
|
+
lastBilledAt: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DividendStats {
|
|
13
|
+
pool: Credit;
|
|
14
|
+
activeUsers: number;
|
|
15
|
+
perUser: Credit;
|
|
16
|
+
nextDistributionAt: string;
|
|
17
|
+
userEligible: boolean;
|
|
18
|
+
userLastPurchaseAt: string | null;
|
|
19
|
+
userWindowExpiresAt: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DividendHistoryEntry {
|
|
23
|
+
date: string;
|
|
24
|
+
amount: Credit;
|
|
25
|
+
pool: Credit;
|
|
26
|
+
activeUsers: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WebhookSeenEvent {
|
|
30
|
+
eventId: string;
|
|
31
|
+
source: string;
|
|
32
|
+
seenAt: number;
|
|
33
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import type { PlatformDb } from "../db/index.js";
|
|
4
|
+
import { createTestDb, truncateAllTables } from "../test/db.js";
|
|
5
|
+
import { CreditLedger } from "./credit-ledger.js";
|
|
6
|
+
import { grantSignupCredits, SIGNUP_GRANT } from "./signup-grant.js";
|
|
7
|
+
|
|
8
|
+
describe("grantSignupCredits", () => {
|
|
9
|
+
let pool: PGlite;
|
|
10
|
+
let db: PlatformDb;
|
|
11
|
+
let ledger: CreditLedger;
|
|
12
|
+
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
({ db, pool } = await createTestDb());
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
await pool.close();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
await truncateAllTables(pool);
|
|
23
|
+
ledger = new CreditLedger(db);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("grants credits to a new tenant and returns true", async () => {
|
|
27
|
+
const result = await grantSignupCredits(ledger, "tenant-1");
|
|
28
|
+
expect(result).toBe(true);
|
|
29
|
+
expect((await ledger.balance("tenant-1")).toCents()).toBe(SIGNUP_GRANT.toCents());
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("returns false for duplicate grant (idempotency)", async () => {
|
|
33
|
+
await grantSignupCredits(ledger, "tenant-1");
|
|
34
|
+
const result = await grantSignupCredits(ledger, "tenant-1");
|
|
35
|
+
expect(result).toBe(false);
|
|
36
|
+
expect((await ledger.balance("tenant-1")).toCents()).toBe(SIGNUP_GRANT.toCents());
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("grants independently to different tenants", async () => {
|
|
40
|
+
await grantSignupCredits(ledger, "tenant-1");
|
|
41
|
+
await grantSignupCredits(ledger, "tenant-2");
|
|
42
|
+
expect((await ledger.balance("tenant-1")).toCents()).toBe(SIGNUP_GRANT.toCents());
|
|
43
|
+
expect((await ledger.balance("tenant-2")).toCents()).toBe(SIGNUP_GRANT.toCents());
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("SIGNUP_GRANT.toCents() equals 500", () => {
|
|
47
|
+
expect(SIGNUP_GRANT.toCents()).toBe(500);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("returns false when credit() throws a unique constraint violation (TOCTOU race)", async () => {
|
|
51
|
+
// Simulate two concurrent requests: both pass hasReferenceId check,
|
|
52
|
+
// then the second credit() call loses the race and gets a unique constraint error.
|
|
53
|
+
const uniqueErr = Object.assign(new Error("duplicate key value violates unique constraint"), {
|
|
54
|
+
code: "23505",
|
|
55
|
+
});
|
|
56
|
+
const racingLedger = new CreditLedger(db);
|
|
57
|
+
vi.spyOn(racingLedger, "hasReferenceId").mockResolvedValue(false);
|
|
58
|
+
vi.spyOn(racingLedger, "credit").mockRejectedValue(uniqueErr);
|
|
59
|
+
|
|
60
|
+
const result = await grantSignupCredits(racingLedger, "tenant-race");
|
|
61
|
+
expect(result).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Credit } from "./credit.js";
|
|
2
|
+
import type { ICreditLedger } from "./credit-ledger.js";
|
|
3
|
+
|
|
4
|
+
/** Signup grant amount: $5.00 */
|
|
5
|
+
export const SIGNUP_GRANT = Credit.fromDollars(5);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Grant the signup credit bonus to a newly verified tenant.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent: uses `signup:<tenantId>` as referenceId to prevent double-grants.
|
|
11
|
+
*
|
|
12
|
+
* @returns true if the grant was applied, false if already granted.
|
|
13
|
+
*/
|
|
14
|
+
export async function grantSignupCredits(ledger: ICreditLedger, tenantId: string): Promise<boolean> {
|
|
15
|
+
const refId = `signup:${tenantId}`;
|
|
16
|
+
|
|
17
|
+
// Idempotency check
|
|
18
|
+
if (await ledger.hasReferenceId(refId)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
await ledger.credit(
|
|
24
|
+
tenantId,
|
|
25
|
+
SIGNUP_GRANT,
|
|
26
|
+
"signup_grant",
|
|
27
|
+
"Welcome bonus — $5.00 credit on email verification",
|
|
28
|
+
refId,
|
|
29
|
+
);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
// Concurrent verify-email request won the race and already inserted the same referenceId.
|
|
32
|
+
// Treat unique constraint violation as a no-op (idempotent).
|
|
33
|
+
if (isUniqueConstraintViolation(err)) return false;
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isUniqueConstraintViolation(err: unknown): boolean {
|
|
41
|
+
if (!(err instanceof Error)) return false;
|
|
42
|
+
if ((err as { code?: string }).code === "23505") return true;
|
|
43
|
+
return err.message.includes("UNIQUE") || err.message.includes("duplicate key");
|
|
44
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stub interface for tenant-customer repository.
|
|
3
|
+
* The Drizzle implementation lives in the billing module (extracted in Task 12).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface TenantCustomerRow {
|
|
7
|
+
tenant: string;
|
|
8
|
+
processor_customer_id: string;
|
|
9
|
+
processor: string;
|
|
10
|
+
tier: string;
|
|
11
|
+
billing_hold: number;
|
|
12
|
+
inference_mode: string;
|
|
13
|
+
created_at: number;
|
|
14
|
+
updated_at: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ITenantCustomerRepository {
|
|
18
|
+
getByTenant(tenant: string): Promise<TenantCustomerRow | null>;
|
|
19
|
+
getByProcessorCustomerId(processorCustomerId: string): Promise<TenantCustomerRow | null>;
|
|
20
|
+
upsert(row: { tenant: string; processorCustomerId: string; tier?: string }): Promise<void>;
|
|
21
|
+
setTier(tenant: string, tier: string): Promise<void>;
|
|
22
|
+
setBillingHold(tenant: string, hold: boolean): Promise<void>;
|
|
23
|
+
hasBillingHold(tenant: string): Promise<boolean>;
|
|
24
|
+
getInferenceMode(tenant: string): Promise<string>;
|
|
25
|
+
setInferenceMode(tenant: string, mode: string): Promise<void>;
|
|
26
|
+
list(): Promise<TenantCustomerRow[]>;
|
|
27
|
+
buildCustomerIdMap(): Promise<Record<string, string>>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth user repository — read/write the better-auth PostgreSQL user and account tables.
|
|
3
|
+
*
|
|
4
|
+
* Uses raw pg queries because better-auth manages its own schema
|
|
5
|
+
* independently of Drizzle.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { hashPassword, verifyPassword } from "better-auth/crypto";
|
|
9
|
+
import type { Pool } from "pg";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Ensure the twoFactorEnabled column exists on the better-auth user table.
|
|
13
|
+
* Idempotent — safe to call on every startup alongside runAuthMigrations().
|
|
14
|
+
*/
|
|
15
|
+
export async function initTwoFactorSchema(pool: Pool): Promise<void> {
|
|
16
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
17
|
+
await pool.query(`ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "twoFactorEnabled" BOOLEAN NOT NULL DEFAULT false`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AuthUser {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
email: string;
|
|
24
|
+
image: string | null;
|
|
25
|
+
twoFactorEnabled: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface LinkedAccount {
|
|
29
|
+
id: string;
|
|
30
|
+
providerId: string;
|
|
31
|
+
accountId: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface IAuthUserRepository {
|
|
35
|
+
getUser(userId: string): Promise<AuthUser | null>;
|
|
36
|
+
updateUser(userId: string, data: { name?: string; image?: string | null }): Promise<AuthUser>;
|
|
37
|
+
changePassword(userId: string, currentPassword: string, newPassword: string): Promise<boolean>;
|
|
38
|
+
listAccounts(userId: string): Promise<LinkedAccount[]>;
|
|
39
|
+
unlinkAccount(userId: string, providerId: string): Promise<boolean>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class BetterAuthUserRepository implements IAuthUserRepository {
|
|
43
|
+
constructor(private readonly pool: Pool) {}
|
|
44
|
+
|
|
45
|
+
async getUser(userId: string): Promise<AuthUser | null> {
|
|
46
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
47
|
+
const { rows } = await this.pool.query(
|
|
48
|
+
`SELECT id, name, email, image, "twoFactorEnabled" FROM "user" WHERE id = $1`,
|
|
49
|
+
[userId],
|
|
50
|
+
);
|
|
51
|
+
const row = rows[0] as AuthUser | undefined;
|
|
52
|
+
if (row) row.twoFactorEnabled = row.twoFactorEnabled ?? false;
|
|
53
|
+
return row ?? null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async updateUser(userId: string, data: { name?: string; image?: string | null }): Promise<AuthUser> {
|
|
57
|
+
const fields: string[] = [];
|
|
58
|
+
const values: unknown[] = [];
|
|
59
|
+
let paramIndex = 1;
|
|
60
|
+
|
|
61
|
+
if (data.name !== undefined) {
|
|
62
|
+
fields.push(`name = $${paramIndex++}`);
|
|
63
|
+
values.push(data.name);
|
|
64
|
+
}
|
|
65
|
+
if (data.image !== undefined) {
|
|
66
|
+
fields.push(`image = $${paramIndex++}`);
|
|
67
|
+
values.push(data.image);
|
|
68
|
+
}
|
|
69
|
+
if (fields.length > 0) {
|
|
70
|
+
values.push(userId);
|
|
71
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
72
|
+
await this.pool.query(`UPDATE "user" SET ${fields.join(", ")} WHERE id = $${paramIndex}`, values);
|
|
73
|
+
}
|
|
74
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
75
|
+
const { rows } = await this.pool.query(
|
|
76
|
+
`SELECT id, name, email, image, "twoFactorEnabled" FROM "user" WHERE id = $1`,
|
|
77
|
+
[userId],
|
|
78
|
+
);
|
|
79
|
+
if (rows.length === 0) throw new Error(`User not found: ${userId}`);
|
|
80
|
+
const result = rows[0] as AuthUser;
|
|
81
|
+
result.twoFactorEnabled = result.twoFactorEnabled ?? false;
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async changePassword(userId: string, currentPassword: string, newPassword: string): Promise<boolean> {
|
|
86
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
87
|
+
const { rows } = await this.pool.query(
|
|
88
|
+
`SELECT password FROM account WHERE user_id = $1 AND provider_id = 'credential'`,
|
|
89
|
+
[userId],
|
|
90
|
+
);
|
|
91
|
+
const row = rows[0] as { password: string } | undefined;
|
|
92
|
+
if (!row?.password) return false;
|
|
93
|
+
const valid = await verifyPassword({ hash: row.password, password: currentPassword });
|
|
94
|
+
if (!valid) return false;
|
|
95
|
+
const newHash = await hashPassword(newPassword);
|
|
96
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
97
|
+
await this.pool.query(`UPDATE account SET password = $1 WHERE user_id = $2 AND provider_id = 'credential'`, [
|
|
98
|
+
newHash,
|
|
99
|
+
userId,
|
|
100
|
+
]);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async listAccounts(userId: string): Promise<LinkedAccount[]> {
|
|
105
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
106
|
+
const { rows } = await this.pool.query(`SELECT id, provider_id, account_id FROM account WHERE user_id = $1`, [
|
|
107
|
+
userId,
|
|
108
|
+
]);
|
|
109
|
+
return rows.map((r: { id: string; provider_id: string; account_id: string }) => ({
|
|
110
|
+
id: r.id,
|
|
111
|
+
providerId: r.provider_id,
|
|
112
|
+
accountId: r.account_id,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async unlinkAccount(userId: string, providerId: string): Promise<boolean> {
|
|
117
|
+
// raw SQL: better-auth manages its own schema outside Drizzle
|
|
118
|
+
const { rowCount } = await this.pool.query(`DELETE FROM account WHERE user_id = $1 AND provider_id = $2`, [
|
|
119
|
+
userId,
|
|
120
|
+
providerId,
|
|
121
|
+
]);
|
|
122
|
+
return (rowCount ?? 0) > 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { customType } from "drizzle-orm/pg-core";
|
|
2
|
+
import { Credit } from "../credits/credit.js";
|
|
3
|
+
|
|
4
|
+
export const creditColumn = customType<{
|
|
5
|
+
data: Credit;
|
|
6
|
+
driverData: string;
|
|
7
|
+
}>({
|
|
8
|
+
dataType() {
|
|
9
|
+
return "bigint"; // nanodollar values exceed int4 range
|
|
10
|
+
},
|
|
11
|
+
toDriver(value: Credit): string {
|
|
12
|
+
return String(value.toRaw());
|
|
13
|
+
},
|
|
14
|
+
fromDriver(value: number | string): Credit {
|
|
15
|
+
return Credit.fromRaw(Number(value));
|
|
16
|
+
},
|
|
17
|
+
});
|
package/src/db/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
2
|
+
import type { PgDatabase, PgQueryResultHKT } from "drizzle-orm/pg-core";
|
|
3
|
+
import type { Pool } from "pg";
|
|
4
|
+
import * as schema from "./schema/index.js";
|
|
5
|
+
|
|
6
|
+
/** The platform schema type (subset of the full application schema). */
|
|
7
|
+
export type PlatformSchema = typeof schema;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Narrower DB type used by platform-core repositories.
|
|
11
|
+
* wopr-platform's full DrizzleDb (wider schema) satisfies this constraint.
|
|
12
|
+
*/
|
|
13
|
+
export type PlatformDb = PgDatabase<PgQueryResultHKT, PlatformSchema>;
|
|
14
|
+
|
|
15
|
+
/** Create a Drizzle database instance wrapping the given pg.Pool. */
|
|
16
|
+
export function createDb(pool: Pool): PlatformDb {
|
|
17
|
+
return drizzle(pool, { schema }) as unknown as PlatformDb;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { schema };
|
|
21
|
+
export { creditColumn } from "./credit-column.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { index, pgTable, text } from "drizzle-orm/pg-core";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Account deletion requests — tracks the lifecycle of GDPR deletion requests.
|
|
6
|
+
*
|
|
7
|
+
* States: pending -> completed | cancelled
|
|
8
|
+
*
|
|
9
|
+
* A pending request has a 30-day grace period. After the grace deadline,
|
|
10
|
+
* the deletion cron executes the purge and marks the request completed.
|
|
11
|
+
* Users can cancel a pending request within the grace period.
|
|
12
|
+
*/
|
|
13
|
+
export const accountDeletionRequests = pgTable(
|
|
14
|
+
"account_deletion_requests",
|
|
15
|
+
{
|
|
16
|
+
id: text("id").primaryKey(),
|
|
17
|
+
/** Tenant / user ID requesting deletion */
|
|
18
|
+
tenantId: text("tenant_id").notNull(),
|
|
19
|
+
/** User ID who initiated the request */
|
|
20
|
+
requestedBy: text("requested_by").notNull(),
|
|
21
|
+
/** pending | completed | cancelled */
|
|
22
|
+
status: text("status").notNull().default("pending"),
|
|
23
|
+
/** ISO timestamp after which deletion should execute */
|
|
24
|
+
deleteAfter: text("delete_after").notNull(),
|
|
25
|
+
/** Reason provided when the deletion was triggered */
|
|
26
|
+
reason: text("reason"),
|
|
27
|
+
/** Reason for cancellation (if cancelled) */
|
|
28
|
+
cancelReason: text("cancel_reason"),
|
|
29
|
+
/** ISO timestamp when purge completed */
|
|
30
|
+
completedAt: text("completed_at"),
|
|
31
|
+
/** JSON summary of what was deleted (table row counts) */
|
|
32
|
+
deletionSummary: text("deletion_summary"),
|
|
33
|
+
createdAt: text("created_at").notNull().default(sql`(now())`),
|
|
34
|
+
updatedAt: text("updated_at").notNull().default(sql`(now())`),
|
|
35
|
+
},
|
|
36
|
+
(table) => [
|
|
37
|
+
index("idx_acct_del_tenant").on(table.tenantId),
|
|
38
|
+
index("idx_acct_del_status").on(table.status),
|
|
39
|
+
index("idx_acct_del_delete_after").on(table.status, table.deleteAfter),
|
|
40
|
+
],
|
|
41
|
+
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { index, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Account export requests — tracks GDPR Article 15 data export requests.
|
|
5
|
+
*
|
|
6
|
+
* States: pending -> processing -> completed | failed
|
|
7
|
+
*/
|
|
8
|
+
export const accountExportRequests = pgTable(
|
|
9
|
+
"account_export_requests",
|
|
10
|
+
{
|
|
11
|
+
id: text("id").primaryKey(),
|
|
12
|
+
tenantId: text("tenant_id").notNull(),
|
|
13
|
+
requestedBy: text("requested_by").notNull(),
|
|
14
|
+
/** pending | processing | completed | failed */
|
|
15
|
+
status: text("status").notNull().default("pending"),
|
|
16
|
+
/** Export format — "json" by default */
|
|
17
|
+
format: text("format").notNull().default("json"),
|
|
18
|
+
/** Signed URL to download the export archive (set on completion) */
|
|
19
|
+
downloadUrl: text("download_url"),
|
|
20
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
21
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
22
|
+
},
|
|
23
|
+
(table) => [index("idx_acct_export_tenant").on(table.tenantId), index("idx_acct_export_status").on(table.status)],
|
|
24
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { bigint, index, pgTable, text } from "drizzle-orm/pg-core";
|
|
3
|
+
|
|
4
|
+
export const adminAuditLog = pgTable(
|
|
5
|
+
"admin_audit_log",
|
|
6
|
+
{
|
|
7
|
+
id: text("id").primaryKey(),
|
|
8
|
+
adminUser: text("admin_user").notNull(),
|
|
9
|
+
action: text("action").notNull(),
|
|
10
|
+
category: text("category").notNull(),
|
|
11
|
+
targetTenant: text("target_tenant"),
|
|
12
|
+
targetUser: text("target_user"),
|
|
13
|
+
details: text("details").notNull().default("{}"),
|
|
14
|
+
ipAddress: text("ip_address"),
|
|
15
|
+
userAgent: text("user_agent"),
|
|
16
|
+
createdAt: bigint("created_at", { mode: "number" })
|
|
17
|
+
.notNull()
|
|
18
|
+
.default(sql`(extract(epoch from now()) * 1000)::bigint`),
|
|
19
|
+
outcome: text("outcome"),
|
|
20
|
+
},
|
|
21
|
+
(table) => [
|
|
22
|
+
index("idx_admin_audit_admin").on(table.adminUser, table.createdAt),
|
|
23
|
+
index("idx_admin_audit_tenant").on(table.targetTenant, table.createdAt),
|
|
24
|
+
index("idx_admin_audit_action").on(table.action, table.createdAt),
|
|
25
|
+
],
|
|
26
|
+
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { bigint, check, index, integer, pgTable, text } from "drizzle-orm/pg-core";
|
|
3
|
+
|
|
4
|
+
export const adminUsers = pgTable(
|
|
5
|
+
"admin_users",
|
|
6
|
+
{
|
|
7
|
+
id: text("id").primaryKey(),
|
|
8
|
+
email: text("email").notNull(),
|
|
9
|
+
name: text("name"),
|
|
10
|
+
tenantId: text("tenant_id").notNull(),
|
|
11
|
+
status: text("status").notNull().default("active"),
|
|
12
|
+
role: text("role").notNull().default("user"),
|
|
13
|
+
creditBalanceCents: integer("credit_balance_cents").notNull().default(0),
|
|
14
|
+
agentCount: integer("agent_count").notNull().default(0),
|
|
15
|
+
lastSeen: bigint("last_seen", { mode: "number" }),
|
|
16
|
+
createdAt: bigint("created_at", { mode: "number" }).notNull(),
|
|
17
|
+
},
|
|
18
|
+
(table) => [
|
|
19
|
+
index("idx_admin_users_email").on(table.email),
|
|
20
|
+
index("idx_admin_users_tenant").on(table.tenantId),
|
|
21
|
+
index("idx_admin_users_status").on(table.status),
|
|
22
|
+
index("idx_admin_users_role").on(table.role),
|
|
23
|
+
index("idx_admin_users_created").on(table.createdAt),
|
|
24
|
+
index("idx_admin_users_last_seen").on(table.lastSeen),
|
|
25
|
+
check(
|
|
26
|
+
"chk_admin_users_status",
|
|
27
|
+
sql`${table.status} IN ('active', 'suspended', 'grace_period', 'dormant', 'banned')`,
|
|
28
|
+
),
|
|
29
|
+
check("chk_admin_users_role", sql`${table.role} IN ('platform_admin', 'tenant_admin', 'user')`),
|
|
30
|
+
],
|
|
31
|
+
);
|