alepha 0.15.3 → 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.
Files changed (318) hide show
  1. package/README.md +26 -11
  2. package/dist/api/audits/index.d.ts +335 -335
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +11 -3
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +3 -3
  7. package/dist/api/files/index.js +4 -3
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +198 -155
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js +103 -5
  12. package/dist/api/jobs/index.js.map +1 -1
  13. package/dist/api/keys/index.d.ts +198 -198
  14. package/dist/api/keys/index.d.ts.map +1 -1
  15. package/dist/api/keys/index.js +3 -3
  16. package/dist/api/keys/index.js.map +1 -1
  17. package/dist/api/notifications/index.browser.js +1 -0
  18. package/dist/api/notifications/index.browser.js.map +1 -1
  19. package/dist/api/notifications/index.d.ts +3 -3
  20. package/dist/api/notifications/index.js +4 -3
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.d.ts +263 -263
  23. package/dist/api/parameters/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.js +41 -30
  25. package/dist/api/parameters/index.js.map +1 -1
  26. package/dist/api/users/index.d.ts +383 -77
  27. package/dist/api/users/index.d.ts.map +1 -1
  28. package/dist/api/users/index.js +284 -72
  29. package/dist/api/users/index.js.map +1 -1
  30. package/dist/api/verifications/index.d.ts +131 -131
  31. package/dist/api/verifications/index.d.ts.map +1 -1
  32. package/dist/api/verifications/index.js +3 -3
  33. package/dist/api/verifications/index.js.map +1 -1
  34. package/dist/batch/index.d.ts +3 -3
  35. package/dist/batch/index.js +3 -3
  36. package/dist/batch/index.js.map +1 -1
  37. package/dist/bucket/index.d.ts +3 -3
  38. package/dist/bucket/index.js +6 -6
  39. package/dist/bucket/index.js.map +1 -1
  40. package/dist/cache/core/index.d.ts +3 -3
  41. package/dist/cache/core/index.js +3 -3
  42. package/dist/cache/core/index.js.map +1 -1
  43. package/dist/cli/index.d.ts +5612 -20
  44. package/dist/cli/index.d.ts.map +1 -1
  45. package/dist/cli/index.js +122 -91
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/command/index.d.ts +11 -4
  48. package/dist/command/index.d.ts.map +1 -1
  49. package/dist/command/index.js +8 -6
  50. package/dist/command/index.js.map +1 -1
  51. package/dist/core/index.browser.js.map +1 -1
  52. package/dist/core/index.d.ts +4 -8
  53. package/dist/core/index.d.ts.map +1 -1
  54. package/dist/core/index.js +3 -3
  55. package/dist/core/index.js.map +1 -1
  56. package/dist/core/index.native.js.map +1 -1
  57. package/dist/datetime/index.d.ts +3 -3
  58. package/dist/datetime/index.js +3 -3
  59. package/dist/datetime/index.js.map +1 -1
  60. package/dist/email/index.d.ts +16 -16
  61. package/dist/email/index.d.ts.map +1 -1
  62. package/dist/email/index.js +10562 -10
  63. package/dist/email/index.js.map +1 -1
  64. package/dist/fake/index.d.ts +3 -3
  65. package/dist/fake/index.js +3 -3
  66. package/dist/fake/index.js.map +1 -1
  67. package/dist/lock/core/index.d.ts +9 -4
  68. package/dist/lock/core/index.d.ts.map +1 -1
  69. package/dist/lock/core/index.js +12 -4
  70. package/dist/lock/core/index.js.map +1 -1
  71. package/dist/logger/index.d.ts +3 -3
  72. package/dist/logger/index.js +6 -3
  73. package/dist/logger/index.js.map +1 -1
  74. package/dist/mcp/index.d.ts +3 -3
  75. package/dist/mcp/index.js +3 -3
  76. package/dist/mcp/index.js.map +1 -1
  77. package/dist/orm/index.d.ts +12 -12
  78. package/dist/orm/index.js +4 -4
  79. package/dist/orm/index.js.map +1 -1
  80. package/dist/queue/core/index.d.ts +3 -3
  81. package/dist/queue/core/index.js +3 -3
  82. package/dist/queue/core/index.js.map +1 -1
  83. package/dist/react/auth/index.browser.js +2 -1
  84. package/dist/react/auth/index.browser.js.map +1 -1
  85. package/dist/react/auth/index.d.ts +3 -3
  86. package/dist/react/auth/index.js +5 -4
  87. package/dist/react/auth/index.js.map +1 -1
  88. package/dist/react/core/index.d.ts +6 -6
  89. package/dist/react/core/index.js +3 -3
  90. package/dist/react/core/index.js.map +1 -1
  91. package/dist/react/form/index.d.ts +3 -3
  92. package/dist/react/form/index.js +3 -3
  93. package/dist/react/form/index.js.map +1 -1
  94. package/dist/react/head/index.d.ts +3 -3
  95. package/dist/react/head/index.js +3 -3
  96. package/dist/react/head/index.js.map +1 -1
  97. package/dist/react/i18n/index.d.ts +3 -3
  98. package/dist/react/i18n/index.js +3 -3
  99. package/dist/react/i18n/index.js.map +1 -1
  100. package/dist/react/intro/index.css +337 -0
  101. package/dist/react/intro/index.css.map +1 -0
  102. package/dist/react/intro/index.d.ts +10 -0
  103. package/dist/react/intro/index.d.ts.map +1 -0
  104. package/dist/react/intro/index.js +222 -0
  105. package/dist/react/intro/index.js.map +1 -0
  106. package/dist/react/router/index.browser.js +2 -2
  107. package/dist/react/router/index.browser.js.map +1 -1
  108. package/dist/react/router/index.d.ts +11 -1
  109. package/dist/react/router/index.d.ts.map +1 -1
  110. package/dist/react/router/index.js +21 -11
  111. package/dist/react/router/index.js.map +1 -1
  112. package/dist/redis/index.d.ts +22 -22
  113. package/dist/redis/index.js +3 -3
  114. package/dist/redis/index.js.map +1 -1
  115. package/dist/retry/index.d.ts +3 -3
  116. package/dist/retry/index.js +3 -3
  117. package/dist/retry/index.js.map +1 -1
  118. package/dist/scheduler/index.d.ts +16 -4
  119. package/dist/scheduler/index.d.ts.map +1 -1
  120. package/dist/scheduler/index.js +45 -7
  121. package/dist/scheduler/index.js.map +1 -1
  122. package/dist/security/index.d.ts +3 -3
  123. package/dist/security/index.js +5 -5
  124. package/dist/security/index.js.map +1 -1
  125. package/dist/server/auth/index.d.ts +3 -3
  126. package/dist/server/auth/index.js +3 -3
  127. package/dist/server/auth/index.js.map +1 -1
  128. package/dist/server/cache/index.d.ts +3 -3
  129. package/dist/server/cache/index.js +3 -3
  130. package/dist/server/cache/index.js.map +1 -1
  131. package/dist/server/compress/index.d.ts +3 -3
  132. package/dist/server/compress/index.d.ts.map +1 -1
  133. package/dist/server/compress/index.js +4 -3
  134. package/dist/server/compress/index.js.map +1 -1
  135. package/dist/server/cookies/index.d.ts +3 -3
  136. package/dist/server/cookies/index.js +3 -3
  137. package/dist/server/cookies/index.js.map +1 -1
  138. package/dist/server/core/index.d.ts +14 -25
  139. package/dist/server/core/index.d.ts.map +1 -1
  140. package/dist/server/core/index.js +13 -29
  141. package/dist/server/core/index.js.map +1 -1
  142. package/dist/server/cors/index.d.ts +3 -3
  143. package/dist/server/cors/index.js +3 -3
  144. package/dist/server/cors/index.js.map +1 -1
  145. package/dist/server/health/index.d.ts +20 -20
  146. package/dist/server/health/index.js +3 -3
  147. package/dist/server/health/index.js.map +1 -1
  148. package/dist/server/helmet/index.d.ts +3 -3
  149. package/dist/server/helmet/index.js +3 -3
  150. package/dist/server/helmet/index.js.map +1 -1
  151. package/dist/server/links/index.d.ts +42 -42
  152. package/dist/server/links/index.d.ts.map +1 -1
  153. package/dist/server/links/index.js +4 -4
  154. package/dist/server/links/index.js.map +1 -1
  155. package/dist/server/metrics/index.d.ts +3 -3
  156. package/dist/server/metrics/index.js +3 -3
  157. package/dist/server/metrics/index.js.map +1 -1
  158. package/dist/server/multipart/index.d.ts +3 -3
  159. package/dist/server/multipart/index.js +3 -3
  160. package/dist/server/multipart/index.js.map +1 -1
  161. package/dist/server/proxy/index.d.ts +3 -3
  162. package/dist/server/proxy/index.js +3 -3
  163. package/dist/server/proxy/index.js.map +1 -1
  164. package/dist/server/rate-limit/index.d.ts +3 -3
  165. package/dist/server/rate-limit/index.js +3 -3
  166. package/dist/server/rate-limit/index.js.map +1 -1
  167. package/dist/server/static/index.d.ts +3 -3
  168. package/dist/server/static/index.js +6 -6
  169. package/dist/server/static/index.js.map +1 -1
  170. package/dist/server/swagger/index.d.ts +3 -3
  171. package/dist/server/swagger/index.js +6 -6
  172. package/dist/server/swagger/index.js.map +1 -1
  173. package/dist/sms/index.d.ts +3 -3
  174. package/dist/sms/index.js +6 -6
  175. package/dist/sms/index.js.map +1 -1
  176. package/dist/system/index.d.ts +3 -3
  177. package/dist/system/index.js +3 -3
  178. package/dist/system/index.js.map +1 -1
  179. package/dist/thread/index.d.ts +3 -3
  180. package/dist/thread/index.js +3 -3
  181. package/dist/thread/index.js.map +1 -1
  182. package/dist/topic/core/index.d.ts +3 -3
  183. package/dist/topic/core/index.js +3 -3
  184. package/dist/topic/core/index.js.map +1 -1
  185. package/dist/vite/index.d.ts +6286 -4
  186. package/dist/vite/index.d.ts.map +1 -1
  187. package/dist/vite/index.js +28 -2
  188. package/dist/vite/index.js.map +1 -1
  189. package/dist/websocket/index.d.ts +37 -37
  190. package/dist/websocket/index.d.ts.map +1 -1
  191. package/dist/websocket/index.js +3 -3
  192. package/dist/websocket/index.js.map +1 -1
  193. package/package.json +12 -4
  194. package/src/api/audits/controllers/AdminAuditController.ts +8 -0
  195. package/src/api/audits/index.ts +3 -3
  196. package/src/api/files/controllers/AdminFileStatsController.ts +1 -0
  197. package/src/api/files/index.ts +3 -3
  198. package/src/api/jobs/controllers/AdminJobController.ts +18 -2
  199. package/src/api/jobs/index.ts +4 -3
  200. package/src/api/jobs/services/JobAudits.spec.ts +89 -0
  201. package/src/api/jobs/services/JobAudits.ts +101 -0
  202. package/src/api/keys/index.ts +3 -3
  203. package/src/api/notifications/controllers/AdminNotificationController.ts +1 -0
  204. package/src/api/notifications/index.ts +3 -3
  205. package/src/api/parameters/controllers/AdminConfigController.ts +10 -0
  206. package/src/api/parameters/index.ts +5 -3
  207. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1 -1
  208. package/src/api/users/__tests__/ApiKeys.spec.ts +1 -1
  209. package/src/api/users/__tests__/EmailVerification.spec.ts +16 -1
  210. package/src/api/users/__tests__/PasswordReset.spec.ts +11 -0
  211. package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -0
  212. package/src/api/users/controllers/AdminIdentityController.ts +3 -0
  213. package/src/api/users/controllers/AdminSessionController.ts +3 -0
  214. package/src/api/users/controllers/AdminUserController.ts +5 -0
  215. package/src/api/users/index.ts +8 -9
  216. package/src/api/users/primitives/$realm.ts +117 -19
  217. package/src/api/users/providers/RealmProvider.ts +15 -7
  218. package/src/api/users/services/CredentialService.spec.ts +11 -0
  219. package/src/api/users/services/CredentialService.ts +47 -24
  220. package/src/api/users/services/IdentityService.ts +12 -4
  221. package/src/api/users/services/RegistrationService.spec.ts +11 -0
  222. package/src/api/users/services/RegistrationService.ts +33 -12
  223. package/src/api/users/services/SessionService.ts +83 -12
  224. package/src/api/users/services/UserAudits.ts +47 -0
  225. package/src/api/users/services/UserFiles.ts +19 -0
  226. package/src/api/users/services/UserJobs.spec.ts +107 -0
  227. package/src/api/users/services/UserJobs.ts +62 -0
  228. package/src/api/users/services/UserParameters.ts +23 -0
  229. package/src/api/users/services/UserService.ts +34 -17
  230. package/src/api/verifications/index.ts +3 -3
  231. package/src/batch/index.ts +3 -3
  232. package/src/bucket/index.ts +3 -3
  233. package/src/cache/core/index.ts +3 -3
  234. package/src/cli/commands/build.ts +1 -0
  235. package/src/cli/commands/db.ts +9 -0
  236. package/src/cli/commands/init.spec.ts +2 -17
  237. package/src/cli/commands/init.ts +37 -1
  238. package/src/cli/providers/ViteDevServerProvider.ts +36 -2
  239. package/src/cli/services/AlephaCliUtils.ts +17 -0
  240. package/src/cli/services/PackageManagerUtils.ts +15 -1
  241. package/src/cli/services/ProjectScaffolder.ts +8 -13
  242. package/src/cli/templates/agentMd.ts +2 -25
  243. package/src/cli/templates/apiAppSecurityTs.ts +37 -2
  244. package/src/cli/templates/mainCss.ts +2 -32
  245. package/src/cli/templates/webAppRouterTs.ts +5 -5
  246. package/src/cli/templates/webHomeComponentTsx.ts +10 -0
  247. package/src/command/helpers/Runner.ts +14 -1
  248. package/src/command/index.ts +3 -3
  249. package/src/core/helpers/primitive.ts +0 -5
  250. package/src/core/index.ts +3 -3
  251. package/src/datetime/index.ts +3 -3
  252. package/src/email/index.ts +3 -3
  253. package/src/email/index.workerd.ts +36 -0
  254. package/src/email/providers/LocalEmailProvider.ts +2 -2
  255. package/src/email/providers/WorkermailerEmailProvider.ts +221 -0
  256. package/src/fake/index.ts +3 -3
  257. package/src/lock/core/index.ts +3 -3
  258. package/src/lock/core/primitives/$lock.ts +13 -1
  259. package/src/logger/index.ts +3 -3
  260. package/src/logger/providers/PrettyFormatterProvider.ts +7 -0
  261. package/src/mcp/index.ts +3 -3
  262. package/src/orm/index.ts +3 -3
  263. package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
  264. package/src/queue/core/index.ts +3 -3
  265. package/src/react/auth/index.ts +3 -3
  266. package/src/react/auth/services/ReactAuth.ts +3 -1
  267. package/src/react/core/index.ts +3 -3
  268. package/src/react/form/index.ts +3 -3
  269. package/src/react/head/index.ts +3 -3
  270. package/src/react/i18n/index.ts +3 -3
  271. package/src/react/intro/components/GettingStarted.css +334 -0
  272. package/src/react/intro/components/GettingStarted.tsx +276 -0
  273. package/src/react/intro/index.ts +1 -0
  274. package/src/react/router/atoms/ssrManifestAtom.ts +7 -0
  275. package/src/react/router/index.browser.ts +2 -0
  276. package/src/react/router/index.ts +2 -0
  277. package/src/react/router/providers/ReactServerProvider.ts +14 -4
  278. package/src/react/router/providers/SSRManifestProvider.ts +7 -0
  279. package/src/redis/index.ts +3 -3
  280. package/src/retry/index.ts +3 -3
  281. package/src/router/index.ts +3 -3
  282. package/src/scheduler/index.ts +3 -3
  283. package/src/scheduler/index.workerd.ts +43 -0
  284. package/src/scheduler/providers/CronProvider.ts +53 -6
  285. package/src/scheduler/providers/WorkerdCronProvider.ts +102 -0
  286. package/src/security/index.ts +3 -3
  287. package/src/security/providers/JwtProvider.ts +2 -2
  288. package/src/server/auth/index.ts +3 -3
  289. package/src/server/cache/index.ts +3 -3
  290. package/src/server/compress/index.ts +3 -3
  291. package/src/server/compress/providers/ServerCompressProvider.ts +6 -0
  292. package/src/server/cookies/index.ts +3 -3
  293. package/src/server/core/index.ts +3 -3
  294. package/src/server/core/primitives/$action.spec.ts +3 -2
  295. package/src/server/core/primitives/$action.ts +6 -2
  296. package/src/server/core/providers/NodeHttpServerProvider.ts +2 -15
  297. package/src/server/core/providers/ServerProvider.ts +4 -2
  298. package/src/server/core/providers/ServerRouterProvider.ts +5 -27
  299. package/src/server/cors/index.ts +3 -3
  300. package/src/server/health/index.ts +3 -3
  301. package/src/server/helmet/index.ts +3 -3
  302. package/src/server/links/index.ts +3 -3
  303. package/src/server/links/providers/ServerLinksProvider.spec.ts +332 -0
  304. package/src/server/links/providers/ServerLinksProvider.ts +1 -1
  305. package/src/server/metrics/index.ts +3 -3
  306. package/src/server/multipart/index.ts +3 -3
  307. package/src/server/proxy/index.ts +3 -3
  308. package/src/server/rate-limit/index.ts +3 -3
  309. package/src/server/static/index.ts +3 -3
  310. package/src/server/swagger/index.ts +3 -3
  311. package/src/sms/index.ts +3 -3
  312. package/src/system/index.ts +3 -3
  313. package/src/thread/index.ts +3 -3
  314. package/src/topic/core/index.ts +3 -3
  315. package/src/vite/tasks/generateCloudflare.ts +38 -2
  316. package/src/websocket/index.ts +3 -3
  317. package/src/cli/templates/webHelloComponentTsx.ts +0 -30
  318. /package/src/api/users/{notifications → services}/UserNotifications.ts +0 -0
