create-forgeon 0.3.15 → 0.3.17

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 (129) hide show
  1. package/package.json +4 -2
  2. package/src/cli/add-options.test.mjs +5 -2
  3. package/src/cli/options.test.mjs +1 -0
  4. package/src/cli/prompt-select.test.mjs +1 -0
  5. package/src/core/docs.test.mjs +80 -40
  6. package/src/core/scaffold.test.mjs +100 -0
  7. package/src/core/validate.test.mjs +1 -0
  8. package/src/modules/accounts.mjs +416 -0
  9. package/src/modules/db-prisma.mjs +23 -55
  10. package/src/modules/dependencies.test.mjs +71 -29
  11. package/src/modules/executor.mjs +3 -2
  12. package/src/modules/executor.test.mjs +631 -500
  13. package/src/modules/files-access.mjs +36 -105
  14. package/src/modules/files-image.mjs +35 -107
  15. package/src/modules/files-local.mjs +15 -6
  16. package/src/modules/files-quotas.mjs +75 -93
  17. package/src/modules/files-s3.mjs +17 -6
  18. package/src/modules/files.mjs +56 -125
  19. package/src/modules/i18n.mjs +17 -121
  20. package/src/modules/idempotency.test.mjs +180 -0
  21. package/src/modules/logger.mjs +0 -9
  22. package/src/modules/probes.test.mjs +204 -0
  23. package/src/modules/queue.mjs +325 -440
  24. package/src/modules/rate-limit.mjs +36 -76
  25. package/src/modules/rbac.mjs +39 -78
  26. package/src/modules/registry.mjs +22 -35
  27. package/src/modules/scheduler.mjs +51 -171
  28. package/src/modules/shared/files-runtime-wiring.mjs +81 -0
  29. package/src/modules/shared/nest-runtime-wiring.mjs +110 -0
  30. package/src/modules/shared/patch-utils.mjs +29 -1
  31. package/src/modules/shared/probes.mjs +235 -0
  32. package/src/modules/sync-integrations.mjs +109 -396
  33. package/src/modules/sync-integrations.test.mjs +141 -0
  34. package/src/run-add-module.test.mjs +154 -0
  35. package/templates/base/README.md +7 -55
  36. package/templates/base/apps/web/src/App.tsx +70 -42
  37. package/templates/base/apps/web/src/probes.ts +61 -0
  38. package/templates/base/apps/web/src/styles.css +86 -25
  39. package/templates/base/package.json +21 -15
  40. package/templates/base/scripts/forgeon-sync-integrations.mjs +65 -281
  41. package/templates/module-fragments/{jwt-auth → accounts}/00_title.md +2 -1
  42. package/templates/module-fragments/{jwt-auth → accounts}/10_overview.md +5 -5
  43. package/templates/module-fragments/accounts/20_scope.md +29 -0
  44. package/templates/module-fragments/accounts/90_status_implemented.md +8 -0
  45. package/templates/module-fragments/accounts/90_status_planned.md +7 -0
  46. package/templates/module-fragments/rbac/30_what_it_adds.md +3 -2
  47. package/templates/module-fragments/rbac/40_how_it_works.md +2 -1
  48. package/templates/module-fragments/rbac/50_how_to_use.md +2 -1
  49. package/templates/module-fragments/swagger/20_scope.md +2 -1
  50. package/templates/module-presets/accounts/apps/api/prisma/migrations/0002_accounts_core/migration.sql +97 -0
  51. package/templates/module-presets/accounts/apps/api/src/accounts/forgeon-accounts-db-prisma.module.ts +17 -0
  52. package/templates/module-presets/accounts/apps/api/src/accounts/prisma-accounts-persistence.store.ts +332 -0
  53. package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/package.json +5 -5
  54. package/templates/module-presets/accounts/packages/accounts-api/src/accounts-email.port.ts +13 -0
  55. package/templates/module-presets/accounts/packages/accounts-api/src/accounts-persistence.port.ts +67 -0
  56. package/templates/module-presets/accounts/packages/accounts-api/src/accounts-rbac.port.ts +14 -0
  57. package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-config.loader.ts +7 -7
  58. package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-config.service.ts +7 -7
  59. package/templates/module-presets/accounts/packages/accounts-api/src/auth-core.service.ts +318 -0
  60. package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-env.schema.ts +4 -4
  61. package/templates/module-presets/accounts/packages/accounts-api/src/auth-jwt.service.ts +58 -0
  62. package/templates/module-presets/accounts/packages/accounts-api/src/auth-password.service.ts +21 -0
  63. package/templates/module-presets/accounts/packages/accounts-api/src/auth.controller.ts +93 -0
  64. package/templates/module-presets/accounts/packages/accounts-api/src/auth.service.ts +48 -0
  65. package/templates/module-presets/accounts/packages/accounts-api/src/auth.types.ts +17 -0
  66. package/templates/module-presets/accounts/packages/accounts-api/src/dto/change-password.dto.ts +13 -0
  67. package/templates/module-presets/accounts/packages/accounts-api/src/dto/confirm-password-reset.dto.ts +12 -0
  68. package/templates/module-presets/accounts/packages/accounts-api/src/dto/index.ts +10 -0
  69. package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/dto/login.dto.ts +1 -1
  70. package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/dto/refresh.dto.ts +1 -1
  71. package/templates/module-presets/accounts/packages/accounts-api/src/dto/register.dto.ts +23 -0
  72. package/templates/module-presets/accounts/packages/accounts-api/src/dto/request-password-reset.dto.ts +7 -0
  73. package/templates/module-presets/accounts/packages/accounts-api/src/dto/update-user-profile.dto.ts +16 -0
  74. package/templates/module-presets/accounts/packages/accounts-api/src/dto/update-user-settings.dto.ts +16 -0
  75. package/templates/module-presets/accounts/packages/accounts-api/src/dto/update-user.dto.ts +8 -0
  76. package/templates/module-presets/accounts/packages/accounts-api/src/dto/verify-email.dto.ts +8 -0
  77. package/templates/module-presets/accounts/packages/accounts-api/src/forgeon-accounts.module.ts +82 -0
  78. package/templates/module-presets/accounts/packages/accounts-api/src/index.ts +24 -0
  79. package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/jwt.strategy.ts +3 -3
  80. package/templates/module-presets/accounts/packages/accounts-api/src/owner-access.guard.ts +39 -0
  81. package/templates/module-presets/accounts/packages/accounts-api/src/users-config.ts +13 -0
  82. package/templates/module-presets/accounts/packages/accounts-api/src/users.controller.ts +65 -0
  83. package/templates/module-presets/accounts/packages/accounts-api/src/users.service.ts +87 -0
  84. package/templates/module-presets/accounts/packages/accounts-api/src/users.types.ts +65 -0
  85. package/templates/module-presets/{jwt-auth/packages/auth-contracts → accounts/packages/accounts-contracts}/package.json +1 -1
  86. package/templates/module-presets/accounts/packages/accounts-contracts/src/index.ts +119 -0
  87. package/templates/module-presets/files/apps/api/src/files/forgeon-files-db-prisma.module.ts +17 -0
  88. package/templates/module-presets/files/apps/api/src/files/prisma-files-persistence.store.ts +164 -0
  89. package/templates/module-presets/files/packages/files/package.json +1 -2
  90. package/templates/module-presets/files/packages/files/src/files.ports.ts +107 -0
  91. package/templates/module-presets/files/packages/files/src/files.service.ts +81 -395
  92. package/templates/module-presets/files/packages/files/src/forgeon-files.module.ts +126 -2
  93. package/templates/module-presets/files/packages/files/src/index.ts +2 -1
  94. package/templates/module-presets/files-local/packages/files-local/src/forgeon-files-local-storage.module.ts +18 -0
  95. package/templates/module-presets/files-local/packages/files-local/src/index.ts +2 -0
  96. package/templates/module-presets/files-local/packages/files-local/src/local-files-storage.adapter.ts +53 -0
  97. package/templates/module-presets/files-quotas/packages/files-quotas/src/forgeon-files-quotas.module.ts +12 -4
  98. package/templates/module-presets/files-s3/packages/files-s3/src/forgeon-files-s3-storage.module.ts +18 -0
  99. package/templates/module-presets/files-s3/packages/files-s3/src/index.ts +2 -0
  100. package/templates/module-presets/files-s3/packages/files-s3/src/s3-files-storage.adapter.ts +130 -0
  101. package/templates/module-presets/i18n/apps/web/src/App.tsx +68 -41
  102. package/templates/module-presets/logger/packages/logger/src/index.ts +0 -1
  103. package/src/modules/jwt-auth.mjs +0 -390
  104. package/templates/base/docs/AI/ARCHITECTURE.md +0 -85
  105. package/templates/base/docs/AI/MODULE_CHECKS.md +0 -28
  106. package/templates/base/docs/AI/MODULE_SPEC.md +0 -77
  107. package/templates/base/docs/AI/PROJECT.md +0 -43
  108. package/templates/base/docs/AI/ROADMAP.md +0 -171
  109. package/templates/base/docs/AI/TASKS.md +0 -60
  110. package/templates/base/docs/AI/VALIDATION.md +0 -31
  111. package/templates/base/docs/README.md +0 -18
  112. package/templates/module-fragments/jwt-auth/20_scope.md +0 -19
  113. package/templates/module-fragments/jwt-auth/90_status_implemented.md +0 -8
  114. package/templates/module-fragments/jwt-auth/90_status_planned.md +0 -3
  115. package/templates/module-presets/jwt-auth/apps/api/prisma/migrations/0002_auth_refresh_token_hash/migration.sql +0 -3
  116. package/templates/module-presets/jwt-auth/apps/api/src/auth/prisma-auth-refresh-token.store.ts +0 -36
  117. package/templates/module-presets/jwt-auth/packages/auth-api/src/auth-refresh-token.store.ts +0 -23
  118. package/templates/module-presets/jwt-auth/packages/auth-api/src/auth.controller.ts +0 -71
  119. package/templates/module-presets/jwt-auth/packages/auth-api/src/auth.service.ts +0 -175
  120. package/templates/module-presets/jwt-auth/packages/auth-api/src/auth.types.ts +0 -6
  121. package/templates/module-presets/jwt-auth/packages/auth-api/src/dto/index.ts +0 -2
  122. package/templates/module-presets/jwt-auth/packages/auth-api/src/forgeon-auth.module.ts +0 -47
  123. package/templates/module-presets/jwt-auth/packages/auth-api/src/index.ts +0 -12
  124. package/templates/module-presets/jwt-auth/packages/auth-contracts/src/index.ts +0 -47
  125. package/templates/module-presets/logger/packages/logger/src/http-logging.interceptor.ts +0 -94
  126. /package/templates/module-presets/{jwt-auth/packages/auth-api/src/jwt-auth.guard.ts → accounts/packages/accounts-api/src/access-token.guard.ts} +0 -0
  127. /package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-config.module.ts +0 -0
  128. /package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/tsconfig.json +0 -0
  129. /package/templates/module-presets/{jwt-auth/packages/auth-contracts → accounts/packages/accounts-contracts}/tsconfig.json +0 -0
