@nestledjs/api 0.0.1

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 (132) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +11 -0
  3. package/eslint.config.cjs +28 -0
  4. package/generators.json +69 -0
  5. package/package.json +21 -0
  6. package/project.json +47 -0
  7. package/src/account/files/data-access/src/index.ts__tmpl__ +5 -0
  8. package/src/account/files/data-access/src/lib/api-account-data-access.module.ts__tmpl__ +10 -0
  9. package/src/account/files/data-access/src/lib/api-account-data-access.service.ts__tmpl__ +152 -0
  10. package/src/account/files/data-access/src/lib/dto/account-create-email.input.ts__tmpl__ +9 -0
  11. package/src/account/files/data-access/src/lib/dto/account-update-password.input.ts__tmpl__ +16 -0
  12. package/src/account/files/data-access/src/lib/dto/account-update-profile.input.ts__tmpl__ +25 -0
  13. package/src/account/files/feature/src/index.ts__tmpl__ +1 -0
  14. package/src/account/files/feature/src/lib/api-account-feature.module.ts__tmpl__ +9 -0
  15. package/src/account/files/feature/src/lib/api-account-feature.resolver.ts__tmpl__ +83 -0
  16. package/src/account/generator.spec.ts +71 -0
  17. package/src/account/generator.ts +20 -0
  18. package/src/account/schema.d.ts +3 -0
  19. package/src/account/schema.json +13 -0
  20. package/src/app/files/src/app.config.ts__tmpl__ +66 -0
  21. package/src/app/files/src/app.module.ts__tmpl__ +43 -0
  22. package/src/app/files/src/applogger.middleware.ts__tmpl__ +21 -0
  23. package/src/app/files/src/main.ts__tmpl__ +33 -0
  24. package/src/app/files/webpack.config.js__tmpl__ +54 -0
  25. package/src/app/generator.spec.ts +112 -0
  26. package/src/app/generator.ts +105 -0
  27. package/src/app/schema.d.ts +1 -0
  28. package/src/app/schema.json +9 -0
  29. package/src/config/files/src/index.ts__tmpl__ +3 -0
  30. package/src/config/files/src/lib/config.service.ts__tmpl__ +51 -0
  31. package/src/config/files/src/lib/configuration.ts__tmpl__ +32 -0
  32. package/src/config/files/src/lib/validation.ts__tmpl__ +21 -0
  33. package/src/config/generator.spec.ts +47 -0
  34. package/src/config/generator.ts +16 -0
  35. package/src/config/schema.d.ts +3 -0
  36. package/src/config/schema.json +13 -0
  37. package/src/core/files/data-access/src/index.ts__tmpl__ +5 -0
  38. package/src/core/files/data-access/src/lib/api-core-data-access.module.ts__tmpl__ +9 -0
  39. package/src/core/files/data-access/src/lib/api-core-data-access.service.ts__tmpl__ +97 -0
  40. package/src/core/files/data-access/src/lib/api-core-pub-sub.ts__tmpl__ +37 -0
  41. package/src/core/files/data-access/src/lib/dto/core-paging.input.ts__tmpl__ +26 -0
  42. package/src/core/files/data-access/src/lib/dto/multi-select-input.ts__tmpl__ +7 -0
  43. package/src/core/files/data-access/src/lib/models/core-paging.ts__tmpl__ +19 -0
  44. package/src/core/files/feature/src/index.ts__tmpl__ +2 -0
  45. package/src/core/files/feature/src/lib/api-core-feature.controller.ts__tmpl__ +12 -0
  46. package/src/core/files/feature/src/lib/api-core-feature.module.ts__tmpl__ +86 -0
  47. package/src/core/files/feature/src/lib/api-core-feature.resolver.ts__tmpl__ +12 -0
  48. package/src/core/files/feature/src/lib/api-core-feature.service.ts__tmpl__ +55 -0
  49. package/src/core/files/feature/src/lib/config/configuration.ts__tmpl__ +32 -0
  50. package/src/core/files/feature/src/lib/config/validation.ts__tmpl__ +25 -0
  51. package/src/core/files/feature/src/lib/plugins/complexity.plugin.ts__tmpl__ +51 -0
  52. package/src/core/files/feature/src/lib/plugins/logging.plugin.ts__tmpl__ +17 -0
  53. package/src/core/files/models/src/index.ts__tmpl__ +1 -0
  54. package/src/core/files/models/src/lib/generate-models.ts__tmpl__ +294 -0
  55. package/src/core/files/models/src/lib/models/core-paging.model.ts__tmpl__ +25 -0
  56. package/src/core/generator.spec.ts +85 -0
  57. package/src/core/generator.ts +35 -0
  58. package/src/core/schema.d.ts +3 -0
  59. package/src/core/schema.json +13 -0
  60. package/src/custom/generator.spec.ts +75 -0
  61. package/src/custom/generator.ts +239 -0
  62. package/src/custom/schema.json +21 -0
  63. package/src/custom/schema.ts +5 -0
  64. package/src/extended/generator.spec.ts +95 -0
  65. package/src/extended/generator.ts +161 -0
  66. package/src/extended/index.ts +1 -0
  67. package/src/extended/schema.json +12 -0
  68. package/src/extended/schema.ts +3 -0
  69. package/src/generate-crud/files/data-access/src/index.ts__tmpl__ +3 -0
  70. package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.module.ts__tmpl__ +11 -0
  71. package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.service.ts__tmpl__ +72 -0
  72. package/src/generate-crud/files/data-access/src/lib/dto/index.ts__tmpl__ +224 -0
  73. package/src/generate-crud/files/feature/.gitkeep +0 -0
  74. package/src/generate-crud/generator.spec.ts +84 -0
  75. package/src/generate-crud/generator.ts +354 -0
  76. package/src/generate-crud/schema.json +32 -0
  77. package/src/generate-crud/schema.ts +8 -0
  78. package/src/index.ts +13 -0
  79. package/src/plugin/generator.spec.ts +18 -0
  80. package/src/plugin/generator.ts +74 -0
  81. package/src/plugin/schema.json +14 -0
  82. package/src/plugin/schema.ts +4 -0
  83. package/src/prisma/files/src/index.ts__tmpl__ +1 -0
  84. package/src/prisma/files/src/lib/.gitkeep +1 -0
  85. package/src/prisma/files/src/lib/schemas/schema.prisma__tmpl__ +402 -0
  86. package/src/prisma/files/src/lib/seed/seed-data/iso-3166-countries.ts__tmpl__ +3239 -0
  87. package/src/prisma/files/src/lib/seed/seed-data/seed-users.ts__tmpl__ +32 -0
  88. package/src/prisma/files/src/lib/seed/seed.ts__tmpl__ +64 -0
  89. package/src/prisma/generator.spec.ts +60 -0
  90. package/src/prisma/generator.ts +61 -0
  91. package/src/prisma/schema.d.ts +3 -0
  92. package/src/prisma/schema.json +13 -0
  93. package/src/setup/generator.md +49 -0
  94. package/src/setup/generator.spec.ts +18 -0
  95. package/src/setup/generator.ts +106 -0
  96. package/src/setup/schema.json +8 -0
  97. package/src/smtp-mailer/files/data-access/src/index.ts__tmpl__ +2 -0
  98. package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.module.ts__tmpl__ +10 -0
  99. package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.service.ts__tmpl__ +61 -0
  100. package/src/smtp-mailer/generator.spec.ts +41 -0
  101. package/src/smtp-mailer/generator.ts +14 -0
  102. package/src/smtp-mailer/schema.d.ts +0 -0
  103. package/src/smtp-mailer/schema.json +7 -0
  104. package/src/user/files/data-access/src/index.ts__tmpl__ +5 -0
  105. package/src/user/files/data-access/src/lib/api-user-data-access.module.ts__tmpl__ +10 -0
  106. package/src/user/files/data-access/src/lib/api-user-data-access.service.ts__tmpl__ +119 -0
  107. package/src/user/files/data-access/src/lib/dto/admin-create-user.input.ts__tmpl__ +20 -0
  108. package/src/user/files/data-access/src/lib/dto/admin-update-user.input.ts__tmpl__ +29 -0
  109. package/src/user/files/feature/src/index.ts__tmpl__ +1 -0
  110. package/src/user/files/feature/src/lib/api-user-feature-admin.resolver.ts__tmpl__ +57 -0
  111. package/src/user/files/feature/src/lib/api-user-feature.module.ts__tmpl__ +10 -0
  112. package/src/user/files/feature/src/lib/api-user-feature.resolver.ts__tmpl__ +17 -0
  113. package/src/user/generator.spec.ts +41 -0
  114. package/src/user/generator.ts +15 -0
  115. package/src/user/schema.d.ts +0 -0
  116. package/src/user/schema.json +7 -0
  117. package/src/utils/files/src/index.ts__tmpl__ +3 -0
  118. package/src/utils/files/src/lib/decorators/ctx-user.decorator.ts__tmpl__ +6 -0
  119. package/src/utils/files/src/lib/guards/gql-auth-admin.guard.ts__tmpl__ +39 -0
  120. package/src/utils/files/src/lib/guards/gql-auth.guard.ts__tmpl__ +11 -0
  121. package/src/utils/generator.ts +14 -0
  122. package/src/utils/schema.json +8 -0
  123. package/src/workspace-setup/generator.md +39 -0
  124. package/src/workspace-setup/generator.spec.ts +82 -0
  125. package/src/workspace-setup/generator.ts +49 -0
  126. package/src/workspace-setup/lib/helpers.ts +142 -0
  127. package/src/workspace-setup/schema.d.ts +3 -0
  128. package/src/workspace-setup/schema.json +7 -0
  129. package/tsconfig.json +16 -0
  130. package/tsconfig.lib.json +23 -0
  131. package/tsconfig.spec.json +22 -0
  132. package/vite.config.mts +37 -0
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "../../node_modules/@nx/devkit/schema.d.json",
3
+ "extends": "../../node_modules/@nx/devkit/generators.json",
4
+ "properties": {
5
+ "name": {
6
+ "description": "Name of the library to generate",
7
+ "type": "string"
8
+ },
9
+ "directory": {
10
+ "description": "Optional directory to place the library",
11
+ "type": "string"
12
+ },
13
+ "model": {
14
+ "description": "Name of the model",
15
+ "type": "string"
16
+ },
17
+ "plural": {
18
+ "description": "Plural form of the model name",
19
+ "type": "string"
20
+ },
21
+ "description": {
22
+ "description": "Description of the model",
23
+ "type": "string"
24
+ },
25
+ "overwrite": {
26
+ "description": "Whether to overwrite existing files in the public libraries",
27
+ "type": "boolean",
28
+ "default": false
29
+ }
30
+ },
31
+ "required": []
32
+ }
@@ -0,0 +1,8 @@
1
+ export interface GenerateCrudGeneratorSchema {
2
+ name: string;
3
+ directory: string;
4
+ model: string;
5
+ plural: string;
6
+ description?: string;
7
+ overwrite?: boolean;
8
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ export * from './account/generator'
2
+ export * from './app/generator'
3
+ export * from './config/generator'
4
+ export * from './core/generator'
5
+ export * from './custom/generator'
6
+ export * from './generate-crud/generator'
7
+ export * from './smtp-mailer/generator'
8
+ export * from './prisma/generator'
9
+ export * from './setup/generator'
10
+ export * from './user/generator'
11
+ export * from './workspace-setup/generator'
12
+ export * from './plugin/generator'
13
+ export * from './utils/generator'
@@ -0,0 +1,18 @@
1
+ import { Tree } from '@nx/devkit'
2
+ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'
3
+ import generator from './generator'
4
+
5
+ describe('plugin-generator', () => {
6
+ let tree: Tree
7
+
8
+ beforeEach(() => {
9
+ tree = createTreeWithEmptyWorkspace()
10
+ })
11
+
12
+ it('should generate plugin files in the plugins folder', async () => {
13
+ await generator(tree, { name: 'TestPlugin' })
14
+ expect(tree.exists('libs/api/custom/src/lib/plugins/test-plugin/test-plugin.service.ts')).toBe(true)
15
+ expect(tree.exists('libs/api/custom/src/lib/plugins/test-plugin/test-plugin.resolver.ts')).toBe(true)
16
+ expect(tree.exists('libs/api/custom/src/lib/plugins/test-plugin/test-plugin.module.ts')).toBe(true)
17
+ })
18
+ })
@@ -0,0 +1,74 @@
1
+ import { formatFiles, installPackagesTask, Tree } from '@nx/devkit'
2
+ import { join } from 'path'
3
+ import { GeneratePluginGeneratorSchema } from './schema'
4
+
5
+ function toKebabCase(str: string): string {
6
+ return str
7
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
8
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
9
+ .toLowerCase()
10
+ }
11
+
12
+ function toPascalCase(str: string): string {
13
+ return str
14
+ .replace(/(^\w|[-_\s]\w)/g, (match) => match.replace(/[-_\s]/, '').toUpperCase())
15
+ }
16
+
17
+ async function ensureDirExists(tree: Tree, path: string) {
18
+ if (!tree.exists(path)) {
19
+ // Directory will be created when a file is written into it
20
+ }
21
+ }
22
+
23
+ export default async function (tree: Tree, schema: GeneratePluginGeneratorSchema) {
24
+ const name = schema.name
25
+ if (!name) throw new Error('Name is required')
26
+ const kebabName = toKebabCase(name)
27
+ const className = toPascalCase(name)
28
+ const customLibraryRoot = schema.directory ? `libs/api/${schema.directory}/custom` : `libs/api/custom`
29
+ const pluginsDir = join(customLibraryRoot, 'src/lib/plugins')
30
+ const pluginFolder = join(pluginsDir, kebabName)
31
+
32
+ await ensureDirExists(tree, pluginsDir)
33
+ await ensureDirExists(tree, pluginFolder)
34
+
35
+ // Service
36
+ const serviceContent = `import { Injectable } from '@nestjs/common'
37
+
38
+ @Injectable()
39
+ export class ${className}Service {
40
+ // Add your service logic here
41
+ }
42
+ `
43
+ tree.write(join(pluginFolder, `${kebabName}.service.ts`), serviceContent)
44
+
45
+ // Resolver
46
+ const resolverContent = `import { Resolver } from '@nestjs/graphql'
47
+ import { Injectable } from '@nestjs/common'
48
+
49
+ @Resolver()
50
+ @Injectable()
51
+ export class ${className}Resolver {
52
+ // Add your resolver logic here
53
+ }
54
+ `
55
+ tree.write(join(pluginFolder, `${kebabName}.resolver.ts`), resolverContent)
56
+
57
+ // Module
58
+ const moduleContent = `import { Module } from '@nestjs/common'
59
+ import { ${className}Service } from './${kebabName}.service'
60
+ import { ${className}Resolver } from './${kebabName}.resolver'
61
+
62
+ @Module({
63
+ providers: [${className}Service, ${className}Resolver],
64
+ exports: [${className}Service, ${className}Resolver],
65
+ })
66
+ export class ${className}Module {}
67
+ `
68
+ tree.write(join(pluginFolder, `${kebabName}.module.ts`), moduleContent)
69
+
70
+ await formatFiles(tree)
71
+ return () => {
72
+ installPackagesTask(tree)
73
+ }
74
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "../../node_modules/@nx/devkit/schema.d.json",
3
+ "properties": {
4
+ "name": {
5
+ "description": "Name of the plugin to generate",
6
+ "type": "string"
7
+ },
8
+ "directory": {
9
+ "description": "Optional directory to place the custom library",
10
+ "type": "string"
11
+ }
12
+ },
13
+ "required": ["name"]
14
+ }
@@ -0,0 +1,4 @@
1
+ export interface GeneratePluginGeneratorSchema {
2
+ name: string;
3
+ directory?: string;
4
+ }
@@ -0,0 +1 @@
1
+ export * from './lib/prisma-generated'
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,402 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ previewFeatures = []
4
+ binaryTargets = ["native"]
5
+ output = "../prisma-generated"
6
+ }
7
+
8
+ datasource db {
9
+ provider = "postgresql"
10
+ url = env("DATABASE_URL")
11
+ }
12
+
13
+ // Models (Alphabetical Order)
14
+
15
+ model Address {
16
+ id String @id @default(cuid())
17
+ createdAt DateTime @default(now())
18
+ updatedAt DateTime @updatedAt
19
+ address1 String?
20
+ address2 String?
21
+ city String?
22
+ region String?
23
+ postalCode String?
24
+ addressType AddressType @default(WORK)
25
+ countryId String?
26
+ country Country? @relation("Country_belongsTo_Address", fields: [countryId], references: [id])
27
+ users User[] @relation("Address_belongsTo_User")
28
+ organizations Organization[] @relation("Address_belongsTo_Organization")
29
+ }
30
+
31
+ model Country {
32
+ id String @id @default(cuid())
33
+ createdAt DateTime @default(now())
34
+ updatedAt DateTime @updatedAt
35
+ name String
36
+ alpha2 String @unique
37
+ alpha3 String @unique
38
+ countryCode String
39
+ iso3166_2 String
40
+ region String
41
+ subRegion String
42
+ intermediateRegion String
43
+ regionCode String
44
+ subRegionCode String
45
+ intermediateRegionCode String
46
+ addresses Address[] @relation("Country_belongsTo_Address")
47
+ }
48
+
49
+ model Email {
50
+ id String @id @default(cuid())
51
+ createdAt DateTime @default(now())
52
+ updatedAt DateTime @default(now()) @updatedAt
53
+ email String @unique // enforce lowercase logic at app level
54
+ public Boolean @default(false)
55
+ primary Boolean @default(false)
56
+ verified Boolean @default(false)
57
+ verifyToken String?
58
+ verifyExpires DateTime?
59
+ userId String?
60
+ emailType EmailType @default(WORK)
61
+ organizationId String?
62
+ user User? @relation("Email_belongsTo_User", fields: [userId], references: [id])
63
+ organization Organization? @relation("Email_belongsTo_Organization", fields: [organizationId], references: [id])
64
+ }
65
+
66
+ model Link {
67
+ id String @id @default(cuid())
68
+ createdAt DateTime @default(now())
69
+ updatedAt DateTime @updatedAt
70
+ name String
71
+ url String
72
+ userId String?
73
+ organizationId String?
74
+ user User? @relation("Link_belongsTo_User", fields: [userId], references: [id])
75
+ organization Organization? @relation("Link_belongsTo_Organization", fields: [organizationId], references: [id])
76
+ }
77
+
78
+ model PhoneNumber {
79
+ id String @id @default(cuid())
80
+ createdAt DateTime @default(now())
81
+ updatedAt DateTime @default(now()) @updatedAt
82
+ phone String @unique
83
+ phoneType PhoneType @default(MOBILE)
84
+ userId String?
85
+ primary Boolean @default(false)
86
+ organizationId String?
87
+ user User? @relation("PhoneNumber_belongsTo_User", fields: [userId], references: [id])
88
+ organization Organization? @relation("PhoneNumber_belongsTo_Organization", fields: [organizationId], references: [id])
89
+ }
90
+
91
+ model Upload {
92
+ id String @id @default(cuid())
93
+ createdAt DateTime @default(now())
94
+ updatedAt DateTime @default(now()) @updatedAt
95
+ type ImageType?
96
+ fileId String?
97
+ filePath String?
98
+ fileType String?
99
+ height Int?
100
+ name String?
101
+ size Int?
102
+ thumbnailUrl String?
103
+ orientation Int?
104
+ url String?
105
+ versionInfo Json?
106
+ width Int?
107
+ userId String?
108
+ organizationId String?
109
+ user User? @relation("Upload_belongsTo_User", fields: [userId], references: [id])
110
+ organization Organization? @relation("Upload_belongsTo_Organization", fields: [organizationId], references: [id])
111
+ }
112
+
113
+ model User {
114
+ id String @id @default(cuid())
115
+ createdAt DateTime @default(now())
116
+ updatedAt DateTime @default(now()) @updatedAt
117
+ firstName String?
118
+ lastName String?
119
+ role UserRole
120
+ bio String?
121
+ displayName String? @unique
122
+ password String?
123
+ passwordResetToken String?
124
+ passwordResetExpires DateTime?
125
+ emailValidated Boolean @default(value: false)
126
+ validateEmailToken String? @unique
127
+ validateEmailTokenExpires DateTime?
128
+ emails Email[] @relation("Email_belongsTo_User")
129
+ links Link[] @relation("Link_belongsTo_User")
130
+ phoneNumbers PhoneNumber[] @relation("PhoneNumber_belongsTo_User")
131
+ images Upload[] @relation("Upload_belongsTo_User")
132
+ organizations OrganizationMember[] @relation("OrganizationMember_belongsTo_User")
133
+ activeOrganizationId String?
134
+ addresses Address[] @relation("Address_belongsTo_User")
135
+ invitesSent Invite[] @relation("Invite_sentBy_User")
136
+
137
+ // 2FA fields
138
+ twoFactorEnabled Boolean @default(false)
139
+ twoFactorSecret String? // Stores the TOTP secret
140
+ twoFactorRecoveryCodes String[] // Backup codes for 2FA recovery
141
+
142
+ // Optional: If you want to support multiple 2FA methods
143
+ twoFactorMethod TwoFactorMethod @default(NONE)
144
+
145
+ // If you want to track 2FA verification status per session
146
+ activeSessions UserSession[]
147
+ loginAttempts LoginAttempt[]
148
+ lastSuccessfulLogin DateTime?
149
+ lastFailedLogin DateTime?
150
+ failedLoginCount Int @default(0)
151
+ lockedUntil DateTime? // For temporary account lockouts
152
+ AuditLog AuditLog[]
153
+ UserPreference UserPreference[]
154
+ TeamMember TeamMember[]
155
+ SecurityEvent SecurityEvent[]
156
+
157
+ // Account lifecycle
158
+ isActive Boolean @default(true)
159
+ deactivatedAt DateTime?
160
+
161
+ // Legal compliance
162
+ termsAcceptedAt DateTime?
163
+ privacyPolicyAcceptedAt DateTime?
164
+ apiTokens ApiToken[] @relation("User_hasMany_APITokens")
165
+ oAuthAccounts OAuthAccount[] @relation("User_hasMany_OAuthAccounts")
166
+ }
167
+
168
+ model Organization {
169
+ id String @id @default(cuid())
170
+ createdAt DateTime @default(now())
171
+ updatedAt DateTime @updatedAt
172
+ name String
173
+ emails Email[] @relation("Email_belongsTo_Organization")
174
+ links Link[] @relation("Link_belongsTo_Organization")
175
+ phoneNumbers PhoneNumber[] @relation("PhoneNumber_belongsTo_Organization")
176
+ images Upload[] @relation("Upload_belongsTo_Organization")
177
+ members OrganizationMember[] @relation("OrganizationMember_belongsTo_Organization")
178
+ addresses Address[] @relation("Address_belongsTo_Organization")
179
+ invites Invite[] @relation("Invite_belongsTo_Organization")
180
+ AuditLog AuditLog[]
181
+ Team Team[]
182
+ }
183
+
184
+ model Invite {
185
+ id String @id @default(cuid())
186
+ createdAt DateTime @default(now())
187
+ updatedAt DateTime @updatedAt
188
+ expiresAt DateTime
189
+ email String
190
+ token String @unique
191
+ inviterId String
192
+ inviter User @relation("Invite_sentBy_User", fields: [inviterId], references: [id])
193
+ organizationId String
194
+ organization Organization @relation("Invite_belongsTo_Organization", fields: [organizationId], references: [id])
195
+ accepted Boolean @default(false)
196
+ used Boolean @default(false)
197
+ issuedAt DateTime?
198
+ }
199
+
200
+ model OrganizationMember {
201
+ id String @id @default(cuid())
202
+ createdAt DateTime @default(now())
203
+ updatedAt DateTime @updatedAt
204
+ role OrganizationRole @default(ORGANIZATION_MEMBER)
205
+ userId String?
206
+ user User? @relation("OrganizationMember_belongsTo_User", fields: [userId], references: [id])
207
+ organizationId String?
208
+ organization Organization? @relation("OrganizationMember_belongsTo_Organization", fields: [organizationId], references: [id])
209
+ }
210
+
211
+ model UserSession {
212
+ id String @id @default(cuid())
213
+ createdAt DateTime @default(now())
214
+ updatedAt DateTime @updatedAt
215
+ lastActiveAt DateTime @default(now())
216
+ userId String
217
+ user User @relation(fields: [userId], references: [id])
218
+ deviceInfo String?
219
+ ipAddress String?
220
+ isValid Boolean @default(true)
221
+
222
+ // Add this field to track 2FA verification status
223
+ twoFactorVerified Boolean @default(false)
224
+ }
225
+
226
+ model AuditLog {
227
+ id String @id @default(cuid())
228
+ createdAt DateTime @default(now())
229
+ updatedAt DateTime @updatedAt
230
+ entityId String // ID of the entity being modified
231
+ entityType String // Name of the model being modified
232
+ action String // CREATE, UPDATE, DELETE
233
+ userId String
234
+ user User @relation(fields: [userId], references: [id])
235
+ organizationId String?
236
+ organization Organization? @relation(fields: [organizationId], references: [id])
237
+ changes Json? // Store what was changed
238
+ }
239
+
240
+ model UserPreference {
241
+ id String @id @default(cuid())
242
+ createdAt DateTime @default(now())
243
+ updatedAt DateTime @updatedAt
244
+ userId String
245
+ user User @relation(fields: [userId], references: [id])
246
+ key String
247
+ value String
248
+
249
+ @@unique([userId, key])
250
+ }
251
+
252
+ // Optional if you need team functionality
253
+ model Team {
254
+ id String @id @default(cuid())
255
+ createdAt DateTime @default(now())
256
+ updatedAt DateTime @updatedAt
257
+ name String
258
+ description String?
259
+ organizationId String
260
+ organization Organization @relation(fields: [organizationId], references: [id])
261
+ members TeamMember[]
262
+ }
263
+
264
+ model TeamMember {
265
+ id String @id @default(cuid())
266
+ createdAt DateTime @default(now())
267
+ updatedAt DateTime @updatedAt
268
+ teamId String
269
+ team Team @relation(fields: [teamId], references: [id])
270
+ userId String
271
+ user User @relation(fields: [userId], references: [id])
272
+ role TeamRole @default(MEMBER)
273
+ }
274
+
275
+ // Enums (Alphabetical Order)
276
+
277
+ enum AddressType {
278
+ HOME
279
+ WORK
280
+ VENUE
281
+ EVENT
282
+ OTHER
283
+ }
284
+
285
+ enum EmailType {
286
+ PERSONAL
287
+ WORK
288
+ OTHER
289
+ }
290
+
291
+ enum ImageType {
292
+ AVATAR
293
+ BACKGROUND
294
+ OTHER
295
+ }
296
+
297
+ enum PhoneType {
298
+ HOME
299
+ WORK
300
+ MOBILE
301
+ OTHER
302
+ }
303
+
304
+ enum UserRole {
305
+ DEVELOPER
306
+ SUPER_ADMIN
307
+ ADMIN
308
+ USER
309
+ }
310
+
311
+ enum OrganizationRole {
312
+ ORGANIZATION_ADMIN
313
+ ORGANIZATION_MEMBER
314
+ }
315
+
316
+ enum TeamRole {
317
+ LEADER
318
+ MEMBER
319
+ }
320
+
321
+ // Add this enum
322
+ enum TwoFactorMethod {
323
+ NONE
324
+ AUTHENTICATOR
325
+ SMS
326
+ EMAIL
327
+ }
328
+
329
+ model LoginAttempt {
330
+ id String @id @default(cuid())
331
+ createdAt DateTime @default(now())
332
+ updatedAt DateTime @updatedAt
333
+ userId String? // Optional because login attempts might be for non-existent users
334
+ user User? @relation(fields: [userId], references: [id])
335
+ email String // The email used in the attempt
336
+ success Boolean @default(false)
337
+ ipAddress String?
338
+ userAgent String? // Browser/client info
339
+ location String? // Geo location if you want to track it
340
+ reason FailureReason? // Why the login failed
341
+ }
342
+
343
+ enum FailureReason {
344
+ INVALID_PASSWORD
345
+ INVALID_EMAIL
346
+ ACCOUNT_LOCKED
347
+ ACCOUNT_DISABLED
348
+ INVALID_2FA
349
+ EXPIRED_TOKEN
350
+ TOO_MANY_ATTEMPTS
351
+ }
352
+
353
+ model SecurityEvent {
354
+ id String @id @default(cuid())
355
+ createdAt DateTime @default(now())
356
+ updatedAt DateTime @updatedAt
357
+ userId String
358
+ user User @relation(fields: [userId], references: [id])
359
+ eventType SecurityEventType
360
+ ipAddress String?
361
+ userAgent String?
362
+ metadata Json? // Additional event-specific data
363
+ }
364
+
365
+ enum SecurityEventType {
366
+ PASSWORD_CHANGED
367
+ EMAIL_CHANGED
368
+ TWO_FACTOR_ENABLED
369
+ TWO_FACTOR_DISABLED
370
+ RECOVERY_CODES_GENERATED
371
+ ACCOUNT_LOCKED
372
+ ACCOUNT_UNLOCKED
373
+ SUSPICIOUS_LOGIN_ATTEMPT
374
+ PASSWORD_RESET_REQUESTED
375
+ LOGIN_LOCATION_CHANGE
376
+ }
377
+
378
+ // Add ApiToken model for API token support
379
+ model ApiToken {
380
+ id String @id @default(cuid())
381
+ createdAt DateTime @default(now())
382
+ updatedAt DateTime @updatedAt
383
+ userId String
384
+ user User @relation("User_hasMany_APITokens", fields: [userId], references: [id])
385
+ token String @unique
386
+ name String?
387
+ expiresAt DateTime?
388
+ revoked Boolean @default(false)
389
+ }
390
+
391
+ // Add OAuthAccount model for social login support
392
+ model OAuthAccount {
393
+ id String @id @default(cuid())
394
+ createdAt DateTime @default(now())
395
+ updatedAt DateTime @updatedAt
396
+ provider String
397
+ providerUserId String
398
+ userId String
399
+ user User @relation("User_hasMany_OAuthAccounts", fields: [userId], references: [id])
400
+
401
+ @@unique([provider, providerUserId])
402
+ }