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
@@ -0,0 +1,61 @@
1
+ import "dotenv/config";
2
+ import { PrismaClient } from "../prisma/generated/client";
3
+ import { PrismaPg } from "@prisma/adapter-pg";
4
+ import argon2 from "argon2";
5
+
6
+ const connectionString = process.env.DB_URI;
7
+ const adapter = new PrismaPg({ connectionString });
8
+ const prisma = new PrismaClient({ adapter });
9
+
10
+ async function adminBootstrap() {
11
+ const { ADMIN_EMAIL, ADMIN_PASSWORD, ADMIN_NAME } = process.env;
12
+
13
+ if (process.env.NODE_ENV === "production") {
14
+ console.log("⚠️ Running in PRODUCTION");
15
+ }
16
+
17
+ if (!ADMIN_EMAIL || !ADMIN_PASSWORD || !ADMIN_NAME) {
18
+ throw new Error("Missing ADMIN env variables");
19
+ }
20
+
21
+ console.log("🚀 Checking for existing admin...");
22
+
23
+ const existingAdmin = await prisma.user.findFirst({
24
+ where: { role: "admin" },
25
+ });
26
+
27
+ if (existingAdmin) {
28
+ console.log("existingAdmin", existingAdmin.id);
29
+
30
+ throw new Error("❌ Admin already exists. Aborting.");
31
+ }
32
+
33
+ const email = ADMIN_EMAIL.toLowerCase();
34
+ const hashedPassword = await argon2.hash(ADMIN_PASSWORD);
35
+
36
+ const [firstName, ...rest] = ADMIN_NAME.trim().split(" ");
37
+ const lastName = rest.join(" ") || null;
38
+
39
+ const admin = await prisma.user.create({
40
+ data: {
41
+ email,
42
+ password: hashedPassword,
43
+ firstName: firstName!,
44
+ lastName,
45
+ displayName: ADMIN_NAME?.trim(),
46
+ isEmailVerified: true,
47
+ role: "admin",
48
+ status: "active",
49
+ },
50
+ });
51
+
52
+ console.log("🎉 First admin created");
53
+ console.log({ id: admin.id, email: admin.email });
54
+ }
55
+
56
+ adminBootstrap()
57
+ .catch((e) => {
58
+ console.error(e.message);
59
+ process.exit(1);
60
+ })
61
+ .finally(() => prisma.$disconnect());
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ # Exit if no arguments were provided
4
+ if [ "$#" -eq 0 ]; then
5
+ echo "❌ Please provide at least one module name"
6
+ echo "👉 Example: ./generate.sh payment booking"
7
+ exit 1
8
+ fi
9
+
10
+ # Loop through all provided arguments
11
+ for NAME in "$@"; do
12
+ echo "🚀 Generating NestJS module: $NAME"
13
+
14
+ nest g module "$NAME"
15
+ nest g service "$NAME"
16
+ nest g controller "$NAME"
17
+
18
+ echo "✅ $NAME generated successfully"
19
+ echo "-----------------------------"
20
+ done
@@ -0,0 +1,52 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const SCHEMA_PATH = path.resolve(__dirname, "../prisma/schema.prisma");
9
+ const OUTPUT_PATH = path.resolve(
10
+ __dirname,
11
+ "../src/modules/prisma/soft-delete.models.ts"
12
+ );
13
+
14
+ const schema = fs.readFileSync(SCHEMA_PATH, "utf8");
15
+
16
+ // Normalize line endings for consistency
17
+ const normalizedSchema = schema.replace(/\r\n/g, "\n");
18
+
19
+ // Match full model blocks (multiline-safe)
20
+ const modelRegex = /model\s+(\w+)\s*\{([\s\S]*?)^\s*\}/gm;
21
+
22
+ const softDeleteModels: string[] = [];
23
+ let totalModels = 0;
24
+
25
+ let match: RegExpExecArray | null;
26
+ while ((match = modelRegex.exec(normalizedSchema))) {
27
+ const modelName = match[1] as string;
28
+ const body = match[2] as string;
29
+ totalModels++;
30
+
31
+ const hasDeletedAt = /deletedAt\s+DateTime\?/i.test(body);
32
+
33
+ if (hasDeletedAt) {
34
+ softDeleteModels.push(modelName);
35
+ }
36
+ }
37
+
38
+ // Generate the output file
39
+ const output = `// AUTO-GENERATED FILE — DO NOT EDIT
40
+ // Generated from schema.prisma
41
+
42
+ export const SoftDeleteModels = new Set<string>([
43
+ ${softDeleteModels.map((m) => ` "${m}"`).join(",\n")}
44
+ ]);
45
+ `;
46
+
47
+ fs.writeFileSync(OUTPUT_PATH, output, "utf8");
48
+
49
+ console.log(
50
+ `✅ Generated soft delete models (${softDeleteModels.length}/${totalModels} models):`
51
+ );
52
+ console.log(softDeleteModels.join(" - "));
@@ -1,46 +1,53 @@
1
1
  import { Module } from "@nestjs/common";
