create-zhx-monorepo 0.2.2 → 2.0.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 (349) hide show
  1. package/README.md +1 -1
  2. package/bin/index.js +0 -0
  3. package/package.json +3 -3
  4. package/templates/monorepo-starter/apps/dashboard/components.json +20 -0
  5. package/templates/monorepo-starter/apps/dashboard/eslint.config.js +1 -0
  6. package/templates/monorepo-starter/apps/dashboard/next-env.d.ts +6 -0
  7. package/templates/monorepo-starter/apps/dashboard/next.config.mjs +30 -0
  8. package/templates/monorepo-starter/apps/dashboard/package.json +43 -0
  9. package/templates/monorepo-starter/apps/dashboard/postcss.config.mjs +1 -0
  10. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/[id]/edit/page.tsx +8 -0
  11. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/[id]/page.tsx +104 -0
  12. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/new/page.tsx +7 -0
  13. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/admin/users/page.tsx +88 -0
  14. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/dashboard/data.json +614 -0
  15. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/dashboard/page.tsx +21 -0
  16. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/layout.tsx +40 -0
  17. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/media/page.tsx +7 -0
  18. package/templates/monorepo-starter/apps/dashboard/src/app/(root)/users/account/page.tsx +75 -0
  19. package/templates/monorepo-starter/apps/dashboard/src/app/auth/[type]/page.tsx +19 -0
  20. package/templates/monorepo-starter/apps/dashboard/src/app/auth/layout.tsx +11 -0
  21. package/templates/monorepo-starter/apps/dashboard/src/app/auth/verify/page.tsx +11 -0
  22. package/templates/monorepo-starter/apps/dashboard/src/app/favicon.ico +0 -0
  23. package/templates/monorepo-starter/apps/dashboard/src/app/layout.tsx +28 -0
  24. package/templates/monorepo-starter/apps/dashboard/src/components/forms/AuthForm.tsx +345 -0
  25. package/templates/monorepo-starter/apps/dashboard/src/components/forms/CUUserForm.tsx +223 -0
  26. package/templates/monorepo-starter/apps/dashboard/src/components/forms/MediaForm.tsx +35 -0
  27. package/templates/monorepo-starter/apps/dashboard/src/components/forms/SocialAuthField.tsx +56 -0
  28. package/templates/monorepo-starter/apps/dashboard/src/components/layout/AppSidebar.tsx +141 -0
  29. package/templates/monorepo-starter/apps/dashboard/src/components/layout/DashboardChart.tsx +293 -0
  30. package/templates/monorepo-starter/apps/dashboard/src/components/layout/DashboardStats.tsx +104 -0
  31. package/templates/monorepo-starter/apps/dashboard/src/components/layout/Header.tsx +21 -0
  32. package/templates/monorepo-starter/apps/dashboard/src/components/layout/NavMain.tsx +99 -0
  33. package/templates/monorepo-starter/apps/dashboard/src/components/media/LibraryPopup.tsx +25 -0
  34. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaCard.tsx +175 -0
  35. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaDetails.tsx +58 -0
  36. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaGrid.tsx +51 -0
  37. package/templates/monorepo-starter/apps/dashboard/src/components/media/MediaUploader.tsx +182 -0
  38. package/templates/monorepo-starter/apps/dashboard/src/components/media/index.tsx +96 -0
  39. package/templates/monorepo-starter/apps/dashboard/src/components/shared/DateWrapper.tsx +43 -0
  40. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericArrayField.tsx +189 -0
  41. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericCUForm.tsx +124 -0
  42. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericDetailsPage.tsx +291 -0
  43. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericFormTable.tsx +189 -0
  44. package/templates/monorepo-starter/apps/dashboard/src/components/shared/GenericTable.tsx +244 -0
  45. package/templates/monorepo-starter/apps/dashboard/src/components/shared/ListPage.tsx +134 -0
  46. package/templates/monorepo-starter/apps/dashboard/src/components/shared/Pagination.tsx +120 -0
  47. package/templates/monorepo-starter/apps/dashboard/src/components/shared/SearchToolbar.tsx +133 -0
  48. package/templates/monorepo-starter/apps/dashboard/src/components/shared/ThemeSwitch.tsx +41 -0
  49. package/templates/monorepo-starter/apps/dashboard/src/components/shared/VerifyAuthPage.tsx +109 -0
  50. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/CUFormSkeleton.tsx +31 -0
  51. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/DashboardSke1eton.tsx +135 -0
  52. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/DetailsPageSkeleton.tsx +64 -0
  53. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/RootLayoutSkeleton.tsx +106 -0
  54. package/templates/monorepo-starter/apps/dashboard/src/components/skeleton/TableSkeleton.tsx +77 -0
  55. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserAccountSection.tsx +530 -0
  56. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserCard.tsx +54 -0
  57. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserProfileSection.tsx +239 -0
  58. package/templates/monorepo-starter/apps/dashboard/src/components/user/UserSessions.tsx +154 -0
  59. package/templates/monorepo-starter/apps/dashboard/src/hooks/admin.ts +100 -0
  60. package/templates/monorepo-starter/apps/dashboard/src/hooks/auth.ts +59 -0
  61. package/templates/monorepo-starter/apps/dashboard/src/hooks/crud.ts +275 -0
  62. package/templates/monorepo-starter/apps/dashboard/src/hooks/lookup.ts +38 -0
  63. package/templates/monorepo-starter/apps/dashboard/src/hooks/media.ts +34 -0
  64. package/templates/monorepo-starter/apps/dashboard/src/hooks/user.ts +58 -0
  65. package/templates/monorepo-starter/apps/dashboard/src/lib/constants.ts +61 -0
  66. package/templates/monorepo-starter/apps/dashboard/src/providers/index.tsx +15 -0
  67. package/templates/monorepo-starter/apps/dashboard/src/providers/media-library.tsx +48 -0
  68. package/templates/monorepo-starter/apps/dashboard/tsconfig.json +20 -0
  69. package/templates/monorepo-starter/apps/web/components.json +20 -20
  70. package/templates/monorepo-starter/apps/web/eslint.config.js +1 -0
  71. package/templates/monorepo-starter/apps/web/next-env.d.ts +6 -6
  72. package/templates/monorepo-starter/apps/web/next.config.mjs +20 -6
  73. package/templates/monorepo-starter/apps/web/package.json +36 -31
  74. package/templates/monorepo-starter/apps/web/postcss.config.mjs +1 -1
  75. package/templates/monorepo-starter/apps/web/src/app/(root)/layout.tsx +2 -2
  76. package/templates/monorepo-starter/apps/web/src/app/favicon.ico +0 -0
  77. package/templates/monorepo-starter/apps/web/src/app/layout.tsx +13 -9
  78. package/templates/monorepo-starter/apps/web/src/components/Footer.tsx +5 -7
  79. package/templates/monorepo-starter/apps/web/src/components/Header.tsx +5 -9
  80. package/templates/monorepo-starter/apps/web/tsconfig.json +20 -20
  81. package/templates/monorepo-starter/eslint.config.mjs +1 -1
  82. package/templates/monorepo-starter/gitignore +13 -4
  83. package/templates/monorepo-starter/package.json +49 -23
  84. package/templates/monorepo-starter/packages/config/eslint/next.mjs +18 -0
  85. package/templates/monorepo-starter/packages/config/eslint/node.mjs +44 -0
  86. package/templates/monorepo-starter/packages/config/eslint/react.mjs +68 -0
  87. package/templates/monorepo-starter/packages/config/package.json +23 -32
  88. package/templates/monorepo-starter/packages/config/typescript/node.json +25 -0
  89. package/templates/monorepo-starter/packages/config/typescript/react.json +20 -3
  90. package/templates/monorepo-starter/packages/contracts/eslint.config.mjs +1 -0
  91. package/templates/monorepo-starter/packages/contracts/package.json +51 -0
  92. package/templates/monorepo-starter/packages/contracts/src/admin/dto.ts +6 -0
  93. package/templates/monorepo-starter/packages/contracts/src/admin/index.ts +2 -0
  94. package/templates/monorepo-starter/packages/contracts/src/admin/schema.ts +28 -0
  95. package/templates/monorepo-starter/packages/contracts/src/admin/types.d.ts +27 -0
  96. package/templates/monorepo-starter/packages/contracts/src/auth/dto.ts +24 -0
  97. package/templates/monorepo-starter/packages/contracts/src/auth/index.ts +2 -0
  98. package/templates/monorepo-starter/packages/contracts/src/auth/schema.ts +35 -0
  99. package/templates/monorepo-starter/packages/contracts/src/auth/types.d.ts +73 -0
  100. package/templates/monorepo-starter/packages/contracts/src/lib/enums.ts +55 -0
  101. package/templates/monorepo-starter/packages/contracts/src/lib/index.ts +4 -0
  102. package/templates/monorepo-starter/packages/contracts/src/lib/prisma.ts +2 -0
  103. package/templates/monorepo-starter/packages/contracts/src/lib/schema.ts +61 -0
  104. package/templates/monorepo-starter/packages/contracts/src/lib/types.d.ts +149 -0
  105. package/templates/monorepo-starter/packages/contracts/src/lib/utils.ts +8 -0
  106. package/templates/monorepo-starter/packages/contracts/src/media/dto.ts +12 -0
  107. package/templates/monorepo-starter/packages/contracts/src/media/index.ts +2 -0
  108. package/templates/monorepo-starter/packages/contracts/src/media/schema.ts +27 -0
  109. package/templates/monorepo-starter/packages/contracts/src/media/types.d.ts +36 -0
  110. package/templates/monorepo-starter/packages/contracts/src/notification/dto.ts +6 -0
  111. package/templates/monorepo-starter/packages/contracts/src/notification/index.ts +2 -0
  112. package/templates/monorepo-starter/packages/contracts/src/notification/schema.ts +7 -0
  113. package/templates/monorepo-starter/packages/contracts/src/notification/types.d.ts +22 -0
  114. package/templates/monorepo-starter/packages/contracts/src/user/dto.ts +4 -0
  115. package/templates/monorepo-starter/packages/contracts/src/user/index.ts +2 -0
  116. package/templates/monorepo-starter/packages/contracts/src/user/schema.ts +16 -0
  117. package/templates/monorepo-starter/packages/contracts/src/user/types.d.ts +36 -0
  118. package/templates/monorepo-starter/packages/contracts/tsconfig.json +12 -0
  119. package/templates/monorepo-starter/packages/contracts/tsup.config.ts +20 -0
  120. package/templates/monorepo-starter/packages/sdk/eslint.config.mjs +1 -0
  121. package/templates/monorepo-starter/packages/sdk/package.json +31 -0
  122. package/templates/monorepo-starter/packages/sdk/src/admin/index.ts +31 -0
  123. package/templates/monorepo-starter/packages/sdk/src/auth/index.ts +71 -0
  124. package/templates/monorepo-starter/packages/sdk/src/lib/api-client.ts +71 -0
  125. package/templates/monorepo-starter/packages/sdk/src/lib/index.ts +2 -0
  126. package/templates/monorepo-starter/packages/sdk/src/lib/types.ts +40 -0
  127. package/templates/monorepo-starter/packages/sdk/src/media/index.ts +27 -0
  128. package/templates/monorepo-starter/packages/sdk/src/notification/index.ts +10 -0
  129. package/templates/monorepo-starter/packages/sdk/src/public/index.ts +6 -0
  130. package/templates/monorepo-starter/packages/sdk/src/user/index.ts +7 -0
  131. package/templates/monorepo-starter/packages/sdk/tsconfig.json +12 -0
  132. package/templates/monorepo-starter/packages/shared/eslint.config.mjs +1 -0
  133. package/templates/monorepo-starter/packages/shared/package.json +36 -0
  134. package/templates/monorepo-starter/packages/shared/src/constants/app.ts +4 -0
  135. package/templates/monorepo-starter/packages/shared/src/constants/index.ts +1 -0
  136. package/templates/monorepo-starter/packages/shared/src/utils/date.ts +137 -0
  137. package/templates/monorepo-starter/packages/shared/src/utils/identifier.ts +11 -0
  138. package/templates/monorepo-starter/packages/shared/src/utils/index.ts +2 -0
  139. package/templates/monorepo-starter/packages/shared/tsconfig.json +12 -0
  140. package/templates/monorepo-starter/packages/shared/tsup.config.ts +16 -0
  141. package/templates/monorepo-starter/packages/templates/eslint.config.mjs +1 -0
  142. package/templates/monorepo-starter/packages/templates/package.json +38 -0
  143. package/templates/monorepo-starter/packages/templates/src/email/auth/disableMfa.tsx +40 -0
  144. package/templates/monorepo-starter/packages/templates/src/email/auth/index.ts +10 -0
  145. package/templates/monorepo-starter/packages/templates/src/email/auth/resetPassword.tsx +41 -0
  146. package/templates/monorepo-starter/packages/templates/src/email/auth/securityAlert.tsx +21 -0
  147. package/templates/monorepo-starter/packages/templates/src/email/auth/setPassword.tsx +42 -0
  148. package/templates/monorepo-starter/packages/templates/src/email/auth/signin.tsx +19 -0
  149. package/templates/monorepo-starter/packages/templates/src/email/auth/signup.tsx +24 -0
  150. package/templates/monorepo-starter/packages/templates/src/email/auth/updateIdentifier.tsx +79 -0
  151. package/templates/monorepo-starter/packages/templates/src/email/auth/updateMfa.tsx +40 -0
  152. package/templates/monorepo-starter/packages/templates/src/email/auth/verifyIdentifier.tsx +42 -0
  153. package/templates/monorepo-starter/packages/templates/src/email/auth/verifyMfa.tsx +23 -0
  154. package/templates/monorepo-starter/packages/templates/src/email/components/actionBlock.tsx +39 -0
  155. package/templates/monorepo-starter/packages/templates/src/email/components/footer.tsx +0 -0
  156. package/templates/monorepo-starter/packages/templates/src/email/components/greeting.tsx +11 -0
  157. package/templates/monorepo-starter/packages/templates/src/email/components/header.tsx +15 -0
  158. package/templates/monorepo-starter/packages/templates/src/email/components/layout.tsx +33 -0
  159. package/templates/monorepo-starter/packages/templates/src/email/components/numericCode.tsx +16 -0
  160. package/templates/monorepo-starter/packages/templates/src/email/index.ts +2 -0
  161. package/templates/monorepo-starter/packages/templates/src/email/service/accountReactivated.tsx +31 -0
  162. package/templates/monorepo-starter/packages/templates/src/email/service/accountSuspended.tsx +37 -0
  163. package/templates/monorepo-starter/packages/templates/src/email/service/index.ts +2 -0
  164. package/templates/monorepo-starter/packages/templates/src/email/test.tsx +141 -0
  165. package/templates/monorepo-starter/packages/templates/src/index.ts +1 -0
  166. package/templates/monorepo-starter/packages/templates/src/lib/constants.ts +22 -0
  167. package/templates/monorepo-starter/packages/templates/src/lib/utils.ts +19 -0
  168. package/templates/monorepo-starter/packages/templates/src/types/global.d.ts +37 -0
  169. package/templates/monorepo-starter/packages/templates/tsconfig.json +13 -0
  170. package/templates/monorepo-starter/packages/templates/tsup.config.ts +23 -0
  171. package/templates/monorepo-starter/packages/ui/components.json +1 -1
  172. package/templates/monorepo-starter/packages/ui/eslint.config.mjs +1 -4
  173. package/templates/monorepo-starter/packages/ui/package.json +81 -38
  174. package/templates/monorepo-starter/packages/ui/src/components/accordion.tsx +66 -0
  175. package/templates/monorepo-starter/packages/ui/src/components/airport-search-field.tsx +203 -0
  176. package/templates/monorepo-starter/packages/ui/src/components/alert-dialog.tsx +157 -0
  177. package/templates/monorepo-starter/packages/ui/src/components/alert.tsx +66 -0
  178. package/templates/monorepo-starter/packages/ui/src/components/aspect-ratio.tsx +11 -0
  179. package/templates/monorepo-starter/packages/ui/src/components/avatar.tsx +53 -0
  180. package/templates/monorepo-starter/packages/ui/src/components/badge.tsx +46 -0
  181. package/templates/monorepo-starter/packages/ui/src/components/breadcrumb.tsx +109 -0
  182. package/templates/monorepo-starter/packages/ui/src/components/button-group.tsx +83 -0
  183. package/templates/monorepo-starter/packages/ui/src/components/button.tsx +2 -1
  184. package/templates/monorepo-starter/packages/ui/src/components/calendar.tsx +220 -0
  185. package/templates/monorepo-starter/packages/ui/src/components/card.tsx +92 -0
  186. package/templates/monorepo-starter/packages/ui/src/components/carousel.tsx +241 -0
  187. package/templates/monorepo-starter/packages/ui/src/components/chart.tsx +357 -0
  188. package/templates/monorepo-starter/packages/ui/src/components/checkbox.tsx +32 -0
  189. package/templates/monorepo-starter/packages/ui/src/components/collapsible.tsx +33 -0
  190. package/templates/monorepo-starter/packages/ui/src/components/combobox-field.tsx +181 -0
  191. package/templates/monorepo-starter/packages/ui/src/components/combobox.tsx +309 -0
  192. package/templates/monorepo-starter/packages/ui/src/components/command.tsx +184 -0
  193. package/templates/monorepo-starter/packages/ui/src/components/context-menu.tsx +252 -0
  194. package/templates/monorepo-starter/packages/ui/src/components/date-field.tsx +65 -0
  195. package/templates/monorepo-starter/packages/ui/src/components/dialog.tsx +143 -0
  196. package/templates/monorepo-starter/packages/ui/src/components/direction.tsx +22 -0
  197. package/templates/monorepo-starter/packages/ui/src/components/drawer.tsx +135 -0
  198. package/templates/monorepo-starter/packages/ui/src/components/dropdown-menu.tsx +257 -0
  199. package/templates/monorepo-starter/packages/ui/src/components/empty.tsx +104 -0
  200. package/templates/monorepo-starter/packages/ui/src/components/field.tsx +248 -0
  201. package/templates/monorepo-starter/packages/ui/src/components/form.tsx +197 -0
  202. package/templates/monorepo-starter/packages/ui/src/components/hover-card.tsx +44 -0
  203. package/templates/monorepo-starter/packages/ui/src/components/input-field.tsx +116 -0
  204. package/templates/monorepo-starter/packages/ui/src/components/input-group.tsx +170 -0
  205. package/templates/monorepo-starter/packages/ui/src/components/input-otp.tsx +77 -0
  206. package/templates/monorepo-starter/packages/ui/src/components/input.tsx +44 -0
  207. package/templates/monorepo-starter/packages/ui/src/components/item.tsx +193 -0
  208. package/templates/monorepo-starter/packages/ui/src/components/kbd.tsx +28 -0
  209. package/templates/monorepo-starter/packages/ui/src/components/label.tsx +24 -0
  210. package/templates/monorepo-starter/packages/ui/src/components/menubar.tsx +276 -0
  211. package/templates/monorepo-starter/packages/ui/src/components/native-select.tsx +53 -0
  212. package/templates/monorepo-starter/packages/ui/src/components/navigation-menu.tsx +168 -0
  213. package/templates/monorepo-starter/packages/ui/src/components/otp-modal.tsx +125 -0
  214. package/templates/monorepo-starter/packages/ui/src/components/pagination.tsx +127 -0
  215. package/templates/monorepo-starter/packages/ui/src/components/popover.tsx +48 -0
  216. package/templates/monorepo-starter/packages/ui/src/components/progress.tsx +31 -0
  217. package/templates/monorepo-starter/packages/ui/src/components/radio-group.tsx +45 -0
  218. package/templates/monorepo-starter/packages/ui/src/components/resizable.tsx +53 -0
  219. package/templates/monorepo-starter/packages/ui/src/components/scroll-area.tsx +58 -0
  220. package/templates/monorepo-starter/packages/ui/src/components/select-field.tsx +165 -0
  221. package/templates/monorepo-starter/packages/ui/src/components/select.tsx +190 -0
  222. package/templates/monorepo-starter/packages/ui/src/components/separator.tsx +28 -0
  223. package/templates/monorepo-starter/packages/ui/src/components/sheet.tsx +139 -0
  224. package/templates/monorepo-starter/packages/ui/src/components/sidebar.tsx +728 -0
  225. package/templates/monorepo-starter/packages/ui/src/components/skeleton.tsx +13 -0
  226. package/templates/monorepo-starter/packages/ui/src/components/slider.tsx +63 -0
  227. package/templates/monorepo-starter/packages/ui/src/components/sonner.tsx +57 -0
  228. package/templates/monorepo-starter/packages/ui/src/components/spinner.tsx +16 -0
  229. package/templates/monorepo-starter/packages/ui/src/components/switch-field.tsx +22 -0
  230. package/templates/monorepo-starter/packages/ui/src/components/switch.tsx +35 -0
  231. package/templates/monorepo-starter/packages/ui/src/components/table.tsx +120 -0
  232. package/templates/monorepo-starter/packages/ui/src/components/tabs.tsx +66 -0
  233. package/templates/monorepo-starter/packages/ui/src/components/textarea.tsx +18 -0
  234. package/templates/monorepo-starter/packages/ui/src/components/toggle-group.tsx +83 -0
  235. package/templates/monorepo-starter/packages/ui/src/components/toggle.tsx +47 -0
  236. package/templates/monorepo-starter/packages/ui/src/components/tooltip.tsx +61 -0
  237. package/templates/monorepo-starter/packages/ui/src/hooks/use-confirm.ts +12 -0
  238. package/templates/monorepo-starter/packages/ui/src/hooks/use-dialog.ts +12 -0
  239. package/templates/monorepo-starter/packages/ui/src/hooks/use-mobile.ts +21 -0
  240. package/templates/monorepo-starter/packages/ui/src/lib/utils.ts +38 -3
  241. package/templates/monorepo-starter/packages/ui/src/provider/confirm-dialog.tsx +99 -0
  242. package/templates/monorepo-starter/packages/ui/src/provider/dialog.tsx +62 -0
  243. package/templates/monorepo-starter/packages/ui/src/provider/index.tsx +30 -0
  244. package/templates/monorepo-starter/packages/ui/src/provider/react-query.tsx +11 -0
  245. package/templates/monorepo-starter/{apps/web/src/providers → packages/ui/src/provider}/theme.tsx +5 -7
  246. package/templates/monorepo-starter/packages/ui/src/styles/globals.css +109 -61
  247. package/templates/monorepo-starter/packages/ui/src/types/airports-data.d.ts +21 -0
  248. package/templates/monorepo-starter/packages/ui/tsconfig.json +1 -3
  249. package/templates/monorepo-starter/pnpm-lock.yaml +17765 -12441
  250. package/templates/monorepo-starter/pnpm-workspace.yaml +3 -16
  251. package/templates/monorepo-starter/server/.env.example +7 -5
  252. package/templates/monorepo-starter/server/eslint.config.mjs +1 -4
  253. package/templates/monorepo-starter/server/package.json +129 -97
  254. package/templates/monorepo-starter/server/prisma/schema.prisma +170 -92
  255. package/templates/monorepo-starter/server/prisma/seed-functions.ts +0 -0
  256. package/templates/monorepo-starter/server/prisma/seed.ts +0 -0
  257. package/templates/monorepo-starter/server/prisma.config.ts +9 -9
  258. package/templates/monorepo-starter/server/scripts/admin-bootstrap.ts +61 -0
  259. package/templates/monorepo-starter/server/scripts/gen-module.sh +20 -0
  260. package/templates/monorepo-starter/server/scripts/gen-soft-delete.ts +52 -0
  261. package/templates/monorepo-starter/server/src/app.module.ts +35 -28
  262. package/templates/monorepo-starter/server/src/lib/constants/index.ts +59 -0
  263. package/templates/monorepo-starter/server/src/lib/decorators/boolean-query.decorator.ts +18 -0
  264. package/templates/monorepo-starter/server/src/lib/decorators/logger.decorator.ts +1 -1
  265. package/templates/monorepo-starter/server/src/lib/decorators/oauth.decorator.ts +7 -0
  266. package/templates/monorepo-starter/server/src/lib/decorators/user.decorator.ts +10 -0
  267. package/templates/monorepo-starter/server/src/lib/filters/exceptions.filter.ts +169 -24
  268. package/templates/monorepo-starter/server/src/lib/guards/auth.guard.ts +29 -65
  269. package/templates/monorepo-starter/server/src/lib/guards/oauth.guard.ts +28 -0
  270. package/templates/monorepo-starter/server/src/lib/interceptors/response.interceptor.ts +42 -5
  271. package/templates/monorepo-starter/server/src/lib/middleware/cache.middleware.ts +24 -0
  272. package/templates/monorepo-starter/server/src/lib/schemas/env.schema.ts +25 -5
  273. package/templates/monorepo-starter/server/src/lib/utils/client.utils.ts +95 -0
  274. package/templates/monorepo-starter/server/src/lib/utils/cookie.util.ts +4 -3
  275. package/templates/monorepo-starter/server/src/main.ts +20 -10
  276. package/templates/monorepo-starter/server/src/modules/admin/admin.controller.ts +54 -0
  277. package/templates/monorepo-starter/server/src/modules/admin/admin.module.ts +13 -0
  278. package/templates/monorepo-starter/server/src/modules/admin/admin.service.ts +134 -0
  279. package/templates/monorepo-starter/server/src/modules/auth/auth.controller.ts +60 -18
  280. package/templates/monorepo-starter/server/src/modules/auth/auth.module.ts +3 -2
  281. package/templates/monorepo-starter/server/src/modules/auth/auth.service.ts +354 -307
  282. package/templates/monorepo-starter/server/src/modules/auth/oauth.controller.ts +50 -28
  283. package/templates/monorepo-starter/server/src/modules/auth/oauth.service.ts +59 -34
  284. package/templates/monorepo-starter/server/src/modules/auth/otp.service.ts +29 -36
  285. package/templates/monorepo-starter/server/src/modules/cache/cache.module.ts +28 -0
  286. package/templates/monorepo-starter/server/src/modules/cache/cache.service.ts +33 -0
  287. package/templates/monorepo-starter/server/src/modules/env/env.service.ts +4 -3
  288. package/templates/monorepo-starter/server/src/modules/logger/logger.module.ts +1 -0
  289. package/templates/monorepo-starter/server/src/modules/logger/logger.service.ts +1 -1
  290. package/templates/monorepo-starter/server/src/modules/logger/winston.config.ts +15 -1
  291. package/templates/monorepo-starter/server/src/modules/media/cloudinary.provider.ts +14 -0
  292. package/templates/monorepo-starter/server/src/modules/media/cloudinary.service.ts +62 -0
  293. package/templates/monorepo-starter/server/src/modules/media/media.controller.ts +72 -0
  294. package/templates/monorepo-starter/server/src/modules/media/media.module.ts +13 -0
  295. package/templates/monorepo-starter/server/src/modules/media/media.service.ts +146 -0
  296. package/templates/monorepo-starter/server/src/modules/notification/email.service.ts +30 -0
  297. package/templates/monorepo-starter/server/src/modules/notification/messaging.service.ts +50 -0
  298. package/templates/monorepo-starter/server/src/modules/notification/notification.controller.ts +44 -0
  299. package/templates/monorepo-starter/server/src/modules/notification/notification.module.ts +7 -3
  300. package/templates/monorepo-starter/server/src/modules/notification/notification.service.ts +121 -78
  301. package/templates/monorepo-starter/server/src/modules/notification/push.service.ts +92 -0
  302. package/templates/monorepo-starter/server/src/modules/prisma/prisma.extension.ts +84 -56
  303. package/templates/monorepo-starter/server/src/modules/prisma/prisma.service.ts +10 -9
  304. package/templates/monorepo-starter/server/src/modules/prisma/soft-delete.models.ts +7 -0
  305. package/templates/monorepo-starter/server/src/modules/public/public.controller.ts +3 -2
  306. package/templates/monorepo-starter/server/src/modules/public/public.module.ts +5 -4
  307. package/templates/monorepo-starter/server/src/modules/public/public.service.ts +7 -4
  308. package/templates/monorepo-starter/server/src/modules/scheduler/cleanup.service.ts +15 -7
  309. package/templates/monorepo-starter/server/src/modules/scheduler/scheduler.module.ts +2 -0
  310. package/templates/monorepo-starter/server/src/modules/token/token.module.ts +3 -4
  311. package/templates/monorepo-starter/server/src/modules/token/token.service.ts +205 -76
  312. package/templates/monorepo-starter/server/src/modules/user/user.controller.ts +21 -0
  313. package/templates/monorepo-starter/server/src/modules/user/user.module.ts +12 -0
  314. package/templates/monorepo-starter/server/src/modules/user/user.service.ts +38 -0
  315. package/templates/monorepo-starter/server/src/types/express.d.ts +6 -2
  316. package/templates/monorepo-starter/server/src/types/index.ts +5 -0
  317. package/templates/monorepo-starter/server/tsconfig.json +17 -12
  318. package/templates/monorepo-starter/server/tsup.config.ts +7 -2
  319. package/templates/monorepo-starter/tsconfig.json +1 -1
  320. package/templates/monorepo-starter/turbo.json +1 -1
  321. package/templates/monorepo-starter/apps/web/eslint.config.mjs +0 -4
  322. package/templates/monorepo-starter/apps/web/src/app/(auth)/layout.tsx +0 -7
  323. package/templates/monorepo-starter/apps/web/src/app/globals.css +0 -1
  324. package/templates/monorepo-starter/apps/web/src/components/ThemeSwitch.tsx +0 -34
  325. package/templates/monorepo-starter/apps/web/src/providers/index.tsx +0 -10
  326. package/templates/monorepo-starter/apps/web/src/types/index.d.ts +0 -16
  327. package/templates/monorepo-starter/packages/config/eslint/base.js +0 -31
  328. package/templates/monorepo-starter/packages/config/eslint/nest.js +0 -26
  329. package/templates/monorepo-starter/packages/config/eslint/next.js +0 -39
  330. package/templates/monorepo-starter/packages/config/eslint/react.js +0 -30
  331. package/templates/monorepo-starter/packages/config/typescript/base.json +0 -31
  332. package/templates/monorepo-starter/packages/config/typescript/nest.json +0 -14
  333. package/templates/monorepo-starter/packages/config/typescript/next.json +0 -10
  334. package/templates/monorepo-starter/packages/ui/tsconfig.lint.json +0 -8
  335. package/templates/monorepo-starter/server/README.md +0 -63
  336. package/templates/monorepo-starter/server/scripts/generate.sh +0 -14
  337. package/templates/monorepo-starter/server/src/lib/dto/auth.dto.ts +0 -64
  338. package/templates/monorepo-starter/server/src/lib/dto/security-setting.dto.ts +0 -21
  339. package/templates/monorepo-starter/server/src/lib/pipes/validation.pipe.ts +0 -7
  340. package/templates/monorepo-starter/server/src/lib/templates/notification.templates.ts +0 -243
  341. package/templates/monorepo-starter/server/src/lib/utils/general.util.ts +0 -24
  342. package/templates/monorepo-starter/server/src/modules/auth/role.service.ts +0 -99
  343. package/templates/monorepo-starter/server/src/modules/auth/security-setting.service.ts +0 -102
  344. package/templates/monorepo-starter/server/src/modules/template/template.module.ts +0 -8
  345. package/templates/monorepo-starter/server/src/modules/template/template.service.ts +0 -33
  346. package/templates/monorepo-starter/server/src/types/index.d.ts +0 -13
  347. package/templates/monorepo-starter/server/test/app.e2e-spec.ts +0 -25
  348. package/templates/monorepo-starter/server/test/jest-e2e.json +0 -9
  349. /package/templates/monorepo-starter/{packages/ui/src/hooks → apps/dashboard/public}/.gitkeep +0 -0
