@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,97 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { decrypt, generateInstanceKey } from "./encryption.js";
|
|
6
|
+
import { forwardSecretsToInstance, writeEncryptedSeed } from "./key-injection.js";
|
|
7
|
+
describe("key-injection", () => {
|
|
8
|
+
describe("writeEncryptedSeed", () => {
|
|
9
|
+
let tmpDir;
|
|
10
|
+
const key = generateInstanceKey();
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
const { mkdtemp } = await import("node:fs/promises");
|
|
13
|
+
tmpDir = await mkdtemp(path.join(os.tmpdir(), "wopr-test-"));
|
|
14
|
+
});
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
const { rm } = await import("node:fs/promises");
|
|
17
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
it("writes secrets.enc to the woprHome directory", async () => {
|
|
20
|
+
const woprHome = path.join(tmpDir, "instance-1");
|
|
21
|
+
const secrets = { ANTHROPIC_API_KEY: "sk-ant-test123" };
|
|
22
|
+
await writeEncryptedSeed(woprHome, secrets, key);
|
|
23
|
+
const seedPath = path.join(woprHome, "secrets.enc");
|
|
24
|
+
const content = await readFile(seedPath, "utf-8");
|
|
25
|
+
const payload = JSON.parse(content);
|
|
26
|
+
expect(payload).toHaveProperty("iv");
|
|
27
|
+
expect(payload).toHaveProperty("authTag");
|
|
28
|
+
expect(payload).toHaveProperty("ciphertext");
|
|
29
|
+
});
|
|
30
|
+
it("encrypted seed can be decrypted back to original secrets", async () => {
|
|
31
|
+
const woprHome = path.join(tmpDir, "instance-2");
|
|
32
|
+
const secrets = { DISCORD_TOKEN: "token123", OPENAI_KEY: "sk-openai" };
|
|
33
|
+
await writeEncryptedSeed(woprHome, secrets, key);
|
|
34
|
+
const seedPath = path.join(woprHome, "secrets.enc");
|
|
35
|
+
const content = await readFile(seedPath, "utf-8");
|
|
36
|
+
const payload = JSON.parse(content);
|
|
37
|
+
const decrypted = JSON.parse(decrypt(payload, key));
|
|
38
|
+
expect(decrypted).toEqual(secrets);
|
|
39
|
+
});
|
|
40
|
+
it("creates directories recursively", async () => {
|
|
41
|
+
const woprHome = path.join(tmpDir, "deep", "nested", "dir");
|
|
42
|
+
await writeEncryptedSeed(woprHome, { KEY: "val" }, key);
|
|
43
|
+
const content = await readFile(path.join(woprHome, "secrets.enc"), "utf-8");
|
|
44
|
+
expect(JSON.parse(content)).toHaveProperty("ciphertext");
|
|
45
|
+
});
|
|
46
|
+
it("seed file does NOT contain plaintext key values", async () => {
|
|
47
|
+
const woprHome = path.join(tmpDir, "instance-3");
|
|
48
|
+
const secrets = { ANTHROPIC_API_KEY: "sk-ant-api0xxxxxxxxxxxxxxxxxxxx" };
|
|
49
|
+
await writeEncryptedSeed(woprHome, secrets, key);
|
|
50
|
+
const content = await readFile(path.join(woprHome, "secrets.enc"), "utf-8");
|
|
51
|
+
expect(content).not.toContain("sk-ant-api0xxxxxxxxxxxxxxxxxxxx");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("forwardSecretsToInstance", () => {
|
|
55
|
+
const originalFetch = globalThis.fetch;
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
globalThis.fetch = vi.fn();
|
|
58
|
+
});
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
globalThis.fetch = originalFetch;
|
|
61
|
+
});
|
|
62
|
+
it("forwards body opaquely and returns ok on success", async () => {
|
|
63
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response(null, { status: 200 }));
|
|
64
|
+
const result = await forwardSecretsToInstance("http://container:3000", "session-token", '{"KEY":"val"}');
|
|
65
|
+
expect(result.ok).toBe(true);
|
|
66
|
+
expect(result.status).toBe(200);
|
|
67
|
+
expect(globalThis.fetch).toHaveBeenCalledWith("http://container:3000/config/secrets", expect.objectContaining({
|
|
68
|
+
method: "PUT",
|
|
69
|
+
body: '{"KEY":"val"}',
|
|
70
|
+
headers: expect.objectContaining({
|
|
71
|
+
Authorization: "Bearer session-token",
|
|
72
|
+
}),
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
it("returns error on non-ok response", async () => {
|
|
76
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response("Forbidden", { status: 403 }));
|
|
77
|
+
const result = await forwardSecretsToInstance("http://container:3000", "token", "{}");
|
|
78
|
+
expect(result.ok).toBe(false);
|
|
79
|
+
expect(result.status).toBe(403);
|
|
80
|
+
expect(result.error).toBe("Forbidden");
|
|
81
|
+
});
|
|
82
|
+
it("returns 502 on network error", async () => {
|
|
83
|
+
vi.mocked(globalThis.fetch).mockRejectedValue(new Error("Connection refused"));
|
|
84
|
+
const result = await forwardSecretsToInstance("http://container:3000", "token", "{}");
|
|
85
|
+
expect(result.ok).toBe(false);
|
|
86
|
+
expect(result.status).toBe(502);
|
|
87
|
+
expect(result.error).toBe("Connection refused");
|
|
88
|
+
});
|
|
89
|
+
it("returns 502 with fallback message when thrown value is not an Error", async () => {
|
|
90
|
+
vi.mocked(globalThis.fetch).mockRejectedValue("string error");
|
|
91
|
+
const result = await forwardSecretsToInstance("http://container:3000", "token", "{}");
|
|
92
|
+
expect(result.ok).toBe(false);
|
|
93
|
+
expect(result.status).toBe(502);
|
|
94
|
+
expect(result.error).toBe("Failed to forward secrets");
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Provider, ProviderEndpoint, ValidateKeyResponse } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Provider API endpoints used for key validation.
|
|
4
|
+
* For CORS-friendly providers, the browser can call these directly.
|
|
5
|
+
* For CORS-blocked providers (e.g., Anthropic), the platform proxy decrypts
|
|
6
|
+
* and validates in memory without ever logging the key.
|
|
7
|
+
* URLs are sourced from PROVIDER_API_URLS; headers are provider-specific.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PROVIDER_ENDPOINTS: Record<Provider, ProviderEndpoint>;
|
|
10
|
+
/**
|
|
11
|
+
* Validate a provider API key by making a lightweight read-only request.
|
|
12
|
+
* The key is held in memory only for the duration of the fetch and then discarded.
|
|
13
|
+
*
|
|
14
|
+
* SECURITY: This function must NEVER log, persist, or return the key itself.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateProviderKey(provider: Provider, key: string): Promise<ValidateKeyResponse>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { PROVIDER_API_URLS } from "../config/provider-endpoints.js";
|
|
2
|
+
/**
|
|
3
|
+
* Provider API endpoints used for key validation.
|
|
4
|
+
* For CORS-friendly providers, the browser can call these directly.
|
|
5
|
+
* For CORS-blocked providers (e.g., Anthropic), the platform proxy decrypts
|
|
6
|
+
* and validates in memory without ever logging the key.
|
|
7
|
+
* URLs are sourced from PROVIDER_API_URLS; headers are provider-specific.
|
|
8
|
+
*/
|
|
9
|
+
export const PROVIDER_ENDPOINTS = {
|
|
10
|
+
anthropic: {
|
|
11
|
+
url: PROVIDER_API_URLS.anthropic,
|
|
12
|
+
headers: (key) => ({
|
|
13
|
+
"x-api-key": key,
|
|
14
|
+
"anthropic-version": "2023-06-01",
|
|
15
|
+
}),
|
|
16
|
+
},
|
|
17
|
+
openai: {
|
|
18
|
+
url: PROVIDER_API_URLS.openai,
|
|
19
|
+
headers: (key) => ({
|
|
20
|
+
Authorization: `Bearer ${key}`,
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
google: {
|
|
24
|
+
url: PROVIDER_API_URLS.google,
|
|
25
|
+
headers: (key) => ({
|
|
26
|
+
"x-goog-api-key": key,
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
discord: {
|
|
30
|
+
url: PROVIDER_API_URLS.discord,
|
|
31
|
+
headers: (key) => ({
|
|
32
|
+
Authorization: `Bot ${key}`,
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
elevenlabs: {
|
|
36
|
+
url: PROVIDER_API_URLS.elevenlabs,
|
|
37
|
+
headers: (key) => ({
|
|
38
|
+
"xi-api-key": key,
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
deepgram: {
|
|
42
|
+
url: PROVIDER_API_URLS.deepgram,
|
|
43
|
+
headers: (key) => ({
|
|
44
|
+
Authorization: `Token ${key}`,
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Validate a provider API key by making a lightweight read-only request.
|
|
50
|
+
* The key is held in memory only for the duration of the fetch and then discarded.
|
|
51
|
+
*
|
|
52
|
+
* SECURITY: This function must NEVER log, persist, or return the key itself.
|
|
53
|
+
*/
|
|
54
|
+
export async function validateProviderKey(provider, key) {
|
|
55
|
+
const endpoint = PROVIDER_ENDPOINTS[provider];
|
|
56
|
+
if (!endpoint) {
|
|
57
|
+
return { valid: false, error: `Unknown provider: ${provider}` };
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch(endpoint.url, {
|
|
61
|
+
method: "GET",
|
|
62
|
+
headers: endpoint.headers(key),
|
|
63
|
+
signal: AbortSignal.timeout(10_000),
|
|
64
|
+
});
|
|
65
|
+
if (response.ok) {
|
|
66
|
+
return { valid: true };
|
|
67
|
+
}
|
|
68
|
+
// 401/403 = invalid key; other errors are transient
|
|
69
|
+
if (response.status === 401 || response.status === 403) {
|
|
70
|
+
return { valid: false, error: "Invalid API key" };
|
|
71
|
+
}
|
|
72
|
+
return { valid: false, error: `Provider returned status ${response.status}` };
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
const message = err instanceof Error ? err.message : "Validation request failed";
|
|
76
|
+
return { valid: false, error: message };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { PROVIDER_API_URLS } from "../config/provider-endpoints.js";
|
|
3
|
+
import { PROVIDER_ENDPOINTS, validateProviderKey } from "./key-validation.js";
|
|
4
|
+
describe("key-validation", () => {
|
|
5
|
+
describe("PROVIDER_API_URLS", () => {
|
|
6
|
+
it("exports a URL for every supported provider", () => {
|
|
7
|
+
expect(PROVIDER_API_URLS.anthropic).toBe("https://api.anthropic.com/v1/models");
|
|
8
|
+
expect(PROVIDER_API_URLS.openai).toBe("https://api.openai.com/v1/models");
|
|
9
|
+
expect(PROVIDER_API_URLS.google).toBe("https://generativelanguage.googleapis.com/v1/models");
|
|
10
|
+
expect(PROVIDER_API_URLS.discord).toBe("https://discord.com/api/v10/users/@me");
|
|
11
|
+
expect(PROVIDER_API_URLS.elevenlabs).toBe("https://api.elevenlabs.io/v1/user");
|
|
12
|
+
expect(PROVIDER_API_URLS.deepgram).toBe("https://api.deepgram.com/v1/projects");
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe("PROVIDER_ENDPOINTS", () => {
|
|
16
|
+
it("has entries for all supported providers", () => {
|
|
17
|
+
expect(PROVIDER_ENDPOINTS.anthropic).toEqual(expect.any(Object));
|
|
18
|
+
expect(PROVIDER_ENDPOINTS.openai).toEqual(expect.any(Object));
|
|
19
|
+
expect(PROVIDER_ENDPOINTS.google).toEqual(expect.any(Object));
|
|
20
|
+
expect(PROVIDER_ENDPOINTS.discord).toEqual(expect.any(Object));
|
|
21
|
+
});
|
|
22
|
+
it("anthropic headers include x-api-key", () => {
|
|
23
|
+
const headers = PROVIDER_ENDPOINTS.anthropic.headers("test-key");
|
|
24
|
+
expect(headers["x-api-key"]).toBe("test-key");
|
|
25
|
+
expect(headers["anthropic-version"]).toBe("2023-06-01");
|
|
26
|
+
});
|
|
27
|
+
it("openai headers include Bearer token", () => {
|
|
28
|
+
const headers = PROVIDER_ENDPOINTS.openai.headers("test-key");
|
|
29
|
+
expect(headers.Authorization).toBe("Bearer test-key");
|
|
30
|
+
});
|
|
31
|
+
it("google headers include x-goog-api-key", () => {
|
|
32
|
+
const headers = PROVIDER_ENDPOINTS.google.headers("test-key");
|
|
33
|
+
expect(headers["x-goog-api-key"]).toBe("test-key");
|
|
34
|
+
});
|
|
35
|
+
it("discord headers include Bot prefix", () => {
|
|
36
|
+
const headers = PROVIDER_ENDPOINTS.discord.headers("test-key");
|
|
37
|
+
expect(headers.Authorization).toBe("Bot test-key");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("validateProviderKey", () => {
|
|
41
|
+
const originalFetch = globalThis.fetch;
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
globalThis.fetch = vi.fn();
|
|
44
|
+
});
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
globalThis.fetch = originalFetch;
|
|
47
|
+
});
|
|
48
|
+
it("returns valid=true when provider returns 200", async () => {
|
|
49
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response(null, { status: 200 }));
|
|
50
|
+
const result = await validateProviderKey("openai", "sk-valid-key");
|
|
51
|
+
expect(result.valid).toBe(true);
|
|
52
|
+
expect(result.error).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
it("returns valid=false with error for 401", async () => {
|
|
55
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response(null, { status: 401 }));
|
|
56
|
+
const result = await validateProviderKey("anthropic", "sk-ant-invalid");
|
|
57
|
+
expect(result.valid).toBe(false);
|
|
58
|
+
expect(result.error).toBe("Invalid API key");
|
|
59
|
+
});
|
|
60
|
+
it("returns valid=false with error for 403", async () => {
|
|
61
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response(null, { status: 403 }));
|
|
62
|
+
const result = await validateProviderKey("discord", "bad-token");
|
|
63
|
+
expect(result.valid).toBe(false);
|
|
64
|
+
expect(result.error).toBe("Invalid API key");
|
|
65
|
+
});
|
|
66
|
+
it("returns valid=false with status for non-auth errors", async () => {
|
|
67
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response(null, { status: 500 }));
|
|
68
|
+
const result = await validateProviderKey("openai", "sk-key");
|
|
69
|
+
expect(result.valid).toBe(false);
|
|
70
|
+
expect(result.error).toBe("Provider returned status 500");
|
|
71
|
+
});
|
|
72
|
+
it("returns valid=false on network error", async () => {
|
|
73
|
+
vi.mocked(globalThis.fetch).mockRejectedValue(new Error("Network timeout"));
|
|
74
|
+
const result = await validateProviderKey("google", "AIza-key");
|
|
75
|
+
expect(result.valid).toBe(false);
|
|
76
|
+
expect(result.error).toBe("Network timeout");
|
|
77
|
+
});
|
|
78
|
+
it("calls the correct provider URL", async () => {
|
|
79
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response(null, { status: 200 }));
|
|
80
|
+
await validateProviderKey("anthropic", "sk-ant-test");
|
|
81
|
+
expect(globalThis.fetch).toHaveBeenCalledWith("https://api.anthropic.com/v1/models", expect.objectContaining({
|
|
82
|
+
method: "GET",
|
|
83
|
+
headers: expect.objectContaining({ "x-api-key": "sk-ant-test" }),
|
|
84
|
+
}));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Throws if `url` is not rooted at one of the allowed origins.
|
|
3
|
+
* Comparison is scheme + host (origin), not prefix string match,
|
|
4
|
+
* to prevent bypasses like `https://app.wopr.bot.evil.com`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function assertSafeRedirectUrl(url: string): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const ALLOWED_REDIRECT_ORIGINS = [
|
|
2
|
+
"https://app.wopr.bot",
|
|
3
|
+
"https://wopr.network",
|
|
4
|
+
...(process.env.NODE_ENV !== "production" ? ["http://localhost:3000", "http://localhost:3001"] : []),
|
|
5
|
+
...(process.env.PLATFORM_UI_URL ? [process.env.PLATFORM_UI_URL] : []),
|
|
6
|
+
...(process.env.NODE_ENV !== "production" ? ["https://example.com"] : []),
|
|
7
|
+
];
|
|
8
|
+
/**
|
|
9
|
+
* Throws if `url` is not rooted at one of the allowed origins.
|
|
10
|
+
* Comparison is scheme + host (origin), not prefix string match,
|
|
11
|
+
* to prevent bypasses like `https://app.wopr.bot.evil.com`.
|
|
12
|
+
*/
|
|
13
|
+
export function assertSafeRedirectUrl(url) {
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = new URL(url);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new Error("Invalid redirect URL");
|
|
20
|
+
}
|
|
21
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
22
|
+
throw new Error("Invalid redirect URL");
|
|
23
|
+
}
|
|
24
|
+
const origin = parsed.origin;
|
|
25
|
+
const allowed = ALLOWED_REDIRECT_ORIGINS.some((o) => {
|
|
26
|
+
try {
|
|
27
|
+
return origin === new URL(o).origin;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (!allowed) {
|
|
34
|
+
throw new Error("Invalid redirect URL");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { assertSafeRedirectUrl } from "./redirect-allowlist.js";
|
|
3
|
+
describe("assertSafeRedirectUrl", () => {
|
|
4
|
+
it("allows https://app.wopr.bot paths", () => {
|
|
5
|
+
expect(() => assertSafeRedirectUrl("https://app.wopr.bot/billing/success")).not.toThrow();
|
|
6
|
+
});
|
|
7
|
+
it("allows https://app.wopr.bot with query params", () => {
|
|
8
|
+
expect(() => assertSafeRedirectUrl("https://app.wopr.bot/dashboard?vps=activated")).not.toThrow();
|
|
9
|
+
});
|
|
10
|
+
it("allows https://wopr.network paths", () => {
|
|
11
|
+
expect(() => assertSafeRedirectUrl("https://wopr.network/welcome")).not.toThrow();
|
|
12
|
+
});
|
|
13
|
+
it("allows http://localhost:3000 in dev", () => {
|
|
14
|
+
expect(() => assertSafeRedirectUrl("http://localhost:3000/billing")).not.toThrow();
|
|
15
|
+
});
|
|
16
|
+
it("allows http://localhost:3001 in dev", () => {
|
|
17
|
+
expect(() => assertSafeRedirectUrl("http://localhost:3001/billing")).not.toThrow();
|
|
18
|
+
});
|
|
19
|
+
it("rejects external domains", () => {
|
|
20
|
+
expect(() => assertSafeRedirectUrl("https://evil.com/phishing")).toThrow("Invalid redirect URL");
|
|
21
|
+
});
|
|
22
|
+
it("rejects subdomain spoofing (app.wopr.bot.evil.com)", () => {
|
|
23
|
+
expect(() => assertSafeRedirectUrl("https://app.wopr.bot.evil.com/phishing")).toThrow("Invalid redirect URL");
|
|
24
|
+
});
|
|
25
|
+
it("rejects non-URL strings", () => {
|
|
26
|
+
expect(() => assertSafeRedirectUrl("not-a-url")).toThrow("Invalid redirect URL");
|
|
27
|
+
});
|
|
28
|
+
it("rejects javascript: URIs", () => {
|
|
29
|
+
expect(() => assertSafeRedirectUrl("javascript:alert(1)")).toThrow("Invalid redirect URL");
|
|
30
|
+
});
|
|
31
|
+
it("rejects data: URIs", () => {
|
|
32
|
+
expect(() => assertSafeRedirectUrl("data:text/html,<h1>pwned</h1>")).toThrow("Invalid redirect URL");
|
|
33
|
+
});
|
|
34
|
+
it("rejects empty string", () => {
|
|
35
|
+
expect(() => assertSafeRedirectUrl("")).toThrow("Invalid redirect URL");
|
|
36
|
+
});
|
|
37
|
+
describe("PLATFORM_UI_URL env-driven entry", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
process.env.PLATFORM_UI_URL = "https://platform.example.com";
|
|
40
|
+
vi.resetModules();
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
delete process.env.PLATFORM_UI_URL;
|
|
44
|
+
vi.resetModules();
|
|
45
|
+
});
|
|
46
|
+
it("allows PLATFORM_UI_URL when set", async () => {
|
|
47
|
+
const { assertSafeRedirectUrl: assertSafe } = await import("./redirect-allowlist.js");
|
|
48
|
+
expect(() => assertSafe("https://platform.example.com/dashboard")).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
it("rejects URLs not matching PLATFORM_UI_URL", async () => {
|
|
51
|
+
const { assertSafeRedirectUrl: assertSafe } = await import("./redirect-allowlist.js");
|
|
52
|
+
expect(() => assertSafe("https://other.example.com/dashboard")).toThrow("Invalid redirect URL");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PlatformDb } from "../../db/index.js";
|
|
2
|
+
export declare const ALL_CAPABILITIES: readonly ["transcription", "image-gen", "text-gen", "embeddings"];
|
|
3
|
+
export type CapabilityName = (typeof ALL_CAPABILITIES)[number];
|
|
4
|
+
export interface TenantCapabilitySetting {
|
|
5
|
+
tenant_id: string;
|
|
6
|
+
capability: string;
|
|
7
|
+
mode: string;
|
|
8
|
+
updated_at: number;
|
|
9
|
+
}
|
|
10
|
+
/** Repository for tenant capability mode settings (hosted vs byok). */
|
|
11
|
+
export interface ICapabilitySettingsRepository {
|
|
12
|
+
listForTenant(tenantId: string): Promise<TenantCapabilitySetting[]>;
|
|
13
|
+
upsert(tenantId: string, capability: string, mode: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare class CapabilitySettingsStore implements ICapabilitySettingsRepository {
|
|
16
|
+
private readonly db;
|
|
17
|
+
constructor(db: PlatformDb);
|
|
18
|
+
/** Get all capability settings for a tenant. Returns empty array if none set (defaults to hosted). */
|
|
19
|
+
listForTenant(tenantId: string): Promise<TenantCapabilitySetting[]>;
|
|
20
|
+
/** Set the mode for a specific capability. Upserts. */
|
|
21
|
+
upsert(tenantId: string, capability: string, mode: string): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { tenantCapabilitySettings } from "../../db/schema/index.js";
|
|
3
|
+
export const ALL_CAPABILITIES = ["transcription", "image-gen", "text-gen", "embeddings"];
|
|
4
|
+
export class CapabilitySettingsStore {
|
|
5
|
+
db;
|
|
6
|
+
constructor(db) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
}
|
|
9
|
+
/** Get all capability settings for a tenant. Returns empty array if none set (defaults to hosted). */
|
|
10
|
+
async listForTenant(tenantId) {
|
|
11
|
+
const rows = await this.db
|
|
12
|
+
.select()
|
|
13
|
+
.from(tenantCapabilitySettings)
|
|
14
|
+
.where(eq(tenantCapabilitySettings.tenantId, tenantId));
|
|
15
|
+
return rows.map((r) => ({
|
|
16
|
+
tenant_id: r.tenantId,
|
|
17
|
+
capability: r.capability,
|
|
18
|
+
mode: r.mode,
|
|
19
|
+
updated_at: r.updatedAt,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
/** Set the mode for a specific capability. Upserts. */
|
|
23
|
+
async upsert(tenantId, capability, mode) {
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
await this.db
|
|
26
|
+
.insert(tenantCapabilitySettings)
|
|
27
|
+
.values({ tenantId, capability, mode, updatedAt: now })
|
|
28
|
+
.onConflictDoUpdate({
|
|
29
|
+
target: [tenantCapabilitySettings.tenantId, tenantCapabilitySettings.capability],
|
|
30
|
+
set: { mode, updatedAt: now },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { createTestDb, truncateAllTables } from "../../test/db.js";
|
|
3
|
+
import { CapabilitySettingsStore } from "./capability-settings-store.js";
|
|
4
|
+
describe("CapabilitySettingsStore", () => {
|
|
5
|
+
let db;
|
|
6
|
+
let pool;
|
|
7
|
+
let store;
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
({ db, pool } = await createTestDb());
|
|
10
|
+
});
|
|
11
|
+
afterAll(async () => {
|
|
12
|
+
await pool.close();
|
|
13
|
+
});
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
await truncateAllTables(pool);
|
|
16
|
+
store = new CapabilitySettingsStore(db);
|
|
17
|
+
});
|
|
18
|
+
it("returns empty array for tenant with no overrides (platform defaults apply)", async () => {
|
|
19
|
+
const settings = await store.listForTenant("tenant-no-overrides");
|
|
20
|
+
expect(settings).toEqual([]);
|
|
21
|
+
});
|
|
22
|
+
it("upsert sets per-tenant override that listForTenant returns", async () => {
|
|
23
|
+
await store.upsert("tenant-1", "transcription", "byok");
|
|
24
|
+
const settings = await store.listForTenant("tenant-1");
|
|
25
|
+
expect(settings).toHaveLength(1);
|
|
26
|
+
expect(settings[0]).toMatchObject({
|
|
27
|
+
tenant_id: "tenant-1",
|
|
28
|
+
capability: "transcription",
|
|
29
|
+
mode: "byok",
|
|
30
|
+
});
|
|
31
|
+
expect(settings[0].updated_at).toBeGreaterThan(0);
|
|
32
|
+
});
|
|
33
|
+
it("upsert can toggle mode between hosted and byok", async () => {
|
|
34
|
+
await store.upsert("tenant-2", "image-gen", "byok");
|
|
35
|
+
let settings = await store.listForTenant("tenant-2");
|
|
36
|
+
expect(settings[0].mode).toBe("byok");
|
|
37
|
+
await store.upsert("tenant-2", "image-gen", "hosted");
|
|
38
|
+
settings = await store.listForTenant("tenant-2");
|
|
39
|
+
expect(settings).toHaveLength(1);
|
|
40
|
+
expect(settings[0].mode).toBe("hosted");
|
|
41
|
+
});
|
|
42
|
+
it("upsert overwrites previous mode via onConflictDoUpdate", async () => {
|
|
43
|
+
await store.upsert("tenant-3", "text-gen", "hosted");
|
|
44
|
+
const before = await store.listForTenant("tenant-3");
|
|
45
|
+
const firstUpdatedAt = before[0].updated_at;
|
|
46
|
+
// Small delay so timestamp differs
|
|
47
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
48
|
+
await store.upsert("tenant-3", "text-gen", "byok");
|
|
49
|
+
const after = await store.listForTenant("tenant-3");
|
|
50
|
+
expect(after).toHaveLength(1);
|
|
51
|
+
expect(after[0].mode).toBe("byok");
|
|
52
|
+
expect(after[0].updated_at).toBeGreaterThanOrEqual(firstUpdatedAt);
|
|
53
|
+
});
|
|
54
|
+
it("tenant A overrides do not appear in tenant B list", async () => {
|
|
55
|
+
await store.upsert("tenant-A", "transcription", "byok");
|
|
56
|
+
await store.upsert("tenant-A", "image-gen", "byok");
|
|
57
|
+
await store.upsert("tenant-B", "embeddings", "hosted");
|
|
58
|
+
const settingsA = await store.listForTenant("tenant-A");
|
|
59
|
+
const settingsB = await store.listForTenant("tenant-B");
|
|
60
|
+
expect(settingsA).toHaveLength(2);
|
|
61
|
+
expect(settingsA.every((s) => s.tenant_id === "tenant-A")).toBe(true);
|
|
62
|
+
expect(settingsB).toHaveLength(1);
|
|
63
|
+
expect(settingsB[0].tenant_id).toBe("tenant-B");
|
|
64
|
+
expect(settingsB[0].capability).toBe("embeddings");
|
|
65
|
+
});
|
|
66
|
+
it("upsert changes are immediately visible in subsequent reads (no stale cache)", async () => {
|
|
67
|
+
await store.upsert("tenant-C", "text-gen", "hosted");
|
|
68
|
+
const read1 = await store.listForTenant("tenant-C");
|
|
69
|
+
expect(read1[0].mode).toBe("hosted");
|
|
70
|
+
await store.upsert("tenant-C", "text-gen", "byok");
|
|
71
|
+
const read2 = await store.listForTenant("tenant-C");
|
|
72
|
+
expect(read2[0].mode).toBe("byok");
|
|
73
|
+
await store.upsert("tenant-C", "image-gen", "byok");
|
|
74
|
+
const read3 = await store.listForTenant("tenant-C");
|
|
75
|
+
expect(read3).toHaveLength(2);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type { IKeyResolutionRepository } from "./key-resolution-repository.js";
|
|
2
|
+
export { DrizzleKeyResolutionRepository } from "./key-resolution-repository.js";
|
|
3
|
+
export type { ResolvedKey } from "./key-resolution.js";
|
|
4
|
+
export { resolveApiKey, buildPooledKeysMap } from "./key-resolution.js";
|
|
5
|
+
export type { TenantApiKey, ITenantKeyRepository } from "./tenant-key-repository.js";
|
|
6
|
+
export { TenantKeyRepository } from "./tenant-key-repository.js";
|
|
7
|
+
export type { CapabilityName, TenantCapabilitySetting, ICapabilitySettingsRepository } from "./capability-settings-store.js";
|
|
8
|
+
export { ALL_CAPABILITIES, CapabilitySettingsStore } from "./capability-settings-store.js";
|
|
9
|
+
export type { IOrgMembershipRepository, OrgResolvedKey } from "./org-key-resolution.js";
|
|
10
|
+
export { DrizzleOrgMembershipRepository, resolveApiKeyWithOrgFallback } from "./org-key-resolution.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { DrizzleKeyResolutionRepository } from "./key-resolution-repository.js";
|
|
2
|
+
export { resolveApiKey, buildPooledKeysMap } from "./key-resolution.js";
|
|
3
|
+
export { TenantKeyRepository } from "./tenant-key-repository.js";
|
|
4
|
+
export { ALL_CAPABILITIES, CapabilitySettingsStore } from "./capability-settings-store.js";
|
|
5
|
+
export { DrizzleOrgMembershipRepository, resolveApiKeyWithOrgFallback } from "./org-key-resolution.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PlatformDb } from "../../db/index.js";
|
|
2
|
+
import type { Provider } from "../types.js";
|
|
3
|
+
/** Repository for looking up tenant BYOK keys by tenant + provider. */
|
|
4
|
+
export interface IKeyResolutionRepository {
|
|
5
|
+
findEncryptedKey(tenantId: string, provider: Provider): Promise<{
|
|
6
|
+
encryptedKey: string;
|
|
7
|
+
} | null>;
|
|
8
|
+
}
|
|
9
|
+
export declare class DrizzleKeyResolutionRepository implements IKeyResolutionRepository {
|
|
10
|
+
private readonly db;
|
|
11
|
+
constructor(db: PlatformDb);
|
|
12
|
+
findEncryptedKey(tenantId: string, provider: Provider): Promise<{
|
|
13
|
+
encryptedKey: string;
|
|
14
|
+
} | null>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { and, eq } from "drizzle-orm";
|
|
2
|
+
import { tenantApiKeys } from "../../db/schema/index.js";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Implementation
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
export class DrizzleKeyResolutionRepository {
|
|
7
|
+
db;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
async findEncryptedKey(tenantId, provider) {
|
|
12
|
+
const row = (await this.db
|
|
13
|
+
.select({ encryptedKey: tenantApiKeys.encryptedKey })
|
|
14
|
+
.from(tenantApiKeys)
|
|
15
|
+
.where(and(eq(tenantApiKeys.tenantId, tenantId), eq(tenantApiKeys.provider, provider))))[0];
|
|
16
|
+
return row ?? null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|