create-ej-app 5.0.0 → 7.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 (364) hide show
  1. package/cli.js +88 -10
  2. package/cli.ts +100 -12
  3. package/package.json +2 -2
  4. package/template/base/.env.example +11 -15
  5. package/template/base/Dockerfile +15 -0
  6. package/template/base/components.json +10 -5
  7. package/template/base/drizzle.config.ts +4 -5
  8. package/template/{new → base}/eslint.config.mjs +6 -1
  9. package/template/base/package.json +58 -75
  10. package/template/base/postcss.config.mjs +1 -4
  11. package/template/base/src/app/(admin)/layout.tsx +20 -0
  12. package/template/base/src/app/(app)/layout.tsx +13 -118
  13. package/template/base/src/app/(auth)/login/page.tsx +65 -42
  14. package/template/base/src/app/(marketing)/layout.tsx +7 -35
  15. package/template/base/src/app/(marketing)/page.tsx +2 -96
  16. package/template/base/src/app/(marketing)/privacy/page.tsx +3 -0
  17. package/template/base/src/app/(marketing)/terms/page.tsx +3 -0
  18. package/template/base/src/app/globals.css +118 -66
  19. package/template/base/src/app/layout.tsx +27 -12
  20. package/template/base/src/app/not-found.tsx +27 -0
  21. package/template/base/src/components/site-footer.tsx +16 -24
  22. package/template/base/src/components/ui/button.tsx +31 -27
  23. package/template/base/src/components/ui/card.tsx +83 -70
  24. package/template/base/src/components/ui/form.tsx +38 -47
  25. package/template/base/src/components/ui/input.tsx +16 -20
  26. package/template/base/src/components/ui/label.tsx +16 -18
  27. package/template/base/src/components/ui/separator.tsx +11 -14
  28. package/template/base/src/components/ui/sonner.tsx +22 -13
  29. package/template/base/src/db/index.ts +4 -11
  30. package/template/base/src/db/schema.ts +60 -268
  31. package/template/{new → base}/src/env.ts +2 -2
  32. package/template/{new → base}/src/lib/auth-client.ts +2 -0
  33. package/template/base/src/lib/auth.ts +64 -80
  34. package/template/base/src/lib/safe-action.ts +30 -6
  35. package/template/base/tsconfig.json +9 -7
  36. package/template/{prisma-advanced → old}/.env.example +10 -9
  37. package/template/{prisma → old}/components.json +1 -1
  38. package/template/old/drizzle.config.ts +11 -0
  39. package/template/old/package.json +87 -0
  40. package/template/old/postcss.config.mjs +8 -0
  41. package/template/{prisma-advanced → old}/src/app/(app)/layout.tsx +11 -3
  42. package/template/{prisma-advanced → old}/src/app/(auth)/login/page.tsx +2 -2
  43. package/template/{prisma → old}/src/app/(marketing)/page.tsx +8 -7
  44. package/template/old/src/app/layout.tsx +31 -0
  45. package/template/old/src/components/ui/sonner.tsx +31 -0
  46. package/template/old/src/db/index.ts +14 -0
  47. package/template/old/src/db/schema.ts +287 -0
  48. package/template/old/src/lib/auth.ts +89 -0
  49. package/template/old/src/lib/safe-action.ts +19 -0
  50. package/template/{prisma-advanced → old}/tsconfig.json +3 -2
  51. package/template/new/.env.example +0 -13
  52. package/template/new/components.json +0 -22
  53. package/template/new/drizzle.config.ts +0 -10
  54. package/template/new/package.json +0 -81
  55. package/template/new/postcss.config.mjs +0 -5
  56. package/template/new/src/app/(auth)/login/page.tsx +0 -72
  57. package/template/new/src/app/globals.css +0 -128
  58. package/template/new/src/app/layout.tsx +0 -50
  59. package/template/new/src/app/page.tsx +0 -3
  60. package/template/new/src/components/ui/button.tsx +0 -60
  61. package/template/new/src/components/ui/card.tsx +0 -92
  62. package/template/new/src/components/ui/form.tsx +0 -167
  63. package/template/new/src/components/ui/input.tsx +0 -21
  64. package/template/new/src/components/ui/label.tsx +0 -24
  65. package/template/new/src/components/ui/separator.tsx +0 -28
  66. package/template/new/src/components/ui/sonner.tsx +0 -40
  67. package/template/new/src/db/index.ts +0 -7
  68. package/template/new/src/db/schema.ts +0 -74
  69. package/template/new/src/lib/auth.ts +0 -67
  70. package/template/new/src/lib/safe-action.ts +0 -15
  71. package/template/new/tsconfig.json +0 -49
  72. package/template/prisma/.env.example +0 -16
  73. package/template/prisma/.eslintrc.cjs +0 -24
  74. package/template/prisma/next.config.mjs +0 -15
  75. package/template/prisma/package.json +0 -80
  76. package/template/prisma/postcss.config.cjs +0 -6
  77. package/template/prisma/prettier.config.ts +0 -11
  78. package/template/prisma/prisma/schema.prisma +0 -115
  79. package/template/prisma/public/next.svg +0 -1
  80. package/template/prisma/src/app/(app)/dashboard/page.tsx +0 -19
  81. package/template/prisma/src/app/(app)/layout.tsx +0 -90
  82. package/template/prisma/src/app/(app)/settings/actions.ts +0 -18
  83. package/template/prisma/src/app/(app)/settings/loading.tsx +0 -28
  84. package/template/prisma/src/app/(app)/settings/page.tsx +0 -28
  85. package/template/prisma/src/app/(app)/settings/render.tsx +0 -113
  86. package/template/prisma/src/app/(app)/tasks/[id]/actions.ts +0 -147
  87. package/template/prisma/src/app/(app)/tasks/[id]/page.tsx +0 -25
  88. package/template/prisma/src/app/(app)/tasks/[id]/render.tsx +0 -413
  89. package/template/prisma/src/app/(app)/tasks/actions.ts +0 -69
  90. package/template/prisma/src/app/(app)/tasks/page.tsx +0 -53
  91. package/template/prisma/src/app/(app)/tasks/render.tsx +0 -107
  92. package/template/prisma/src/app/(app)/users/[id]/actions.ts +0 -83
  93. package/template/prisma/src/app/(app)/users/[id]/page.tsx +0 -32
  94. package/template/prisma/src/app/(app)/users/[id]/render.tsx +0 -240
  95. package/template/prisma/src/app/(app)/users/actions.ts +0 -55
  96. package/template/prisma/src/app/(app)/users/page.tsx +0 -49
  97. package/template/prisma/src/app/(app)/users/render.tsx +0 -80
  98. package/template/prisma/src/app/(auth)/layout.tsx +0 -20
  99. package/template/prisma/src/app/(auth)/login/page.tsx +0 -49
  100. package/template/prisma/src/app/(auth)/login/user-auth-form.tsx +0 -152
  101. package/template/prisma/src/app/api/auth/[...nextauth]/route.ts +0 -7
  102. package/template/prisma/src/app/api/upload/route.ts +0 -36
  103. package/template/prisma/src/app/favicon.ico +0 -0
  104. package/template/prisma/src/app/layout.tsx +0 -34
  105. package/template/prisma/src/components/app-nav-command.tsx +0 -160
  106. package/template/prisma/src/components/dark-mode-toggle.tsx +0 -43
  107. package/template/prisma/src/components/data-table/data-table-column-header.tsx +0 -91
  108. package/template/prisma/src/components/data-table/data-table-faceted-filter.tsx +0 -149
  109. package/template/prisma/src/components/data-table/data-table-loading.tsx +0 -77
  110. package/template/prisma/src/components/data-table/data-table-pagination.tsx +0 -94
  111. package/template/prisma/src/components/data-table/data-table-toolbar.tsx +0 -78
  112. package/template/prisma/src/components/data-table/data-table-types.ts +0 -17
  113. package/template/prisma/src/components/data-table/data-table-view-options.tsx +0 -59
  114. package/template/prisma/src/components/data-table/data-table.tsx +0 -301
  115. package/template/prisma/src/components/data-table/simple-data-table.tsx +0 -109
  116. package/template/prisma/src/components/empty-placeholder.tsx +0 -78
  117. package/template/prisma/src/components/heading.tsx +0 -17
  118. package/template/prisma/src/components/icons.tsx +0 -85
  119. package/template/prisma/src/components/main-nav.tsx +0 -64
  120. package/template/prisma/src/components/mobile-nav.tsx +0 -50
  121. package/template/prisma/src/components/shell.tsx +0 -13
  122. package/template/prisma/src/components/side-nav.tsx +0 -51
  123. package/template/prisma/src/components/site-footer.tsx +0 -31
  124. package/template/prisma/src/components/system-info.tsx +0 -36
  125. package/template/prisma/src/components/theme-provider.tsx +0 -13
  126. package/template/prisma/src/components/ui/alert-dialog.tsx +0 -141
  127. package/template/prisma/src/components/ui/avatar.tsx +0 -50
  128. package/template/prisma/src/components/ui/badge.tsx +0 -36
  129. package/template/prisma/src/components/ui/button.tsx +0 -56
  130. package/template/prisma/src/components/ui/card.tsx +0 -79
  131. package/template/prisma/src/components/ui/checkbox.tsx +0 -30
  132. package/template/prisma/src/components/ui/command.tsx +0 -155
  133. package/template/prisma/src/components/ui/dialog.tsx +0 -122
  134. package/template/prisma/src/components/ui/dropdown-menu.tsx +0 -200
  135. package/template/prisma/src/components/ui/form.tsx +0 -176
  136. package/template/prisma/src/components/ui/input.tsx +0 -25
  137. package/template/prisma/src/components/ui/label.tsx +0 -26
  138. package/template/prisma/src/components/ui/popover.tsx +0 -31
  139. package/template/prisma/src/components/ui/select.tsx +0 -160
  140. package/template/prisma/src/components/ui/separator.tsx +0 -31
  141. package/template/prisma/src/components/ui/skeleton.tsx +0 -15
  142. package/template/prisma/src/components/ui/table.tsx +0 -117
  143. package/template/prisma/src/components/ui/textarea.tsx +0 -24
  144. package/template/prisma/src/components/ui/toast.tsx +0 -127
  145. package/template/prisma/src/components/ui/toaster.tsx +0 -35
  146. package/template/prisma/src/components/ui/use-toast.ts +0 -189
  147. package/template/prisma/src/components/user-account-nav.tsx +0 -60
  148. package/template/prisma/src/components/user-avatar.tsx +0 -25
  149. package/template/prisma/src/env.mjs +0 -78
  150. package/template/prisma/src/hooks/use-debounce.ts +0 -17
  151. package/template/prisma/src/hooks/use-lock-body.tsx +0 -12
  152. package/template/prisma/src/lib/auth.ts +0 -80
  153. package/template/prisma/src/lib/cn.ts +0 -4
  154. package/template/prisma/src/lib/db.ts +0 -20
  155. package/template/prisma/src/lib/delay.ts +0 -2
  156. package/template/prisma/src/lib/formatDate.ts +0 -11
  157. package/template/prisma/src/lib/generateLabel.ts +0 -5
  158. package/template/prisma/src/lib/siteConfig.ts +0 -4
  159. package/template/prisma/src/lib/storageClient.ts +0 -11
  160. package/template/prisma/src/styles/editor.css +0 -61
  161. package/template/prisma/src/styles/globals.css +0 -76
  162. package/template/prisma/src/types/UnwrapPromise.ts +0 -1
  163. package/template/prisma/tailwind.config.ts +0 -80
  164. package/template/prisma/tsconfig.json +0 -46
  165. package/template/prisma-advanced/.eslintrc.cjs +0 -24
  166. package/template/prisma-advanced/components.json +0 -17
  167. package/template/prisma-advanced/next.config.mjs +0 -15
  168. package/template/prisma-advanced/package.json +0 -80
  169. package/template/prisma-advanced/postcss.config.cjs +0 -6
  170. package/template/prisma-advanced/prettier.config.ts +0 -11
  171. package/template/prisma-advanced/prisma/schema.prisma +0 -154
  172. package/template/prisma-advanced/public/next.svg +0 -1
  173. package/template/prisma-advanced/src/app/(app)/dashboard/page.tsx +0 -19
  174. package/template/prisma-advanced/src/app/(app)/roles/[id]/actions.ts +0 -100
  175. package/template/prisma-advanced/src/app/(app)/roles/[id]/page.tsx +0 -36
  176. package/template/prisma-advanced/src/app/(app)/roles/[id]/render.tsx +0 -266
  177. package/template/prisma-advanced/src/app/(app)/roles/actions.ts +0 -67
  178. package/template/prisma-advanced/src/app/(app)/roles/page.tsx +0 -51
  179. package/template/prisma-advanced/src/app/(app)/roles/render.tsx +0 -70
  180. package/template/prisma-advanced/src/app/(app)/settings/actions.ts +0 -18
  181. package/template/prisma-advanced/src/app/(app)/settings/loading.tsx +0 -28
  182. package/template/prisma-advanced/src/app/(app)/settings/page.tsx +0 -28
  183. package/template/prisma-advanced/src/app/(app)/settings/render.tsx +0 -113
  184. package/template/prisma-advanced/src/app/(app)/tasks/[id]/actions.ts +0 -147
  185. package/template/prisma-advanced/src/app/(app)/tasks/[id]/page.tsx +0 -55
  186. package/template/prisma-advanced/src/app/(app)/tasks/[id]/render.tsx +0 -418
  187. package/template/prisma-advanced/src/app/(app)/tasks/actions.ts +0 -69
  188. package/template/prisma-advanced/src/app/(app)/tasks/page.tsx +0 -63
  189. package/template/prisma-advanced/src/app/(app)/tasks/render.tsx +0 -114
  190. package/template/prisma-advanced/src/app/(app)/users/[id]/actions.ts +0 -104
  191. package/template/prisma-advanced/src/app/(app)/users/[id]/page.tsx +0 -39
  192. package/template/prisma-advanced/src/app/(app)/users/[id]/render.tsx +0 -320
  193. package/template/prisma-advanced/src/app/(app)/users/actions.ts +0 -67
  194. package/template/prisma-advanced/src/app/(app)/users/page.tsx +0 -61
  195. package/template/prisma-advanced/src/app/(app)/users/render.tsx +0 -108
  196. package/template/prisma-advanced/src/app/(auth)/layout.tsx +0 -20
  197. package/template/prisma-advanced/src/app/(auth)/login/user-auth-form.tsx +0 -152
  198. package/template/prisma-advanced/src/app/(marketing)/layout.tsx +0 -42
  199. package/template/prisma-advanced/src/app/(marketing)/page.tsx +0 -96
  200. package/template/prisma-advanced/src/app/api/auth/[...nextauth]/route.ts +0 -7
  201. package/template/prisma-advanced/src/app/api/upload/route.ts +0 -36
  202. package/template/prisma-advanced/src/app/favicon.ico +0 -0
  203. package/template/prisma-advanced/src/app/layout.tsx +0 -34
  204. package/template/prisma-advanced/src/components/app-nav-command.tsx +0 -160
  205. package/template/prisma-advanced/src/components/dark-mode-toggle.tsx +0 -43
  206. package/template/prisma-advanced/src/components/data-table/data-table-column-header.tsx +0 -91
  207. package/template/prisma-advanced/src/components/data-table/data-table-faceted-filter.tsx +0 -149
  208. package/template/prisma-advanced/src/components/data-table/data-table-loading.tsx +0 -77
  209. package/template/prisma-advanced/src/components/data-table/data-table-pagination.tsx +0 -94
  210. package/template/prisma-advanced/src/components/data-table/data-table-toolbar.tsx +0 -78
  211. package/template/prisma-advanced/src/components/data-table/data-table-types.ts +0 -17
  212. package/template/prisma-advanced/src/components/data-table/data-table-view-options.tsx +0 -59
  213. package/template/prisma-advanced/src/components/data-table/data-table.tsx +0 -301
  214. package/template/prisma-advanced/src/components/data-table/simple-data-table.tsx +0 -109
  215. package/template/prisma-advanced/src/components/empty-placeholder.tsx +0 -78
  216. package/template/prisma-advanced/src/components/heading.tsx +0 -17
  217. package/template/prisma-advanced/src/components/icons.tsx +0 -85
  218. package/template/prisma-advanced/src/components/main-nav.tsx +0 -64
  219. package/template/prisma-advanced/src/components/shell.tsx +0 -13
  220. package/template/prisma-advanced/src/components/side-nav.tsx +0 -51
  221. package/template/prisma-advanced/src/components/system-info.tsx +0 -36
  222. package/template/prisma-advanced/src/components/theme-provider.tsx +0 -13
  223. package/template/prisma-advanced/src/components/ui/alert-dialog.tsx +0 -141
  224. package/template/prisma-advanced/src/components/ui/avatar.tsx +0 -50
  225. package/template/prisma-advanced/src/components/ui/badge.tsx +0 -36
  226. package/template/prisma-advanced/src/components/ui/checkbox.tsx +0 -30
  227. package/template/prisma-advanced/src/components/ui/command.tsx +0 -155
  228. package/template/prisma-advanced/src/components/ui/dialog.tsx +0 -122
  229. package/template/prisma-advanced/src/components/ui/dropdown-menu.tsx +0 -200
  230. package/template/prisma-advanced/src/components/ui/popover.tsx +0 -31
  231. package/template/prisma-advanced/src/components/ui/select.tsx +0 -160
  232. package/template/prisma-advanced/src/components/ui/skeleton.tsx +0 -15
  233. package/template/prisma-advanced/src/components/ui/table.tsx +0 -117
  234. package/template/prisma-advanced/src/components/ui/textarea.tsx +0 -24
  235. package/template/prisma-advanced/src/components/ui/toast.tsx +0 -127
  236. package/template/prisma-advanced/src/components/ui/toaster.tsx +0 -35
  237. package/template/prisma-advanced/src/components/ui/use-toast.ts +0 -189
  238. package/template/prisma-advanced/src/components/user-account-nav.tsx +0 -60
  239. package/template/prisma-advanced/src/components/user-avatar.tsx +0 -25
  240. package/template/prisma-advanced/src/env.mjs +0 -78
  241. package/template/prisma-advanced/src/hooks/use-debounce.ts +0 -17
  242. package/template/prisma-advanced/src/hooks/use-lock-body.tsx +0 -12
  243. package/template/prisma-advanced/src/lib/auth.ts +0 -180
  244. package/template/prisma-advanced/src/lib/cn.ts +0 -4
  245. package/template/prisma-advanced/src/lib/db.ts +0 -20
  246. package/template/prisma-advanced/src/lib/delay.ts +0 -2
  247. package/template/prisma-advanced/src/lib/formatDate.ts +0 -11
  248. package/template/prisma-advanced/src/lib/generateLabel.ts +0 -5
  249. package/template/prisma-advanced/src/lib/siteConfig.ts +0 -4
  250. package/template/prisma-advanced/src/lib/storageClient.ts +0 -11
  251. package/template/prisma-advanced/src/styles/editor.css +0 -61
  252. package/template/prisma-advanced/src/types/UnwrapPromise.ts +0 -1
  253. package/template/prisma-advanced/tailwind.config.ts +0 -80
  254. package/template/{new → base}/next.config.ts +0 -0
  255. package/template/{new → base}/public/placeholder.svg +0 -0
  256. package/template/{new → base}/src/app/(auth)/login/email-login-form.tsx +0 -0
  257. package/template/{new → base}/src/app/(auth)/login/social-login-buttons.tsx +0 -0
  258. package/template/{new → base}/src/app/api/auth/[...all]/route.ts +0 -0
  259. package/template/{new → base}/src/components/loader.tsx +0 -0
  260. package/template/{new → base}/src/components/react-query-provider.tsx +0 -0
  261. package/template/{new → base}/src/components/screen-size.tsx +0 -0
  262. package/template/{new → base}/src/components/theme-toggle.tsx +0 -0
  263. package/template/{new → base}/src/lib/toast-message.ts +0 -0
  264. package/template/{base → old}/.eslintrc.cjs +0 -0
  265. package/template/{base → old}/next.config.mjs +0 -0
  266. package/template/{new → old}/prettier.config.mjs +0 -0
  267. package/template/{base → old}/public/next.svg +0 -0
  268. package/template/{base → old}/public/vercel.svg +0 -0
  269. package/template/{base → old}/src/app/(app)/dashboard/layout.tsx +0 -0
  270. package/template/{base → old}/src/app/(app)/dashboard/page.tsx +0 -0
  271. package/template/{base → old}/src/app/(app)/roles/[id]/actions.ts +0 -0
  272. package/template/{base → old}/src/app/(app)/roles/[id]/page.tsx +0 -0
  273. package/template/{base → old}/src/app/(app)/roles/[id]/render.tsx +0 -0
  274. package/template/{base → old}/src/app/(app)/roles/[id]/validation.ts +0 -0
  275. package/template/{base → old}/src/app/(app)/roles/actions.ts +0 -0
  276. package/template/{base → old}/src/app/(app)/roles/page.tsx +0 -0
  277. package/template/{base → old}/src/app/(app)/roles/render.tsx +0 -0
  278. package/template/{base → old}/src/app/(app)/settings/actions.ts +0 -0
  279. package/template/{base → old}/src/app/(app)/settings/layout.tsx +0 -0
  280. package/template/{base → old}/src/app/(app)/settings/loading.tsx +0 -0
  281. package/template/{base → old}/src/app/(app)/settings/page.tsx +0 -0
  282. package/template/{base → old}/src/app/(app)/settings/render.tsx +0 -0
  283. package/template/{base → old}/src/app/(app)/settings/validation.ts +0 -0
  284. package/template/{base → old}/src/app/(app)/tasks/[id]/actions.ts +0 -0
  285. package/template/{base → old}/src/app/(app)/tasks/[id]/page.tsx +0 -0
  286. package/template/{base → old}/src/app/(app)/tasks/[id]/render.tsx +0 -0
  287. package/template/{base → old}/src/app/(app)/tasks/[id]/validation.ts +0 -0
  288. package/template/{base → old}/src/app/(app)/tasks/actions.ts +0 -0
  289. package/template/{base → old}/src/app/(app)/tasks/page.tsx +0 -0
  290. package/template/{base → old}/src/app/(app)/tasks/render.tsx +0 -0
  291. package/template/{base → old}/src/app/(app)/tasks/statusEnum.ts +0 -0
  292. package/template/{base → old}/src/app/(app)/users/[id]/actions.ts +0 -0
  293. package/template/{base → old}/src/app/(app)/users/[id]/page.tsx +0 -0
  294. package/template/{base → old}/src/app/(app)/users/[id]/render.tsx +0 -0
  295. package/template/{base → old}/src/app/(app)/users/[id]/validation.ts +0 -0
  296. package/template/{base → old}/src/app/(app)/users/actions.ts +0 -0
  297. package/template/{base → old}/src/app/(app)/users/page.tsx +0 -0
  298. package/template/{base → old}/src/app/(app)/users/render.tsx +0 -0
  299. package/template/{base → old}/src/app/(auth)/layout.tsx +0 -0
  300. package/template/{base → old}/src/app/(auth)/login/render.tsx +0 -0
  301. package/template/{prisma → old}/src/app/(marketing)/layout.tsx +1 -1
  302. /package/template/{base → old}/src/app/api/auth/[...nextauth]/route.ts +0 -0
  303. /package/template/{new → old}/src/app/favicon.ico +0 -0
  304. /package/template/{prisma-advanced/src/styles → old/src/app}/globals.css +0 -0
  305. /package/template/{base → old}/src/components/app-nav-command.tsx +0 -0
  306. /package/template/{base → old}/src/components/dark-mode-toggle.tsx +0 -0
  307. /package/template/{base → old}/src/components/data-table/data-table-column-header.tsx +0 -0
  308. /package/template/{base → old}/src/components/data-table/data-table-faceted-filter.tsx +0 -0
  309. /package/template/{base → old}/src/components/data-table/data-table-loading.tsx +0 -0
  310. /package/template/{base → old}/src/components/data-table/data-table-pagination.tsx +0 -0
  311. /package/template/{base → old}/src/components/data-table/data-table-toolbar.tsx +0 -0
  312. /package/template/{base → old}/src/components/data-table/data-table-types.ts +0 -0
  313. /package/template/{base → old}/src/components/data-table/data-table-view-options.tsx +0 -0
  314. /package/template/{base → old}/src/components/data-table/data-table.tsx +0 -0
  315. /package/template/{base → old}/src/components/data-table/simple-data-table.tsx +0 -0
  316. /package/template/{base → old}/src/components/empty-placeholder.tsx +0 -0
  317. /package/template/{base → old}/src/components/heading.tsx +0 -0
  318. /package/template/{base → old}/src/components/icons.tsx +0 -0
  319. /package/template/{base → old}/src/components/main-nav.tsx +0 -0
  320. /package/template/{base → old}/src/components/mobile-nav.tsx +0 -0
  321. /package/template/{base → old}/src/components/shell.tsx +0 -0
  322. /package/template/{base → old}/src/components/side-nav.tsx +0 -0
  323. /package/template/{prisma-advanced → old}/src/components/site-footer.tsx +0 -0
  324. /package/template/{base → old}/src/components/system-info.tsx +0 -0
  325. /package/template/{base → old}/src/components/ui/alert-dialog.tsx +0 -0
  326. /package/template/{base → old}/src/components/ui/avatar.tsx +0 -0
  327. /package/template/{base → old}/src/components/ui/badge.tsx +0 -0
  328. /package/template/{prisma-advanced → old}/src/components/ui/button.tsx +0 -0
  329. /package/template/{prisma-advanced → old}/src/components/ui/card.tsx +0 -0
  330. /package/template/{base → old}/src/components/ui/checkbox.tsx +0 -0
  331. /package/template/{base → old}/src/components/ui/command.tsx +0 -0
  332. /package/template/{base → old}/src/components/ui/dialog.tsx +0 -0
  333. /package/template/{base → old}/src/components/ui/dropdown-menu.tsx +0 -0
  334. /package/template/{prisma-advanced → old}/src/components/ui/form.tsx +0 -0
  335. /package/template/{prisma-advanced → old}/src/components/ui/input.tsx +0 -0
  336. /package/template/{prisma-advanced → old}/src/components/ui/label.tsx +0 -0
  337. /package/template/{base → old}/src/components/ui/popover.tsx +0 -0
  338. /package/template/{base → old}/src/components/ui/select.tsx +0 -0
  339. /package/template/{prisma-advanced → old}/src/components/ui/separator.tsx +0 -0
  340. /package/template/{base → old}/src/components/ui/skeleton.tsx +0 -0
  341. /package/template/{base → old}/src/components/ui/table.tsx +0 -0
  342. /package/template/{base → old}/src/components/user-account-nav.tsx +0 -0
  343. /package/template/{base → old}/src/components/user-avatar.tsx +0 -0
  344. /package/template/{base → old}/src/data-access/resource.ts +0 -0
  345. /package/template/{base → old}/src/data-access/role-permission.ts +0 -0
  346. /package/template/{base → old}/src/data-access/role.ts +0 -0
  347. /package/template/{base → old}/src/data-access/task.ts +0 -0
  348. /package/template/{base → old}/src/data-access/user.ts +0 -0
  349. /package/template/{base → old}/src/env.mjs +0 -0
  350. /package/template/{base → old}/src/hooks/use-debounce.tsx +0 -0
  351. /package/template/{base → old}/src/hooks/use-lock-body.tsx +0 -0
  352. /package/template/{new → old}/src/lib/cn.ts +0 -0
  353. /package/template/{base → old}/src/lib/currentUser.ts +0 -0
  354. /package/template/{new → old}/src/lib/delay.ts +0 -0
  355. /package/template/{base → old}/src/lib/formatDate.ts +0 -0
  356. /package/template/{base → old}/src/lib/generateLabel.ts +0 -0
  357. /package/template/{base → old}/src/lib/s3Client.ts +0 -0
  358. /package/template/{new → old}/src/lib/siteConfig.ts +0 -0
  359. /package/template/{base → old}/src/lib/sortOrderEnum.ts +0 -0
  360. /package/template/{base → old}/src/use-case/role-permission.ts +0 -0
  361. /package/template/{base → old}/src/use-case/role.ts +0 -0
  362. /package/template/{base → old}/src/use-case/task.ts +0 -0
  363. /package/template/{base → old}/src/use-case/user.ts +0 -0
  364. /package/template/{base → old}/tailwind.config.ts +0 -0
