create-zhx-monorepo 0.1.0

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 (125) hide show
  1. package/README.md +34 -0
  2. package/bin/index.js +65 -0
  3. package/package.json +18 -0
  4. package/templates/monorepo-starter/.vscode/settings.json +3 -0
  5. package/templates/monorepo-starter/README.md +42 -0
  6. package/templates/monorepo-starter/apps/web/components.json +20 -0
  7. package/templates/monorepo-starter/apps/web/eslint.config.mjs +4 -0
  8. package/templates/monorepo-starter/apps/web/next-env.d.ts +6 -0
  9. package/templates/monorepo-starter/apps/web/next.config.mjs +6 -0
  10. package/templates/monorepo-starter/apps/web/package.json +31 -0
  11. package/templates/monorepo-starter/apps/web/postcss.config.mjs +1 -0
  12. package/templates/monorepo-starter/apps/web/public/.gitkeep +0 -0
  13. package/templates/monorepo-starter/apps/web/sitemap.config.cjs +6 -0
  14. package/templates/monorepo-starter/apps/web/src/app/(auth)/layout.tsx +7 -0
  15. package/templates/monorepo-starter/apps/web/src/app/(root)/layout.tsx +15 -0
  16. package/templates/monorepo-starter/apps/web/src/app/(root)/page.tsx +14 -0
  17. package/templates/monorepo-starter/apps/web/src/app/globals.css +1 -0
  18. package/templates/monorepo-starter/apps/web/src/app/layout.tsx +24 -0
  19. package/templates/monorepo-starter/apps/web/src/components/Footer.tsx +9 -0
  20. package/templates/monorepo-starter/apps/web/src/components/Header.tsx +11 -0
  21. package/templates/monorepo-starter/apps/web/src/components/ThemeSwitch.tsx +34 -0
  22. package/templates/monorepo-starter/apps/web/src/hooks/.gitkeep +0 -0
  23. package/templates/monorepo-starter/apps/web/src/lib/.gitkeep +0 -0
  24. package/templates/monorepo-starter/apps/web/src/providers/index.tsx +10 -0
  25. package/templates/monorepo-starter/apps/web/src/providers/theme.tsx +19 -0
  26. package/templates/monorepo-starter/apps/web/src/types/index.d.ts +16 -0
  27. package/templates/monorepo-starter/apps/web/tsconfig.json +20 -0
  28. package/templates/monorepo-starter/eslint.config.mjs +12 -0
  29. package/templates/monorepo-starter/package.json +23 -0
  30. package/templates/monorepo-starter/packages/config/eslint/base.js +31 -0
  31. package/templates/monorepo-starter/packages/config/eslint/nest.js +26 -0
  32. package/templates/monorepo-starter/packages/config/eslint/next.js +39 -0
  33. package/templates/monorepo-starter/packages/config/eslint/react.js +30 -0
  34. package/templates/monorepo-starter/packages/config/package.json +32 -0
  35. package/templates/monorepo-starter/packages/config/typescript/base.json +31 -0
  36. package/templates/monorepo-starter/packages/config/typescript/nest.json +14 -0
  37. package/templates/monorepo-starter/packages/config/typescript/next.json +10 -0
  38. package/templates/monorepo-starter/packages/config/typescript/react.json +9 -0
  39. package/templates/monorepo-starter/packages/ui/components.json +20 -0
  40. package/templates/monorepo-starter/packages/ui/eslint.config.mjs +4 -0
  41. package/templates/monorepo-starter/packages/ui/package.json +38 -0
  42. package/templates/monorepo-starter/packages/ui/postcss.config.mjs +6 -0
  43. package/templates/monorepo-starter/packages/ui/src/components/button.tsx +71 -0
  44. package/templates/monorepo-starter/packages/ui/src/hooks/.gitkeep +0 -0
  45. package/templates/monorepo-starter/packages/ui/src/lib/utils.ts +6 -0
  46. package/templates/monorepo-starter/packages/ui/src/styles/globals.css +182 -0
  47. package/templates/monorepo-starter/packages/ui/tsconfig.json +13 -0
  48. package/templates/monorepo-starter/packages/ui/tsconfig.lint.json +8 -0
  49. package/templates/monorepo-starter/pnpm-lock.yaml +12441 -0
  50. package/templates/monorepo-starter/pnpm-workspace.yaml +17 -0
  51. package/templates/monorepo-starter/server/.env.example +64 -0
  52. package/templates/monorepo-starter/server/README.md +63 -0
  53. package/templates/monorepo-starter/server/eslint.config.mjs +4 -0
  54. package/templates/monorepo-starter/server/nest-cli.json +12 -0
  55. package/templates/monorepo-starter/server/package.json +97 -0
  56. package/templates/monorepo-starter/server/prisma/generated/browser.ts +54 -0
  57. package/templates/monorepo-starter/server/prisma/generated/client.ts +76 -0
  58. package/templates/monorepo-starter/server/prisma/generated/commonInputTypes.ts +577 -0
  59. package/templates/monorepo-starter/server/prisma/generated/enums.ts +68 -0
  60. package/templates/monorepo-starter/server/prisma/generated/internal/class.ts +250 -0
  61. package/templates/monorepo-starter/server/prisma/generated/internal/prismaNamespace.ts +1436 -0
  62. package/templates/monorepo-starter/server/prisma/generated/internal/prismaNamespaceBrowser.ts +227 -0
  63. package/templates/monorepo-starter/server/prisma/generated/models/BackupCode.ts +1375 -0
  64. package/templates/monorepo-starter/server/prisma/generated/models/Notification.ts +1587 -0
  65. package/templates/monorepo-starter/server/prisma/generated/models/Otp.ts +1488 -0
  66. package/templates/monorepo-starter/server/prisma/generated/models/RefreshToken.ts +1515 -0
  67. package/templates/monorepo-starter/server/prisma/generated/models/RoleAssignment.ts +1385 -0
  68. package/templates/monorepo-starter/server/prisma/generated/models/SecuritySetting.ts +1422 -0
  69. package/templates/monorepo-starter/server/prisma/generated/models/User.ts +2498 -0
  70. package/templates/monorepo-starter/server/prisma/generated/models.ts +18 -0
  71. package/templates/monorepo-starter/server/prisma/migrations/20251218164821_init/migration.sql +210 -0
  72. package/templates/monorepo-starter/server/prisma/migrations/migration_lock.toml +3 -0
  73. package/templates/monorepo-starter/server/prisma/schema.prisma +193 -0
  74. package/templates/monorepo-starter/server/prisma.config.ts +13 -0
  75. package/templates/monorepo-starter/server/scripts/generate.sh +14 -0
  76. package/templates/monorepo-starter/server/src/app.module.ts +49 -0
  77. package/templates/monorepo-starter/server/src/lib/decorators/logger.decorator.ts +20 -0
  78. package/templates/monorepo-starter/server/src/lib/decorators/public.decorator.ts +4 -0
  79. package/templates/monorepo-starter/server/src/lib/decorators/roles.decorator.ts +5 -0
  80. package/templates/monorepo-starter/server/src/lib/dto/auth.dto.ts +64 -0
  81. package/templates/monorepo-starter/server/src/lib/dto/security-setting.dto.ts +21 -0
  82. package/templates/monorepo-starter/server/src/lib/filters/exceptions.filter.ts +62 -0
  83. package/templates/monorepo-starter/server/src/lib/guards/auth.guard.ts +104 -0
  84. package/templates/monorepo-starter/server/src/lib/interceptors/response.interceptor.ts +33 -0
  85. package/templates/monorepo-starter/server/src/lib/pipes/validation.pipe.ts +7 -0
  86. package/templates/monorepo-starter/server/src/lib/schemas/env.schema.ts +99 -0
  87. package/templates/monorepo-starter/server/src/lib/utils/cookie.util.ts +23 -0
  88. package/templates/monorepo-starter/server/src/lib/utils/general.util.ts +24 -0
  89. package/templates/monorepo-starter/server/src/main.ts +41 -0
  90. package/templates/monorepo-starter/server/src/modules/auth/auth.controller.ts +74 -0
  91. package/templates/monorepo-starter/server/src/modules/auth/auth.module.ts +16 -0
  92. package/templates/monorepo-starter/server/src/modules/auth/auth.service.ts +525 -0
  93. package/templates/monorepo-starter/server/src/modules/auth/oauth.controller.ts +58 -0
  94. package/templates/monorepo-starter/server/src/modules/auth/oauth.service.ts +165 -0
  95. package/templates/monorepo-starter/server/src/modules/auth/otp.service.ts +133 -0
  96. package/templates/monorepo-starter/server/src/modules/auth/role.service.ts +99 -0
  97. package/templates/monorepo-starter/server/src/modules/auth/security-setting.service.ts +102 -0
  98. package/templates/monorepo-starter/server/src/modules/env/env.module.ts +9 -0
  99. package/templates/monorepo-starter/server/src/modules/env/env.service.ts +22 -0
  100. package/templates/monorepo-starter/server/src/modules/logger/logger.module.ts +12 -0
  101. package/templates/monorepo-starter/server/src/modules/logger/logger.service.ts +37 -0
  102. package/templates/monorepo-starter/server/src/modules/logger/winston.config.ts +32 -0
  103. package/templates/monorepo-starter/server/src/modules/notification/notification.module.ts +11 -0
  104. package/templates/monorepo-starter/server/src/modules/notification/notification.service.ts +118 -0
  105. package/templates/monorepo-starter/server/src/modules/prisma/prisma.extension.ts +72 -0
  106. package/templates/monorepo-starter/server/src/modules/prisma/prisma.module.ts +9 -0
  107. package/templates/monorepo-starter/server/src/modules/prisma/prisma.service.ts +49 -0
  108. package/templates/monorepo-starter/server/src/modules/public/public.controller.ts +21 -0
  109. package/templates/monorepo-starter/server/src/modules/public/public.module.ts +9 -0
  110. package/templates/monorepo-starter/server/src/modules/public/public.service.ts +30 -0
  111. package/templates/monorepo-starter/server/src/modules/scheduler/cleanup.service.ts +33 -0
  112. package/templates/monorepo-starter/server/src/modules/scheduler/scheduler.module.ts +7 -0
  113. package/templates/monorepo-starter/server/src/modules/template/template.module.ts +8 -0
  114. package/templates/monorepo-starter/server/src/modules/template/template.service.ts +33 -0
  115. package/templates/monorepo-starter/server/src/modules/token/token.module.ts +11 -0
  116. package/templates/monorepo-starter/server/src/modules/token/token.service.ts +131 -0
  117. package/templates/monorepo-starter/server/src/types/express.d.ts +10 -0
  118. package/templates/monorepo-starter/server/src/types/index.d.ts +13 -0
  119. package/templates/monorepo-starter/server/templates/notification.templates.ts +243 -0
  120. package/templates/monorepo-starter/server/test/app.e2e-spec.ts +25 -0
  121. package/templates/monorepo-starter/server/test/jest-e2e.json +9 -0
  122. package/templates/monorepo-starter/server/tsconfig.json +23 -0
  123. package/templates/monorepo-starter/server/tsup.config.ts +14 -0
  124. package/templates/monorepo-starter/tsconfig.json +3 -0
  125. package/templates/monorepo-starter/turbo.json +21 -0
