@nextsparkjs/core 0.1.0-beta.82 → 0.1.0-beta.84

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 (365) hide show
  1. package/dist/components/entities/wrappers/EntityDetailWrapper.d.ts.map +1 -1
  2. package/dist/components/entities/wrappers/EntityDetailWrapper.js +11 -39
  3. package/dist/hooks/useEntityQuery.d.ts.map +1 -1
  4. package/dist/hooks/useEntityQuery.js +21 -3
  5. package/dist/lib/theme/get-default-theme-mode.d.ts +11 -0
  6. package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
  7. package/dist/lib/theme/get-default-theme-mode.js +42 -25
  8. package/dist/styles/classes.json +1 -1
  9. package/dist/types/theme.d.ts +2 -0
  10. package/dist/types/theme.d.ts.map +1 -1
  11. package/package.json +3 -3
  12. package/dist/templates/app/(auth)/forgot-password/page.tsx +0 -216
  13. package/dist/templates/app/(auth)/layout.tsx +0 -51
  14. package/dist/templates/app/(auth)/login/page.tsx +0 -21
  15. package/dist/templates/app/(auth)/reset-password/page.tsx +0 -212
  16. package/dist/templates/app/(auth)/signup/page.tsx +0 -21
  17. package/dist/templates/app/(auth)/verify-email/page.tsx +0 -190
  18. package/dist/templates/app/(public)/[...slug]/page.tsx +0 -378
  19. package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +0 -90
  20. package/dist/templates/app/(public)/docs/layout.tsx +0 -25
  21. package/dist/templates/app/(public)/docs/page.tsx +0 -81
  22. package/dist/templates/app/(public)/layout.tsx +0 -41
  23. package/dist/templates/app/(public)/page.tsx +0 -19
  24. package/dist/templates/app/403/page.tsx +0 -89
  25. package/dist/templates/app/api/auth/[...all]/route.ts +0 -78
  26. package/dist/templates/app/api/cron/billing/lifecycle/route.ts +0 -98
  27. package/dist/templates/app/api/csp-report/route.ts +0 -175
  28. package/dist/templates/app/api/devtools/config/entities/route.ts +0 -108
  29. package/dist/templates/app/api/devtools/config/theme/route.ts +0 -66
  30. package/dist/templates/app/api/devtools/tests/[...path]/route.ts +0 -130
  31. package/dist/templates/app/api/devtools/tests/route.ts +0 -134
  32. package/dist/templates/app/api/health/route.ts +0 -29
  33. package/dist/templates/app/api/internal/user-metadata/route.ts +0 -36
  34. package/dist/templates/app/api/superadmin/subscriptions/route.ts +0 -310
  35. package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +0 -286
  36. package/dist/templates/app/api/superadmin/teams/route.ts +0 -188
  37. package/dist/templates/app/api/superadmin/users/[userId]/route.ts +0 -540
  38. package/dist/templates/app/api/superadmin/users/route.ts +0 -323
  39. package/dist/templates/app/api/user/delete-account/route.ts +0 -55
  40. package/dist/templates/app/api/user/plan-flags/route.ts +0 -283
  41. package/dist/templates/app/api/user/profile/route.ts +0 -133
  42. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +0 -210
  43. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +0 -331
  44. package/dist/templates/app/api/v1/[entity]/[id]/route.ts +0 -35
  45. package/dist/templates/app/api/v1/[entity]/docs.md +0 -369
  46. package/dist/templates/app/api/v1/[entity]/presets.ts +0 -194
  47. package/dist/templates/app/api/v1/[entity]/route.ts +0 -31
  48. package/dist/templates/app/api/v1/api-keys/[id]/route.ts +0 -303
  49. package/dist/templates/app/api/v1/api-keys/docs.md +0 -101
  50. package/dist/templates/app/api/v1/api-keys/presets.ts +0 -31
  51. package/dist/templates/app/api/v1/api-keys/route.ts +0 -250
  52. package/dist/templates/app/api/v1/auth/docs.md +0 -184
  53. package/dist/templates/app/api/v1/auth/presets.ts +0 -44
  54. package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +0 -227
  55. package/dist/templates/app/api/v1/billing/cancel/route.ts +0 -206
  56. package/dist/templates/app/api/v1/billing/change-plan/route.ts +0 -97
  57. package/dist/templates/app/api/v1/billing/check-action/route.ts +0 -81
  58. package/dist/templates/app/api/v1/billing/checkout/route.ts +0 -124
  59. package/dist/templates/app/api/v1/billing/docs.md +0 -209
  60. package/dist/templates/app/api/v1/billing/plans/route.ts +0 -85
  61. package/dist/templates/app/api/v1/billing/portal/route.ts +0 -90
  62. package/dist/templates/app/api/v1/billing/presets.ts +0 -121
  63. package/dist/templates/app/api/v1/billing/webhooks/stripe/route.ts +0 -428
  64. package/dist/templates/app/api/v1/blocks/[slug]/route.ts +0 -29
  65. package/dist/templates/app/api/v1/blocks/docs.md +0 -173
  66. package/dist/templates/app/api/v1/blocks/presets.ts +0 -121
  67. package/dist/templates/app/api/v1/blocks/route.ts +0 -45
  68. package/dist/templates/app/api/v1/blocks/validate/route.ts +0 -45
  69. package/dist/templates/app/api/v1/cron/docs.md +0 -116
  70. package/dist/templates/app/api/v1/cron/presets.ts +0 -26
  71. package/dist/templates/app/api/v1/cron/process/route.ts +0 -108
  72. package/dist/templates/app/api/v1/devtools/blocks/route.ts +0 -82
  73. package/dist/templates/app/api/v1/devtools/docs/route.ts +0 -150
  74. package/dist/templates/app/api/v1/devtools/docs.md +0 -204
  75. package/dist/templates/app/api/v1/devtools/features/route.ts +0 -61
  76. package/dist/templates/app/api/v1/devtools/flows/route.ts +0 -61
  77. package/dist/templates/app/api/v1/devtools/presets.ts +0 -113
  78. package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +0 -120
  79. package/dist/templates/app/api/v1/devtools/testing/route.ts +0 -82
  80. package/dist/templates/app/api/v1/media/docs.md +0 -117
  81. package/dist/templates/app/api/v1/media/presets.ts +0 -24
  82. package/dist/templates/app/api/v1/media/upload/route.ts +0 -150
  83. package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +0 -116
  84. package/dist/templates/app/api/v1/plugin/[...path]/route.ts +0 -373
  85. package/dist/templates/app/api/v1/plugin/docs.md +0 -79
  86. package/dist/templates/app/api/v1/plugin/presets.ts +0 -21
  87. package/dist/templates/app/api/v1/plugin/route.ts +0 -96
  88. package/dist/templates/app/api/v1/post-categories/[id]/route.ts +0 -255
  89. package/dist/templates/app/api/v1/post-categories/docs.md +0 -134
  90. package/dist/templates/app/api/v1/post-categories/presets.ts +0 -78
  91. package/dist/templates/app/api/v1/post-categories/route.ts +0 -119
  92. package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +0 -179
  93. package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +0 -120
  94. package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +0 -89
  95. package/dist/templates/app/api/v1/team-invitations/docs.md +0 -88
  96. package/dist/templates/app/api/v1/team-invitations/presets.ts +0 -43
  97. package/dist/templates/app/api/v1/team-invitations/route.ts +0 -114
  98. package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +0 -171
  99. package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +0 -105
  100. package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +0 -125
  101. package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +0 -263
  102. package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +0 -358
  103. package/dist/templates/app/api/v1/teams/[teamId]/route.ts +0 -322
  104. package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +0 -50
  105. package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +0 -91
  106. package/dist/templates/app/api/v1/teams/docs.md +0 -320
  107. package/dist/templates/app/api/v1/teams/presets.ts +0 -178
  108. package/dist/templates/app/api/v1/teams/route.ts +0 -293
  109. package/dist/templates/app/api/v1/teams/switch/route.ts +0 -88
  110. package/dist/templates/app/api/v1/theme/[...path]/route.ts +0 -361
  111. package/dist/templates/app/api/v1/theme/docs.md +0 -74
  112. package/dist/templates/app/api/v1/theme/presets.ts +0 -21
  113. package/dist/templates/app/api/v1/theme/route.ts +0 -96
  114. package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +0 -363
  115. package/dist/templates/app/api/v1/users/[id]/route.ts +0 -302
  116. package/dist/templates/app/api/v1/users/docs.md +0 -93
  117. package/dist/templates/app/api/v1/users/presets.ts +0 -59
  118. package/dist/templates/app/api/v1/users/route.ts +0 -197
  119. package/dist/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +0 -117
  120. package/dist/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +0 -103
  121. package/dist/templates/app/dashboard/(main)/[entity]/create/page.tsx +0 -95
  122. package/dist/templates/app/dashboard/(main)/[entity]/error.tsx +0 -51
  123. package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +0 -113
  124. package/dist/templates/app/dashboard/(main)/[entity]/loading.tsx +0 -61
  125. package/dist/templates/app/dashboard/(main)/[entity]/page.tsx +0 -90
  126. package/dist/templates/app/dashboard/(main)/layout.tsx +0 -98
  127. package/dist/templates/app/dashboard/(main)/loading.tsx +0 -5
  128. package/dist/templates/app/dashboard/(main)/page.tsx +0 -201
  129. package/dist/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +0 -114
  130. package/dist/templates/app/dashboard/(main)/patterns/[id]/page.tsx +0 -20
  131. package/dist/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +0 -171
  132. package/dist/templates/app/dashboard/(main)/patterns/create/page.tsx +0 -86
  133. package/dist/templates/app/dashboard/(main)/patterns/page.tsx +0 -444
  134. package/dist/templates/app/dashboard/features/analytics/page.tsx +0 -35
  135. package/dist/templates/app/dashboard/features/automation/page.tsx +0 -35
  136. package/dist/templates/app/dashboard/features/layout.tsx +0 -13
  137. package/dist/templates/app/dashboard/features/loading.tsx +0 -5
  138. package/dist/templates/app/dashboard/features/webhooks/page.tsx +0 -35
  139. package/dist/templates/app/dashboard/layout.tsx +0 -86
  140. package/dist/templates/app/dashboard/permission-denied/page.tsx +0 -29
  141. package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +0 -5
  142. package/dist/templates/app/dashboard/settings/api-keys/page.tsx +0 -513
  143. package/dist/templates/app/dashboard/settings/billing/loading.tsx +0 -5
  144. package/dist/templates/app/dashboard/settings/billing/page.tsx +0 -284
  145. package/dist/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +0 -222
  146. package/dist/templates/app/dashboard/settings/invoices/loading.tsx +0 -5
  147. package/dist/templates/app/dashboard/settings/invoices/page.tsx +0 -82
  148. package/dist/templates/app/dashboard/settings/layout.tsx +0 -151
  149. package/dist/templates/app/dashboard/settings/loading.tsx +0 -5
  150. package/dist/templates/app/dashboard/settings/notifications/loading.tsx +0 -5
  151. package/dist/templates/app/dashboard/settings/notifications/page.tsx +0 -462
  152. package/dist/templates/app/dashboard/settings/page.tsx +0 -92
  153. package/dist/templates/app/dashboard/settings/password/loading.tsx +0 -5
  154. package/dist/templates/app/dashboard/settings/password/page.tsx +0 -306
  155. package/dist/templates/app/dashboard/settings/plans/loading.tsx +0 -5
  156. package/dist/templates/app/dashboard/settings/plans/page.tsx +0 -40
  157. package/dist/templates/app/dashboard/settings/profile/loading.tsx +0 -5
  158. package/dist/templates/app/dashboard/settings/profile/page.tsx +0 -686
  159. package/dist/templates/app/dashboard/settings/security/loading.tsx +0 -5
  160. package/dist/templates/app/dashboard/settings/security/page.tsx +0 -505
  161. package/dist/templates/app/dashboard/settings/teams/loading.tsx +0 -5
  162. package/dist/templates/app/dashboard/settings/teams/page.tsx +0 -272
  163. package/dist/templates/app/dashboard/settings/teams/permissions/page.tsx +0 -92
  164. package/dist/templates/app/devtools/blocks/[slug]/page.tsx +0 -39
  165. package/dist/templates/app/devtools/blocks/page.tsx +0 -31
  166. package/dist/templates/app/devtools/config/page.tsx +0 -31
  167. package/dist/templates/app/devtools/features/page.tsx +0 -31
  168. package/dist/templates/app/devtools/flows/page.tsx +0 -31
  169. package/dist/templates/app/devtools/layout.tsx +0 -58
  170. package/dist/templates/app/devtools/page.tsx +0 -121
  171. package/dist/templates/app/devtools/scheduled-actions/page.tsx +0 -157
  172. package/dist/templates/app/devtools/style/page.tsx +0 -330
  173. package/dist/templates/app/devtools/tags/page.tsx +0 -31
  174. package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +0 -47
  175. package/dist/templates/app/favicon.ico +0 -0
  176. package/dist/templates/app/globals.css +0 -12
  177. package/dist/templates/app/layout.tsx +0 -96
  178. package/dist/templates/app/public/page.tsx +0 -30
  179. package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +0 -92
  180. package/dist/templates/app/superadmin/docs/page.tsx +0 -75
  181. package/dist/templates/app/superadmin/layout.tsx +0 -67
  182. package/dist/templates/app/superadmin/page.tsx +0 -149
  183. package/dist/templates/app/superadmin/subscriptions/page.tsx +0 -655
  184. package/dist/templates/app/superadmin/team-roles/page.tsx +0 -493
  185. package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +0 -687
  186. package/dist/templates/app/superadmin/teams/page.tsx +0 -302
  187. package/dist/templates/app/superadmin/users/[userId]/page.tsx +0 -548
  188. package/dist/templates/app/superadmin/users/page.tsx +0 -528
  189. package/templates/app/(auth)/forgot-password/page.tsx +0 -216
  190. package/templates/app/(auth)/layout.tsx +0 -51
  191. package/templates/app/(auth)/login/page.tsx +0 -21
  192. package/templates/app/(auth)/reset-password/page.tsx +0 -212
  193. package/templates/app/(auth)/signup/page.tsx +0 -21
  194. package/templates/app/(auth)/verify-email/page.tsx +0 -190
  195. package/templates/app/(public)/[...slug]/page.tsx +0 -378
  196. package/templates/app/(public)/docs/[section]/[page]/page.tsx +0 -90
  197. package/templates/app/(public)/docs/layout.tsx +0 -25
  198. package/templates/app/(public)/docs/page.tsx +0 -81
  199. package/templates/app/(public)/layout.tsx +0 -41
  200. package/templates/app/(public)/page.tsx +0 -19
  201. package/templates/app/403/page.tsx +0 -89
  202. package/templates/app/api/auth/[...all]/route.ts +0 -78
  203. package/templates/app/api/cron/billing/lifecycle/route.ts +0 -98
  204. package/templates/app/api/csp-report/route.ts +0 -175
  205. package/templates/app/api/devtools/config/entities/route.ts +0 -108
  206. package/templates/app/api/devtools/config/theme/route.ts +0 -66
  207. package/templates/app/api/devtools/tests/[...path]/route.ts +0 -130
  208. package/templates/app/api/devtools/tests/route.ts +0 -134
  209. package/templates/app/api/health/route.ts +0 -29
  210. package/templates/app/api/internal/user-metadata/route.ts +0 -36
  211. package/templates/app/api/superadmin/subscriptions/route.ts +0 -310
  212. package/templates/app/api/superadmin/teams/[teamId]/route.ts +0 -286
  213. package/templates/app/api/superadmin/teams/route.ts +0 -188
  214. package/templates/app/api/superadmin/users/[userId]/route.ts +0 -540
  215. package/templates/app/api/superadmin/users/route.ts +0 -323
  216. package/templates/app/api/user/delete-account/route.ts +0 -55
  217. package/templates/app/api/user/plan-flags/route.ts +0 -283
  218. package/templates/app/api/user/profile/route.ts +0 -133
  219. package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +0 -210
  220. package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +0 -331
  221. package/templates/app/api/v1/[entity]/[id]/route.ts +0 -35
  222. package/templates/app/api/v1/[entity]/docs.md +0 -369
  223. package/templates/app/api/v1/[entity]/presets.ts +0 -194
  224. package/templates/app/api/v1/[entity]/route.ts +0 -31
  225. package/templates/app/api/v1/api-keys/[id]/route.ts +0 -303
  226. package/templates/app/api/v1/api-keys/docs.md +0 -101
  227. package/templates/app/api/v1/api-keys/presets.ts +0 -31
  228. package/templates/app/api/v1/api-keys/route.ts +0 -250
  229. package/templates/app/api/v1/auth/docs.md +0 -184
  230. package/templates/app/api/v1/auth/presets.ts +0 -44
  231. package/templates/app/api/v1/auth/signup-with-invite/route.ts +0 -227
  232. package/templates/app/api/v1/billing/cancel/route.ts +0 -206
  233. package/templates/app/api/v1/billing/change-plan/route.ts +0 -97
  234. package/templates/app/api/v1/billing/check-action/route.ts +0 -81
  235. package/templates/app/api/v1/billing/checkout/route.ts +0 -124
  236. package/templates/app/api/v1/billing/docs.md +0 -209
  237. package/templates/app/api/v1/billing/plans/route.ts +0 -85
  238. package/templates/app/api/v1/billing/portal/route.ts +0 -90
  239. package/templates/app/api/v1/billing/presets.ts +0 -121
  240. package/templates/app/api/v1/billing/webhooks/stripe/route.ts +0 -428
  241. package/templates/app/api/v1/blocks/[slug]/route.ts +0 -29
  242. package/templates/app/api/v1/blocks/docs.md +0 -173
  243. package/templates/app/api/v1/blocks/presets.ts +0 -121
  244. package/templates/app/api/v1/blocks/route.ts +0 -45
  245. package/templates/app/api/v1/blocks/validate/route.ts +0 -45
  246. package/templates/app/api/v1/cron/docs.md +0 -116
  247. package/templates/app/api/v1/cron/presets.ts +0 -26
  248. package/templates/app/api/v1/cron/process/route.ts +0 -108
  249. package/templates/app/api/v1/devtools/blocks/route.ts +0 -82
  250. package/templates/app/api/v1/devtools/docs/route.ts +0 -150
  251. package/templates/app/api/v1/devtools/docs.md +0 -204
  252. package/templates/app/api/v1/devtools/features/route.ts +0 -61
  253. package/templates/app/api/v1/devtools/flows/route.ts +0 -61
  254. package/templates/app/api/v1/devtools/presets.ts +0 -113
  255. package/templates/app/api/v1/devtools/scheduled-actions/route.ts +0 -120
  256. package/templates/app/api/v1/devtools/testing/route.ts +0 -82
  257. package/templates/app/api/v1/media/docs.md +0 -117
  258. package/templates/app/api/v1/media/presets.ts +0 -24
  259. package/templates/app/api/v1/media/upload/route.ts +0 -150
  260. package/templates/app/api/v1/patterns/[id]/usages/route.ts +0 -116
  261. package/templates/app/api/v1/plugin/[...path]/route.ts +0 -373
  262. package/templates/app/api/v1/plugin/docs.md +0 -79
  263. package/templates/app/api/v1/plugin/presets.ts +0 -21
  264. package/templates/app/api/v1/plugin/route.ts +0 -96
  265. package/templates/app/api/v1/post-categories/[id]/route.ts +0 -255
  266. package/templates/app/api/v1/post-categories/docs.md +0 -134
  267. package/templates/app/api/v1/post-categories/presets.ts +0 -78
  268. package/templates/app/api/v1/post-categories/route.ts +0 -119
  269. package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +0 -179
  270. package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +0 -120
  271. package/templates/app/api/v1/team-invitations/[token]/route.ts +0 -89
  272. package/templates/app/api/v1/team-invitations/docs.md +0 -88
  273. package/templates/app/api/v1/team-invitations/presets.ts +0 -43
  274. package/templates/app/api/v1/team-invitations/route.ts +0 -114
  275. package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +0 -171
  276. package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +0 -105
  277. package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +0 -125
  278. package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +0 -263
  279. package/templates/app/api/v1/teams/[teamId]/members/route.ts +0 -358
  280. package/templates/app/api/v1/teams/[teamId]/route.ts +0 -322
  281. package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +0 -50
  282. package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +0 -91
  283. package/templates/app/api/v1/teams/docs.md +0 -320
  284. package/templates/app/api/v1/teams/presets.ts +0 -178
  285. package/templates/app/api/v1/teams/route.ts +0 -293
  286. package/templates/app/api/v1/teams/switch/route.ts +0 -88
  287. package/templates/app/api/v1/theme/[...path]/route.ts +0 -361
  288. package/templates/app/api/v1/theme/docs.md +0 -74
  289. package/templates/app/api/v1/theme/presets.ts +0 -21
  290. package/templates/app/api/v1/theme/route.ts +0 -96
  291. package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +0 -363
  292. package/templates/app/api/v1/users/[id]/route.ts +0 -302
  293. package/templates/app/api/v1/users/docs.md +0 -93
  294. package/templates/app/api/v1/users/presets.ts +0 -59
  295. package/templates/app/api/v1/users/route.ts +0 -197
  296. package/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +0 -117
  297. package/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +0 -103
  298. package/templates/app/dashboard/(main)/[entity]/create/page.tsx +0 -95
  299. package/templates/app/dashboard/(main)/[entity]/error.tsx +0 -51
  300. package/templates/app/dashboard/(main)/[entity]/layout.tsx +0 -113
  301. package/templates/app/dashboard/(main)/[entity]/loading.tsx +0 -61
  302. package/templates/app/dashboard/(main)/[entity]/page.tsx +0 -90
  303. package/templates/app/dashboard/(main)/layout.tsx +0 -98
  304. package/templates/app/dashboard/(main)/loading.tsx +0 -5
  305. package/templates/app/dashboard/(main)/page.tsx +0 -201
  306. package/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +0 -114
  307. package/templates/app/dashboard/(main)/patterns/[id]/page.tsx +0 -20
  308. package/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +0 -171
  309. package/templates/app/dashboard/(main)/patterns/create/page.tsx +0 -86
  310. package/templates/app/dashboard/(main)/patterns/page.tsx +0 -444
  311. package/templates/app/dashboard/features/analytics/page.tsx +0 -35
  312. package/templates/app/dashboard/features/automation/page.tsx +0 -35
  313. package/templates/app/dashboard/features/layout.tsx +0 -13
  314. package/templates/app/dashboard/features/loading.tsx +0 -5
  315. package/templates/app/dashboard/features/webhooks/page.tsx +0 -35
  316. package/templates/app/dashboard/layout.tsx +0 -86
  317. package/templates/app/dashboard/permission-denied/page.tsx +0 -29
  318. package/templates/app/dashboard/settings/api-keys/loading.tsx +0 -5
  319. package/templates/app/dashboard/settings/api-keys/page.tsx +0 -513
  320. package/templates/app/dashboard/settings/billing/loading.tsx +0 -5
  321. package/templates/app/dashboard/settings/billing/page.tsx +0 -284
  322. package/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +0 -222
  323. package/templates/app/dashboard/settings/invoices/loading.tsx +0 -5
  324. package/templates/app/dashboard/settings/invoices/page.tsx +0 -82
  325. package/templates/app/dashboard/settings/layout.tsx +0 -151
  326. package/templates/app/dashboard/settings/loading.tsx +0 -5
  327. package/templates/app/dashboard/settings/notifications/loading.tsx +0 -5
  328. package/templates/app/dashboard/settings/notifications/page.tsx +0 -462
  329. package/templates/app/dashboard/settings/page.tsx +0 -92
  330. package/templates/app/dashboard/settings/password/loading.tsx +0 -5
  331. package/templates/app/dashboard/settings/password/page.tsx +0 -306
  332. package/templates/app/dashboard/settings/plans/loading.tsx +0 -5
  333. package/templates/app/dashboard/settings/plans/page.tsx +0 -40
  334. package/templates/app/dashboard/settings/profile/loading.tsx +0 -5
  335. package/templates/app/dashboard/settings/profile/page.tsx +0 -686
  336. package/templates/app/dashboard/settings/security/loading.tsx +0 -5
  337. package/templates/app/dashboard/settings/security/page.tsx +0 -505
  338. package/templates/app/dashboard/settings/teams/loading.tsx +0 -5
  339. package/templates/app/dashboard/settings/teams/page.tsx +0 -272
  340. package/templates/app/dashboard/settings/teams/permissions/page.tsx +0 -92
  341. package/templates/app/devtools/blocks/[slug]/page.tsx +0 -39
  342. package/templates/app/devtools/blocks/page.tsx +0 -31
  343. package/templates/app/devtools/config/page.tsx +0 -31
  344. package/templates/app/devtools/features/page.tsx +0 -31
  345. package/templates/app/devtools/flows/page.tsx +0 -31
  346. package/templates/app/devtools/layout.tsx +0 -58
  347. package/templates/app/devtools/page.tsx +0 -121
  348. package/templates/app/devtools/scheduled-actions/page.tsx +0 -157
  349. package/templates/app/devtools/style/page.tsx +0 -330
  350. package/templates/app/devtools/tags/page.tsx +0 -31
  351. package/templates/app/devtools/tests/[[...path]]/page.tsx +0 -47
  352. package/templates/app/favicon.ico +0 -0
  353. package/templates/app/globals.css +0 -12
  354. package/templates/app/layout.tsx +0 -96
  355. package/templates/app/public/page.tsx +0 -30
  356. package/templates/app/superadmin/docs/[section]/[page]/page.tsx +0 -92
  357. package/templates/app/superadmin/docs/page.tsx +0 -75
  358. package/templates/app/superadmin/layout.tsx +0 -67
  359. package/templates/app/superadmin/page.tsx +0 -149
  360. package/templates/app/superadmin/subscriptions/page.tsx +0 -655
  361. package/templates/app/superadmin/team-roles/page.tsx +0 -493
  362. package/templates/app/superadmin/teams/[teamId]/page.tsx +0 -687
  363. package/templates/app/superadmin/teams/page.tsx +0 -302
  364. package/templates/app/superadmin/users/[userId]/page.tsx +0 -548
  365. package/templates/app/superadmin/users/page.tsx +0 -528