@@ -5,7 +5,7 @@
5
5
  "tsx": true,
6
6
  "tailwind": {
7
7
  "config": "tailwind.config.ts",
8
- "css": "src/styles/globals.css",
8
+ "css": "src/app/globals.css",
9
9
  "baseColor": "neutral",
10
10
  "cssVariables": true,
11
11
  "prefix": ""
@@ -0,0 +1,11 @@
1
+ import { type Config } from 'drizzle-kit'
2
+
3
+ import { env } from '@/env.mjs'
4
+
5
+ export default {
6
+ schema: './src/db/schema.ts',
7
+ dialect: 'postgresql',
8
+ dbCredentials: {
9
+ url: env.DATABASE_URL
10
+ }
11
+ } satisfies Config
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "my-app",
3
+ "version": "1.0.0",
4
+ "description": "A Legendary Next.js App",
5
+ "private": true,
6
+ "scripts": {
7
+ "format": "prettier --write \"src/**/*.{js,cjs,mjs,jsx,ts,tsx}\"",
8
+ "lint": "next lint",
9
+ "dev": "next dev",
10
+ "build": "next build",
11
+ "start": "next start",
12
+ "db:generate": "drizzle-kit generate",
13
+ "db:push": "drizzle-kit push",
14
+ "db:studio": "drizzle-kit studio"
15
+ },
16
+ "dependencies": {
17
+ "@auth/drizzle-adapter": "^1.4.1",
18
+ "@aws-sdk/client-s3": "^3.600.0",
19
+ "@aws-sdk/s3-presigned-post": "^3.600.0",
20
+ "@editorjs/checklist": "^1.6.0",
21
+ "@editorjs/code": "^2.9.0",
22
+ "@editorjs/delimiter": "^1.4.0",
23
+ "@editorjs/editorjs": "^2.29.1",
24
+ "@editorjs/header": "^2.8.1",
25
+ "@editorjs/image": "^2.9.0",
26
+ "@editorjs/inline-code": "^1.5.0",
27
+ "@editorjs/list": "^1.9.0",
28
+ "@editorjs/nested-list": "^1.4.2",
29
+ "@editorjs/quote": "^2.6.0",
30
+ "@editorjs/table": "^2.3.0",
31
+ "@hookform/resolvers": "^3.6.0",
32
+ "@paralleldrive/cuid2": "^2.2.2",
33
+ "@radix-ui/react-alert-dialog": "^1.1.1",
34
+ "@radix-ui/react-avatar": "^1.1.0",
35
+ "@radix-ui/react-checkbox": "^1.1.0",
36
+ "@radix-ui/react-dialog": "^1.1.1",
37
+ "@radix-ui/react-dropdown-menu": "^2.1.1",
38
+ "@radix-ui/react-label": "^2.1.0",
39
+ "@radix-ui/react-popover": "^1.1.1",
40
+ "@radix-ui/react-select": "^2.1.1",
41
+ "@radix-ui/react-separator": "^1.1.0",
42
+ "@radix-ui/react-slot": "^1.1.0",
43
+ "@t3-oss/env-nextjs": "^0.10.1",
44
+ "@tailwindcss/typography": "^0.5.13",
45
+ "@tanstack/react-table": "^8.17.3",
46
+ "@types/eslint": "^8.56.10",
47
+ "@types/node": "^20.14.9",
48
+ "@types/nodemailer": "^6.4.15",
49
+ "@types/react": "^18.3.3",
50
+ "@types/react-dom": "^18.3.0",
51
+ "@typescript-eslint/eslint-plugin": "^7.14.1",
52
+ "@typescript-eslint/parser": "^7.14.1",
53
+ "class-variance-authority": "^0.7.0",
54
+ "clsx": "^2.1.1",
55
+ "cmdk": "^1.0.0",
56
+ "dayjs": "^1.11.11",
57
+ "drizzle-kit": "^0.22.7",
58
+ "drizzle-orm": "^0.31.2",
59
+ "editorjs-html": "^3.4.3",
60
+ "eslint": "^8.57.0",
61
+ "eslint-config-next": "^14.2.4",
62
+ "eslint-plugin-drizzle": "^0.2.3",
63
+ "geist": "^1.3.0",
64
+ "lucide-react": "^0.397.0",
65
+ "next": "^14.2.4",
66
+ "next-auth": "^4.24.7",
67
+ "next-safe-action": "^7.1.3",
68
+ "next-themes": "^0.3.0",
69
+ "nextjs-toploader": "^1.6.12",
70
+ "nodemailer": "^6.9.14",
71
+ "postcss": "^8.4.38",
72
+ "postcss-load-config": "^6.0.1",
73
+ "postgres": "^3.4.4",
74
+ "prettier": "^3.3.2",
75
+ "prettier-plugin-organize-imports": "^3.2.4",
76
+ "prettier-plugin-tailwindcss": "^0.6.5",
77
+ "react": "^18.3.1",
78
+ "react-dom": "^18.3.1",
79
+ "react-hook-form": "^7.52.0",
80
+ "sonner": "^1.5.0",
81
+ "tailwind-merge": "^2.3.0",
82
+ "tailwindcss": "^3.4.4",
83
+ "tailwindcss-animate": "^1.0.7",
84
+ "typescript": "^5.5.2",
85
+ "zod": "^3.23.8"
86
+ }
87
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {}
5
+ }
6
+ }
7
+
8
+ export default config
@@ -6,7 +6,8 @@ import { MainNav } from '@/components/main-nav'
6
6
  import { SideNav } from '@/components/side-nav'