@@ -1,84 +1,33 @@
1
1
  import {
2
2
  BadRequestException,
3
+ ForbiddenException,
3
4
  Injectable,
4
5
  UnauthorizedException,
5
6
  } from "@nestjs/common";
6
- import type { Request, Response } from "express";
7
7
  import argon2 from "argon2";
8
- import { PrismaService } from "@modules/prisma/prisma.service";
9
- import {
10
- ChangeIdentifierDto,
11
- RequestOtpDto,
12
- ResetPasswordDto,
13
- SignInDto,
14
- SignUpDto,
15
- ValidateOtpDto,
16
- } from "@dto/auth.dto";
17
- import { TokenService } from "@modules/token/token.service";
18
- import { Prisma, type User } from "@generated/prisma";
8
+ import type { Request, Response } from "express";
9
+ import type { UserStatus } from "@generated/prisma";
10
+
19
11
  import { OtpService } from "./otp.service";
20
- import { NotificationService } from "@modules/notification/notification.service";
21
- import { LoggerService } from "@modules/logger/logger.service";
22
- import { InjectLogger } from "@decorators/logger.decorator";
12
+ import { PrismaService } from "@/modules/prisma/prisma.service";
13
+ import { TokenService } from "@/modules/token/token.service";
14
+ import { NotificationService } from "@/modules/notification/notification.service";
23
15
 
