@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.
- package/CHANGELOG.md +5 -0
- package/README.md +11 -0
- package/eslint.config.cjs +28 -0
- package/generators.json +69 -0
- package/package.json +21 -0
- package/project.json +47 -0
- package/src/account/files/data-access/src/index.ts__tmpl__ +5 -0
- package/src/account/files/data-access/src/lib/api-account-data-access.module.ts__tmpl__ +10 -0
- package/src/account/files/data-access/src/lib/api-account-data-access.service.ts__tmpl__ +152 -0
- package/src/account/files/data-access/src/lib/dto/account-create-email.input.ts__tmpl__ +9 -0
- package/src/account/files/data-access/src/lib/dto/account-update-password.input.ts__tmpl__ +16 -0
- package/src/account/files/data-access/src/lib/dto/account-update-profile.input.ts__tmpl__ +25 -0
- package/src/account/files/feature/src/index.ts__tmpl__ +1 -0
- package/src/account/files/feature/src/lib/api-account-feature.module.ts__tmpl__ +9 -0
- package/src/account/files/feature/src/lib/api-account-feature.resolver.ts__tmpl__ +83 -0
- package/src/account/generator.spec.ts +71 -0
- package/src/account/generator.ts +20 -0
- package/src/account/schema.d.ts +3 -0
- package/src/account/schema.json +13 -0
- package/src/app/files/src/app.config.ts__tmpl__ +66 -0
- package/src/app/files/src/app.module.ts__tmpl__ +43 -0
- package/src/app/files/src/applogger.middleware.ts__tmpl__ +21 -0
- package/src/app/files/src/main.ts__tmpl__ +33 -0
- package/src/app/files/webpack.config.js__tmpl__ +54 -0
- package/src/app/generator.spec.ts +112 -0
- package/src/app/generator.ts +105 -0
- package/src/app/schema.d.ts +1 -0
- package/src/app/schema.json +9 -0
- package/src/config/files/src/index.ts__tmpl__ +3 -0
- package/src/config/files/src/lib/config.service.ts__tmpl__ +51 -0
- package/src/config/files/src/lib/configuration.ts__tmpl__ +32 -0
- package/src/config/files/src/lib/validation.ts__tmpl__ +21 -0
- package/src/config/generator.spec.ts +47 -0
- package/src/config/generator.ts +16 -0
- package/src/config/schema.d.ts +3 -0
- package/src/config/schema.json +13 -0
- package/src/core/files/data-access/src/index.ts__tmpl__ +5 -0
- package/src/core/files/data-access/src/lib/api-core-data-access.module.ts__tmpl__ +9 -0
- package/src/core/files/data-access/src/lib/api-core-data-access.service.ts__tmpl__ +97 -0
- package/src/core/files/data-access/src/lib/api-core-pub-sub.ts__tmpl__ +37 -0
- package/src/core/files/data-access/src/lib/dto/core-paging.input.ts__tmpl__ +26 -0
- package/src/core/files/data-access/src/lib/dto/multi-select-input.ts__tmpl__ +7 -0
- package/src/core/files/data-access/src/lib/models/core-paging.ts__tmpl__ +19 -0
- package/src/core/files/feature/src/index.ts__tmpl__ +2 -0
- package/src/core/files/feature/src/lib/api-core-feature.controller.ts__tmpl__ +12 -0
- package/src/core/files/feature/src/lib/api-core-feature.module.ts__tmpl__ +86 -0
- package/src/core/files/feature/src/lib/api-core-feature.resolver.ts__tmpl__ +12 -0
- package/src/core/files/feature/src/lib/api-core-feature.service.ts__tmpl__ +55 -0
- package/src/core/files/feature/src/lib/config/configuration.ts__tmpl__ +32 -0
- package/src/core/files/feature/src/lib/config/validation.ts__tmpl__ +25 -0
- package/src/core/files/feature/src/lib/plugins/complexity.plugin.ts__tmpl__ +51 -0
- package/src/core/files/feature/src/lib/plugins/logging.plugin.ts__tmpl__ +17 -0
- package/src/core/files/models/src/index.ts__tmpl__ +1 -0
- package/src/core/files/models/src/lib/generate-models.ts__tmpl__ +294 -0
- package/src/core/files/models/src/lib/models/core-paging.model.ts__tmpl__ +25 -0
- package/src/core/generator.spec.ts +85 -0
- package/src/core/generator.ts +35 -0
- package/src/core/schema.d.ts +3 -0
- package/src/core/schema.json +13 -0
- package/src/custom/generator.spec.ts +75 -0
- package/src/custom/generator.ts +239 -0
- package/src/custom/schema.json +21 -0
- package/src/custom/schema.ts +5 -0
- package/src/extended/generator.spec.ts +95 -0
- package/src/extended/generator.ts +161 -0
- package/src/extended/index.ts +1 -0
- package/src/extended/schema.json +12 -0
- package/src/extended/schema.ts +3 -0
- package/src/generate-crud/files/data-access/src/index.ts__tmpl__ +3 -0
- package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.module.ts__tmpl__ +11 -0
- package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.service.ts__tmpl__ +72 -0
- package/src/generate-crud/files/data-access/src/lib/dto/index.ts__tmpl__ +224 -0
- package/src/generate-crud/files/feature/.gitkeep +0 -0
- package/src/generate-crud/generator.spec.ts +84 -0
- package/src/generate-crud/generator.ts +354 -0
- package/src/generate-crud/schema.json +32 -0
- package/src/generate-crud/schema.ts +8 -0
- package/src/index.ts +13 -0
- package/src/plugin/generator.spec.ts +18 -0
- package/src/plugin/generator.ts +74 -0
- package/src/plugin/schema.json +14 -0
- package/src/plugin/schema.ts +4 -0
- package/src/prisma/files/src/index.ts__tmpl__ +1 -0
- package/src/prisma/files/src/lib/.gitkeep +1 -0
- package/src/prisma/files/src/lib/schemas/schema.prisma__tmpl__ +402 -0
- package/src/prisma/files/src/lib/seed/seed-data/iso-3166-countries.ts__tmpl__ +3239 -0
- package/src/prisma/files/src/lib/seed/seed-data/seed-users.ts__tmpl__ +32 -0
- package/src/prisma/files/src/lib/seed/seed.ts__tmpl__ +64 -0
- package/src/prisma/generator.spec.ts +60 -0
- package/src/prisma/generator.ts +61 -0
- package/src/prisma/schema.d.ts +3 -0
- package/src/prisma/schema.json +13 -0
- package/src/setup/generator.md +49 -0
- package/src/setup/generator.spec.ts +18 -0
- package/src/setup/generator.ts +106 -0
- package/src/setup/schema.json +8 -0
- package/src/smtp-mailer/files/data-access/src/index.ts__tmpl__ +2 -0
- package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.module.ts__tmpl__ +10 -0
- package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.service.ts__tmpl__ +61 -0
- package/src/smtp-mailer/generator.spec.ts +41 -0
- package/src/smtp-mailer/generator.ts +14 -0
- package/src/smtp-mailer/schema.d.ts +0 -0
- package/src/smtp-mailer/schema.json +7 -0
- package/src/user/files/data-access/src/index.ts__tmpl__ +5 -0
- package/src/user/files/data-access/src/lib/api-user-data-access.module.ts__tmpl__ +10 -0
- package/src/user/files/data-access/src/lib/api-user-data-access.service.ts__tmpl__ +119 -0
- package/src/user/files/data-access/src/lib/dto/admin-create-user.input.ts__tmpl__ +20 -0
- package/src/user/files/data-access/src/lib/dto/admin-update-user.input.ts__tmpl__ +29 -0
- package/src/user/files/feature/src/index.ts__tmpl__ +1 -0
- package/src/user/files/feature/src/lib/api-user-feature-admin.resolver.ts__tmpl__ +57 -0
- package/src/user/files/feature/src/lib/api-user-feature.module.ts__tmpl__ +10 -0
- package/src/user/files/feature/src/lib/api-user-feature.resolver.ts__tmpl__ +17 -0
- package/src/user/generator.spec.ts +41 -0
- package/src/user/generator.ts +15 -0
- package/src/user/schema.d.ts +0 -0
- package/src/user/schema.json +7 -0
- package/src/utils/files/src/index.ts__tmpl__ +3 -0
- package/src/utils/files/src/lib/decorators/ctx-user.decorator.ts__tmpl__ +6 -0
- package/src/utils/files/src/lib/guards/gql-auth-admin.guard.ts__tmpl__ +39 -0
- package/src/utils/files/src/lib/guards/gql-auth.guard.ts__tmpl__ +11 -0
- package/src/utils/generator.ts +14 -0
- package/src/utils/schema.json +8 -0
- package/src/workspace-setup/generator.md +39 -0
- package/src/workspace-setup/generator.spec.ts +82 -0
- package/src/workspace-setup/generator.ts +49 -0
- package/src/workspace-setup/lib/helpers.ts +142 -0
- package/src/workspace-setup/schema.d.ts +3 -0
- package/src/workspace-setup/schema.json +7 -0
- package/tsconfig.json +16 -0
- package/tsconfig.lib.json +23 -0
- package/tsconfig.spec.json +22 -0
- 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
|
+
}
|
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 @@
|
|
|
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
|
+
}
|