@@ -0,0 +1,17 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { ACCOUNTS_PERSISTENCE_PORT } from '@forgeon/accounts-api';
3
+ import { DbPrismaModule } from '@forgeon/db-prisma';
4
+ import { PrismaAccountsPersistenceStore } from './prisma-accounts-persistence.store';
5
+
6
+ @Module({
7
+ imports: [DbPrismaModule],
8
+ providers: [
9
+ PrismaAccountsPersistenceStore,
10
+ {
11
+ provide: ACCOUNTS_PERSISTENCE_PORT,
12
+ useExisting: PrismaAccountsPersistenceStore,
13
+ },
14
+ ],
15
+ exports: [ACCOUNTS_PERSISTENCE_PORT],
16
+ })
17
+ export class ForgeonAccountsDbPrismaModule {}
@@ -0,0 +1,332 @@
1
+ import { Prisma } from '@prisma/client';
2
+ import {
3
+ type AccountsPersistencePort,
4
+ type CreatePasswordAccountInput,
5
+ type PasswordAccountRecord,
6
+ type RefreshTokenRecord,
7
+ } from '@forgeon/accounts-api';
8
+ import { PrismaService } from '@forgeon/db-prisma';
9
+ import { Injectable, NotFoundException } from '@nestjs/common';
10
+
11
+ @Injectable()
12
+ export class PrismaAccountsPersistenceStore implements AccountsPersistencePort {
13
+ constructor(private readonly prisma: PrismaService) {}
14
+
15
+ async createPasswordAccount(input: CreatePasswordAccountInput): Promise<PasswordAccountRecord> {
16
+ const userId = await this.prisma.$transaction(async (tx) => {
17
+ const user = await tx.user.create({
18
+ data: {
19
+ status: input.status,
20
+ data: this.toNullableJson(input.userData),
21
+ profile: {
22
+ create: {
23
+ name: input.profile.name,
24
+ avatar: input.profile.avatar,
25
+ data: this.toNullableJson(input.profile.data),
26
+ },
27
+ },
28
+ settings: {
29
+ create: {
30
+ theme: input.settings.theme,
31
+ locale: input.settings.locale,
32
+ data: this.toNullableJson(input.settings.data),
33
+ },
34
+ },
35
+ },
36
+ select: { id: true },
37
+ });
38
+
39
+ await tx.authIdentity.create({
40
+ data: {
41
+ userId: user.id,
42
+ provider: 'email',
43
+ providerId: input.email,
44
+ },
45
+ });
46
+
47
+ await tx.authCredential.create({
48
+ data: {
49
+ userId: user.id,
50
+ passwordHash: input.passwordHash,
51
+ },
52
+ });
53
+
54
+ return user.id;
55
+ });
56
+
57
+ const account = await this.findAccountByUserId(userId);
58
+ if (!account) {
59
+ throw new NotFoundException('Account could not be created');
60
+ }
61
+
62
+ return account;
63
+ }
64
+
65
+ async findPasswordAccountByEmail(email: string): Promise<PasswordAccountRecord | null> {
66
+ const identity = await this.prisma.authIdentity.findUnique({
67
+ where: {
68
+ provider_providerId: {
69
+ provider: 'email',
70
+ providerId: email,
71
+ },
72
+ },
73
+ include: {
74
+ user: {
75
+ include: {
76
+ profile: true,
77
+ settings: true,
78
+ authCredential: true,
79
+ authIdentities: {
80
+ where: { provider: 'email' },
81
+ select: { provider: true, providerId: true },
82
+ take: 1,
83
+ },
84
+ },
85
+ },
86
+ },
87
+ });
88
+
89
+ return identity?.user ? this.mapPasswordAccount(identity.user) : null;
90
+ }
91
+
92
+ async findAccountByUserId(userId: string): Promise<PasswordAccountRecord | null> {
93
+ const user = await this.prisma.user.findUnique({
94
+ where: { id: userId },
95
+ include: {
96
+ profile: true,
97
+ settings: true,
98
+ authCredential: true,
99
+ authIdentities: {
100
+ where: { provider: 'email' },
101
+ select: { provider: true, providerId: true },
102
+ take: 1,
103
+ },
104
+ },
105
+ });
106
+
107
+ return user ? this.mapPasswordAccount(user) : null;
108
+ }
109
+
110
+ async updatePassword(userId: string, passwordHash: string): Promise<void> {
111
+ await this.prisma.authCredential.upsert({
112
+ where: { userId },
113
+ create: { userId, passwordHash },
114
+ update: { passwordHash },
115
+ });
116
+ }
117
+
118
+ async createRefreshToken(input: {
119
+ id: string;
120
+ userId: string;
121
+ tokenHash: string;
122
+ expiresAt: Date;
123
+ }): Promise<void> {
124
+ await this.prisma.authRefreshToken.create({
125
+ data: input,
126
+ });
127
+ }
128
+
129
+ async findRefreshTokenById(id: string): Promise<RefreshTokenRecord | null> {
130
+ const token = await this.prisma.authRefreshToken.findUnique({
131
+ where: { id },
132
+ });
133
+
134
+ if (!token) {
135
+ return null;
136
+ }
137
+
138
+ return {
139
+ id: token.id,
140
+ userId: token.userId,
141
+ tokenHash: token.tokenHash,
142
+ expiresAt: token.expiresAt,
143
+ revokedAt: token.revokedAt,
144
+ createdAt: token.createdAt,
145
+ };
146
+ }
147
+
148
+ async revokeRefreshToken(id: string, revokedAt: Date): Promise<void> {
149
+ await this.prisma.authRefreshToken.updateMany({
150
+ where: { id, revokedAt: null },
151
+ data: { revokedAt },
152
+ });
153
+ }
154
+
155
+ async revokeRefreshTokensForUser(userId: string, revokedAt: Date): Promise<void> {
156
+ await this.prisma.authRefreshToken.updateMany({
157
+ where: { userId, revokedAt: null },
158
+ data: { revokedAt },
159
+ });
160
+ }
161
+
162
+ async findUserById(userId: string) {
163
+ const user = await this.prisma.user.findUnique({
164
+ where: { id: userId },
165
+ include: {
166
+ profile: true,
167
+ settings: true,
168
+ authIdentities: {
169
+ where: { provider: 'email' },
170
+ select: { providerId: true },
171
+ take: 1,
172
+ },
173
+ },
174
+ });
175
+
176
+ return user ? this.mapUser(user) : null;
177
+ }
178
+
179
+ async updateUser(input: { userId: string; data: Record<string, unknown> | null }) {
180
+ const user = await this.prisma.user.update({
181
+ where: { id: input.userId },
182
+ data: {
183
+ data: this.toNullableJson(input.data),
184
+ },
185
+ include: {
186
+ profile: true,
187
+ settings: true,
188
+ authIdentities: {
189
+ where: { provider: 'email' },
190
+ select: { providerId: true },
191
+ take: 1,
192
+ },
193
+ },
194
+ });
195
+
196
+ return this.mapUser(user);
197
+ }
198
+
199
+ async updateUserProfile(input: {
200
+ userId: string;
201
+ name: string | null;
202
+ avatar: string | null;
203
+ data: Record<string, unknown> | null;
204
+ }) {
205
+ await this.prisma.userProfile.upsert({
206
+ where: { userId: input.userId },
207
+ create: {
208
+ userId: input.userId,
209
+ name: input.name,
210
+ avatar: input.avatar,
211
+ data: this.toNullableJson(input.data),
212
+ },
213
+ update: {
214
+ name: input.name,
215
+ avatar: input.avatar,
216
+ data: this.toNullableJson(input.data),
217
+ },
218
+ });
219
+
220
+ const user = await this.findUserById(input.userId);
221
+ if (!user) {
222
+ throw new NotFoundException('User not found');
223
+ }
224
+ return user;
225
+ }
226
+
227
+ async updateUserSettings(input: {
228
+ userId: string;
229
+ theme: string | null;
230
+ locale: string | null;
231
+ data: Record<string, unknown> | null;
232
+ }) {
233
+ await this.prisma.userSettings.upsert({
234
+ where: { userId: input.userId },
235
+ create: {
236
+ userId: input.userId,
237
+ theme: input.theme,
238
+ locale: input.locale,
239
+ data: this.toNullableJson(input.data),
240
+ },
241
+ update: {
242
+ theme: input.theme,
243
+ locale: input.locale,
244
+ data: this.toNullableJson(input.data),
245
+ },
246
+ });
247
+
248
+ const user = await this.findUserById(input.userId);
249
+ if (!user) {
250
+ throw new NotFoundException('User not found');
251
+ }
252
+ return user;
253
+ }
254
+
255
+ async softDeleteUser(userId: string, deletedAt: Date): Promise<void> {
256
+ await this.prisma.user.update({
257
+ where: { id: userId },
258
+ data: {
259
+ status: 'deleted',
260
+ deletedAt,
261
+ },
262
+ });
263
+ }
264
+
265
+ private mapPasswordAccount(user: {
266
+ id: string;
267
+ status: string;
268
+ data: Prisma.JsonValue | null;
269
+ createdAt: Date;
270
+ updatedAt: Date;
271
+ deletedAt: Date | null;
272
+ profile: { name: string | null; avatar: string | null; data: Prisma.JsonValue | null } | null;
273
+ settings: { theme: string | null; locale: string | null; data: Prisma.JsonValue | null } | null;
274
+ authCredential: { passwordHash: string } | null;
275
+ authIdentities: Array<{ provider?: string; providerId: string }>;
276
+ }): PasswordAccountRecord {
277
+ const emailIdentity = user.authIdentities[0];
278
+ return {
279
+ ...this.mapUser(user),
280
+ provider: 'email',
281
+ providerId: emailIdentity?.providerId ?? '',
282
+ passwordHash: user.authCredential?.passwordHash ?? null,
283
+ };
284
+ }
285
+
286
+ private mapUser(user: {
287
+ id: string;
288
+ status: string;
289
+ data: Prisma.JsonValue | null;
290
+ createdAt: Date;
291
+ updatedAt: Date;
292
+ deletedAt: Date | null;
293
+ profile: { name: string | null; avatar: string | null; data: Prisma.JsonValue | null } | null;
294
+ settings: { theme: string | null; locale: string | null; data: Prisma.JsonValue | null } | null;
295
+ authIdentities: Array<{ providerId: string }>;
296
+ }) {
297
+ return {
298
+ id: user.id,
299
+ email: user.authIdentities[0]?.providerId ?? null,
300
+ status: user.status,
301
+ data: this.fromJson(user.data),
302
+ createdAt: user.createdAt,
303
+ updatedAt: user.updatedAt,
304
+ deletedAt: user.deletedAt,
305
+ profile: user.profile
306
+ ? {
307
+ name: user.profile.name,
308
+ avatar: user.profile.avatar,
309
+ data: this.fromJson(user.profile.data),
310
+ }
311
+ : null,
312
+ settings: user.settings
313
+ ? {
314
+ theme: user.settings.theme,
315
+ locale: user.settings.locale,
316
+ data: this.fromJson(user.settings.data),
317
+ }
318
+ : null,
319
+ };
320
+ }
321
+
322
+ private toNullableJson(value: Record<string, unknown> | null) {
323
+ return value === null ? Prisma.JsonNull : (value as Prisma.InputJsonValue);
324
+ }
325
+
326
+ private fromJson(value: Prisma.JsonValue | null): Record<string, unknown> | null {
327
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
328
+ return null;
329
+ }
330
+ return value as Record<string, unknown>;
331
+ }
332
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "@forgeon/auth-api",
2
+ "name": "@forgeon/accounts-api",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "main": "dist/index.js",
@@ -8,21 +8,21 @@
8
8
  "build": "tsc -p tsconfig.json"