24
16
  @Injectable()
25
17
  export class AuthService {
26
- @InjectLogger()
27
- private readonly logger!: LoggerService;
28
-
29
18
  constructor(
30
19
  private readonly prisma: PrismaService,
31
20
  private readonly tokenService: TokenService,
32
21
  private readonly otpService: OtpService,
33
- private readonly notifyService: NotificationService
22
+ private readonly notifyService: NotificationService,
34
23
  ) {}
35
24
 
36
25
  async signUp(dto: SignUpDto) {
37
- const { key, value, query } = this.parseIdentifier(dto.identifier);
38
- this.logger.log(`🔐 Sign-up attempt`, {
39
- identifier: dto.identifier,
40
- });
41
-
42
- const existingUser = await this.prisma.user.findUnique({
43
- where: query,
44
- });
45
-
46
- if (existingUser) {
47
- throw new BadRequestException(`${key} already in use.`);
26
+ if (!dto.password) {
27
+ throw new BadRequestException("Password should not be empty.");
48
28
  }
49
29
 
50
- const hashedPassword = await this.hashPassword(dto.password);
51
-
52
- const newUser = await this.prisma.user.create({
53
- data: {
54
- [key]: value,
55
- password: hashedPassword,
56
- firstName: dto.firstName,
57
- lastName: dto.lastName,
58
- displayName: `${dto.firstName} ${dto.lastName}`.trim(),
59
- username: dto.username,
60
- roles: { create: [{ role: "customer" }] },
61
- },
62
- });
63
-
64
- await this.otpService.sendOtp({
65
- userId: newUser.id,
66
- identifier: value,
67
- purpose: "verifyIdentifier",
68
- metadata: { user: newUser },
69
- });
70
-
71
- await this.notifyService.sendNotification({
72
- userId: newUser.id,
73
- purpose: "signup",
74
- to: value,
75
- metadata: { user: newUser },
76
- });
77
-
78
- this.logger.log(`✅ Sign-up success`, {
79
- userId: newUser.id,
80
- identifier: dto.identifier,
81
- });
30
+ const { key } = await this.createUser(dto, "customer");
82
31
 
83
32
  return {
84
33
  message: `User created successfully. Please verify your ${key}.`,
@@ -86,61 +35,55 @@ export class AuthService {
86
35
  }
87
36
 
88
37
  async signIn(dto: SignInDto, req: Request, res: Response) {
89
- const { user, key, value } = await this.findUserByIdentifier(
38
+ const { user, key, value, meta } = await this.findUserFail404(
90
39
  dto.identifier,
91
- {
92
- roles: true,
93
- securitySetting: true,
94
- }
95
40
  );
96
41
 
97
- this.logger.log(`🔐 Sign-in attempt`, {
98
- identifier: dto.identifier,
99
- });
100
-
101
- if (!user.password) {
42
+ if (!meta.password) {
102
43
  await this.otpService.sendOtp({
103
- userId: user.id,
104
- purpose: "setPassword",
44
+ purpose: "authSetPassword",
105
45
  identifier: value,
106
- type: "token",
107
- metadata: { user },
46
+ type: "secureToken",
47
+ meta: { user },
108
48
  });
109
49
 
110
50
  throw new UnauthorizedException(
111
- "Password not set. Please set your password to continue."
51
+ "Password not set. Please set your password to continue.",
112
52
  );
113
53
  }
114
54
 
55
+ if (!dto.password) {
56
+ throw new BadRequestException("Password should not be empty.");
57
+ }
58
+
115
59
  const isPasswordValid = await this.verifyPassword(
116
60
  dto.password,
117
- user.password
61
+ meta.password,
118
62
  );
119
63
 
120
64
  if (!isPasswordValid) {
121
65
  throw new UnauthorizedException("Invalid credentials");
122
66
  }
123
67
 
68
+ this.checkUserStatus(user.status);
124
69
  await this.checkVerificationStatus(user, key, value, "unverified");
125
70
 
126
- if (user.securitySetting?.isMfaEnabled) {
127
- const otp = await this.otpService.sendOtp({
128
- userId: user.id,
71
+ if (user.preferredMfa) {
72
+ await this.otpService.sendOtp({
129
73
  identifier: value,
130
- purpose: "verifyMfa",
131
- metadata: { user },
74
+ purpose: "authVerifyMfa",
75
+ meta: { user },
132
76
  });
133
77
  return {
134
78
  message: "MFA code sent. Please verify to complete login.",
135
- data: { secret: otp.secret },
79
+ action: "verifyMfa",
136
80
  };
137
81
  }
138
82
 
139
- const roles = user.roles.map((r) => r.role);
140
-
141
83
  await this.tokenService.createAuthSession(req, res, {
142
84
  id: user.id,
143
- roles: roles,
85
+ role: user.role,
86
+ status: user.status,
144
87
  });
145
88
 
146
89
  await this.prisma.user.update({
@@ -148,111 +91,114 @@ export class AuthService {
148
91
  data: { lastLoginAt: new Date() },
149
92
  });
150
93
 
151
- await this.notifyService.sendNotification({
152
- userId: user.id,
153
- purpose: "signin",
154
- to: value,
155
- metadata: { user },
156
- });
94
+ if (user.loginAlerts) {
95
+ await this.notifyService.sendNotification({
96
+ purpose: "authSignIn",
97
+ to: value,
98
+ meta: { user },
99
+ });
100
+ }
157
101
 
158
- this.logger.log(`✅ Sign-in success`, {
159
- userId: user.id,
160
- identifier: dto.identifier,
161
- });
162
102
  return {
163
103
  message: "Signed in successfully",
164
- data: { id: user.id, roles: roles },
165
104
  };
166
105
  }
167
106
 
168
- async signOut(req: Request, res: Response) {
169
- const refreshToken = req.cookies.refreshToken;
170
- const tokenId = req.cookies.tokenId;
171
-
172
- if (refreshToken && tokenId) {
173
- await this.prisma.refreshToken.update({
174
- where: { token: refreshToken, id: tokenId },
175
- data: { blacklisted: true },
176
- });
177
- }
178
-
179
- this.tokenService.clearAuthCookies(res);
180
- this.logger.log("🚪 User signed out", { tokenId });
181
-
107
+ async signOut(sessionId: string, res: Response) {
108
+ await this.tokenService.revokeSession(sessionId, res);
182
109
  return { message: "Signed out successfully" };
183
110
  }
184
111
 
185
112
  async requestOtp(dto: RequestOtpDto) {
186
- const { user, key, value } = await this.findUserByIdentifier(
113
+ const { user, key, value, meta } = await this.findUserFail404(
187
114
  dto.identifier,
188
- { securitySetting: true }
189
115
  );
190
116
 
191
- if (dto.purpose === "verifyIdentifier") {
192
- await this.checkVerificationStatus(user, key, value, "verified");
193
- await this.otpService.sendOtp({
194
- userId: user.id,
195
- identifier: value,
196
- purpose: dto.purpose,
197
- metadata: { user },
198
- });
199
- return { message: `Verification OTP sent.` };
200
- }
117
+ console.log("request received", dto.purpose);
201
118
 
202
- if (dto.purpose.includes("Password")) {
203
- if (dto.purpose === "setPassword" && user.password) {
204
- throw new BadRequestException(
205
- "Password already set. Use resetPassword."
206
- );
207
- } else if (dto.purpose === "resetPassword" && !user.password) {
208
- throw new BadRequestException("No password set. Use setPassword.");
209
- }
119
+ switch (dto.purpose) {
120
+ case "authVerifyIdentifier": {
121
+ await this.checkVerificationStatus(user, key, value, "verified");
210
122
 
211
- await this.otpService.sendOtp({
212
- userId: user.id,
213
- identifier: value,
214
- purpose: dto.purpose,
215
- metadata: { user },
216
- });
217
- return { message: `${dto.purpose} OTP sent.` };
218
- }
123
+ await this.otpService.sendOtp({
124
+ identifier: value,
125
+ purpose: dto.purpose,
126
+ meta: { user },
127
+ });
219
128
 
220
- if (dto.purpose === "changeIdentifier") {
221
- await this.otpService.sendOtp({
222
- userId: user.id,
223
- identifier: value,
224
- purpose: dto.purpose,
225
- metadata: { user },
226
- });
227
- return { message: `Change ${key} Otp Send` };
228
- }
129
+ return { message: "Verification OTP sent." };
130
+ }
229
131
 
230
- if (dto.purpose.includes("Mfa")) {
231
- if (dto.purpose === "enableMfa") {
232
- if (user.securitySetting?.isMfaEnabled) {
233
- throw new BadRequestException("MFA is already enabled.");
132
+ case "authSetPassword":
133
+ case "authResetPassword": {
134
+ if (dto.purpose === "authSetPassword" && meta.password) {
135
+ throw new BadRequestException(
136
+ "Password already set. Use resetPassword.",
137
+ );
234
138
  }
235
- } else if (dto.purpose === "disableMfa") {
236
- if (!user.securitySetting?.isMfaEnabled) {
139
+
140
+ await this.otpService.sendOtp({
141
+ identifier: value,
142
+ purpose: dto.purpose,
143
+ meta: { user },
144
+ });
145
+
146
+ return { message: `${dto.purpose} OTP sent.` };
147
+ }
148
+
149
+ case "authUpdateIdentifier": {
150
+ await this.otpService.sendOtp({
151
+ identifier: value,
152
+ purpose: dto.purpose,
153
+ meta: { user },
154
+ });
155
+
156
+ return { message: `Update ${key} OTP sent.` };
157
+ }
158
+
159
+ case "authUpdateMfa": {
160
+ await this.otpService.sendOtp({
161
+ identifier: value,
162
+ purpose: dto.purpose,
163
+ meta: { user },
164
+ });
165
+
166
+ return {
167
+ message: `Mfa ${user.preferredMfa ? "Change" : "Enable"} OTP sent.`,
168
+ };
169
+ }
170
+
171
+ case "authDisableMfa": {
172
+ if (!user.preferredMfa) {
237
173
  throw new BadRequestException("MFA is already disabled.");
238
174
  }
175
+
176
+ await this.otpService.sendOtp({
177
+ identifier: value,
178
+ purpose: dto.purpose,
179
+ meta: { user },
180
+ });
181
+
182
+ return { message: "disableMfa OTP sent." };
183
+ }
184
+
185
+ case "authVerifyMfa": {
186
+ await this.otpService.sendOtp({
187
+ identifier: value,
188
+ purpose: dto.purpose,
189
+ meta: { user },
190
+ });
191
+
192
+ return { message: "verifyMfa OTP sent." };
239
193
  }
240
- await this.otpService.sendOtp({
241
- userId: user.id,
242
- identifier: value,
243
- purpose: dto.purpose,
244
- metadata: { user },
245
- });
246
- return { message: `${dto.purpose} OTP sent.` };
247
- }
248
194
 
249
- throw new BadRequestException(`Invalid purpose: ${dto.purpose}`);
195
+ default:
196
+ throw new BadRequestException(`Invalid purpose: ${dto.purpose}`);
197
+ }
250
198
  }
251
199
 
252
200
  async validateOtp(dto: ValidateOtpDto, req: Request, res: Response) {
253
- const { key, value, user } = await this.findUserByIdentifier(
254
- dto.identifier
255
- );
201
+ const { key, value, user } = await this.findUserFail404(dto.identifier);
256
202
 
257
203
  await this.otpService.verifyOtp({
258
204
  userId: user.id,
@@ -261,102 +207,114 @@ export class AuthService {
261
207
  type: dto.type,
262
208
  });
263
209
 
264
- if (dto.purpose === "verifyIdentifier") {
265
- await this.prisma.user.update({
266
- where: { id: user.id },
267
- data:
268
- key === "email"
269
- ? { isEmailVerified: true }
270
- : { isPhoneVerified: true },
271
- });
210
+ switch (dto.purpose) {
211
+ case "authVerifyIdentifier": {
212
+ await this.prisma.user.update({
213
+ where: { id: user.id },
214
+ data:
215
+ key === "email"
216
+ ? { isEmailVerified: true }
217
+ : { isPhoneVerified: true },
218
+ });
272
219
 
273
- return { message: `${key} verified successfully.` };
274
- }
220
+ return { message: `${key} verified successfully.` };
221
+ }
275
222
 
276
- if (dto.purpose.includes("Password")) {
277
- const otp = await this.otpService.sendOtp({
278
- userId: user.id,
279
- identifier: value,
280
- purpose: dto.purpose,
281
- type: "token",
282
- notify: false,
283
- metadata: { user },
284
- });
285
- return {
286
- message: "OTP validated successfully.",
287
- data: { secret: otp.secret },
288
- };
289
- }
223
+ case "authSetPassword":
224
+ case "authResetPassword": {
225
+ const otp = await this.otpService.sendOtp({
226
+ identifier: value,
227
+ purpose: dto.purpose,
228
+ type: "secureToken",
229
+ notify: false,
230
+ meta: { user },
231
+ });
290
232
 
291
- if (dto.purpose === "changeIdentifier") {
292
- const otp = await this.otpService.sendOtp({
293
- userId: user.id,
294
- identifier: value,
295
- purpose: dto.purpose,
296
- type: "token",
297
- notify: false,
298
- metadata: { user },
299
- });
233
+ return {
234
+ message: "OTP validated successfully.",
235
+ meta: { secret: otp.secret },
236
+ };
237
+ }
300
238
 
301
- return {
302
- message: "OTP validated successfully.",
303
- data: { secret: otp.secret },
304
- };
305
- }
239
+ case "authUpdateIdentifier": {
240
+ const otp = await this.otpService.sendOtp({
241
+ identifier: value,
242
+ purpose: dto.purpose,
243
+ type: "secureToken",
244
+ notify: false,
245
+ meta: { user },
246
+ });
247
+
248
+ return {
249
+ message: "OTP validated successfully.",
250
+ meta: { secret: otp.secret },
251
+ };
252
+ }
253
+
254
+ case "authUpdateMfa": {
255
+ const otp = await this.otpService.sendOtp({
256
+ identifier: value,
257
+ purpose: dto.purpose,
258
+ type: "secureToken",
259
+ notify: false,
260
+ meta: { user },
261
+ });
306
262
 
307
- if (dto.purpose.includes("Mfa")) {
308
- //TODO add logic enable mfa
309
- if (dto.purpose === "enableMfa") {
310
- await this.prisma.securitySetting.update({
311
- where: { userId: user.id },
312
- data: { isMfaEnabled: true }, // TODO add mfa method details
263
+ return {
264
+ message: "OTP verified. Setup MFA details.",
265
+ meta: { secret: otp.secret },
266
+ };
267
+ }
268
+
269
+ case "authDisableMfa": {
270
+ await this.prisma.user.update({
271
+ where: { id: user.id },
272
+ data: { preferredMfa: null },
313
273
  });
314
- } else if (dto.purpose === "disableMfa") {
315
- await this.prisma.securitySetting.update({
316
- where: { userId: user.id },
317
- data: { isMfaEnabled: false },
274
+
275
+ await this.notifyService.sendNotification({
276
+ to: dto.identifier,
277
+ purpose: dto.purpose,
278
+ meta: { user },
318
279
  });
319
- } else if (dto.purpose === "verifyMfa") {
320
- const roles = user.roles.map((r) => r.role);
280
+
281
+ return { message: "disableMfa successfully." };
282
+ }
283
+
284
+ case "authVerifyMfa": {
285
+ this.checkUserStatus(user.status);
321
286
 
322
287
  await this.tokenService.createAuthSession(req, res, {
323
288
  id: user.id,
324
- roles: roles,
289
+ role: user.role,
290
+ status: user.status,
291
+ });
292
+
293
+ await this.prisma.user.update({
294
+ where: { id: user.id },
295
+ data: { lastLoginAt: new Date() },
325
296
  });
326
297
 
327
298
  return {
328
299
  message: "MFA verified. Signed in successfully.",
329
- data: { id: user.id, roles: roles },
330
300
  };
331
301
  }
332
302
 
333
- await this.notifyService.sendNotification({
334
- userId: user.id,
335
- to: dto.identifier,
336
- purpose: dto.purpose,
337
- metadata: { user },
338
- });
339
-
340
- return { message: `${dto.purpose} Successfully` };
303
+ default:
304
+ throw new BadRequestException(`Invalid purpose: ${dto.purpose}`);
341
305
  }
342
-
343
- throw new BadRequestException(`Invalid purpose: ${dto.purpose}`);
344
306
  }
345
307
 
346
308
  async resetPassword(dto: ResetPasswordDto) {
347
- const { user } = await this.findUserByIdentifier(dto.identifier);
309
+ const { user } = await this.findUserFail404(dto.identifier);
348
310
 
349
- const isTokenValid = await this.otpService.verifyOtp({
311
+ await this.otpService.verifyOtp({
350
312
  userId: user.id,
351
313
  purpose: dto.purpose,
352
314
  secret: dto.secret,
353
- type: "token",
315
+ type: "secureToken",
354
316
  });
355
317
 
356
- if (!isTokenValid) {
357
- throw new BadRequestException("Invalid Token");
358
- }
359
-
360
318
  const hashedPassword = await this.hashPassword(dto.newPassword);
361
319
 
362
320
  await this.prisma.user.update({
@@ -365,127 +323,215 @@ export class AuthService {
365
323
  });
366
324
 
367
325
  await this.notifyService.sendNotification({
368
- userId: user.id,
369
326
  to: dto.identifier,
370
327
  purpose: dto.purpose,
371
- metadata: { user },
328
+ meta: { user },
372
329
  });
373
330
 
374
- this.logger.log(`🔑 Password reset successful`, { userId: user.id });
375
-
376
- return { message: "Password reset successfully" };
331
+ return {
332
+ message: `Password ${dto.purpose.split("Password")[0]} successfully`,
333
+ };
377
334
  }
378
335
 
379
- async changeIdentifierReq(dto: ChangeIdentifierDto) {
380
- const { user } = await this.findUserByIdentifier(dto.identifier);
336
+ async updateMfa(dto: UpdateMfaDto) {
337
+ const { user } = await this.findUserFail404(dto.identifier);
381
338
 
382
- const {
383
- key: newKey,
384
- value: newValue,
385
- query: newQuery,
386
- } = this.parseIdentifier(dto.newIdentifier);
339
+ await this.otpService.verifyOtp({
340
+ userId: user.id,
341
+ purpose: dto.purpose,
342
+ secret: dto.secret,
343
+ type: "secureToken",
344
+ });
345
+
346
+ await this.prisma.user.update({
347
+ where: { id: user.id },
348
+ data: { preferredMfa: dto.preferredMfa },
349
+ });
387
350
 
388
- const existingUser = await this.prisma.user.findFirst({
389
- where: newQuery,
351
+ await this.notifyService.sendNotification({
352
+ to: dto.identifier,
353
+ purpose: dto.purpose,
354
+ meta: { user },
390
355
  });
391
356
 
392
- if (existingUser) {
393
- throw new BadRequestException(`${newKey} already in use.`);
394
- }
357
+ return {
358
+ message: `Mfa ${user.preferredMfa ? "Updated" : "Enabled"} successfully.`,
359
+ };
360
+ }
395
361
 
396
- const isTokenValid = await this.otpService.verifyOtp({
362
+ async requestUpdateIdentifier(dto: UpdateIdentifierDto) {
363
+ const { user, key, value } = await this.findUserFail404(dto.identifier);
364
+
365
+ const { key: newKey, value: newValue } = await this.findUserFail200(
366
+ dto.newIdentifier,
367
+ );
368
+
369
+ await this.otpService.verifyOtp({
397
370
  userId: user.id,
398
371
  purpose: dto.purpose,
399
372
  secret: dto.secret,
400
- type: "token",
373
+ type: "secureToken",
401
374
  });
402
375
 
403
- if (!isTokenValid) {
404
- throw new BadRequestException("Invalid Token");
405
- }
376
+ const isSameType = key === newKey;
377
+ const action = isSameType ? "change" : "update";
406
378
 
407
379
  await this.otpService.sendOtp({
408
- userId: user.id,
409
380
  identifier: newValue,
410
381
  purpose: dto.purpose,
411
- metadata: { user },
412
- });
413
-
414
- this.logger.log("🔄 Identifier change requested", {
415
- userId: user.id,
416
- newIdentifier: dto.newIdentifier,
382
+ type: "secureToken",
383
+ meta: { user, identifier: value, newIdentifier: newValue },
417
384
  });
418
385
 
419
386
  return {
420
- message: `Link sent to new ${newKey}. Please verify to complete the change.`,
387
+ message: `Link sent to new ${newKey}. Please verify to complete the ${action}.`,
421
388
  };
422
389
  }
423
390
 
424
- async changeIdentifier(dto: ChangeIdentifierDto) {
425
- const { user, key } = await this.findUserByIdentifier(dto.identifier);
391
+ async verifyUpdateIdentifier(dto: UpdateIdentifierDto) {
392
+ const { user } = await this.findUserFail404(dto.identifier);
426
393
 
427
- await this.otpService.verifyOtp({
394
+ const otp = await this.otpService.verifyOtp({
428
395
  userId: user.id,
429
396
  purpose: dto.purpose,
430
397
  secret: dto.secret,
431
- type: "token",
398
+ type: "secureToken",
432
399
  });
433
400
 
401
+ const newIdentifier = (otp.meta as Record<string, string>)?.newIdentifier;
402
+
403
+ if (!newIdentifier || newIdentifier !== dto.newIdentifier) {
404
+ throw new BadRequestException("Invalid identifier update token.");
405
+ }
406
+
407
+ const { key } = this.parseIdentifier(newIdentifier);
408
+
434
409
  await this.prisma.user.update({
435
410
  where: { id: user.id },
436
- data: { [key]: dto.newIdentifier },
411
+ data: {
412
+ [key]: newIdentifier,
413
+ },
437
414
  });
438
415
 
439
- await this.prisma.refreshToken.updateMany({
440
- where: { userId: user.id },
441
- data: { blacklisted: true },
416
+ await this.tokenService.revokeAllSessions(user);
417
+ await this.notifyService.sendNotification({
418
+ to: dto.identifier,
419
+ purpose: dto.purpose,
420
+ meta: {
421
+ user,
422
+ identifier: dto.identifier,
423
+ newIdentifier: dto.newIdentifier,
424
+ },
442
425
  });
443
426
 
444
427
  await this.notifyService.sendNotification({
445
- userId: user.id,
446
428
  to: dto.newIdentifier,
447
429
  purpose: dto.purpose,
448
- metadata: {
430
+ meta: {
449
431
  user,
450
432
  identifier: dto.identifier,
451
433
  newIdentifier: dto.newIdentifier,
452
434
  },
453
435
  });
454
436
 
455
- this.logger.log("✅ Identifier changed successfully", { userId: user.id });
456
-
457
437
  return { message: `${key} changed successfully.` };
458
438
  }
459
439
 
460
- private async hashPassword(password: string): Promise<string> {
440
+ async createUser(dto: SignUpDto, role: UserRole) {
441
+ const { key, value } = await this.findUserFail200(dto.identifier);
442
+
443
+ const hashedPassword = dto.password
444
+ ? await this.hashPassword(dto.password)
445
+ : undefined;
446
+
447
+ const user = await this.prisma.user.create({
448
+ data: {
449
+ [key]: value,
450
+ password: hashedPassword,
451
+ firstName: dto.firstName,
452
+ lastName: dto.lastName,
453
+ displayName: `${dto.firstName} ${dto.lastName}`.trim(),
454
+ role,
455
+ },
456
+ ...this.userView,
457
+ });
458
+
459
+ await this.notifyService.sendNotification({
460
+ purpose: "authSignUp",
461
+ to: value,
462
+ meta: { user },
463
+ });
464
+
465
+ await this.otpService.sendOtp({
466
+ identifier: value,
467
+ purpose: "authVerifyIdentifier",
468
+ meta: { user },
469
+ });
470
+
471
+ return { user, key };
472
+ }
473
+
474
+ async hashPassword(password: string): Promise<string> {
461
475
  return argon2.hash(password);
462
476
  }
463
477
 
464
478
  private async verifyPassword(
465
479
  password: string,
466
- hash: string
480
+ hash: string,
467
481
  ): Promise<boolean> {
468
482
  return argon2.verify(hash, password);
469
483
  }
470
484
 
471
- private async findUserByIdentifier(
472
- identifier: string,
473
- include: Prisma.UserInclude = {}
474
- ) {
475
- const { key, value, query } = this.parseIdentifier(identifier);
485
+ async findUserFail404(i: string) {
486
+ const { key, value, query } = this.parseIdentifier(i);
487
+
488
+ const user = await this.prisma.user.findUniqueOrThrow({
489
+ where: query,
490
+ });
491
+
492
+ if (!user) {
493
+ throw new BadRequestException("User Not Found");
494
+ }
495
+
496
+ const { password, ...reset } = user;
497
+ return {
498
+ key,
499
+ value,
500
+ user: reset,
501
+ meta: { password },
502
+ };
503
+ }
504
+
505
+ private findUserFail200 = async (i: string) => {
506
+ const { key, value, query } = this.parseIdentifier(i);
476
507
  const user = await this.prisma.user.findUnique({
477
508
  where: query,
478
- include,
479
509
  });
480
- if (!user) throw new BadRequestException("User not found");
481
- return { user, key, value };
510
+
511
+ if (user) {
512
+ throw new BadRequestException(`${key} already in use.`);
513
+ }
514
+
515
+ return { key, value };
516
+ };
517
+
518
+ checkUserStatus(status: UserStatus) {
519
+ if (status === "pending") {
520
+ throw new ForbiddenException(
521
+ "Your account is pending approval. Please contact support.",
522
+ );
523
+ } else if (status === "suspended") {
524
+ throw new ForbiddenException(
525
+ "Your account has been suspended. Contact support for assistance.",
526
+ );
527
+ }
482
528
  }
483
529
 
484
530
  private async checkVerificationStatus(
485
- user: User,
486
- key: IdentifierKey,
531
+ user: SafeUser,
532
+ key: IdentifierType,
487
533
  value: string,
488
- check: "verified" | "unverified"
534
+ check: "verified" | "unverified",
489
535
  ) {
490
536
  const isVerified =
491
537
  key === "email" ? user.isEmailVerified : user.isPhoneVerified;
@@ -496,30 +542,31 @@ export class AuthService {
496
542
 
497
543
  if (check === "unverified" && !isVerified) {
498
544
  await this.otpService.sendOtp({
499
- userId: user.id,
500
545
  identifier: value,
501
- purpose: "verifyIdentifier",
502
- metadata: { user },
546
+ purpose: "authVerifyIdentifier",
547
+ meta: { user },
503
548
  });
504
- this.logger.log(
505
- "📨 Auto-sent verification OTP due to unverified identifier",
506
- { userId: user.id, key, value }
507
- );
508
549
 
509
- throw new UnauthorizedException(`${key} not verified`);
550
+ throw new UnauthorizedException({
551
+ message: `${key} not verified`,
552
+ action: "verifyIdentifier",
553
+ });
510
554
  }
511
555
  }
512
556
 
513
- private parseIdentifier(identifier: string): {
514
- key: IdentifierKey;
515
- value: string;
516
- query: Prisma.UserWhereUniqueInput;
517
- } {
518
- const isEmail = identifier.includes("@");
519
- const key = isEmail ? "email" : "phone";
520
- const value = isEmail ? identifier.toLowerCase() : identifier;
557
+ parseIdentifier(i: string) {
558
+ const isEmail = i.includes("@");
559
+ const key: IdentifierType = isEmail ? "email" : "phone";
560
+ const value = isEmail ? i.toLowerCase() : i;
521
561
  const query = key === "email" ? { email: value } : { phone: value };
522
562
 
523
563
  return { key, value, query };
524
564
  }
565
+
566
+ userView = {
567
+ omit: { password: true },
568
+ include: {
569
+ image: { include: { uploadedBy: { omit: { password: true } } } },
570
+ },
571
+ };
525
572
  }