alepha 0.15.4 → 0.15.5
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 +26 -11
- package/dist/api/audits/index.d.ts +3 -3
- package/dist/api/audits/index.js +3 -3
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +3 -3
- package/dist/api/files/index.js +3 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +47 -4
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +100 -5
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +3 -3
- package/dist/api/keys/index.js +3 -3
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +3 -3
- package/dist/api/notifications/index.js +3 -3
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +263 -263
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +31 -30
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +373 -67
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +273 -72
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +3 -3
- package/dist/api/verifications/index.js +3 -3
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +7 -7
- package/dist/batch/index.js +3 -3
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +3 -3
- package/dist/bucket/index.js +6 -6
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +3 -3
- package/dist/cache/core/index.js +3 -3
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cli/index.d.ts +5607 -20
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +103 -89
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +11 -4
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +8 -6
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +4 -8
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +3 -3
- package/dist/datetime/index.js +3 -3
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +3 -3
- package/dist/email/index.js +8 -8
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +3 -3
- package/dist/fake/index.js +3 -3
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +3 -3
- package/dist/lock/core/index.js +3 -3
- package/dist/lock/core/index.js.map +1 -1
- package/dist/logger/index.d.ts +3 -3
- package/dist/logger/index.js +6 -3
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +3 -3
- package/dist/mcp/index.js +3 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.d.ts +12 -12
- package/dist/orm/index.js +4 -4
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +3 -3
- package/dist/queue/core/index.js +3 -3
- package/dist/queue/core/index.js.map +1 -1
- package/dist/react/auth/index.d.ts +3 -3
- package/dist/react/auth/index.js +3 -3
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.d.ts +3 -3
- package/dist/react/core/index.js +3 -3
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +3 -3
- package/dist/react/form/index.js +3 -3
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.d.ts +3 -3
- package/dist/react/head/index.js +3 -3
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +3 -3
- package/dist/react/i18n/index.js +3 -3
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.css +337 -0
- package/dist/react/intro/index.css.map +1 -0
- package/dist/react/intro/index.d.ts +10 -0
- package/dist/react/intro/index.d.ts.map +1 -0
- package/dist/react/intro/index.js +222 -0
- package/dist/react/intro/index.js.map +1 -0
- package/dist/react/router/index.browser.js +2 -2
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +1 -1
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +5 -5
- package/dist/react/router/index.js.map +1 -1
- package/dist/redis/index.d.ts +17 -17
- package/dist/redis/index.js +3 -3
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +3 -3
- package/dist/retry/index.js +3 -3
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +3 -3
- package/dist/scheduler/index.js +3 -3
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/security/index.js +5 -5
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +3 -3
- package/dist/server/auth/index.js +3 -3
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +3 -3
- package/dist/server/cache/index.js +3 -3
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +3 -3
- package/dist/server/compress/index.js +3 -3
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +3 -3
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +5 -16
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +13 -29
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +3 -3
- package/dist/server/cors/index.js +3 -3
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/health/index.d.ts +20 -20
- package/dist/server/health/index.js +3 -3
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +3 -3
- package/dist/server/helmet/index.js +3 -3
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.d.ts +42 -42
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +3 -3
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +3 -3
- package/dist/server/metrics/index.js +3 -3
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +3 -3
- package/dist/server/multipart/index.js +3 -3
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +3 -3
- package/dist/server/proxy/index.js +3 -3
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +3 -3
- package/dist/server/rate-limit/index.js +3 -3
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +3 -3
- package/dist/server/static/index.js +6 -6
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +3 -3
- package/dist/server/swagger/index.js +6 -6
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -3
- package/dist/sms/index.js +6 -6
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.d.ts +3 -3
- package/dist/system/index.js +3 -3
- package/dist/system/index.js.map +1 -1
- package/dist/thread/index.d.ts +3 -3
- package/dist/thread/index.js +3 -3
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +3 -3
- package/dist/topic/core/index.js +3 -3
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts +6284 -3
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/websocket/index.d.ts +3 -3
- package/dist/websocket/index.js +3 -3
- package/dist/websocket/index.js.map +1 -1
- package/package.json +7 -2
- package/src/api/audits/index.ts +3 -3
- package/src/api/files/index.ts +3 -3
- package/src/api/jobs/controllers/AdminJobController.ts +15 -2
- package/src/api/jobs/index.ts +4 -3
- package/src/api/jobs/services/JobAudits.spec.ts +89 -0
- package/src/api/jobs/services/JobAudits.ts +101 -0
- package/src/api/keys/index.ts +3 -3
- package/src/api/notifications/index.ts +3 -3
- package/src/api/parameters/index.ts +5 -3
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1 -1
- package/src/api/users/__tests__/ApiKeys.spec.ts +1 -1
- package/src/api/users/__tests__/EmailVerification.spec.ts +16 -1
- package/src/api/users/__tests__/PasswordReset.spec.ts +11 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -0
- package/src/api/users/index.ts +8 -9
- package/src/api/users/primitives/$realm.ts +117 -19
- package/src/api/users/providers/RealmProvider.ts +15 -7
- package/src/api/users/services/CredentialService.spec.ts +11 -0
- package/src/api/users/services/CredentialService.ts +47 -24
- package/src/api/users/services/IdentityService.ts +12 -4
- package/src/api/users/services/RegistrationService.spec.ts +11 -0
- package/src/api/users/services/RegistrationService.ts +33 -12
- package/src/api/users/services/SessionService.ts +83 -12
- package/src/api/users/services/UserAudits.ts +47 -0
- package/src/api/users/services/UserFiles.ts +19 -0
- package/src/api/users/services/UserJobs.spec.ts +107 -0
- package/src/api/users/services/UserJobs.ts +62 -0
- package/src/api/users/services/UserParameters.ts +23 -0
- package/src/api/users/services/UserService.ts +34 -17
- package/src/api/verifications/index.ts +3 -3
- package/src/batch/index.ts +3 -3
- package/src/bucket/index.ts +3 -3
- package/src/cache/core/index.ts +3 -3
- package/src/cli/commands/db.ts +9 -0
- package/src/cli/commands/init.spec.ts +2 -17
- package/src/cli/commands/init.ts +37 -1
- package/src/cli/providers/ViteDevServerProvider.ts +5 -2
- package/src/cli/services/AlephaCliUtils.ts +17 -0
- package/src/cli/services/PackageManagerUtils.ts +15 -1
- package/src/cli/services/ProjectScaffolder.ts +8 -13
- package/src/cli/templates/agentMd.ts +2 -25
- package/src/cli/templates/apiAppSecurityTs.ts +37 -2
- package/src/cli/templates/mainCss.ts +2 -32
- package/src/cli/templates/webAppRouterTs.ts +5 -5
- package/src/cli/templates/webHomeComponentTsx.ts +10 -0
- package/src/command/helpers/Runner.ts +14 -1
- package/src/command/index.ts +3 -3
- package/src/core/helpers/primitive.ts +0 -5
- package/src/core/index.ts +3 -3
- package/src/datetime/index.ts +3 -3
- package/src/email/index.ts +3 -3
- package/src/email/providers/LocalEmailProvider.ts +2 -2
- package/src/fake/index.ts +3 -3
- package/src/lock/core/index.ts +3 -3
- package/src/logger/index.ts +3 -3
- package/src/logger/providers/PrettyFormatterProvider.ts +7 -0
- package/src/mcp/index.ts +3 -3
- package/src/orm/index.ts +3 -3
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
- package/src/queue/core/index.ts +3 -3
- package/src/react/auth/index.ts +3 -3
- package/src/react/core/index.ts +3 -3
- package/src/react/form/index.ts +3 -3
- package/src/react/head/index.ts +3 -3
- package/src/react/i18n/index.ts +3 -3
- package/src/react/intro/components/GettingStarted.css +334 -0
- package/src/react/intro/components/GettingStarted.tsx +276 -0
- package/src/react/intro/index.ts +1 -0
- package/src/react/router/index.browser.ts +2 -0
- package/src/react/router/index.ts +2 -0
- package/src/redis/index.ts +3 -3
- package/src/retry/index.ts +3 -3
- package/src/router/index.ts +3 -3
- package/src/scheduler/index.ts +3 -3
- package/src/security/index.ts +3 -3
- package/src/security/providers/JwtProvider.ts +2 -2
- package/src/server/auth/index.ts +3 -3
- package/src/server/cache/index.ts +3 -3
- package/src/server/compress/index.ts +3 -3
- package/src/server/cookies/index.ts +3 -3
- package/src/server/core/index.ts +3 -3
- package/src/server/core/primitives/$action.spec.ts +3 -2
- package/src/server/core/primitives/$action.ts +6 -2
- package/src/server/core/providers/NodeHttpServerProvider.ts +2 -15
- package/src/server/core/providers/ServerProvider.ts +4 -2
- package/src/server/core/providers/ServerRouterProvider.ts +5 -27
- package/src/server/cors/index.ts +3 -3
- package/src/server/health/index.ts +3 -3
- package/src/server/helmet/index.ts +3 -3
- package/src/server/links/index.ts +3 -3
- package/src/server/metrics/index.ts +3 -3
- package/src/server/multipart/index.ts +3 -3
- package/src/server/proxy/index.ts +3 -3
- package/src/server/rate-limit/index.ts +3 -3
- package/src/server/static/index.ts +3 -3
- package/src/server/swagger/index.ts +3 -3
- package/src/sms/index.ts +3 -3
- package/src/system/index.ts +3 -3
- package/src/thread/index.ts +3 -3
- package/src/topic/core/index.ts +3 -3
- package/src/websocket/index.ts +3 -3
- package/src/cli/templates/webHelloComponentTsx.ts +0 -30
- /package/src/api/users/{notifications → services}/UserNotifications.ts +0 -0
package/dist/api/users/index.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { $atom, $context, $inject, $module, Alepha, AlephaError, Json, isFileLike, t } from "alepha";
|
|
2
|
-
import { $notification, AlephaApiNotifications } from "alepha/api/notifications";
|
|
3
|
-
import { AlephaApiVerification } from "alepha/api/verifications";
|
|
4
2
|
import { AlephaEmail } from "alepha/email";
|
|
5
3
|
import { AlephaServerCompress } from "alepha/server/compress";
|
|
6
4
|
import { AlephaServerHelmet } from "alepha/server/helmet";
|
|
7
5
|
import { $action, BadRequestError, ConflictError, ForbiddenError, HttpError, NotFoundError, UnauthorizedError, okSchema } from "alepha/server";
|
|
8
6
|
import { $entity, $repository, db, pageQuerySchema, parseQueryString, sql } from "alepha/orm";
|
|
9
|
-
import { AlephaApiAudits, AuditService } from "alepha/api/audits";
|
|
10
7
|
import { $logger } from "alepha/logger";
|
|
11
|
-
import {
|
|
8
|
+
import { AuditService } from "alepha/api/audits";
|
|
12
9
|
import { $client } from "alepha/server/links";
|
|
10
|
+
import { $notification } from "alepha/api/notifications";
|
|
13
11
|
import { $authCredentials, $authGithub, $authGoogle, ServerAuthProvider, authenticationProviderSchema } from "alepha/server/auth";
|
|
14
12
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
15
13
|
import { $cache } from "alepha/cache";
|
|
@@ -21,8 +19,10 @@ import { access, copyFile, cp, mkdir, readFile, readdir, rename, rm, stat, write
|
|
|
21
19
|
import { PassThrough, Readable } from "node:stream";
|
|
22
20
|
import { fileURLToPath } from "node:url";
|
|
23
21
|
import { exec, spawn } from "node:child_process";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
22
|
+
import { AlephaApiVerification } from "alepha/api/verifications";
|
|
23
|
+
import { $bucket } from "alepha/bucket";
|
|
24
|
+
import { $job } from "alepha/api/jobs";
|
|
25
|
+
import { $config } from "alepha/api/parameters";
|
|
26
26
|
|
|
27
27
|
//#region ../../src/api/users/schemas/identityQuerySchema.ts
|
|
28
28
|
const identityQuerySchema = t.extend(pageQuerySchema, {
|
|
@@ -112,6 +112,8 @@ const realmAuthSettingsAtom = $atom({
|
|
|
112
112
|
firstNameLastNameEnabled: t.boolean({ description: "Enable first and last name for user accounts" }),
|
|
113
113
|
firstNameLastNameRequired: t.boolean({ description: "Require first and last name for user accounts" }),
|
|
114
114
|
resetPasswordAllowed: t.boolean({ description: "Enable forgot password functionality" }),
|
|
115
|
+
adminEmails: t.array(t.email(), { description: "List of email addresses that are automatically promoted to admin role on login" }),
|
|
116
|
+
adminUsernames: t.array(t.text(), { description: "List of usernames that are automatically promoted to admin role on login" }),
|
|
115
117
|
passwordPolicy: t.object({
|
|
116
118
|
minLength: t.integer({
|
|
117
119
|
description: "Minimum password length",
|
|
@@ -138,6 +140,8 @@ const realmAuthSettingsAtom = $atom({
|
|
|
138
140
|
resetPasswordAllowed: false,
|
|
139
141
|
firstNameLastNameEnabled: false,
|
|
140
142
|
firstNameLastNameRequired: false,
|
|
143
|
+
adminEmails: [],
|
|
144
|
+
adminUsernames: [],
|
|
141
145
|
passwordPolicy: {
|
|
142
146
|
minLength: 8,
|
|
143
147
|
requireUppercase: true,
|
|
@@ -181,16 +185,17 @@ var RealmProvider = class {
|
|
|
181
185
|
defaultSessions = $repository(sessions);
|
|
182
186
|
defaultUsers = $repository(users);
|
|
183
187
|
realms = /* @__PURE__ */ new Map();
|
|
184
|
-
avatars = $bucket({
|
|
185
|
-
maxSize: 5 * 1024 * 1024,
|
|
186
|
-
mimeTypes: [
|
|
187
|
-
"image/jpeg",
|
|
188
|
-
"image/png",
|
|
189
|
-
"image/gif",
|
|
190
|
-
"image/webp"
|
|
191
|
-
]
|
|
192
|
-
});
|
|
193
188
|
register(realmName, realmOptions = {}) {
|
|
189
|
+
const features = {
|
|
190
|
+
jobs: false,
|
|
191
|
+
notifications: false,
|
|
192
|
+
apiKeys: false,
|
|
193
|
+
parameters: false,
|
|
194
|
+
files: false,
|
|
195
|
+
audits: false,
|
|
196
|
+
organizations: false,
|
|
197
|
+
...realmOptions.features
|
|
198
|
+
};
|
|
194
199
|
this.realms.set(realmName, {
|
|
195
200
|
name: realmName,
|
|
196
201
|
repositories: {
|
|
@@ -205,7 +210,8 @@ var RealmProvider = class {
|
|
|
205
210
|
...realmAuthSettingsAtom.options.default.passwordPolicy,
|
|
206
211
|
...realmOptions.settings?.passwordPolicy
|
|
207
212
|
}
|
|
208
|
-
}
|
|
213
|
+
},
|
|
214
|
+
features
|
|
209
215
|
});
|
|
210
216
|
return this.getRealm(realmName);
|
|
211
217
|
}
|
|
@@ -233,12 +239,45 @@ var RealmProvider = class {
|
|
|
233
239
|
}
|
|
234
240
|
};
|
|
235
241
|
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region ../../src/api/users/services/UserAudits.ts
|
|
244
|
+
/**
|
|
245
|
+
* User-specific audit wrapper service.
|
|
246
|
+
*
|
|
247
|
+
* This service wraps the core AuditService to provide user-related audit logging.
|
|
248
|
+
* It is lazy-loaded when the `audits` feature is enabled in the realm.
|
|
249
|
+
*/
|
|
250
|
+
var UserAudits = class {
|
|
251
|
+
auditService = $inject(AuditService);
|
|
252
|
+
/**
|
|
253
|
+
* Record a user-related audit event.
|
|
254
|
+
*/
|
|
255
|
+
recordUser(action, context) {
|
|
256
|
+
return this.auditService.recordUser(action, context);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Record an authentication-related audit event.
|
|
260
|
+
*/
|
|
261
|
+
recordAuth(action, context) {
|
|
262
|
+
return this.auditService.recordAuth(action, context);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Record a generic audit event.
|
|
266
|
+
*/
|
|
267
|
+
record(category, action, context) {
|
|
268
|
+
return this.auditService.record(category, action, context);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
236
272
|
//#endregion
|
|
237
273
|
//#region ../../src/api/users/services/IdentityService.ts
|
|
238
274
|
var IdentityService = class {
|
|
275
|
+
alepha = $inject(Alepha);
|
|
239
276
|
log = $logger();
|
|
240
277
|
realmProvider = $inject(RealmProvider);
|
|
241
|
-
|
|
278
|
+
userAudits(realmName) {
|
|
279
|
+
if (this.realmProvider.getRealm(realmName).features.audits) return this.alepha.inject(UserAudits);
|
|
280
|
+
}
|
|
242
281
|
identities(userRealmName) {
|
|
243
282
|
return this.realmProvider.identityRepository(userRealmName);
|
|
244
283
|
}
|
|
@@ -293,7 +332,7 @@ var IdentityService = class {
|
|
|
293
332
|
userId: identity.userId
|
|
294
333
|
});
|
|
295
334
|
const realm = this.realmProvider.getRealm(userRealmName);
|
|
296
|
-
await this.
|
|
335
|
+
await this.userAudits(userRealmName)?.recordUser("update", {
|
|
297
336
|
userRealm: realm.name,
|
|
298
337
|
resourceId: identity.userId,
|
|
299
338
|
description: `Identity provider disconnected: ${identity.provider}`,
|
|
@@ -541,7 +580,7 @@ const userQuerySchema = t.extend(pageQuerySchema, {
|
|
|
541
580
|
const userResourceSchema = users.schema;
|
|
542
581
|
|
|
543
582
|
//#endregion
|
|
544
|
-
//#region ../../src/api/users/
|
|
583
|
+
//#region ../../src/api/users/services/UserNotifications.ts
|
|
545
584
|
var UserNotifications = class {
|
|
546
585
|
passwordReset = $notification({
|
|
547
586
|
category: "security",
|
|
@@ -672,11 +711,16 @@ var UserNotifications = class {
|
|
|
672
711
|
//#endregion
|
|
673
712
|
//#region ../../src/api/users/services/UserService.ts
|
|
674
713
|
var UserService = class {
|
|
714
|
+
alepha = $inject(Alepha);
|
|
675
715
|
log = $logger();
|
|
676
716
|
verificationController = $client();
|
|
677
|
-
userNotifications = $inject(UserNotifications);
|
|
678
717
|
realmProvider = $inject(RealmProvider);
|
|
679
|
-
|
|
718
|
+
userAudits(realmName) {
|
|
719
|
+
if (this.realmProvider.getRealm(realmName).features.audits) return this.alepha.inject(UserAudits);
|
|
720
|
+
}
|
|
721
|
+
userNotifications(realmName) {
|
|
722
|
+
if (this.realmProvider.getRealm(realmName).features.notifications) return this.alepha.inject(UserNotifications);
|
|
723
|
+
}
|
|
680
724
|
users(userRealmName) {
|
|
681
725
|
return this.realmProvider.userRepository(userRealmName);
|
|
682
726
|
}
|
|
@@ -715,7 +759,7 @@ var UserService = class {
|
|
|
715
759
|
url.searchParams.set("email", email);
|
|
716
760
|
url.searchParams.set("token", verification.token);
|
|
717
761
|
const fullVerifyUrl = verifyUrl ? `${verifyUrl}${url.search}` : url.pathname + url.search;
|
|
718
|
-
await this.userNotifications
|
|
762
|
+
await this.userNotifications(userRealmName)?.emailVerificationLink.push({
|
|
719
763
|
contact: email,
|
|
720
764
|
variables: {
|
|
721
765
|
email,
|
|
@@ -728,7 +772,7 @@ var UserService = class {
|
|
|
728
772
|
userId: user.id
|
|
729
773
|
});
|
|
730
774
|
} else {
|
|
731
|
-
await this.userNotifications
|
|
775
|
+
await this.userNotifications(userRealmName)?.emailVerification.push({
|
|
732
776
|
contact: email,
|
|
733
777
|
variables: {
|
|
734
778
|
email,
|
|
@@ -783,7 +827,7 @@ var UserService = class {
|
|
|
783
827
|
type
|
|
784
828
|
});
|
|
785
829
|
const realm = this.realmProvider.getRealm(userRealmName);
|
|
786
|
-
await this.
|
|
830
|
+
await this.userAudits(userRealmName)?.recordUser("update", {
|
|
787
831
|
userId: user.id,
|
|
788
832
|
userEmail: email,
|
|
789
833
|
userRealm: realm.name,
|
|
@@ -875,7 +919,7 @@ var UserService = class {
|
|
|
875
919
|
username: user.username,
|
|
876
920
|
email: user.email
|
|
877
921
|
});
|
|
878
|
-
await this.
|
|
922
|
+
await this.userAudits(userRealmName)?.recordUser("create", {
|
|
879
923
|
userRealm: realm.name,
|
|
880
924
|
resourceId: user.id,
|
|
881
925
|
description: "User created",
|
|
@@ -905,7 +949,7 @@ var UserService = class {
|
|
|
905
949
|
to: data[key]
|
|
906
950
|
};
|
|
907
951
|
const isRoleChange = data.roles !== void 0 && JSON.stringify(before.roles) !== JSON.stringify(data.roles);
|
|
908
|
-
await this.
|
|
952
|
+
await this.userAudits(userRealmName)?.recordUser(isRoleChange ? "role_change" : "update", {
|
|
909
953
|
userRealm: realm.name,
|
|
910
954
|
resourceId: user.id,
|
|
911
955
|
description: isRoleChange ? "User roles changed" : `User updated: ${Object.keys(changes).join(", ")}`,
|
|
@@ -925,7 +969,7 @@ var UserService = class {
|
|
|
925
969
|
await this.users(userRealmName).deleteById(id);
|
|
926
970
|
this.log.info("User deleted", { userId: id });
|
|
927
971
|
const realm = this.realmProvider.getRealm(userRealmName);
|
|
928
|
-
await this.
|
|
972
|
+
await this.userAudits(userRealmName)?.recordUser("delete", {
|
|
929
973
|
userRealm: realm.name,
|
|
930
974
|
resourceId: id,
|
|
931
975
|
severity: "warning",
|
|
@@ -1180,13 +1224,18 @@ const registrationIntentResponseSchema = t.object({
|
|
|
1180
1224
|
//#region ../../src/api/users/services/CredentialService.ts
|
|
1181
1225
|
const INTENT_TTL_MINUTES$1 = 10;
|
|
1182
1226
|
var CredentialService = class {
|
|
1227
|
+
alepha = $inject(Alepha);
|
|
1183
1228
|
log = $logger();
|
|
1184
1229
|
cryptoProvider = $inject(CryptoProvider);
|
|
1185
1230
|
dateTimeProvider = $inject(DateTimeProvider);
|
|
1186
1231
|
verificationController = $client();
|
|
1187
|
-
userNotifications = $inject(UserNotifications);
|
|
1188
1232
|
realmProvider = $inject(RealmProvider);
|
|
1189
|
-
|
|
1233
|
+
userAudits(realmName) {
|
|
1234
|
+
if (this.realmProvider.getRealm(realmName).features.audits) return this.alepha.inject(UserAudits);
|
|
1235
|
+
}
|
|
1236
|
+
userNotifications(realmName) {
|
|
1237
|
+
if (this.realmProvider.getRealm(realmName).features.notifications) return this.alepha.inject(UserNotifications);
|
|
1238
|
+
}
|
|
1190
1239
|
intentCache = $cache({
|
|
1191
1240
|
name: "password-reset-intents",
|
|
1192
1241
|
ttl: [INTENT_TTL_MINUTES$1, "minutes"]
|
|
@@ -1241,7 +1290,7 @@ var CredentialService = class {
|
|
|
1241
1290
|
params: { type: "code" },
|
|
1242
1291
|
body: { target: email }
|
|
1243
1292
|
});
|
|
1244
|
-
await this.userNotifications
|
|
1293
|
+
await this.userNotifications(userRealmName)?.passwordReset.push({
|
|
1245
1294
|
contact: email,
|
|
1246
1295
|
variables: {
|
|
1247
1296
|
email,
|
|
@@ -1316,7 +1365,7 @@ var CredentialService = class {
|
|
|
1316
1365
|
email: intent.email
|
|
1317
1366
|
});
|
|
1318
1367
|
const realm = this.realmProvider.getRealm(intent.realmName);
|
|
1319
|
-
await this.
|
|
1368
|
+
await this.userAudits(intent.realmName)?.recordUser("update", {
|
|
1320
1369
|
userId: intent.userId,
|
|
1321
1370
|
userEmail: intent.email,
|
|
1322
1371
|
userRealm: realm.name,
|
|
@@ -1324,7 +1373,7 @@ var CredentialService = class {
|
|
|
1324
1373
|
description: "Password reset completed",
|
|
1325
1374
|
metadata: { email: intent.email }
|
|
1326
1375
|
});
|
|
1327
|
-
await this.
|
|
1376
|
+
await this.userAudits(intent.realmName)?.record("security", "sessions_invalidated", {
|
|
1328
1377
|
userId: intent.userId,
|
|
1329
1378
|
userEmail: intent.email,
|
|
1330
1379
|
userRealm: realm.name,
|
|
@@ -1375,7 +1424,7 @@ var CredentialService = class {
|
|
|
1375
1424
|
await this.identities(userRealmName).updateById(identity.id, { password: hashedPassword });
|
|
1376
1425
|
await this.sessions(userRealmName).deleteMany({ userId: { eq: user.id } });
|
|
1377
1426
|
const realm = this.realmProvider.getRealm(userRealmName);
|
|
1378
|
-
await this.
|
|
1427
|
+
await this.userAudits(userRealmName)?.recordUser("update", {
|
|
1379
1428
|
userId: user.id,
|
|
1380
1429
|
userEmail: email,
|
|
1381
1430
|
userRealm: realm.name,
|
|
@@ -1383,7 +1432,7 @@ var CredentialService = class {
|
|
|
1383
1432
|
description: "Password reset completed (legacy)",
|
|
1384
1433
|
metadata: { email }
|
|
1385
1434
|
});
|
|
1386
|
-
await this.
|
|
1435
|
+
await this.userAudits(userRealmName)?.record("security", "sessions_invalidated", {
|
|
1387
1436
|
userId: user.id,
|
|
1388
1437
|
userEmail: email,
|
|
1389
1438
|
userRealm: realm.name,
|
|
@@ -1398,17 +1447,22 @@ var CredentialService = class {
|
|
|
1398
1447
|
//#region ../../src/api/users/services/RegistrationService.ts
|
|
1399
1448
|
const INTENT_TTL_MINUTES = 10;
|
|
1400
1449
|
var RegistrationService = class {
|
|
1450
|
+
alepha = $inject(Alepha);
|
|
1401
1451
|
log = $logger();
|
|
1402
1452
|
dateTimeProvider = $inject(DateTimeProvider);
|
|
1403
1453
|
cryptoProvider = $inject(CryptoProvider);
|
|
1404
1454
|
verificationController = $client();
|
|
1405
|
-
userNotifications = $inject(UserNotifications);
|
|
1406
1455
|
realmProvider = $inject(RealmProvider);
|
|
1407
|
-
auditService = $inject(AuditService);
|
|
1408
1456
|
intentCache = $cache({
|
|
1409
1457
|
name: "registration-intents",
|
|
1410
1458
|
ttl: [INTENT_TTL_MINUTES, "minutes"]
|
|
1411
1459
|
});
|
|
1460
|
+
userAudits(realmName) {
|
|
1461
|
+
if (this.realmProvider.getRealm(realmName).features.audits) return this.alepha.inject(UserAudits);
|
|
1462
|
+
}
|
|
1463
|
+
userNotifications(realmName) {
|
|
1464
|
+
if (this.realmProvider.getRealm(realmName).features.notifications) return this.alepha.inject(UserNotifications);
|
|
1465
|
+
}
|
|
1412
1466
|
/**
|
|
1413
1467
|
* Phase 1: Create a registration intent.
|
|
1414
1468
|
*
|
|
@@ -1457,8 +1511,8 @@ var RegistrationService = class {
|
|
|
1457
1511
|
phone: realmSettings?.verifyPhoneRequired === true && !!body.phoneNumber,
|
|
1458
1512
|
captcha: false
|
|
1459
1513
|
};
|
|
1460
|
-
if (requirements.email && body.email) await this.sendEmailVerification(body.email);
|
|
1461
|
-
if (requirements.phone && body.phoneNumber) await this.sendPhoneVerification(body.phoneNumber);
|
|
1514
|
+
if (requirements.email && body.email) await this.sendEmailVerification(body.email, userRealmName);
|
|
1515
|
+
if (requirements.phone && body.phoneNumber) await this.sendPhoneVerification(body.phoneNumber, userRealmName);
|
|
1462
1516
|
const intentId = randomUUID();
|
|
1463
1517
|
const expiresAt = this.dateTimeProvider.now().add(INTENT_TTL_MINUTES, "minutes").toISOString();
|
|
1464
1518
|
const intent = {
|
|
@@ -1558,7 +1612,7 @@ var RegistrationService = class {
|
|
|
1558
1612
|
username: user.username
|
|
1559
1613
|
});
|
|
1560
1614
|
const realm = this.realmProvider.getRealm(userRealmName);
|
|
1561
|
-
await this.
|
|
1615
|
+
await this.userAudits(userRealmName)?.recordUser("create", {
|
|
1562
1616
|
userId: user.id,
|
|
1563
1617
|
userEmail: user.email ?? void 0,
|
|
1564
1618
|
userRealm: realm.name,
|
|
@@ -1600,13 +1654,13 @@ var RegistrationService = class {
|
|
|
1600
1654
|
/**
|
|
1601
1655
|
* Send email verification code.
|
|
1602
1656
|
*/
|
|
1603
|
-
async sendEmailVerification(email) {
|
|
1657
|
+
async sendEmailVerification(email, realmName) {
|
|
1604
1658
|
this.log.debug("Sending email verification code", { email });
|
|
1605
1659
|
const verification = await this.verificationController.requestVerificationCode({
|
|
1606
1660
|
params: { type: "code" },
|
|
1607
1661
|
body: { target: email }
|
|
1608
1662
|
});
|
|
1609
|
-
await this.userNotifications
|
|
1663
|
+
await this.userNotifications(realmName)?.emailVerification.push({
|
|
1610
1664
|
contact: email,
|
|
1611
1665
|
variables: {
|
|
1612
1666
|
email,
|
|
@@ -1619,14 +1673,14 @@ var RegistrationService = class {
|
|
|
1619
1673
|
/**
|
|
1620
1674
|
* Send phone verification code.
|
|
1621
1675
|
*/
|
|
1622
|
-
async sendPhoneVerification(phoneNumber) {
|
|
1676
|
+
async sendPhoneVerification(phoneNumber, realmName) {
|
|
1623
1677
|
this.log.debug("Sending phone verification code", { phoneNumber });
|
|
1624
1678
|
try {
|
|
1625
1679
|
const verification = await this.verificationController.requestVerificationCode({
|
|
1626
1680
|
params: { type: "code" },
|
|
1627
1681
|
body: { target: phoneNumber }
|
|
1628
1682
|
});
|
|
1629
|
-
await this.userNotifications
|
|
1683
|
+
await this.userNotifications(realmName)?.phoneVerification.push({
|
|
1630
1684
|
contact: phoneNumber,
|
|
1631
1685
|
variables: {
|
|
1632
1686
|
phoneNumber,
|
|
@@ -3710,9 +3764,9 @@ var ShellProvider = class {};
|
|
|
3710
3764
|
//#endregion
|
|
3711
3765
|
//#region ../../src/system/index.ts
|
|
3712
3766
|
/**
|
|
3713
|
-
* |
|
|
3714
|
-
*
|
|
3715
|
-
* |
|
|
3767
|
+
* | Stability | Since | Runtime |
|
|
3768
|
+
* |-----------|-------|---------|
|
|
3769
|
+
* | 3 - stable | 0.14.0 | node, bun, browser|
|
|
3716
3770
|
*
|
|
3717
3771
|
* System-level abstractions for portable code across runtimes.
|
|
3718
3772
|
*
|
|
@@ -3757,7 +3811,9 @@ var SessionService = class {
|
|
|
3757
3811
|
log = $logger();
|
|
3758
3812
|
realmProvider = $inject(RealmProvider);
|
|
3759
3813
|
fileController = $client();
|
|
3760
|
-
|
|
3814
|
+
userAudits(realmName) {
|
|
3815
|
+
if (this.realmProvider.getRealm(realmName).features.audits) return this.alepha.inject(UserAudits);
|
|
3816
|
+
}
|
|
3761
3817
|
users(userRealmName) {
|
|
3762
3818
|
return this.realmProvider.userRepository(userRealmName);
|
|
3763
3819
|
}
|
|
@@ -3768,6 +3824,40 @@ var SessionService = class {
|
|
|
3768
3824
|
return this.realmProvider.identityRepository(userRealmName);
|
|
3769
3825
|
}
|
|
3770
3826
|
/**
|
|
3827
|
+
* Check if user should be auto-promoted to admin based on adminEmails/adminUsernames settings.
|
|
3828
|
+
* If user matches and doesn't have admin role, promote them.
|
|
3829
|
+
*/
|
|
3830
|
+
async ensureAdminRole(user, userRealmName) {
|
|
3831
|
+
if (user.roles.includes("admin")) return false;
|
|
3832
|
+
const { settings, name } = this.realmProvider.getRealm(userRealmName);
|
|
3833
|
+
const adminEmails = settings.adminEmails ?? [];
|
|
3834
|
+
const adminUsernames = settings.adminUsernames ?? [];
|
|
3835
|
+
const isAdminByEmail = user.email && adminEmails.includes(user.email);
|
|
3836
|
+
const isAdminByUsername = user.username && adminUsernames.includes(user.username);
|
|
3837
|
+
if (!isAdminByEmail && !isAdminByUsername) return false;
|
|
3838
|
+
user.roles = [...user.roles.filter((r) => r !== "admin"), "admin"];
|
|
3839
|
+
await this.users(userRealmName).updateById(user.id, { roles: user.roles });
|
|
3840
|
+
const reason = isAdminByEmail ? "adminEmails" : "adminUsernames";
|
|
3841
|
+
this.log.info(`User auto-promoted to admin via ${reason} setting`, {
|
|
3842
|
+
userId: user.id,
|
|
3843
|
+
email: user.email,
|
|
3844
|
+
username: user.username,
|
|
3845
|
+
realm: name
|
|
3846
|
+
});
|
|
3847
|
+
await this.userAudits(userRealmName)?.recordUser("role_change", {
|
|
3848
|
+
userId: user.id,
|
|
3849
|
+
userEmail: user.email ?? void 0,
|
|
3850
|
+
userRealm: name,
|
|
3851
|
+
resourceId: user.id,
|
|
3852
|
+
description: `User auto-promoted to admin via ${reason} setting`,
|
|
3853
|
+
metadata: {
|
|
3854
|
+
addedRole: "admin",
|
|
3855
|
+
reason
|
|
3856
|
+
}
|
|
3857
|
+
});
|
|
3858
|
+
return true;
|
|
3859
|
+
}
|
|
3860
|
+
/**
|
|
3771
3861
|
* Random delay to prevent timing attacks (50-200ms)
|
|
3772
3862
|
* Uses cryptographically secure random number generation
|
|
3773
3863
|
*/
|
|
@@ -3796,7 +3886,7 @@ var SessionService = class {
|
|
|
3796
3886
|
username,
|
|
3797
3887
|
realm: name
|
|
3798
3888
|
});
|
|
3799
|
-
await this.
|
|
3889
|
+
await this.userAudits(userRealmName)?.recordAuth("login_failed", {
|
|
3800
3890
|
userRealm: name,
|
|
3801
3891
|
description: "Username does not match required format",
|
|
3802
3892
|
metadata: {
|
|
@@ -3816,7 +3906,7 @@ var SessionService = class {
|
|
|
3816
3906
|
username,
|
|
3817
3907
|
realm: name
|
|
3818
3908
|
});
|
|
3819
|
-
await this.
|
|
3909
|
+
await this.userAudits(userRealmName)?.recordAuth("login_failed", {
|
|
3820
3910
|
userRealm: name,
|
|
3821
3911
|
description: "Invalid login identifier format",
|
|
3822
3912
|
metadata: {
|
|
@@ -3833,7 +3923,7 @@ var SessionService = class {
|
|
|
3833
3923
|
username,
|
|
3834
3924
|
realm: name
|
|
3835
3925
|
});
|
|
3836
|
-
await this.
|
|
3926
|
+
await this.userAudits(userRealmName)?.recordAuth("login_failed", {
|
|
3837
3927
|
userRealm: name,
|
|
3838
3928
|
description: "User not found",
|
|
3839
3929
|
metadata: {
|
|
@@ -3863,7 +3953,7 @@ var SessionService = class {
|
|
|
3863
3953
|
username,
|
|
3864
3954
|
realm: name
|
|
3865
3955
|
});
|
|
3866
|
-
await this.
|
|
3956
|
+
await this.userAudits(userRealmName)?.recordAuth("login_failed", {
|
|
3867
3957
|
userRealm: name,
|
|
3868
3958
|
resourceId: user.id,
|
|
3869
3959
|
description: "Invalid password",
|
|
@@ -3874,7 +3964,7 @@ var SessionService = class {
|
|
|
3874
3964
|
});
|
|
3875
3965
|
throw new InvalidCredentialsError();
|
|
3876
3966
|
}
|
|
3877
|
-
await this.
|
|
3967
|
+
await this.userAudits(userRealmName)?.recordAuth("login", {
|
|
3878
3968
|
userId: user.id,
|
|
3879
3969
|
userEmail: user.email ?? void 0,
|
|
3880
3970
|
userRealm: name,
|
|
@@ -3885,6 +3975,7 @@ var SessionService = class {
|
|
|
3885
3975
|
username
|
|
3886
3976
|
}
|
|
3887
3977
|
});
|
|
3978
|
+
await this.ensureAdminRole(user, userRealmName);
|
|
3888
3979
|
return user;
|
|
3889
3980
|
} catch (error) {
|
|
3890
3981
|
if (error instanceof InvalidCredentialsError) throw error;
|
|
@@ -3931,6 +4022,7 @@ var SessionService = class {
|
|
|
3931
4022
|
throw new UnauthorizedError("Session expired");
|
|
3932
4023
|
}
|
|
3933
4024
|
const user = await this.users(userRealmName).findOne({ where: { id: { eq: session.userId } } });
|
|
4025
|
+
await this.ensureAdminRole(user, userRealmName);
|
|
3934
4026
|
this.log.debug("Session refreshed", {
|
|
3935
4027
|
sessionId: session.id,
|
|
3936
4028
|
userId: session.userId
|
|
@@ -3948,7 +4040,7 @@ var SessionService = class {
|
|
|
3948
4040
|
this.log.debug("Session deleted");
|
|
3949
4041
|
if (session) {
|
|
3950
4042
|
const { name } = this.realmProvider.getRealm(userRealmName);
|
|
3951
|
-
await this.
|
|
4043
|
+
await this.userAudits(userRealmName)?.recordAuth("logout", {
|
|
3952
4044
|
userId: session.userId,
|
|
3953
4045
|
userRealm: name,
|
|
3954
4046
|
sessionId: session.id,
|
|
@@ -3976,7 +4068,7 @@ var SessionService = class {
|
|
|
3976
4068
|
userId: identity.userId
|
|
3977
4069
|
});
|
|
3978
4070
|
const user = await users.findById(identity.userId);
|
|
3979
|
-
await this.
|
|
4071
|
+
await this.userAudits(userRealmName)?.recordAuth("login", {
|
|
3980
4072
|
userId: user.id,
|
|
3981
4073
|
userEmail: user.email ?? void 0,
|
|
3982
4074
|
userRealm: realm.name,
|
|
@@ -3987,6 +4079,7 @@ var SessionService = class {
|
|
|
3987
4079
|
providerUserId: profile.sub
|
|
3988
4080
|
}
|
|
3989
4081
|
});
|
|
4082
|
+
await this.ensureAdminRole(user, userRealmName);
|
|
3990
4083
|
return user;
|
|
3991
4084
|
}
|
|
3992
4085
|
if (!profile.email) {
|
|
@@ -4012,7 +4105,7 @@ var SessionService = class {
|
|
|
4012
4105
|
providerUserId: profile.sub,
|
|
4013
4106
|
userId: existing.id
|
|
4014
4107
|
});
|
|
4015
|
-
await this.
|
|
4108
|
+
await this.userAudits(userRealmName)?.recordAuth("login", {
|
|
4016
4109
|
userId: existing.id,
|
|
4017
4110
|
userEmail: existing.email ?? void 0,
|
|
4018
4111
|
userRealm: realm.name,
|
|
@@ -4024,6 +4117,7 @@ var SessionService = class {
|
|
|
4024
4117
|
linked: true
|
|
4025
4118
|
}
|
|
4026
4119
|
});
|
|
4120
|
+
await this.ensureAdminRole(existing, userRealmName);
|
|
4027
4121
|
return existing;
|
|
4028
4122
|
}
|
|
4029
4123
|
const user = await users.create({
|
|
@@ -4060,7 +4154,7 @@ var SessionService = class {
|
|
|
4060
4154
|
email: user.email,
|
|
4061
4155
|
username: user.username
|
|
4062
4156
|
});
|
|
4063
|
-
await this.
|
|
4157
|
+
await this.userAudits(userRealmName)?.recordUser("create", {
|
|
4064
4158
|
userId: user.id,
|
|
4065
4159
|
userEmail: user.email ?? void 0,
|
|
4066
4160
|
userRealm: realm.name,
|
|
@@ -4073,7 +4167,7 @@ var SessionService = class {
|
|
|
4073
4167
|
email: user.email
|
|
4074
4168
|
}
|
|
4075
4169
|
});
|
|
4076
|
-
await this.
|
|
4170
|
+
await this.userAudits(userRealmName)?.recordAuth("login", {
|
|
4077
4171
|
userId: user.id,
|
|
4078
4172
|
userEmail: user.email ?? void 0,
|
|
4079
4173
|
userRealm: realm.name,
|
|
@@ -4085,6 +4179,7 @@ var SessionService = class {
|
|
|
4085
4179
|
firstLogin: true
|
|
4086
4180
|
}
|
|
4087
4181
|
});
|
|
4182
|
+
await this.ensureAdminRole(user, userRealmName);
|
|
4088
4183
|
return user;
|
|
4089
4184
|
}
|
|
4090
4185
|
};
|
|
@@ -4519,9 +4614,9 @@ var ApiKeyController = class {
|
|
|
4519
4614
|
//#endregion
|
|
4520
4615
|
//#region ../../src/api/keys/index.ts
|
|
4521
4616
|
/**
|
|
4522
|
-
* |
|
|
4523
|
-
*
|
|
4524
|
-
* |
|
|
4617
|
+
* | Stability | Since | Runtime |
|
|
4618
|
+
* |-----------|-------|---------|
|
|
4619
|
+
* | 3 - stable | 0.11.0 | node, bun, workerd|
|
|
4525
4620
|
*
|
|
4526
4621
|
* API key management module for programmatic access.
|
|
4527
4622
|
*
|
|
@@ -4555,6 +4650,95 @@ const AlephaApiKeys = $module({
|
|
|
4555
4650
|
]
|
|
4556
4651
|
});
|
|
4557
4652
|
|
|
4653
|
+
//#endregion
|
|
4654
|
+
//#region ../../src/api/users/services/UserFiles.ts
|
|
4655
|
+
/**
|
|
4656
|
+
* User-specific file storage wrapper service.
|
|
4657
|
+
*
|
|
4658
|
+
* This service provides file storage for user-related files such as:
|
|
4659
|
+
* - User avatars/profile pictures
|
|
4660
|
+
*
|
|
4661
|
+
* It is lazy-loaded when the `files` feature is enabled in the realm.
|
|
4662
|
+
*/
|
|
4663
|
+
var UserFiles = class {
|
|
4664
|
+
/**
|
|
4665
|
+
* Bucket for user avatar storage.
|
|
4666
|
+
*/
|
|
4667
|
+
avatars = $bucket({
|
|
4668
|
+
maxSize: 5 * 1024 * 1024,
|
|
4669
|
+
mimeTypes: [
|
|
4670
|
+
"image/jpeg",
|
|
4671
|
+
"image/png",
|
|
4672
|
+
"image/gif",
|
|
4673
|
+
"image/webp"
|
|
4674
|
+
]
|
|
4675
|
+
});
|
|
4676
|
+
};
|
|
4677
|
+
|
|
4678
|
+
//#endregion
|
|
4679
|
+
//#region ../../src/api/users/services/UserJobs.ts
|
|
4680
|
+
/**
|
|
4681
|
+
* User-specific jobs wrapper service.
|
|
4682
|
+
*
|
|
4683
|
+
* This service handles user-related scheduled jobs such as:
|
|
4684
|
+
* - Session purge (cleaning up expired sessions)
|
|
4685
|
+
* - Verification code cleanup
|
|
4686
|
+
* - Inactive user notifications
|
|
4687
|
+
*
|
|
4688
|
+
* It is lazy-loaded when the `jobs` feature is enabled in the realm.
|
|
4689
|
+
*/
|
|
4690
|
+
var UserJobs = class {
|
|
4691
|
+
log = $logger();
|
|
4692
|
+
dateTimeProvider = $inject(DateTimeProvider);
|
|
4693
|
+
sessionRepository = $repository(sessions);
|
|
4694
|
+
/**
|
|
4695
|
+
* Purge expired sessions from the database.
|
|
4696
|
+
*
|
|
4697
|
+
* This job runs daily at 3:00 AM and removes all sessions
|
|
4698
|
+
* where the `expiresAt` timestamp has passed.
|
|
4699
|
+
*/
|
|
4700
|
+
purgeExpiredSessions = $job({
|
|
4701
|
+
name: "users.purgeExpiredSessions",
|
|
4702
|
+
description: "Remove expired user sessions from the database",
|
|
4703
|
+
cron: "0 3 * * *",
|
|
4704
|
+
handler: async () => {
|
|
4705
|
+
const now = this.dateTimeProvider.nowISOString();
|
|
4706
|
+
this.log.info("Starting expired sessions purge", { cutoffTime: now });
|
|
4707
|
+
const expiredSessions = await this.sessionRepository.findMany({ where: { expiresAt: { lt: now } } });
|
|
4708
|
+
if (expiredSessions.length === 0) {
|
|
4709
|
+
this.log.info("No expired sessions found");
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
this.log.info("Found expired sessions", { count: expiredSessions.length });
|
|
4713
|
+
const deletedIds = await this.sessionRepository.deleteMany({ expiresAt: { lt: now } });
|
|
4714
|
+
this.log.info("Expired sessions purged successfully", { deletedCount: deletedIds.length });
|
|
4715
|
+
}
|
|
4716
|
+
});
|
|
4717
|
+
};
|
|
4718
|
+
|
|
4719
|
+
//#endregion
|
|
4720
|
+
//#region ../../src/api/users/services/UserParameters.ts
|
|
4721
|
+
/**
|
|
4722
|
+
* User-specific configuration service.
|
|
4723
|
+
*
|
|
4724
|
+
* This service wraps the core ConfigStore to provide realm settings management.
|
|
4725
|
+
* It is lazy-loaded when the `parameters` feature is enabled in the realm.
|
|
4726
|
+
*/
|
|
4727
|
+
var UserParameters = class {
|
|
4728
|
+
/**
|
|
4729
|
+
* Realm authentication settings configuration.
|
|
4730
|
+
*
|
|
4731
|
+
* Controls user registration, login methods, verification requirements,
|
|
4732
|
+
* and password policies for the realm.
|
|
4733
|
+
*/
|
|
4734
|
+
realmSettings = $config({
|
|
4735
|
+
name: "alepha.api.users.realmSettings",
|
|
4736
|
+
description: "Realm authentication and registration settings",
|
|
4737
|
+
schema: realmAuthSettingsAtom.schema,
|
|
4738
|
+
default: realmAuthSettingsAtom.options.default
|
|
4739
|
+
});
|
|
4740
|
+
};
|
|
4741
|
+
|
|
4558
4742
|
//#endregion
|
|
4559
4743
|
//#region ../../src/api/users/primitives/$realm.ts
|
|
4560
4744
|
/**
|
|
@@ -4580,12 +4764,32 @@ const $realm = (options = {}) => {
|
|
|
4580
4764
|
if (options.settings.emailRequired) options.settings.emailEnabled = true;
|
|
4581
4765
|
if (options.settings.usernameRequired) options.settings.usernameEnabled = true;
|
|
4582
4766
|
if (options.settings.phoneRequired) options.settings.phoneEnabled = true;
|
|
4767
|
+
const features = {
|
|
4768
|
+
jobs: false,
|
|
4769
|
+
notifications: false,
|
|
4770
|
+
apiKeys: false,
|
|
4771
|
+
parameters: false,
|
|
4772
|
+
files: false,
|
|
4773
|
+
audits: false,
|
|
4774
|
+
organizations: false,
|
|
4775
|
+
...options.features
|
|
4776
|
+
};
|
|
4777
|
+
if (!features.notifications) {
|
|
4778
|
+
options.settings.verifyEmailRequired = false;
|
|
4779
|
+
options.settings.verifyPhoneRequired = false;
|
|
4780
|
+
options.settings.resetPasswordAllowed = false;
|
|
4781
|
+
}
|
|
4583
4782
|
const realmRegistration = realmProvider.register(name, options);
|
|
4584
|
-
alepha.with(
|
|
4585
|
-
alepha.with(
|
|
4586
|
-
alepha.with(
|
|
4783
|
+
if (features.files) alepha.with(UserFiles);
|
|
4784
|
+
if (features.audits) alepha.with(UserAudits);
|
|
4785
|
+
if (features.jobs) alepha.with(UserJobs);
|
|
4786
|
+
if (features.notifications) {
|
|
4787
|
+
alepha.with(AlephaApiVerification);
|
|
4788
|
+
alepha.with(UserNotifications);
|
|
4789
|
+
}
|
|
4790
|
+
if (features.parameters) alepha.with(UserParameters);
|
|
4587
4791
|
const customResolvers = [...options.issuer?.resolvers ?? []];
|
|
4588
|
-
if (
|
|
4792
|
+
if (features.apiKeys) {
|
|
4589
4793
|
alepha.with(AlephaApiKeys);
|
|
4590
4794
|
const apiKeyService = alepha.inject(ApiKeyService);
|
|
4591
4795
|
customResolvers.push(apiKeyService.createResolver());
|
|
@@ -4701,9 +4905,9 @@ const resetPasswordSchema = t.object({
|
|
|
4701
4905
|
//#endregion
|
|
4702
4906
|
//#region ../../src/api/users/index.ts
|
|
4703
4907
|
/**
|
|
4704
|
-
* |
|
|
4705
|
-
*
|
|
4706
|
-
* |
|
|
4908
|
+
* | Stability | Since | Runtime |
|
|
4909
|
+
* |-----------|-------|---------|
|
|
4910
|
+
* | 3 - stable | 0.5.0 | node, bun, workerd|
|
|
4707
4911
|
*
|
|
4708
4912
|
* Complete user management with multi-realm support for multi-tenant applications.
|
|
4709
4913
|
*
|
|
@@ -4722,8 +4926,6 @@ const resetPasswordSchema = t.object({
|
|
|
4722
4926
|
const AlephaApiUsers = $module({
|
|
4723
4927
|
name: "alepha.api.users",
|
|
4724
4928
|
services: [
|
|
4725
|
-
AlephaApiVerification,
|
|
4726
|
-
AlephaApiNotifications,
|
|
4727
4929
|
AlephaServerHelmet,
|
|
4728
4930
|
AlephaServerCompress,
|
|
4729
4931
|
AlephaEmail,
|
|
@@ -4738,11 +4940,10 @@ const AlephaApiUsers = $module({
|
|
|
4738
4940
|
AdminUserController,
|
|
4739
4941
|
AdminSessionController,
|
|
4740
4942
|
AdminIdentityController,
|
|
4741
|
-
RealmController
|
|
4742
|
-
UserNotifications
|
|
4943
|
+
RealmController
|
|
4743
4944
|
]
|
|
4744
4945
|
});
|
|
4745
4946
|
|
|
4746
4947
|
//#endregion
|
|
4747
|
-
export { $realm, AdminIdentityController, AdminSessionController, AdminUserController, AlephaApiUsers, CredentialService, DEFAULT_USER_REALM_NAME, IdentityService, RealmController, RealmProvider, RegistrationService, SessionCrudService, SessionService, UserController, UserService, completePasswordResetRequestSchema, completeRegistrationRequestSchema, createUserSchema, identities, identityQuerySchema, identityResourceSchema, loginSchema, passwordResetIntentResponseSchema, realmAuthSettingsAtom, realmConfigSchema, registerSchema, registrationIntentResponseSchema, resetPasswordRequestSchema, resetPasswordSchema, sessionQuerySchema, sessionResourceSchema, sessions, updateUserSchema, userQuerySchema, userResourceSchema, users };
|
|
4948
|
+
export { $realm, AdminIdentityController, AdminSessionController, AdminUserController, AlephaApiUsers, CredentialService, DEFAULT_USER_REALM_NAME, IdentityService, RealmController, RealmProvider, RegistrationService, SessionCrudService, SessionService, UserAudits, UserController, UserFiles, UserJobs, UserNotifications, UserParameters, UserService, completePasswordResetRequestSchema, completeRegistrationRequestSchema, createUserSchema, identities, identityQuerySchema, identityResourceSchema, loginSchema, passwordResetIntentResponseSchema, realmAuthSettingsAtom, realmConfigSchema, registerSchema, registrationIntentResponseSchema, resetPasswordRequestSchema, resetPasswordSchema, sessionQuerySchema, sessionResourceSchema, sessions, updateUserSchema, userQuerySchema, userResourceSchema, users };
|
|
4748
4949
|
//# sourceMappingURL=index.js.map
|