alepha 0.13.1 → 0.13.3
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/README.md +1 -1
- package/dist/api-files/index.browser.js +80 -0
- package/dist/api-files/index.browser.js.map +1 -0
- package/dist/api-files/index.d.ts +28 -91
- package/dist/api-files/index.js +10 -755
- package/dist/api-files/index.js.map +1 -1
- package/dist/api-jobs/index.browser.js +56 -0
- package/dist/api-jobs/index.browser.js.map +1 -0
- package/dist/api-jobs/index.d.ts +46 -46
- package/dist/api-jobs/index.js +13 -13
- package/dist/api-jobs/index.js.map +1 -1
- package/dist/api-notifications/index.browser.js +382 -0
- package/dist/api-notifications/index.browser.js.map +1 -0
- package/dist/api-notifications/index.d.ts +231 -193
- package/dist/api-notifications/index.js +108 -78
- package/dist/api-notifications/index.js.map +1 -1
- package/dist/api-parameters/index.browser.js +29 -0
- package/dist/api-parameters/index.browser.js.map +1 -0
- package/dist/api-parameters/index.d.ts +21 -22
- package/dist/api-parameters/index.js +22 -22
- package/dist/api-parameters/index.js.map +1 -1
- package/dist/api-users/index.d.ts +237 -2001
- package/dist/api-users/index.js +969 -4795
- package/dist/api-users/index.js.map +1 -1
- package/dist/api-verifications/index.browser.js +52 -0
- package/dist/api-verifications/index.browser.js.map +1 -0
- package/dist/api-verifications/index.d.ts +119 -97
- package/dist/api-verifications/index.js +1 -1
- package/dist/api-verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +13 -13
- package/dist/batch/index.js +8 -13
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +14 -14
- package/dist/bucket/index.js +19 -17
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/index.d.ts +11 -11
- package/dist/cache/index.js +9 -9
- package/dist/cache/index.js.map +1 -1
- package/dist/cli/{dist-Dl9Vl7Ur.js → dist-lGnqsKpu.js} +11 -15
- package/dist/cli/dist-lGnqsKpu.js.map +1 -0
- package/dist/cli/index.d.ts +32 -49
- package/dist/cli/index.js +90 -71
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +20 -19
- package/dist/command/index.js +34 -25
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +218 -218
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +232 -232
- package/dist/core/index.js +218 -218
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +2113 -0
- package/dist/core/index.native.js.map +1 -0
- package/dist/datetime/index.d.ts +9 -9
- package/dist/datetime/index.js +7 -7
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +16 -16
- package/dist/email/index.js +14 -9
- package/dist/email/index.js.map +1 -1
- package/dist/file/index.js +1 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/index.d.ts +9 -9
- package/dist/lock/index.js +8 -8
- package/dist/lock/index.js.map +1 -1
- package/dist/lock-redis/index.js +3 -66
- package/dist/lock-redis/index.js.map +1 -1
- package/dist/logger/index.d.ts +5 -5
- package/dist/logger/index.js +8 -8
- package/dist/logger/index.js.map +1 -1
- package/dist/orm/index.browser.js +114 -114
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.d.ts +218 -218
- package/dist/orm/index.js +49 -49
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/index.d.ts +29 -29
- package/dist/queue/index.js +20 -20
- package/dist/queue/index.js.map +1 -1
- package/dist/queue-redis/index.d.ts +2 -2
- package/dist/redis/index.d.ts +4 -4
- package/dist/retry/index.d.ts +19 -19
- package/dist/retry/index.js +7 -7
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +16 -16
- package/dist/scheduler/index.js +9 -9
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +53 -53
- package/dist/security/index.js +35 -35
- package/dist/security/index.js.map +1 -1
- package/dist/server/index.browser.js +1 -1
- package/dist/server/index.browser.js.map +1 -1
- package/dist/server/index.d.ts +92 -92
- package/dist/server/index.js +16 -16
- package/dist/server/index.js.map +1 -1
- package/dist/server-auth/index.browser.js +4 -982
- package/dist/server-auth/index.browser.js.map +1 -1
- package/dist/server-auth/index.d.ts +204 -785
- package/dist/server-auth/index.js +47 -1239
- package/dist/server-auth/index.js.map +1 -1
- package/dist/server-cache/index.d.ts +10 -10
- package/dist/server-cache/index.js +2 -2
- package/dist/server-cache/index.js.map +1 -1
- package/dist/server-compress/index.d.ts +4 -4
- package/dist/server-compress/index.js +1 -1
- package/dist/server-compress/index.js.map +1 -1
- package/dist/server-cookies/index.browser.js +8 -8
- package/dist/server-cookies/index.browser.js.map +1 -1
- package/dist/server-cookies/index.d.ts +17 -17
- package/dist/server-cookies/index.js +11 -11
- package/dist/server-cookies/index.js.map +1 -1
- package/dist/server-cors/index.d.ts +17 -17
- package/dist/server-cors/index.js +9 -9
- package/dist/server-cors/index.js.map +1 -1
- package/dist/server-health/index.d.ts +19 -19
- package/dist/server-helmet/index.d.ts +1 -1
- package/dist/server-links/index.browser.js +12 -12
- package/dist/server-links/index.browser.js.map +1 -1
- package/dist/server-links/index.d.ts +59 -251
- package/dist/server-links/index.js +23 -502
- package/dist/server-links/index.js.map +1 -1
- package/dist/server-metrics/index.d.ts +4 -4
- package/dist/server-multipart/index.d.ts +2 -2
- package/dist/server-proxy/index.d.ts +12 -12
- package/dist/server-proxy/index.js +10 -10
- package/dist/server-proxy/index.js.map +1 -1
- package/dist/server-rate-limit/index.d.ts +22 -22
- package/dist/server-rate-limit/index.js +12 -12
- package/dist/server-rate-limit/index.js.map +1 -1
- package/dist/server-security/index.d.ts +22 -22
- package/dist/server-security/index.js +15 -15
- package/dist/server-security/index.js.map +1 -1
- package/dist/server-static/index.d.ts +14 -14
- package/dist/server-static/index.js +26 -10
- package/dist/server-static/index.js.map +1 -1
- package/dist/server-swagger/index.d.ts +25 -184
- package/dist/server-swagger/index.js +21 -724
- package/dist/server-swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +14 -14
- package/dist/sms/index.js +9 -9
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +11 -11
- package/dist/thread/index.js +17 -17
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/index.d.ts +26 -26
- package/dist/topic/index.js +16 -16
- package/dist/topic/index.js.map +1 -1
- package/dist/topic-redis/index.d.ts +1 -1
- package/dist/vite/index.d.ts +3 -3
- package/dist/vite/index.js +8 -8
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +11 -11
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +58 -58
- package/dist/websocket/index.js +13 -13
- package/dist/websocket/index.js.map +1 -1
- package/package.json +128 -57
- package/src/api-files/index.browser.ts +17 -0
- package/src/api-files/services/FileService.ts +5 -7
- package/src/api-jobs/index.browser.ts +15 -0
- package/src/api-jobs/index.ts +1 -1
- package/src/api-jobs/{descriptors → primitives}/$job.ts +8 -8
- package/src/api-jobs/providers/JobProvider.ts +9 -9
- package/src/api-jobs/services/JobService.ts +5 -5
- package/src/api-notifications/controllers/NotificationController.ts +26 -1
- package/src/api-notifications/index.browser.ts +17 -0
- package/src/api-notifications/index.ts +6 -15
- package/src/api-notifications/{descriptors → primitives}/$notification.ts +10 -10
- package/src/api-notifications/schemas/notificationQuerySchema.ts +13 -0
- package/src/api-notifications/services/NotificationSenderService.ts +3 -3
- package/src/api-notifications/services/NotificationService.ts +45 -2
- package/src/api-parameters/index.browser.ts +12 -0
- package/src/api-parameters/index.ts +1 -1
- package/src/api-parameters/{descriptors → primitives}/$config.ts +7 -12
- package/src/api-users/atoms/realmAuthSettingsAtom.ts +3 -1
- package/src/api-users/controllers/UserController.ts +21 -1
- package/src/api-users/index.ts +1 -1
- package/src/api-users/{descriptors → primitives}/$userRealm.ts +40 -17
- package/src/api-users/providers/UserRealmProvider.ts +2 -1
- package/src/api-users/services/SessionService.ts +2 -0
- package/src/api-users/services/UserService.ts +56 -16
- package/src/api-verifications/index.browser.ts +15 -0
- package/src/api-verifications/index.ts +1 -0
- package/src/batch/index.ts +3 -3
- package/src/batch/{descriptors → primitives}/$batch.ts +13 -16
- package/src/batch/providers/BatchProvider.ts +0 -7
- package/src/bucket/index.ts +15 -13
- package/src/bucket/{descriptors → primitives}/$bucket.ts +8 -8
- package/src/bucket/providers/LocalFileStorageProvider.ts +3 -3
- package/src/cache/index.ts +4 -4
- package/src/cache/{descriptors → primitives}/$cache.ts +15 -15
- package/src/cli/apps/AlephaCli.ts +27 -1
- package/src/cli/apps/AlephaPackageBuilderCli.ts +27 -2
- package/src/cli/commands/CoreCommands.ts +6 -2
- package/src/cli/commands/DrizzleCommands.ts +6 -6
- package/src/cli/commands/VerifyCommands.ts +1 -1
- package/src/cli/commands/ViteCommands.ts +8 -2
- package/src/cli/services/ProjectUtils.ts +74 -78
- package/src/command/helpers/Asker.ts +10 -0
- package/src/command/index.ts +5 -5
- package/src/command/{descriptors → primitives}/$command.ts +9 -12
- package/src/command/providers/CliProvider.ts +10 -10
- package/src/core/Alepha.ts +30 -33
- package/src/core/constants/KIND.ts +1 -1
- package/src/core/constants/OPTIONS.ts +1 -1
- package/src/core/helpers/{descriptor.ts → primitive.ts} +18 -18
- package/src/core/helpers/ref.ts +1 -1
- package/src/core/index.shared.ts +8 -8
- package/src/core/{descriptors → primitives}/$context.ts +5 -5
- package/src/core/{descriptors → primitives}/$hook.ts +4 -4
- package/src/core/{descriptors → primitives}/$inject.ts +2 -2
- package/src/core/{descriptors → primitives}/$module.ts +9 -9
- package/src/core/{descriptors → primitives}/$use.ts +2 -2
- package/src/core/providers/CodecManager.ts +1 -1
- package/src/core/providers/JsonSchemaCodec.ts +1 -1
- package/src/core/providers/StateManager.ts +2 -2
- package/src/datetime/index.ts +3 -3
- package/src/datetime/{descriptors → primitives}/$interval.ts +6 -6
- package/src/email/index.ts +17 -9
- package/src/email/{descriptors → primitives}/$email.ts +8 -8
- package/src/file/index.ts +1 -1
- package/src/lock/index.ts +3 -3
- package/src/lock/{descriptors → primitives}/$lock.ts +10 -10
- package/src/logger/index.ts +8 -8
- package/src/logger/{descriptors → primitives}/$logger.ts +2 -2
- package/src/logger/services/Logger.ts +1 -1
- package/src/orm/constants/PG_SYMBOLS.ts +2 -2
- package/src/orm/index.browser.ts +2 -2
- package/src/orm/index.ts +8 -8
- package/src/orm/{descriptors → primitives}/$entity.ts +11 -11
- package/src/orm/{descriptors → primitives}/$repository.ts +2 -2
- package/src/orm/{descriptors → primitives}/$sequence.ts +8 -8
- package/src/orm/{descriptors → primitives}/$transaction.ts +4 -4
- package/src/orm/providers/PostgresTypeProvider.ts +3 -3
- package/src/orm/providers/RepositoryProvider.ts +4 -4
- package/src/orm/providers/drivers/DatabaseProvider.ts +7 -7
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -3
- package/src/orm/services/ModelBuilder.ts +9 -9
- package/src/orm/services/PgRelationManager.ts +2 -2
- package/src/orm/services/PostgresModelBuilder.ts +5 -5
- package/src/orm/services/Repository.ts +7 -7
- package/src/orm/services/SqliteModelBuilder.ts +5 -5
- package/src/queue/index.ts +7 -7
- package/src/queue/{descriptors → primitives}/$consumer.ts +15 -15
- package/src/queue/{descriptors → primitives}/$queue.ts +12 -12
- package/src/queue/providers/WorkerProvider.ts +7 -7
- package/src/retry/index.ts +3 -3
- package/src/retry/{descriptors → primitives}/$retry.ts +14 -14
- package/src/scheduler/index.ts +3 -3
- package/src/scheduler/{descriptors → primitives}/$scheduler.ts +9 -9
- package/src/scheduler/providers/CronProvider.ts +1 -1
- package/src/security/index.ts +9 -9
- package/src/security/{descriptors → primitives}/$permission.ts +7 -7
- package/src/security/{descriptors → primitives}/$realm.ts +6 -12
- package/src/security/{descriptors → primitives}/$role.ts +12 -12
- package/src/security/{descriptors → primitives}/$serviceAccount.ts +8 -8
- package/src/server/index.browser.ts +1 -1
- package/src/server/index.ts +14 -14
- package/src/server/{descriptors → primitives}/$action.ts +13 -13
- package/src/server/{descriptors → primitives}/$route.ts +9 -9
- package/src/server/providers/NodeHttpServerProvider.ts +1 -1
- package/src/server/services/HttpClient.ts +1 -1
- package/src/server-auth/index.browser.ts +1 -1
- package/src/server-auth/index.ts +6 -6
- package/src/server-auth/{descriptors → primitives}/$auth.ts +10 -10
- package/src/server-auth/{descriptors → primitives}/$authCredentials.ts +4 -4
- package/src/server-auth/{descriptors → primitives}/$authGithub.ts +4 -4
- package/src/server-auth/{descriptors → primitives}/$authGoogle.ts +4 -4
- package/src/server-auth/providers/ServerAuthProvider.ts +4 -4
- package/src/server-cache/providers/ServerCacheProvider.ts +7 -7
- package/src/server-compress/providers/ServerCompressProvider.ts +3 -3
- package/src/server-cookies/index.browser.ts +2 -2
- package/src/server-cookies/index.ts +5 -5
- package/src/server-cookies/{descriptors → primitives}/$cookie.browser.ts +12 -12
- package/src/server-cookies/{descriptors → primitives}/$cookie.ts +13 -13
- package/src/server-cookies/providers/ServerCookiesProvider.ts +6 -5
- package/src/server-cookies/services/CookieParser.ts +1 -1
- package/src/server-cors/index.ts +3 -3
- package/src/server-cors/{descriptors → primitives}/$cors.ts +11 -13
- package/src/server-cors/providers/ServerCorsProvider.ts +5 -5
- package/src/server-links/index.browser.ts +5 -5
- package/src/server-links/index.ts +9 -9
- package/src/server-links/{descriptors → primitives}/$remote.ts +11 -11
- package/src/server-links/providers/LinkProvider.ts +7 -7
- package/src/server-links/providers/{RemoteDescriptorProvider.ts → RemotePrimitiveProvider.ts} +6 -6
- package/src/server-links/providers/ServerLinksProvider.ts +3 -3
- package/src/server-proxy/index.ts +3 -3
- package/src/server-proxy/{descriptors → primitives}/$proxy.ts +8 -8
- package/src/server-proxy/providers/ServerProxyProvider.ts +4 -4
- package/src/server-rate-limit/index.ts +6 -6
- package/src/server-rate-limit/{descriptors → primitives}/$rateLimit.ts +13 -13
- package/src/server-rate-limit/providers/ServerRateLimitProvider.ts +5 -5
- package/src/server-security/index.ts +3 -3
- package/src/server-security/{descriptors → primitives}/$basicAuth.ts +13 -13
- package/src/server-security/providers/ServerBasicAuthProvider.ts +5 -5
- package/src/server-security/providers/ServerSecurityProvider.ts +4 -4
- package/src/server-static/index.ts +3 -3
- package/src/server-static/{descriptors → primitives}/$serve.ts +8 -10
- package/src/server-static/providers/ServerStaticProvider.ts +24 -9
- package/src/server-swagger/index.ts +5 -5
- package/src/server-swagger/{descriptors → primitives}/$swagger.ts +9 -9
- package/src/server-swagger/providers/ServerSwaggerProvider.ts +11 -10
- package/src/sms/index.ts +4 -4
- package/src/sms/{descriptors → primitives}/$sms.ts +8 -8
- package/src/thread/index.ts +3 -3
- package/src/thread/{descriptors → primitives}/$thread.ts +13 -13
- package/src/thread/providers/ThreadProvider.ts +7 -9
- package/src/topic/index.ts +5 -5
- package/src/topic/{descriptors → primitives}/$subscriber.ts +14 -14
- package/src/topic/{descriptors → primitives}/$topic.ts +10 -10
- package/src/topic/providers/TopicProvider.ts +4 -4
- package/src/vite/tasks/copyAssets.ts +1 -1
- package/src/vite/tasks/generateSitemap.ts +3 -3
- package/src/vite/tasks/prerenderPages.ts +2 -2
- package/src/vite/tasks/runAlepha.ts +2 -2
- package/src/websocket/index.browser.ts +3 -3
- package/src/websocket/index.shared.ts +2 -2
- package/src/websocket/index.ts +4 -4
- package/src/websocket/interfaces/WebSocketInterfaces.ts +3 -3
- package/src/websocket/{descriptors → primitives}/$channel.ts +10 -10
- package/src/websocket/{descriptors → primitives}/$websocket.ts +8 -8
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +7 -7
- package/src/websocket/providers/WebSocketServerProvider.ts +3 -3
- package/src/websocket/services/WebSocketClient.ts +5 -5
- package/dist/cli/dist-Dl9Vl7Ur.js.map +0 -1
- package/src/api-notifications/providers/MemorySmsProvider.ts +0 -20
- package/src/api-notifications/providers/SmsProvider.ts +0 -8
- /package/src/core/{descriptors → primitives}/$atom.ts +0 -0
- /package/src/core/{descriptors → primitives}/$env.ts +0 -0
- /package/src/server-auth/{descriptors → primitives}/$authApple.ts +0 -0
- /package/src/server-links/{descriptors → primitives}/$client.ts +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { $module, t } from "alepha";
|
|
2
|
+
import { $entity, pg } from "alepha/orm";
|
|
3
|
+
|
|
4
|
+
//#region src/api-verifications/schemas/verificationTypeEnumSchema.ts
|
|
5
|
+
const verificationTypeEnumSchema = t.enum(["code", "link"]);
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/api-verifications/entities/verifications.ts
|
|
9
|
+
const verifications = $entity({
|
|
10
|
+
name: "verification",
|
|
11
|
+
schema: t.object({
|
|
12
|
+
id: pg.primaryKey(t.bigint()),
|
|
13
|
+
createdAt: pg.createdAt(),
|
|
14
|
+
updatedAt: pg.updatedAt(),
|
|
15
|
+
version: pg.version(),
|
|
16
|
+
type: verificationTypeEnumSchema,
|
|
17
|
+
target: t.text({ description: "Can be a phone (E.164 format) or email address" }),
|
|
18
|
+
code: t.text({ description: "Hashed verification token (n-digit code or UUID)" }),
|
|
19
|
+
verifiedAt: t.optional(t.datetime({ description: "When it was successfully verified" })),
|
|
20
|
+
attempts: pg.default(t.integer({ description: "Number of failed attempts (to prevent brute-force)" }), 0)
|
|
21
|
+
}),
|
|
22
|
+
indexes: ["createdAt", { columns: ["target", "code"] }]
|
|
23
|
+
});
|
|
24
|
+
const verificationEntitySchema = verifications.schema;
|
|
25
|
+
const verificationEntityInsertSchema = verifications.insertSchema;
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/api-verifications/schemas/requestVerificationCodeResponseSchema.ts
|
|
29
|
+
const requestVerificationCodeResponseSchema = t.object({
|
|
30
|
+
token: t.string({ description: "The verification token (6-digit code for phone, UUID for email). The caller should send this to the user via their preferred notification method." }),
|
|
31
|
+
codeExpiration: t.integer({ description: "Time in seconds before your verification token expires." }),
|
|
32
|
+
verificationCooldown: t.integer({ description: "Cooldown period in seconds before you can request another verification." }),
|
|
33
|
+
maxVerificationAttempts: t.integer({ description: "Maximum number of verification attempts allowed before the token is locked." })
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/api-verifications/schemas/validateVerificationCodeResponseSchema.ts
|
|
38
|
+
const validateVerificationCodeResponseSchema = t.object({
|
|
39
|
+
ok: t.boolean({ description: "Indicates whether the verification was successful." }),
|
|
40
|
+
alreadyVerified: t.optional(t.boolean({ description: "Indicates whether the target was already verified." }))
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/api-verifications/index.browser.ts
|
|
45
|
+
const AlephaApiVerification = $module({
|
|
46
|
+
name: "alepha.api.verifications",
|
|
47
|
+
services: []
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { AlephaApiVerification, requestVerificationCodeResponseSchema, validateVerificationCodeResponseSchema, verificationEntityInsertSchema, verificationEntitySchema, verificationTypeEnumSchema, verifications };
|
|
52
|
+
//# sourceMappingURL=index.browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.browser.js","names":[],"sources":["../../src/api-verifications/schemas/verificationTypeEnumSchema.ts","../../src/api-verifications/entities/verifications.ts","../../src/api-verifications/schemas/requestVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/validateVerificationCodeResponseSchema.ts","../../src/api-verifications/index.browser.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationTypeEnumSchema = t.enum([\"code\", \"link\"]);\nexport type VerificationTypeEnum = Static<typeof verificationTypeEnumSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport const verifications = $entity({\n name: \"verification\",\n schema: t.object({\n id: pg.primaryKey(t.bigint()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n version: pg.version(),\n\n type: verificationTypeEnumSchema,\n\n target: t.text({\n description: \"Can be a phone (E.164 format) or email address\",\n }),\n\n code: t.text({\n description: \"Hashed verification token (n-digit code or UUID)\",\n }),\n\n verifiedAt: t.optional(\n t.datetime({\n description: \"When it was successfully verified\",\n }),\n ),\n\n attempts: pg.default(\n t.integer({\n description: \"Number of failed attempts (to prevent brute-force)\",\n }),\n 0,\n ),\n }),\n indexes: [\n \"createdAt\",\n {\n columns: [\"target\", \"code\"],\n },\n ],\n});\n\nexport const verificationEntitySchema = verifications.schema;\nexport const verificationEntityInsertSchema = verifications.insertSchema;\nexport type VerificationEntity = Static<typeof verifications.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const requestVerificationCodeResponseSchema = t.object({\n token: t.string({\n description:\n \"The verification token (6-digit code for phone, UUID for email). The caller should send this to the user via their preferred notification method.\",\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before your verification token expires.\",\n }),\n verificationCooldown: t.integer({\n description:\n \"Cooldown period in seconds before you can request another verification.\",\n }),\n maxVerificationAttempts: t.integer({\n description:\n \"Maximum number of verification attempts allowed before the token is locked.\",\n }),\n});\n\nexport type RequestVerificationResponse = Static<\n typeof requestVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const validateVerificationCodeResponseSchema = t.object({\n ok: t.boolean({\n description: \"Indicates whether the verification was successful.\",\n }),\n alreadyVerified: t.optional(\n t.boolean({\n description: \"Indicates whether the target was already verified.\",\n }),\n ),\n});\n\nexport type ValidateVerificationCodeResponse = Static<\n typeof validateVerificationCodeResponseSchema\n>;\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./entities/verifications.ts\";\nexport * from \"./schemas/requestVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/validateVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/verificationTypeEnumSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaApiVerification = $module({\n name: \"alepha.api.verifications\",\n services: [],\n});\n"],"mappings":";;;;AAGA,MAAa,6BAA6B,EAAE,KAAK,CAAC,QAAQ,OAAO,CAAC;;;;ACElE,MAAa,gBAAgB,QAAQ;CACnC,MAAM;CACN,QAAQ,EAAE,OAAO;EACf,IAAI,GAAG,WAAW,EAAE,QAAQ,CAAC;EAE7B,WAAW,GAAG,WAAW;EAEzB,WAAW,GAAG,WAAW;EAEzB,SAAS,GAAG,SAAS;EAErB,MAAM;EAEN,QAAQ,EAAE,KAAK,EACb,aAAa,kDACd,CAAC;EAEF,MAAM,EAAE,KAAK,EACX,aAAa,oDACd,CAAC;EAEF,YAAY,EAAE,SACZ,EAAE,SAAS,EACT,aAAa,qCACd,CAAC,CACH;EAED,UAAU,GAAG,QACX,EAAE,QAAQ,EACR,aAAa,sDACd,CAAC,EACF,EACD;EACF,CAAC;CACF,SAAS,CACP,aACA,EACE,SAAS,CAAC,UAAU,OAAO,EAC5B,CACF;CACF,CAAC;AAEF,MAAa,2BAA2B,cAAc;AACtD,MAAa,iCAAiC,cAAc;;;;AC7C5D,MAAa,wCAAwC,EAAE,OAAO;CAC5D,OAAO,EAAE,OAAO,EACd,aACE,qJACH,CAAC;CACF,gBAAgB,EAAE,QAAQ,EACxB,aAAa,2DACd,CAAC;CACF,sBAAsB,EAAE,QAAQ,EAC9B,aACE,2EACH,CAAC;CACF,yBAAyB,EAAE,QAAQ,EACjC,aACE,+EACH,CAAC;CACH,CAAC;;;;AChBF,MAAa,yCAAyC,EAAE,OAAO;CAC7D,IAAI,EAAE,QAAQ,EACZ,aAAa,sDACd,CAAC;CACF,iBAAiB,EAAE,SACjB,EAAE,QAAQ,EACR,aAAa,sDACd,CAAC,CACH;CACF,CAAC;;;;ACDF,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,UAAU,EAAE;CACb,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as alepha52 from "alepha";
|
|
2
2
|
import { Static } from "alepha";
|
|
3
3
|
import * as alepha_server0 from "alepha/server";
|
|
4
4
|
import { DateTimeProvider } from "alepha/datetime";
|
|
@@ -6,35 +6,57 @@ import * as alepha_logger0 from "alepha/logger";
|
|
|
6
6
|
import * as alepha_orm56 from "alepha/orm";
|
|
7
7
|
|
|
8
8
|
//#region src/api-verifications/entities/verifications.d.ts
|
|
9
|
-
declare const verifications: alepha_orm56.
|
|
10
|
-
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
11
|
-
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
12
|
-
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
13
|
-
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
14
|
-
type:
|
|
15
|
-
target:
|
|
16
|
-
code:
|
|
17
|
-
verifiedAt:
|
|
18
|
-
attempts: alepha_orm56.PgAttr<
|
|
9
|
+
declare const verifications: alepha_orm56.EntityPrimitive<alepha52.TObject<{
|
|
10
|
+
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_PRIMARY_KEY>, typeof alepha_orm56.PG_DEFAULT>;
|
|
11
|
+
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_CREATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
12
|
+
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_UPDATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
13
|
+
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_VERSION>, typeof alepha_orm56.PG_DEFAULT>;
|
|
14
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
15
|
+
target: alepha52.TString;
|
|
16
|
+
code: alepha52.TString;
|
|
17
|
+
verifiedAt: alepha52.TOptional<alepha52.TString>;
|
|
18
|
+
attempts: alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_DEFAULT>;
|
|
19
|
+
}>>;
|
|
20
|
+
declare const verificationEntitySchema: alepha52.TObject<{
|
|
21
|
+
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_PRIMARY_KEY>, typeof alepha_orm56.PG_DEFAULT>;
|
|
22
|
+
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_CREATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
23
|
+
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_UPDATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
24
|
+
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_VERSION>, typeof alepha_orm56.PG_DEFAULT>;
|
|
25
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
26
|
+
target: alepha52.TString;
|
|
27
|
+
code: alepha52.TString;
|
|
28
|
+
verifiedAt: alepha52.TOptional<alepha52.TString>;
|
|
29
|
+
attempts: alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_DEFAULT>;
|
|
30
|
+
}>;
|
|
31
|
+
declare const verificationEntityInsertSchema: alepha_orm56.TObjectInsert<alepha52.TObject<{
|
|
32
|
+
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_PRIMARY_KEY>, typeof alepha_orm56.PG_DEFAULT>;
|
|
33
|
+
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_CREATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
34
|
+
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_UPDATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
35
|
+
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_VERSION>, typeof alepha_orm56.PG_DEFAULT>;
|
|
36
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
37
|
+
target: alepha52.TString;
|
|
38
|
+
code: alepha52.TString;
|
|
39
|
+
verifiedAt: alepha52.TOptional<alepha52.TString>;
|
|
40
|
+
attempts: alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_DEFAULT>;
|
|
19
41
|
}>>;
|
|
20
42
|
type VerificationEntity = Static<typeof verifications.schema>;
|
|
21
43
|
//#endregion
|
|
22
44
|
//#region src/api-verifications/schemas/verificationSettingsSchema.d.ts
|
|
23
|
-
declare const verificationSettingsSchema:
|
|
24
|
-
code:
|
|
25
|
-
maxAttempts:
|
|
26
|
-
codeLength:
|
|
27
|
-
codeExpiration:
|
|
28
|
-
verificationCooldown:
|
|
29
|
-
limitPerDay:
|
|
45
|
+
declare const verificationSettingsSchema: alepha52.TObject<{
|
|
46
|
+
code: alepha52.TObject<{
|
|
47
|
+
maxAttempts: alepha52.TInteger;
|
|
48
|
+
codeLength: alepha52.TInteger;
|
|
49
|
+
codeExpiration: alepha52.TInteger;
|
|
50
|
+
verificationCooldown: alepha52.TInteger;
|
|
51
|
+
limitPerDay: alepha52.TInteger;
|
|
30
52
|
}>;
|
|
31
|
-
link:
|
|
32
|
-
maxAttempts:
|
|
33
|
-
codeExpiration:
|
|
34
|
-
verificationCooldown:
|
|
35
|
-
limitPerDay:
|
|
53
|
+
link: alepha52.TObject<{
|
|
54
|
+
maxAttempts: alepha52.TInteger;
|
|
55
|
+
codeExpiration: alepha52.TInteger;
|
|
56
|
+
verificationCooldown: alepha52.TInteger;
|
|
57
|
+
limitPerDay: alepha52.TInteger;
|
|
36
58
|
}>;
|
|
37
|
-
purgeDays:
|
|
59
|
+
purgeDays: alepha52.TInteger;
|
|
38
60
|
}>;
|
|
39
61
|
type VerificationSettings = Static<typeof verificationSettingsSchema>;
|
|
40
62
|
//#endregion
|
|
@@ -42,21 +64,21 @@ type VerificationSettings = Static<typeof verificationSettingsSchema>;
|
|
|
42
64
|
/**
|
|
43
65
|
* Verification settings configuration atom
|
|
44
66
|
*/
|
|
45
|
-
declare const verificationOptions:
|
|
46
|
-
code:
|
|
47
|
-
maxAttempts:
|
|
48
|
-
codeLength:
|
|
49
|
-
codeExpiration:
|
|
50
|
-
verificationCooldown:
|
|
51
|
-
limitPerDay:
|
|
67
|
+
declare const verificationOptions: alepha52.Atom<alepha52.TObject<{
|
|
68
|
+
code: alepha52.TObject<{
|
|
69
|
+
maxAttempts: alepha52.TInteger;
|
|
70
|
+
codeLength: alepha52.TInteger;
|
|
71
|
+
codeExpiration: alepha52.TInteger;
|
|
72
|
+
verificationCooldown: alepha52.TInteger;
|
|
73
|
+
limitPerDay: alepha52.TInteger;
|
|
52
74
|
}>;
|
|
53
|
-
link:
|
|
54
|
-
maxAttempts:
|
|
55
|
-
codeExpiration:
|
|
56
|
-
verificationCooldown:
|
|
57
|
-
limitPerDay:
|
|
75
|
+
link: alepha52.TObject<{
|
|
76
|
+
maxAttempts: alepha52.TInteger;
|
|
77
|
+
codeExpiration: alepha52.TInteger;
|
|
78
|
+
verificationCooldown: alepha52.TInteger;
|
|
79
|
+
limitPerDay: alepha52.TInteger;
|
|
58
80
|
}>;
|
|
59
|
-
purgeDays:
|
|
81
|
+
purgeDays: alepha52.TInteger;
|
|
60
82
|
}>, "alepha.api.verifications.options">;
|
|
61
83
|
type VerificationOptions = Static<typeof verificationOptions.schema>;
|
|
62
84
|
declare module "alepha" {
|
|
@@ -85,23 +107,23 @@ declare class VerificationParameters {
|
|
|
85
107
|
}
|
|
86
108
|
//#endregion
|
|
87
109
|
//#region src/api-verifications/schemas/requestVerificationCodeResponseSchema.d.ts
|
|
88
|
-
declare const requestVerificationCodeResponseSchema:
|
|
89
|
-
token:
|
|
90
|
-
codeExpiration:
|
|
91
|
-
verificationCooldown:
|
|
92
|
-
maxVerificationAttempts:
|
|
110
|
+
declare const requestVerificationCodeResponseSchema: alepha52.TObject<{
|
|
111
|
+
token: alepha52.TString;
|
|
112
|
+
codeExpiration: alepha52.TInteger;
|
|
113
|
+
verificationCooldown: alepha52.TInteger;
|
|
114
|
+
maxVerificationAttempts: alepha52.TInteger;
|
|
93
115
|
}>;
|
|
94
116
|
type RequestVerificationResponse = Static<typeof requestVerificationCodeResponseSchema>;
|
|
95
117
|
//#endregion
|
|
96
118
|
//#region src/api-verifications/schemas/validateVerificationCodeResponseSchema.d.ts
|
|
97
|
-
declare const validateVerificationCodeResponseSchema:
|
|
98
|
-
ok:
|
|
99
|
-
alreadyVerified:
|
|
119
|
+
declare const validateVerificationCodeResponseSchema: alepha52.TObject<{
|
|
120
|
+
ok: alepha52.TBoolean;
|
|
121
|
+
alreadyVerified: alepha52.TOptional<alepha52.TBoolean>;
|
|
100
122
|
}>;
|
|
101
123
|
type ValidateVerificationCodeResponse = Static<typeof validateVerificationCodeResponseSchema>;
|
|
102
124
|
//#endregion
|
|
103
125
|
//#region src/api-verifications/schemas/verificationTypeEnumSchema.d.ts
|
|
104
|
-
declare const verificationTypeEnumSchema:
|
|
126
|
+
declare const verificationTypeEnumSchema: alepha52.TUnsafe<"link" | "code">;
|
|
105
127
|
type VerificationTypeEnum = Static<typeof verificationTypeEnumSchema>;
|
|
106
128
|
//#endregion
|
|
107
129
|
//#region src/api-verifications/services/VerificationService.d.ts
|
|
@@ -109,38 +131,38 @@ declare class VerificationService {
|
|
|
109
131
|
protected readonly log: alepha_logger0.Logger;
|
|
110
132
|
protected readonly dateTimeProvider: DateTimeProvider;
|
|
111
133
|
protected readonly verificationParameters: VerificationParameters;
|
|
112
|
-
protected readonly verificationRepository: alepha_orm56.Repository<
|
|
113
|
-
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
114
|
-
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
115
|
-
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
116
|
-
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
117
|
-
type:
|
|
118
|
-
target:
|
|
119
|
-
code:
|
|
120
|
-
verifiedAt:
|
|
121
|
-
attempts: alepha_orm56.PgAttr<
|
|
134
|
+
protected readonly verificationRepository: alepha_orm56.Repository<alepha52.TObject<{
|
|
135
|
+
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_PRIMARY_KEY>, typeof alepha_orm56.PG_DEFAULT>;
|
|
136
|
+
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_CREATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
137
|
+
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_UPDATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
138
|
+
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_VERSION>, typeof alepha_orm56.PG_DEFAULT>;
|
|
139
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
140
|
+
target: alepha52.TString;
|
|
141
|
+
code: alepha52.TString;
|
|
142
|
+
verifiedAt: alepha52.TOptional<alepha52.TString>;
|
|
143
|
+
attempts: alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_DEFAULT>;
|
|
122
144
|
}>>;
|
|
123
145
|
findByEntry(entry: VerificationEntry): Promise<VerificationEntity>;
|
|
124
|
-
findRecentsByEntry(entry: VerificationEntry): Promise<alepha_orm56.PgStatic<
|
|
125
|
-
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
126
|
-
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
127
|
-
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
128
|
-
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
129
|
-
type:
|
|
130
|
-
target:
|
|
131
|
-
code:
|
|
132
|
-
verifiedAt:
|
|
133
|
-
attempts: alepha_orm56.PgAttr<
|
|
134
|
-
}>, alepha_orm56.PgRelationMap<
|
|
135
|
-
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
136
|
-
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
137
|
-
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
138
|
-
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<
|
|
139
|
-
type:
|
|
140
|
-
target:
|
|
141
|
-
code:
|
|
142
|
-
verifiedAt:
|
|
143
|
-
attempts: alepha_orm56.PgAttr<
|
|
146
|
+
findRecentsByEntry(entry: VerificationEntry): Promise<alepha_orm56.PgStatic<alepha52.TObject<{
|
|
147
|
+
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_PRIMARY_KEY>, typeof alepha_orm56.PG_DEFAULT>;
|
|
148
|
+
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_CREATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
149
|
+
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_UPDATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
150
|
+
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_VERSION>, typeof alepha_orm56.PG_DEFAULT>;
|
|
151
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
152
|
+
target: alepha52.TString;
|
|
153
|
+
code: alepha52.TString;
|
|
154
|
+
verifiedAt: alepha52.TOptional<alepha52.TString>;
|
|
155
|
+
attempts: alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_DEFAULT>;
|
|
156
|
+
}>, alepha_orm56.PgRelationMap<alepha52.TObject<{
|
|
157
|
+
id: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_PRIMARY_KEY>, typeof alepha_orm56.PG_DEFAULT>;
|
|
158
|
+
createdAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_CREATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
159
|
+
updatedAt: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TString, typeof alepha_orm56.PG_UPDATED_AT>, typeof alepha_orm56.PG_DEFAULT>;
|
|
160
|
+
version: alepha_orm56.PgAttr<alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_VERSION>, typeof alepha_orm56.PG_DEFAULT>;
|
|
161
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
162
|
+
target: alepha52.TString;
|
|
163
|
+
code: alepha52.TString;
|
|
164
|
+
verifiedAt: alepha52.TOptional<alepha52.TString>;
|
|
165
|
+
attempts: alepha_orm56.PgAttr<alepha52.TInteger, typeof alepha_orm56.PG_DEFAULT>;
|
|
144
166
|
}>>>[]>;
|
|
145
167
|
/**
|
|
146
168
|
* Creates a verification entry and returns the token.
|
|
@@ -162,31 +184,31 @@ declare class VerificationController {
|
|
|
162
184
|
protected readonly verificationService: VerificationService;
|
|
163
185
|
readonly url = "/verifications";
|
|
164
186
|
readonly group = "verifications";
|
|
165
|
-
readonly requestVerificationCode: alepha_server0.
|
|
166
|
-
params:
|
|
167
|
-
type:
|
|
187
|
+
readonly requestVerificationCode: alepha_server0.ActionPrimitiveFn<{
|
|
188
|
+
params: alepha52.TObject<{
|
|
189
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
168
190
|
}>;
|
|
169
|
-
body:
|
|
170
|
-
target:
|
|
191
|
+
body: alepha52.TObject<{
|
|
192
|
+
target: alepha52.TString;
|
|
171
193
|
}>;
|
|
172
|
-
response:
|
|
173
|
-
token:
|
|
174
|
-
codeExpiration:
|
|
175
|
-
verificationCooldown:
|
|
176
|
-
maxVerificationAttempts:
|
|
194
|
+
response: alepha52.TObject<{
|
|
195
|
+
token: alepha52.TString;
|
|
196
|
+
codeExpiration: alepha52.TInteger;
|
|
197
|
+
verificationCooldown: alepha52.TInteger;
|
|
198
|
+
maxVerificationAttempts: alepha52.TInteger;
|
|
177
199
|
}>;
|
|
178
200
|
}>;
|
|
179
|
-
readonly validateVerificationCode: alepha_server0.
|
|
180
|
-
params:
|
|
181
|
-
type:
|
|
201
|
+
readonly validateVerificationCode: alepha_server0.ActionPrimitiveFn<{
|
|
202
|
+
params: alepha52.TObject<{
|
|
203
|
+
type: alepha52.TUnsafe<"link" | "code">;
|
|
182
204
|
}>;
|
|
183
|
-
body:
|
|
184
|
-
target:
|
|
185
|
-
token:
|
|
205
|
+
body: alepha52.TObject<{
|
|
206
|
+
target: alepha52.TString;
|
|
207
|
+
token: alepha52.TString;
|
|
186
208
|
}>;
|
|
187
|
-
response:
|
|
188
|
-
ok:
|
|
189
|
-
alreadyVerified:
|
|
209
|
+
response: alepha52.TObject<{
|
|
210
|
+
ok: alepha52.TBoolean;
|
|
211
|
+
alreadyVerified: alepha52.TOptional<alepha52.TBoolean>;
|
|
190
212
|
}>;
|
|
191
213
|
}>;
|
|
192
214
|
}
|
|
@@ -201,7 +223,7 @@ declare class VerificationController {
|
|
|
201
223
|
*
|
|
202
224
|
* @module alepha.api.verifications
|
|
203
225
|
*/
|
|
204
|
-
declare const AlephaApiVerification:
|
|
226
|
+
declare const AlephaApiVerification: alepha52.Service<alepha52.Module>;
|
|
205
227
|
//#endregion
|
|
206
|
-
export { AlephaApiVerification, RequestVerificationResponse, ValidateVerificationCodeResponse, VerificationController, VerificationEntry, VerificationService, VerificationTypeEnum, requestVerificationCodeResponseSchema, validateVerificationCodeResponseSchema, verificationTypeEnumSchema };
|
|
228
|
+
export { AlephaApiVerification, RequestVerificationResponse, ValidateVerificationCodeResponse, VerificationController, VerificationEntity, VerificationEntry, VerificationService, VerificationTypeEnum, requestVerificationCodeResponseSchema, validateVerificationCodeResponseSchema, verificationEntityInsertSchema, verificationEntitySchema, verificationTypeEnumSchema, verifications };
|
|
207
229
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -398,5 +398,5 @@ const AlephaApiVerification = $module({
|
|
|
398
398
|
});
|
|
399
399
|
|
|
400
400
|
//#endregion
|
|
401
|
-
export { AlephaApiVerification, VerificationController, VerificationService, requestVerificationCodeResponseSchema, validateVerificationCodeResponseSchema, verificationTypeEnumSchema };
|
|
401
|
+
export { AlephaApiVerification, VerificationController, VerificationService, requestVerificationCodeResponseSchema, validateVerificationCodeResponseSchema, verificationEntityInsertSchema, verificationEntitySchema, verificationTypeEnumSchema, verifications };
|
|
402
402
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/api-verifications/schemas/requestVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/validateVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/verificationTypeEnumSchema.ts","../../src/api-verifications/entities/verifications.ts","../../src/api-verifications/schemas/verificationSettingsSchema.ts","../../src/api-verifications/parameters/VerificationParameters.ts","../../src/api-verifications/services/VerificationService.ts","../../src/api-verifications/controllers/VerificationController.ts","../../src/api-verifications/jobs/VerificationJobs.ts","../../src/api-verifications/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const requestVerificationCodeResponseSchema = t.object({\n token: t.string({\n description:\n \"The verification token (6-digit code for phone, UUID for email). The caller should send this to the user via their preferred notification method.\",\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before your verification token expires.\",\n }),\n verificationCooldown: t.integer({\n description:\n \"Cooldown period in seconds before you can request another verification.\",\n }),\n maxVerificationAttempts: t.integer({\n description:\n \"Maximum number of verification attempts allowed before the token is locked.\",\n }),\n});\n\nexport type RequestVerificationResponse = Static<\n typeof requestVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const validateVerificationCodeResponseSchema = t.object({\n ok: t.boolean({\n description: \"Indicates whether the verification was successful.\",\n }),\n alreadyVerified: t.optional(\n t.boolean({\n description: \"Indicates whether the target was already verified.\",\n }),\n ),\n});\n\nexport type ValidateVerificationCodeResponse = Static<\n typeof validateVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationTypeEnumSchema = t.enum([\"code\", \"link\"]);\nexport type VerificationTypeEnum = Static<typeof verificationTypeEnumSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport const verifications = $entity({\n name: \"verification\",\n schema: t.object({\n id: pg.primaryKey(t.bigint()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n version: pg.version(),\n\n type: verificationTypeEnumSchema,\n\n target: t.text({\n description: \"Can be a phone (E.164 format) or email address\",\n }),\n\n code: t.text({\n description: \"Hashed verification token (n-digit code or UUID)\",\n }),\n\n verifiedAt: t.optional(\n t.datetime({\n description: \"When it was successfully verified\",\n }),\n ),\n\n attempts: pg.default(\n t.integer({\n description: \"Number of failed attempts (to prevent brute-force)\",\n }),\n 0,\n ),\n }),\n indexes: [\n \"createdAt\",\n {\n columns: [\"target\", \"code\"],\n },\n ],\n});\n\nexport const verificationEntitySchema = verifications.schema;\nexport const verificationEntityInsertSchema = verifications.insertSchema;\nexport type VerificationEntity = Static<typeof verifications.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationSettingsSchema = t.object({\n code: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeLength: t.integer({\n description: \"Length of the verification code.\",\n minimum: 4,\n maximum: 12,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification code expires.\",\n minimum: 60, // 1 minute\n maximum: 3600, // 1 hour\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to code verifications.\",\n },\n ),\n link: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification token expires.\",\n minimum: 60, // 1 minute\n maximum: 7200, // 2 hours\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to link verifications.\",\n },\n ),\n purgeDays: t.integer({\n description:\n \"Number of days after which expired verifications are automatically deleted. Set to 0 to disable auto-deletion.\",\n minimum: 0,\n maximum: 365,\n }),\n});\n\nexport type VerificationSettings = Static<typeof verificationSettingsSchema>;\n","import { $atom, $use, type Static } from \"alepha\";\nimport {\n type VerificationSettings,\n verificationSettingsSchema,\n} from \"../schemas/verificationSettingsSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Verification settings configuration atom\n */\nexport const verificationOptions = $atom({\n name: \"alepha.api.verifications.options\",\n schema: verificationSettingsSchema,\n default: {\n code: {\n maxAttempts: 5,\n codeLength: 6,\n codeExpiration: 300, // 5 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n link: {\n maxAttempts: 3, // Lower since UUIDs are harder to guess\n codeExpiration: 1800, // 30 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n purgeDays: 1,\n },\n});\n\nexport type VerificationOptions = Static<typeof verificationOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [verificationOptions.key]: VerificationOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class VerificationParameters {\n protected readonly options = $use(verificationOptions);\n\n public get<K extends keyof VerificationSettings>(\n key: K,\n ): VerificationSettings[K] {\n return this.options[key];\n }\n}\n","import { createHash, randomInt, randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { BadRequestError, NotFoundError } from \"alepha/server\";\nimport {\n type VerificationEntity,\n verifications,\n} from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\nimport type { RequestVerificationResponse } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport type { ValidateVerificationCodeResponse } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport type { VerificationTypeEnum } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport class VerificationService {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly verificationRepository = $repository(verifications);\n\n public async findByEntry(\n entry: VerificationEntry,\n ): Promise<VerificationEntity> {\n this.log.trace(\"Finding verification by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n const results = await this.verificationRepository.findMany({\n limit: 1, // only need the most recent entry\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n },\n });\n\n if (results.length === 0) {\n this.log.debug(\"Verification entry not found\", {\n type: entry.type,\n target: entry.target,\n });\n throw new NotFoundError(\"Verification entry not found\");\n }\n\n this.log.debug(\"Verification entry found\", {\n id: results[0].id,\n type: entry.type,\n target: entry.target,\n });\n\n return results[0];\n }\n\n public findRecentsByEntry(entry: VerificationEntry) {\n this.log.trace(\"Finding recent verifications by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n return this.verificationRepository.findMany({\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n createdAt: {\n gte: this.dateTimeProvider.now().startOf(\"day\").toISOString(),\n },\n },\n });\n }\n\n /**\n * Creates a verification entry and returns the token.\n * The caller is responsible for sending notifications with the token.\n * This allows for context-specific notifications (e.g., password reset vs email verification).\n */\n public async createVerification(\n entry: VerificationEntry,\n ): Promise<RequestVerificationResponse> {\n this.log.trace(\"Creating verification\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const recents = await this.findRecentsByEntry(entry);\n if (recents.length >= settings.limitPerDay) {\n this.log.warn(\"Daily verification limit reached\", {\n type: entry.type,\n target: entry.target,\n limit: settings.limitPerDay,\n count: recents.length,\n });\n throw new BadRequestError(\n `Maximum number of verification requests per day reached (${settings.limitPerDay})`,\n );\n }\n\n const existingVerification = recents[0];\n if (existingVerification) {\n const nowSec = this.dateTimeProvider.now().unix();\n const createdAtSec = this.dateTimeProvider\n .of(existingVerification.createdAt)\n .unix();\n\n const diffSec = nowSec - createdAtSec;\n if (diffSec < settings.verificationCooldown) {\n const remainingCooldown = Math.floor(\n settings.verificationCooldown - diffSec,\n );\n this.log.debug(\"Verification on cooldown\", {\n type: entry.type,\n target: entry.target,\n remainingSeconds: remainingCooldown,\n });\n throw new BadRequestError(\n `Verification is on cooldown for ${remainingCooldown} seconds`,\n );\n }\n }\n\n const token = this.generateToken(entry.type);\n\n const verification = await this.verificationRepository.create({\n type: entry.type,\n target: entry.target,\n code: this.hashCode(token),\n });\n\n this.log.info(\"Verification created\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n expiresInSeconds: settings.codeExpiration,\n });\n\n return {\n token,\n codeExpiration: settings.codeExpiration,\n verificationCooldown: settings.verificationCooldown,\n maxVerificationAttempts: settings.maxAttempts,\n };\n }\n\n public async verifyCode(\n entry: VerificationEntry,\n code: string,\n ): Promise<ValidateVerificationCodeResponse> {\n this.log.trace(\"Verifying code\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const verification = await this.findByEntry(entry);\n if (verification.verifiedAt) {\n this.log.debug(\"Verification already verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n verifiedAt: verification.verifiedAt,\n });\n return { ok: true, alreadyVerified: true };\n }\n\n // DO NOT DELETE THE VERIFICATION WHEN IT IS REJECTED,\n // or we won't be able to cooldown the verification\n\n const now = this.dateTimeProvider.now();\n const expirationDate = this.dateTimeProvider\n .of(verification.createdAt)\n .add(settings.codeExpiration, \"seconds\");\n\n if (now > expirationDate) {\n this.log.warn(\"Verification code expired\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n createdAt: verification.createdAt,\n expiredAt: expirationDate.toISOString(),\n });\n throw new BadRequestError(\"Verification code has expired\");\n }\n\n if (verification.attempts >= settings.maxAttempts) {\n this.log.warn(\"Verification locked due to max attempts\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: verification.attempts,\n maxAttempts: settings.maxAttempts,\n });\n throw new BadRequestError(\n \"Maximum number of attempts reached - verification is locked\",\n );\n }\n\n if (verification.code !== this.hashCode(code)) {\n const newAttempts = verification.attempts + 1;\n this.log.warn(\"Invalid verification code\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: newAttempts,\n maxAttempts: settings.maxAttempts,\n });\n await this.verificationRepository.updateById(verification.id, {\n attempts: newAttempts,\n });\n throw new BadRequestError(\"Invalid verification code\");\n }\n\n await this.verificationRepository.updateById(verification.id, {\n verifiedAt: this.dateTimeProvider.nowISOString(),\n });\n\n this.log.info(\"Verification code verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n });\n\n return { ok: true };\n }\n\n public hashCode(code: string): string {\n return createHash(\"sha256\").update(code).digest(\"hex\");\n }\n\n public generateToken(type: VerificationTypeEnum): string {\n if (type === \"code\") {\n const settings = this.verificationParameters.get(\"code\");\n return randomInt(0, 1_000_000)\n .toString()\n .padStart(settings.codeLength, \"0\");\n } else if (type === \"link\") {\n return randomUUID();\n }\n\n throw new BadRequestError(`Invalid verification type: ${type}`);\n }\n}\n\nexport interface VerificationEntry {\n type: VerificationTypeEnum;\n target: string;\n}\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { requestVerificationCodeResponseSchema } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport { validateVerificationCodeResponseSchema } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\nimport { VerificationService } from \"../services/VerificationService.ts\";\n\nexport class VerificationController {\n protected readonly verificationService = $inject(VerificationService);\n\n public readonly url = \"/verifications\";\n public readonly group = \"verifications\";\n\n public readonly requestVerificationCode = $action({\n path: `${this.url}/:type`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n }),\n response: requestVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return await this.verificationService.createVerification({\n type: params.type,\n target: body.target,\n });\n },\n });\n\n public readonly validateVerificationCode = $action({\n path: `${this.url}/:type/validate`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n token: t.text({\n description:\n \"The verification token (6-digit code for phone, UUID for email).\",\n }),\n }),\n response: validateVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return this.verificationService.verifyCode(\n {\n type: params.type,\n target: body.target,\n },\n body.token,\n );\n },\n });\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $repository } from \"alepha/orm\";\nimport { $scheduler } from \"alepha/scheduler\";\nimport { verifications } from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\n\nexport class VerificationJobs {\n protected readonly verificationRepository = $repository(verifications);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n public readonly cleanExpired = $scheduler({\n cron: \"0 0 * * *\", // Every day at midnight\n description: \"Clean expired verifications\",\n handler: async () => {\n const purgeDays = this.verificationParameters.get(\"purgeDays\");\n if (purgeDays <= 0) {\n return; // Auto deletion is disabled\n }\n\n const dayMs = 24 * 60 * 60 * 1000;\n const purgeThreshold = Date.now() - purgeDays * dayMs;\n\n await this.verificationRepository.deleteMany({\n createdAt: {\n lt: this.dateTimeProvider.of(purgeThreshold).toISOString(),\n },\n });\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { VerificationController } from \"./controllers/VerificationController.ts\";\nimport { VerificationJobs } from \"./jobs/VerificationJobs.ts\";\nimport { VerificationService } from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/VerificationController.ts\";\nexport * from \"./schemas/requestVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/validateVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/verificationTypeEnumSchema.ts\";\nexport * from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides email/phone verification management API endpoints for Alepha applications.\n *\n * This module includes verification code generation, validation,\n * and related functionalities. Notifications are handled by the consuming module\n * (e.g., api-users) for context-specific messaging.\n *\n * @module alepha.api.verifications\n */\nexport const AlephaApiVerification = $module({\n name: \"alepha.api.verifications\",\n services: [VerificationController, VerificationJobs, VerificationService],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/api-verifications/schemas/requestVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/validateVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/verificationTypeEnumSchema.ts","../../src/api-verifications/entities/verifications.ts","../../src/api-verifications/schemas/verificationSettingsSchema.ts","../../src/api-verifications/parameters/VerificationParameters.ts","../../src/api-verifications/services/VerificationService.ts","../../src/api-verifications/controllers/VerificationController.ts","../../src/api-verifications/jobs/VerificationJobs.ts","../../src/api-verifications/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const requestVerificationCodeResponseSchema = t.object({\n token: t.string({\n description:\n \"The verification token (6-digit code for phone, UUID for email). The caller should send this to the user via their preferred notification method.\",\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before your verification token expires.\",\n }),\n verificationCooldown: t.integer({\n description:\n \"Cooldown period in seconds before you can request another verification.\",\n }),\n maxVerificationAttempts: t.integer({\n description:\n \"Maximum number of verification attempts allowed before the token is locked.\",\n }),\n});\n\nexport type RequestVerificationResponse = Static<\n typeof requestVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const validateVerificationCodeResponseSchema = t.object({\n ok: t.boolean({\n description: \"Indicates whether the verification was successful.\",\n }),\n alreadyVerified: t.optional(\n t.boolean({\n description: \"Indicates whether the target was already verified.\",\n }),\n ),\n});\n\nexport type ValidateVerificationCodeResponse = Static<\n typeof validateVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationTypeEnumSchema = t.enum([\"code\", \"link\"]);\nexport type VerificationTypeEnum = Static<typeof verificationTypeEnumSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport const verifications = $entity({\n name: \"verification\",\n schema: t.object({\n id: pg.primaryKey(t.bigint()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n version: pg.version(),\n\n type: verificationTypeEnumSchema,\n\n target: t.text({\n description: \"Can be a phone (E.164 format) or email address\",\n }),\n\n code: t.text({\n description: \"Hashed verification token (n-digit code or UUID)\",\n }),\n\n verifiedAt: t.optional(\n t.datetime({\n description: \"When it was successfully verified\",\n }),\n ),\n\n attempts: pg.default(\n t.integer({\n description: \"Number of failed attempts (to prevent brute-force)\",\n }),\n 0,\n ),\n }),\n indexes: [\n \"createdAt\",\n {\n columns: [\"target\", \"code\"],\n },\n ],\n});\n\nexport const verificationEntitySchema = verifications.schema;\nexport const verificationEntityInsertSchema = verifications.insertSchema;\nexport type VerificationEntity = Static<typeof verifications.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationSettingsSchema = t.object({\n code: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeLength: t.integer({\n description: \"Length of the verification code.\",\n minimum: 4,\n maximum: 12,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification code expires.\",\n minimum: 60, // 1 minute\n maximum: 3600, // 1 hour\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to code verifications.\",\n },\n ),\n link: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification token expires.\",\n minimum: 60, // 1 minute\n maximum: 7200, // 2 hours\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to link verifications.\",\n },\n ),\n purgeDays: t.integer({\n description:\n \"Number of days after which expired verifications are automatically deleted. Set to 0 to disable auto-deletion.\",\n minimum: 0,\n maximum: 365,\n }),\n});\n\nexport type VerificationSettings = Static<typeof verificationSettingsSchema>;\n","import { $atom, $use, type Static } from \"alepha\";\nimport {\n type VerificationSettings,\n verificationSettingsSchema,\n} from \"../schemas/verificationSettingsSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Verification settings configuration atom\n */\nexport const verificationOptions = $atom({\n name: \"alepha.api.verifications.options\",\n schema: verificationSettingsSchema,\n default: {\n code: {\n maxAttempts: 5,\n codeLength: 6,\n codeExpiration: 300, // 5 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n link: {\n maxAttempts: 3, // Lower since UUIDs are harder to guess\n codeExpiration: 1800, // 30 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n purgeDays: 1,\n },\n});\n\nexport type VerificationOptions = Static<typeof verificationOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [verificationOptions.key]: VerificationOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class VerificationParameters {\n protected readonly options = $use(verificationOptions);\n\n public get<K extends keyof VerificationSettings>(\n key: K,\n ): VerificationSettings[K] {\n return this.options[key];\n }\n}\n","import { createHash, randomInt, randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { BadRequestError, NotFoundError } from \"alepha/server\";\nimport {\n type VerificationEntity,\n verifications,\n} from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\nimport type { RequestVerificationResponse } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport type { ValidateVerificationCodeResponse } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport type { VerificationTypeEnum } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport class VerificationService {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly verificationRepository = $repository(verifications);\n\n public async findByEntry(\n entry: VerificationEntry,\n ): Promise<VerificationEntity> {\n this.log.trace(\"Finding verification by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n const results = await this.verificationRepository.findMany({\n limit: 1, // only need the most recent entry\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n },\n });\n\n if (results.length === 0) {\n this.log.debug(\"Verification entry not found\", {\n type: entry.type,\n target: entry.target,\n });\n throw new NotFoundError(\"Verification entry not found\");\n }\n\n this.log.debug(\"Verification entry found\", {\n id: results[0].id,\n type: entry.type,\n target: entry.target,\n });\n\n return results[0];\n }\n\n public findRecentsByEntry(entry: VerificationEntry) {\n this.log.trace(\"Finding recent verifications by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n return this.verificationRepository.findMany({\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n createdAt: {\n gte: this.dateTimeProvider.now().startOf(\"day\").toISOString(),\n },\n },\n });\n }\n\n /**\n * Creates a verification entry and returns the token.\n * The caller is responsible for sending notifications with the token.\n * This allows for context-specific notifications (e.g., password reset vs email verification).\n */\n public async createVerification(\n entry: VerificationEntry,\n ): Promise<RequestVerificationResponse> {\n this.log.trace(\"Creating verification\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const recents = await this.findRecentsByEntry(entry);\n if (recents.length >= settings.limitPerDay) {\n this.log.warn(\"Daily verification limit reached\", {\n type: entry.type,\n target: entry.target,\n limit: settings.limitPerDay,\n count: recents.length,\n });\n throw new BadRequestError(\n `Maximum number of verification requests per day reached (${settings.limitPerDay})`,\n );\n }\n\n const existingVerification = recents[0];\n if (existingVerification) {\n const nowSec = this.dateTimeProvider.now().unix();\n const createdAtSec = this.dateTimeProvider\n .of(existingVerification.createdAt)\n .unix();\n\n const diffSec = nowSec - createdAtSec;\n if (diffSec < settings.verificationCooldown) {\n const remainingCooldown = Math.floor(\n settings.verificationCooldown - diffSec,\n );\n this.log.debug(\"Verification on cooldown\", {\n type: entry.type,\n target: entry.target,\n remainingSeconds: remainingCooldown,\n });\n throw new BadRequestError(\n `Verification is on cooldown for ${remainingCooldown} seconds`,\n );\n }\n }\n\n const token = this.generateToken(entry.type);\n\n const verification = await this.verificationRepository.create({\n type: entry.type,\n target: entry.target,\n code: this.hashCode(token),\n });\n\n this.log.info(\"Verification created\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n expiresInSeconds: settings.codeExpiration,\n });\n\n return {\n token,\n codeExpiration: settings.codeExpiration,\n verificationCooldown: settings.verificationCooldown,\n maxVerificationAttempts: settings.maxAttempts,\n };\n }\n\n public async verifyCode(\n entry: VerificationEntry,\n code: string,\n ): Promise<ValidateVerificationCodeResponse> {\n this.log.trace(\"Verifying code\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const verification = await this.findByEntry(entry);\n if (verification.verifiedAt) {\n this.log.debug(\"Verification already verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n verifiedAt: verification.verifiedAt,\n });\n return { ok: true, alreadyVerified: true };\n }\n\n // DO NOT DELETE THE VERIFICATION WHEN IT IS REJECTED,\n // or we won't be able to cooldown the verification\n\n const now = this.dateTimeProvider.now();\n const expirationDate = this.dateTimeProvider\n .of(verification.createdAt)\n .add(settings.codeExpiration, \"seconds\");\n\n if (now > expirationDate) {\n this.log.warn(\"Verification code expired\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n createdAt: verification.createdAt,\n expiredAt: expirationDate.toISOString(),\n });\n throw new BadRequestError(\"Verification code has expired\");\n }\n\n if (verification.attempts >= settings.maxAttempts) {\n this.log.warn(\"Verification locked due to max attempts\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: verification.attempts,\n maxAttempts: settings.maxAttempts,\n });\n throw new BadRequestError(\n \"Maximum number of attempts reached - verification is locked\",\n );\n }\n\n if (verification.code !== this.hashCode(code)) {\n const newAttempts = verification.attempts + 1;\n this.log.warn(\"Invalid verification code\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: newAttempts,\n maxAttempts: settings.maxAttempts,\n });\n await this.verificationRepository.updateById(verification.id, {\n attempts: newAttempts,\n });\n throw new BadRequestError(\"Invalid verification code\");\n }\n\n await this.verificationRepository.updateById(verification.id, {\n verifiedAt: this.dateTimeProvider.nowISOString(),\n });\n\n this.log.info(\"Verification code verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n });\n\n return { ok: true };\n }\n\n public hashCode(code: string): string {\n return createHash(\"sha256\").update(code).digest(\"hex\");\n }\n\n public generateToken(type: VerificationTypeEnum): string {\n if (type === \"code\") {\n const settings = this.verificationParameters.get(\"code\");\n return randomInt(0, 1_000_000)\n .toString()\n .padStart(settings.codeLength, \"0\");\n } else if (type === \"link\") {\n return randomUUID();\n }\n\n throw new BadRequestError(`Invalid verification type: ${type}`);\n }\n}\n\nexport interface VerificationEntry {\n type: VerificationTypeEnum;\n target: string;\n}\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { requestVerificationCodeResponseSchema } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport { validateVerificationCodeResponseSchema } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\nimport { VerificationService } from \"../services/VerificationService.ts\";\n\nexport class VerificationController {\n protected readonly verificationService = $inject(VerificationService);\n\n public readonly url = \"/verifications\";\n public readonly group = \"verifications\";\n\n public readonly requestVerificationCode = $action({\n path: `${this.url}/:type`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n }),\n response: requestVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return await this.verificationService.createVerification({\n type: params.type,\n target: body.target,\n });\n },\n });\n\n public readonly validateVerificationCode = $action({\n path: `${this.url}/:type/validate`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n token: t.text({\n description:\n \"The verification token (6-digit code for phone, UUID for email).\",\n }),\n }),\n response: validateVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return this.verificationService.verifyCode(\n {\n type: params.type,\n target: body.target,\n },\n body.token,\n );\n },\n });\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $repository } from \"alepha/orm\";\nimport { $scheduler } from \"alepha/scheduler\";\nimport { verifications } from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\n\nexport class VerificationJobs {\n protected readonly verificationRepository = $repository(verifications);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n public readonly cleanExpired = $scheduler({\n cron: \"0 0 * * *\", // Every day at midnight\n description: \"Clean expired verifications\",\n handler: async () => {\n const purgeDays = this.verificationParameters.get(\"purgeDays\");\n if (purgeDays <= 0) {\n return; // Auto deletion is disabled\n }\n\n const dayMs = 24 * 60 * 60 * 1000;\n const purgeThreshold = Date.now() - purgeDays * dayMs;\n\n await this.verificationRepository.deleteMany({\n createdAt: {\n lt: this.dateTimeProvider.of(purgeThreshold).toISOString(),\n },\n });\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { VerificationController } from \"./controllers/VerificationController.ts\";\nimport { VerificationJobs } from \"./jobs/VerificationJobs.ts\";\nimport { VerificationService } from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/VerificationController.ts\";\nexport * from \"./entities/verifications.ts\";\nexport * from \"./schemas/requestVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/validateVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/verificationTypeEnumSchema.ts\";\nexport * from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides email/phone verification management API endpoints for Alepha applications.\n *\n * This module includes verification code generation, validation,\n * and related functionalities. Notifications are handled by the consuming module\n * (e.g., api-users) for context-specific messaging.\n *\n * @module alepha.api.verifications\n */\nexport const AlephaApiVerification = $module({\n name: \"alepha.api.verifications\",\n services: [VerificationController, VerificationJobs, VerificationService],\n});\n"],"mappings":"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"}
|
package/dist/batch/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as alepha1 from "alepha";
|
|
2
|
-
import { Alepha,
|
|
2
|
+
import { Alepha, KIND, Primitive, Static, TSchema } from "alepha";
|
|
3
3
|
import { DateTimeProvider, DurationLike } from "alepha/datetime";
|
|
4
4
|
import * as alepha_logger0 from "alepha/logger";
|
|
5
|
-
import { RetryBackoffOptions,
|
|
5
|
+
import { RetryBackoffOptions, RetryPrimitiveOptions, RetryProvider } from "alepha/retry";
|
|
6
6
|
|
|
7
7
|
//#region src/batch/providers/BatchProvider.d.ts
|
|
8
8
|
interface BatchOptions<TItem, TResponse = any> {
|
|
@@ -191,15 +191,15 @@ declare class BatchProvider {
|
|
|
191
191
|
protected startProcessing<TItem, TResponse>(context: BatchContext<TItem, TResponse>): Promise<void>;
|
|
192
192
|
}
|
|
193
193
|
//#endregion
|
|
194
|
-
//#region src/batch/
|
|
194
|
+
//#region src/batch/primitives/$batch.d.ts
|
|
195
195
|
/**
|
|
196
|
-
* Creates a batch processing
|
|
196
|
+
* Creates a batch processing primitive for efficient grouping and processing of multiple operations.
|
|
197
197
|
*/
|
|
198
198
|
declare const $batch: {
|
|
199
|
-
<TItem extends TSchema, TResponse>(options:
|
|
200
|
-
[KIND]: typeof
|
|
199
|
+
<TItem extends TSchema, TResponse>(options: BatchPrimitiveOptions<TItem, TResponse>): BatchPrimitive<TItem, TResponse>;
|
|
200
|
+
[KIND]: typeof BatchPrimitive;
|
|
201
201
|
};
|
|
202
|
-
interface
|
|
202
|
+
interface BatchPrimitiveOptions<TItem extends TSchema, TResponse = any> {
|
|
203
203
|
/**
|
|
204
204
|
* TypeBox schema for validating each item added to the batch.
|
|
205
205
|
*/
|
|
@@ -232,12 +232,12 @@ interface BatchDescriptorOptions<TItem extends TSchema, TResponse = any> {
|
|
|
232
232
|
/**
|
|
233
233
|
* Retry configuration for failed batch processing operations.
|
|
234
234
|
*/
|
|
235
|
-
retry?: Omit<
|
|
235
|
+
retry?: Omit<RetryPrimitiveOptions<() => Array<Static<TItem>>>, "handler">;
|
|
236
236
|
}
|
|
237
|
-
declare class
|
|
237
|
+
declare class BatchPrimitive<TItem extends TSchema, TResponse = any> extends Primitive<BatchPrimitiveOptions<TItem, TResponse>> {
|
|
238
238
|
protected readonly batchProvider: BatchProvider;
|
|
239
239
|
protected readonly context: BatchContext<Static<TItem>, TResponse>;
|
|
240
|
-
constructor(...args: ConstructorParameters<typeof
|
|
240
|
+
constructor(...args: ConstructorParameters<typeof Primitive<BatchPrimitiveOptions<TItem, TResponse>>>);
|
|
241
241
|
/**
|
|
242
242
|
* Pushes an item into the batch and returns immediately with a unique ID.
|
|
243
243
|
* The item will be processed asynchronously with other items when the batch is flushed.
|
|
@@ -277,8 +277,8 @@ declare class BatchDescriptor<TItem extends TSchema, TResponse = any> extends De
|
|
|
277
277
|
* @returns The number of items cleared
|
|
278
278
|
*/
|
|
279
279
|
clearCompleted(status?: "completed" | "failed"): number;
|
|
280
|
-
protected readonly onReady: alepha1.
|
|
281
|
-
protected readonly dispose: alepha1.
|
|
280
|
+
protected readonly onReady: alepha1.HookPrimitive<"ready">;
|
|
281
|
+
protected readonly dispose: alepha1.HookPrimitive<"stop">;
|
|
282
282
|
}
|
|
283
283
|
//#endregion
|
|
284
284
|
//#region src/batch/index.d.ts
|
|
@@ -326,5 +326,5 @@ declare class BatchDescriptor<TItem extends TSchema, TResponse = any> extends De
|
|
|
326
326
|
*/
|
|
327
327
|
declare const AlephaBatch: alepha1.Service<alepha1.Module>;
|
|
328
328
|
//#endregion
|
|
329
|
-
export { $batch, AlephaBatch, BatchContext,
|
|
329
|
+
export { $batch, AlephaBatch, BatchContext, type BatchItemState, type BatchItemStatus, BatchOptions, BatchPrimitive, BatchPrimitiveOptions, BatchProvider, PartitionState };
|
|
330
330
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/batch/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $hook, $inject, $module,
|
|
1
|
+
import { $hook, $inject, $module, KIND, Primitive, createPrimitive } from "alepha";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { DateTimeProvider } from "alepha/datetime";
|
|
4
4
|
import { $logger } from "alepha/logger";
|
|
@@ -67,11 +67,6 @@ var BatchProvider = class {
|
|
|
67
67
|
partitionKey,
|
|
68
68
|
status: "pending"
|
|
69
69
|
};
|
|
70
|
-
this.log.trace("Pushing item to batch", {
|
|
71
|
-
id,
|
|
72
|
-
partitionKey,
|
|
73
|
-
item
|
|
74
|
-
});
|
|
75
70
|
context.itemStates.set(id, itemState);
|
|
76
71
|
if (!context.partitions.has(partitionKey)) context.partitions.set(partitionKey, {
|
|
77
72
|
itemIds: [],
|
|
@@ -272,12 +267,12 @@ var BatchProvider = class {
|
|
|
272
267
|
};
|
|
273
268
|
|
|
274
269
|
//#endregion
|
|
275
|
-
//#region src/batch/
|
|
270
|
+
//#region src/batch/primitives/$batch.ts
|
|
276
271
|
/**
|
|
277
|
-
* Creates a batch processing
|
|
272
|
+
* Creates a batch processing primitive for efficient grouping and processing of multiple operations.
|
|
278
273
|
*/
|
|
279
|
-
const $batch = (options) =>
|
|
280
|
-
var
|
|
274
|
+
const $batch = (options) => createPrimitive(BatchPrimitive, options);
|
|
275
|
+
var BatchPrimitive = class extends Primitive {
|
|
281
276
|
batchProvider = $inject(BatchProvider);
|
|
282
277
|
context;
|
|
283
278
|
constructor(...args) {
|
|
@@ -348,7 +343,7 @@ var BatchDescriptor = class extends Descriptor {
|
|
|
348
343
|
}
|
|
349
344
|
});
|
|
350
345
|
};
|
|
351
|
-
$batch[KIND] =
|
|
346
|
+
$batch[KIND] = BatchPrimitive;
|
|
352
347
|
|
|
353
348
|
//#endregion
|
|
354
349
|
//#region src/batch/index.ts
|
|
@@ -396,10 +391,10 @@ $batch[KIND] = BatchDescriptor;
|
|
|
396
391
|
*/
|
|
397
392
|
const AlephaBatch = $module({
|
|
398
393
|
name: "alepha.batch",
|
|
399
|
-
|
|
394
|
+
primitives: [$batch],
|
|
400
395
|
services: [BatchProvider]
|
|
401
396
|
});
|
|
402
397
|
|
|
403
398
|
//#endregion
|
|
404
|
-
export { $batch, AlephaBatch,
|
|
399
|
+
export { $batch, AlephaBatch, BatchPrimitive, BatchProvider };
|
|
405
400
|
//# sourceMappingURL=index.js.map
|