7
7
  import { SiteFooter } from '@/components/site-footer'
8
8
  import { UserAccountNav } from '@/components/user-account-nav'
9
- import { checkForPermission, getAuthSession } from '@/lib/auth'
9
+ import { getAuthSession } from '@/lib/auth'
10
+ import { checkForRolePermissionUseCase } from '@/use-case/role-permission'
10
11
 
11
12
  const Layout = async ({ children }: { children: ReactNode }) => {
12
13
  const session = await getAuthSession()
@@ -14,7 +15,13 @@ const Layout = async ({ children }: { children: ReactNode }) => {
14
15
  if (!session?.user) return notFound()
15
16
 
16
17
  const isRoot = session.user.type === 'Root'
17
- const [isTask] = await Promise.all([checkForPermission('TaskList', session)])
18
+ const [isTask] = await Promise.all([
19
+ checkForRolePermissionUseCase(
20
+ session.user.type,
21
+ session.user.roleId,
22
+ 'TaskList'
23
+ )
24
+ ])
18
25
 
19
26
  return (
20
27
  <div className="flex min-h-screen flex-col space-y-6">
@@ -52,7 +59,8 @@ const Layout = async ({ children }: { children: ReactNode }) => {
52
59
  <div className="flex items-center justify-center space-x-4">
53
60
  <AppNavCommand
54
61
  modules={{
55
- task: true
62
+ task: true,
63
+ root: session.user.type === 'Root'
56
64
  }}
57
65
  />
58
66
  <UserAccountNav
@@ -2,7 +2,7 @@ import Link from 'next/link'
2
2
 
3
3
  import { Icons } from '@/components/icons'
4
4
  import { Button } from '@/components/ui/button'
5
- import { UserAuthForm } from './user-auth-form'
5
+ import { Render } from './render'
6
6
 
7
7
  const LoginPage = () => {
8
8
  return (
@@ -23,7 +23,7 @@ const LoginPage = () => {
23
23
  Enter your email to sign in to your account
24
24
  </p>
25
25
  </div>
26
- <UserAuthForm />
26
+ <Render />
27
27
  <p className="px-8 text-center text-sm text-muted-foreground">
28
28
  By clicking continue, you agree to our{' '}
29
29
  <Link
@@ -13,9 +13,10 @@ const links = [
13
13
  'Learn about TypeScript and how it helps you write better JavaScript.'
14
14
  },
15
15
  {
16
- href: 'https://prisma.io',
17
- title: 'Prisma',
18
- description: 'Prisma is a next-generation ORM for Node.js and TypeScript.'
16
+ href: 'https://orm.drizzle.team',
17
+ title: 'Drizzle',
18
+ description:
19
+ 'Lightweight and performant TypeScript ORM with developer experience in mind.'
19
20
  },
20
21
  {
21
22
  href: 'https://next-auth.js.org',
@@ -50,14 +51,14 @@ const links = [
50
51
  const Home = () => {
51
52
  return (
52
53
  <div className="flex min-h-[80vh] flex-col items-center justify-between">
53
- <div className="max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
54
- <p className="flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
54
+ <div className="w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
55
+ <p className="flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
55
56
  Get started by editing&nbsp;
56
57
  <code className="font-mono font-bold">src/app/page.tsx</code>
57
58
  </p>
58
59
  </div>
59
60
 
60
- <div className="mt-12 flex place-items-center before:absolute before:h-[300px] before:w-[240px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[120px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
61
+ <div className="before:bg-gradient-radial after:bg-gradient-conic z-[-1] mt-12 flex place-items-center before:absolute before:h-[300px] before:w-[240px] before:-translate-x-1/2 before:rounded-full before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[120px] after:translate-x-1/3 after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px]">
61
62
  <Image
62
63
  className="dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
63
64
  src="/next.svg"
@@ -68,7 +69,7 @@ const Home = () => {
68
69
  />
69
70
  </div>
70
71
 
71
- <div className="my-8 grid text-center lg:max-w-5xl lg:w-full lg:grid-cols-4 lg:text-left gap-2">
72
+ <div className="my-8 grid gap-2 text-center lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
72
73
  {links.map((l, i) => (
73
74
  <a
74
75
  key={i}
@@ -0,0 +1,31 @@
1
+ import { GeistSans } from 'geist/font/sans'
2
+ import type { Metadata } from 'next'
3
+ import { ThemeProvider } from 'next-themes'
4
+ import NextTopLoader from 'nextjs-toploader'
5
+
6
+ import { Toaster } from '@/components/ui/sonner'
7
+ import { siteConfig } from '@/lib/siteConfig'
8
+ import './globals.css'
9
+
10
+ export const metadata: Metadata = {
11
+ title: siteConfig.name,
12
+ description: siteConfig.description
13
+ }
14
+
15
+ export default function RootLayout({
16
+ children
17
+ }: Readonly<{
18
+ children: React.ReactNode
19
+ }>) {
20
+ return (
21
+ <html lang="en" className={GeistSans.className} suppressHydrationWarning>
22
+ <body className="min-h-screen bg-background font-sans antialiased">
23
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
24
+ <NextTopLoader color="#777" />
25
+ {children}
26
+ <Toaster />
27
+ </ThemeProvider>
28
+ </body>
29
+ </html>
30
+ )
31
+ }
@@ -0,0 +1,31 @@
1
+ 'use client'
2
+
3
+ import { useTheme } from 'next-themes'
4
+ import { Toaster as Sonner } from 'sonner'
5
+
6
+ type ToasterProps = React.ComponentProps<typeof Sonner>
7
+
8
+ const Toaster = ({ ...props }: ToasterProps) => {
9
+ const { theme = 'system' } = useTheme()
10
+
11
+ return (
12
+ <Sonner
13
+ theme={theme as ToasterProps['theme']}
14
+ className="toaster group"
15
+ toastOptions={{
16
+ classNames: {
17
+ toast:
18
+ 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
19
+ description: 'group-[.toast]:text-muted-foreground',
20
+ actionButton:
21
+ 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
22
+ cancelButton:
23
+ 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground'
24
+ }
25
+ }}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ export { Toaster }
@@ -0,0 +1,14 @@
1
+ import { drizzle } from 'drizzle-orm/postgres-js'
2
+ import postgres from 'postgres'
3
+
4
+ import { env } from '@/env.mjs'
5
+ import * as schema from './schema'
6
+
7
+ const globalForDb = globalThis as unknown as {
8
+ conn: postgres.Sql | undefined
9
+ }
10
+
11
+ const conn = globalForDb.conn ?? postgres(env.DATABASE_URL)
12
+ if (env.NODE_ENV !== 'production') globalForDb.conn = conn
13
+
14
+ export const db = drizzle(conn, { schema })
@@ -0,0 +1,287 @@
1
+ import { createId } from '@paralleldrive/cuid2'
2
+ import { relations, sql } from 'drizzle-orm'
3
+ import {
4
+ bigint,
5
+ boolean,
6
+ index,
7
+ integer,
8
+ pgEnum,
9
+ pgTable,
10
+ primaryKey,
11
+ text,
12
+ timestamp,
13
+ unique,
14
+ varchar
15
+ } from 'drizzle-orm/pg-core'
16
+
17
+ export const Account = pgTable(
18
+ 'Accounts',
19
+ {
20
+ id: text('id')
21
+ .notNull()
22
+ .primaryKey()
23
+ .$defaultFn(() => createId()),
24
+ userId: text('userId')
25
+ .notNull()
26
+ .references(() => User.id),
27
+ type: varchar('type', { length: 255 }).notNull(),
28
+ provider: varchar('provider', { length: 255 }).notNull(),
29
+ providerAccountId: varchar('providerAccountId', { length: 255 }).notNull(),
30
+ refresh_token: text('refresh_token'),
31
+ access_token: text('access_token'),
32
+ expires_at: integer('expires_at'),
33
+ token_type: varchar('token_type', { length: 255 }),
34
+ scope: varchar('scope', { length: 255 }),
35
+ id_token: text('id_token'),
36
+ session_state: varchar('session_state', { length: 255 })
37
+ },
38
+ account => ({
39
+ userIdIdx: index('Accounts_idx_userId').on(account.userId),
40
+ uniqueProviderAccount: unique('Accounts_uq_provider_providerAccountId').on(
41
+ account.provider,
42
+ account.providerAccountId
43
+ )
44
+ })
45
+ )
46
+
47
+ export const AccountRelations = relations(Account, ({ one }) => ({
48
+ user: one(User, { fields: [Account.userId], references: [User.id] })
49
+ }))
50
+
51
+ export const Session = pgTable(
52
+ 'Sessions',
53
+ {
54
+ id: text('id')
55
+ .notNull()
56
+ .primaryKey()
57
+ .$defaultFn(() => createId()),
58
+ sessionToken: varchar('sessionToken', { length: 255 }).notNull().unique(),
59
+ userId: text('userId')
60
+ .notNull()
61
+ .references(() => User.id),
62
+ expires: timestamp('expires', {
63
+ mode: 'date',
64
+ withTimezone: true
65
+ }).notNull(),
66
+ createdAt: timestamp('createdAt', {
67
+ mode: 'date',
68
+ withTimezone: true
69
+ })
70
+ .default(sql`CURRENT_TIMESTAMP`)
71
+ .notNull(),
72
+ updatedAt: timestamp('updatedAt', {
73
+ mode: 'date',
74
+ withTimezone: true
75
+ })
76
+ },
77
+ session => ({
78
+ userIdIdx: index('Sessions_idx_userId').on(session.userId)
79
+ })
80
+ )
81
+
82
+ export const SessionRelations = relations(Session, ({ one }) => ({
83
+ user: one(User, { fields: [Session.userId], references: [User.id] })
84
+ }))
85
+
86
+ export const Role = pgTable('Roles', {
87
+ id: text('id')
88
+ .notNull()
89
+ .primaryKey()
90
+ .$defaultFn(() => createId()),
91
+ name: varchar('name', { length: 255 }).notNull().unique(),
92
+ createdAt: timestamp('createdAt', {
93
+ mode: 'date',
94
+ withTimezone: true
95
+ })
96
+ .default(sql`CURRENT_TIMESTAMP`)
97
+ .notNull(),
98
+ updatedAt: timestamp('updatedAt', {
99
+ mode: 'date',
100
+ withTimezone: true
101
+ })
102
+ })
103
+
104
+ export const RoleRelations = relations(Role, ({ many }) => ({
105
+ permissions: many(RolePermission),
106
+ users: many(User)
107
+ }))
108
+
109
+ export const PermissionEnum = pgEnum('PermissionEnum', [
110
+ 'TaskList',
111
+ 'TaskView',
112
+ 'TaskCreate',
113
+ 'TaskUpdate',
114
+ 'TaskDelete'
115
+ ])
116
+
117
+ export const RolePermission = pgTable(
118
+ 'RolePermissions',
119
+ {
120
+ id: text('id')
121
+ .notNull()
122
+ .primaryKey()
123
+ .$defaultFn(() => createId()),
124
+ roleId: text('roleId')
125
+ .notNull()
126
+ .references(() => Role.id, {
127
+ onDelete: 'cascade'
128
+ }),
129
+ permission: PermissionEnum('permission').notNull()
130
+ },
131
+ rp => ({
132
+ roleIdIdx: index('RolePermissions_idx_roleId').on(rp.roleId),
133
+ uniqueRolePermission: unique('RolePermissions_uq_roleId_permission').on(
134
+ rp.roleId,
135
+ rp.permission
136
+ )
137
+ })
138
+ )
139
+
140
+ export const RolePermissionRelations = relations(RolePermission, ({ one }) => ({
141
+ role: one(Role, { fields: [RolePermission.roleId], references: [Role.id] })
142
+ }))
143
+
144
+ export const UserTypeEnum = pgEnum('UserTypeEnum', ['Root', 'Normal'])
145
+
146
+ export const User = pgTable('Users', {
147
+ id: text('id')
148
+ .notNull()
149
+ .primaryKey()
150
+ .$defaultFn(() => createId()),
151
+ name: varchar('name', { length: 255 }),
152
+ email: varchar('email', { length: 255 }).notNull().unique(),
153
+ emailVerified: timestamp('emailVerified', {
154
+ mode: 'date',
155
+ withTimezone: true
156
+ }),
157
+ image: varchar('image', { length: 255 }),
158
+ type: UserTypeEnum('type').notNull().default('Normal'),
159
+ roleId: text('roleId').references(() => Role.id),
160
+ createdAt: timestamp('createdAt', {
161
+ mode: 'date',
162
+ withTimezone: true
163
+ })
164
+ .default(sql`CURRENT_TIMESTAMP`)
165
+ .notNull(),
166
+ updatedAt: timestamp('updatedAt', {
167
+ mode: 'date',
168
+ withTimezone: true
169
+ })
170
+ })
171
+
172
+ export const UserRelations = relations(User, ({ many, one }) => ({
173
+ accounts: many(Account),
174
+ sessions: many(Session),
175
+ role: one(Role, {
176
+ fields: [User.roleId],
177
+ references: [Role.id]
178
+ })
179
+ }))
180
+
181
+ export const VerificationToken = pgTable(
182
+ 'VerificationTokens',
183
+ {
184
+ identifier: varchar('identifier', { length: 255 }).notNull(),
185
+ token: varchar('token', { length: 255 }).notNull(),
186
+ expires: timestamp('expires', {
187
+ mode: 'date',
188
+ withTimezone: true
189
+ }).notNull()
190
+ },
191
+ vt => ({
192
+ pk: primaryKey({ columns: [vt.identifier, vt.token] })
193
+ })
194
+ )
195
+
196
+ export const TaskStatusEnum = pgEnum('TaskStatusEnum', [
197
+ 'Backlog',
198
+ 'Todo',
199
+ 'In Progress',
200
+ 'Done',
201
+ 'Cancelled'
202
+ ])
203
+
204
+ export const Task = pgTable('Tasks', {
205
+ id: text('id')
206
+ .notNull()
207
+ .primaryKey()
208
+ .$defaultFn(() => createId()),
209
+ title: varchar('title', { length: 255 }).notNull(),
210
+ status: TaskStatusEnum('status').notNull().default('Todo'),
211
+ description: text('description'),
212
+ createdAt: timestamp('createdAt', {
213
+ mode: 'date',
214
+ withTimezone: true
215
+ })
216
+ .default(sql`CURRENT_TIMESTAMP`)
217
+ .notNull(),
218
+ updatedAt: timestamp('updatedAt', {
219
+ mode: 'date',
220
+ withTimezone: true
221
+ }),
222
+ createdById: text('createdById')
223
+ .notNull()
224
+ .references(() => User.id),
225
+ updatedById: text('updatedById').references(() => User.id)
226
+ })
227
+
228
+ export const TaskRelations = relations(Task, ({ one, many }) => ({
229
+ createdBy: one(User, {
230
+ fields: [Task.createdById],
231
+ references: [User.id]
232
+ }),
233
+ updatedBy: one(User, {
234
+ fields: [Task.updatedById],
235
+ references: [User.id]
236
+ }),
237
+ images: many(Resource)
238
+ }))
239
+
240
+ export const Resource = pgTable(
241
+ 'Resources',
242
+ {
243
+ id: text('id')
244
+ .notNull()
245
+ .primaryKey()
246
+ .$defaultFn(() => createId()),
247
+ filename: varchar('filename', { length: 255 }).notNull(),
248
+ key: varchar('key', { length: 255 }).notNull().unique(),
249
+ isTemp: boolean('isTemp').notNull().default(false),
250
+ url: varchar('url', { length: 255 }).notNull().unique(),
251
+ size: bigint('size', { mode: 'number' }).notNull(),
252
+ contentType: varchar('contentType', { length: 255 }).notNull(),
253
+
254
+ taskId: text('taskId').references(() => Task.id),
255
+
256
+ createdAt: timestamp('createdAt', {
257
+ mode: 'date',
258
+ withTimezone: true
259
+ })
260
+ .default(sql`CURRENT_TIMESTAMP`)
261
+ .notNull(),
262
+ updatedAt: timestamp('updatedAt', {
263
+ mode: 'date',
264
+ withTimezone: true
265
+ }),
266
+ createdById: text('createdById').references(() => User.id),
267
+ updatedById: text('updatedById').references(() => User.id)
268
+ },
269
+ resource => ({
270
+ taskIdIdx: index('Resources_idx_taskId').on(resource.taskId)
271
+ })
272
+ )
273
+
274
+ export const ResourceRelations = relations(Resource, ({ one }) => ({
275
+ task: one(Task, {
276
+ fields: [Resource.taskId],
277
+ references: [Task.id]
278
+ }),
279
+ createdBy: one(User, {
280
+ fields: [Resource.createdById],
281
+ references: [User.id]
282
+ }),
283
+ updatedBy: one(User, {
284
+ fields: [Resource.updatedById],
285
+ references: [User.id]
286
+ })
287
+ }))
@@ -0,0 +1,89 @@
1
+ import { DrizzleAdapter } from '@auth/drizzle-adapter'
2
+ import { InferSelectModel } from 'drizzle-orm'
3
+ import {
4
+ Session as NextAuthSession,
5
+ getServerSession,
6
+ type DefaultSession,
7
+ type NextAuthOptions
8
+ } from 'next-auth'
9
+ import { Adapter } from 'next-auth/adapters'
10
+ import EmailProvider from 'next-auth/providers/email'
11
+ import GitHubProvider from 'next-auth/providers/github'
12
+ import GoogleProvider from 'next-auth/providers/google'
13
+ import { cache } from 'react'
14
+
15
+ import type { RolePermissionType } from '@/data-access/role-permission'
16
+ import type { UserType } from '@/data-access/user'
17
+ import { db } from '@/db'
18
+ import { Account, Session, User, VerificationToken } from '@/db/schema'
19
+ import { env } from '@/env.mjs'
20
+ import { checkForRolePermissionUseCase } from '@/use-case/role-permission'
21
+
22
+ declare module 'next-auth' {
23
+ interface Session extends DefaultSession {
24
+ user: {
25
+ id: string
26
+ type: UserType['type']
27
+ roleId?: string | null
28
+ } & DefaultSession['user']
29
+ }
30
+
31
+ interface User extends InferSelectModel<typeof User> {}
32
+ }
33
+
34
+ export const authOptions: NextAuthOptions = {
35
+ pages: {
36
+ signIn: '/login'
37
+ },
38
+ callbacks: {
39
+ session({ session, user }) {
40
+ return {
41
+ ...session,
42
+ user: {
43
+ ...session.user,
44
+ id: user.id,
45
+ type: user.type,
46
+ roleId: user.roleId
47
+ }
48
+ }
49
+ }
50
+ },
51
+ adapter: DrizzleAdapter(db, {
52
+ usersTable: User,
53
+ accountsTable: Account,
54
+ sessionsTable: Session,
55
+ verificationTokensTable: VerificationToken
56
+ }) as Adapter,
57
+ providers: [
58
+ EmailProvider({
59
+ server: {
60
+ host: env.EMAIL_SERVER_HOST,
61
+ port: env.EMAIL_SERVER_PORT,
62
+ auth: {
63
+ user: env.EMAIL_SERVER_USER,
64
+ pass: env.EMAIL_SERVER_PASSWORD
65
+ }
66
+ },
67
+ from: env.EMAIL_FROM
68
+ }),
69
+ GoogleProvider({
70
+ clientId: env.GOOGLE_ID,
71
+ clientSecret: env.GOOGLE_SECRET
72
+ }),
73
+ GitHubProvider({
74
+ clientId: env.GITHUB_ID,
75
+ clientSecret: env.GITHUB_SECRET
76
+ })
77
+ ]
78
+ }
79
+
80
+ export const getAuthSession = cache(() => getServerSession(authOptions))
81
+
82
+ export const authGuard = cache(
83
+ (session: NextAuthSession, permission?: RolePermissionType['permission']) =>
84
+ checkForRolePermissionUseCase(
85
+ session.user.type,
86
+ session.user.roleId,
87
+ permission
88
+ )
89
+ )
@@ -0,0 +1,19 @@
1
+ import { createSafeActionClient } from 'next-safe-action'
2
+
3
+ import { getAuthSession } from './auth'
4
+
5
+ const actionClient = createSafeActionClient()
6
+
7
+ export const authActionClient = actionClient.use(async ({ next }) => {
8
+ const session = await getAuthSession()
9
+
10
+ if (!session?.user) {
11
+ throw new Error('Unauthorized')
12
+ }
13
+
14
+ return next({
15
+ ctx: {
16
+ user: session.user
17
+ }
18
+ })
19
+ })