alepha 0.14.4 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -4
- package/dist/api/audits/index.d.ts +619 -731
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +185 -298
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +0 -1
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +245 -356
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/notifications/index.d.ts +238 -350
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/parameters/index.d.ts +499 -611
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.browser.js +1 -2
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1697 -1804
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +178 -151
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +132 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.d.ts +122 -122
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +1 -2
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +163 -163
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/cache/core/index.d.ts +46 -46
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cli/index.d.ts +302 -299
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +966 -564
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +303 -299
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +11 -7
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +419 -99
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +718 -625
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +420 -99
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +419 -99
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +44 -44
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +4 -4
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +97 -50
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +129 -33
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +7981 -14
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/file/index.d.ts +523 -390
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +253 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +208 -208
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +25 -26
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/mcp/index.d.ts +197 -197
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/orm/chunk-DtkW-qnP.js +38 -0
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +2814 -0
- package/dist/orm/index.bun.js.map +1 -0
- package/dist/orm/index.d.ts +1205 -1057
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +2056 -1753
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +248 -248
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/redis/index.bun.js +285 -0
- package/dist/redis/index.bun.js.map +1 -0
- package/dist/redis/index.d.ts +118 -136
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +18 -38
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +69 -69
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/router/index.d.ts +6 -6
- package/dist/router/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +25 -25
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/security/index.browser.js +5 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +417 -254
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +386 -86
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +277 -277
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +20 -20
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +60 -57
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +3 -3
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/cookies/index.d.ts +6 -6
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +242 -150
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +288 -122
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +11 -12
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +0 -1
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/helmet/index.d.ts +2 -2
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +84 -85
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +1 -2
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/multipart/index.d.ts +6 -6
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +102 -103
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts +16 -16
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/static/index.d.ts +44 -44
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +48 -49
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1 -2
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +13 -11
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +7 -7
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +71 -72
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/topic/core/index.d.ts +318 -318
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts +6 -6
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/vite/index.d.ts +5720 -159
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +41 -18
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +6 -6
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +247 -247
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +6 -6
- package/dist/websocket/index.js.map +1 -1
- package/package.json +9 -14
- package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
- package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
- package/src/api/users/entities/users.ts +1 -1
- package/src/api/users/index.ts +8 -8
- package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
- package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
- package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
- package/src/api/users/services/CredentialService.ts +7 -7
- package/src/api/users/services/IdentityService.ts +4 -4
- package/src/api/users/services/RegistrationService.spec.ts +25 -27
- package/src/api/users/services/RegistrationService.ts +38 -27
- package/src/api/users/services/SessionCrudService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +3 -3
- package/src/api/users/services/SessionService.ts +28 -9
- package/src/api/users/services/UserService.ts +7 -7
- package/src/batch/providers/BatchProvider.ts +1 -2
- package/src/cli/apps/AlephaPackageBuilderCli.ts +38 -19
- package/src/cli/assets/apiHelloControllerTs.ts +18 -0
- package/src/cli/assets/apiIndexTs.ts +16 -0
- package/src/cli/assets/claudeMd.ts +303 -0
- package/src/cli/assets/mainBrowserTs.ts +2 -2
- package/src/cli/assets/mainServerTs.ts +24 -0
- package/src/cli/assets/webAppRouterTs.ts +15 -0
- package/src/cli/assets/webHelloComponentTsx.ts +16 -0
- package/src/cli/assets/webIndexTs.ts +16 -0
- package/src/cli/commands/build.ts +41 -21
- package/src/cli/commands/db.ts +21 -18
- package/src/cli/commands/deploy.ts +17 -5
- package/src/cli/commands/dev.ts +13 -17
- package/src/cli/commands/format.ts +8 -2
- package/src/cli/commands/init.ts +74 -29
- package/src/cli/commands/lint.ts +8 -2
- package/src/cli/commands/test.ts +8 -2
- package/src/cli/commands/typecheck.ts +5 -1
- package/src/cli/commands/verify.ts +4 -2
- package/src/cli/services/AlephaCliUtils.ts +39 -600
- package/src/cli/services/PackageManagerUtils.ts +301 -0
- package/src/cli/services/ProjectScaffolder.ts +306 -0
- package/src/command/helpers/Runner.ts +15 -3
- package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +2 -0
- package/src/core/primitives/$hook.ts +6 -2
- package/src/core/primitives/$module.spec.ts +4 -0
- package/src/core/providers/AlsProvider.ts +1 -1
- package/src/core/providers/CodecManager.spec.ts +12 -6
- package/src/core/providers/CodecManager.ts +26 -6
- package/src/core/providers/EventManager.ts +169 -13
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +621 -0
- package/src/core/providers/KeylessJsonSchemaCodec.ts +407 -0
- package/src/core/providers/StateManager.spec.ts +27 -16
- package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
- package/src/email/providers/LocalEmailProvider.ts +52 -15
- package/src/email/providers/NodemailerEmailProvider.ts +167 -56
- package/src/file/errors/FileError.ts +7 -0
- package/src/file/index.ts +9 -1
- package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
- package/src/orm/index.browser.ts +1 -19
- package/src/orm/index.bun.ts +77 -0
- package/src/orm/index.shared-server.ts +22 -0
- package/src/orm/index.shared.ts +15 -0
- package/src/orm/index.ts +19 -39
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
- package/src/orm/services/Repository.ts +8 -0
- package/src/redis/index.bun.ts +35 -0
- package/src/redis/providers/BunRedisProvider.ts +12 -43
- package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
- package/src/redis/providers/NodeRedisProvider.ts +16 -34
- package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
- package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
- package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
- package/src/security/index.browser.ts +5 -0
- package/src/security/index.ts +90 -7
- package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
- package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
- package/src/security/primitives/$role.ts +5 -5
- package/src/security/primitives/$serviceAccount.spec.ts +5 -5
- package/src/security/primitives/$serviceAccount.ts +3 -3
- package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
- package/src/server/auth/primitives/$auth.ts +10 -10
- package/src/server/auth/primitives/$authCredentials.ts +3 -3
- package/src/server/auth/primitives/$authGithub.ts +3 -3
- package/src/server/auth/primitives/$authGoogle.ts +3 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
- package/src/server/cache/providers/ServerCacheProvider.ts +1 -1
- package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
- package/src/server/core/providers/NodeHttpServerProvider.ts +25 -6
- package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
- package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
- package/src/server/core/providers/ServerProvider.ts +144 -21
- package/src/server/core/providers/ServerRouterProvider.ts +259 -115
- package/src/server/core/providers/ServerTimingProvider.ts +2 -2
- package/src/server/links/index.ts +1 -1
- package/src/server/links/providers/LinkProvider.ts +1 -1
- package/src/server/swagger/index.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
- package/src/sms/providers/LocalSmsProvider.ts +8 -7
- package/src/vite/helpers/boot.ts +28 -17
- package/src/vite/tasks/buildServer.ts +12 -1
- package/src/vite/tasks/devServer.ts +3 -1
- package/src/vite/tasks/generateCloudflare.ts +7 -0
- package/dist/server/security/index.browser.js +0 -13
- package/dist/server/security/index.browser.js.map +0 -1
- package/dist/server/security/index.d.ts +0 -173
- package/dist/server/security/index.d.ts.map +0 -1
- package/dist/server/security/index.js +0 -311
- package/dist/server/security/index.js.map +0 -1
- package/src/cli/assets/appRouterTs.ts +0 -9
- package/src/cli/assets/mainTs.ts +0 -13
- package/src/server/security/index.browser.ts +0 -10
- package/src/server/security/index.ts +0 -94
- /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
- /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
|
@@ -10,7 +10,7 @@ import { BadRequestError, ConflictError, HttpError } from "alepha/server";
|
|
|
10
10
|
import { $client } from "alepha/server/links";
|
|
11
11
|
import type { UserEntity } from "../entities/users.ts";
|
|
12
12
|
import { UserNotifications } from "../notifications/UserNotifications.ts";
|
|
13
|
-
import {
|
|
13
|
+
import { RealmProvider } from "../providers/RealmProvider.ts";
|
|
14
14
|
import type { CompleteRegistrationRequest } from "../schemas/completeRegistrationRequestSchema.ts";
|
|
15
15
|
import type { RegisterRequest } from "../schemas/registerRequestSchema.ts";
|
|
16
16
|
import type { RegistrationIntentResponse } from "../schemas/registrationIntentResponseSchema.ts";
|
|
@@ -45,7 +45,7 @@ export class RegistrationService {
|
|
|
45
45
|
protected readonly cryptoProvider = $inject(CryptoProvider);
|
|
46
46
|
protected readonly verificationController = $client<VerificationController>();
|
|
47
47
|
protected readonly userNotifications = $inject(UserNotifications);
|
|
48
|
-
protected readonly
|
|
48
|
+
protected readonly realmProvider = $inject(RealmProvider);
|
|
49
49
|
protected readonly auditService = $inject(AuditService);
|
|
50
50
|
|
|
51
51
|
protected readonly intentCache = $cache<RegistrationIntent>({
|
|
@@ -69,9 +69,7 @@ export class RegistrationService {
|
|
|
69
69
|
userRealmName,
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
const realmSettings =
|
|
73
|
-
this.userRealmProvider.getRealm(userRealmName).settings;
|
|
74
|
-
const userRepository = this.userRealmProvider.userRepository(userRealmName);
|
|
72
|
+
const realmSettings = this.realmProvider.getRealm(userRealmName).settings;
|
|
75
73
|
|
|
76
74
|
// Check if registration is allowed
|
|
77
75
|
if (realmSettings?.registrationAllowed === false) {
|
|
@@ -87,6 +85,22 @@ export class RegistrationService {
|
|
|
87
85
|
throw new BadRequestError("Username is required");
|
|
88
86
|
}
|
|
89
87
|
|
|
88
|
+
if (body.username) {
|
|
89
|
+
const usernameRegExp = realmSettings?.usernameRegExp;
|
|
90
|
+
if (usernameRegExp) {
|
|
91
|
+
const regex = new RegExp(usernameRegExp);
|
|
92
|
+
if (!regex.test(body.username)) {
|
|
93
|
+
this.log.debug("Registration rejected: username regex mismatch", {
|
|
94
|
+
userRealmName,
|
|
95
|
+
username: body.username,
|
|
96
|
+
});
|
|
97
|
+
throw new BadRequestError(
|
|
98
|
+
"Username does not meet the required format",
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
90
104
|
if (realmSettings?.emailRequired !== false && !body.email) {
|
|
91
105
|
this.log.debug("Registration rejected: email required", {
|
|
92
106
|
userRealmName,
|
|
@@ -189,9 +203,9 @@ export class RegistrationService {
|
|
|
189
203
|
}
|
|
190
204
|
|
|
191
205
|
const userRealmName = intent.realmName;
|
|
192
|
-
const userRepository = this.
|
|
206
|
+
const userRepository = this.realmProvider.userRepository(userRealmName);
|
|
193
207
|
const identityRepository =
|
|
194
|
-
this.
|
|
208
|
+
this.realmProvider.identityRepository(userRealmName);
|
|
195
209
|
|
|
196
210
|
// Validate email verification if required
|
|
197
211
|
if (intent.requirements.email) {
|
|
@@ -275,7 +289,7 @@ export class RegistrationService {
|
|
|
275
289
|
username: user.username,
|
|
276
290
|
});
|
|
277
291
|
|
|
278
|
-
const realm = this.
|
|
292
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
279
293
|
|
|
280
294
|
await this.auditService.recordUser("create", {
|
|
281
295
|
userId: user.id,
|
|
@@ -301,7 +315,7 @@ export class RegistrationService {
|
|
|
301
315
|
body: Pick<RegisterRequest, "username" | "email" | "phoneNumber">,
|
|
302
316
|
userRealmName?: string,
|
|
303
317
|
): Promise<void> {
|
|
304
|
-
const userRepository = this.
|
|
318
|
+
const userRepository = this.realmProvider.userRepository(userRealmName);
|
|
305
319
|
|
|
306
320
|
if (body.username) {
|
|
307
321
|
const existingUser = await userRepository
|
|
@@ -341,26 +355,23 @@ export class RegistrationService {
|
|
|
341
355
|
*/
|
|
342
356
|
protected async sendEmailVerification(email: string): Promise<void> {
|
|
343
357
|
this.log.debug("Sending email verification code", { email });
|
|
344
|
-
try {
|
|
345
|
-
const verification =
|
|
346
|
-
await this.verificationController.requestVerificationCode({
|
|
347
|
-
params: { type: "code" },
|
|
348
|
-
body: { target: email },
|
|
349
|
-
});
|
|
350
358
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
code: verification.token,
|
|
356
|
-
expiresInMinutes: Math.floor(verification.codeExpiration / 60),
|
|
357
|
-
},
|
|
359
|
+
const verification =
|
|
360
|
+
await this.verificationController.requestVerificationCode({
|
|
361
|
+
params: { type: "code" },
|
|
362
|
+
body: { target: email },
|
|
358
363
|
});
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
+
|
|
365
|
+
await this.userNotifications.emailVerification.push({
|
|
366
|
+
contact: email,
|
|
367
|
+
variables: {
|
|
368
|
+
email,
|
|
369
|
+
code: verification.token,
|
|
370
|
+
expiresInMinutes: Math.floor(verification.codeExpiration / 60),
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
this.log.debug("Email verification code sent", { email });
|
|
364
375
|
}
|
|
365
376
|
|
|
366
377
|
/**
|
|
@@ -2,15 +2,15 @@ import { $inject } from "alepha";
|
|
|
2
2
|
import { $logger } from "alepha/logger";
|
|
3
3
|
import type { Page } from "alepha/orm";
|
|
4
4
|
import type { SessionEntity } from "../entities/sessions.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { RealmProvider } from "../providers/RealmProvider.ts";
|
|
6
6
|
import type { SessionQuery } from "../schemas/sessionQuerySchema.ts";
|
|
7
7
|
|
|
8
8
|
export class SessionCrudService {
|
|
9
9
|
protected readonly log = $logger();
|
|
10
|
-
protected readonly
|
|
10
|
+
protected readonly realmProvider = $inject(RealmProvider);
|
|
11
11
|
|
|
12
12
|
public sessions(userRealmName?: string) {
|
|
13
|
-
return this.
|
|
13
|
+
return this.realmProvider.sessionRepository(userRealmName);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
import { describe, it } from "vitest";
|
|
8
8
|
import {
|
|
9
9
|
AlephaApiUsers,
|
|
10
|
+
RealmProvider,
|
|
10
11
|
SessionService,
|
|
11
|
-
UserRealmProvider,
|
|
12
12
|
UserService,
|
|
13
13
|
} from "../index.ts";
|
|
14
14
|
|
|
@@ -26,8 +26,8 @@ const setup = async (options?: { usernameEnabled?: boolean }) => {
|
|
|
26
26
|
|
|
27
27
|
// Configure realm settings if provided
|
|
28
28
|
if (options?.usernameEnabled) {
|
|
29
|
-
const
|
|
30
|
-
|
|
29
|
+
const realmProvider = alepha.inject(RealmProvider);
|
|
30
|
+
realmProvider.register("default", {
|
|
31
31
|
settings: {
|
|
32
32
|
usernameEnabled: true,
|
|
33
33
|
} as never,
|
|
@@ -14,7 +14,7 @@ import { type ServerRequest, UnauthorizedError } from "alepha/server";
|
|
|
14
14
|
import type { OAuth2Profile } from "alepha/server/auth";
|
|
15
15
|
import { $client } from "alepha/server/links";
|
|
16
16
|
import type { UserEntity } from "../entities/users.ts";
|
|
17
|
-
import {
|
|
17
|
+
import { RealmProvider } from "../providers/RealmProvider.ts";
|
|
18
18
|
|
|
19
19
|
export class SessionService {
|
|
20
20
|
protected readonly alepha = $inject(Alepha);
|
|
@@ -22,20 +22,20 @@ export class SessionService {
|
|
|
22
22
|
protected readonly dateTimeProvider = $inject(DateTimeProvider);
|
|
23
23
|
protected readonly cryptoProvider = $inject(CryptoProvider);
|
|
24
24
|
protected readonly log = $logger();
|
|
25
|
-
protected readonly
|
|
25
|
+
protected readonly realmProvider = $inject(RealmProvider);
|
|
26
26
|
protected readonly fileController = $client<FileController>();
|
|
27
27
|
protected readonly auditService = $inject(AuditService);
|
|
28
28
|
|
|
29
29
|
public users(userRealmName?: string) {
|
|
30
|
-
return this.
|
|
30
|
+
return this.realmProvider.userRepository(userRealmName);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
public sessions(userRealmName?: string) {
|
|
34
|
-
return this.
|
|
34
|
+
return this.realmProvider.sessionRepository(userRealmName);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
public identities(userRealmName?: string) {
|
|
38
|
-
return this.
|
|
38
|
+
return this.realmProvider.identityRepository(userRealmName);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -55,7 +55,7 @@ export class SessionService {
|
|
|
55
55
|
password: string,
|
|
56
56
|
userRealmName?: string,
|
|
57
57
|
): Promise<UserEntity> {
|
|
58
|
-
const { settings, name } = this.
|
|
58
|
+
const { settings, name } = this.realmProvider.getRealm(userRealmName);
|
|
59
59
|
const isEmail = username.includes("@");
|
|
60
60
|
const isPhone = /^[+\d][\d\s()-]+$/.test(username);
|
|
61
61
|
const isUsername = !isEmail && !isPhone;
|
|
@@ -70,6 +70,25 @@ export class SessionService {
|
|
|
70
70
|
where.realm = name;
|
|
71
71
|
|
|
72
72
|
if (settings.usernameEnabled !== false && isUsername) {
|
|
73
|
+
// validate username format if regex is provided
|
|
74
|
+
if (settings.usernameRegExp) {
|
|
75
|
+
const regex = new RegExp(settings.usernameRegExp);
|
|
76
|
+
if (!regex.test(username)) {
|
|
77
|
+
this.log.warn("Username does not match required format", {
|
|
78
|
+
provider,
|
|
79
|
+
username,
|
|
80
|
+
realm: name,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await this.auditService.recordAuth("login_failed", {
|
|
84
|
+
userRealm: name,
|
|
85
|
+
description: "Username does not match required format",
|
|
86
|
+
metadata: { provider, username },
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
throw new InvalidCredentialsError();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
73
92
|
where.username = username;
|
|
74
93
|
} else if (settings.emailEnabled !== false && isEmail) {
|
|
75
94
|
where.email = username;
|
|
@@ -237,7 +256,7 @@ export class SessionService {
|
|
|
237
256
|
userId: session.userId,
|
|
238
257
|
});
|
|
239
258
|
|
|
240
|
-
const { name } = this.
|
|
259
|
+
const { name } = this.realmProvider.getRealm(userRealmName);
|
|
241
260
|
|
|
242
261
|
await this.auditService.recordAuth("token_refresh", {
|
|
243
262
|
userId: user.id,
|
|
@@ -270,7 +289,7 @@ export class SessionService {
|
|
|
270
289
|
this.log.debug("Session deleted");
|
|
271
290
|
|
|
272
291
|
if (session) {
|
|
273
|
-
const { name } = this.
|
|
292
|
+
const { name } = this.realmProvider.getRealm(userRealmName);
|
|
274
293
|
|
|
275
294
|
await this.auditService.recordAuth("logout", {
|
|
276
295
|
userId: session.userId,
|
|
@@ -292,7 +311,7 @@ export class SessionService {
|
|
|
292
311
|
email: profile.email,
|
|
293
312
|
});
|
|
294
313
|
|
|
295
|
-
const realm = this.
|
|
314
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
296
315
|
const identities = this.identities(userRealmName);
|
|
297
316
|
const users = this.users(userRealmName);
|
|
298
317
|
|
|
@@ -7,7 +7,7 @@ import { BadRequestError } from "alepha/server";
|
|
|
7
7
|
import { $client } from "alepha/server/links";
|
|
8
8
|
import type { UserEntity } from "../entities/users.ts";
|
|
9
9
|
import { UserNotifications } from "../notifications/UserNotifications.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { RealmProvider } from "../providers/RealmProvider.ts";
|
|
11
11
|
import type { CreateUser } from "../schemas/createUserSchema.ts";
|
|
12
12
|
import type { UpdateUser } from "../schemas/updateUserSchema.ts";
|
|
13
13
|
import type { UserQuery } from "../schemas/userQuerySchema.ts";
|
|
@@ -16,11 +16,11 @@ export class UserService {
|
|
|
16
16
|
protected readonly log = $logger();
|
|
17
17
|
protected readonly verificationController = $client<VerificationController>();
|
|
18
18
|
protected readonly userNotifications = $inject(UserNotifications);
|
|
19
|
-
protected readonly
|
|
19
|
+
protected readonly realmProvider = $inject(RealmProvider);
|
|
20
20
|
protected readonly auditService = $inject(AuditService);
|
|
21
21
|
|
|
22
22
|
public users(userRealmName?: string) {
|
|
23
|
-
return this.
|
|
23
|
+
return this.realmProvider.userRepository(userRealmName);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -156,7 +156,7 @@ export class UserService {
|
|
|
156
156
|
|
|
157
157
|
this.log.info("Email verified", { email, userId: user.id, type });
|
|
158
158
|
|
|
159
|
-
const realm = this.
|
|
159
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
160
160
|
|
|
161
161
|
await this.auditService.recordUser("update", {
|
|
162
162
|
userId: user.id,
|
|
@@ -256,7 +256,7 @@ export class UserService {
|
|
|
256
256
|
userRealmName,
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
-
const realm = this.
|
|
259
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
260
260
|
|
|
261
261
|
// TODO: one query instead of 3
|
|
262
262
|
|
|
@@ -342,7 +342,7 @@ export class UserService {
|
|
|
342
342
|
const user = await this.users(userRealmName).updateById(id, data);
|
|
343
343
|
this.log.debug("User updated", { userId: id });
|
|
344
344
|
|
|
345
|
-
const realm = this.
|
|
345
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
346
346
|
|
|
347
347
|
// Build changes object showing what was updated
|
|
348
348
|
const changes: Record<string, { from: unknown; to: unknown }> = {};
|
|
@@ -382,7 +382,7 @@ export class UserService {
|
|
|
382
382
|
await this.users(userRealmName).deleteById(id);
|
|
383
383
|
this.log.info("User deleted", { userId: id });
|
|
384
384
|
|
|
385
|
-
const realm = this.
|
|
385
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
386
386
|
|
|
387
387
|
await this.auditService.recordUser("delete", {
|
|
388
388
|
userRealm: realm.name,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import { $inject, type Alepha } from "alepha";
|
|
3
2
|
import { DateTimeProvider, type DurationLike } from "alepha/datetime";
|
|
4
3
|
import { $logger } from "alepha/logger";
|
|
@@ -188,7 +187,7 @@ export class BatchProvider {
|
|
|
188
187
|
item: TItem,
|
|
189
188
|
): string {
|
|
190
189
|
// 1. Generate unique ID
|
|
191
|
-
const id = randomUUID();
|
|
190
|
+
const id = crypto.randomUUID();
|
|
192
191
|
|
|
193
192
|
// 2. Determine the partition key (with error handling)
|
|
194
193
|
let partitionKey: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access, readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { $inject } from "alepha";
|
|
4
|
+
import { $inject, AlephaError } from "alepha";
|
|
5
5
|
import { $command } from "alepha/command";
|
|
6
6
|
import { FileSystemProvider } from "alepha/file";
|
|
7
7
|
import type { InlineConfig } from "tsdown";
|
|
@@ -11,6 +11,7 @@ interface Module {
|
|
|
11
11
|
dependencies: string[];
|
|
12
12
|
native?: boolean;
|
|
13
13
|
browser?: boolean;
|
|
14
|
+
bun?: boolean;
|
|
14
15
|
node?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -24,13 +25,13 @@ export class AlephaPackageBuilderCli {
|
|
|
24
25
|
handler: async ({ run, root }) => {
|
|
25
26
|
const modules: Array<Module> = [];
|
|
26
27
|
|
|
27
|
-
const
|
|
28
|
-
const pkgData = JSON.parse(
|
|
28
|
+
const pkgBuffer = await this.fs.readFile("package.json");
|
|
29
|
+
const pkgData = JSON.parse(pkgBuffer.toString("utf-8"));
|
|
29
30
|
const packageName = pkgData.name as string;
|
|
30
31
|
|
|
31
32
|
await run("analyze modules", async () => {
|
|
32
33
|
modules.push(
|
|
33
|
-
...(await analyzeModules(join(root, this.src), packageName)),
|
|
34
|
+
...(await analyzeModules(this.fs.join(root, this.src), packageName)),
|
|
34
35
|
);
|
|
35
36
|
});
|
|
36
37
|
|
|
@@ -56,6 +57,10 @@ export class AlephaPackageBuilderCli {
|
|
|
56
57
|
pkgData.exports[path].browser = `./src/${item.name}/index.browser.ts`;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
if (item.bun) {
|
|
61
|
+
pkgData.exports[path].bun = `./src/${item.name}/index.bun.ts`;
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
pkgData.exports[path].import = `./src/${item.name}/index.ts`;
|
|
60
65
|
pkgData.exports[path].default = `./src/${item.name}/index.ts`;
|
|
61
66
|
}
|
|
@@ -72,21 +77,20 @@ export class AlephaPackageBuilderCli {
|
|
|
72
77
|
|
|
73
78
|
await this.fs.writeFile("package.json", JSON.stringify(pkgData, null, 2));
|
|
74
79
|
|
|
75
|
-
const tmpDir = join(root, "node_modules/.alepha");
|
|
80
|
+
const tmpDir = this.fs.join(root, "node_modules/.alepha");
|
|
76
81
|
await this.fs.mkdir(tmpDir, { recursive: true }).catch(() => {});
|
|
77
82
|
|
|
78
83
|
await this.fs.writeFile(
|
|
79
|
-
join(tmpDir, "module-dependencies.json"),
|
|
84
|
+
this.fs.join(tmpDir, "module-dependencies.json"),
|
|
80
85
|
JSON.stringify(modules, null, 2),
|
|
81
86
|
);
|
|
82
87
|
|
|
83
|
-
const
|
|
84
|
-
join(root, "../../tsconfig.json"),
|
|
85
|
-
"utf-8",
|
|
88
|
+
const tsconfigBuffer = await this.fs.readFile(
|
|
89
|
+
this.fs.join(root, "../../tsconfig.json"),
|
|
86
90
|
);
|
|
87
91
|
|
|
88
92
|
const external: string[] = Object.keys(
|
|
89
|
-
JSON.parse(
|
|
93
|
+
JSON.parse(tsconfigBuffer.toString("utf-8")).compilerOptions.paths,
|
|
90
94
|
);
|
|
91
95
|
|
|
92
96
|
external.push("bun");
|
|
@@ -96,11 +100,11 @@ export class AlephaPackageBuilderCli {
|
|
|
96
100
|
|
|
97
101
|
const build = async (item: Module) => {
|
|
98
102
|
const entries: InlineConfig[] = [];
|
|
99
|
-
const src = join(root, this.src, item.name);
|
|
100
|
-
const dest = join(root, this.dist, item.name);
|
|
103
|
+
const src = this.fs.join(root, this.src, item.name);
|
|
104
|
+
const dest = this.fs.join(root, this.dist, item.name);
|
|
101
105
|
|
|
102
106
|
entries.push({
|
|
103
|
-
entry: join(src, "index.ts"),
|
|
107
|
+
entry: this.fs.join(src, "index.ts"),
|
|
104
108
|
outDir: dest,
|
|
105
109
|
format: ["esm"],
|
|
106
110
|
sourcemap: true,
|
|
@@ -109,13 +113,12 @@ export class AlephaPackageBuilderCli {
|
|
|
109
113
|
external,
|
|
110
114
|
dts: {
|
|
111
115
|
sourcemap: true,
|
|
112
|
-
resolve: false,
|
|
113
116
|
},
|
|
114
117
|
});
|
|
115
118
|
|
|
116
119
|
if (item.native) {
|
|
117
120
|
entries.push({
|
|
118
|
-
entry: join(src, "index.native.ts"),
|
|
121
|
+
entry: this.fs.join(src, "index.native.ts"),
|
|
119
122
|
outDir: dest,
|
|
120
123
|
platform: "neutral",
|
|
121
124
|
sourcemap: true,
|
|
@@ -126,7 +129,7 @@ export class AlephaPackageBuilderCli {
|
|
|
126
129
|
|
|
127
130
|
if (item.browser) {
|
|
128
131
|
entries.push({
|
|
129
|
-
entry: join(src, "index.browser.ts"),
|
|
132
|
+
entry: this.fs.join(src, "index.browser.ts"),
|
|
130
133
|
outDir: dest,
|
|
131
134
|
platform: "browser",
|
|
132
135
|
sourcemap: true,
|
|
@@ -135,7 +138,19 @@ export class AlephaPackageBuilderCli {
|
|
|
135
138
|
});
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
if (item.bun) {
|
|
142
|
+
entries.push({
|
|
143
|
+
entry: this.fs.join(src, "index.bun.ts"),
|
|
144
|
+
outDir: dest,
|
|
145
|
+
platform: "node",
|
|
146
|
+
sourcemap: true,
|
|
147
|
+
fixedExtension: false,
|
|
148
|
+
dts: false,
|
|
149
|
+
external,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const config = this.fs.join(
|
|
139
154
|
tmpDir,
|
|
140
155
|
`tsdown-${item.name.replace("/", "-")}.config.js`,
|
|
141
156
|
);
|
|
@@ -261,7 +276,9 @@ function detectCircularDependencies(modules: Module[]): void {
|
|
|
261
276
|
for (const module of modules) {
|
|
262
277
|
const cycle = hasCycle(module.name);
|
|
263
278
|
if (cycle) {
|
|
264
|
-
throw new
|
|
279
|
+
throw new AlephaError(
|
|
280
|
+
`Circular dependency detected: ${cycle.join(" -> ")}`,
|
|
281
|
+
);
|
|
265
282
|
}
|
|
266
283
|
}
|
|
267
284
|
}
|
|
@@ -287,13 +304,14 @@ export async function analyzeModules(
|
|
|
287
304
|
// This is a module
|
|
288
305
|
const dependencies = new Set<string>();
|
|
289
306
|
|
|
290
|
-
// Check for browser/node entry points
|
|
307
|
+
// Check for browser/node/bun entry points
|
|
291
308
|
const hasBrowser = await fileExists(
|
|
292
309
|
join(modulePath, "index.browser.ts"),
|
|
293
310
|
);
|
|
294
311
|
const hasNative = await fileExists(
|
|
295
312
|
join(modulePath, "index.native.ts"),
|
|
296
313
|
);
|
|
314
|
+
const hasBun = await fileExists(join(modulePath, "index.bun.ts"));
|
|
297
315
|
const hasNode = await fileExists(join(modulePath, "index.node.ts"));
|
|
298
316
|
|
|
299
317
|
// Get all .ts/.tsx files in this module
|
|
@@ -328,6 +346,7 @@ export async function analyzeModules(
|
|
|
328
346
|
|
|
329
347
|
if (hasNative) module.native = true;
|
|
330
348
|
if (hasBrowser) module.browser = true;
|
|
349
|
+
if (hasBun) module.bun = true;
|
|
331
350
|
if (hasNode) module.node = true;
|
|
332
351
|
|
|
333
352
|
modules.push(module);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const apiHelloControllerTs = () => `
|
|
2
|
+
import { t } from "alepha";
|
|
3
|
+
import { $action } from "alepha/server";
|
|
4
|
+
|
|
5
|
+
export class HelloController {
|
|
6
|
+
hello = $action({
|
|
7
|
+
path: "/hello",
|
|
8
|
+
schema: {
|
|
9
|
+
response: t.object({
|
|
10
|
+
message: t.string(),
|
|
11
|
+
}),
|
|
12
|
+
},
|
|
13
|
+
handler: () => ({
|
|
14
|
+
message: "Hello, Alepha!",
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
`.trim();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ApiIndexTsOptions {
|
|
2
|
+
appName?: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const apiIndexTs = (options: ApiIndexTsOptions = {}) => {
|
|
6
|
+
const { appName = "app" } = options;
|
|
7
|
+
return `
|
|
8
|
+
import { $module } from "alepha";
|
|
9
|
+
import { HelloController } from "./controllers/HelloController.ts";
|
|
10
|
+
|
|
11
|
+
export const ApiModule = $module({
|
|
12
|
+
name: "${appName}.api",
|
|
13
|
+
services: [HelloController],
|
|
14
|
+
});
|
|
15
|
+
`.trim();
|
|
16
|
+
};
|