2
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";
3
+ import { APP_GUARD, APP_PIPE } from "@nestjs/core";
4
+ import { ZodValidationPipe } from "nestjs-zod";
5
+
6
+ import { EnvModule } from "@/modules/env/env.module";
7
+ import { validateEnv } from "@/schemas/env.schema";
8
+ import { AuthGuard } from "@/guards/auth.guard";
9
+ import { AuthModule } from "@/modules/auth/auth.module";
10
+ import { TokenModule } from "@/modules/token/token.module";
11
+ import { PublicModule } from "@/modules/public/public.module";
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 { AllExceptionsFilter } from "@/filters/exceptions.filter";
17
+ import { ResponseInterceptor } from "@/interceptors/response.interceptor";
18
+ import { AdminModule } from "@/modules/admin/admin.module";
19
+ import { CacheModule } from "@/modules/cache/cache.module";
20
+ import { UserModule } from "@/modules/user/user.module";
21
+ import { MediaModule } from "@/modules/media/media.module";
19
22
 
20
23
  @Module({
21
24
  imports: [
25
+ ConfigModule.forRoot({
26
+ isGlobal: true,
27
+ validate: validateEnv,
28
+ }),
29
+ EnvModule,
30
+ CacheModule,
31
+ SchedulerModule,
22
32
  PublicModule,
23
33
  PrismaModule,
24
34
  LoggerModule,
25
- EnvModule,
35
+ NotificationModule,
26
36
  AuthModule,
27
37
  TokenModule,
28
- TemplateModule,
29
- NotificationModule,
30
- SchedulerModule,
31
- ScheduleModule.forRoot(),
32
- ConfigModule.forRoot({
33
- isGlobal: true,
34
- validate: validateEnv,
35
- }),
38
+ UserModule,
39
+ AdminModule,
40
+ MediaModule,
36
41
  ],
37
42
 
38
43
  providers: [
39
44
  {
40
45
  provide: APP_GUARD,
41
- useFactory: (tokenService, reflector) =>
42
- new AuthGuard(tokenService, reflector),
43
- inject: [TokenService, Reflector],
46
+ useClass: AuthGuard,
47
+ },
48
+ {
49
+ provide: APP_PIPE,
50
+ useClass: ZodValidationPipe,
44
51
  },
45
52
  AllExceptionsFilter,
46
53
  ResponseInterceptor,
@@ -0,0 +1,59 @@
1
+ /**
2
+ * IMPORTANT = user must see this even if it costs money (SMS / WhatsApp)
3
+ * Do NOT mark notifications important unless missing it causes
4
+ * loss of access, money, or travel.
5
+ */
6
+
7
+ export const NOTIFICATION_POLICY_MAP: Record<
8
+ NotificationPurpose,
9
+ { priority: NotificationPriority; push: boolean }
10
+ > = {
11
+ // AUTH
12
+ authSignUp: {
13
+ priority: "normal",
14
+ push: false,
15
+ },
16
+ authSignIn: {
17
+ priority: "normal",
18
+ push: false,
19
+ },
20
+
21
+ authSetPassword: {
22
+ priority: "important",
23
+ push: false,
24
+ },
25
+ authResetPassword: {
26
+ priority: "important",
27
+ push: false,
28
+ },
29
+ authVerifyIdentifier: {
30
+ priority: "important",
31
+ push: false,
32
+ },
33
+ authUpdateIdentifier: {
34
+ priority: "important",
35
+ push: false,
36
+ },
37
+ authUpdateMfa: {
38
+ priority: "important",
39
+ push: false,
40
+ },
41
+ authDisableMfa: {
42
+ priority: "important",
43
+ push: false,
44
+ },
45
+ authVerifyMfa: {
46
+ priority: "important",
47
+ push: false,
48
+ },
49
+
50
+ // ACCOUNT
51
+ accountSuspended: {
52
+ priority: "important",
53
+ push: true,
54
+ },
55
+ accountReactivated: {
56
+ priority: "important",
57
+ push: true,
58
+ },
59
+ };
@@ -0,0 +1,18 @@
1
+ import { createParamDecorator, type ExecutionContext } from "@nestjs/common";
2
+
3
+ export const BooleanQuery = createParamDecorator(
4
+ (key: string, ctx: ExecutionContext): boolean => {
5
+ const request = ctx.switchToHttp().getRequest();
6
+ const value = request.query[key];
7
+
8
+ if (value === undefined) return false;
9
+
10
+ if (typeof value === "boolean") return value;
11
+
12
+ if (typeof value === "string") {
13
+ return value.toLowerCase() === "true";
14
+ }
15
+
16
+ return false;
17
+ },
18
+ );
@@ -1,5 +1,5 @@
1
1
  import { Inject } from "@nestjs/common";
2
- import { LoggerService } from "@modules/logger/logger.service";
2
+ import { LoggerService } from "@/modules/logger/logger.service";
3
3
 
4
4
  export function InjectLogger(context?: string): PropertyDecorator {
5
5
  return (target: object, propertyKey: string | symbol) => {
@@ -0,0 +1,7 @@
1
+ import { applyDecorators, UseGuards } from "@nestjs/common";
2
+ import { OAuthGuard, type OAuthProvider } from "@/guards/oauth.guard";
3
+ import { Public } from "@/decorators/public.decorator";
4
+
5
+ export function UseOAuthGuard(provider: OAuthProvider) {
6
+ return applyDecorators(Public(), UseGuards(OAuthGuard(provider)));
7
+ }
@@ -0,0 +1,10 @@
1
+ import { createParamDecorator, type ExecutionContext } from "@nestjs/common";
2
+
3
+ export const User = createParamDecorator(
4
+ (data: keyof Express.User | undefined, ctx: ExecutionContext) => {
5
+ const req = ctx.switchToHttp().getRequest();
6
+ const user = req.user;
7
+
8
+ return data ? user?.[data] : user;
9
+ },
10
+ );
@@ -1,4 +1,5 @@
1
- import { LoggerService } from "@modules/logger/logger.service";
1
+ import { ZodError } from "zod";
2
+ import { ZodValidationException } from "nestjs-zod";
2
3
  import type { Request, Response } from "express";
3
4
  import {
4
5
  Catch,
@@ -7,7 +8,10 @@ import {
7
8
  type ExceptionFilter,
8
9
  type ArgumentsHost,
9
10
  } from "@nestjs/common";
10
- import { InjectLogger } from "@decorators/logger.decorator";
11
+ import { Prisma } from "@generated/prisma";
12
+
13
+ import { InjectLogger } from "@/decorators/logger.decorator";
14
+ import { LoggerService } from "@/modules/logger/logger.service";
11
15
 
12
16
  @Catch()
13
17
  export class AllExceptionsFilter implements ExceptionFilter {
@@ -19,44 +23,185 @@ export class AllExceptionsFilter implements ExceptionFilter {
19
23
  const req = ctx.getRequest<Request>();
20
24
  const res = ctx.getResponse<Response>();
21
25
 
22
- const status =
23
- exception instanceof HttpException
24
- ? exception.getStatus()
25
- : HttpStatus.INTERNAL_SERVER_ERROR;
26
-
27
- let message: any = "Internal server error";
26
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
27
+ let message = "Internal server error";
28
+ let action: string | undefined = undefined;
29
+ let errorCode: string | undefined = undefined;
30
+ let meta: Record<string, any> | undefined = undefined;
28
31
 
32
+ // ---------- NestJS HttpException ----------
29
33
  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") {
34
+ status = exception.getStatus();
35
+ const response = exception.getResponse();
36
+
37
+ if (typeof response === "object" && response !== null) {
38
+ const resObj = response as any;
39
+
40
+ if (Array.isArray(resObj.message)) {
41
+ message = resObj.message.join(", ");
42
+ } else if (typeof resObj.message === "string") {
43
+ message = resObj.message;
44
+ } else {
45
+ message = "Request failed";
46
+ }
47
+
48
+ action = resObj.action;
49
+ meta = resObj.meta;
50
+ errorCode = resObj.errorCode;
51
+ } else {
42
52
  message = exception.message;
43
53
  }
44
54
  }
45
- // if (response.headersSent) return;
46
55
 
47
- this.logger.error(`❌ Exception caught`, {
56
+ // -------- Prisma ClientKnownRequestError --------
57
+ else if (exception instanceof Prisma.PrismaClientKnownRequestError) {
58
+ switch (exception.code) {
59
+ case "P2002": {
60
+ status = 409;
61
+ const target = Array.isArray(exception.meta?.target)
62
+ ? exception.meta?.target.join(", ")
63
+ : String(exception.meta?.target ?? "");
64
+ message = `Duplicate entry${target ? `: ${target}` : ""}`;
65
+ break;
66
+ }
67
+ case "P2025": {
68
+ status = 404;
69
+
70
+ const model =
71
+ typeof exception.meta?.modelName === "string"
72
+ ? exception.meta.modelName
73
+ : "Resource";
74
+
75
+ message = `${model} not found`;
76
+ break;
77
+ }
78
+ case "P2003":
79
+ status = 400;
80
+ message = "Foreign key constraint failed";
81
+ break;
82
+ case "P2016":
83
+ status = 400;
84
+ message = "Query interpretation error";
85
+ break;
86
+ case "P2011":
87
+ status = 400;
88
+ message = "Null constraint violation";
89
+ break;
90
+ case "P2012":
91
+ status = 400;
92
+ message = "Missing required value";
93
+ break;
94
+ case "P2014":
95
+ status = 400;
96
+ message = "Invalid relation operation";
97
+ break;
98
+ case "P2001":
99
+ status = 404;
100
+ message = "Record does not exist";
101
+ break;
102
+ default:
103
+ status = 400;
104
+ message = "Database operation failed";
105
+ }
106
+ }
107
+
108
+ // -------- Prisma Validation Error --------
109
+ else if (exception instanceof Prisma.PrismaClientValidationError) {
110
+ status = 400;
111
+ const errorMessage = exception.message;
112
+
113
+ if (errorMessage.includes("Unknown argument")) {
114
+ const match = errorMessage.match(/Unknown argument `(\w+)`/);
115
+ if (match) {
116
+ message = `Invalid field: ${match[1]} is not a valid field for this operation`;
117
+ } else {
118
+ message = "Invalid data provided for database operation";
119
+ }
120
+ } else if (errorMessage.includes("Invalid value")) {
121
+ message = "Invalid data format provided";
122
+ } else {
123
+ message = "Database validation failed";
124
+ }
125
+ }
126
+
127
+ // -------- Prisma Unknown Request Error --------
128
+ else if (exception instanceof Prisma.PrismaClientUnknownRequestError) {
129
+ status = 400;
130
+ message = "Database request error";
131
+
132
+ if (process.env.NODE_ENV === "development") {
133
+ meta = { detail: exception.message };
134
+ }
135
+ }
136
+
137
+ // -------- Prisma Initialization Error --------
138
+ else if (exception instanceof Prisma.PrismaClientInitializationError) {
139
+ status = 500;
140
+ message = "Database connection failed";
141
+
142
+ if (process.env.NODE_ENV === "development") {
143
+ meta = { detail: exception.message };
144
+ }
145
+ }
146
+
147
+ // -------- Prisma Rust Panic Error --------
148
+ else if (exception instanceof Prisma.PrismaClientRustPanicError) {
149
+ status = 500;
150
+ message = "Database engine error";
151
+
152
+ if (process.env.NODE_ENV === "development") {
153
+ meta = { detail: exception.message };
154
+ }
155
+ }
156
+
157
+ // ---------- Zod validation errors ----------
158
+ else if (
159
+ exception instanceof ZodValidationException ||
160
+ exception instanceof ZodError
161
+ ) {
162
+ status = HttpStatus.BAD_REQUEST;
163
+
164
+ const zodError = (
165
+ exception instanceof ZodValidationException
166
+ ? exception.getZodError()
167
+ : exception
168
+ ) as ZodError;
169
+
170
+ meta = zodError.issues.map((issue) => ({
171
+ field: issue.path.join("."),
172
+ message: issue.message,
173
+ errorCode: issue.code,
174
+ }));
175
+
176
+ console.log("meta", meta);
177
+
178
+ message = "Validation failed";
179
+ }
180
+
181
+ // ---------- Other runtime errors ----------
182
+ else if (exception instanceof Error) {
183
+ message = exception.message || message;
184
+ }
185
+
186
+ // ---------- Logging ----------
187
+ this.logger.error("❌ Exception caught", {
48
188
  message: exception.message,
49
189
  stack: exception.stack,
50
190
  status,
51
191
  path: req.url,
52
192
  method: req.method,
193
+ errorCode,
194
+ meta,
53
195
  });
54
196
 
197
+ // ---------- Response ----------
55
198
  res.status(status).json({
56
199
  status,
57
- data: null,
58
- message,
59
200
  success: false,
201
+ message,
202
+ action,
203
+ meta,
204
+ errorCode,
60
205
  });
61
206
  }
62
207
  }
@@ -1,13 +1,19 @@
1
- import { Injectable, UnauthorizedException } from "@nestjs/common";
2
- import type { CanActivate, ExecutionContext } from "@nestjs/common";
3
1
  import { Reflector } from "@nestjs/core";
4
2
  import type { Request, Response } from "express";
5
- import { IS_PUBLIC_KEY } from "@decorators/public.decorator";
6
- import { ROLES_KEY } from "@decorators/roles.decorator";
3
+ import {
4
+ Injectable,
5
+ ForbiddenException,
6
+ UnauthorizedException,
7
+ type CanActivate,
8
+ type ExecutionContext,
9
+ } from "@nestjs/common";
7
10
  import type { UserRole } from "@generated/prisma";
8
- import { TokenService } from "@modules/token/token.service";
9
- import { LoggerService } from "@modules/logger/logger.service";
10
- import { InjectLogger } from "@decorators/logger.decorator";
11
+
12
+ import { ROLES_KEY } from "@/decorators/roles.decorator";
13
+ import { InjectLogger } from "@/decorators/logger.decorator";
14
+ import { IS_PUBLIC_KEY } from "@/decorators/public.decorator";
15
+ import { TokenService } from "@/modules/token/token.service";
16
+ import { LoggerService } from "@/modules/logger/logger.service";
11
17
 
12
18
  @Injectable()
13
19
  export class AuthGuard implements CanActivate {
@@ -16,7 +22,7 @@ export class AuthGuard implements CanActivate {
16
22
 
17
23
  constructor(
18
24
  private readonly tokenService: TokenService,
19
- private readonly reflector: Reflector
25
+ private readonly reflector: Reflector,
20
26
  ) {}
21
27
 
22
28
  async canActivate(context: ExecutionContext): Promise<boolean> {
@@ -29,76 +35,34 @@ export class AuthGuard implements CanActivate {
29
35
 
30
36
  const requiredRoles = this.reflector.getAllAndOverride<UserRole[]>(
31
37
  ROLES_KEY,
32
- [context.getHandler(), context.getClass()]
38
+ [context.getHandler(), context.getClass()],
33
39
  );
34
40
 
35
41
  const ctx = context.switchToHttp();
36
42
  const req = ctx.getRequest<Request>();
37
43
  const res = ctx.getResponse<Response>();
38
44
 
39
- const accessToken = req.cookies["accessToken"];
40
- const refreshToken = req.cookies["refreshToken"];
41
-
42
45
  try {
43
- if (!accessToken) {
44
- this.logger.warn(`Access token missing}`, {
45
- route: req.url,
46
- });
47
- throw new Error("Access token is missing or expired");
48
- }
49
- const decoded = await this.tokenService.verifyToken(
50
- accessToken,
51
- "access"
52
- );
53
- if (!decoded) throw new UnauthorizedException("Invalid Access Token");
54
- this.checkRoles(decoded.roles, requiredRoles);
55
- this.tokenService.attachDecodedUser(req, decoded);
56
- this.logger.debug(`✅ User Attached - Auth success`, {
57
- userId: decoded.sub,
58
- });
46
+ const payload = await this.tokenService.verifyToken(req, "accessToken");
47
+ this.checkRoles(payload.rol, requiredRoles);
48
+ this.tokenService.attachAuthContext(req, payload);
59
49
  return true;
60
- } catch (err) {
61
- if (!refreshToken)
62
- throw new UnauthorizedException("Refresh token is missing");
63
-
64
- try {
65
- const decoded = await this.tokenService.verifyToken(
66
- refreshToken,
67
- "refresh"
68
- );
69
- if (!decoded) throw new Error("Invalid or expire Refresh Token");
70
- this.logger.debug(`Refresh token verified`, {
71
- userId: decoded.sub,
72
- });
73
- this.checkRoles(decoded.roles, requiredRoles);
74
- await this.tokenService.refreshTokens(req, res, decoded);
75
-
76
- if (!req["user"]) throw new UnauthorizedException("User not found");
50
+ } catch (err: any) {
51
+ const code = err?.response?.errorCode ?? err?.errorCode;
52
+ if (code !== "access_token_missing") throw err;
77
53
 
78
- this.logger.log(`✅ Auth success`, {
79
- userId: req.user?.id,
80
- method: req.method,
81
- });
82
-
83
- return true;
84
- } catch {
85
- this.logger.warn(`Access denied: Missing or invalid token`);
86
-
87
- throw new UnauthorizedException(
88
- "Unauthorized: Invalid or expired token."
89
- );
90
- }
54
+ const payload = await this.tokenService.verifyToken(req, "refreshToken");
55
+ this.checkRoles(payload.rol, requiredRoles);
56
+ await this.tokenService.refreshTokens(req, res, payload);
57
+ if (!req["user"]) throw new UnauthorizedException("User not found");
58
+ return true;
91
59
  }
92
60
  }
93
61
 
94
- private checkRoles(userRoles: UserRole[], requiredRoles?: UserRole[]) {
62
+ private checkRoles(userRole: UserRole, requiredRoles?: UserRole[]) {
95
63
  if (requiredRoles?.length) {
96
- const hasRole = userRoles.some((role) => requiredRoles.includes(role));
97
- if (!hasRole) {
98
- throw new UnauthorizedException(
99
- `Forbidden: Requires ${requiredRoles.join(", ")} access.`
100
- );
101
- }
64
+ const hasRole = requiredRoles.some((role) => role === userRole);
65
+ if (!hasRole) throw new ForbiddenException({ errorCode: "forbidden" });
102
66
  }
103
67
  }
104
68
  }