alepha 0.20.5 → 0.20.7
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/AGENTS.md +0 -1
- package/CLAUDE.md +0 -1
- package/assets/agents-template.md +0 -1
- package/dist/api/audits/index.browser.js +1 -0
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +701 -654
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +24 -1
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +1 -0
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +193 -166
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +52 -0
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +40 -14
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +639 -333
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +495 -162
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +222 -188
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js +54 -0
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +265 -236
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +55 -13
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/organizations/index.d.ts +100 -97
- package/dist/api/organizations/index.d.ts.map +1 -1
- package/dist/api/organizations/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +332 -314
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +37 -0
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.d.ts +431 -376
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/payments/index.js +202 -87
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/subscriptions/index.d.ts +1695 -0
- package/dist/api/subscriptions/index.d.ts.map +1 -0
- package/dist/api/subscriptions/index.js +1919 -0
- package/dist/api/subscriptions/index.js.map +1 -0
- package/dist/api/users/index.d.ts +1001 -844
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +237 -28
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +123 -122
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +21 -2
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +47 -0
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +24 -0
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +134 -7
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +181 -15
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +181 -15
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/database/index.d.ts +156 -0
- package/dist/cache/database/index.d.ts.map +1 -0
- package/dist/cache/database/index.js +266 -0
- package/dist/cache/database/index.js.map +1 -0
- package/dist/cache/redis/index.d.ts +3 -2
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +142 -128
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +160 -13
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +3 -2
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +346 -290
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +106 -7
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +12 -11
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts +6 -5
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +119 -118
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +1 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +1 -1
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.browser.js.map +1 -1
- package/dist/crypto/index.d.ts +3 -2
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/brevo/index.js.map +1 -1
- package/dist/email/core/index.d.ts +3 -2
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/core/index.workerd.js.map +1 -1
- package/dist/email/smtp/index.d.ts +7 -6
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +5 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.d.ts +10 -9
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +9 -8
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +9 -3
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +31 -10
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +33 -14
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +31 -10
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +6 -5
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +5 -4
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.d.ts +3 -2
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +5 -0
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +8 -4
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.browser.js.map +1 -1
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +2 -1
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +206 -205
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +11 -11
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +25 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +12 -0
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +12 -0
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js +29 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +82 -35
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +56 -3
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +163 -158
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +16 -4
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +35 -34
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +7 -6
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.js.map +1 -1
- package/dist/server/health/index.d.ts +16 -15
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +51 -50
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +6 -5
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +2 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/topic/redis/index.d.ts +3 -2
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/topic/redis/index.js.map +1 -1
- package/package.json +33 -39
- package/src/api/audits/controllers/AdminAuditController.ts +29 -0
- package/src/api/audits/entities/audits.ts +1 -0
- package/src/api/files/controllers/FileController.ts +24 -0
- package/src/api/files/entities/files.ts +1 -0
- package/src/api/files/services/FileService.ts +41 -0
- package/src/api/jobs/__tests__/$job.spec.ts +501 -24
- package/src/api/jobs/entities/jobExecutionEntity.ts +4 -3
- package/src/api/jobs/index.ts +47 -10
- package/src/api/jobs/primitives/$job.ts +22 -9
- package/src/api/jobs/providers/DirectJobDispatcher.ts +71 -0
- package/src/api/jobs/providers/JobDispatcher.ts +49 -0
- package/src/api/jobs/providers/JobProvider.ts +385 -147
- package/src/api/jobs/providers/JobQueueProvider.ts +43 -18
- package/src/api/jobs/schemas/jobConfigAtom.ts +9 -3
- package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +11 -0
- package/src/api/jobs/schemas/jobRegistrationSchema.ts +4 -2
- package/src/api/jobs/services/JobService.ts +21 -11
- package/src/api/keys/controllers/AdminApiKeyController.ts +23 -0
- package/src/api/keys/entities/apiKeyEntity.ts +1 -0
- package/src/api/keys/services/ApiKeyService.ts +42 -0
- package/src/api/notifications/__tests__/AlephaApiNotifications.spec.ts +63 -0
- package/src/api/notifications/controllers/AdminNotificationController.ts +48 -1
- package/src/api/notifications/index.ts +13 -3
- package/src/api/notifications/jobs/NotificationJobs.ts +0 -6
- package/src/api/parameters/controllers/AdminParameterController.ts +26 -0
- package/src/api/parameters/services/ParameterProvider.ts +18 -0
- package/src/api/payments/controllers/MockCheckoutController.ts +146 -0
- package/src/api/payments/index.ts +3 -0
- package/src/api/payments/providers/MemoryPaymentProvider.ts +9 -4
- package/src/api/payments/providers/PaymentProvider.ts +25 -9
- package/src/api/payments/services/PaymentService.ts +3 -0
- package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
- package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
- package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
- package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
- package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
- package/src/api/subscriptions/entities/subscriptions.ts +68 -0
- package/src/api/subscriptions/index.ts +133 -0
- package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
- package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
- package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
- package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
- package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
- package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
- package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
- package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
- package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
- package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
- package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
- package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
- package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
- package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
- package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
- package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
- package/src/api/subscriptions/services/BillingService.ts +437 -0
- package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
- package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
- package/src/api/subscriptions/services/UsageService.ts +118 -0
- package/src/api/users/__tests__/Registration-emailMode.spec.ts +203 -0
- package/src/api/users/__tests__/UsernameSlugger.spec.ts +138 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +41 -3
- package/src/api/users/controllers/AdminSessionController.ts +29 -0
- package/src/api/users/controllers/AdminUserController.ts +32 -0
- package/src/api/users/index.ts +3 -0
- package/src/api/users/services/CredentialService.ts +5 -0
- package/src/api/users/services/RegistrationService.ts +49 -1
- package/src/api/users/services/SessionCrudService.ts +16 -0
- package/src/api/users/services/SessionService.ts +17 -59
- package/src/api/users/services/UsernameSlugger.ts +195 -0
- package/src/bucket/primitives/$bucket.ts +21 -0
- package/src/bucket/providers/CloudflareR2Provider.ts +15 -0
- package/src/bucket/providers/FileStorageProvider.ts +9 -0
- package/src/bucket/providers/LocalFileStorageProvider.ts +14 -0
- package/src/bucket/providers/MemoryFileStorageProvider.ts +9 -0
- package/src/bucket/providers/NodeS3BucketProvider.ts +35 -0
- package/src/cache/core/__tests__/$cache.memory.spec.ts +450 -0
- package/src/cache/core/__tests__/$cache.swr.spec.ts +394 -0
- package/src/cache/core/index.ts +16 -0
- package/src/cache/core/primitives/$cache.ts +367 -24
- package/src/cache/database/__tests__/DatabaseCacheProvider.behavior.spec.ts +203 -0
- package/src/cache/database/__tests__/DatabaseCacheProvider.spec.ts +110 -0
- package/src/cache/database/entities/cacheEntries.ts +55 -0
- package/src/cache/database/index.ts +36 -0
- package/src/cache/database/providers/DatabaseCacheProvider.ts +348 -0
- package/src/cli/core/services/ProjectScaffolder.ts +0 -2
- package/src/cli/core/tasks/BuildCloudflareTask.ts +33 -3
- package/src/cli/core/tasks/BuildSitemapTask.ts +7 -0
- package/src/cli/core/tasks/BuildVercelTask.ts +82 -3
- package/src/cli/core/templates/agentMd.ts +39 -4
- package/src/cli/core/templates/biomeJson.ts +25 -1
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +2 -2
- package/src/cli/platform/__tests__/CloudflareAdapter.spec.ts +117 -0
- package/src/cli/platform/__tests__/detectResources.spec.ts +96 -0
- package/src/cli/platform/adapters/CloudflareAdapter.ts +104 -7
- package/src/cli/platform/atoms/platformOptions.ts +13 -0
- package/src/cli/platform/commands/platform.ts +7 -1
- package/src/cli/platform/schemas/platform.ts +1 -0
- package/src/cli/platform/services/CloudflareApi.ts +61 -0
- package/src/cli/platform/services/PlatformOrchestrator.ts +9 -4
- package/src/core/__tests__/$module.spec.ts +2 -2
- package/src/core/primitives/$module.ts +4 -4
- package/src/mcp/providers/McpServerProvider.ts +1 -1
- package/src/orm/core/providers/DatabaseTypeProvider.ts +9 -3
- package/src/orm/core/providers/drivers/DatabaseProvider.ts +1 -1
- package/src/orm/core/schemas/insertSchema.ts +10 -2
- package/src/orm/core/services/Repository.ts +27 -7
- package/src/react/form/hooks/useFormState.ts +8 -1
- package/src/react/form/index.ts +10 -1
- package/src/react/form/services/FormModel.ts +9 -3
- package/src/scheduler/index.ts +14 -0
- package/src/scheduler/providers/CronProvider.ts +13 -0
- package/src/security/atoms/currentTenantAtom.ts +34 -0
- package/src/security/index.browser.ts +1 -0
- package/src/security/index.ts +12 -1
- package/src/security/primitives/$issuer.ts +17 -1
- package/src/security/providers/SecurityProvider.ts +37 -0
- package/src/server/auth/__tests__/validateRedirectUri.spec.ts +78 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +21 -5
- package/tsconfig.base.json +2 -1
- package/dist/react/websocket/index.d.ts +0 -117
- package/dist/react/websocket/index.d.ts.map +0 -1
- package/dist/react/websocket/index.js +0 -108
- package/dist/react/websocket/index.js.map +0 -1
- package/dist/websocket/index.browser.js +0 -844
- package/dist/websocket/index.browser.js.map +0 -1
- package/dist/websocket/index.d.ts +0 -876
- package/dist/websocket/index.d.ts.map +0 -1
- package/dist/websocket/index.js +0 -1175
- package/dist/websocket/index.js.map +0 -1
- package/src/react/websocket/hooks/useRoom.tsx +0 -251
- package/src/react/websocket/index.ts +0 -7
- package/src/websocket/__tests__/$channel.spec.ts +0 -30
- package/src/websocket/__tests__/$websocket-new.spec.ts +0 -195
- package/src/websocket/__tests__/RoomManager.spec.ts +0 -146
- package/src/websocket/__tests__/websocket-integration.spec.ts +0 -951
- package/src/websocket/errors/WebSocketError.ts +0 -34
- package/src/websocket/index.browser.ts +0 -25
- package/src/websocket/index.shared.ts +0 -8
- package/src/websocket/index.ts +0 -85
- package/src/websocket/interfaces/WebSocketInterfaces.ts +0 -252
- package/src/websocket/primitives/$channel.ts +0 -131
- package/src/websocket/primitives/$websocket.ts +0 -107
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +0 -617
- package/src/websocket/providers/WebSocketServerProvider.ts +0 -56
- package/src/websocket/services/RoomManager.ts +0 -160
- package/src/websocket/services/WebSocketClient.ts +0 -642
- package/src/websocket/services/WebSocketTopicService.ts +0 -108
|
@@ -1,42 +1,67 @@
|
|
|
1
|
-
import { $inject, t } from "alepha";
|
|
1
|
+
import { $inject, Alepha, t } from "alepha";
|
|
2
2
|
import { $queue } from "alepha/queue";
|
|
3
|
+
import { JobDispatcher } from "./JobDispatcher.ts";
|
|
3
4
|
import { JobProvider } from "./JobProvider.ts";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
+
* Queue-backed `JobDispatcher` registered by `AlephaApiJobsQueue`.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
9
|
+
* Extends {@link JobDispatcher} and substitutes the default
|
|
10
|
+
* `DirectJobDispatcher` so that `$job.push()` is delivered through
|
|
11
|
+
* `AlephaQueue` (e.g. Cloudflare Queues, Redis, in-memory) instead of
|
|
12
|
+
* being processed in-process.
|
|
13
|
+
*
|
|
14
|
+
* The class is also kept as a `JobQueueProvider` export name for backwards
|
|
15
|
+
* compatibility — it has always been the queue path's entry point.
|
|
11
16
|
*/
|
|
12
|
-
export class JobQueueProvider {
|
|
13
|
-
|
|
17
|
+
export class JobQueueProvider extends JobDispatcher {
|
|
18
|
+
public readonly kind = "queue" as const;
|
|
19
|
+
protected readonly alepha = $inject(Alepha);
|
|
20
|
+
|
|
21
|
+
// Lazy to avoid the JobProvider ↔ JobDispatcher injection cycle
|
|
22
|
+
// (JobProvider injects JobDispatcher; the queue consumer needs
|
|
23
|
+
// JobProvider to process). Resolved at message-receive time.
|
|
24
|
+
protected jobProviderRef?: JobProvider;
|
|
25
|
+
protected getJobProvider(): JobProvider {
|
|
26
|
+
if (!this.jobProviderRef) {
|
|
27
|
+
this.jobProviderRef = this.alepha.inject(JobProvider);
|
|
28
|
+
}
|
|
29
|
+
return this.jobProviderRef;
|
|
30
|
+
}
|
|
14
31
|
|
|
15
32
|
protected readonly queue = $queue({
|
|
16
33
|
name: "api:jobs:dispatch",
|
|
17
34
|
schema: t.object({ jobName: t.text(), executionId: t.text() }),
|
|
18
35
|
handler: async (msg) => {
|
|
19
|
-
await this.
|
|
36
|
+
await this.getJobProvider().processExecution(
|
|
20
37
|
msg.payload.jobName,
|
|
21
38
|
msg.payload.executionId,
|
|
22
39
|
);
|
|
23
40
|
},
|
|
24
41
|
});
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// so JobProvider can validate presence and queue-mode push works
|
|
29
|
-
// from any lifecycle point.
|
|
30
|
-
this.wireDispatcher();
|
|
43
|
+
public async dispatch(jobName: string, executionId: string): Promise<void> {
|
|
44
|
+
await this.queue.push({ jobName, executionId });
|
|
31
45
|
}
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Fan-out to a single variadic `queue.push(...payloads)` call so the
|
|
49
|
+
* underlying queue provider can batch the network round-trips when it
|
|
50
|
+
* supports it (Cloudflare Queues, Redis pipelines).
|
|
51
|
+
*/
|
|
52
|
+
public override async dispatchMany(
|
|
53
|
+
items: Array<{ jobName: string; executionId: string }>,
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
if (items.length === 0) return;
|
|
56
|
+
await this.queue.push(...items);
|
|
37
57
|
}
|
|
38
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Backwards-compatible alias for {@link dispatch}. Older code paths called
|
|
61
|
+
* `JobQueueProvider.push(jobName, executionId)` directly; new code should
|
|
62
|
+
* go through the `JobDispatcher.dispatch` API.
|
|
63
|
+
*/
|
|
39
64
|
public async push(jobName: string, executionId: string): Promise<void> {
|
|
40
|
-
|
|
65
|
+
return this.dispatch(jobName, executionId);
|
|
41
66
|
}
|
|
42
67
|
}
|
|
@@ -4,8 +4,13 @@ export const jobConfig = $atom({
|
|
|
4
4
|
name: "alepha.jobs",
|
|
5
5
|
description: "Configuration for the $job primitive.",
|
|
6
6
|
schema: t.object({
|
|
7
|
-
|
|
8
|
-
description:
|
|
7
|
+
sweepCron: t.text({
|
|
8
|
+
description:
|
|
9
|
+
"Cron expression for the sweep tick. Must be minute-granular at minimum (cron resolution). On Cloudflare Workers this expression is emitted into wrangler.jsonc by the build.",
|
|
10
|
+
}),
|
|
11
|
+
trimCron: t.text({
|
|
12
|
+
description:
|
|
13
|
+
"Cron expression for the ring-buffer trim tick (per-job keepLastSuccess/keepLastError enforcement). Decoupled from `sweepCron` because trim is bounded by job execution rate, not retry latency — running it every sweep is wasted work for most apps.",
|
|
9
14
|
}),
|
|
10
15
|
staleThreshold: t.integer({
|
|
11
16
|
description: "Pending age (ms) before the sweep re-dispatches it.",
|
|
@@ -29,7 +34,8 @@ export const jobConfig = $atom({
|
|
|
29
34
|
}),
|
|
30
35
|
}),
|
|
31
36
|
default: {
|
|
32
|
-
|
|
37
|
+
sweepCron: "*/5 * * * *",
|
|
38
|
+
trimCron: "0 * * * *",
|
|
33
39
|
staleThreshold: 300_000,
|
|
34
40
|
runTimeout: 1_800_000,
|
|
35
41
|
keepLastSuccess: 10,
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { type Static, t } from "alepha";
|
|
2
2
|
import { jobExecutionEntity } from "../entities/jobExecutionEntity.ts";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Public-facing schema for a job execution row.
|
|
6
|
+
*
|
|
7
|
+
* Diverges from the raw entity in two places, both for API ergonomics:
|
|
8
|
+
*
|
|
9
|
+
* - `priority` is exposed as the **string enum** (`critical`/`high`/...)
|
|
10
|
+
* instead of the numeric value used internally for SQL ordering. The
|
|
11
|
+
* `JobService` is responsible for the int → string transform.
|
|
12
|
+
* - `can` derives the available admin actions from the row's status.
|
|
13
|
+
*/
|
|
4
14
|
export const jobExecutionResourceSchema = t.extend(
|
|
5
15
|
jobExecutionEntity.schema,
|
|
6
16
|
{
|
|
17
|
+
priority: t.enum(["critical", "high", "normal", "low"]),
|
|
7
18
|
can: t.object({
|
|
8
19
|
retry: t.boolean(),
|
|
9
20
|
cancel: t.boolean(),
|
|
@@ -3,14 +3,16 @@ import { type Static, t } from "alepha";
|
|
|
3
3
|
export const jobRegistrationSchema = t.object({
|
|
4
4
|
name: t.text(),
|
|
5
5
|
description: t.optional(t.text()),
|
|
6
|
-
type: t.enum(["cron", "queue"]
|
|
6
|
+
type: t.enum(["cron", "queue", "direct"], {
|
|
7
|
+
description:
|
|
8
|
+
"Effective runtime mode. 'cron' = scheduled. 'queue' = push-driven, dispatched via AlephaApiJobsQueue. 'direct' = push-driven, processed in-process (no queue infrastructure loaded), with the sweep as the safety net.",
|
|
9
|
+
}),
|
|
7
10
|
priority: t.enum(["critical", "high", "normal", "low"]),
|
|
8
11
|
cron: t.optional(t.text()),
|
|
9
12
|
timeout: t.optional(t.text()),
|
|
10
13
|
retry: t.optional(
|
|
11
14
|
t.object({
|
|
12
15
|
retries: t.integer(),
|
|
13
|
-
hasBackoff: t.boolean(),
|
|
14
16
|
}),
|
|
15
17
|
),
|
|
16
18
|
recent: t.object({
|
|
@@ -5,8 +5,9 @@ import { NotFoundError } from "alepha/server";
|
|
|
5
5
|
import { jobExecutionEntity } from "../entities/jobExecutionEntity.ts";
|
|
6
6
|
import { $job } from "../primitives/$job.ts";
|
|
7
7
|
import type { JobTriggerContext } from "../providers/JobProvider.ts";
|
|
8
|
-
import { JobProvider } from "../providers/JobProvider.ts";
|
|
8
|
+
import { JobProvider, PRIORITY_REVERSE } from "../providers/JobProvider.ts";
|
|
9
9
|
import type { JobExecutionQuery } from "../schemas/jobExecutionQuerySchema.ts";
|
|
10
|
+
import type { JobExecutionResource } from "../schemas/jobExecutionResourceSchema.ts";
|
|
10
11
|
import type { JobRegistration } from "../schemas/jobRegistrationSchema.ts";
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -31,6 +32,22 @@ export class JobService {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Convert the int-priority storage column into the public enum string.
|
|
37
|
+
* The cast through `unknown` skips TypeScript's structural check between
|
|
38
|
+
* the entity-level row (`priority: number`) and the resource schema
|
|
39
|
+
* (`priority: enum`); the runtime values are correct.
|
|
40
|
+
*/
|
|
41
|
+
protected toResource<T extends { priority: number; status: string }>(
|
|
42
|
+
row: T,
|
|
43
|
+
): JobExecutionResource {
|
|
44
|
+
return {
|
|
45
|
+
...row,
|
|
46
|
+
priority: PRIORITY_REVERSE[row.priority] ?? "normal",
|
|
47
|
+
can: this.computeCan(row.status),
|
|
48
|
+
} as unknown as JobExecutionResource;
|
|
49
|
+
}
|
|
50
|
+
|
|
34
51
|
/**
|
|
35
52
|
* List every registered job with recent ok/error counts and lastRun.
|
|
36
53
|
* One aggregate query covers all jobs.
|
|
@@ -87,14 +104,13 @@ export class JobService {
|
|
|
87
104
|
result.push({
|
|
88
105
|
name,
|
|
89
106
|
description: opts.description,
|
|
90
|
-
type:
|
|
107
|
+
type: this.jobProvider.effectiveMode(name),
|
|
91
108
|
cron: opts.cron,
|
|
92
109
|
priority: (opts.priority ?? "normal") as JobRegistration["priority"],
|
|
93
110
|
timeout: opts.timeout ? String(opts.timeout) : undefined,
|
|
94
111
|
retry: opts.retry
|
|
95
112
|
? {
|
|
96
113
|
retries: opts.retry.retries,
|
|
97
|
-
hasBackoff: Boolean(opts.retry.backoff),
|
|
98
114
|
}
|
|
99
115
|
: undefined,
|
|
100
116
|
recent: counts,
|
|
@@ -121,10 +137,7 @@ export class JobService {
|
|
|
121
137
|
orderBy: { column: "startedAt", direction: "desc" },
|
|
122
138
|
limit: query.limit ?? 20,
|
|
123
139
|
});
|
|
124
|
-
return rows.map((row) => (
|
|
125
|
-
...row,
|
|
126
|
-
can: this.computeCan(row.status),
|
|
127
|
-
}));
|
|
140
|
+
return rows.map((row) => this.toResource(row));
|
|
128
141
|
}
|
|
129
142
|
|
|
130
143
|
/**
|
|
@@ -135,10 +148,7 @@ export class JobService {
|
|
|
135
148
|
if (!execution) {
|
|
136
149
|
throw new NotFoundError(`Execution not found: ${id}`);
|
|
137
150
|
}
|
|
138
|
-
return
|
|
139
|
-
...execution,
|
|
140
|
-
can: this.computeCan(execution.status),
|
|
141
|
-
};
|
|
151
|
+
return this.toResource(execution);
|
|
142
152
|
}
|
|
143
153
|
|
|
144
154
|
/**
|
|
@@ -73,4 +73,27 @@ export class AdminApiKeyController {
|
|
|
73
73
|
return { ok: true, id: params.id };
|
|
74
74
|
},
|
|
75
75
|
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Revoke many API keys in one request.
|
|
79
|
+
*/
|
|
80
|
+
public readonly revokeApiKeys = $action({
|
|
81
|
+
method: "POST",
|
|
82
|
+
path: `${this.url}/revoke`,
|
|
83
|
+
group: this.group,
|
|
84
|
+
use: [$secure({ permissions: ["admin:api-key:delete"] })],
|
|
85
|
+
description: "Revoke many API keys",
|
|
86
|
+
schema: {
|
|
87
|
+
body: t.object({
|
|
88
|
+
ids: t.array(t.uuid(), { minItems: 1, maxItems: 1000 }),
|
|
89
|
+
}),
|
|
90
|
+
response: t.object({
|
|
91
|
+
revoked: t.array(t.uuid()),
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
handler: async ({ body }) => {
|
|
95
|
+
const revoked = await this.apiKeyService.revokeManyByAdmin(body.ids);
|
|
96
|
+
return { revoked };
|
|
97
|
+
},
|
|
98
|
+
});
|
|
76
99
|
}
|
|
@@ -16,8 +16,21 @@ export class ApiKeyService {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Cache validated API keys for 15 minutes.
|
|
19
|
+
*
|
|
20
|
+
* Pinned to per-isolate memory:
|
|
21
|
+
* - The cache replaces a single indexed SELECT on `api_keys`. Routing it
|
|
22
|
+
* through a distributed K/V (KV/Redis) buys little — the SELECT is
|
|
23
|
+
* already cheap — and pinning to DB would actively trade one SQL read
|
|
24
|
+
* for another.
|
|
25
|
+
* - Cold-start gives a fresh DB read on every new isolate, which is
|
|
26
|
+
* *better* for revocation visibility than a distributed cache that
|
|
27
|
+
* keeps serving stale entries until its own TTL.
|
|
28
|
+
* - Avoids provisioning KV/Redis just for this one cache. Users who need
|
|
29
|
+
* cross-isolate sharing for high-throughput API auth can override
|
|
30
|
+
* globally via `alepha.with({ provide: CacheProvider, use: ... })`.
|
|
19
31
|
*/
|
|
20
32
|
protected readonly validationCache = $cache<ApiKeyEntity | null, [string]>({
|
|
33
|
+
provider: "memory",
|
|
21
34
|
name: "api:keys:validation",
|
|
22
35
|
ttl: [15, "minutes"],
|
|
23
36
|
});
|
|
@@ -180,6 +193,35 @@ export class ApiKeyService {
|
|
|
180
193
|
});
|
|
181
194
|
}
|
|
182
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Revoke many API keys in one repository call (admin only). Already-revoked
|
|
198
|
+
* keys are silently skipped. Returns the ids that were actually revoked.
|
|
199
|
+
*/
|
|
200
|
+
public async revokeManyByAdmin(ids: string[]): Promise<string[]> {
|
|
201
|
+
if (ids.length === 0) return [];
|
|
202
|
+
|
|
203
|
+
const keys = await this.repo.findMany({
|
|
204
|
+
where: { id: { inArray: ids } },
|
|
205
|
+
columns: ["id", "tokenHash", "revokedAt"],
|
|
206
|
+
});
|
|
207
|
+
const toRevoke = keys.filter((k) => !k.revokedAt);
|
|
208
|
+
if (toRevoke.length === 0) return [];
|
|
209
|
+
|
|
210
|
+
await Promise.all(
|
|
211
|
+
toRevoke.map((k) => this.validationCache.invalidate(k.tokenHash)),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
await this.repo.updateMany(
|
|
215
|
+
{ id: { inArray: toRevoke.map((k) => k.id) } },
|
|
216
|
+
{
|
|
217
|
+
revokedAt: this.dateTimeProvider.now().toISOString(),
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
this.log.info("API keys revoked by admin", { count: toRevoke.length });
|
|
222
|
+
return toRevoke.map((k) => k.id);
|
|
223
|
+
}
|
|
224
|
+
|
|
183
225
|
// -------------------------------------------------------------------------
|
|
184
226
|
// User Operations
|
|
185
227
|
// -------------------------------------------------------------------------
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { AlephaApiJobs, JobProvider } from "alepha/api/jobs";
|
|
3
|
+
import { AlephaEmail, MemoryEmailProvider } from "alepha/email";
|
|
4
|
+
import { AlephaOrmPostgres } from "alepha/orm/postgres";
|
|
5
|
+
import { AlephaSms } from "alepha/sms";
|
|
6
|
+
import { describe, it } from "vitest";
|
|
7
|
+
import {
|
|
8
|
+
$notification,
|
|
9
|
+
AlephaApiNotifications,
|
|
10
|
+
NotificationJobs,
|
|
11
|
+
} from "../index.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The notifications module historically forced `AlephaApiJobsQueue`. After
|
|
15
|
+
* the direct-mode change it must boot without any queue infrastructure and
|
|
16
|
+
* deliver via direct mode (push → process in-process → row removed on success).
|
|
17
|
+
*/
|
|
18
|
+
describe("AlephaApiNotifications — runs without AlephaApiJobsQueue", () => {
|
|
19
|
+
it("module starts in direct mode and delivers an email notification", async ({
|
|
20
|
+
expect,
|
|
21
|
+
}) => {
|
|
22
|
+
const alepha = Alepha.create()
|
|
23
|
+
.with(AlephaOrmPostgres)
|
|
24
|
+
.with(AlephaEmail)
|
|
25
|
+
.with(AlephaSms)
|
|
26
|
+
.with(AlephaApiJobs)
|
|
27
|
+
.with(AlephaApiNotifications);
|
|
28
|
+
|
|
29
|
+
class Templates {
|
|
30
|
+
readonly welcome = $notification({
|
|
31
|
+
name: "welcome-email",
|
|
32
|
+
schema: t.object({ name: t.text() }),
|
|
33
|
+
email: {
|
|
34
|
+
subject: "Welcome",
|
|
35
|
+
body: (vars) => `Hello ${vars.name}`,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const templates = alepha.inject(Templates);
|
|
41
|
+
await alepha.start();
|
|
42
|
+
|
|
43
|
+
// Confirm the underlying $job runs in direct mode (no queue loaded).
|
|
44
|
+
const jobs = alepha.inject(JobProvider);
|
|
45
|
+
const sendName = alepha.inject(NotificationJobs).sendNotification.name;
|
|
46
|
+
expect(jobs.effectiveMode(sendName)).toBe("direct");
|
|
47
|
+
|
|
48
|
+
await templates.welcome.push({
|
|
49
|
+
contact: "alice@example.com",
|
|
50
|
+
variables: { name: "Alice" },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const mail = alepha.inject(MemoryEmailProvider);
|
|
54
|
+
const deadline = Date.now() + 1500;
|
|
55
|
+
while (mail.records.length === 0 && Date.now() < deadline) {
|
|
56
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
57
|
+
}
|
|
58
|
+
expect(mail.records).toHaveLength(1);
|
|
59
|
+
expect(mail.records[0].to).toBe("alice@example.com");
|
|
60
|
+
expect(mail.records[0].subject).toBe("Welcome");
|
|
61
|
+
expect(mail.records[0].body).toBe("Hello Alice");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -2,7 +2,7 @@ import { $inject, t } from "alepha";
|
|
|
2
2
|
import { jobExecutionEntity } from "alepha/api/jobs";
|
|
3
3
|
import { $repository } from "alepha/orm";
|
|
4
4
|
import { $secure } from "alepha/security";
|
|
5
|
-
import { $action, NotFoundError } from "alepha/server";
|
|
5
|
+
import { $action, NotFoundError, okSchema } from "alepha/server";
|
|
6
6
|
import { NotificationJobs } from "../jobs/NotificationJobs.ts";
|
|
7
7
|
import { notificationDetailResourceSchema } from "../schemas/notificationDetailResourceSchema.ts";
|
|
8
8
|
import { notificationQuerySchema } from "../schemas/notificationQuerySchema.ts";
|
|
@@ -61,6 +61,53 @@ export class AdminNotificationController {
|
|
|
61
61
|
},
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
+
public readonly deleteNotification = $action({
|
|
65
|
+
method: "DELETE",
|
|
66
|
+
path: `${this.url}/:id`,
|
|
67
|
+
group: this.group,
|
|
68
|
+
use: [$secure({ permissions: ["admin:notification:delete"] })],
|
|
69
|
+
description: "Delete a notification record",
|
|
70
|
+
schema: {
|
|
71
|
+
params: t.object({
|
|
72
|
+
id: t.uuid(),
|
|
73
|
+
}),
|
|
74
|
+
response: okSchema,
|
|
75
|
+
},
|
|
76
|
+
handler: async ({ params }) => {
|
|
77
|
+
const exec = await this.executions.findById(params.id);
|
|
78
|
+
if (!exec || exec.jobName !== this.jobName) {
|
|
79
|
+
throw new NotFoundError(`Notification not found: ${params.id}`);
|
|
80
|
+
}
|
|
81
|
+
await this.executions.deleteById(params.id);
|
|
82
|
+
return { ok: true, id: params.id };
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
public readonly deleteNotifications = $action({
|
|
87
|
+
method: "POST",
|
|
88
|
+
path: `${this.url}/delete`,
|
|
89
|
+
group: this.group,
|
|
90
|
+
use: [$secure({ permissions: ["admin:notification:delete"] })],
|
|
91
|
+
description: "Delete many notification records in one call",
|
|
92
|
+
schema: {
|
|
93
|
+
body: t.object({
|
|
94
|
+
ids: t.array(t.uuid(), { minItems: 1, maxItems: 1000 }),
|
|
95
|
+
}),
|
|
96
|
+
response: t.object({
|
|
97
|
+
deleted: t.array(t.uuid()),
|
|
98
|
+
}),
|
|
99
|
+
},
|
|
100
|
+
handler: async ({ body }) => {
|
|
101
|
+
// Constrain to this job's executions so an admin can't delete arbitrary
|
|
102
|
+
// job rows through this endpoint.
|
|
103
|
+
const deleted = await this.executions.deleteMany({
|
|
104
|
+
id: { inArray: body.ids },
|
|
105
|
+
jobName: { eq: this.jobName },
|
|
106
|
+
});
|
|
107
|
+
return { deleted: deleted.map(String) };
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
64
111
|
protected toResource(exec: Record<string, unknown>) {
|
|
65
112
|
const payload = (exec.payload ?? {}) as Record<string, unknown>;
|
|
66
113
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $module } from "alepha";
|
|
2
|
-
import {
|
|
2
|
+
import { AlephaApiJobs } from "alepha/api/jobs";
|
|
3
3
|
import { AlephaApiParameters } from "alepha/api/parameters";
|
|
4
4
|
import { AdminNotificationController } from "./controllers/AdminNotificationController.ts";
|
|
5
5
|
import { NotificationJobs } from "./jobs/NotificationJobs.ts";
|
|
@@ -26,15 +26,25 @@ export * from "./services/NotificationSenderService.ts";
|
|
|
26
26
|
*
|
|
27
27
|
* **Features:**
|
|
28
28
|
* - Notification definitions (email/SMS templates)
|
|
29
|
-
* -
|
|
29
|
+
* - Delivery via `$job` with retry and audit trail (`record: "all"` + no ring buffer trim)
|
|
30
30
|
* - Runtime-editable retention window via `$parameter` — purge cron respects it live
|
|
31
31
|
* - Admin API for inspecting sent notifications
|
|
32
32
|
*
|
|
33
|
+
* **Delivery mode** is decided at runtime by the `$job` system:
|
|
34
|
+
* - If your app loads `AlephaApiJobsQueue` (and thus `AlephaQueue`), notifications
|
|
35
|
+
* go through the queue (best for high-volume systems).
|
|
36
|
+
* - Otherwise, notifications run in **direct** mode: pushed to the outbox table
|
|
37
|
+
* and processed in the same process right after the HTTP response is returned.
|
|
38
|
+
* The reconciliation sweep is the safety net for crashes / retries.
|
|
39
|
+
*
|
|
40
|
+
* Direct mode is the recommended default for small / cheap deployments
|
|
41
|
+
* (Cloudflare Workers, single-instance Node) — no queue infrastructure required.
|
|
42
|
+
*
|
|
33
43
|
* @module alepha.api.notifications
|
|
34
44
|
*/
|
|
35
45
|
export const AlephaApiNotifications = $module({
|
|
36
46
|
name: "alepha.api.notifications",
|
|
37
|
-
imports: [
|
|
47
|
+
imports: [AlephaApiJobs, AlephaApiParameters],
|
|
38
48
|
primitives: [$notification],
|
|
39
49
|
services: [
|
|
40
50
|
NotificationSenderService,
|
|
@@ -55,12 +55,6 @@ export class NotificationJobs {
|
|
|
55
55
|
schema: notificationPayloadSchema,
|
|
56
56
|
retry: {
|
|
57
57
|
retries: 3,
|
|
58
|
-
backoff: {
|
|
59
|
-
initial: [5, "seconds"],
|
|
60
|
-
factor: 4,
|
|
61
|
-
max: [10, "minutes"],
|
|
62
|
-
jitter: true,
|
|
63
|
-
},
|
|
64
58
|
},
|
|
65
59
|
timeout: [30, "seconds"],
|
|
66
60
|
record: "all",
|
|
@@ -267,4 +267,30 @@ export class AdminParameterController {
|
|
|
267
267
|
return { ok: true };
|
|
268
268
|
},
|
|
269
269
|
});
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Delete many parameters (all versions of each) in one request.
|
|
273
|
+
*/
|
|
274
|
+
deleteParameters = $action({
|
|
275
|
+
group: this.group,
|
|
276
|
+
use: [$secure({ permissions: ["admin:parameter:delete"] })],
|
|
277
|
+
description: "Delete all versions of many parameters by name.",
|
|
278
|
+
path: "/parameters/delete",
|
|
279
|
+
method: "POST",
|
|
280
|
+
schema: {
|
|
281
|
+
body: t.object({
|
|
282
|
+
names: t.array(t.string({ minLength: 1 }), {
|
|
283
|
+
minItems: 1,
|
|
284
|
+
maxItems: 1000,
|
|
285
|
+
}),
|
|
286
|
+
}),
|
|
287
|
+
response: t.object({
|
|
288
|
+
deleted: t.array(t.string()),
|
|
289
|
+
}),
|
|
290
|
+
},
|
|
291
|
+
handler: async ({ body }) => {
|
|
292
|
+
const deleted = await this.provider.deleteMany(body.names);
|
|
293
|
+
return { deleted };
|
|
294
|
+
},
|
|
295
|
+
});
|
|
270
296
|
}
|
|
@@ -486,6 +486,24 @@ export class ParameterProvider {
|
|
|
486
486
|
this.log.info("Parameter deleted", { name });
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
+
/**
|
|
490
|
+
* Delete all versions of many parameters by name in one repository call.
|
|
491
|
+
*/
|
|
492
|
+
public async deleteMany(names: string[]): Promise<string[]> {
|
|
493
|
+
if (names.length === 0) return [];
|
|
494
|
+
await this.repo.deleteMany({ name: { inArray: names } });
|
|
495
|
+
for (const name of names) {
|
|
496
|
+
this.cachedCurrent.delete(name);
|
|
497
|
+
this.cachedNext.delete(name);
|
|
498
|
+
this.loaded.delete(name);
|
|
499
|
+
this.loadPromises.delete(name);
|
|
500
|
+
this.loadGeneration.delete(name);
|
|
501
|
+
this.migrationChecked.delete(name);
|
|
502
|
+
}
|
|
503
|
+
this.log.info("Parameters deleted", { count: names.length });
|
|
504
|
+
return names;
|
|
505
|
+
}
|
|
506
|
+
|
|
489
507
|
/**
|
|
490
508
|
* Get a specific version of a parameter.
|
|
491
509
|
*/
|