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,164 @@
1
+ import {
2
+ FILES_PERSISTENCE_PORT,
3
+ type FilesBlobCreateInput,
4
+ type FilesBlobRef,
5
+ type FilesPersistencePort,
6
+ type FilesRecordAggregate,
7
+ type FilesRecordCreateInput,
8
+ type FilesVariantCreateInput,
9
+ } from '@forgeon/files';
10
+ import { PrismaService } from '@forgeon/db-prisma';
11
+ import { Injectable } from '@nestjs/common';
12
+
13
+ @Injectable()
14
+ export class PrismaFilesPersistenceStore implements FilesPersistencePort {
15
+ constructor(private readonly prisma: PrismaService) {}
16
+
17
+ async createFileRecord(data: FilesRecordCreateInput): Promise<{ id: string; publicId: string }> {
18
+ const record = await this.prisma.fileRecord.create({ data });
19
+ return {
20
+ id: record.id,
21
+ publicId: record.publicId,
22
+ };
23
+ }
24
+
25
+ async deleteFileRecordById(id: string): Promise<void> {
26
+ await this.prisma.fileRecord.delete({ where: { id } });
27
+ }
28
+
29
+ async deleteFileRecordByPublicId(publicId: string): Promise<void> {
30
+ await this.prisma.fileRecord.delete({ where: { publicId } });
31
+ }
32
+
33
+ async findFileRecordWithVariantKeys(publicId: string): Promise<FilesRecordAggregate | null> {
34
+ return this.prisma.fileRecord.findUnique({
35
+ where: { publicId },
36
+ include: {
37
+ variants: {
38
+ select: {
39
+ variantKey: true,
40
+ blobId: true,
41
+ mimeType: true,
42
+ size: true,
43
+ status: true,
44
+ },
45
+ },
46
+ },
47
+ });
48
+ }
49
+
50
+ async findFileRecordForDelete(publicId: string): Promise<FilesRecordAggregate | null> {
51
+ return this.prisma.fileRecord.findUnique({
52
+ where: { publicId },
53
+ include: {
54
+ variants: {
55
+ select: {
56
+ variantKey: true,
57
+ blobId: true,
58
+ mimeType: true,
59
+ size: true,
60
+ status: true,
61
+ blob: {
62
+ select: {
63
+ storageDriver: true,
64
+ storageKey: true,
65
+ },
66
+ },
67
+ },
68
+ },
69
+ },
70
+ });
71
+ }
72
+
73
+ async findFileRecordForDownload(publicId: string): Promise<FilesRecordAggregate | null> {
74
+ return this.prisma.fileRecord.findUnique({
75
+ where: { publicId },
76
+ include: {
77
+ variants: {
78
+ select: {
79
+ variantKey: true,
80
+ blobId: true,
81
+ mimeType: true,
82
+ size: true,
83
+ status: true,
84
+ blob: {
85
+ select: {
86
+ storageDriver: true,
87
+ storageKey: true,
88
+ },
89
+ },
90
+ },
91
+ },
92
+ },
93
+ });
94
+ }
95
+
96
+ async countOwnerUsage(ownerType: string, ownerId: string): Promise<{ filesCount: number; totalBytes: number }> {
97
+ const aggregate = await this.prisma.fileRecord.aggregate({
98
+ where: {
99
+ ownerType,
100
+ ownerId,
101
+ },
102
+ _count: {
103
+ _all: true,
104
+ },
105
+ _sum: {
106
+ size: true,
107
+ },
108
+ });
109
+
110
+ return {
111
+ filesCount: aggregate._count._all ?? 0,
112
+ totalBytes: aggregate._sum.size ?? 0,
113
+ };
114
+ }
115
+
116
+ async findBlobRef(hash: string, size: number, mimeType: string, storageDriver: string): Promise<FilesBlobRef | null> {
117
+ const existing = await this.prisma.fileBlob.findFirst({
118
+ where: {
119
+ hash,
120
+ size,
121
+ mimeType,
122
+ storageDriver,
123
+ },
124
+ });
125
+ if (!existing) {
126
+ return null;
127
+ }
128
+ return {
129
+ id: existing.id,
130
+ hash: existing.hash,
131
+ size: existing.size,
132
+ mimeType: existing.mimeType,
133
+ storageDriver: existing.storageDriver,
134
+ storageKey: existing.storageKey,
135
+ created: false,
136
+ };
137
+ }
138
+
139
+ async createBlob(data: FilesBlobCreateInput) {
140
+ return this.prisma.fileBlob.create({ data });
141
+ }
142
+
143
+ async createVariants(data: FilesVariantCreateInput[]): Promise<void> {
144
+ await this.prisma.fileVariant.createMany({ data });
145
+ }
146
+
147
+ async findBlobById(id: string) {
148
+ return this.prisma.fileBlob.findUnique({
149
+ where: { id },
150
+ });
151
+ }
152
+
153
+ async deleteBlobIfUnreferenced(id: string): Promise<boolean> {
154
+ const deleted = await this.prisma.fileBlob.deleteMany({
155
+ where: {
156
+ id,
157
+ variants: {
158
+ none: {},
159
+ },
160
+ },
161
+ });
162
+ return deleted.count > 0;
163
+ }
164
+ }
@@ -8,7 +8,6 @@
8
8
  "build": "tsc -p tsconfig.json"
9
9
  },
10
10
  "dependencies": {
11
- "@forgeon/db-prisma": "workspace:*",
12
11
  "@nestjs/common": "^11.0.1",
13
12
  "@nestjs/config": "^4.0.0",
14
13
  "@nestjs/platform-express": "^11.0.1",
@@ -21,4 +20,4 @@
21
20
  "@types/node": "^22.10.7",
22
21
  "typescript": "^5.7.3"
23
22
  }
24
- }
23
+ }
@@ -0,0 +1,107 @@
1
+ import { Readable } from 'node:stream';
2
+ import type { FileVariantKey } from './files.types';
3
+
4
+ export const FILES_PERSISTENCE_PORT = 'FORGEON_FILES_PERSISTENCE_PORT';
5
+ export const FILES_STORAGE_ADAPTER = 'FORGEON_FILES_STORAGE_ADAPTER';
6
+
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
+ export interface FilesStorageAdapter {
101
+ readonly driver: string;
102
+ put(buffer: Buffer, fileName: string): Promise<{
103
+ storageKey: string;
104
+ }>;
105
+ open(storageKey: string): Promise<Readable>;
106
+ delete(storageKey: string): Promise<void>;
107
+ }