@@ -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 { $bucket } from "alepha/bucket";
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 { AlephaApiFiles } from "alepha/api/files";
25
- import { AlephaApiJobs } from "alepha/api/jobs";
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
- auditService = $inject(AuditService);
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.auditService.recordUser("update", {
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}`,
@@ -318,6 +357,7 @@ var AdminIdentityController = class {
318
357
  findIdentities = $action({
319
358
  path: this.url,
320
359
  group: this.group,
360
+ secure: true,
321
361
  description: "Find identities with pagination and filtering",
322
362
  schema: {
323
363
  query: t.extend(identityQuerySchema, { userRealmName: t.optional(t.string()) }),
@@ -334,6 +374,7 @@ var AdminIdentityController = class {
334
374
  getIdentity = $action({
335
375
  path: `${this.url}/:id`,
336
376
  group: this.group,
377
+ secure: true,
337
378
  description: "Get an identity by ID",
338
379
  schema: {
339
380
  params: t.object({ id: t.uuid() }),
@@ -349,6 +390,7 @@ var AdminIdentityController = class {
349
390
  method: "DELETE",
350
391
  path: `${this.url}/:id`,
351
392
  group: this.group,
393
+ secure: true,
352
394
  description: "Delete an identity",
353
395
  schema: {
354
396
  params: t.object({ id: t.uuid() }),
@@ -458,6 +500,7 @@ var AdminSessionController = class {
458
500
  findSessions = $action({
459
501
  path: this.url,
460
502
  group: this.group,
503
+ secure: true,
461
504
  description: "Find sessions with pagination and filtering",
462
505
  schema: {
463
506
  query: t.extend(sessionQuerySchema, { userRealmName: t.optional(t.string()) }),
@@ -474,6 +517,7 @@ var AdminSessionController = class {
474
517
  getSession = $action({
475
518
  path: `${this.url}/:id`,
476
519
  group: this.group,
520
+ secure: true,
477
521
  description: "Get a session by ID",
478
522
  schema: {
479
523
  params: t.object({ id: t.uuid() }),
@@ -489,6 +533,7 @@ var AdminSessionController = class {
489
533
  method: "DELETE",
490
534
  path: `${this.url}/:id`,
491
535
  group: this.group,
536
+ secure: true,
492
537
  description: "Delete a session",
493
538
  schema: {
494
539
  params: t.object({ id: t.uuid() }),
@@ -535,7 +580,7 @@ const userQuerySchema = t.extend(pageQuerySchema, {
535
580
  const userResourceSchema = users.schema;
536
581
 
537
582
  //#endregion
538
- //#region ../../src/api/users/notifications/UserNotifications.ts
583
+ //#region ../../src/api/users/services/UserNotifications.ts
539
584
  var UserNotifications = class {
540
585
  passwordReset = $notification({
541
586
  category: "security",
@@ -666,11 +711,16 @@ var UserNotifications = class {
666
711
  //#endregion
667
712
  //#region ../../src/api/users/services/UserService.ts
668
713
  var UserService = class {
714
+ alepha = $inject(Alepha);
669
715
  log = $logger();
670
716
  verificationController = $client();
671
- userNotifications = $inject(UserNotifications);
672
717
  realmProvider = $inject(RealmProvider);
673
- auditService = $inject(AuditService);
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
+ }
674
724
  users(userRealmName) {
675
725
  return this.realmProvider.userRepository(userRealmName);
676
726
  }
@@ -709,7 +759,7 @@ var UserService = class {
709
759
  url.searchParams.set("email", email);
710
760
  url.searchParams.set("token", verification.token);
711
761
  const fullVerifyUrl = verifyUrl ? `${verifyUrl}${url.search}` : url.pathname + url.search;
712
- await this.userNotifications.emailVerificationLink.push({
762
+ await this.userNotifications(userRealmName)?.emailVerificationLink.push({
713
763
  contact: email,
714
764
  variables: {
715
765
  email,
@@ -722,7 +772,7 @@ var UserService = class {
722
772
  userId: user.id
723
773
  });
724
774
  } else {
725
- await this.userNotifications.emailVerification.push({
775
+ await this.userNotifications(userRealmName)?.emailVerification.push({
726
776
  contact: email,
727
777
  variables: {
728
778
  email,
@@ -777,7 +827,7 @@ var UserService = class {
777
827
  type
778
828
  });
779
829
  const realm = this.realmProvider.getRealm(userRealmName);
780
- await this.auditService.recordUser("update", {
830
+ await this.userAudits(userRealmName)?.recordUser("update", {
781
831
  userId: user.id,
782
832
  userEmail: email,
783
833
  userRealm: realm.name,
@@ -869,7 +919,7 @@ var UserService = class {
869
919
  username: user.username,
870
920
  email: user.email
871
921
  });
872
- await this.auditService.recordUser("create", {
922
+ await this.userAudits(userRealmName)?.recordUser("create", {
873
923
  userRealm: realm.name,
874
924
  resourceId: user.id,
875
925
  description: "User created",
@@ -899,7 +949,7 @@ var UserService = class {
899
949
  to: data[key]
900
950
  };
901
951
  const isRoleChange = data.roles !== void 0 && JSON.stringify(before.roles) !== JSON.stringify(data.roles);
902
- await this.auditService.recordUser(isRoleChange ? "role_change" : "update", {
952
+ await this.userAudits(userRealmName)?.recordUser(isRoleChange ? "role_change" : "update", {
903
953
  userRealm: realm.name,
904
954
  resourceId: user.id,
905
955
  description: isRoleChange ? "User roles changed" : `User updated: ${Object.keys(changes).join(", ")}`,
@@ -919,7 +969,7 @@ var UserService = class {
919
969
  await this.users(userRealmName).deleteById(id);
920
970
  this.log.info("User deleted", { userId: id });
921
971
  const realm = this.realmProvider.getRealm(userRealmName);
922
- await this.auditService.recordUser("delete", {
972
+ await this.userAudits(userRealmName)?.recordUser("delete", {
923
973
  userRealm: realm.name,
924
974
  resourceId: id,
925
975
  severity: "warning",
@@ -944,6 +994,7 @@ var AdminUserController = class {
944
994
  findUsers = $action({
945
995
  path: this.url,
946
996
  group: this.group,
997
+ secure: true,
947
998
  description: "Find users with pagination and filtering",
948
999
  schema: {
949
1000
  query: t.extend(userQuerySchema, { userRealmName: t.optional(t.string()) }),
@@ -960,6 +1011,7 @@ var AdminUserController = class {
960
1011
  getUser = $action({
961
1012
  path: `${this.url}/:id`,
962
1013
  group: this.group,
1014
+ secure: true,
963
1015
  description: "Get a user by ID",
964
1016
  schema: {
965
1017
  params: t.object({ id: t.uuid() }),
@@ -975,6 +1027,7 @@ var AdminUserController = class {
975
1027
  method: "POST",
976
1028
  path: this.url,
977
1029
  group: this.group,
1030
+ secure: true,
978
1031
  description: "Create a new user",
979
1032
  schema: {
980
1033
  query: t.object({ userRealmName: t.optional(t.string()) }),
@@ -990,6 +1043,7 @@ var AdminUserController = class {
990
1043
  method: "PATCH",
991
1044
  path: `${this.url}/:id`,
992
1045
  group: this.group,
1046
+ secure: true,
993
1047
  description: "Update a user",
994
1048
  schema: {
995
1049
  params: t.object({ id: t.uuid() }),
@@ -1006,6 +1060,7 @@ var AdminUserController = class {
1006
1060
  method: "DELETE",
1007
1061
  path: `${this.url}/:id`,
1008
1062
  group: this.group,
1063
+ secure: true,
1009
1064
  description: "Delete a user",
1010
1065
  schema: {
1011
1066
  params: t.object({ id: t.uuid() }),
@@ -1169,13 +1224,18 @@ const registrationIntentResponseSchema = t.object({
1169
1224
  //#region ../../src/api/users/services/CredentialService.ts
1170
1225
  const INTENT_TTL_MINUTES$1 = 10;
1171
1226
  var CredentialService = class {
1227
+ alepha = $inject(Alepha);
1172
1228
  log = $logger();
1173
1229
  cryptoProvider = $inject(CryptoProvider);
1174
1230
  dateTimeProvider = $inject(DateTimeProvider);
1175
1231
  verificationController = $client();
1176
- userNotifications = $inject(UserNotifications);
1177
1232
  realmProvider = $inject(RealmProvider);
1178
- auditService = $inject(AuditService);
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
+ }
1179
1239
  intentCache = $cache({
1180
1240
  name: "password-reset-intents",
1181
1241
  ttl: [INTENT_TTL_MINUTES$1, "minutes"]
@@ -1230,7 +1290,7 @@ var CredentialService = class {
1230
1290
  params: { type: "code" },
1231
1291
  body: { target: email }
1232
1292
  });
1233
- await this.userNotifications.passwordReset.push({
1293
+ await this.userNotifications(userRealmName)?.passwordReset.push({
1234
1294
  contact: email,
1235
1295
  variables: {
1236
1296
  email,
@@ -1305,7 +1365,7 @@ var CredentialService = class {
1305
1365
  email: intent.email
1306
1366
  });
1307
1367
  const realm = this.realmProvider.getRealm(intent.realmName);
1308
- await this.auditService.recordUser("update", {
1368
+ await this.userAudits(intent.realmName)?.recordUser("update", {
1309
1369
  userId: intent.userId,
1310
1370
  userEmail: intent.email,
1311
1371
  userRealm: realm.name,
@@ -1313,7 +1373,7 @@ var CredentialService = class {
1313
1373
  description: "Password reset completed",
1314
1374
  metadata: { email: intent.email }
1315
1375
  });
1316
- await this.auditService.record("security", "sessions_invalidated", {
1376
+ await this.userAudits(intent.realmName)?.record("security", "sessions_invalidated", {
1317
1377
  userId: intent.userId,
1318
1378
  userEmail: intent.email,
1319
1379
  userRealm: realm.name,
@@ -1364,7 +1424,7 @@ var CredentialService = class {
1364
1424
  await this.identities(userRealmName).updateById(identity.id, { password: hashedPassword });
1365
1425
  await this.sessions(userRealmName).deleteMany({ userId: { eq: user.id } });
1366
1426
  const realm = this.realmProvider.getRealm(userRealmName);
1367
- await this.auditService.recordUser("update", {
1427
+ await this.userAudits(userRealmName)?.recordUser("update", {
1368
1428
  userId: user.id,
1369
1429
  userEmail: email,
1370
1430
  userRealm: realm.name,
@@ -1372,7 +1432,7 @@ var CredentialService = class {
1372
1432
  description: "Password reset completed (legacy)",
1373
1433
  metadata: { email }
1374
1434
  });
1375
- await this.auditService.record("security", "sessions_invalidated", {
1435
+ await this.userAudits(userRealmName)?.record("security", "sessions_invalidated", {
1376
1436
  userId: user.id,
1377
1437
  userEmail: email,
1378
1438
  userRealm: realm.name,
@@ -1387,17 +1447,22 @@ var CredentialService = class {
1387
1447
  //#region ../../src/api/users/services/RegistrationService.ts
1388
1448
  const INTENT_TTL_MINUTES = 10;
1389
1449
  var RegistrationService = class {
1450
+ alepha = $inject(Alepha);
1390
1451
  log = $logger();
1391
1452
  dateTimeProvider = $inject(DateTimeProvider);
1392
1453
  cryptoProvider = $inject(CryptoProvider);
1393
1454
  verificationController = $client();
1394
- userNotifications = $inject(UserNotifications);
1395
1455
  realmProvider = $inject(RealmProvider);
1396
- auditService = $inject(AuditService);
1397
1456
  intentCache = $cache({
1398
1457
  name: "registration-intents",
1399
1458
  ttl: [INTENT_TTL_MINUTES, "minutes"]
1400
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
+ }
1401
1466
  /**
1402
1467
  * Phase 1: Create a registration intent.
1403
1468
  *
@@ -1446,8 +1511,8 @@ var RegistrationService = class {
1446
1511
  phone: realmSettings?.verifyPhoneRequired === true && !!body.phoneNumber,
1447
1512
  captcha: false
1448
1513
  };
1449
- if (requirements.email && body.email) await this.sendEmailVerification(body.email);
1450
- 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);
1451
1516
  const intentId = randomUUID();
1452
1517
  const expiresAt = this.dateTimeProvider.now().add(INTENT_TTL_MINUTES, "minutes").toISOString();
1453
1518
  const intent = {
@@ -1547,7 +1612,7 @@ var RegistrationService = class {
1547
1612
  username: user.username
1548
1613
  });
1549
1614
  const realm = this.realmProvider.getRealm(userRealmName);
1550
- await this.auditService.recordUser("create", {
1615
+ await this.userAudits(userRealmName)?.recordUser("create", {
1551
1616
  userId: user.id,
1552
1617
  userEmail: user.email ?? void 0,
1553
1618
  userRealm: realm.name,
@@ -1589,13 +1654,13 @@ var RegistrationService = class {
1589
1654
  /**
1590
1655
  * Send email verification code.
1591
1656
  */
1592
- async sendEmailVerification(email) {
1657
+ async sendEmailVerification(email, realmName) {
1593
1658
  this.log.debug("Sending email verification code", { email });
1594
1659
  const verification = await this.verificationController.requestVerificationCode({
1595
1660
  params: { type: "code" },
1596
1661
  body: { target: email }
1597
1662
  });
1598
- await this.userNotifications.emailVerification.push({
1663
+ await this.userNotifications(realmName)?.emailVerification.push({
1599
1664
  contact: email,
1600
1665
  variables: {
1601
1666
  email,
@@ -1608,14 +1673,14 @@ var RegistrationService = class {
1608
1673
  /**
1609
1674
  * Send phone verification code.
1610
1675
  */
1611
- async sendPhoneVerification(phoneNumber) {
1676
+ async sendPhoneVerification(phoneNumber, realmName) {
1612
1677
  this.log.debug("Sending phone verification code", { phoneNumber });
1613
1678
  try {
1614
1679
  const verification = await this.verificationController.requestVerificationCode({
1615
1680
  params: { type: "code" },
1616
1681
  body: { target: phoneNumber }
1617
1682
  });
1618
- await this.userNotifications.phoneVerification.push({
1683
+ await this.userNotifications(realmName)?.phoneVerification.push({
1619
1684
  contact: phoneNumber,
1620
1685
  variables: {
1621
1686
  phoneNumber,
@@ -3699,9 +3764,9 @@ var ShellProvider = class {};
3699
3764
  //#endregion
3700
3765
  //#region ../../src/system/index.ts
3701
3766
  /**
3702
- * | type | quality | stability |
3703
- * |------|---------|-----------|
3704
- * | tooling | standard | stable |
3767
+ * | Stability | Since | Runtime |
3768
+ * |-----------|-------|---------|
3769
+ * | 3 - stable | 0.14.0 | node, bun, browser|
3705
3770
  *
3706
3771
  * System-level abstractions for portable code across runtimes.
3707
3772
  *
@@ -3746,7 +3811,9 @@ var SessionService = class {
3746
3811
  log = $logger();
3747
3812
  realmProvider = $inject(RealmProvider);
3748
3813
  fileController = $client();
3749
- auditService = $inject(AuditService);
3814
+ userAudits(realmName) {
3815
+ if (this.realmProvider.getRealm(realmName).features.audits) return this.alepha.inject(UserAudits);
3816
+ }
3750
3817
  users(userRealmName) {
3751
3818
  return this.realmProvider.userRepository(userRealmName);
3752
3819
  }
@@ -3757,6 +3824,40 @@ var SessionService = class {
3757
3824
  return this.realmProvider.identityRepository(userRealmName);
3758
3825
  }
3759
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
+ /**
3760
3861
  * Random delay to prevent timing attacks (50-200ms)
3761
3862
  * Uses cryptographically secure random number generation
3762
3863
  */
@@ -3785,7 +3886,7 @@ var SessionService = class {
3785
3886
  username,
3786
3887
  realm: name
3787
3888
  });
3788
- await this.auditService.recordAuth("login_failed", {
3889
+ await this.userAudits(userRealmName)?.recordAuth("login_failed", {
3789
3890
  userRealm: name,
3790
3891
  description: "Username does not match required format",
3791
3892
  metadata: {
@@ -3805,7 +3906,7 @@ var SessionService = class {
3805
3906
  username,
3806
3907
  realm: name
3807
3908
  });
3808
- await this.auditService.recordAuth("login_failed", {
3909
+ await this.userAudits(userRealmName)?.recordAuth("login_failed", {
3809
3910
  userRealm: name,
3810
3911
  description: "Invalid login identifier format",
3811
3912
  metadata: {
@@ -3822,7 +3923,7 @@ var SessionService = class {
3822
3923
  username,
3823
3924
  realm: name
3824
3925
  });
3825
- await this.auditService.recordAuth("login_failed", {
3926
+ await this.userAudits(userRealmName)?.recordAuth("login_failed", {
3826
3927
  userRealm: name,
3827
3928
  description: "User not found",
3828
3929
  metadata: {
@@ -3852,7 +3953,7 @@ var SessionService = class {
3852
3953
  username,
3853
3954
  realm: name
3854
3955
  });
3855
- await this.auditService.recordAuth("login_failed", {
3956
+ await this.userAudits(userRealmName)?.recordAuth("login_failed", {
3856
3957
  userRealm: name,
3857
3958
  resourceId: user.id,
3858
3959
  description: "Invalid password",
@@ -3863,7 +3964,7 @@ var SessionService = class {
3863
3964
  });
3864
3965
  throw new InvalidCredentialsError();
3865
3966
  }
3866
- await this.auditService.recordAuth("login", {
3967
+ await this.userAudits(userRealmName)?.recordAuth("login", {
3867
3968
  userId: user.id,
3868
3969
  userEmail: user.email ?? void 0,
3869
3970
  userRealm: name,
@@ -3874,6 +3975,7 @@ var SessionService = class {
3874
3975
  username
3875
3976
  }
3876
3977
  });
3978
+ await this.ensureAdminRole(user, userRealmName);
3877
3979
  return user;
3878
3980
  } catch (error) {
3879
3981
  if (error instanceof InvalidCredentialsError) throw error;
@@ -3920,6 +4022,7 @@ var SessionService = class {
3920
4022
  throw new UnauthorizedError("Session expired");
3921
4023
  }
3922
4024
  const user = await this.users(userRealmName).findOne({ where: { id: { eq: session.userId } } });
4025
+ await this.ensureAdminRole(user, userRealmName);
3923
4026
  this.log.debug("Session refreshed", {
3924
4027
  sessionId: session.id,
3925
4028
  userId: session.userId
@@ -3937,7 +4040,7 @@ var SessionService = class {
3937
4040
  this.log.debug("Session deleted");
3938
4041
  if (session) {
3939
4042
  const { name } = this.realmProvider.getRealm(userRealmName);
3940
- await this.auditService.recordAuth("logout", {
4043
+ await this.userAudits(userRealmName)?.recordAuth("logout", {
3941
4044
  userId: session.userId,
3942
4045
  userRealm: name,
3943
4046
  sessionId: session.id,
@@ -3965,7 +4068,7 @@ var SessionService = class {
3965
4068
  userId: identity.userId
3966
4069
  });
3967
4070
  const user = await users.findById(identity.userId);
3968
- await this.auditService.recordAuth("login", {
4071
+ await this.userAudits(userRealmName)?.recordAuth("login", {
3969
4072
  userId: user.id,
3970
4073
  userEmail: user.email ?? void 0,
3971
4074
  userRealm: realm.name,
@@ -3976,6 +4079,7 @@ var SessionService = class {
3976
4079
  providerUserId: profile.sub
3977
4080
  }
3978
4081
  });
4082
+ await this.ensureAdminRole(user, userRealmName);
3979
4083
  return user;
3980
4084
  }
3981
4085
  if (!profile.email) {
@@ -4001,7 +4105,7 @@ var SessionService = class {
4001
4105
  providerUserId: profile.sub,
4002
4106
  userId: existing.id
4003
4107
  });
4004
- await this.auditService.recordAuth("login", {
4108
+ await this.userAudits(userRealmName)?.recordAuth("login", {
4005
4109
  userId: existing.id,
4006
4110
  userEmail: existing.email ?? void 0,
4007
4111
  userRealm: realm.name,
@@ -4013,6 +4117,7 @@ var SessionService = class {
4013
4117
  linked: true
4014
4118
  }
4015
4119
  });
4120
+ await this.ensureAdminRole(existing, userRealmName);
4016
4121
  return existing;
4017
4122
  }
4018
4123
  const user = await users.create({
@@ -4049,7 +4154,7 @@ var SessionService = class {
4049
4154
  email: user.email,
4050
4155
  username: user.username
4051
4156
  });
4052
- await this.auditService.recordUser("create", {
4157
+ await this.userAudits(userRealmName)?.recordUser("create", {
4053
4158
  userId: user.id,
4054
4159
  userEmail: user.email ?? void 0,
4055
4160
  userRealm: realm.name,
@@ -4062,7 +4167,7 @@ var SessionService = class {
4062
4167
  email: user.email
4063
4168
  }
4064
4169
  });
4065
- await this.auditService.recordAuth("login", {
4170
+ await this.userAudits(userRealmName)?.recordAuth("login", {
4066
4171
  userId: user.id,
4067
4172
  userEmail: user.email ?? void 0,
4068
4173
  userRealm: realm.name,
@@ -4074,6 +4179,7 @@ var SessionService = class {
4074
4179
  firstLogin: true
4075
4180
  }
4076
4181
  });
4182
+ await this.ensureAdminRole(user, userRealmName);
4077
4183
  return user;
4078
4184
  }
4079
4185
  };
@@ -4508,9 +4614,9 @@ var ApiKeyController = class {
4508
4614
  //#endregion
4509
4615
  //#region ../../src/api/keys/index.ts
4510
4616
  /**
4511
- * | type | quality | stability |
4512
- * |------|---------|--------------|
4513
- * | backend | good | experimental |
4617
+ * | Stability | Since | Runtime |
4618
+ * |-----------|-------|---------|
4619
+ * | 3 - stable | 0.11.0 | node, bun, workerd|
4514
4620
  *
4515
4621
  * API key management module for programmatic access.
4516
4622
  *
@@ -4544,6 +4650,95 @@ const AlephaApiKeys = $module({
4544
4650
  ]
4545
4651
  });
4546
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
+
4547
4742
  //#endregion
4548
4743
  //#region ../../src/api/users/primitives/$realm.ts
4549
4744
  /**
@@ -4569,12 +4764,32 @@ const $realm = (options = {}) => {
4569
4764
  if (options.settings.emailRequired) options.settings.emailEnabled = true;
4570
4765
  if (options.settings.usernameRequired) options.settings.usernameEnabled = true;
4571
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
+ }
4572
4782
  const realmRegistration = realmProvider.register(name, options);
4573
- alepha.with(AlephaApiFiles);
4574
- alepha.with(AlephaApiAudits);
4575
- alepha.with(AlephaApiJobs);
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);
4576
4791
  const customResolvers = [...options.issuer?.resolvers ?? []];
4577
- if (options.apiKeys) {
4792
+ if (features.apiKeys) {
4578
4793
  alepha.with(AlephaApiKeys);
4579
4794
  const apiKeyService = alepha.inject(ApiKeyService);
4580
4795
  customResolvers.push(apiKeyService.createResolver());
@@ -4690,9 +4905,9 @@ const resetPasswordSchema = t.object({
4690
4905
  //#endregion
4691
4906
  //#region ../../src/api/users/index.ts
4692
4907
  /**
4693
- * | type | quality | stability |
4694
- * |------|---------|-----------|
4695
- * | backend | epic | stable |
4908
+ * | Stability | Since | Runtime |
4909
+ * |-----------|-------|---------|
4910
+ * | 3 - stable | 0.5.0 | node, bun, workerd|
4696
4911
  *
4697
4912
  * Complete user management with multi-realm support for multi-tenant applications.
4698
4913
  *
@@ -4711,8 +4926,6 @@ const resetPasswordSchema = t.object({
4711
4926
  const AlephaApiUsers = $module({
4712
4927
  name: "alepha.api.users",
4713
4928
  services: [
4714
- AlephaApiVerification,
4715
- AlephaApiNotifications,
4716
4929
  AlephaServerHelmet,
4717
4930
  AlephaServerCompress,
4718
4931
  AlephaEmail,
@@ -4727,11 +4940,10 @@ const AlephaApiUsers = $module({
4727
4940
  AdminUserController,
4728
4941
  AdminSessionController,
4729
4942
  AdminIdentityController,
4730
- RealmController,
4731
- UserNotifications
4943
+ RealmController
4732
4944
  ]
4733
4945
  });
4734
4946
 
4735
4947
  //#endregion
4736
- 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 };
4737
4949
  //# sourceMappingURL=index.js.map