create-forgeon 0.3.18 → 0.3.20

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 (34) hide show
  1. package/package.json +1 -1
  2. package/src/cli/add-help.mjs +3 -2
  3. package/src/core/docs.test.mjs +1 -0
  4. package/src/core/scaffold.test.mjs +1 -0
  5. package/src/modules/accounts.mjs +28 -22
  6. package/src/modules/dependencies.mjs +153 -4
  7. package/src/modules/dependencies.test.mjs +58 -0
  8. package/src/modules/executor.test.mjs +73 -37
  9. package/src/modules/files-image.mjs +6 -4
  10. package/src/modules/files.mjs +5 -6
  11. package/src/modules/idempotency.test.mjs +3 -2
  12. package/src/modules/registry.mjs +20 -0
  13. package/src/modules/shared/files-runtime-wiring.mjs +13 -10
  14. package/src/run-add-module.mjs +39 -26
  15. package/src/run-add-module.test.mjs +76 -0
  16. package/src/run-scan-integrations.mjs +1 -0
  17. package/templates/base/package.json +1 -0
  18. package/templates/module-presets/accounts/packages/accounts-api/package.json +1 -0
  19. package/templates/module-presets/accounts/packages/accounts-api/src/auth-core.service.ts +15 -19
  20. package/templates/module-presets/accounts/{apps/api/src/accounts/prisma-accounts-persistence.store.ts → packages/accounts-api/src/auth.store.ts} +44 -166
  21. package/templates/module-presets/accounts/packages/accounts-api/src/forgeon-accounts.module.ts +7 -1
  22. package/templates/module-presets/accounts/packages/accounts-api/src/index.ts +3 -4
  23. package/templates/module-presets/accounts/packages/accounts-api/src/users.service.ts +10 -11
  24. package/templates/module-presets/accounts/packages/accounts-api/src/users.store.ts +113 -0
  25. package/templates/module-presets/accounts/packages/accounts-api/src/users.types.ts +48 -0
  26. package/templates/module-presets/files/packages/files/package.json +1 -0
  27. package/templates/module-presets/files/packages/files/src/files.ports.ts +0 -95
  28. package/templates/module-presets/files/packages/files/src/files.service.ts +43 -36
  29. package/templates/module-presets/files/{apps/api/src/files/prisma-files-persistence.store.ts → packages/files/src/files.store.ts} +77 -13
  30. package/templates/module-presets/files/packages/files/src/forgeon-files.module.ts +7 -116
  31. package/templates/module-presets/files/packages/files/src/index.ts +1 -0
  32. package/templates/module-presets/accounts/apps/api/src/accounts/forgeon-accounts-db-prisma.module.ts +0 -17
  33. package/templates/module-presets/accounts/packages/accounts-api/src/accounts-persistence.port.ts +0 -67
  34. package/templates/module-presets/files/apps/api/src/files/forgeon-files-db-prisma.module.ts +0 -17
@@ -1,15 +1,43 @@
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
1
  import { Injectable, NotFoundException } from '@nestjs/common';
2
+ import type { IdentityProvider, JsonObject } from '@forgeon/accounts-contracts';
3
+ import { PrismaService } from '@forgeon/db-prisma';
4
+ import type { UserRecord } from './users.types';
5
+ import { mapUserRecord, toPrismaJsonInput } from './users.types';
6
+
7
+ export type PasswordAccountRecord = UserRecord & {
8
+ provider: IdentityProvider;
9
+ providerId: string;
10
+ passwordHash: string | null;
11
+ };
12
+
13
+ export type RefreshTokenRecord = {
14
+ id: string;
15
+ userId: string;
16
+ tokenHash: string;
17
+ expiresAt: Date;
18
+ revokedAt: Date | null;
19
+ createdAt: Date;
20
+ };
21
+
22
+ export interface CreatePasswordAccountInput {
23
+ email: string;
24
+ passwordHash: string;
25
+ status: string;
26
+ userData: JsonObject | null;
27
+ profile: {
28
+ name: string | null;
29
+ avatar: string | null;
30
+ data: JsonObject | null;
31
+ };
32
+ settings: {
33
+ theme: string | null;
34
+ locale: string | null;
35
+ data: JsonObject | null;
36
+ };
37
+ }
10
38
 