9
9
  },
10
10
  "dependencies": {
11
- "@forgeon/auth-contracts": "workspace:*",
11
+ "@forgeon/accounts-contracts": "workspace:*",
12
12
  "@nestjs/common": "^11.0.1",
13
13
  "@nestjs/config": "^4.0.2",
14
14
  "@nestjs/jwt": "^11.0.1",
15
15
  "@nestjs/passport": "^11.0.5",
16
- "bcryptjs": "^2.4.3",
16
+ "argon2": "^0.41.1",
17
+ "class-transformer": "^0.5.1",
17
18
  "class-validator": "^0.14.1",
18
19
  "passport": "^0.7.0",
19
20
  "passport-jwt": "^4.0.1",
20
21
  "zod": "^3.23.8"
21
22
  },
22
23
  "devDependencies": {
23
- "@types/bcryptjs": "^2.4.6",
24
24
  "@types/node": "^22.10.7",
25
25
  "@types/passport-jwt": "^4.0.1",
26
26
  "typescript": "^5.7.3"
27
27
  }
28
- }
28
+ }
@@ -0,0 +1,13 @@
1
+ export const ACCOUNTS_EMAIL_PORT = 'FORGEON_ACCOUNTS_EMAIL_PORT';
2
+
3
+ export interface AccountsEmailPort {
4
+ sendVerificationEmail(input: { email: string; token: string; userId: string }): Promise<void>;
5
+ sendPasswordResetEmail(input: { email: string; token: string; userId: string }): Promise<void>;
6
+ sendWelcomeEmail(input: { email: string; userId: string }): Promise<void>;
7
+ }
8
+
9
+ export class StubAccountsEmailAdapter implements AccountsEmailPort {
10
+ async sendVerificationEmail(): Promise<void> {}
11
+ async sendPasswordResetEmail(): Promise<void> {}
12
+ async sendWelcomeEmail(): Promise<void> {}
13
+ }
@@ -0,0 +1,67 @@
1
+ import type { IdentityProvider, JsonObject } from '@forgeon/accounts-contracts';
2
+ import type { UserRecord } from './users.types';
3
+
4
+ export const ACCOUNTS_PERSISTENCE_PORT = 'FORGEON_ACCOUNTS_PERSISTENCE_PORT';
5
+
6
+ export type PasswordAccountRecord = UserRecord & {
7
+ provider: IdentityProvider;
8
+ providerId: string;
9
+ passwordHash: string | null;
10
+ };
11
+
12
+ export type RefreshTokenRecord = {
13
+ id: string;
14
+ userId: string;
15
+ tokenHash: string;
16
+ expiresAt: Date;
17
+ revokedAt: Date | null;
18
+ createdAt: Date;
19
+ };
20
+
21
+ export interface CreatePasswordAccountInput {
22
+ email: string;
23
+ passwordHash: string;
24
+ status: string;
25
+ userData: JsonObject | null;
26
+ profile: {
27
+ name: string | null;
28
+ avatar: string | null;
29
+ data: JsonObject | null;
30
+ };
31
+ settings: {
32
+ theme: string | null;
33
+ locale: string | null;
34
+ data: JsonObject | null;
35
+ };
36
+ }
37
+
38
+ export interface AccountsPersistencePort {
39
+ createPasswordAccount(input: CreatePasswordAccountInput): Promise<PasswordAccountRecord>;
40
+ findPasswordAccountByEmail(email: string): Promise<PasswordAccountRecord | null>;
41
+ findAccountByUserId(userId: string): Promise<PasswordAccountRecord | null>;
42
+ updatePassword(userId: string, passwordHash: string): Promise<void>;
43
+ createRefreshToken(input: {
44
+ id: string;
45
+ userId: string;
46
+ tokenHash: string;
47
+ expiresAt: Date;
48
+ }): Promise<void>;
49
+ findRefreshTokenById(id: string): Promise<RefreshTokenRecord | null>;
50
+ revokeRefreshToken(id: string, revokedAt: Date): Promise<void>;
51
+ revokeRefreshTokensForUser(userId: string, revokedAt: Date): Promise<void>;
52
+ findUserById(userId: string): Promise<UserRecord | null>;
53
+ updateUser(input: { userId: string; data: JsonObject | null }): Promise<UserRecord>;
54
+ updateUserProfile(input: {
55
+ userId: string;
56
+ name: string | null;
57
+ avatar: string | null;
58
+ data: JsonObject | null;
59
+ }): Promise<UserRecord>;
60
+ updateUserSettings(input: {
61
+ userId: string;
62
+ theme: string | null;
63
+ locale: string | null;
64
+ data: JsonObject | null;
65
+ }): Promise<UserRecord>;
66
+ softDeleteUser(userId: string, deletedAt: Date): Promise<void>;
67
+ }
@@ -0,0 +1,14 @@
1
+ export const ACCOUNTS_AUTHZ_CLAIMS_RESOLVER = 'FORGEON_ACCOUNTS_AUTHZ_CLAIMS_RESOLVER';
2
+
3
+ export interface AccountsAuthzClaimsResolver {
4
+ resolveClaims(_userId: string): Promise<{
5
+ roles?: string[];
6
+ permissions?: string[];
7
+ }>;
8
+ }
9
+
10
+ export class NoopAccountsAuthzClaimsResolver implements AccountsAuthzClaimsResolver {
11
+ async resolveClaims(): Promise<{ roles?: string[]; permissions?: string[] }> {
12
+ return {};
13
+ }
14
+ }
@@ -8,9 +8,9 @@ export interface AuthConfigValues {
8
8
  accessExpiresIn: string;
9
9
  refreshSecret: string;
10
10
  refreshExpiresIn: string;
11
- bcryptRounds: number;
12
- demoEmail: string;
13
- demoPassword: string;
11
+ argon2MemoryCost: number;
12
+ argon2TimeCost: number;
13
+ argon2Parallelism: number;
14
14
  }
