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

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 (374) 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 +16 -16
  12. package/scripts/build/docs-registry.mjs +0 -0
  13. package/scripts/create-theme.mjs +0 -0
  14. package/scripts/deploy/release-version.mjs +0 -0
  15. package/scripts/deploy/vercel-deploy.mjs +0 -0
  16. package/scripts/dev/watch-plugins.mjs +0 -0
  17. package/scripts/maintenance/update-core.mjs +0 -0
  18. package/scripts/setup/npm-postinstall.mjs +0 -0
  19. package/scripts/setup/setup-claude.mjs +0 -0
  20. package/scripts/validation/check-imports.sh +0 -0
  21. package/dist/templates/app/(auth)/forgot-password/page.tsx +0 -216
  22. package/dist/templates/app/(auth)/layout.tsx +0 -51
  23. package/dist/templates/app/(auth)/login/page.tsx +0 -21
  24. package/dist/templates/app/(auth)/reset-password/page.tsx +0 -212
  25. package/dist/templates/app/(auth)/signup/page.tsx +0 -21
  26. package/dist/templates/app/(auth)/verify-email/page.tsx +0 -190
  27. package/dist/templates/app/(public)/[...slug]/page.tsx +0 -378
  28. package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +0 -90
  29. package/dist/templates/app/(public)/docs/layout.tsx +0 -25
  30. package/dist/templates/app/(public)/docs/page.tsx +0 -81
  31. package/dist/templates/app/(public)/layout.tsx +0 -41
  32. package/dist/templates/app/(public)/page.tsx +0 -19
  33. package/dist/templates/app/403/page.tsx +0 -89
  34. package/dist/templates/app/api/auth/[...all]/route.ts +0 -78
  35. package/dist/templates/app/api/cron/billing/lifecycle/route.ts +0 -98
  36. package/dist/templates/app/api/csp-report/route.ts +0 -175
  37. package/dist/templates/app/api/devtools/config/entities/route.ts +0 -108
  38. package/dist/templates/app/api/devtools/config/theme/route.ts +0 -66
  39. package/dist/templates/app/api/devtools/tests/[...path]/route.ts +0 -130
  40. package/dist/templates/app/api/devtools/tests/route.ts +0 -134
  41. package/dist/templates/app/api/health/route.ts +0 -29
  42. package/dist/templates/app/api/internal/user-metadata/route.ts +0 -36
  43. package/dist/templates/app/api/superadmin/subscriptions/route.ts +0 -310
  44. package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +0 -286
  45. package/dist/templates/app/api/superadmin/teams/route.ts +0 -188
  46. package/dist/templates/app/api/superadmin/users/[userId]/route.ts +0 -540
  47. package/dist/templates/app/api/superadmin/users/route.ts +0 -323
  48. package/dist/templates/app/api/user/delete-account/route.ts +0 -55
  49. package/dist/templates/app/api/user/plan-flags/route.ts +0 -283
  50. package/dist/templates/app/api/user/profile/route.ts +0 -133
  51. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +0 -210
  52. package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +0 -331
  53. package/dist/templates/app/api/v1/[entity]/[id]/route.ts +0 -35
  54. package/dist/templates/app/api/v1/[entity]/docs.md +0 -369
  55. package/dist/templates/app/api/v1/[entity]/presets.ts +0 -194
  56. package/dist/templates/app/api/v1/[entity]/route.ts +0 -31
  57. package/dist/templates/app/api/v1/api-keys/[id]/route.ts +0 -303
  58. package/dist/templates/app/api/v1/api-keys/docs.md +0 -101
  59. package/dist/templates/app/api/v1/api-keys/presets.ts +0 -31
  60. package/dist/templates/app/api/v1/api-keys/route.ts +0 -250
  61. package/dist/templates/app/api/v1/auth/docs.md +0 -184
  62. package/dist/templates/app/api/v1/auth/presets.ts +0 -44
  63. package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +0 -227
  64. package/dist/templates/app/api/v1/billing/cancel/route.ts +0 -206
  65. package/dist/templates/app/api/v1/billing/change-plan/route.ts +0 -97
  66. package/dist/templates/app/api/v1/billing/check-action/route.ts +0 -81
  67. package/dist/templates/app/api/v1/billing/checkout/route.ts +0 -124
  68. package/dist/templates/app/api/v1/billing/docs.md +0 -209
  69. package/dist/templates/app/api/v1/billing/plans/route.ts +0 -85
  70. package/dist/templates/app/api/v1/billing/portal/route.ts +0 -90
  71. package/dist/templates/app/api/v1/billing/presets.ts +0 -121
  72. package/dist/templates/app/api/v1/billing/webhooks/stripe/route.ts +0 -428
  73. package/dist/templates/app/api/v1/blocks/[slug]/route.ts +0 -29
  74. package/dist/templates/app/api/v1/blocks/docs.md +0 -173
  75. package/dist/templates/app/api/v1/blocks/presets.ts +0 -121
  76. package/dist/templates/app/api/v1/blocks/route.ts +0 -45
  77. package/dist/templates/app/api/v1/blocks/validate/route.ts +0 -45
  78. package/dist/templates/app/api/v1/cron/docs.md +0 -116
  79. package/dist/templates/app/api/v1/cron/presets.ts +0 -26
  80. package/dist/templates/app/api/v1/cron/process/route.ts +0 -108
  81. package/dist/templates/app/api/v1/devtools/blocks/route.ts +0 -82
  82. package/dist/templates/app/api/v1/devtools/docs/route.ts +0 -150
  83. package/dist/templates/app/api/v1/devtools/docs.md +0 -204
  84. package/dist/templates/app/api/v1/devtools/features/route.ts +0 -61
  85. package/dist/templates/app/api/v1/devtools/flows/route.ts +0 -61
  86. package/dist/templates/app/api/v1/devtools/presets.ts +0 -113
  87. package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +0 -120
  88. package/dist/templates/app/api/v1/devtools/testing/route.ts +0 -82
  89. package/dist/templates/app/api/v1/media/docs.md +0 -117
  90. package/dist/templates/app/api/v1/media/presets.ts +0 -24
  91. package/dist/templates/app/api/v1/media/upload/route.ts +0 -150
  92. package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +0 -116
  93. package/dist/templates/app/api/v1/plugin/[...path]/route.ts +0 -373
  94. package/dist/templates/app/api/v1/plugin/docs.md +0 -79
  95. package/dist/templates/app/api/v1/plugin/presets.ts +0 -21
  96. package/dist/templates/app/api/v1/plugin/route.ts +0 -96
  97. package/dist/templates/app/api/v1/post-categories/[id]/route.ts +0 -255
  98. package/dist/templates/app/api/v1/post-categories/docs.md +0 -134
  99. package/dist/templates/app/api/v1/post-categories/presets.ts +0 -78
  100. package/dist/templates/app/api/v1/post-categories/route.ts +0 -119
  101. package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +0 -179
  102. package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +0 -120
  103. package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +0 -89
  104. package/dist/templates/app/api/v1/team-invitations/docs.md +0 -88
  105. package/dist/templates/app/api/v1/team-invitations/presets.ts +0 -43
  106. package/dist/templates/app/api/v1/team-invitations/route.ts +0 -114
  107. package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +0 -171
  108. package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +0 -105
  109. package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +0 -125
  110. package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +0 -263
  111. package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +0 -358
  112. package/dist/templates/app/api/v1/teams/[teamId]/route.ts +0 -322
  113. package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +0 -50
  114. package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +0 -91
  115. package/dist/templates/app/api/v1/teams/docs.md +0 -320
  116. package/dist/templates/app/api/v1/teams/presets.ts +0 -178
  117. package/dist/templates/app/api/v1/teams/route.ts +0 -293
  118. package/dist/templates/app/api/v1/teams/switch/route.ts +0 -88
  119. package/dist/templates/app/api/v1/theme/[...path]/route.ts +0 -361
  120. package/dist/templates/app/api/v1/theme/docs.md +0 -74
  121. package/dist/templates/app/api/v1/theme/presets.ts +0 -21
  122. package/dist/templates/app/api/v1/theme/route.ts +0 -96
  123. package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +0 -363
  124. package/dist/templates/app/api/v1/users/[id]/route.ts +0 -302
  125. package/dist/templates/app/api/v1/users/docs.md +0 -93
  126. package/dist/templates/app/api/v1/users/presets.ts +0 -59
  127. package/dist/templates/app/api/v1/users/route.ts +0 -197
  128. package/dist/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +0 -117
  129. package/dist/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +0 -103
  130. package/dist/templates/app/dashboard/(main)/[entity]/create/page.tsx +0 -95
  131. package/dist/templates/app/dashboard/(main)/[entity]/error.tsx +0 -51
  132. package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +0 -113
  133. package/dist/templates/app/dashboard/(main)/[entity]/loading.tsx +0 -61
  134. package/dist/templates/app/dashboard/(main)/[entity]/page.tsx +0 -90
  135. package/dist/templates/app/dashboard/(main)/layout.tsx +0 -98
  136. package/dist/templates/app/dashboard/(main)/loading.tsx +0 -5
  137. package/dist/templates/app/dashboard/(main)/page.tsx +0 -201
  138. package/dist/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +0 -114
  139. package/dist/templates/app/dashboard/(main)/patterns/[id]/page.tsx +0 -20
  140. package/dist/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +0 -171
  141. package/dist/templates/app/dashboard/(main)/patterns/create/page.tsx +0 -86
  142. package/dist/templates/app/dashboard/(main)/patterns/page.tsx +0 -444
  143. package/dist/templates/app/dashboard/features/analytics/page.tsx +0 -35
  144. package/dist/templates/app/dashboard/features/automation/page.tsx +0 -35
  145. package/dist/templates/app/dashboard/features/layout.tsx +0 -13
  146. package/dist/templates/app/dashboard/features/loading.tsx +0 -5
  147. package/dist/templates/app/dashboard/features/webhooks/page.tsx +0 -35
  148. package/dist/templates/app/dashboard/layout.tsx +0 -86
  149. package/dist/templates/app/dashboard/permission-denied/page.tsx +0 -29
  150. package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +0 -5
  151. package/dist/templates/app/dashboard/settings/api-keys/page.tsx +0 -513
  152. package/dist/templates/app/dashboard/settings/billing/loading.tsx +0 -5
  153. package/dist/templates/app/dashboard/settings/billing/page.tsx +0 -284
  154. package/dist/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +0 -222
  155. package/dist/templates/app/dashboard/settings/invoices/loading.tsx +0 -5
  156. package/dist/templates/app/dashboard/settings/invoices/page.tsx +0 -82
  157. package/dist/templates/app/dashboard/settings/layout.tsx +0 -151
  158. package/dist/templates/app/dashboard/settings/loading.tsx +0 -5
  159. package/dist/templates/app/dashboard/settings/notifications/loading.tsx +0 -5
  160. package/dist/templates/app/dashboard/settings/notifications/page.tsx +0 -462
  161. package/dist/templates/app/dashboard/settings/page.tsx +0 -92
  162. package/dist/templates/app/dashboard/settings/password/loading.tsx +0 -5
  163. package/dist/templates/app/dashboard/settings/password/page.tsx +0 -306
  164. package/dist/templates/app/dashboard/settings/plans/loading.tsx +0 -5
  165. package/dist/templates/app/dashboard/settings/plans/page.tsx +0 -40
  166. package/dist/templates/app/dashboard/settings/profile/loading.tsx +0 -5
  167. package/dist/templates/app/dashboard/settings/profile/page.tsx +0 -686
  168. package/dist/templates/app/dashboard/settings/security/loading.tsx +0 -5
  169. package/dist/templates/app/dashboard/settings/security/page.tsx +0 -505
  170. package/dist/templates/app/dashboard/settings/teams/loading.tsx +0 -5
  171. package/dist/templates/app/dashboard/settings/teams/page.tsx +0 -272
  172. package/dist/templates/app/dashboard/settings/teams/permissions/page.tsx +0 -92
  173. package/dist/templates/app/devtools/blocks/[slug]/page.tsx +0 -39
  174. package/dist/templates/app/devtools/blocks/page.tsx +0 -31
  175. package/dist/templates/app/devtools/config/page.tsx +0 -31
  176. package/dist/templates/app/devtools/features/page.tsx +0 -31
  177. package/dist/templates/app/devtools/flows/page.tsx +0 -31
  178. package/dist/templates/app/devtools/layout.tsx +0 -58
  179. package/dist/templates/app/devtools/page.tsx +0 -121
  180. package/dist/templates/app/devtools/scheduled-actions/page.tsx +0 -157
  181. package/dist/templates/app/devtools/style/page.tsx +0 -330
  182. package/dist/templates/app/devtools/tags/page.tsx +0 -31
  183. package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +0 -47
  184. package/dist/templates/app/favicon.ico +0 -0
  185. package/dist/templates/app/globals.css +0 -12
  186. package/dist/templates/app/layout.tsx +0 -96
  187. package/dist/templates/app/public/page.tsx +0 -30
  188. package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +0 -92
  189. package/dist/templates/app/superadmin/docs/page.tsx +0 -75
  190. package/dist/templates/app/superadmin/layout.tsx +0 -67
  191. package/dist/templates/app/superadmin/page.tsx +0 -149
  192. package/dist/templates/app/superadmin/subscriptions/page.tsx +0 -655
  193. package/dist/templates/app/superadmin/team-roles/page.tsx +0 -493
  194. package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +0 -687
  195. package/dist/templates/app/superadmin/teams/page.tsx +0 -302
  196. package/dist/templates/app/superadmin/users/[userId]/page.tsx +0 -548
  197. package/dist/templates/app/superadmin/users/page.tsx +0 -528
  198. package/templates/app/(auth)/forgot-password/page.tsx +0 -216
  199. package/templates/app/(auth)/layout.tsx +0 -51
  200. package/templates/app/(auth)/login/page.tsx +0 -21
  201. package/templates/app/(auth)/reset-password/page.tsx +0 -212
  202. package/templates/app/(auth)/signup/page.tsx +0 -21
  203. package/templates/app/(auth)/verify-email/page.tsx +0 -190
  204. package/templates/app/(public)/[...slug]/page.tsx +0 -378
  205. package/templates/app/(public)/docs/[section]/[page]/page.tsx +0 -90
  206. package/templates/app/(public)/docs/layout.tsx +0 -25
  207. package/templates/app/(public)/docs/page.tsx +0 -81
  208. package/templates/app/(public)/layout.tsx +0 -41
  209. package/templates/app/(public)/page.tsx +0 -19
  210. package/templates/app/403/page.tsx +0 -89
  211. package/templates/app/api/auth/[...all]/route.ts +0 -78
  212. package/templates/app/api/cron/billing/lifecycle/route.ts +0 -98
  213. package/templates/app/api/csp-report/route.ts +0 -175
  214. package/templates/app/api/devtools/config/entities/route.ts +0 -108
  215. package/templates/app/api/devtools/config/theme/route.ts +0 -66
  216. package/templates/app/api/devtools/tests/[...path]/route.ts +0 -130
  217. package/templates/app/api/devtools/tests/route.ts +0 -134
  218. package/templates/app/api/health/route.ts +0 -29
  219. package/templates/app/api/internal/user-metadata/route.ts +0 -36
  220. package/templates/app/api/superadmin/subscriptions/route.ts +0 -310
  221. package/templates/app/api/superadmin/teams/[teamId]/route.ts +0 -286
  222. package/templates/app/api/superadmin/teams/route.ts +0 -188
  223. package/templates/app/api/superadmin/users/[userId]/route.ts +0 -540
  224. package/templates/app/api/superadmin/users/route.ts +0 -323
  225. package/templates/app/api/user/delete-account/route.ts +0 -55
  226. package/templates/app/api/user/plan-flags/route.ts +0 -283
  227. package/templates/app/api/user/profile/route.ts +0 -133
  228. package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +0 -210
  229. package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +0 -331
  230. package/templates/app/api/v1/[entity]/[id]/route.ts +0 -35
  231. package/templates/app/api/v1/[entity]/docs.md +0 -369
  232. package/templates/app/api/v1/[entity]/presets.ts +0 -194
  233. package/templates/app/api/v1/[entity]/route.ts +0 -31
  234. package/templates/app/api/v1/api-keys/[id]/route.ts +0 -303
  235. package/templates/app/api/v1/api-keys/docs.md +0 -101
  236. package/templates/app/api/v1/api-keys/presets.ts +0 -31
  237. package/templates/app/api/v1/api-keys/route.ts +0 -250
  238. package/templates/app/api/v1/auth/docs.md +0 -184
  239. package/templates/app/api/v1/auth/presets.ts +0 -44
  240. package/templates/app/api/v1/auth/signup-with-invite/route.ts +0 -227
  241. package/templates/app/api/v1/billing/cancel/route.ts +0 -206
  242. package/templates/app/api/v1/billing/change-plan/route.ts +0 -97
  243. package/templates/app/api/v1/billing/check-action/route.ts +0 -81
  244. package/templates/app/api/v1/billing/checkout/route.ts +0 -124
  245. package/templates/app/api/v1/billing/docs.md +0 -209
  246. package/templates/app/api/v1/billing/plans/route.ts +0 -85
  247. package/templates/app/api/v1/billing/portal/route.ts +0 -90
  248. package/templates/app/api/v1/billing/presets.ts +0 -121
  249. package/templates/app/api/v1/billing/webhooks/stripe/route.ts +0 -428
  250. package/templates/app/api/v1/blocks/[slug]/route.ts +0 -29
  251. package/templates/app/api/v1/blocks/docs.md +0 -173
  252. package/templates/app/api/v1/blocks/presets.ts +0 -121
  253. package/templates/app/api/v1/blocks/route.ts +0 -45
  254. package/templates/app/api/v1/blocks/validate/route.ts +0 -45
  255. package/templates/app/api/v1/cron/docs.md +0 -116
  256. package/templates/app/api/v1/cron/presets.ts +0 -26
  257. package/templates/app/api/v1/cron/process/route.ts +0 -108
  258. package/templates/app/api/v1/devtools/blocks/route.ts +0 -82
  259. package/templates/app/api/v1/devtools/docs/route.ts +0 -150
  260. package/templates/app/api/v1/devtools/docs.md +0 -204
  261. package/templates/app/api/v1/devtools/features/route.ts +0 -61
  262. package/templates/app/api/v1/devtools/flows/route.ts +0 -61
  263. package/templates/app/api/v1/devtools/presets.ts +0 -113
  264. package/templates/app/api/v1/devtools/scheduled-actions/route.ts +0 -120
  265. package/templates/app/api/v1/devtools/testing/route.ts +0 -82
  266. package/templates/app/api/v1/media/docs.md +0 -117
  267. package/templates/app/api/v1/media/presets.ts +0 -24
  268. package/templates/app/api/v1/media/upload/route.ts +0 -150
  269. package/templates/app/api/v1/patterns/[id]/usages/route.ts +0 -116
  270. package/templates/app/api/v1/plugin/[...path]/route.ts +0 -373
  271. package/templates/app/api/v1/plugin/docs.md +0 -79
  272. package/templates/app/api/v1/plugin/presets.ts +0 -21
  273. package/templates/app/api/v1/plugin/route.ts +0 -96
  274. package/templates/app/api/v1/post-categories/[id]/route.ts +0 -255
  275. package/templates/app/api/v1/post-categories/docs.md +0 -134
  276. package/templates/app/api/v1/post-categories/presets.ts +0 -78
  277. package/templates/app/api/v1/post-categories/route.ts +0 -119
  278. package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +0 -179
  279. package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +0 -120
  280. package/templates/app/api/v1/team-invitations/[token]/route.ts +0 -89
  281. package/templates/app/api/v1/team-invitations/docs.md +0 -88
  282. package/templates/app/api/v1/team-invitations/presets.ts +0 -43
  283. package/templates/app/api/v1/team-invitations/route.ts +0 -114
  284. package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +0 -171
  285. package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +0 -105
  286. package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +0 -125
  287. package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +0 -263
  288. package/templates/app/api/v1/teams/[teamId]/members/route.ts +0 -358
  289. package/templates/app/api/v1/teams/[teamId]/route.ts +0 -322
  290. package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +0 -50
  291. package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +0 -91
  292. package/templates/app/api/v1/teams/docs.md +0 -320
  293. package/templates/app/api/v1/teams/presets.ts +0 -178
  294. package/templates/app/api/v1/teams/route.ts +0 -293
  295. package/templates/app/api/v1/teams/switch/route.ts +0 -88
  296. package/templates/app/api/v1/theme/[...path]/route.ts +0 -361
  297. package/templates/app/api/v1/theme/docs.md +0 -74
  298. package/templates/app/api/v1/theme/presets.ts +0 -21
  299. package/templates/app/api/v1/theme/route.ts +0 -96
  300. package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +0 -363
  301. package/templates/app/api/v1/users/[id]/route.ts +0 -302
  302. package/templates/app/api/v1/users/docs.md +0 -93
  303. package/templates/app/api/v1/users/presets.ts +0 -59
  304. package/templates/app/api/v1/users/route.ts +0 -197
  305. package/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +0 -117
  306. package/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +0 -103
  307. package/templates/app/dashboard/(main)/[entity]/create/page.tsx +0 -95
  308. package/templates/app/dashboard/(main)/[entity]/error.tsx +0 -51
  309. package/templates/app/dashboard/(main)/[entity]/layout.tsx +0 -113
  310. package/templates/app/dashboard/(main)/[entity]/loading.tsx +0 -61
  311. package/templates/app/dashboard/(main)/[entity]/page.tsx +0 -90
  312. package/templates/app/dashboard/(main)/layout.tsx +0 -98
  313. package/templates/app/dashboard/(main)/loading.tsx +0 -5
  314. package/templates/app/dashboard/(main)/page.tsx +0 -201
  315. package/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +0 -114
  316. package/templates/app/dashboard/(main)/patterns/[id]/page.tsx +0 -20
  317. package/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +0 -171
  318. package/templates/app/dashboard/(main)/patterns/create/page.tsx +0 -86
  319. package/templates/app/dashboard/(main)/patterns/page.tsx +0 -444
  320. package/templates/app/dashboard/features/analytics/page.tsx +0 -35
  321. package/templates/app/dashboard/features/automation/page.tsx +0 -35
  322. package/templates/app/dashboard/features/layout.tsx +0 -13
  323. package/templates/app/dashboard/features/loading.tsx +0 -5
  324. package/templates/app/dashboard/features/webhooks/page.tsx +0 -35
  325. package/templates/app/dashboard/layout.tsx +0 -86
  326. package/templates/app/dashboard/permission-denied/page.tsx +0 -29
  327. package/templates/app/dashboard/settings/api-keys/loading.tsx +0 -5
  328. package/templates/app/dashboard/settings/api-keys/page.tsx +0 -513
  329. package/templates/app/dashboard/settings/billing/loading.tsx +0 -5
  330. package/templates/app/dashboard/settings/billing/page.tsx +0 -284
  331. package/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +0 -222
  332. package/templates/app/dashboard/settings/invoices/loading.tsx +0 -5
  333. package/templates/app/dashboard/settings/invoices/page.tsx +0 -82
  334. package/templates/app/dashboard/settings/layout.tsx +0 -151
  335. package/templates/app/dashboard/settings/loading.tsx +0 -5
  336. package/templates/app/dashboard/settings/notifications/loading.tsx +0 -5
  337. package/templates/app/dashboard/settings/notifications/page.tsx +0 -462
  338. package/templates/app/dashboard/settings/page.tsx +0 -92
  339. package/templates/app/dashboard/settings/password/loading.tsx +0 -5
  340. package/templates/app/dashboard/settings/password/page.tsx +0 -306
  341. package/templates/app/dashboard/settings/plans/loading.tsx +0 -5
  342. package/templates/app/dashboard/settings/plans/page.tsx +0 -40
  343. package/templates/app/dashboard/settings/profile/loading.tsx +0 -5
  344. package/templates/app/dashboard/settings/profile/page.tsx +0 -686
  345. package/templates/app/dashboard/settings/security/loading.tsx +0 -5
  346. package/templates/app/dashboard/settings/security/page.tsx +0 -505
  347. package/templates/app/dashboard/settings/teams/loading.tsx +0 -5
  348. package/templates/app/dashboard/settings/teams/page.tsx +0 -272
  349. package/templates/app/dashboard/settings/teams/permissions/page.tsx +0 -92
  350. package/templates/app/devtools/blocks/[slug]/page.tsx +0 -39
  351. package/templates/app/devtools/blocks/page.tsx +0 -31
  352. package/templates/app/devtools/config/page.tsx +0 -31
  353. package/templates/app/devtools/features/page.tsx +0 -31
  354. package/templates/app/devtools/flows/page.tsx +0 -31
  355. package/templates/app/devtools/layout.tsx +0 -58
  356. package/templates/app/devtools/page.tsx +0 -121
  357. package/templates/app/devtools/scheduled-actions/page.tsx +0 -157
  358. package/templates/app/devtools/style/page.tsx +0 -330
  359. package/templates/app/devtools/tags/page.tsx +0 -31
  360. package/templates/app/devtools/tests/[[...path]]/page.tsx +0 -47
  361. package/templates/app/favicon.ico +0 -0
  362. package/templates/app/globals.css +0 -12
  363. package/templates/app/layout.tsx +0 -96
  364. package/templates/app/public/page.tsx +0 -30
  365. package/templates/app/superadmin/docs/[section]/[page]/page.tsx +0 -92
  366. package/templates/app/superadmin/docs/page.tsx +0 -75
  367. package/templates/app/superadmin/layout.tsx +0 -67
  368. package/templates/app/superadmin/page.tsx +0 -149
  369. package/templates/app/superadmin/subscriptions/page.tsx +0 -655
  370. package/templates/app/superadmin/team-roles/page.tsx +0 -493
  371. package/templates/app/superadmin/teams/[teamId]/page.tsx +0 -687
  372. package/templates/app/superadmin/teams/page.tsx +0 -302
  373. package/templates/app/superadmin/users/[userId]/page.tsx +0 -548
  374. 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
- )