11
39
  @Injectable()
12
- export class PrismaAccountsPersistenceStore implements AccountsPersistencePort {
40
+ export class AuthStore {
13
41
  constructor(private readonly prisma: PrismaService) {}
14
42
 
15
43
  async createPasswordAccount(input: CreatePasswordAccountInput): Promise<PasswordAccountRecord> {
@@ -17,19 +45,19 @@ export class PrismaAccountsPersistenceStore implements AccountsPersistencePort {
17
45
  const user = await tx.user.create({
18
46
  data: {
19
47
  status: input.status,
20
- data: this.toNullableJson(input.userData),
48
+ data: toPrismaJsonInput(input.userData),
21
49
  profile: {
22
50
  create: {
23
51
  name: input.profile.name,
24
52
  avatar: input.profile.avatar,
25
- data: this.toNullableJson(input.profile.data),
53
+ data: toPrismaJsonInput(input.profile.data),
26
54
  },
27
55
  },
28
56
  settings: {
29
57
  create: {
30
58
  theme: input.settings.theme,
31
59
  locale: input.settings.locale,
32
- data: this.toNullableJson(input.settings.data),
60
+ data: toPrismaJsonInput(input.settings.data),
33
61
  },
34
62
  },
35
63
  },
@@ -159,174 +187,24 @@ export class PrismaAccountsPersistenceStore implements AccountsPersistencePort {
159
187
  });
160
188
  }
161
189
 
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
190
  private mapPasswordAccount(user: {
266
191
  id: string;
267
192
  status: string;
268
- data: Prisma.JsonValue | null;
193
+ data: unknown;
269
194
  createdAt: Date;
270
195
  updatedAt: Date;
271
196
  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;
197
+ profile: { name: string | null; avatar: string | null; data: unknown } | null;
198
+ settings: { theme: string | null; locale: string | null; data: unknown } | null;
274
199
  authCredential: { passwordHash: string } | null;
275
200
  authIdentities: Array<{ provider?: string; providerId: string }>;
276
201
  }): PasswordAccountRecord {
277
202
  const emailIdentity = user.authIdentities[0];
278
203
  return {
279
- ...this.mapUser(user),
204
+ ...mapUserRecord(user),
280
205
  provider: 'email',
281
206
  providerId: emailIdentity?.providerId ?? '',
282
207
  passwordHash: user.authCredential?.passwordHash ?? null,
283
208
  };
284
209
  }
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
210
  }
@@ -1,4 +1,4 @@
1
- import {
1
+ import {
2
2
  DynamicModule,
3
3
  Module,
4
4
  ModuleMetadata,
@@ -6,6 +6,7 @@
6
6
  } from '@nestjs/common';
7
7
  import { JwtModule } from '@nestjs/jwt';
8
8
  import { PassportModule } from '@nestjs/passport';
9
+ import { DbPrismaModule } from '@forgeon/db-prisma';
9
10
  import {
10
11
  ACCOUNTS_AUTHZ_CLAIMS_RESOLVER,
11
12
  NoopAccountsAuthzClaimsResolver,
@@ -17,12 +18,14 @@ import { AuthCoreService } from './auth-core.service';
17
18
  import { AuthJwtService } from './auth-jwt.service';
18
19
  import { AuthPasswordService } from './auth-password.service';
19
20
  import { AuthService } from './auth.service';
21
+ import { AuthStore } from './auth.store';
20
22
  import { JwtAuthGuard } from './access-token.guard';
21
23
  import { JwtStrategy } from './jwt.strategy';
22
24
  import { OwnerAccessGuard } from './owner-access.guard';
23
25
  import { UsersController } from './users.controller';
24
26
  import { UsersModule, USERS_MODULE_OPTIONS, type UsersModuleOptions } from './users-config';
25
27
  import { UsersService } from './users.service';
28
+ import { UsersStore } from './users.store';
26
29
 
27
30
  export interface ForgeonAccountsModuleOptions {
28
31
  imports?: ModuleMetadata['imports'];
@@ -37,6 +40,7 @@ export class ForgeonAccountsModule {
37
40
  module: ForgeonAccountsModule,
38
41
  imports: [
39
42
  AuthConfigModule,
43
+ DbPrismaModule,
40
44
  PassportModule.register({ defaultStrategy: 'jwt' }),
41
45
  JwtModule.register({}),
42
46
  ...(options.imports ?? []),
@@ -55,6 +59,8 @@ export class ForgeonAccountsModule {
55
59
  provide: ACCOUNTS_AUTHZ_CLAIMS_RESOLVER,
56
60
  useClass: NoopAccountsAuthzClaimsResolver,
57
61
  },
62
+ AuthStore,
63
+ UsersStore,
58
64
  AuthCoreService,
59
65
  AuthJwtService,
60
66
  AuthPasswordService,
@@ -1,5 +1,4 @@
1
- export * from './accounts-email.port';
2
- export * from './accounts-persistence.port';
1
+ export * from './accounts-email.port';
3
2
  export * from './accounts-rbac.port';
4
3
  export * from './auth-config.loader';
5
4
  export * from './auth-config.module';
@@ -10,6 +9,7 @@ export * from './auth-env.schema';
10
9
  export * from './auth-jwt.service';
11
10
  export * from './auth-password.service';
12
11
  export * from './auth.service';
12
+ export * from './auth.store';
13
13
  export * from './auth.types';
14
14
  export * from './dto';
15
15
  export * from './forgeon-accounts.module';
@@ -19,6 +19,5 @@ export * from './owner-access.guard';
19
19
  export * from './users-config';
20
20
  export * from './users.controller';
21
21
  export * from './users.service';
22
+ export * from './users.store';
22
23
  export * from './users.types';
23
-
24
-
@@ -1,20 +1,19 @@
1
1
  import { Inject, Injectable, NotFoundException } from '@nestjs/common';
2
2
  import type { UpdateUserProfileRequest, UpdateUserSettingsRequest, UpdateUserRequest } from '@forgeon/accounts-contracts';
3
- import { ACCOUNTS_PERSISTENCE_PORT, type AccountsPersistencePort } from './accounts-persistence.port';
4
3
  import { USERS_MODULE_OPTIONS, type UsersModuleOptions } from './users-config';
4
+ import { UsersStore } from './users.store';
5
5
  import { mergeObjects, normalizeObject, toUserRecordDto } from './users.types';
6
6
 
7
7
  @Injectable()
8
8
  export class UsersService {
9
9
  constructor(
10
- @Inject(ACCOUNTS_PERSISTENCE_PORT)
11
- private readonly persistence: AccountsPersistencePort,
10
+ private readonly usersStore: UsersStore,
12
11
  @Inject(USERS_MODULE_OPTIONS)
13
12
  private readonly usersModuleOptions: UsersModuleOptions,
14
13
  ) {}
15
14
 
16
15
  async findById(userId: string) {
17
- const user = await this.persistence.findUserById(userId);
16
+ const user = await this.usersStore.findById(userId);
18
17
  return user ? toUserRecordDto(user) : null;
19
18
  }
20
19
 
@@ -27,12 +26,12 @@ export class UsersService {
27
26
  }
28
27
 
29
28
  async update(userId: string, input: UpdateUserRequest) {
30
- const current = await this.persistence.findUserById(userId);
29
+ const current = await this.usersStore.findById(userId);
31
30
  if (!current) {
32
31
  throw new NotFoundException('User not found');
33
32
  }
34
33
 
35
- const updated = await this.persistence.updateUser({
34
+ const updated = await this.usersStore.updateUser({
36
35
  userId,
37
36
  data: mergeObjects(current.data ?? this.usersModuleOptions.user, input.data),
38
37
  });
@@ -40,12 +39,12 @@ export class UsersService {
40
39
  }
41
40
 
42
41
  async updateProfile(userId: string, input: UpdateUserProfileRequest) {
43
- const current = await this.persistence.findUserById(userId);
42
+ const current = await this.usersStore.findById(userId);
44
43
  if (!current) {
45
44
  throw new NotFoundException('User not found');
46
45
  }
47
46
 
48
- const updated = await this.persistence.updateUserProfile({
47
+ const updated = await this.usersStore.updateUserProfile({
49
48
  userId,
50
49
  name: input.name ?? current.profile?.name ?? null,
51
50
  avatar: input.avatar ?? current.profile?.avatar ?? null,
@@ -55,12 +54,12 @@ export class UsersService {
55
54
  }
56
55
 
57
56
  async updateSettings(userId: string, input: UpdateUserSettingsRequest) {
58
- const current = await this.persistence.findUserById(userId);
57
+ const current = await this.usersStore.findById(userId);
59
58
  if (!current) {
60
59
  throw new NotFoundException('User not found');
61
60
  }
62
61
 
63
- const updated = await this.persistence.updateUserSettings({
62
+ const updated = await this.usersStore.updateUserSettings({
64
63
  userId,
65
64
  theme: input.theme ?? current.settings?.theme ?? null,
66
65
  locale: input.locale ?? current.settings?.locale ?? null,
@@ -70,7 +69,7 @@ export class UsersService {
70
69
  }
71
70
 
72
71
  async softDelete(userId: string): Promise<void> {
73
- await this.persistence.softDeleteUser(userId, new Date());
72
+ await this.usersStore.softDelete(userId, new Date());
74
73
  }
75
74
 
76
75
  resolveUserData(input: unknown) {
@@ -0,0 +1,113 @@
1
+ import { Injectable, NotFoundException } from '@nestjs/common';
2
+ import type { JsonObject } from '@forgeon/accounts-contracts';
3
+ import { PrismaService } from '@forgeon/db-prisma';
4
+ import type { UserRecord } from './users.types';
5
+ import { mapUserRecord, toPrismaJsonInput } from './users.types';
6
+
7
+ @Injectable()
8
+ export class UsersStore {
9
+ constructor(private readonly prisma: PrismaService) {}
10
+
11
+ async findById(userId: string): Promise<UserRecord | null> {
12
+ const user = await this.prisma.user.findUnique({
13
+ where: { id: userId },
14
+ include: {
15
+ profile: true,
16
+ settings: true,
17
+ authIdentities: {
18
+ where: { provider: 'email' },
19
+ select: { providerId: true },
20
+ take: 1,
21
+ },
22
+ },
23
+ });
24
+
25
+ return user ? mapUserRecord(user) : null;
26
+ }
27
+
28
+ async updateUser(input: { userId: string; data: JsonObject | null }): Promise<UserRecord> {
29
+ const user = await this.prisma.user.update({
30
+ where: { id: input.userId },
31
+ data: {
32
+ data: toPrismaJsonInput(input.data),
33
+ },
34
+ include: {
35
+ profile: true,
36
+ settings: true,
37
+ authIdentities: {
38
+ where: { provider: 'email' },
39
+ select: { providerId: true },
40
+ take: 1,
41
+ },
42
+ },
43
+ });
44
+
45
+ return mapUserRecord(user);
46
+ }
47
+
48
+ async updateUserProfile(input: {
49
+ userId: string;
50
+ name: string | null;
51
+ avatar: string | null;
52
+ data: JsonObject | null;
53
+ }): Promise<UserRecord> {
54
+ await this.prisma.userProfile.upsert({
55
+ where: { userId: input.userId },
56
+ create: {
57
+ userId: input.userId,
58
+ name: input.name,
59
+ avatar: input.avatar,
60
+ data: toPrismaJsonInput(input.data),
61
+ },
62
+ update: {
63
+ name: input.name,
64
+ avatar: input.avatar,
65
+ data: toPrismaJsonInput(input.data),
66
+ },
67
+ });
68
+
69
+ const user = await this.findById(input.userId);
70
+ if (!user) {
71
+ throw new NotFoundException('User not found');
72
+ }
73
+ return user;
74
+ }
75
+
76
+ async updateUserSettings(input: {
77
+ userId: string;
78
+ theme: string | null;
79
+ locale: string | null;
80
+ data: JsonObject | null;
81
+ }): Promise<UserRecord> {
82
+ await this.prisma.userSettings.upsert({
83
+ where: { userId: input.userId },
84
+ create: {
85
+ userId: input.userId,
86
+ theme: input.theme,
87
+ locale: input.locale,
88
+ data: toPrismaJsonInput(input.data),
89
+ },
90
+ update: {
91
+ theme: input.theme,
92
+ locale: input.locale,
93
+ data: toPrismaJsonInput(input.data),
94
+ },
95
+ });
96
+
97
+ const user = await this.findById(input.userId);
98
+ if (!user) {
99
+ throw new NotFoundException('User not found');
100
+ }
101
+ return user;
102
+ }
103
+
104
+ async softDelete(userId: string, deletedAt: Date): Promise<void> {
105
+ await this.prisma.user.update({
106
+ where: { id: userId },
107
+ data: {
108
+ status: 'deleted',
109
+ deletedAt,
110
+ },
111
+ });
112
+ }
113
+ }
@@ -34,6 +34,54 @@ export function mergeObjects(baseValue: unknown, patchValue: unknown): JsonObjec
34
34
  return Object.keys(merged).length > 0 ? merged : null;
35
35
  }
36
36
 
37
+ export function toPrismaJsonInput(value: JsonObject | null) {
38
+ return (value ?? undefined) as never;
39
+ }
40
+
41
+ export function mapUserRecord(source: {
42
+ id: string;
43
+ status: string;
44
+ data: unknown;
45
+ createdAt: Date;
46
+ updatedAt: Date;
47
+ deletedAt: Date | null;
48
+ profile?: {
49
+ name: string | null;
50
+ avatar: string | null;
51
+ data: unknown;
52
+ } | null;
53
+ settings?: {
54
+ theme: string | null;
55
+ locale: string | null;
56
+ data: unknown;
57
+ } | null;
58
+ authIdentities?: Array<{ providerId: string }>;
59
+ }): UserRecord {
60
+ return {
61
+ id: source.id,
62
+ email: source.authIdentities?.[0]?.providerId ?? null,
63
+ status: source.status,
64
+ data: normalizeObject(source.data),
65
+ createdAt: source.createdAt,
66
+ updatedAt: source.updatedAt,
67
+ deletedAt: source.deletedAt,
68
+ profile: source.profile
69
+ ? {
70
+ name: source.profile.name,
71
+ avatar: source.profile.avatar,
72
+ data: normalizeObject(source.profile.data),
73
+ }
74
+ : null,
75
+ settings: source.settings
76
+ ? {
77
+ theme: source.settings.theme,
78
+ locale: source.settings.locale,
79
+ data: normalizeObject(source.settings.data),
80
+ }
81
+ : null,
82
+ };
83
+ }
84
+
37
85
  export function toProfileDto(record: UserRecord['profile']): UserProfileDto {
38
86
  return {
39
87
  name: record?.name ?? null,
@@ -8,6 +8,7 @@
8
8
  "build": "tsc -p tsconfig.json"
9
9
  },
10
10
  "dependencies": {
11
+ "@forgeon/db-prisma": "workspace:*",
11
12
  "@nestjs/common": "^11.0.1",
12
13
  "@nestjs/config": "^4.0.0",
13
14
  "@nestjs/platform-express": "^11.0.1",
@@ -1,102 +1,7 @@
1
1
  import { Readable } from 'node:stream';
2
- import type { FileVariantKey } from './files.types';
3
2
 
4
- export const FILES_PERSISTENCE_PORT = 'FORGEON_FILES_PERSISTENCE_PORT';
5
3
  export const FILES_STORAGE_ADAPTER = 'FORGEON_FILES_STORAGE_ADAPTER';
6
4
 
7
- export type FilesBlobRecord = {
8
- id: string;
9
- hash: string;
10
- size: number;
11
- mimeType: string;
12
- storageDriver: string;
13
- storageKey: string;
14
- };
15
-
16
- export type FilesBlobRef = FilesBlobRecord & {
17
- created: boolean;
18
- };
19
-
20
- export type FilesRecordVariant = {
21
- variantKey: string;
22
- blobId: string;
23
- mimeType: string;
24
- size: number;
25
- status: string;
26
- blob?: {
27
- storageDriver: string;
28
- storageKey: string;
29
- };
30
- };
31
-
32
- export type FilesRecordAggregate = {
33
- id: string;
34
- publicId: string;
35
- storageKey: string;
36
- originalName: string;
37
- mimeType: string;
38
- size: number;
39
- storageDriver: string;
40
- ownerType: string;
41
- ownerId: string | null;
42
- visibility: string;
43
- createdById: string | null;
44
- createdAt: Date;
45
- updatedAt: Date;
46
- variants?: FilesRecordVariant[];
47
- };
48
-
49
- export type FilesRecordCreateInput = {
50
- publicId: string;
51
- storageKey: string;
52
- originalName: string;
53
- mimeType: string;
54
- size: number;
55
- storageDriver: string;
56
- ownerType: string;
57
- ownerId: string | null;
58
- visibility: string;
59
- createdById: string | null;
60
- };
61
-
62
- export type FilesBlobCreateInput = {
63
- hash: string;
64
- size: number;
65
- mimeType: string;
66
- storageDriver: string;
67
- storageKey: string;
68
- };
69
-
70
- export type FilesVariantCreateInput = {
71
- fileId: string;
72
- variantKey: FileVariantKey;
73
- blobId: string;
74
- mimeType: string;
75
- size: number;
76
- status: string;
77
- };
78
-
79
- export interface FilesPersistencePort {
80
- createFileRecord(data: FilesRecordCreateInput): Promise<{
81
- id: string;
82
- publicId: string;
83
- }>;
84
- deleteFileRecordById(id: string): Promise<void>;
85
- deleteFileRecordByPublicId(publicId: string): Promise<void>;
86
- findFileRecordWithVariantKeys(publicId: string): Promise<FilesRecordAggregate | null>;
87
- findFileRecordForDelete(publicId: string): Promise<FilesRecordAggregate | null>;
88
- findFileRecordForDownload(publicId: string): Promise<FilesRecordAggregate | null>;
89
- countOwnerUsage(ownerType: string, ownerId: string): Promise<{
90
- filesCount: number;
91
- totalBytes: number;
92
- }>;
93
- findBlobRef(hash: string, size: number, mimeType: string, storageDriver: string): Promise<FilesBlobRef | null>;
94
- createBlob(data: FilesBlobCreateInput): Promise<FilesBlobRecord>;
95
- createVariants(data: FilesVariantCreateInput[]): Promise<void>;
96
- findBlobById(id: string): Promise<FilesBlobRecord | null>;
97
- deleteBlobIfUnreferenced(id: string): Promise<boolean>;
98
- }
99
-
100
5
  export interface FilesStorageAdapter {
101
6
  readonly driver: string;
102
7
  put(buffer: Buffer, fileName: string): Promise<{