@@ -0,0 +1,18 @@
1
+
2
+ /* !!! This is code generated by Prisma. Do not edit directly. !!! */
3
+ /* eslint-disable */
4
+ // biome-ignore-all lint: generated file
5
+ // @ts-nocheck
6
+ /*
7
+ * This is a barrel export file for all models and their related types.
8
+ *
9
+ * 🟢 You can import this file directly.
10
+ */
11
+ export type * from './models/User'
12
+ export type * from './models/RoleAssignment'
13
+ export type * from './models/SecuritySetting'
14
+ export type * from './models/BackupCode'
15
+ export type * from './models/RefreshToken'
16
+ export type * from './models/Otp'
17
+ export type * from './models/Notification'
18
+ export type * from './commonInputTypes'
@@ -0,0 +1,210 @@
1
+ -- CreateEnum
2
+ CREATE TYPE "UserRole" AS ENUM ('admin', 'customer');
3
+
4
+ -- CreateEnum
5
+ CREATE TYPE "OtpPurpose" AS ENUM ('setPassword', 'resetPassword', 'verifyIdentifier', 'changeIdentifier', 'enableMfa', 'disableMfa', 'verifyMfa');
6
+
7
+ -- CreateEnum
8
+ CREATE TYPE "OtpType" AS ENUM ('otp', 'token');
9
+
10
+ -- CreateEnum
11
+ CREATE TYPE "MfaMethod" AS ENUM ('email', 'sms', 'whatsapp', 'authApp');
12
+
13
+ -- CreateEnum
14
+ CREATE TYPE "NotificationType" AS ENUM ('email', 'sms', 'whatsapp', 'inApp');
15
+
16
+ -- CreateEnum
17
+ CREATE TYPE "NotificationStatus" AS ENUM ('pending', 'sent', 'failed', 'read');
18
+
19
+ -- CreateTable
20
+ CREATE TABLE "User" (
21
+ "id" TEXT NOT NULL,
22
+ "username" TEXT,
23
+ "password" TEXT,
24
+ "firstName" TEXT NOT NULL,
25
+ "lastName" TEXT,
26
+ "displayName" TEXT NOT NULL,
27
+ "imageUrl" TEXT,
28
+ "email" TEXT,
29
+ "phone" TEXT,
30
+ "isEmailVerified" BOOLEAN NOT NULL DEFAULT false,
31
+ "isPhoneVerified" BOOLEAN NOT NULL DEFAULT false,
32
+ "lastLoginAt" TIMESTAMP(3),
33
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
34
+ "updatedAt" TIMESTAMP(3) NOT NULL,
35
+ "deletedAt" TIMESTAMP(3),
36
+
37
+ CONSTRAINT "User_pkey" PRIMARY KEY ("id")
38
+ );
39
+
40
+ -- CreateTable
41
+ CREATE TABLE "RoleAssignment" (
42
+ "id" TEXT NOT NULL,
43
+ "userId" TEXT NOT NULL,
44
+ "role" "UserRole" NOT NULL,
45
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
46
+ "updatedAt" TIMESTAMP(3) NOT NULL,
47
+ "revokedAt" TIMESTAMP(3),
48
+
49
+ CONSTRAINT "RoleAssignment_pkey" PRIMARY KEY ("id")
50
+ );
51
+
52
+ -- CreateTable
53
+ CREATE TABLE "SecuritySetting" (
54
+ "id" TEXT NOT NULL,
55
+ "userId" TEXT NOT NULL,
56
+ "preferredMfa" "MfaMethod",
57
+ "recoveryEmail" TEXT,
58
+ "recoveryPhone" TEXT,
59
+ "isMfaEnabled" BOOLEAN NOT NULL DEFAULT false,
60
+ "loginAlerts" BOOLEAN NOT NULL DEFAULT true,
61
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
62
+ "updatedAt" TIMESTAMP(3) NOT NULL,
63
+
64
+ CONSTRAINT "SecuritySetting_pkey" PRIMARY KEY ("id")
65
+ );
66
+
67
+ -- CreateTable
68
+ CREATE TABLE "BackupCode" (
69
+ "id" TEXT NOT NULL,
70
+ "userId" TEXT NOT NULL,
71
+ "code" TEXT NOT NULL,
72
+ "usedAt" TIMESTAMP(3),
73
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
74
+ "expiresAt" TIMESTAMP(3) NOT NULL,
75
+
76
+ CONSTRAINT "BackupCode_pkey" PRIMARY KEY ("id")
77
+ );
78
+
79
+ -- CreateTable
80
+ CREATE TABLE "RefreshToken" (
81
+ "id" TEXT NOT NULL,
82
+ "token" TEXT NOT NULL,
83
+ "userId" TEXT NOT NULL,
84
+ "ip" TEXT NOT NULL,
85
+ "userAgent" TEXT NOT NULL,
86
+ "lastUsed" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
87
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
88
+ "expiresAt" TIMESTAMP(3) NOT NULL,
89
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
90
+ "blacklisted" BOOLEAN NOT NULL DEFAULT false,
91
+
92
+ CONSTRAINT "RefreshToken_pkey" PRIMARY KEY ("id")
93
+ );
94
+
95
+ -- CreateTable
96
+ CREATE TABLE "Otp" (
97
+ "id" TEXT NOT NULL,
98
+ "userId" TEXT NOT NULL,
99
+ "purpose" "OtpPurpose" NOT NULL,
100
+ "type" "OtpType" NOT NULL DEFAULT 'otp',
101
+ "secret" TEXT NOT NULL,
102
+ "expiresAt" TIMESTAMP(3) NOT NULL,
103
+ "usedAt" TIMESTAMP(3),
104
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
105
+ "updatedAt" TIMESTAMP(3) NOT NULL,
106
+
107
+ CONSTRAINT "Otp_pkey" PRIMARY KEY ("id")
108
+ );
109
+
110
+ -- CreateTable
111
+ CREATE TABLE "Notification" (
112
+ "id" TEXT NOT NULL,
113
+ "userId" TEXT NOT NULL,
114
+ "type" "NotificationType" NOT NULL,
115
+ "title" TEXT,
116
+ "message" TEXT,
117
+ "purpose" TEXT NOT NULL,
118
+ "metadata" JSONB,
119
+ "status" "NotificationStatus" NOT NULL DEFAULT 'pending',
120
+ "sentAt" TIMESTAMP(3),
121
+ "readAt" TIMESTAMP(3),
122
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
123
+ "updatedAt" TIMESTAMP(3) NOT NULL,
124
+
125
+ CONSTRAINT "Notification_pkey" PRIMARY KEY ("id")
126
+ );
127
+
128
+ -- CreateIndex
129
+ CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
130
+
131
+ -- CreateIndex
132
+ CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
133
+
134
+ -- CreateIndex
135
+ CREATE UNIQUE INDEX "User_phone_key" ON "User"("phone");
136
+
137
+ -- CreateIndex
138
+ CREATE INDEX "User_email_idx" ON "User"("email");
139
+
140
+ -- CreateIndex
141
+ CREATE INDEX "User_phone_idx" ON "User"("phone");
142
+
143
+ -- CreateIndex
144
+ CREATE INDEX "User_username_idx" ON "User"("username");
145
+
146
+ -- CreateIndex
147
+ CREATE INDEX "User_email_isEmailVerified_idx" ON "User"("email", "isEmailVerified");
148
+
149
+ -- CreateIndex
150
+ CREATE INDEX "User_phone_isPhoneVerified_idx" ON "User"("phone", "isPhoneVerified");
151
+
152
+ -- CreateIndex
153
+ CREATE UNIQUE INDEX "RoleAssignment_userId_role_key" ON "RoleAssignment"("userId", "role");
154
+
155
+ -- CreateIndex
156
+ CREATE UNIQUE INDEX "SecuritySetting_userId_key" ON "SecuritySetting"("userId");
157
+
158
+ -- CreateIndex
159
+ CREATE INDEX "SecuritySetting_userId_idx" ON "SecuritySetting"("userId");
160
+
161
+ -- CreateIndex
162
+ CREATE INDEX "SecuritySetting_isMfaEnabled_idx" ON "SecuritySetting"("isMfaEnabled");
163
+
164
+ -- CreateIndex
165
+ CREATE UNIQUE INDEX "BackupCode_code_key" ON "BackupCode"("code");
166
+
167
+ -- CreateIndex
168
+ CREATE INDEX "BackupCode_userId_usedAt_idx" ON "BackupCode"("userId", "usedAt");
169
+
170
+ -- CreateIndex
171
+ CREATE UNIQUE INDEX "RefreshToken_token_key" ON "RefreshToken"("token");
172
+
173
+ -- CreateIndex
174
+ CREATE INDEX "RefreshToken_userId_idx" ON "RefreshToken"("userId");
175
+
176
+ -- CreateIndex
177
+ CREATE INDEX "RefreshToken_expiresAt_idx" ON "RefreshToken"("expiresAt");
178
+
179
+ -- CreateIndex
180
+ CREATE INDEX "RefreshToken_userId_expiresAt_idx" ON "RefreshToken"("userId", "expiresAt");
181
+
182
+ -- CreateIndex
183
+ CREATE INDEX "Otp_userId_purpose_idx" ON "Otp"("userId", "purpose");
184
+
185
+ -- CreateIndex
186
+ CREATE INDEX "Otp_expiresAt_idx" ON "Otp"("expiresAt");
187
+
188
+ -- CreateIndex
189
+ CREATE INDEX "Notification_userId_idx" ON "Notification"("userId");
190
+
191
+ -- CreateIndex
192
+ CREATE INDEX "Notification_userId_readAt_idx" ON "Notification"("userId", "readAt");
193
+
194
+ -- AddForeignKey
195
+ ALTER TABLE "RoleAssignment" ADD CONSTRAINT "RoleAssignment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
196
+
197
+ -- AddForeignKey
198
+ ALTER TABLE "SecuritySetting" ADD CONSTRAINT "SecuritySetting_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
199
+
200
+ -- AddForeignKey
201
+ ALTER TABLE "BackupCode" ADD CONSTRAINT "BackupCode_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
202
+
203
+ -- AddForeignKey
204
+ ALTER TABLE "RefreshToken" ADD CONSTRAINT "RefreshToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
205
+
206
+ -- AddForeignKey
207
+ ALTER TABLE "Otp" ADD CONSTRAINT "Otp_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
208
+
209
+ -- AddForeignKey
210
+ ALTER TABLE "Notification" ADD CONSTRAINT "Notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,3 @@
1
+ # Please do not edit this file manually
2
+ # It should be added in your version-control system (e.g., Git)
3
+ provider = "postgresql"
@@ -0,0 +1,193 @@
1
+ // prisma/schema.prisma
2
+
3
+ generator client {
4
+ provider = "prisma-client"
5
+ output = "./generated"
6
+ }
7
+
8
+ datasource db {
9
+ provider = "postgresql"
10
+ }
11
+
12
+ //
13
+ // ENUMS
14
+ //
15
+
16
+ enum UserRole {
17
+ admin
18
+ customer
19
+ }
20
+
21
+ enum OtpPurpose {
22
+ setPassword
23
+ resetPassword
24
+ verifyIdentifier
25
+ changeIdentifier
26
+ enableMfa
27
+ disableMfa
28
+ verifyMfa
29
+ }
30
+
31
+ enum OtpType {
32
+ otp
33
+ token
34
+ }
35
+
36
+ enum MfaMethod {
37
+ email
38
+ sms
39
+ whatsapp
40
+ authApp
41
+ }
42
+
43
+ enum NotificationType {
44
+ email
45
+ sms
46
+ whatsapp
47
+ inApp
48
+ }
49
+
50
+ enum NotificationStatus {
51
+ pending
52
+ sent
53
+ failed
54
+ read
55
+ }
56
+
57
+ //
58
+ // AUTH MODELS
59
+ //
60
+
61
+ model User {
62
+ id String @id @default(ulid())
63
+ username String? @unique
64
+ password String?
65
+ firstName String
66
+ lastName String?
67
+ displayName String
68
+ imageUrl String?
69
+
70
+ // Contact
71
+ email String? @unique
72
+ phone String? @unique
73
+ isEmailVerified Boolean @default(false)
74
+ isPhoneVerified Boolean @default(false)
75
+
76
+ // Status
77
+ lastLoginAt DateTime?
78
+
79
+ createdAt DateTime @default(now())
80
+ updatedAt DateTime @updatedAt
81
+ deletedAt DateTime?
82
+
83
+ // Relations
84
+ refreshTokens RefreshToken[]
85
+ otps Otp[]
86
+ roles RoleAssignment[]
87
+ securitySetting SecuritySetting?
88
+
89
+ notifications Notification[]
90
+ backupCodes BackupCode[]
91
+
92
+ @@index([email])
93
+ @@index([phone])
94
+ @@index([username])
95
+ @@index([email, isEmailVerified])
96
+ @@index([phone, isPhoneVerified])
97
+ }
98
+
99
+ model RoleAssignment {
100
+ id String @id @default(ulid())
101
+ userId String
102
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
103
+ role UserRole
104
+
105
+ createdAt DateTime @default(now())
106
+ updatedAt DateTime @updatedAt
107
+ revokedAt DateTime?
108
+
109
+ @@unique([userId, role])
110
+ }
111
+
112
+ model SecuritySetting {
113
+ id String @id @default(ulid())
114
+ userId String @unique
115
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
116
+
117
+ preferredMfa MfaMethod?
118
+ recoveryEmail String?
119
+ recoveryPhone String?
120
+ isMfaEnabled Boolean @default(false)
121
+ loginAlerts Boolean @default(true)
122
+
123
+ createdAt DateTime @default(now())
124
+ updatedAt DateTime @updatedAt
125
+
126
+ @@index([userId])
127
+ @@index([isMfaEnabled])
128
+ }
129
+
130
+ model BackupCode {
131
+ id String @id @default(ulid())
132
+ userId String
133
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
134
+ code String @unique
135
+ usedAt DateTime?
136
+ createdAt DateTime @default(now())
137
+ expiresAt DateTime
138
+
139
+ @@index([userId, usedAt])
140
+ }
141
+
142
+ model RefreshToken {
143
+ id String @id @default(ulid())
144
+ token String @unique
145
+ userId String
146
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
147
+ ip String
148
+ userAgent String
149
+ lastUsed DateTime @default(now())
150
+ createdAt DateTime @default(now())
151
+ expiresAt DateTime
152
+ isActive Boolean @default(true)
153
+ blacklisted Boolean @default(false)
154
+
155
+ @@index([userId])
156
+ @@index([expiresAt])
157
+ @@index([userId, expiresAt])
158
+ }
159
+
160
+ model Otp {
161
+ id String @id @default(ulid())
162
+ userId String
163
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
164
+ purpose OtpPurpose
165
+ type OtpType @default(otp)
166
+ secret String
167
+ expiresAt DateTime
168
+ usedAt DateTime?
169
+ createdAt DateTime @default(now())
170
+ updatedAt DateTime @updatedAt
171
+
172
+ @@index([userId, purpose])
173
+ @@index([expiresAt])
174
+ }
175
+
176
+ model Notification {
177
+ id String @id @default(cuid())
178
+ userId String
179
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
180
+ type NotificationType
181
+ title String?
182
+ message String?
183
+ purpose String
184
+ metadata Json?
185
+ status NotificationStatus @default(pending)
186
+ sentAt DateTime?
187
+ readAt DateTime?
188
+ createdAt DateTime @default(now())
189
+ updatedAt DateTime @updatedAt
190
+
191
+ @@index([userId])
192
+ @@index([userId, readAt])
193
+ }
@@ -0,0 +1,13 @@
1
+ import 'dotenv/config'
2
+ import { defineConfig, env } from "prisma/config";
3
+
4
+ export default defineConfig({
5
+ schema: 'prisma/schema.prisma',
6
+ migrations: {
7
+ path: 'prisma/migrations',
8
+ seed: 'tsx prisma/seed.ts',
9
+ },
10
+ datasource: {
11
+ url: env("DB_URI")
12
+ }
13
+ });
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+
3
+ # Get the name (e.g., "auth") from the argument
4
+ NAME=$1
5
+
6
+ if [ -z "$NAME" ]; then
7
+ echo "❌ Please provide a name (e.g., ./generate.sh auth)"
8
+ exit 1
9
+ fi
10
+
11
+ # Run NestJS CLI commands
12
+ nest g module $NAME
13
+ nest g service $NAME
14
+ nest g controller $NAME
@@ -0,0 +1,49 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { ConfigModule } from "@nestjs/config";
3
+ import { ScheduleModule } from "@nestjs/schedule";
4
+ import { APP_GUARD, Reflector } from "@nestjs/core";
5
+ import { EnvModule } from "@modules/env/env.module";
6
+ import { validateEnv } from "@schemas/env.schema";
7
+ import { AuthGuard } from "@guards/auth.guard";
8
+ import { AuthModule } from "@modules/auth/auth.module";
9
+ import { TokenModule } from "@modules/token/token.module";
10
+ import { PublicModule } from "@modules/public/public.module";
11
+ import { TokenService } from "@modules/token/token.service";
12
+ import { PrismaModule } from "@modules/prisma/prisma.module";
13
+ import { LoggerModule } from "@modules/logger/logger.module";
14
+ import { SchedulerModule } from "@modules/scheduler/scheduler.module";
15
+ import { NotificationModule } from "@modules/notification/notification.module";
16
+ import { TemplateModule } from "@modules/template/template.module";
17
+ import { AllExceptionsFilter } from "@filters/exceptions.filter";
18
+ import { ResponseInterceptor } from "@/lib/interceptors/response.interceptor";
19
+
20
+ @Module({
21
+ imports: [
22
+ PublicModule,
23
+ PrismaModule,
24
+ LoggerModule,
25
+ EnvModule,
26
+ AuthModule,
27
+ TokenModule,
28
+ TemplateModule,
29
+ NotificationModule,
30
+ SchedulerModule,
31
+ ScheduleModule.forRoot(),
32
+ ConfigModule.forRoot({
33
+ isGlobal: true,
34
+ validate: validateEnv,
35
+ }),
36
+ ],
37
+
38
+ providers: [
39
+ {
40
+ provide: APP_GUARD,
41
+ useFactory: (tokenService, reflector) =>
42
+ new AuthGuard(tokenService, reflector),
43
+ inject: [TokenService, Reflector],
44
+ },
45
+ AllExceptionsFilter,
46
+ ResponseInterceptor,
47
+ ],
48
+ })
49
+ export class AppModule {}
@@ -0,0 +1,20 @@
1
+ import { Inject } from "@nestjs/common";
2
+ import { LoggerService } from "@modules/logger/logger.service";
3
+
4
+ export function InjectLogger(context?: string): PropertyDecorator {
5
+ return (target: object, propertyKey: string | symbol) => {
6
+ Inject(LoggerService)(target, propertyKey);
7
+
8
+ const originalInit = target.constructor.prototype?.onModuleInit;
9
+
10
+ target.constructor.prototype.onModuleInit = function (...args: any[]) {
11
+ const logger: LoggerService = (this as any)[propertyKey];
12
+ if (logger && typeof logger.setContext === "function") {
13
+ const ctx = context || target.constructor.name;
14
+ logger.setContext(ctx);
15
+ }
16
+
17
+ if (originalInit) return originalInit.apply(this, args);
18
+ };
19
+ };
20
+ }
@@ -0,0 +1,4 @@
1
+ import { SetMetadata } from "@nestjs/common";
2
+
3
+ export const IS_PUBLIC_KEY = "isPublic";
4
+ export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
@@ -0,0 +1,5 @@
1
+ import { SetMetadata } from "@nestjs/common";
2
+ import { UserRole } from "@generated/prisma";
3
+
4
+ export const ROLES_KEY = "roles";
5
+ export const Roles = (...roles: UserRole[]) => SetMetadata(ROLES_KEY, roles);
@@ -0,0 +1,64 @@
1
+ import { OtpPurpose, OtpType } from "@generated/prisma";
2
+ import {
3
+ IsEnum,
4
+ IsNotEmpty,
5
+ IsOptional,
6
+ IsString,
7
+ MinLength,
8
+ } from "class-validator";
9
+
10
+ export class SignInDto {
11
+ @IsString()
12
+ @IsNotEmpty()
13
+ identifier!: string;
14
+
15
+ @IsString()
16
+ @IsNotEmpty()
17
+ @MinLength(8)
18
+ password!: string;
19
+ }
20
+
21
+ export class SignUpDto extends SignInDto {
22
+ @IsOptional()
23
+ @IsString()
24
+ username?: string;
25
+
26
+ @IsString()
27
+ @IsNotEmpty()
28
+ firstName!: string;
29
+
30
+ @IsOptional()
31
+ @IsString()
32
+ lastName?: string;
33
+ }
34
+
35
+ export class RequestOtpDto {
36
+ @IsString()
37
+ @IsNotEmpty()
38
+ identifier!: string;
39
+
40
+ @IsEnum(OtpPurpose)
41
+ purpose!: OtpPurpose;
42
+ }
43
+
44
+ export class ValidateOtpDto extends RequestOtpDto {
45
+ @IsString()
46
+ @IsNotEmpty()
47
+ secret!: string;
48
+
49
+ @IsOptional()
50
+ @IsEnum(OtpType)
51
+ type?: OtpType;
52
+ }
53
+
54
+ export class ResetPasswordDto extends ValidateOtpDto {
55
+ @IsString()
56
+ @IsNotEmpty()
57
+ newPassword!: string;
58
+ }
59
+
60
+ export class ChangeIdentifierDto extends ValidateOtpDto {
61
+ @IsString()
62
+ @IsNotEmpty()
63
+ newIdentifier!: string;
64
+ }
@@ -0,0 +1,21 @@
1
+ import { IsBoolean, IsNotEmpty, IsOptional, IsString } from "class-validator";
2
+
3
+ export class VerifyBackupCodeDto {
4
+ @IsNotEmpty()
5
+ @IsString()
6
+ code!: string;
7
+ }
8
+
9
+ export class UpdateSecuritySettingDto {
10
+ @IsOptional()
11
+ @IsString()
12
+ recoveryEmail?: string;
13
+
14
+ @IsOptional()
15
+ @IsString()
16
+ recoveryPhone?: string;
17
+
18
+ @IsOptional()
19
+ @IsBoolean()
20
+ loginAlerts?: boolean;
21
+ }
@@ -0,0 +1,62 @@
1
+ import { LoggerService } from "@modules/logger/logger.service";
2
+ import type { Request, Response } from "express";
3
+ import {
4
+ Catch,
5
+ HttpException,
6
+ HttpStatus,
7
+ type ExceptionFilter,
8
+ type ArgumentsHost,
9
+ } from "@nestjs/common";
10
+ import { InjectLogger } from "@decorators/logger.decorator";
11
+
12
+ @Catch()
13
+ export class AllExceptionsFilter implements ExceptionFilter {
14
+ @InjectLogger()
15
+ private readonly logger!: LoggerService;
16
+
17
+ catch(exception: any, host: ArgumentsHost) {
18
+ const ctx = host.switchToHttp();
19
+ const req = ctx.getRequest<Request>();
20
+ const res = ctx.getResponse<Response>();
21
+
22
+ const status =
23
+ exception instanceof HttpException
24
+ ? exception.getStatus()
25
+ : HttpStatus.INTERNAL_SERVER_ERROR;
26
+
27
+ let message: any = "Internal server error";
28
+
29
+ if (exception instanceof HttpException) {
30
+ const res = exception.getResponse();
31
+
32
+ if (
33
+ res &&
34
+ typeof res === "object" &&
35
+ "message" in res &&
36
+ Array.isArray((res as any).message)
37
+ ) {
38
+ message = (res as any).message;
39
+ } else if (typeof res === "string") {
40
+ message = res;
41
+ } else if (typeof exception.message === "string") {
42
+ message = exception.message;
43
+ }
44
+ }
45
+ // if (response.headersSent) return;
46
+
47
+ this.logger.error(`❌ Exception caught`, {
48
+ message: exception.message,
49
+ stack: exception.stack,
50
+ status,
51
+ path: req.url,
52
+ method: req.method,
53
+ });
54
+
55
+ res.status(status).json({
56
+ status,
57
+ data: null,
58
+ message,
59
+ success: false,
60
+ });
61
+ }
62
+ }