@@ -1,358 +0,0 @@
1
- import { NextRequest, NextResponse } from 'next/server'
2
- import { queryWithRLS, queryOneWithRLS, mutateWithRLS } from '@nextsparkjs/core/lib/db'
3
- import {
4
- createApiResponse,
5
- createApiError,
6
- createPaginationMeta,
7
- withApiLogging,
8
- handleCorsPreflightRequest,
9
- addCorsHeaders,
10
- } from '@nextsparkjs/core/lib/api/helpers'
11
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
- import { isSuperAdmin } from '@nextsparkjs/core/lib/api/auth/permissions'
13
- import { checkRateLimit } from '@nextsparkjs/core/lib/api/rate-limit'
14
- import { RATE_LIMITS } from '@nextsparkjs/core/lib/api/keys'
15
- import { inviteMemberSchema, memberListQuerySchema } from '@nextsparkjs/core/lib/teams/schema'
16
- import { TeamMemberService, MembershipService } from '@nextsparkjs/core/lib/services'
17
- import type { TeamMember, TeamInvitation, TeamRole, Team } from '@nextsparkjs/core/lib/teams/types'
18
- import { EmailFactory } from '@nextsparkjs/core/lib/email/factory'
19
- import { createTeamInvitationEmail } from '@nextsparkjs/core/lib/email/templates'
20
-
21
- // Role hierarchy for invite validation (higher number = more power)
22
- const ROLE_HIERARCHY: Record<TeamRole, number> = {
23
- owner: 4,
24
- admin: 3,
25
- member: 2,
26
- viewer: 1,
27
- }
28
-
29
- // Check if a user can invite to a specific role (same level or below)
30
- function canInviteToRole(actorRole: TeamRole, targetRole: TeamRole): boolean {
31
- return ROLE_HIERARCHY[actorRole] >= ROLE_HIERARCHY[targetRole]
32
- }
33
-
34
- // Handle CORS preflight
35
- export async function OPTIONS() {
36
- return handleCorsPreflightRequest()
37
- }
38
-
39
- // GET /api/v1/teams/:teamId/members - List team members
40
- export const GET = withApiLogging(
41
- async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
42
- try {
43
- // Authenticate using dual auth
44
- const authResult = await authenticateRequest(req)
45
-
46
- if (!authResult.success) {
47
- return NextResponse.json(
48
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
49
- { status: 401 }
50
- )
51
- }
52
-
53
- if (authResult.rateLimitResponse) {
54
- return authResult.rateLimitResponse as NextResponse
55
- }
56
-
57
- const { teamId } = await params
58
-
59
- // Validate that teamId is not empty
60
- if (!teamId || teamId.trim() === '') {
61
- const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
62
- return addCorsHeaders(response)
63
- }
64
-
65
- // Superadmins can view members of any team
66
- const userIsSuperAdmin = isSuperAdmin(authResult)
67
-
68
- // Check if user is a member of the team (unless superadmin)
69
- if (!userIsSuperAdmin) {
70
- const isMember = await TeamMemberService.isMember(teamId, authResult.user!.id)
71
-
72
- if (!isMember) {
73
- const response = createApiError('Team not found or access denied', 404, null, 'TEAM_NOT_FOUND')
74
- return addCorsHeaders(response)
75
- }
76
- }
77
-
78
- // Parse query parameters (filter out null values so Zod defaults work)
79
- const { searchParams } = new URL(req.url)
80
- const queryParams = Object.fromEntries(
81
- Object.entries({
82
- page: searchParams.get('page'),
83
- limit: searchParams.get('limit'),
84
- role: searchParams.get('role'),
85
- search: searchParams.get('search'),
86
- }).filter(([, value]) => value !== null && value !== '')
87
- )
88
-
89
- const validatedQuery = memberListQuerySchema.parse(queryParams)
90
- const { page, limit, role, search } = validatedQuery
91
- const offset = (page - 1) * limit
92
-
93
- // Build WHERE clause based on filters
94
- let whereClause = 'WHERE tm."teamId" = $1'
95
- const queryValues: unknown[] = [teamId]
96
- let paramCount = 2
97
-
98
- if (role) {
99
- whereClause += ` AND tm.role = $${paramCount}`
100
- queryValues.push(role)
101
- paramCount++
102
- }
103
-
104
- if (search) {
105
- whereClause += ` AND (u.name ILIKE $${paramCount} OR u.email ILIKE $${paramCount})`
106
- queryValues.push(`%${search}%`)
107
- paramCount++
108
- }
109
-
110
- // Add pagination params
111
- queryValues.push(limit, offset)
112
-
113
- const members = await queryWithRLS<
114
- TeamMember & {
115
- userName: string | null
116
- userEmail: string
117
- userImage: string | null
118
- }
119
- >(
120
- `SELECT
121
- tm.*,
122
- u.name as "userName",
123
- u.email as "userEmail",
124
- u.image as "userImage"
125
- FROM "team_members" tm
126
- INNER JOIN "users" u ON tm."userId" = u.id
127
- ${whereClause}
128
- ORDER BY
129
- CASE tm.role
130
- WHEN 'owner' THEN 1
131
- WHEN 'admin' THEN 2
132
- WHEN 'member' THEN 3
133
- WHEN 'viewer' THEN 4
134
- END,
135
- tm."joinedAt" ASC
136
- LIMIT $${paramCount++} OFFSET $${paramCount++}`,
137
- queryValues,
138
- authResult.user!.id
139
- )
140
-
141
- // Get total count for pagination
142
- const totalResult = await queryWithRLS<{ count: string }>(
143
- `SELECT COUNT(*) as count
144
- FROM "team_members" tm
145
- INNER JOIN "users" u ON tm."userId" = u.id
146
- ${whereClause}`,
147
- queryValues.slice(0, -2), // Remove limit and offset
148
- authResult.user!.id
149
- )
150
-
151
- const total = parseInt(totalResult[0]?.count || '0', 10)
152
- const paginationMeta = createPaginationMeta(page, limit, total)
153
-
154
- const response = createApiResponse(members, paginationMeta)
155
- return addCorsHeaders(response)
156
- } catch (error) {
157
- console.error('Error fetching team members:', error)
158
- const response = createApiError('Internal server error', 500)
159
- return addCorsHeaders(response)
160
- }
161
- }
162
- )
163
-
164
- // POST /api/v1/teams/:teamId/members - Invite new member (creates invitation)
165
- export const POST = withApiLogging(
166
- async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
167
- try {
168
- // Authenticate using dual auth
169
- const authResult = await authenticateRequest(req)
170
-
171
- if (!authResult.success) {
172
- return NextResponse.json(
173
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
174
- { status: 401 }
175
- )
176
- }
177
-
178
- if (authResult.rateLimitResponse) {
179
- return authResult.rateLimitResponse as NextResponse
180
- }
181
-
182
- // Apply specific rate limit for creating invitations (20 req/min)
183
- const rateLimitConfig = RATE_LIMITS['teams:invite']
184
- const rateLimitKey = `invite:create:${authResult.user!.id}`
185
- const rateLimit = checkRateLimit(rateLimitKey, rateLimitConfig.requests, rateLimitConfig.windowMs)
186
-
187
- if (!rateLimit.allowed) {
188
- const response = createApiError(
189
- 'Rate limit exceeded. Please try again later.',
190
- 429,
191
- { retryAfter: Math.ceil((rateLimit.resetTime - Date.now()) / 1000) },
192
- 'RATE_LIMIT_EXCEEDED'
193
- )
194
- return addCorsHeaders(response)
195
- }
196
-
197
- const { teamId } = await params
198
-
199
- // Validate that teamId is not empty
200
- if (!teamId || teamId.trim() === '') {
201
- const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
202
- return addCorsHeaders(response)
203
- }
204
-
205
- // Check if user has permission to invite members using MembershipService
206
- const membership = await MembershipService.get(authResult.user!.id, teamId)
207
- const actionResult = membership.canPerformAction('members.invite')
208
-
209
- if (!actionResult.allowed) {
210
- const response = NextResponse.json(
211
- {
212
- success: false,
213
- error: actionResult.message,
214
- reason: actionResult.reason,
215
- meta: actionResult.meta,
216
- },
217
- { status: 403 }
218
- )
219
- return addCorsHeaders(response)
220
- }
221
-
222
- // Get user's role for role hierarchy validation
223
- const userRole = membership.role as TeamRole
224
-
225
- const body = await req.json()
226
- const validatedData = inviteMemberSchema.parse(body)
227
-
228
- // Check role hierarchy - users can only invite to same role or below
229
- if (!canInviteToRole(userRole, validatedData.role)) {
230
- const response = createApiError(
231
- `You cannot invite members to a role higher than your own. Your role: ${userRole}, requested role: ${validatedData.role}`,
232
- 403,
233
- null,
234
- 'ROLE_HIERARCHY_VIOLATION'
235
- )
236
- return addCorsHeaders(response)
237
- }
238
-
239
- // Check if user already exists and is already a member
240
- const existingUser = await queryOneWithRLS<{ id: string }>(
241
- 'SELECT id FROM "users" WHERE email = $1',
242
- [validatedData.email],
243
- authResult.user!.id
244
- )
245
-
246
- if (existingUser) {
247
- // Check if already a member
248
- const existingMember = await queryOneWithRLS<{ id: string }>(
249
- 'SELECT id FROM "team_members" WHERE "teamId" = $1 AND "userId" = $2',
250
- [teamId, existingUser.id],
251
- authResult.user!.id
252
- )
253
-
254
- if (existingMember) {
255
- const response = createApiError('User is already a member of this team', 409, null, 'ALREADY_MEMBER')
256
- return addCorsHeaders(response)
257
- }
258
- }
259
-
260
- // Check if there's already a pending invitation for this email
261
- const existingInvitation = await queryOneWithRLS<TeamInvitation>(
262
- `SELECT * FROM "team_invitations"
263
- WHERE "teamId" = $1 AND email = $2 AND status = 'pending'`,
264
- [teamId, validatedData.email],
265
- authResult.user!.id
266
- )
267
-
268
- if (existingInvitation) {
269
- const response = createApiError(
270
- 'A pending invitation already exists for this email',
271
- 409,
272
- null,
273
- 'INVITATION_EXISTS'
274
- )
275
- return addCorsHeaders(response)
276
- }
277
-
278
- // Get team info for email
279
- const team = await queryOneWithRLS<Team>(
280
- 'SELECT * FROM "teams" WHERE id = $1',
281
- [teamId],
282
- authResult.user!.id
283
- )
284
-
285
- if (!team) {
286
- const response = createApiError('Team not found', 404, null, 'TEAM_NOT_FOUND')
287
- return addCorsHeaders(response)
288
- }
289
-
290
- // Create invitation
291
- const token = globalThis.crypto.randomUUID()
292
- const expiresAt = new Date()
293
- expiresAt.setDate(expiresAt.getDate() + 7) // Expires in 7 days
294
-
295
- const result = await mutateWithRLS(
296
- `INSERT INTO "team_invitations" ("teamId", email, role, status, token, "invitedBy", "expiresAt")
297
- VALUES ($1, $2, $3, 'pending', $4, $5, $6)
298
- RETURNING *`,
299
- [teamId, validatedData.email, validatedData.role, token, authResult.user!.id, expiresAt.toISOString()],
300
- authResult.user!.id
301
- )
302
-
303
- const invitation = result.rows[0]
304
-
305
- // Build accept URL
306
- const baseUrl = (process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:5173').replace(/\/$/, '')
307
- const acceptUrl = `${baseUrl}/accept-invite/${token}`
308
-
309
- // Send invitation email
310
- try {
311
- const emailProvider = EmailFactory.getInstance()
312
- const inviterName = authResult.user!.email // Use email as inviter name
313
- const emailContent = createTeamInvitationEmail(
314
- validatedData.email,
315
- inviterName,
316
- team.name,
317
- validatedData.role,
318
- acceptUrl,
319
- '7 days'
320
- )
321
-
322
- await emailProvider.send({
323
- to: validatedData.email,
324
- subject: emailContent.subject,
325
- html: emailContent.html
326
- })
327
-
328
- // Log for testing purposes (like email verification)
329
- console.log('\n' + '🎫'.repeat(30))
330
- console.log('📨 TEAM INVITATION CREATED')
331
- console.log('🎫'.repeat(30))
332
- console.log(`📧 To: ${validatedData.email}`)
333
- console.log(`👤 Invited by: ${inviterName}`)
334
- console.log(`🏢 Team: ${team.name}`)
335
- console.log(`👑 Role: ${validatedData.role}`)
336
- console.log(`🔗 Accept URL: ${acceptUrl}`)
337
- console.log('🎫'.repeat(30) + '\n')
338
- } catch (emailError) {
339
- // Log error but don't fail the invitation creation
340
- console.error('Failed to send invitation email:', emailError)
341
- console.log(`⚠️ Email failed but invitation created. Accept URL: ${acceptUrl}`)
342
- }
343
-
344
- const response = createApiResponse(invitation, { created: true }, 201)
345
- return addCorsHeaders(response)
346
- } catch (error) {
347
- if (error instanceof Error && error.name === 'ZodError') {
348
- const zodError = error as { issues?: unknown[] }
349
- const response = createApiError('Validation error', 400, zodError.issues, 'VALIDATION_ERROR')
350
- return addCorsHeaders(response)
351
- }
352
-
353
- console.error('Error creating invitation:', error)
354
- const response = createApiError('Internal server error', 500)
355
- return addCorsHeaders(response)
356
- }
357
- }
358
- )
@@ -1,322 +0,0 @@
1
- import { NextRequest, NextResponse } from 'next/server'
2
- import { queryOneWithRLS, mutateWithRLS } from '@nextsparkjs/core/lib/db'
3
- import {
4
- createApiResponse,
5
- createApiError,
6
- withApiLogging,
7
- handleCorsPreflightRequest,
8
- addCorsHeaders,
9
- } from '@nextsparkjs/core/lib/api/helpers'
10
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
- import { ownerUpdateTeamSchema, adminUpdateTeamSchema } from '@nextsparkjs/core/lib/teams/schema'
12
- import { TeamService, MembershipService } from '@nextsparkjs/core/lib/services'
13
- import type { Team, TeamRole } from '@nextsparkjs/core/lib/teams/types'
14
-
15
- // Handle CORS preflight
16
- export async function OPTIONS() {
17
- return handleCorsPreflightRequest()
18
- }
19
-
20
- // GET /api/v1/teams/:teamId - Get team details
21
- export const GET = withApiLogging(
22
- async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
23
- try {
24
- // Authenticate using dual auth
25
- const authResult = await authenticateRequest(req)
26
-
27
- if (!authResult.success) {
28
- return NextResponse.json(
29
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
30
- { status: 401 }
31
- )
32
- }
33
-
34
- if (authResult.rateLimitResponse) {
35
- return authResult.rateLimitResponse as NextResponse
36
- }
37
-
38
- const { teamId } = await params
39
-
40
- // Validate that teamId is not empty
41
- if (!teamId || teamId.trim() === '') {
42
- const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
43
- return addCorsHeaders(response)
44
- }
45
-
46
- // Check if user is a member of the team
47
- const userRole = await queryOneWithRLS<{ role: TeamRole }>(
48
- 'SELECT role FROM "team_members" WHERE "teamId" = $1 AND "userId" = $2',
49
- [teamId, authResult.user!.id],
50
- authResult.user!.id
51
- )
52
-
53
- if (!userRole) {
54
- const response = createApiError('Team not found or access denied', 404, null, 'TEAM_NOT_FOUND')
55
- return addCorsHeaders(response)
56
- }
57
-
58
- // Fetch team with member count
59
- const team = await queryOneWithRLS<Team & { memberCount: string; userRole: TeamRole }>(
60
- `SELECT
61
- t.*,
62
- COUNT(DISTINCT tm.id) as "memberCount",
63
- $2::text as "userRole"
64
- FROM "teams" t
65
- LEFT JOIN "team_members" tm ON t.id = tm."teamId"
66
- WHERE t.id = $1
67
- GROUP BY t.id`,
68
- [teamId, userRole.role],
69
- authResult.user!.id
70
- )
71
-
72
- if (!team) {
73
- const response = createApiError('Team not found', 404, null, 'TEAM_NOT_FOUND')
74
- return addCorsHeaders(response)
75
- }
76
-
77
- const responseData = {
78
- ...team,
79
- memberCount: parseInt(team.memberCount, 10),
80
- }
81
-
82
- const response = createApiResponse(responseData)
83
- return addCorsHeaders(response)
84
- } catch (error) {
85
- console.error('Error fetching team:', error)
86
- const response = createApiError('Internal server error', 500)
87
- return addCorsHeaders(response)
88
- }
89
- }
90
- )
91
-
92
- // PATCH /api/v1/teams/:teamId - Update team (owners/admins only)
93
- export const PATCH = withApiLogging(
94
- async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
95
- try {
96
- // Authenticate using dual auth
97
- const authResult = await authenticateRequest(req)
98
-
99
- if (!authResult.success) {
100
- return NextResponse.json(
101
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
102
- { status: 401 }
103
- )
104
- }
105
-
106
- if (authResult.rateLimitResponse) {
107
- return authResult.rateLimitResponse as NextResponse
108
- }
109
-
110
- const { teamId } = await params
111
-
112
- // Validate that teamId is not empty
113
- if (!teamId || teamId.trim() === '') {
114
- const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
115
- return addCorsHeaders(response)
116
- }
117
-
118
- const body = await req.json()
119
-
120
- // FIX #2: Check owner-only requirement FIRST for name/description fields
121
- // This provides clearer error messages to users
122
- // Issue: https://github.com/NextSpark-js/nextspark/pull/1 (Issue #2)
123
- // FIX #1: Use 'in' operator to check property existence (not value truthiness)
124
- // This prevents type coercion bypass with falsy values (empty string, null, 0, false)
125
- // Issue: https://github.com/NextSpark-js/nextspark/pull/1 (Issue #1)
126
- const isOwnerOnlyUpdate = 'name' in body || 'description' in body
127
-
128
- if (isOwnerOnlyUpdate) {
129
- // Fetch team to verify ownership BEFORE other checks
130
- const team = await TeamService.getById(teamId, authResult.user!.id)
131
-
132
- if (!team || team.ownerId !== authResult.user!.id) {
133
- const response = createApiError(
134
- 'Only team owners can edit team name and description',
135
- 403,
136
- null,
137
- 'OWNER_ONLY'
138
- )
139
- return addCorsHeaders(response)
140
- }
141
-
142
- // Proceed with owner update (skip general permission check)
143
- } else {
144
- // Check general teams.update permission for non-owner-only fields
145
- const membership = await MembershipService.get(authResult.user!.id, teamId)
146
- const actionResult = membership.canPerformAction('teams.update')
147
-
148
- if (!actionResult.allowed) {
149
- const response = NextResponse.json(
150
- {
151
- success: false,
152
- error: actionResult.message,
153
- reason: actionResult.reason,
154
- meta: actionResult.meta,
155
- },
156
- { status: 403 }
157
- )
158
- return addCorsHeaders(response)
159
- }
160
- }
161
-
162
- // Validate with appropriate schema based on update type
163
- const schema = isOwnerOnlyUpdate ? ownerUpdateTeamSchema : adminUpdateTeamSchema
164
- const validatedData = schema.parse(body) as Record<string, unknown>
165
-
166
- // Check if slug is being changed and if it's available
167
- if ('slug' in validatedData && typeof validatedData.slug === 'string') {
168
- const slugAvailable = await TeamService.isSlugAvailable(validatedData.slug, teamId)
169
- if (!slugAvailable) {
170
- const response = createApiError('Team slug already exists', 409, null, 'SLUG_EXISTS')
171
- return addCorsHeaders(response)
172
- }
173
- }
174
-
175
- // Build dynamic update query
176
- const updates = []
177
- const values = []
178
- let paramCount = 1
179
-
180
- if ('name' in validatedData && validatedData.name !== undefined) {
181
- updates.push(`name = $${paramCount++}`)
182
- values.push(validatedData.name)
183
- }
184
-
185
- if ('slug' in validatedData && validatedData.slug !== undefined) {
186
- updates.push(`slug = $${paramCount++}`)
187
- values.push(validatedData.slug)
188
- }
189
-
190
- if ('description' in validatedData && validatedData.description !== undefined) {
191
- updates.push(`description = $${paramCount++}`)
192
- values.push(validatedData.description)
193
- }
194
-
195
- if ('avatarUrl' in validatedData && validatedData.avatarUrl !== undefined) {
196
- updates.push(`"avatarUrl" = $${paramCount++}`)
197
- values.push(validatedData.avatarUrl)
198
- }
199
-
200
- if ('settings' in validatedData && validatedData.settings !== undefined) {
201
- updates.push(`settings = $${paramCount++}`)
202
- values.push(JSON.stringify(validatedData.settings))
203
- }
204
-
205
- if (updates.length === 0) {
206
- const response = createApiError('No fields to update', 400, null, 'NO_FIELDS')
207
- return addCorsHeaders(response)
208
- }
209
-
210
- updates.push(`"updatedAt" = CURRENT_TIMESTAMP`)
211
- values.push(teamId)
212
-
213
- const query = `
214
- UPDATE "teams"
215
- SET ${updates.join(', ')}
216
- WHERE id = $${paramCount}
217
- RETURNING *
218
- `
219
-
220
- const result = await mutateWithRLS(query, values, authResult.user!.id)
221
-
222
- if (result.rows.length === 0) {
223
- const response = createApiError('Team not found', 404, null, 'TEAM_NOT_FOUND')
224
- return addCorsHeaders(response)
225
- }
226
-
227
- // Fetch team with member count
228
- const teamWithCount = await queryOneWithRLS<Team & { memberCount: string }>(
229
- `SELECT
230
- t.*,
231
- COUNT(DISTINCT tm.id) as "memberCount"
232
- FROM "teams" t
233
- LEFT JOIN "team_members" tm ON t.id = tm."teamId"
234
- WHERE t.id = $1
235
- GROUP BY t.id`,
236
- [teamId],
237
- authResult.user!.id
238
- )
239
-
240
- const responseData = {
241
- ...teamWithCount,
242
- memberCount: parseInt(teamWithCount?.memberCount || '0', 10),
243
- }
244
-
245
- const response = createApiResponse(responseData)
246
- return addCorsHeaders(response)
247
- } catch (error) {
248
- if (error instanceof Error && error.name === 'ZodError') {
249
- const zodError = error as { issues?: unknown[] }
250
- const response = createApiError('Validation error', 400, zodError.issues, 'VALIDATION_ERROR')
251
- return addCorsHeaders(response)
252
- }
253
-
254
- console.error('Error updating team:', error)
255
- const response = createApiError('Internal server error', 500)
256
- return addCorsHeaders(response)
257
- }
258
- }
259
- )
260
-
261
- // DELETE /api/v1/teams/:teamId - Delete team (owners only, NOT personal teams)
262
- export const DELETE = withApiLogging(
263
- async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
264
- try {
265
- // Authenticate using dual auth
266
- const authResult = await authenticateRequest(req)
267
-
268
- if (!authResult.success) {
269
- return NextResponse.json(
270
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
271
- { status: 401 }
272
- )
273
- }
274
-
275
- if (authResult.rateLimitResponse) {
276
- return authResult.rateLimitResponse as NextResponse
277
- }
278
-
279
- const { teamId } = await params
280
-
281
- // Validate that teamId is not empty
282
- if (!teamId || teamId.trim() === '') {
283
- const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
284
- return addCorsHeaders(response)
285
- }
286
-
287
- // Use the TeamService.delete (handles owner check and personal team protection)
288
- try {
289
- await TeamService.delete(teamId, authResult.user!.id)
290
-
291
- const response = createApiResponse({ deleted: true, id: teamId })
292
- return addCorsHeaders(response)
293
- } catch (error) {
294
- if (error instanceof Error && error.message === 'Only team owner can delete the team') {
295
- const response = createApiError(
296
- 'Insufficient permissions. Only team owner can delete the team.',
297
- 403,
298
- null,
299
- 'INSUFFICIENT_PERMISSIONS'
300
- )
301
- return addCorsHeaders(response)
302
- }
303
-
304
- if (error instanceof Error && error.message === 'Team not found') {
305
- const response = createApiError('Team not found', 404, null, 'TEAM_NOT_FOUND')
306
- return addCorsHeaders(response)
307
- }
308
-
309
- if (error instanceof Error && error.message === 'Personal teams cannot be deleted') {
310
- const response = createApiError('Personal teams cannot be deleted', 403, null, 'PERSONAL_TEAM_DELETE_FORBIDDEN')
311
- return addCorsHeaders(response)
312
- }
313
-
314
- throw error
315
- }
316
- } catch (error) {
317
- console.error('Error deleting team:', error)
318
- const response = createApiError('Internal server error', 500)
319
- return addCorsHeaders(response)
320
- }
321
- }
322
- )