15
15
 
16
16
  export const authConfig = registerAs(AUTH_CONFIG_NAMESPACE, (): AuthConfigValues => {
@@ -20,8 +20,8 @@ export const authConfig = registerAs(AUTH_CONFIG_NAMESPACE, (): AuthConfigValues
20
20
  accessExpiresIn: env.JWT_ACCESS_EXPIRES_IN,
21
21
  refreshSecret: env.JWT_REFRESH_SECRET,
22
22
  refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN,
23
- bcryptRounds: env.AUTH_BCRYPT_ROUNDS,
24
- demoEmail: env.AUTH_DEMO_EMAIL.toLowerCase(),
25
- demoPassword: env.AUTH_DEMO_PASSWORD,
23
+ argon2MemoryCost: env.AUTH_ARGON2_MEMORY_COST,
24
+ argon2TimeCost: env.AUTH_ARGON2_TIME_COST,
25
+ argon2Parallelism: env.AUTH_ARGON2_PARALLELISM,
26
26
  };
27
- });
27
+ });
@@ -22,15 +22,15 @@ export class AuthConfigService {
22
22
  return this.configService.getOrThrow<string>(`${AUTH_CONFIG_NAMESPACE}.refreshExpiresIn`);
23
23
  }
24
24
 
25
- get bcryptRounds(): number {
26
- return this.configService.getOrThrow<number>(`${AUTH_CONFIG_NAMESPACE}.bcryptRounds`);
25
+ get argon2MemoryCost(): number {
26
+ return this.configService.getOrThrow<number>(`${AUTH_CONFIG_NAMESPACE}.argon2MemoryCost`);
27
27
  }
28
28
 
29
- get demoEmail(): string {
30
- return this.configService.getOrThrow<string>(`${AUTH_CONFIG_NAMESPACE}.demoEmail`);
29
+ get argon2TimeCost(): number {
30
+ return this.configService.getOrThrow<number>(`${AUTH_CONFIG_NAMESPACE}.argon2TimeCost`);
31
31
  }
32
32
 
33
- get demoPassword(): string {
34
- return this.configService.getOrThrow<string>(`${AUTH_CONFIG_NAMESPACE}.demoPassword`);
33
+ get argon2Parallelism(): number {
34
+ return this.configService.getOrThrow<number>(`${AUTH_CONFIG_NAMESPACE}.argon2Parallelism`);
35
35
  }
36
- }
36
+ }