alepha 0.19.1 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +6 -9
- package/dist/api/audits/index.d.ts +378 -346
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +216 -184
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/jobs/index.d.ts +528 -496
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +3 -3
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +207 -207
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/notifications/index.d.ts +152 -152
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/organizations/index.browser.js +48 -0
- package/dist/api/organizations/index.browser.js.map +1 -0
- package/dist/api/organizations/index.d.ts +516 -0
- package/dist/api/organizations/index.d.ts.map +1 -0
- package/dist/api/organizations/index.js +202 -0
- package/dist/api/organizations/index.js.map +1 -0
- package/dist/api/parameters/index.d.ts +391 -358
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +5 -1
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.browser.js +7 -5
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +978 -913
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +160 -112
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +135 -135
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +2 -2
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +6 -6
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/billing/index.d.ts +1048 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +713 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/bin/index.js +0 -2
- package/dist/bin/index.js.map +1 -1
- package/dist/bucket/index.d.ts +10 -10
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +2 -2
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +9 -9
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +2 -2
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +2 -2
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/redis/index.d.ts +6 -6
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +2 -2
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +12 -2
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js +4 -0
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +183 -140
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +279 -89
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +45 -0
- package/dist/cli/devtools/index.d.ts.map +1 -0
- package/dist/cli/devtools/index.js +170 -0
- package/dist/cli/devtools/index.js.map +1 -0
- package/dist/cli/platform/index.d.ts +383 -492
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +42 -511
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +196 -0
- package/dist/cli/vendor/index.d.ts.map +1 -0
- package/dist/cli/vendor/index.js +384 -0
- package/dist/cli/vendor/index.js.map +1 -0
- package/dist/command/index.d.ts +18 -18
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2 -2
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +4 -4
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +10 -10
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +4 -4
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +4 -4
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.d.ts +7 -7
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/datetime/index.d.ts +4 -4
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/email/brevo/index.d.ts +4 -4
- package/dist/email/brevo/index.d.ts.map +1 -1
- package/dist/email/core/index.d.ts +15 -11
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/core/index.js +12 -35
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/smtp/index.d.ts +12 -12
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/email/smtp/index.js +7 -4
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.d.ts +4 -8
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +55 -889
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +13 -13
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +2 -2
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.d.ts +4 -4
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +16 -15
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +5 -2
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +11 -11
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +11 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +53 -16
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +95 -51
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +55 -14
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +18 -17
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/queue/core/index.d.ts +14 -14
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js +2 -2
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js +2 -2
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.d.ts +4 -4
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/queue/redis/index.js +2 -2
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.d.ts +9 -9
- package/dist/react/auth/index.d.ts.map +1 -1
- package/dist/react/core/index.d.ts +6 -6
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +5 -4
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +4 -4
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/head/index.d.ts +4 -4
- package/dist/react/head/index.d.ts.map +1 -1
- package/dist/react/i18n/index.d.ts +9 -9
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/intro/index.d.ts +2 -2
- package/dist/react/intro/index.d.ts.map +1 -1
- package/dist/react/intro/index.js +1 -1
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +4 -5
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +215 -215
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +6 -7
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/testing/index.d.ts +2 -2
- package/dist/react/testing/index.d.ts.map +1 -1
- package/dist/react/testing/index.js +2 -4
- package/dist/react/testing/index.js.map +1 -1
- package/dist/redis/index.d.ts +19 -19
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/retry/index.d.ts +4 -4
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +13 -13
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +2 -2
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +2 -2
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +47 -47
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +9 -12
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +170 -169
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +16 -2
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +7 -7
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/core/index.d.ts +76 -76
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +23 -17
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +13 -13
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js +2 -2
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.d.ts +9 -9
- package/dist/server/etag/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +20 -20
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js +2 -2
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +66 -66
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +4 -4
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +7 -7
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +5 -5
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts +12 -12
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js +2 -2
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +5 -5
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +7 -7
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +2 -2
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +11 -7
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +9 -15
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.d.ts +4 -4
- package/dist/system/index.d.ts.map +1 -1
- package/dist/topic/core/index.d.ts +6 -6
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts +7 -7
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/topic/redis/index.js +2 -2
- package/dist/topic/redis/index.js.map +1 -1
- package/dist/websocket/index.d.ts +36 -36
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +2 -2
- package/dist/websocket/index.js.map +1 -1
- package/package.json +36 -14
- package/src/api/jobs/{services → __tests__}/JobService.spec.ts +1 -1
- package/src/api/jobs/providers/JobProvider.ts +3 -3
- package/src/api/keys/{services → __tests__}/ApiKeyService.spec.ts +1 -1
- package/src/api/organizations/__tests__/OrganizationService.spec.ts +193 -0
- package/src/api/organizations/controllers/AdminOrganizationController.ts +103 -0
- package/src/api/organizations/entities/organizations.ts +20 -0
- package/src/api/organizations/index.browser.ts +10 -0
- package/src/api/organizations/index.ts +31 -0
- package/src/api/organizations/schemas/createOrganizationSchema.ts +10 -0
- package/src/api/organizations/schemas/organizationQuerySchema.ts +10 -0
- package/src/api/organizations/schemas/organizationResourceSchema.ts +6 -0
- package/src/api/organizations/schemas/updateOrganizationSchema.ts +7 -0
- package/src/api/organizations/services/OrganizationService.ts +75 -0
- package/src/api/parameters/services/ParameterProvider.ts +6 -1
- package/src/api/users/{services → __tests__}/SessionService.spec.ts +67 -0
- package/src/api/users/{jobs → __tests__}/UserJobs.spec.ts +1 -1
- package/src/api/users/entities/users.ts +9 -3
- package/src/api/users/index.ts +23 -4
- package/src/api/users/primitives/$realm.ts +6 -4
- package/src/api/users/providers/RealmProvider.ts +1 -1
- package/src/api/users/services/RegistrationService.ts +1 -1
- package/src/api/users/services/SessionService.ts +92 -5
- package/src/api/users/services/UserService.ts +1 -1
- package/src/api/verifications/{jobs → __tests__}/VerificationJobs.spec.ts +4 -2
- package/src/api/verifications/parameters/VerificationParameters.ts +2 -2
- package/src/billing/__tests__/BillingService.spec.ts +136 -0
- package/src/billing/__tests__/PaymentMethodService.spec.ts +78 -0
- package/src/billing/controllers/AdminBillingController.ts +149 -0
- package/src/billing/controllers/BillingController.ts +108 -0
- package/src/billing/entities/paymentIntents.ts +34 -0
- package/src/billing/entities/paymentMethods.ts +24 -0
- package/src/billing/entities/refunds.ts +22 -0
- package/src/billing/errors/BillingError.ts +5 -0
- package/src/billing/index.ts +76 -0
- package/src/billing/providers/BillingProvider.ts +79 -0
- package/src/billing/providers/MemoryBillingProvider.ts +139 -0
- package/src/billing/schemas/intentSchemas.ts +60 -0
- package/src/billing/schemas/paymentMethodSchemas.ts +13 -0
- package/src/billing/schemas/refundSchemas.ts +6 -0
- package/src/billing/services/BillingService.ts +325 -0
- package/src/billing/services/PaymentMethodService.ts +82 -0
- package/src/bin/index.ts +0 -2
- package/src/bucket/providers/LocalFileStorageProvider.ts +2 -2
- package/src/cache/core/{primitives → __tests__}/$cache.middleware.spec.ts +1 -1
- package/src/cache/core/{providers → __tests__}/MemoryCacheProvider.spec.ts +1 -1
- package/src/cache/core/primitives/$cache.ts +2 -2
- package/src/cache/redis/providers/RedisCacheProvider.ts +2 -2
- package/src/cli/config/defineConfig.ts +20 -0
- package/src/cli/core/{services → __tests__}/ProjectScaffolder.spec.ts +1 -1
- package/src/cli/core/{commands/gen → __tests__}/changelog.spec.ts +1 -1
- package/src/cli/core/{commands → __tests__}/init.spec.ts +2 -8
- package/src/cli/core/atoms/devOptions.ts +0 -5
- package/src/cli/core/commands/build.ts +2 -2
- package/src/cli/core/commands/dev.ts +165 -30
- package/src/cli/core/commands/gen/changelog.ts +2 -2
- package/src/cli/core/commands/init.ts +2 -7
- package/src/cli/core/commands/verify.ts +0 -1
- package/src/cli/core/providers/AppEntryProvider.ts +2 -2
- package/src/cli/core/providers/ViteDevServerProvider.ts +54 -66
- package/src/cli/core/services/PackageManagerUtils.ts +8 -3
- package/src/cli/core/services/ProjectScaffolder.ts +18 -18
- package/src/cli/core/tasks/BuildClientTask.ts +8 -0
- package/src/cli/core/tasks/BuildServerTask.ts +17 -4
- package/src/cli/core/templates/alephaConfigTs.ts +0 -6
- package/src/cli/core/templates/webAdminDashboardTsx.ts +17 -0
- package/src/cli/core/templates/webAppRouterTs.ts +85 -2
- package/src/cli/devtools/atoms/devtoolsOptions.ts +26 -0
- package/src/cli/devtools/index.ts +194 -0
- package/src/cli/platform/{adapters → __tests__}/CloudflareAdapter.spec.ts +2 -2
- package/src/cli/platform/{providers → __tests__}/GitHubSecretStore.spec.ts +1 -1
- package/src/cli/platform/{services → __tests__}/NamingService.spec.ts +1 -1
- package/src/cli/platform/{providers → __tests__}/PlatformCacheProvider.spec.ts +1 -1
- package/src/cli/platform/{services → __tests__}/PlatformInspector.spec.ts +1 -1
- package/src/cli/platform/{services → __tests__}/PlatformOrchestrator.spec.ts +3 -3
- package/src/cli/platform/{services → __tests__}/SecretFilterService.spec.ts +1 -1
- package/src/cli/platform/{commands → __tests__}/SecretsCommand.spec.ts +1 -1
- package/src/cli/platform/{adapters → __tests__}/VercelAdapter.spec.ts +2 -2
- package/src/cli/platform/atoms/platformOptions.ts +2 -10
- package/src/cli/platform/commands/SecretsCommand.ts +2 -2
- package/src/cli/platform/commands/platform.ts +2 -11
- package/src/cli/platform/index.ts +34 -11
- package/src/cli/platform/services/PlatformInspector.ts +2 -2
- package/src/cli/platform/services/PlatformOrchestrator.ts +0 -9
- package/src/cli/vendor/__tests__/VendorService.spec.ts +407 -0
- package/src/cli/vendor/atoms/vendorOptions.ts +41 -0
- package/src/cli/vendor/commands/VendorCommand.ts +204 -0
- package/src/cli/vendor/index.ts +43 -0
- package/src/cli/vendor/services/VendorService.ts +338 -0
- package/src/command/{providers → __tests__}/CliProvider.spec.ts +1 -1
- package/src/command/{helpers → __tests__}/EnvUtils.spec.ts +1 -1
- package/src/command/providers/CliProvider.ts +2 -2
- package/src/core/{primitives → __tests__}/$atom.spec.ts +2 -2
- package/src/core/{primitives → __tests__}/$memoize.spec.ts +1 -1
- package/src/core/{primitives → __tests__}/$mode.spec.ts +1 -1
- package/src/core/{primitives → __tests__}/$pipeline.spec.ts +1 -1
- package/src/core/{primitives → __tests__}/$scope.spec.ts +2 -2
- package/src/core/{providers → __tests__}/KeylessJsonSchemaCodec.spec.ts +1 -1
- package/src/core/{providers → __tests__}/SchemaValidator.spec.ts +1 -1
- package/src/core/{helpers → __tests__}/jsonSchemaToTypeBox.spec.ts +1 -1
- package/src/core/index.shared.ts +1 -1
- package/src/core/primitives/{$use.ts → $state.ts} +4 -4
- package/src/crypto/{providers → __tests__}/BrowserCryptoProvider.browser.spec.ts +1 -1
- package/src/crypto/{providers → __tests__}/CryptoProvider.spec.ts +1 -1
- package/src/datetime/{primitives → __tests__}/$debounce.spec.ts +1 -1
- package/src/datetime/{primitives → __tests__}/$throttle.spec.ts +1 -1
- package/src/datetime/{primitives → __tests__}/$timeout.spec.ts +1 -1
- package/src/email/brevo/{providers → __tests__}/BrevoEmailProvider.spec.ts +1 -1
- package/src/email/core/{providers → __tests__}/LocalEmailProvider.spec.ts +39 -150
- package/src/email/core/providers/LocalEmailProvider.ts +13 -51
- package/src/email/smtp/providers/NodemailerEmailProvider.ts +2 -2
- package/src/lock/core/{primitives → __tests__}/$lock.middleware.spec.ts +1 -1
- package/src/lock/core/primitives/$lock.ts +2 -2
- package/src/logger/index.ts +10 -4
- package/src/mcp/transports/SseMcpTransport.ts +2 -2
- package/src/orm/__tests__/ModelBuilder-tests.ts +53 -0
- package/src/orm/__tests__/ModelBuilder.spec.ts +80 -0
- package/src/orm/__tests__/organization-tests.ts +200 -0
- package/src/orm/__tests__/organization.spec.ts +103 -0
- package/src/orm/core/{providers/drivers → __tests__}/BunSqliteProvider.bun.spec.ts +5 -2
- package/src/orm/core/constants/PG_SYMBOLS.ts +2 -0
- package/src/orm/core/index.shared.ts +1 -0
- package/src/orm/core/primitives/$entity.ts +31 -0
- package/src/orm/core/providers/DatabaseTypeProvider.ts +11 -0
- package/src/orm/core/providers/DrizzleKitProvider.ts +1 -1
- package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -2
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +3 -3
- package/src/orm/core/services/ModelBuilder.ts +11 -0
- package/src/orm/core/services/QueryManager.ts +16 -2
- package/src/orm/core/services/Repository.ts +70 -10
- package/src/orm/postgres/{providers → __tests__}/BunPostgresProvider.bun.spec.ts +1 -1
- package/src/queue/core/providers/WorkerProvider.ts +2 -2
- package/src/queue/redis/providers/RedisQueueProvider.ts +2 -2
- package/src/react/core/{hooks → __tests__}/useAction.browser.spec.tsx +1 -1
- package/src/react/core/hooks/useAction.ts +7 -6
- package/src/react/head/{providers → __tests__}/BrowserHeadProvider.browser.spec.ts +1 -1
- package/src/react/head/{helpers → __tests__}/SeoExpander.spec.ts +1 -1
- package/src/react/i18n/{providers → __tests__}/I18nProvider.spec.ts +1 -1
- package/src/react/i18n/{hooks → __tests__}/useI18n.browser.spec.tsx +1 -1
- package/src/react/intro/components/GettingStartedDevtoolsSlide.tsx +1 -1
- package/src/react/router/{providers → __tests__}/ReactBrowserProvider.browser.spec.ts +1 -1
- package/src/react/router/providers/ReactBrowserProvider.ts +2 -2
- package/src/react/router/providers/ReactPageProvider.ts +2 -2
- package/src/react/router/providers/ReactServerProvider.ts +3 -3
- package/src/redis/{providers → __tests__}/BunRedisProvider.bun.spec.ts +4 -4
- package/src/retry/{primitives → __tests__}/$retry.middleware.spec.ts +1 -1
- package/src/router/{TemplatedPathParser.spec.ts → __tests__/TemplatedPathParser.spec.ts} +1 -1
- package/src/scheduler/primitives/$scheduler.ts +2 -2
- package/src/security/{primitives → __tests__}/$secure-browser.spec.ts +1 -1
- package/src/security/{primitives → __tests__}/$secure.spec.ts +1 -1
- package/src/security/primitives/$issuer.ts +1 -1
- package/src/security/providers/JwtProvider.ts +6 -10
- package/src/security/providers/SecurityProvider.ts +6 -11
- package/src/security/schemas/userAccountInfoSchema.ts +3 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +24 -2
- package/src/server/cookies/{services → __tests__}/CookieParser.spec.ts +1 -1
- package/src/server/core/{primitives → __tests__}/$circuit.spec.ts +1 -1
- package/src/server/core/{providers → __tests__}/NodeHttpServerProvider.spec.ts +1 -1
- package/src/server/core/{providers → __tests__}/ServerBodyParserProvider.spec.ts +31 -1
- package/src/server/core/{providers → __tests__}/ServerCompressProvider.spec.ts +1 -1
- package/src/server/core/{providers → __tests__}/ServerHelmetProvider.spec.ts +4 -1
- package/src/server/core/{providers → __tests__}/ServerMultipartProvider.spec.ts +1 -1
- package/src/server/core/{services → __tests__}/ServerRequestParser.spec.ts +1 -1
- package/src/server/core/primitives/$action.ts +2 -2
- package/src/server/core/primitives/$sse.ts +2 -2
- package/src/server/core/providers/ServerBodyParserProvider.ts +21 -12
- package/src/server/core/providers/ServerCompressProvider.ts +2 -2
- package/src/server/core/providers/ServerHelmetProvider.ts +2 -2
- package/src/server/core/providers/ServerMultipartProvider.ts +2 -2
- package/src/server/core/providers/ServerRouterProvider.ts +1 -5
- package/src/server/cors/{primitives → __tests__}/$cors.spec.ts +1 -1
- package/src/server/cors/providers/ServerCorsProvider.ts +2 -2
- package/src/server/links/{services → __tests__}/BatchCollector.spec.ts +1 -1
- package/src/server/links/providers/LinkProvider.ts +2 -2
- package/src/server/links/providers/RemotePrimitiveProvider.ts +2 -2
- package/src/server/links/providers/ServerLinksProvider.ts +2 -2
- package/src/server/rate-limit/{primitives → __tests__}/$rateLimit.spec.ts +1 -1
- package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +2 -2
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +2 -2
- package/src/sms/{providers → __tests__}/LocalSmsProvider.spec.ts +35 -29
- package/src/sms/providers/LocalSmsProvider.ts +13 -24
- package/src/system/{providers → __tests__}/MemoryFileSystemProvider.spec.ts +1 -1
- package/src/system/{providers → __tests__}/MemoryShellProvider.spec.ts +1 -1
- package/src/topic/redis/providers/RedisTopicProvider.ts +2 -2
- package/src/websocket/{services → __tests__}/RoomManager.spec.ts +1 -1
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +2 -2
- package/tsconfig.base.json +1 -0
- package/src/cli/platform/adapters/DockerAdapter.spec.ts +0 -378
- package/src/cli/platform/adapters/DockerAdapter.ts +0 -417
- package/src/cli/platform/services/DockerComposeGenerator.spec.ts +0 -490
- package/src/cli/platform/services/DockerComposeGenerator.ts +0 -353
- package/src/cli/platform/services/DockerSshService.spec.ts +0 -47
- package/src/cli/platform/services/DockerSshService.ts +0 -61
- /package/src/api/audits/{primitives → __tests__}/$audit.spec.ts +0 -0
- /package/src/api/audits/{services → __tests__}/AuditService.spec.ts +0 -0
- /package/src/api/files/{controllers → __tests__}/AdminFileStatsController.spec.ts +0 -0
- /package/src/api/files/{controllers → __tests__}/FileController.spec.ts +0 -0
- /package/src/api/files/{jobs → __tests__}/FileJobs.spec.ts +0 -0
- /package/src/api/files/{services → __tests__}/FileService.spec.ts +0 -0
- /package/src/api/jobs/{primitives → __tests__}/$job-middleware.spec.ts +0 -0
- /package/src/api/parameters/{primitives → __tests__}/$parameter.spec.ts +0 -0
- /package/src/api/users/{primitives → __tests__}/$realm.spec.ts +0 -0
- /package/src/api/users/{controllers → __tests__}/AdminIdentityController.spec.ts +0 -0
- /package/src/api/users/{controllers → __tests__}/AdminSessionController.spec.ts +0 -0
- /package/src/api/users/{controllers → __tests__}/AdminUserController.spec.ts +0 -0
- /package/src/api/users/{services → __tests__}/CredentialService.spec.ts +0 -0
- /package/src/api/users/{providers → __tests__}/RealmProvider.spec.ts +0 -0
- /package/src/api/users/{services → __tests__}/RegistrationService.spec.ts +0 -0
- /package/src/batch/{primitives → __tests__}/$batch.spec.ts +0 -0
- /package/src/batch/{providers → __tests__}/BatchProvider.spec.ts +0 -0
- /package/src/bucket/{primitives → __tests__}/$bucket.spec.ts +0 -0
- /package/src/bucket/{providers → __tests__}/FileStorageProvider.spec.ts +0 -0
- /package/src/bucket/{providers → __tests__}/LocalFileStorageProvider.spec.ts +0 -0
- /package/src/bucket/{providers → __tests__}/MemoryFileStorageProvider.spec.ts +0 -0
- /package/src/cache/core/{primitives → __tests__}/$cache.spec.ts +0 -0
- /package/src/cache/redis/{providers → __tests__}/RedisCacheProvider.spec.ts +0 -0
- /package/src/command/{primitives → __tests__}/$command.spec.ts +0 -0
- /package/src/command/{helpers → __tests__}/Asker.spec.ts +0 -0
- /package/src/command/{helpers → __tests__}/Runner.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$context.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$env.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$hook.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$inject.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$module.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/CodecManager.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/EventManager.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/StateManager.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/TypeProvider.spec.ts +0 -0
- /package/src/datetime/{primitives → __tests__}/$interval.spec.ts +0 -0
- /package/src/datetime/{providers → __tests__}/DateTimeProvider.spec.ts +0 -0
- /package/src/email/core/{primitives → __tests__}/$email.spec.ts +0 -0
- /package/src/fake/{providers → __tests__}/FakeProvider.spec.ts +0 -0
- /package/src/lock/core/{providers → __tests__}/MemoryLockProvider.spec.ts +0 -0
- /package/src/lock/redis/{providers → __tests__}/RedisLockProvider.spec.ts +0 -0
- /package/src/logger/{primitives → __tests__}/$logger.spec.ts +0 -0
- /package/src/logger/{services → __tests__}/Logger.spec.ts +0 -0
- /package/src/mcp/{primitives → __tests__}/$prompt.spec.ts +0 -0
- /package/src/mcp/{primitives → __tests__}/$resource.spec.ts +0 -0
- /package/src/mcp/{primitives → __tests__}/$tool.spec.ts +0 -0
- /package/src/mcp/{providers → __tests__}/McpServerProvider.spec.ts +0 -0
- /package/src/mcp/{helpers → __tests__}/jsonrpc.spec.ts +0 -0
- /package/src/orm/core/{helpers → __tests__}/parseQueryString.spec.ts +0 -0
- /package/src/queue/core/{primitives → __tests__}/$consumer.spec.ts +0 -0
- /package/src/queue/core/{providers → __tests__}/MemoryQueueProvider.spec.ts +0 -0
- /package/src/queue/core/{providers → __tests__}/WorkerProvider.spec.ts +0 -0
- /package/src/queue/redis/{providers → __tests__}/RedisQueueProvider.spec.ts +0 -0
- /package/src/react/form/{hooks → __tests__}/useForm.browser.spec.tsx +0 -0
- /package/src/react/head/{hooks → __tests__}/useHead.spec.tsx +0 -0
- /package/src/react/i18n/{components → __tests__}/Localize.spec.tsx +0 -0
- /package/src/react/router/{primitives → __tests__}/$page.browser.spec.tsx +0 -0
- /package/src/react/router/{primitives → __tests__}/$page.middleware.spec.tsx +0 -0
- /package/src/react/router/{primitives → __tests__}/$page.spec.tsx +0 -0
- /package/src/react/router/{providers → __tests__}/ReactPreloadProvider.spec.ts +0 -0
- /package/src/react/router/{providers → __tests__}/ReactServerProvider.spec.tsx +0 -0
- /package/src/react/router/{providers → __tests__}/ReactServerTemplateProvider.spec.ts +0 -0
- /package/src/retry/{primitives → __tests__}/$retry.spec.ts +0 -0
- /package/src/retry/{providers → __tests__}/RetryProvider.spec.ts +0 -0
- /package/src/router/{providers → __tests__}/RouterProvider.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$issuer.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$permission.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$role.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$serviceAccount.spec.ts +0 -0
- /package/src/security/{providers → __tests__}/SecurityProvider.spec.ts +0 -0
- /package/src/server/cookies/{providers → __tests__}/ServerCookiesProvider.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$action.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$middleware.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$route.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$sse.spec.ts +0 -0
- /package/src/server/core/{providers → __tests__}/BunHttpServerProvider.bun.spec.ts +0 -0
- /package/src/server/core/{services → __tests__}/HttpClient.spec.ts +0 -0
- /package/src/server/core/{providers → __tests__}/ServerLoggerProvider.spec.ts +0 -0
- /package/src/server/core/{services → __tests__}/UserAgentParser.spec.ts +0 -0
- /package/src/server/cors/{providers → __tests__}/ServerCorsProvider.spec.ts +0 -0
- /package/src/server/etag/{providers → __tests__}/ServerEtagProvider.spec.ts +0 -0
- /package/src/server/health/{providers → __tests__}/ServerHealthProvider.spec.ts +0 -0
- /package/src/server/links/{primitives → __tests__}/$remote.spec.ts +0 -0
- /package/src/server/links/{services → __tests__}/BatchEndpoint.spec.ts +0 -0
- /package/src/server/links/{providers → __tests__}/LinkProvider.spec.ts +0 -0
- /package/src/server/links/{providers → __tests__}/ServerLinksProvider.spec.ts +0 -0
- /package/src/server/metrics/{providers → __tests__}/ServerMetricsProvider.spec.ts +0 -0
- /package/src/server/proxy/{primitives → __tests__}/$proxy.spec.ts +0 -0
- /package/src/server/rate-limit/{providers → __tests__}/ServerRateLimitProvider.spec.ts +0 -0
- /package/src/server/static/{primitives → __tests__}/$serve.spec.ts +0 -0
- /package/src/server/swagger/{primitives → __tests__}/$swagger.spec.ts +0 -0
- /package/src/sms/{primitives → __tests__}/$sms.spec.ts +0 -0
- /package/src/sms/{providers → __tests__}/MemorySmsProvider.spec.ts +0 -0
- /package/src/system/{services → __tests__}/FileDetector.spec.ts +0 -0
- /package/src/system/{providers → __tests__}/NodeFileSystemProvider.spec.ts +0 -0
- /package/src/topic/core/{primitives → __tests__}/$subscriber.spec.ts +0 -0
- /package/src/topic/core/{providers → __tests__}/MemoryTopicProvider.spec.ts +0 -0
- /package/src/topic/redis/{providers → __tests__}/RedisTopicProvider.spec.ts +0 -0
- /package/src/websocket/{primitives → __tests__}/$channel.spec.ts +0 -0
|
@@ -63,7 +63,7 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
63
63
|
|
|
64
64
|
// Merge features with defaults
|
|
65
65
|
const features: RealmFeatures = {
|
|
66
|
-
|
|
66
|
+
jobs: false,
|
|
67
67
|
notifications: false,
|
|
68
68
|
apiKeys: false,
|
|
69
69
|
parameters: false,
|
|
@@ -95,7 +95,7 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
95
95
|
alepha.with(UserAudits);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
if (features.
|
|
98
|
+
if (features.jobs) {
|
|
99
99
|
alepha.with(UserJobs);
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -227,11 +227,13 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
227
227
|
|
|
228
228
|
export interface RealmFeatures {
|
|
229
229
|
/**
|
|
230
|
-
*
|
|
230
|
+
* Will enable Job module.
|
|
231
|
+
*
|
|
232
|
+
* - Enable session purge functionality for cleaning up expired sessions.
|
|
231
233
|
*
|
|
232
234
|
* @default false
|
|
233
235
|
*/
|
|
234
|
-
|
|
236
|
+
jobs?: boolean;
|
|
235
237
|
|
|
236
238
|
/**
|
|
237
239
|
* Enable notification system for password reset, verification emails, etc.
|
|
@@ -339,7 +339,7 @@ export class RegistrationService {
|
|
|
339
339
|
|
|
340
340
|
if (body.username) {
|
|
341
341
|
const existingUser = await userRepository.findOne({
|
|
342
|
-
where: { username: {
|
|
342
|
+
where: { username: { ilike: body.username } },
|
|
343
343
|
});
|
|
344
344
|
if (existingUser) {
|
|
345
345
|
this.log.debug("Username already taken", { username: body.username });
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
InvalidCredentialsError,
|
|
10
10
|
type UserAccount,
|
|
11
11
|
} from "alepha/security";
|
|
12
|
-
import { UnauthorizedError } from "alepha/server";
|
|
12
|
+
import { BadRequestError, UnauthorizedError } from "alepha/server";
|
|
13
13
|
import type { OAuth2Profile } from "alepha/server/auth";
|
|
14
14
|
import { $client } from "alepha/server/links";
|
|
15
15
|
import { FileSystemProvider } from "alepha/system";
|
|
@@ -98,6 +98,78 @@ export class SessionService {
|
|
|
98
98
|
return true;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Generate a unique username from an OAuth profile.
|
|
103
|
+
*
|
|
104
|
+
* 1. Extract candidate from email prefix
|
|
105
|
+
* 2. Sanitize against realm's usernameRegExp (strip invalid chars, truncate)
|
|
106
|
+
* 3. Check case-insensitive uniqueness, append suffix (2, 3, ...) if taken
|
|
107
|
+
* 4. Fall back to "user" + random 6-char alphanumeric if all else fails
|
|
108
|
+
*/
|
|
109
|
+
protected async generateUniqueUsername(
|
|
110
|
+
profile: OAuth2Profile,
|
|
111
|
+
realmSettings: any,
|
|
112
|
+
users: any,
|
|
113
|
+
): Promise<string> {
|
|
114
|
+
const maxLength = 30;
|
|
115
|
+
const maxSuffixAttempts = 10;
|
|
116
|
+
|
|
117
|
+
// Extract candidate from email or profile name
|
|
118
|
+
let candidate = profile.email?.split("@")[0] ?? profile.name ?? "";
|
|
119
|
+
|
|
120
|
+
// Strip characters not allowed in usernames (keep alphanumeric, underscore, dot, hyphen)
|
|
121
|
+
candidate = candidate.replace(/[^a-zA-Z0-9_.-]/g, "");
|
|
122
|
+
|
|
123
|
+
// If realm has a custom regex, further sanitize
|
|
124
|
+
if (realmSettings?.usernameRegExp) {
|
|
125
|
+
try {
|
|
126
|
+
const regex = new RegExp(realmSettings.usernameRegExp);
|
|
127
|
+
if (!regex.test(candidate)) {
|
|
128
|
+
// Strip to basic alphanumeric as safe fallback
|
|
129
|
+
candidate = candidate.replace(/[^a-zA-Z0-9_]/g, "");
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// Invalid regex, continue with sanitized candidate
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Ensure minimum length
|
|
137
|
+
if (candidate.length < 3) {
|
|
138
|
+
candidate = `user${candidate}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Truncate to leave room for suffix
|
|
142
|
+
candidate = candidate.slice(0, maxLength - 2);
|
|
143
|
+
|
|
144
|
+
// Check uniqueness (case-insensitive)
|
|
145
|
+
const isAvailable = async (name: string) => {
|
|
146
|
+
const existing = await users.findMany({
|
|
147
|
+
where: { username: { contains: name } },
|
|
148
|
+
limit: 1,
|
|
149
|
+
});
|
|
150
|
+
// Case-insensitive check
|
|
151
|
+
return !existing.some(
|
|
152
|
+
(u: any) => u.username?.toLowerCase() === name.toLowerCase(),
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (await isAvailable(candidate)) {
|
|
157
|
+
return candidate;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Try with numeric suffix
|
|
161
|
+
for (let i = 2; i <= maxSuffixAttempts + 1; i++) {
|
|
162
|
+
const withSuffix = `${candidate}${i}`;
|
|
163
|
+
if (withSuffix.length <= maxLength && (await isAvailable(withSuffix))) {
|
|
164
|
+
return withSuffix;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Final fallback: random username
|
|
169
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
170
|
+
return `user${random}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
101
173
|
/**
|
|
102
174
|
* Random delay to prevent timing attacks (50-200ms)
|
|
103
175
|
* Uses cryptographically secure random number generation
|
|
@@ -222,7 +294,7 @@ export class SessionService {
|
|
|
222
294
|
throw new InvalidCredentialsError();
|
|
223
295
|
}
|
|
224
296
|
}
|
|
225
|
-
where.username = username;
|
|
297
|
+
where.username = { ilike: username };
|
|
226
298
|
} else if (settings.email !== "none" && isEmail) {
|
|
227
299
|
where.email = username;
|
|
228
300
|
} else if (settings.phoneNumber !== "none" && isPhone) {
|
|
@@ -596,12 +668,27 @@ export class SessionService {
|
|
|
596
668
|
return existing;
|
|
597
669
|
}
|
|
598
670
|
|
|
599
|
-
|
|
600
|
-
|
|
671
|
+
const realmSettings = await realm.getSettings();
|
|
672
|
+
const adminEmails = realmSettings?.adminEmails ?? [];
|
|
673
|
+
const isAdmin = profile.email && adminEmails.includes(profile.email);
|
|
674
|
+
|
|
675
|
+
if (realmSettings?.registrationAllowed === false && !isAdmin) {
|
|
676
|
+
this.log.warn("Registration not allowed for realm via OAuth2", {
|
|
677
|
+
provider,
|
|
678
|
+
userRealmName,
|
|
679
|
+
});
|
|
680
|
+
throw new BadRequestError("Account doesn't exist");
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const username = await this.generateUniqueUsername(
|
|
684
|
+
profile,
|
|
685
|
+
realmSettings,
|
|
686
|
+
users,
|
|
687
|
+
);
|
|
601
688
|
|
|
602
689
|
const user = await users.create({
|
|
603
690
|
realm: realm.name,
|
|
604
|
-
username
|
|
691
|
+
username,
|
|
605
692
|
email: profile.email,
|
|
606
693
|
// we trust the OAuth2 provider
|
|
607
694
|
emailVerified: true,
|
|
@@ -276,7 +276,7 @@ export class UserService {
|
|
|
276
276
|
// Check for existing user based on provided unique fields
|
|
277
277
|
if (data.username) {
|
|
278
278
|
const existingUser = await this.users(userRealmName).findOne({
|
|
279
|
-
where: { username: {
|
|
279
|
+
where: { username: { ilike: data.username } },
|
|
280
280
|
});
|
|
281
281
|
|
|
282
282
|
if (existingUser) {
|
|
@@ -23,6 +23,8 @@ describe("VerificationJobs", () => {
|
|
|
23
23
|
const time = alepha.inject(DateTimeProvider);
|
|
24
24
|
const jobs = alepha.inject(VerificationJobs);
|
|
25
25
|
|
|
26
|
+
await db.verifications.deleteMany({ target: { eq: "hello@mail.com" } });
|
|
27
|
+
|
|
26
28
|
const entry = {
|
|
27
29
|
type: "link",
|
|
28
30
|
target: "hello@mail.com",
|
|
@@ -36,12 +38,12 @@ describe("VerificationJobs", () => {
|
|
|
36
38
|
|
|
37
39
|
await db.verifications.create({
|
|
38
40
|
...entry,
|
|
39
|
-
createdAt: time.now().subtract(
|
|
41
|
+
createdAt: time.now().subtract(2, "days").toISOString(),
|
|
40
42
|
});
|
|
41
43
|
|
|
42
44
|
await db.verifications.create({
|
|
43
45
|
...entry,
|
|
44
|
-
createdAt: time.now().subtract(
|
|
46
|
+
createdAt: time.now().subtract(3, "days").toISOString(),
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
expect(await db.verifications.count()).toBe(3);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $atom, $
|
|
1
|
+
import { $atom, $state, type Static } from "alepha";
|
|
2
2
|
import {
|
|
3
3
|
type VerificationSettings,
|
|
4
4
|
verificationSettingsSchema,
|
|
@@ -41,7 +41,7 @@ declare module "alepha" {
|
|
|
41
41
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
42
42
|
|
|
43
43
|
export class VerificationParameters {
|
|
44
|
-
protected readonly options = $
|
|
44
|
+
protected readonly options = $state(verificationOptions);
|
|
45
45
|
|
|
46
46
|
public get<K extends keyof VerificationSettings>(
|
|
47
47
|
key: K,
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Alepha } from "alepha";
|
|
2
|
+
import { AlephaOrmPostgres } from "alepha/orm/postgres";
|
|
3
|
+
import { describe, it } from "vitest";
|
|
4
|
+
import { AlephaBilling } from "../index.ts";
|
|
5
|
+
import { BillingService } from "../services/BillingService.ts";
|
|
6
|
+
|
|
7
|
+
describe("BillingService", () => {
|
|
8
|
+
it("should create an intent in 'created' status", async ({ expect }) => {
|
|
9
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
10
|
+
const billing = alepha.inject(BillingService);
|
|
11
|
+
await alepha.start();
|
|
12
|
+
|
|
13
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
14
|
+
expect(intent.amount).toBe(1500);
|
|
15
|
+
expect(intent.currency).toBe("eur");
|
|
16
|
+
expect(intent.status).toBe("created");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should create a session and transition to 'processing'", async ({
|
|
20
|
+
expect,
|
|
21
|
+
}) => {
|
|
22
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
23
|
+
const billing = alepha.inject(BillingService);
|
|
24
|
+
await alepha.start();
|
|
25
|
+
|
|
26
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
27
|
+
const session = await billing.createSession(
|
|
28
|
+
intent.id,
|
|
29
|
+
"https://example.com/return",
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(session.url).toContain("/billing/mock-checkout/");
|
|
33
|
+
expect(session.intentId).toBe(intent.id);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should capture an authorized intent", async ({ expect }) => {
|
|
37
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
38
|
+
const billing = alepha.inject(BillingService);
|
|
39
|
+
await alepha.start();
|
|
40
|
+
|
|
41
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
42
|
+
await billing.createSession(intent.id, "https://example.com", true);
|
|
43
|
+
await billing.handleWebhookEvent(intent.id, "authorized");
|
|
44
|
+
|
|
45
|
+
const captured = await billing.capture(intent.id);
|
|
46
|
+
expect(captured.status).toBe("captured");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should void an authorized intent", async ({ expect }) => {
|
|
50
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
51
|
+
const billing = alepha.inject(BillingService);
|
|
52
|
+
await alepha.start();
|
|
53
|
+
|
|
54
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
55
|
+
await billing.createSession(intent.id, "https://example.com", true);
|
|
56
|
+
await billing.handleWebhookEvent(intent.id, "authorized");
|
|
57
|
+
|
|
58
|
+
const voided = await billing.void(intent.id);
|
|
59
|
+
expect(voided.status).toBe("voided");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should refund a captured intent", async ({ expect }) => {
|
|
63
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
64
|
+
const billing = alepha.inject(BillingService);
|
|
65
|
+
await alepha.start();
|
|
66
|
+
|
|
67
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
68
|
+
await billing.createSession(intent.id, "https://example.com");
|
|
69
|
+
await billing.handleWebhookEvent(intent.id, "captured");
|
|
70
|
+
|
|
71
|
+
const refund = await billing.refund(intent.id, 500, "Customer request");
|
|
72
|
+
expect(refund.amount).toBe(500);
|
|
73
|
+
expect(refund.status).toBe("completed");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should record a cash payment directly as captured", async ({
|
|
77
|
+
expect,
|
|
78
|
+
}) => {
|
|
79
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
80
|
+
const billing = alepha.inject(BillingService);
|
|
81
|
+
await alepha.start();
|
|
82
|
+
|
|
83
|
+
const intent = await billing.recordCashPayment(1500, "eur", {
|
|
84
|
+
orderId: "order-1",
|
|
85
|
+
});
|
|
86
|
+
expect(intent.status).toBe("captured");
|
|
87
|
+
expect(intent.metadata).toEqual({ orderId: "order-1" });
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should cancel a created intent", async ({ expect }) => {
|
|
91
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
92
|
+
const billing = alepha.inject(BillingService);
|
|
93
|
+
await alepha.start();
|
|
94
|
+
|
|
95
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
96
|
+
const cancelled = await billing.cancel(intent.id);
|
|
97
|
+
expect(cancelled.status).toBe("cancelled");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should reject capture from wrong status", async ({ expect }) => {
|
|
101
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
102
|
+
const billing = alepha.inject(BillingService);
|
|
103
|
+
await alepha.start();
|
|
104
|
+
|
|
105
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
106
|
+
await expect(billing.capture(intent.id)).rejects.toThrowError();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should reject refund from wrong status", async ({ expect }) => {
|
|
110
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
111
|
+
const billing = alepha.inject(BillingService);
|
|
112
|
+
await alepha.start();
|
|
113
|
+
|
|
114
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
115
|
+
await expect(billing.refund(intent.id, 500)).rejects.toThrowError();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should reject void from wrong status", async ({ expect }) => {
|
|
119
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
120
|
+
const billing = alepha.inject(BillingService);
|
|
121
|
+
await alepha.start();
|
|
122
|
+
|
|
123
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
124
|
+
await expect(billing.void(intent.id)).rejects.toThrowError();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should reject cancel from wrong status", async ({ expect }) => {
|
|
128
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
129
|
+
const billing = alepha.inject(BillingService);
|
|
130
|
+
await alepha.start();
|
|
131
|
+
|
|
132
|
+
const intent = await billing.createIntent(1500, "eur");
|
|
133
|
+
await billing.createSession(intent.id, "https://example.com");
|
|
134
|
+
await expect(billing.cancel(intent.id)).rejects.toThrowError();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Alepha } from "alepha";
|
|
3
|
+
import { AlephaOrmPostgres } from "alepha/orm/postgres";
|
|
4
|
+
import { describe, it } from "vitest";
|
|
5
|
+
import { AlephaBilling } from "../index.ts";
|
|
6
|
+
import { PaymentMethodService } from "../services/PaymentMethodService.ts";
|
|
7
|
+
|
|
8
|
+
describe("PaymentMethodService", () => {
|
|
9
|
+
const userId = randomUUID();
|
|
10
|
+
const userId2 = randomUUID();
|
|
11
|
+
const orgId = randomUUID();
|
|
12
|
+
it("should add a payment method", async ({ expect }) => {
|
|
13
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
14
|
+
const service = alepha.inject(PaymentMethodService);
|
|
15
|
+
await alepha.start();
|
|
16
|
+
|
|
17
|
+
const method = await service.addPaymentMethod(userId, orgId, "tok_visa");
|
|
18
|
+
expect(method.type).toBe("card");
|
|
19
|
+
expect(method.last4).toBe("4242");
|
|
20
|
+
expect(method.isDefault).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should list payment methods for a user", async ({ expect }) => {
|
|
24
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
25
|
+
const service = alepha.inject(PaymentMethodService);
|
|
26
|
+
await alepha.start();
|
|
27
|
+
|
|
28
|
+
await service.addPaymentMethod(userId, orgId, "tok_visa");
|
|
29
|
+
await service.addPaymentMethod(userId, orgId, "tok_mastercard");
|
|
30
|
+
|
|
31
|
+
const methods = await service.listPaymentMethods(userId);
|
|
32
|
+
expect(methods).toHaveLength(2);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should remove a payment method", async ({ expect }) => {
|
|
36
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
37
|
+
const service = alepha.inject(PaymentMethodService);
|
|
38
|
+
await alepha.start();
|
|
39
|
+
|
|
40
|
+
const method = await service.addPaymentMethod(userId, orgId, "tok_visa");
|
|
41
|
+
await service.removePaymentMethod(method.id, userId);
|
|
42
|
+
|
|
43
|
+
const methods = await service.listPaymentMethods(userId);
|
|
44
|
+
expect(methods).toHaveLength(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should set a default payment method", async ({ expect }) => {
|
|
48
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
49
|
+
const service = alepha.inject(PaymentMethodService);
|
|
50
|
+
await alepha.start();
|
|
51
|
+
|
|
52
|
+
const method1 = await service.addPaymentMethod(userId, orgId, "tok_visa");
|
|
53
|
+
const method2 = await service.addPaymentMethod(
|
|
54
|
+
userId,
|
|
55
|
+
orgId,
|
|
56
|
+
"tok_mastercard",
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
await service.setDefault(method1.id, userId);
|
|
60
|
+
|
|
61
|
+
const methods = await service.listPaymentMethods(userId);
|
|
62
|
+
const defaultMethod = methods.find((m) => m.isDefault);
|
|
63
|
+
expect(defaultMethod?.id).toBe(method1.id);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should reject removing another user's payment method", async ({
|
|
67
|
+
expect,
|
|
68
|
+
}) => {
|
|
69
|
+
const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
|
|
70
|
+
const service = alepha.inject(PaymentMethodService);
|
|
71
|
+
await alepha.start();
|
|
72
|
+
|
|
73
|
+
const method = await service.addPaymentMethod(userId, orgId, "tok_visa");
|
|
74
|
+
await expect(
|
|
75
|
+
service.removePaymentMethod(method.id, userId2),
|
|
76
|
+
).rejects.toThrowError();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { $inject, t } from "alepha";
|
|
2
|
+
import { $secure } from "alepha/security";
|
|
3
|
+
import { $action, okSchema } from "alepha/server";
|
|
4
|
+
import {
|
|
5
|
+
captureIntentSchema,
|
|
6
|
+
intentQuerySchema,
|
|
7
|
+
intentResourceSchema,
|
|
8
|
+
recordCashSchema,
|
|
9
|
+
refundIntentSchema,
|
|
10
|
+
} from "../schemas/intentSchemas.ts";
|
|
11
|
+
import { refundResourceSchema } from "../schemas/refundSchemas.ts";
|
|
12
|
+
import { BillingService } from "../services/BillingService.ts";
|
|
13
|
+
|
|
14
|
+
export class AdminBillingController {
|
|
15
|
+
protected readonly url = "/admin/billing";
|
|
16
|
+
protected readonly group = "admin:billing";
|
|
17
|
+
protected readonly billing = $inject(BillingService);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* List payment intents with pagination and filtering.
|
|
21
|
+
*/
|
|
22
|
+
public readonly listIntents = $action({
|
|
23
|
+
path: `${this.url}/intents`,
|
|
24
|
+
group: this.group,
|
|
25
|
+
use: [$secure({ permissions: ["billing:read"] })],
|
|
26
|
+
description: "List payment intents",
|
|
27
|
+
schema: {
|
|
28
|
+
query: intentQuerySchema,
|
|
29
|
+
response: t.page(intentResourceSchema),
|
|
30
|
+
},
|
|
31
|
+
handler: ({ query }) => this.billing.findIntents(query),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get a payment intent by ID.
|
|
36
|
+
*/
|
|
37
|
+
public readonly getIntent = $action({
|
|
38
|
+
path: `${this.url}/intents/:id`,
|
|
39
|
+
group: this.group,
|
|
40
|
+
use: [$secure({ permissions: ["billing:read"] })],
|
|
41
|
+
description: "Get payment intent details",
|
|
42
|
+
schema: {
|
|
43
|
+
params: t.object({ id: t.uuid() }),
|
|
44
|
+
response: intentResourceSchema,
|
|
45
|
+
},
|
|
46
|
+
handler: ({ params }) => this.billing.getIntent(params.id),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Capture an authorized intent.
|
|
51
|
+
*/
|
|
52
|
+
public readonly captureIntent = $action({
|
|
53
|
+
method: "POST",
|
|
54
|
+
path: `${this.url}/intents/:id/capture`,
|
|
55
|
+
group: this.group,
|
|
56
|
+
use: [$secure({ permissions: ["billing:write"] })],
|
|
57
|
+
description: "Capture an authorized payment intent",
|
|
58
|
+
schema: {
|
|
59
|
+
params: t.object({ id: t.uuid() }),
|
|
60
|
+
body: captureIntentSchema,
|
|
61
|
+
response: intentResourceSchema,
|
|
62
|
+
},
|
|
63
|
+
handler: ({ params, body }) => this.billing.capture(params.id, body.amount),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Void an authorized intent.
|
|
68
|
+
*/
|
|
69
|
+
public readonly voidIntent = $action({
|
|
70
|
+
method: "POST",
|
|
71
|
+
path: `${this.url}/intents/:id/void`,
|
|
72
|
+
group: this.group,
|
|
73
|
+
use: [$secure({ permissions: ["billing:write"] })],
|
|
74
|
+
description: "Void an authorized payment intent",
|
|
75
|
+
schema: {
|
|
76
|
+
params: t.object({ id: t.uuid() }),
|
|
77
|
+
response: intentResourceSchema,
|
|
78
|
+
},
|
|
79
|
+
handler: ({ params }) => this.billing.void(params.id),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Refund a captured intent.
|
|
84
|
+
*/
|
|
85
|
+
public readonly refundIntent = $action({
|
|
86
|
+
method: "POST",
|
|
87
|
+
path: `${this.url}/intents/:id/refund`,
|
|
88
|
+
group: this.group,
|
|
89
|
+
use: [$secure({ permissions: ["billing:write"] })],
|
|
90
|
+
description: "Issue partial or full refund",
|
|
91
|
+
schema: {
|
|
92
|
+
params: t.object({ id: t.uuid() }),
|
|
93
|
+
body: refundIntentSchema,
|
|
94
|
+
response: refundResourceSchema,
|
|
95
|
+
},
|
|
96
|
+
handler: ({ params, body }) =>
|
|
97
|
+
this.billing.refund(params.id, body.amount, body.reason),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Cancel a created intent.
|
|
102
|
+
*/
|
|
103
|
+
public readonly cancelIntent = $action({
|
|
104
|
+
method: "POST",
|
|
105
|
+
path: `${this.url}/intents/:id/cancel`,
|
|
106
|
+
group: this.group,
|
|
107
|
+
use: [$secure({ permissions: ["billing:write"] })],
|
|
108
|
+
description: "Cancel a created payment intent",
|
|
109
|
+
schema: {
|
|
110
|
+
params: t.object({ id: t.uuid() }),
|
|
111
|
+
response: intentResourceSchema,
|
|
112
|
+
},
|
|
113
|
+
handler: ({ params }) => this.billing.cancel(params.id),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Record a cash payment.
|
|
118
|
+
*/
|
|
119
|
+
public readonly recordCash = $action({
|
|
120
|
+
method: "POST",
|
|
121
|
+
path: `${this.url}/cash`,
|
|
122
|
+
group: this.group,
|
|
123
|
+
use: [$secure({ permissions: ["billing:write"] })],
|
|
124
|
+
description: "Record a cash payment",
|
|
125
|
+
schema: {
|
|
126
|
+
body: recordCashSchema,
|
|
127
|
+
response: intentResourceSchema,
|
|
128
|
+
},
|
|
129
|
+
handler: ({ body }) =>
|
|
130
|
+
this.billing.recordCashPayment(body.amount, body.currency, body.metadata),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* PSP webhook endpoint (not under /admin, no auth — verified by provider).
|
|
135
|
+
*/
|
|
136
|
+
public readonly webhook = $action({
|
|
137
|
+
method: "POST",
|
|
138
|
+
path: "/billing/webhook",
|
|
139
|
+
group: this.group,
|
|
140
|
+
description: "PSP webhook endpoint",
|
|
141
|
+
schema: {
|
|
142
|
+
response: okSchema,
|
|
143
|
+
},
|
|
144
|
+
handler: async (request) => {
|
|
145
|
+
await this.billing.handleWebhook(request.raw.web!.req);
|
|
146
|
+
return { ok: true };
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|