@devvistatech/devvista-kit 0.0.10 → 0.0.13

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 (337) hide show
  1. package/CHANGELOG.md +12 -12
  2. package/LICENSE +6 -6
  3. package/README.md +55 -15
  4. package/app/ClientLayout.tsx +66 -0
  5. package/app/about/page.tsx +61 -298
  6. package/app/adRequest/page.tsx +625 -549
  7. package/app/admin-profile/page.tsx +123 -0
  8. package/app/analytics/page.tsx +382 -346
  9. package/app/api/about/route.ts +290 -306
  10. package/app/api/adRequest/route.ts +547 -567
  11. package/app/api/analytics/[reportType]/route.ts +274 -337
  12. package/app/api/bio/route.ts +297 -313
  13. package/app/api/blog/route.ts +288 -306
  14. package/app/api/chat/route.ts +14 -14
  15. package/app/api/contact/route.ts +409 -409
  16. package/app/api/contacts/route.ts +179 -224
  17. package/app/api/files/route.ts +415 -429
  18. package/app/api/gallery-data/route.ts +727 -735
  19. package/app/api/schedule/route.ts +439 -455
  20. package/app/api/signup/route.ts +129 -0
  21. package/app/api/sync-user/route.ts +306 -132
  22. package/app/api/trial-request/route.ts +297 -297
  23. package/app/api/verify-admin/route.ts +46 -0
  24. package/app/blog/[id]/page.tsx +307 -288
  25. package/app/blog/page.tsx +249 -216
  26. package/app/contact/page.tsx +284 -284
  27. package/app/faq/page.tsx +191 -191
  28. package/app/favicon.ico +0 -0
  29. package/app/gallery/page.tsx +336 -315
  30. package/app/globals.css +58 -58
  31. package/app/layout.tsx +59 -110
  32. package/app/not-found.tsx +20 -20
  33. package/app/page.tsx +47 -338
  34. package/app/products/constants/product.ts +27 -0
  35. package/app/products/page.tsx +296 -0
  36. package/app/products/productOne/page.tsx +266 -0
  37. package/app/products/productTwo/page.tsx +272 -0
  38. package/app/schedule/page.tsx +698 -660
  39. package/bin/init.js +207 -219
  40. package/components/addOns/functional/CalendlyWidget.tsx +107 -107
  41. package/components/addOns/functional/ClassList.tsx +149 -145
  42. package/components/addOns/functional/ClassPopup.tsx +398 -398
  43. package/components/addOns/functional/ContactForm.tsx +284 -284
  44. package/components/addOns/functional/NewUserAnalytics.tsx +100 -100
  45. package/components/addOns/functional/ProductList.tsx +1027 -0
  46. package/components/addOns/functional/aboutSections/AboutSection.tsx +581 -544
  47. package/components/addOns/functional/aboutSections/constants/aboutSection.ts +70 -65
  48. package/components/addOns/functional/banner/Banner.tsx +150 -0
  49. package/components/addOns/functional/banner/BannerDashboard.tsx +283 -0
  50. package/components/addOns/functional/bioSections/BioEditor.tsx +471 -0
  51. package/components/addOns/functional/bioSections/constants/bioEditor.ts +36 -0
  52. package/components/addOns/functional/blogSections/BlogDashboard.tsx +184 -184
  53. package/components/addOns/functional/blogSections/BlogFormPopUp.tsx +555 -554
  54. package/components/addOns/functional/blogSections/BlogList.tsx +148 -148
  55. package/components/addOns/functional/blogSections/BlogSidebar.tsx +58 -58
  56. package/components/addOns/functional/blogSections/constants/blogDashboard.ts +28 -28
  57. package/components/addOns/functional/blogSections/constants/blogFormPopUp.ts +97 -97
  58. package/components/addOns/functional/blogSections/constants/blogList.ts +22 -22
  59. package/components/addOns/functional/blogSections/constants/blogSidebar.ts +15 -15
  60. package/components/addOns/functional/{ImageDescCarousel.tsx → carousels/ImageDescCarousel.tsx} +839 -730
  61. package/components/addOns/functional/carousels/ProductDescCarousel.tsx +1129 -0
  62. package/components/addOns/functional/{ScheduleCarousel.tsx → carousels/ScheduleCarousel.tsx} +231 -171
  63. package/components/addOns/functional/carousels/constants.ts/productDescCarousel.ts +197 -0
  64. package/components/addOns/functional/carousels/constants.ts/scheduleCarousel.ts +20 -0
  65. package/components/addOns/functional/contactsDashboard/ContactsDashboard.tsx +366 -366
  66. package/components/addOns/functional/contactsDashboard/constants/contactsDashboard.ts +70 -70
  67. package/components/addOns/functional/fileUploaders/FileUploader.tsx +437 -0
  68. package/components/addOns/functional/fileUploaders/constants/fileUploader.ts +45 -0
  69. package/components/addOns/functional/galleries/GalleryComplex.tsx +1037 -836
  70. package/components/addOns/functional/galleries/GallerySimple.tsx +537 -509
  71. package/components/addOns/functional/galleries/ThreeSetGallery.tsx +260 -0
  72. package/components/addOns/functional/galleries/constants/galleryComplex.ts +106 -106
  73. package/components/addOns/functional/galleries/constants/gallerySimple.ts +76 -76
  74. package/components/addOns/functional/schedules/ScheduleGridOne.tsx +276 -262
  75. package/components/addOns/functional/schedules/ScheduleGridTwo.tsx +299 -294
  76. package/components/addOns/functional/schedules/ScheduleGridTwoBasic.tsx +293 -288
  77. package/components/addOns/functional/schedules/SchedulerForm.tsx +428 -428
  78. package/components/addOns/functional/schedules/constants/ScheduleGridTwo.ts +40 -40
  79. package/components/addOns/functional/schedules/constants/ScheduleGridTwoBasic.ts +40 -40
  80. package/components/addOns/functional/schedules/constants/SchedulerForm.ts +65 -65
  81. package/components/addOns/functional/schedules/constants/scheduleGridOne.ts +54 -54
  82. package/components/addOns/non-functional/AnnouncementBanner.tsx +46 -46
  83. package/components/addOns/non-functional/IconBubble.tsx +49 -49
  84. package/components/addOns/non-functional/SampleCarousel.tsx +204 -204
  85. package/components/addOns/non-functional/Testimonials.tsx +334 -334
  86. package/components/addOns/non-functional/ThreeSetGallery.tsx +63 -63
  87. package/components/addOns/non-functional/aboutSections/AboutSection.tsx +62 -62
  88. package/components/addOns/non-functional/aboutSections/constants/aboutSection.ts +24 -24
  89. package/components/addOns/non-functional/featureSections/FeaturesSection.tsx +74 -0
  90. package/components/addOns/non-functional/featureSections/constants/featuresSection.ts +30 -0
  91. package/components/addOns/non-functional/{Heros/HeroSection.tsx → heros/HomeHero.tsx} +144 -142
  92. package/components/addOns/non-functional/heros/ProductHero.tsx +111 -0
  93. package/components/addOns/non-functional/heros/constants/hero.ts +62 -0
  94. package/components/addOns/non-functional/imageCarousels/ProductSlider.tsx +117 -117
  95. package/components/addOns/non-functional/imageCarousels/ProgramCarousel.tsx +232 -232
  96. package/components/addOns/non-functional/imageCarousels/constants/programCarousel.ts +39 -39
  97. package/components/addOns/non-functional/imageCarousels/constants/programSlider.ts +36 -36
  98. package/components/addOns/non-functional/spinner.tsx +21 -21
  99. package/components/footers/footer.tsx +416 -453
  100. package/components/navBars/navbar.tsx +310 -310
  101. package/components/other/accordion.tsx +58 -58
  102. package/components/other/admin-menu.tsx +68 -68
  103. package/components/other/alert-dialog.tsx +141 -141
  104. package/components/other/alert.tsx +59 -59
  105. package/components/other/aspect-ratio.tsx +7 -7
  106. package/components/other/avatar.tsx +50 -50
  107. package/components/other/badge.tsx +36 -36
  108. package/components/other/breadcrumb.tsx +115 -115
  109. package/components/other/button.tsx +738 -738
  110. package/components/other/calendar.tsx +66 -66
  111. package/components/other/card.tsx +86 -86
  112. package/components/other/carousel.tsx +274 -274
  113. package/components/other/chart.tsx +363 -363
  114. package/components/other/checkbox.tsx +30 -30
  115. package/components/other/collapsible.tsx +11 -11
  116. package/components/other/command.tsx +155 -155
  117. package/components/other/context-menu.tsx +200 -200
  118. package/components/other/dialog.tsx +122 -122
  119. package/components/other/drawer.tsx +118 -118
  120. package/components/other/dropdown-menu.tsx +200 -200
  121. package/components/other/form.tsx +179 -179
  122. package/components/other/hover-card.tsx +29 -29
  123. package/components/other/input-otp.tsx +71 -71
  124. package/components/other/input.tsx +25 -25
  125. package/components/other/label.tsx +26 -26
  126. package/components/other/menubar.tsx +236 -236
  127. package/components/other/mobile-icon.tsx +21 -21
  128. package/components/other/navigation-menu.tsx +128 -128
  129. package/components/other/pagination.tsx +117 -117
  130. package/components/other/popover.tsx +31 -31
  131. package/components/other/progress.tsx +28 -28
  132. package/components/other/radio-group.tsx +44 -44
  133. package/components/other/resizable.tsx +45 -45
  134. package/components/other/scroll-area.tsx +48 -48
  135. package/components/other/select.tsx +160 -160
  136. package/components/other/separator.tsx +31 -31
  137. package/components/other/sheet.tsx +140 -140
  138. package/components/other/skeleton.tsx +15 -15
  139. package/components/other/slider.tsx +28 -28
  140. package/components/other/social-icons.tsx +39 -39
  141. package/components/other/sonner.tsx +31 -31
  142. package/components/other/switch.tsx +29 -29
  143. package/components/other/table.tsx +117 -117
  144. package/components/other/tabs.tsx +55 -55
  145. package/components/other/textarea.tsx +24 -24
  146. package/components/other/toast.tsx +122 -122
  147. package/components/other/toaster.tsx +35 -35
  148. package/components/other/toggle-group.tsx +61 -61
  149. package/components/other/toggle.tsx +45 -45
  150. package/components/other/tooltip.tsx +30 -30
  151. package/components/theme-provider.tsx +8 -8
  152. package/hooks/use-toast.ts +188 -188
  153. package/lib/auth/auth-context.tsx +225 -0
  154. package/lib/auth/auth-utils.tsx +30 -0
  155. package/lib/constants/about.ts +34 -34
  156. package/lib/constants/adRequest.ts +256 -113
  157. package/lib/constants/admin-profile.ts +12 -0
  158. package/lib/constants/contact.ts +40 -40
  159. package/lib/constants/faq.ts +34 -34
  160. package/lib/constants/gallery.ts +42 -42
  161. package/lib/constants/page.ts +69 -69
  162. package/lib/constants/schedule.ts +71 -71
  163. package/lib/google/google-analytics-tracking.tsx +44 -0
  164. package/lib/{google-analytics.tsx → google/google-analytics.tsx} +97 -97
  165. package/lib/types.ts +235 -0
  166. package/lib/utils/compressImage.tsx +32 -0
  167. package/middleware.ts +46 -42
  168. package/netlify.toml +5 -5
  169. package/next.config.js +10 -10
  170. package/package.json +117 -116
  171. package/public/images/test.png +0 -0
  172. package/tailwind.config.ts +89 -89
  173. package/tsconfig.json +23 -23
  174. package/components/addOns/functional/BioEditor.tsx +0 -447
  175. package/components/addOns/functional/FileUploader.tsx +0 -295
  176. package/components/addOns/non-functional/FeaturesSection.tsx +0 -63
  177. package/components/types.ts +0 -50
  178. package/dist/.next/types/app/api/about/route.js +0 -52
  179. package/dist/.next/types/app/api/blog/route.js +0 -52
  180. package/dist/.next/types/app/api/files/route.js +0 -52
  181. package/dist/.next/types/app/api/schedule/route.js +0 -52
  182. package/dist/.next/types/app/api/sync-user/route.js +0 -52
  183. package/dist/.next/types/app/layout.js +0 -22
  184. package/dist/.next/types/app/page.js +0 -22
  185. package/dist/app/about/page.jsx +0 -258
  186. package/dist/app/adRequest/page.jsx +0 -531
  187. package/dist/app/analytics/page.jsx +0 -298
  188. package/dist/app/api/about/route.js +0 -285
  189. package/dist/app/api/adRequest/route.js +0 -440
  190. package/dist/app/api/analytics/[reportType]/route.js +0 -357
  191. package/dist/app/api/bio/route.js +0 -293
  192. package/dist/app/api/blog/route.js +0 -366
  193. package/dist/app/api/chat/route.js +0 -58
  194. package/dist/app/api/contact/route.js +0 -163
  195. package/dist/app/api/contacts/route.js +0 -234
  196. package/dist/app/api/files/route.js +0 -444
  197. package/dist/app/api/gallery-data/route.js +0 -719
  198. package/dist/app/api/schedule/route.js +0 -461
  199. package/dist/app/api/sync-user/route.js +0 -186
  200. package/dist/app/api/trial-request/route.js +0 -165
  201. package/dist/app/blog/[id]/page.jsx +0 -312
  202. package/dist/app/blog/page.jsx +0 -210
  203. package/dist/app/constants/about.js +0 -32
  204. package/dist/app/constants/adRequest.js +0 -113
  205. package/dist/app/constants/contact.js +0 -40
  206. package/dist/app/constants/faq.js +0 -36
  207. package/dist/app/constants/gallery.js +0 -42
  208. package/dist/app/constants/page.js +0 -69
  209. package/dist/app/constants/schedule.js +0 -71
  210. package/dist/app/contact/page.jsx +0 -119
  211. package/dist/app/faq/page.jsx +0 -97
  212. package/dist/app/gallery/page.jsx +0 -281
  213. package/dist/app/layout.jsx +0 -45
  214. package/dist/app/not-found.jsx +0 -14
  215. package/dist/app/page.jsx +0 -324
  216. package/dist/app/schedule/page.jsx +0 -500
  217. package/dist/components/addOns/functional/BioEditor.jsx +0 -187
  218. package/dist/components/addOns/functional/CalendlyWidget.jsx +0 -61
  219. package/dist/components/addOns/functional/ClassList.jsx +0 -158
  220. package/dist/components/addOns/functional/ClassPopup.jsx +0 -300
  221. package/dist/components/addOns/functional/ContactForm.jsx +0 -219
  222. package/dist/components/addOns/functional/FileUploader.jsx +0 -222
  223. package/dist/components/addOns/functional/ImageDescCarousel.jsx +0 -491
  224. package/dist/components/addOns/functional/NewUserAnalytics.jsx +0 -71
  225. package/dist/components/addOns/functional/ScheduleCarousel.jsx +0 -68
  226. package/dist/components/addOns/functional/aboutSections/AboutSection.jsx +0 -372
  227. package/dist/components/addOns/functional/aboutSections/constants/aboutSection.js +0 -65
  228. package/dist/components/addOns/functional/blogSections/BlogDashboard.jsx +0 -111
  229. package/dist/components/addOns/functional/blogSections/BlogFormPopUp.jsx +0 -465
  230. package/dist/components/addOns/functional/blogSections/BlogList.jsx +0 -170
  231. package/dist/components/addOns/functional/blogSections/BlogSidebar.jsx +0 -35
  232. package/dist/components/addOns/functional/blogSections/constants/blogDashboard.js +0 -28
  233. package/dist/components/addOns/functional/blogSections/constants/blogFormPopUp.js +0 -97
  234. package/dist/components/addOns/functional/blogSections/constants/blogList.js +0 -22
  235. package/dist/components/addOns/functional/blogSections/constants/blogSidebar.js +0 -15
  236. package/dist/components/addOns/functional/contactsDashboard/ContactsDashboard.jsx +0 -355
  237. package/dist/components/addOns/functional/contactsDashboard/constants/contactsDashboard.js +0 -70
  238. package/dist/components/addOns/functional/galleries/GalleryComplex.jsx +0 -605
  239. package/dist/components/addOns/functional/galleries/GallerySimple.jsx +0 -363
  240. package/dist/components/addOns/functional/galleries/constants/galleryComplex.js +0 -106
  241. package/dist/components/addOns/functional/galleries/constants/gallerySimple.js +0 -76
  242. package/dist/components/addOns/functional/schedules/ScheduleGridOne.jsx +0 -167
  243. package/dist/components/addOns/functional/schedules/ScheduleGridTwo.jsx +0 -100
  244. package/dist/components/addOns/functional/schedules/ScheduleGridTwoBasic.jsx +0 -97
  245. package/dist/components/addOns/functional/schedules/SchedulerForm.jsx +0 -188
  246. package/dist/components/addOns/functional/schedules/constants/ScheduleGridTwo.js +0 -40
  247. package/dist/components/addOns/functional/schedules/constants/ScheduleGridTwoBasic.js +0 -40
  248. package/dist/components/addOns/functional/schedules/constants/SchedulerForm.js +0 -65
  249. package/dist/components/addOns/functional/schedules/constants/scheduleGridOne.js +0 -54
  250. package/dist/components/addOns/non-functional/AnnouncementBanner.jsx +0 -24
  251. package/dist/components/addOns/non-functional/FeaturesSection.jsx +0 -38
  252. package/dist/components/addOns/non-functional/HeroSection.jsx +0 -71
  253. package/dist/components/addOns/non-functional/Heros/HeroSection.jsx +0 -71
  254. package/dist/components/addOns/non-functional/IconBubble.jsx +0 -36
  255. package/dist/components/addOns/non-functional/SampleCarousel.jsx +0 -114
  256. package/dist/components/addOns/non-functional/Testimonials.jsx +0 -177
  257. package/dist/components/addOns/non-functional/ThreeSetGallery.jsx +0 -40
  258. package/dist/components/addOns/non-functional/aboutSections/AboutSection.jsx +0 -35
  259. package/dist/components/addOns/non-functional/aboutSections/constants/aboutSection.js +0 -24
  260. package/dist/components/addOns/non-functional/imageCarousels/ProductSlider.jsx +0 -80
  261. package/dist/components/addOns/non-functional/imageCarousels/ProgramCarousel.jsx +0 -155
  262. package/dist/components/addOns/non-functional/imageCarousels/constants/programCarousel.js +0 -39
  263. package/dist/components/addOns/non-functional/imageCarousels/constants/programSlider.js +0 -36
  264. package/dist/components/addOns/non-functional/spinner.jsx +0 -13
  265. package/dist/components/footers/footer.jsx +0 -217
  266. package/dist/components/navBars/navbar.jsx +0 -159
  267. package/dist/components/other/accordion.jsx +0 -40
  268. package/dist/components/other/admin-menu.jsx +0 -34
  269. package/dist/components/other/alert-dialog.jsx +0 -64
  270. package/dist/components/other/alert.jsx +0 -41
  271. package/dist/components/other/aspect-ratio.jsx +0 -4
  272. package/dist/components/other/avatar.jsx +0 -31
  273. package/dist/components/other/badge.jsx +0 -32
  274. package/dist/components/other/breadcrumb.jsx +0 -57
  275. package/dist/components/other/button.jsx +0 -322
  276. package/dist/components/other/calendar.jsx +0 -43
  277. package/dist/components/other/card.jsx +0 -44
  278. package/dist/components/other/carousel.jsx +0 -140
  279. package/dist/components/other/chart.jsx +0 -182
  280. package/dist/components/other/checkbox.jsx +0 -26
  281. package/dist/components/other/collapsible.jsx +0 -6
  282. package/dist/components/other/command.jsx +0 -68
  283. package/dist/components/other/context-menu.jsx +0 -88
  284. package/dist/components/other/dialog.jsx +0 -60
  285. package/dist/components/other/drawer.jsx +0 -60
  286. package/dist/components/other/dropdown-menu.jsx +0 -90
  287. package/dist/components/other/form.jsx +0 -89
  288. package/dist/components/other/hover-card.jsx +0 -23
  289. package/dist/components/other/input-otp.jsx +0 -46
  290. package/dist/components/other/input.jsx +0 -19
  291. package/dist/components/other/label.jsx +0 -23
  292. package/dist/components/other/login-popup.jsx +0 -1
  293. package/dist/components/other/menubar.jsx +0 -96
  294. package/dist/components/other/mobile-icon.jsx +0 -11
  295. package/dist/components/other/navigation-menu.jsx +0 -62
  296. package/dist/components/other/pagination.jsx +0 -63
  297. package/dist/components/other/popover.jsx +0 -25
  298. package/dist/components/other/progress.jsx +0 -23
  299. package/dist/components/other/radio-group.jsx +0 -31
  300. package/dist/components/other/resizable.jsx +0 -29
  301. package/dist/components/other/scroll-area.jsx +0 -36
  302. package/dist/components/other/select.jsx +0 -83
  303. package/dist/components/other/separator.jsx +0 -21
  304. package/dist/components/other/sheet.jsx +0 -74
  305. package/dist/components/other/signup-popup.jsx +0 -1
  306. package/dist/components/other/skeleton.jsx +0 -17
  307. package/dist/components/other/slider.jsx +0 -26
  308. package/dist/components/other/social-icons.jsx +0 -15
  309. package/dist/components/other/sonner.jsx +0 -27
  310. package/dist/components/other/switch.jsx +0 -23
  311. package/dist/components/other/table.jsx +0 -56
  312. package/dist/components/other/tabs.jsx +0 -32
  313. package/dist/components/other/textarea.jsx +0 -19
  314. package/dist/components/other/toast.jsx +0 -58
  315. package/dist/components/other/toaster.jsx +0 -31
  316. package/dist/components/other/toggle-group.jsx +0 -41
  317. package/dist/components/other/toggle.jsx +0 -39
  318. package/dist/components/other/tooltip.jsx +0 -24
  319. package/dist/components/theme-provider.jsx +0 -18
  320. package/dist/components/types.js +0 -1
  321. package/dist/hooks/use-toast.js +0 -135
  322. package/dist/lib/auth-context.jsx +0 -144
  323. package/dist/lib/constants/about.js +0 -32
  324. package/dist/lib/constants/adRequest.js +0 -113
  325. package/dist/lib/constants/contact.js +0 -40
  326. package/dist/lib/constants/faq.js +0 -36
  327. package/dist/lib/constants/gallery.js +0 -42
  328. package/dist/lib/constants/page.js +0 -69
  329. package/dist/lib/constants/schedule.js +0 -71
  330. package/dist/lib/google-analytics.jsx +0 -148
  331. package/dist/lib/utils.js +0 -9
  332. package/dist/lib/verify-user.js +0 -142
  333. package/dist/middleware.js +0 -37
  334. package/dist/tailwind.config.js +0 -86
  335. package/dist/tsconfig.tsbuildinfo +0 -1
  336. package/lib/auth-context.tsx +0 -131
  337. package/lib/verify-user.ts +0 -118
@@ -0,0 +1,129 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import crypto from "crypto";
3
+
4
+ export async function POST(req: NextRequest) {
5
+ const STRAPI_USER_LIST_API_URL = process.env.STRAPI_USER_LIST_API_URL;
6
+ const STRAPI_API_TOKEN = process.env.STRAPI_API_TOKEN;
7
+ const allowedOrigin = process.env.NEXT_PUBLIC_BASE_URL;
8
+
9
+ if (!STRAPI_USER_LIST_API_URL || !STRAPI_API_TOKEN || !allowedOrigin) {
10
+ console.error("POST /api/signup: Missing environment variables", {
11
+ hasStrapiUrl: !!STRAPI_USER_LIST_API_URL,
12
+ hasStrapiToken: !!STRAPI_API_TOKEN,
13
+ hasBaseUrl: !!allowedOrigin,
14
+ timestamp: new Date().toISOString(),
15
+ });
16
+ return NextResponse.json({ error: "Server configuration error" }, { status: 500 });
17
+ }
18
+
19
+ const headers = new Headers({
20
+ "Access-Control-Allow-Origin": allowedOrigin,
21
+ "Access-Control-Allow-Methods": "POST",
22
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
23
+ });
24
+
25
+ if (req.method === "OPTIONS") {
26
+ return new NextResponse(null, { status: 204, headers });
27
+ }
28
+
29
+ const requestId = crypto.randomUUID();
30
+ let body;
31
+ try {
32
+ body = await req.json();
33
+ } catch (error) {
34
+ console.error("POST /api/signup: Failed to parse request body", { requestId, error, timestamp: new Date().toISOString() });
35
+ return NextResponse.json({ error: "Invalid request body" }, { status: 400, headers });
36
+ }
37
+
38
+ const { authId, email, username } = body;
39
+
40
+ if (!authId || !email || !username) {
41
+ console.error("POST /api/signup: Missing required fields", { requestId, body, timestamp: new Date().toISOString() });
42
+ return NextResponse.json({ error: "Missing required fields: authId, email, or username" }, { status: 400, headers });
43
+ }
44
+
45
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
46
+ if (!emailRegex.test(email)) {
47
+ console.error("POST /api/signup: Invalid email format", { requestId, email, timestamp: new Date().toISOString() });
48
+ return NextResponse.json({ error: "Invalid email format" }, { status: 400, headers });
49
+ }
50
+
51
+ try {
52
+ // Check if user exists by authId or email
53
+ const checkResponse = await fetch(
54
+ `${STRAPI_USER_LIST_API_URL}?filters[$or][0][authId][$eq]=${encodeURIComponent(authId)}&filters[$or][1][email][$eq]=${encodeURIComponent(email)}`,
55
+ {
56
+ headers: {
57
+ Authorization: `Bearer ${STRAPI_API_TOKEN}`,
58
+ "Content-Type": "application/json",
59
+ },
60
+ }
61
+ );
62
+
63
+ if (!checkResponse.ok) {
64
+ console.error("POST /api/signup: Failed to check existing user:", {
65
+ requestId,
66
+ status: checkResponse.status,
67
+ timestamp: new Date().toISOString(),
68
+ });
69
+ return NextResponse.json({ error: "Failed to check existing user" }, { status: checkResponse.status, headers });
70
+ }
71
+
72
+ const checkResult = await checkResponse.json();
73
+ if (checkResult.data && checkResult.data.length > 0) {
74
+ return NextResponse.json(checkResult.data[0], { status: 200, headers });
75
+ }
76
+
77
+ // Create new Strapi user
78
+ const strapiUserData = {
79
+ data: {
80
+ authId,
81
+ email,
82
+ username,
83
+ userRole: "user",
84
+ businessAdminId: null,
85
+ publishedAt: new Date().toISOString(),
86
+ },
87
+ };
88
+
89
+ const createResponse = await fetch(STRAPI_USER_LIST_API_URL, {
90
+ method: "POST",
91
+ headers: {
92
+ Authorization: `Bearer ${STRAPI_API_TOKEN}`,
93
+ "Content-Type": "application/json",
94
+ },
95
+ body: JSON.stringify(strapiUserData),
96
+ });
97
+
98
+ if (!createResponse.ok) {
99
+ const errorData = await createResponse.json();
100
+ console.error("POST /api/signup: Failed to create Strapi user:", {
101
+ requestId,
102
+ status: createResponse.status,
103
+ errorData,
104
+ timestamp: new Date().toISOString(),
105
+ });
106
+ return NextResponse.json({ error: errorData.error?.message || "Failed to create user" }, { status: createResponse.status, headers });
107
+ }
108
+
109
+ const newStrapiUser = await createResponse.json();
110
+
111
+ return NextResponse.json(newStrapiUser.data, { status: 201, headers });
112
+ } catch (error) {
113
+ console.error("POST /api/signup: Error:", {
114
+ requestId,
115
+ error: error instanceof Error ? error.message : "Unknown error",
116
+ timestamp: new Date().toISOString(),
117
+ });
118
+ return NextResponse.json(
119
+ { error: "Internal server error", details: error instanceof Error ? error.message : "Unknown error" },
120
+ { status: 500, headers }
121
+ );
122
+ }
123
+ }
124
+
125
+ export const config = {
126
+ api: {
127
+ bodyParser: true,
128
+ },
129
+ };
@@ -1,132 +1,306 @@
1
- import { NextRequest, NextResponse } from "next/server";
2
- import { getAuth } from "@clerk/nextjs/server";
3
-
4
- export async function POST(req: NextRequest) {
5
- const { userId } = getAuth(req);
6
- if (!userId) {
7
- console.error("No userId, returning 401");
8
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
9
- }
10
-
11
- const clerkUserData = await req.json();
12
- const STRAPI_USER_LIST_API_URL = process.env.STRAPI_USER_LIST_API_URL;
13
- const STRAPI_API_TOKEN = process.env.STRAPI_API_TOKEN;
14
- const CLERK_SECRET_KEY = process.env.CLERK_SECRET_KEY;
15
-
16
- if (!STRAPI_USER_LIST_API_URL || !STRAPI_API_TOKEN || !CLERK_SECRET_KEY) {
17
- console.error("Missing environment variables:", {
18
- STRAPI_USER_LIST_API_URL,
19
- STRAPI_API_TOKEN: !!STRAPI_API_TOKEN,
20
- CLERK_SECRET_KEY: !!CLERK_SECRET_KEY,
21
- });
22
- return NextResponse.json(
23
- { error: "Server configuration error: Missing required environment variables" },
24
- { status: 500 }
25
- );
26
- }
27
-
28
- try {
29
- const clerkResponse = await fetch(`https://api.clerk.dev/v1/users/${userId}`, {
30
- headers: { Authorization: `Bearer ${CLERK_SECRET_KEY}` },
31
- });
32
- if (!clerkResponse.ok) {
33
- const errorText = await clerkResponse.text();
34
- console.error("Clerk fetch failed:", clerkResponse.status, errorText);
35
- throw new Error(`Failed to fetch Clerk user: ${clerkResponse.status}`);
36
- }
37
- const clerkUser = await clerkResponse.json();
38
-
39
- const userResponse = await fetch(
40
- `${STRAPI_USER_LIST_API_URL}?filters[authId][$eq]=${userId}&filters[userRole][$eq]=admin`,
41
- {
42
- headers: {
43
- Authorization: `Bearer ${STRAPI_API_TOKEN}`,
44
- "Content-Type": "application/json",
45
- },
46
- }
47
- );
48
- if (!userResponse.ok) {
49
- const errorText = await userResponse.text();
50
- console.error("Strapi user fetch error:", errorText);
51
- throw new Error(`Failed to fetch user-lists for admin check: ${userResponse.status}`);
52
- }
53
- const userData = await userResponse.json();
54
- const isAdmin = userData.data?.length > 0;
55
-
56
- const strapiUserData = {
57
- authId: userId,
58
- authProvider: "clerk",
59
- email: clerkUser.email_addresses[0]?.email_address || clerkUserData.emailAddresses[0]?.emailAddress,
60
- username:
61
- clerkUser.username ||
62
- clerkUser.email_addresses[0]?.email_address.split("@")[0] ||
63
- clerkUserData.emailAddresses[0]?.emailAddress.split("@")[0],
64
- businessAdminId: isAdmin ? (userData.data[0]?.businessAdminId || clerkUser.public_metadata?.businessAdminId || "") : clerkUser.public_metadata?.businessAdminId || "",
65
- userRole: isAdmin ? "admin" : clerkUser.public_metadata?.userRole || "user",
66
- isVerified: clerkUser.email_addresses[0]?.verification?.status === "verified" || false,
67
- firstName: clerkUser.first_name || "",
68
- lastName: clerkUser.last_name || "",
69
- businessOwner: isAdmin ? (userData.data[0]?.businessOwner || clerkUser.public_metadata?.businessOwner || false) : clerkUser.public_metadata?.businessOwner || false,
70
- userStatus: "active",
71
- };
72
-
73
- const existingUsersResponse = await fetch(
74
- `${STRAPI_USER_LIST_API_URL}?filters[authId][$eq]=${userId}`,
75
- {
76
- headers: {
77
- Authorization: `Bearer ${STRAPI_API_TOKEN}`,
78
- "Content-Type": "application/json",
79
- },
80
- }
81
- );
82
- if (!existingUsersResponse.ok) {
83
- const errorText = await existingUsersResponse.text();
84
- console.error("Strapi fetch error:", errorText);
85
- throw new Error(`Failed to fetch user-lists: ${existingUsersResponse.status}`);
86
- }
87
-
88
- const existingUsers = await existingUsersResponse.json();
89
- const existingUser = existingUsers.data?.[0];
90
-
91
- let strapiUser;
92
- if (existingUser) {
93
- const updateUrl = `${STRAPI_USER_LIST_API_URL}/${existingUser.documentId}`;
94
- const updateResponse = await fetch(updateUrl, {
95
- method: "PUT",
96
- headers: {
97
- Authorization: `Bearer ${STRAPI_API_TOKEN}`,
98
- "Content-Type": "application/json",
99
- },
100
- body: JSON.stringify({ data: strapiUserData }),
101
- });
102
- const strapiUserDataResponse = await updateResponse.json();
103
- if (!updateResponse.ok) {
104
- console.error("Update failed:", updateResponse.status, strapiUserDataResponse);
105
- throw new Error(`Failed to update user: ${updateResponse.status}`);
106
- }
107
- strapiUser = strapiUserDataResponse.data?.attributes || strapiUserDataResponse.data;
108
- } else {
109
- const createResponse = await fetch(`${STRAPI_USER_LIST_API_URL}`, {
110
- method: "POST",
111
- headers: {
112
- Authorization: `Bearer ${STRAPI_API_TOKEN}`,
113
- "Content-Type": "application/json",
114
- },
115
- body: JSON.stringify({ data: strapiUserData }),
116
- });
117
- if (!createResponse.ok) {
118
- const errorText = await createResponse.text();
119
- console.error("Create failed:", createResponse.status, errorText);
120
- throw new Error(`Failed to create user: ${createResponse.status}`);
121
- }
122
- const responseData = await createResponse.json();
123
- strapiUser = responseData.data?.attributes || responseData.data;
124
- }
125
-
126
- return NextResponse.json(strapiUser, { status: 200 });
127
- } catch (error) {
128
- console.error("Error syncing user to Strapi:", error);
129
- const errorMessage = error instanceof Error ? error.message : "Failed to sync user to Strapi";
130
- return NextResponse.json({ error: errorMessage }, { status: 500 });
131
- }
132
- }
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { getAuth } from "@clerk/nextjs/server";
3
+ import crypto from "crypto";
4
+
5
+ // GET handler for /api/sync-user: Retrieves a Strapi user by Clerk authId (user ID).
6
+ // This is called from the client to check if a user exists in Strapi.
7
+ export async function GET(req: NextRequest) {
8
+ // Retrieve the allowed origin for CORS from environment variables, default to 'http://localhost:3000'. TODO: add development mode check
9
+ const allowedOrigin = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
10
+ // Environment variables for Strapi API integration.
11
+ const STRAPI_USER_LIST_API_URL = process.env.STRAPI_USER_LIST_API_URL;
12
+ const STRAPI_API_TOKEN = process.env.STRAPI_API_TOKEN;
13
+ // Extract the authId (Clerk user ID) from query parameters.
14
+ const authId = req.nextUrl.searchParams.get("authId");
15
+
16
+ // Validate required environment variables and authId; return 500 if missing.
17
+ if (!STRAPI_USER_LIST_API_URL || !STRAPI_API_TOKEN || !authId) {
18
+ console.error("GET /api/sync-user: Missing environment variables or authId", {
19
+ hasBaseUrl: !!allowedOrigin,
20
+ hasStrapiUrl: !!STRAPI_USER_LIST_API_URL,
21
+ hasStrapiToken: !!STRAPI_API_TOKEN,
22
+ hasAuthId: !!authId,
23
+ timestamp: new Date().toISOString(),
24
+ });
25
+ return NextResponse.json({ error: "Server configuration error or missing authId" }, { status: 500 });
26
+ }
27
+
28
+ // Set CORS headers for the response.
29
+ const headers = new Headers({
30
+ "Access-Control-Allow-Origin": allowedOrigin,
31
+ "Access-Control-Allow-Methods": "GET",
32
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
33
+ });
34
+
35
+ try {
36
+ // Fetch the user from Strapi using the authId filter.
37
+ const checkResponse = await fetch(
38
+ `${STRAPI_USER_LIST_API_URL}?filters[authId][$eq]=${encodeURIComponent(authId)}`,
39
+ {
40
+ headers: {
41
+ Authorization: `Bearer ${STRAPI_API_TOKEN}`,
42
+ "Content-Type": "application/json",
43
+ },
44
+ }
45
+ );
46
+
47
+ // If Strapi fetch fails, log and return the error status.
48
+ if (!checkResponse.ok) {
49
+ console.error("GET /api/sync-user: Failed to fetch Strapi user", {
50
+ status: checkResponse.status,
51
+ timestamp: new Date().toISOString(),
52
+ });
53
+ return NextResponse.json({ error: "Failed to fetch user" }, { status: checkResponse.status, headers });
54
+ }
55
+
56
+ // Parse the Strapi response.
57
+ const checkResult = await checkResponse.json();
58
+ // If a user is found (data array has at least one item), return the user data.
59
+ if (checkResult.data && checkResult.data.length > 0) {
60
+ const existingUser = checkResult.data[0];
61
+ // Return a sanitized version of the user object, excluding sensitive fields.
62
+ return NextResponse.json(
63
+ {
64
+ id: existingUser.id,
65
+ username: existingUser.username,
66
+ email: existingUser.email,
67
+ authId: existingUser.authId,
68
+ businessAdminId: existingUser.businessAdminId,
69
+ userRole: existingUser.userRole,
70
+ isAdmin: existingUser.isAdmin || false,
71
+ firstName: existingUser.firstName,
72
+ lastName: existingUser.lastName,
73
+ businessId: existingUser.businessId,
74
+ dateJoined: existingUser.dateJoined,
75
+ businessOwner: existingUser.businessOwner,
76
+ userStatus: existingUser.userStatus,
77
+ timezone: existingUser.timezone,
78
+ language: existingUser.language,
79
+ isVerified: existingUser.isVerified,
80
+ authProvider: existingUser.authProvider,
81
+ businessTitle: existingUser.businessTitle,
82
+ userTitle: existingUser.userTitle,
83
+ number: existingUser.number,
84
+ address: existingUser.address,
85
+ websiteUrl: existingUser.websiteUrl,
86
+ primaryBusinessColor: existingUser.primaryBusinessColor,
87
+ secondaryBusinessColor: existingUser.secondaryBusinessColor,
88
+ logoImage: existingUser.logoImage,
89
+ },
90
+ { status: 200, headers }
91
+ );
92
+ }
93
+ // If no user found, return 404.
94
+ return NextResponse.json({ error: "User not found" }, { status: 404, headers });
95
+ } catch (error) {
96
+ // Catch any unexpected errors, log them, and return 500.
97
+ console.error("GET /api/sync-user: Error:", {
98
+ error: error instanceof Error ? error.message : "Unknown error",
99
+ timestamp: new Date().toISOString(),
100
+ });
101
+ return NextResponse.json(
102
+ { error: "Internal server error", details: error instanceof Error ? error.message : "Unknown error" },
103
+ { status: 500, headers }
104
+ );
105
+ }
106
+ }
107
+
108
+ // POST handler for /api/sync-user: Verifies the current Clerk user and checks for existing Strapi user.
109
+ // This seems to be used for post-signup verification rather than creation (creation is handled elsewhere).
110
+ export async function POST(req: NextRequest) {
111
+ // Retrieve the allowed origin for CORS.
112
+ const allowedOrigin = process.env.NEXT_PUBLIC_BASE_URL;
113
+ if (!allowedOrigin) {
114
+ console.error("NEXT_PUBLIC_BASE_URL not configured", { timestamp: new Date().toISOString() });
115
+ return NextResponse.json({ error: "Server configuration error: Missing base URL" }, { status: 500 });
116
+ }
117
+
118
+ // Set CORS headers for POST requests.
119
+ const headers = new Headers({
120
+ "Access-Control-Allow-Origin": allowedOrigin,
121
+ "Access-Control-Allow-Methods": "POST",
122
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
123
+ });
124
+
125
+ // Handle preflight OPTIONS request for CORS.
126
+ if (req.method === "OPTIONS") {
127
+ return new NextResponse(null, { status: 204, headers });
128
+ }
129
+
130
+ // Get the authenticated user ID from Clerk middleware.
131
+ const { userId } = getAuth(req);
132
+ if (!userId) {
133
+ console.error("No userId found", { timestamp: new Date().toISOString() });
134
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401, headers });
135
+ }
136
+
137
+ // Generate a unique request ID for logging.
138
+ const requestId = crypto.randomUUID();
139
+
140
+ // Parse the request body (expected to contain authId, email, username).
141
+ let body;
142
+ try {
143
+ body = await req.json();
144
+ } catch (error) {
145
+ console.warn("Failed to parse request body", { requestId, error, timestamp: new Date().toISOString() });
146
+ return NextResponse.json({ error: "Invalid request body" }, { status: 400, headers });
147
+ }
148
+
149
+ // Extract required fields from body.
150
+ const { authId, email, username } = body;
151
+
152
+ // Validate required fields; return 400 if missing.
153
+ if (!authId || !email || !username) {
154
+ console.error("Missing required fields", { requestId, body, timestamp: new Date().toISOString() });
155
+ return NextResponse.json({ error: "Missing required fields" }, { status: 400, headers });
156
+ }
157
+
158
+ // Environment variables for integrations.
159
+ const STRAPI_USER_LIST_API_URL = process.env.STRAPI_USER_LIST_API_URL;
160
+ const STRAPI_API_TOKEN = process.env.STRAPI_API_TOKEN;
161
+ const CLERK_SECRET_KEY = process.env.CLERK_SECRET_KEY;
162
+
163
+ // Validate environment variables; return 500 if missing.
164
+ if (!STRAPI_USER_LIST_API_URL || !STRAPI_API_TOKEN || !CLERK_SECRET_KEY) {
165
+ console.error("Missing environment variables", {
166
+ requestId,
167
+ hasStrapiUrl: !!STRAPI_USER_LIST_API_URL,
168
+ hasStrapiToken: !!STRAPI_API_TOKEN,
169
+ hasClerkSecret: !!CLERK_SECRET_KEY,
170
+ timestamp: new Date().toISOString(),
171
+ });
172
+ return NextResponse.json({ error: "Server configuration error" }, { status: 500, headers });
173
+ }
174
+
175
+ try {
176
+ // Fetch the full Clerk user details using the userId from middleware (with 5s timeout).
177
+ const clerkController = new AbortController();
178
+ const clerkTimeout = setTimeout(() => clerkController.abort(), 5000);
179
+ const clerkResponse = await fetch(`https://api.clerk.dev/v1/users/${userId}`, {
180
+ headers: {
181
+ Authorization: `Bearer ${CLERK_SECRET_KEY}`,
182
+ "Content-Type": "application/json",
183
+ },
184
+ signal: clerkController.signal,
185
+ });
186
+ clearTimeout(clerkTimeout);
187
+
188
+ // If Clerk fetch fails, log and return 500.
189
+ if (!clerkResponse.ok) {
190
+ const errorText = await clerkResponse.text();
191
+ console.error("Failed to fetch Clerk user", {
192
+ requestId,
193
+ status: clerkResponse.status,
194
+ errorText,
195
+ timestamp: new Date().toISOString(),
196
+ });
197
+ return NextResponse.json({ error: "Failed to verify user" }, { status: 500, headers });
198
+ }
199
+
200
+ // Parse Clerk user and ensure an email exists.
201
+ const clerkUser = await clerkResponse.json();
202
+ if (!clerkUser.email_addresses?.[0]?.email_address) {
203
+ console.error("No email found for user", { requestId, userId, timestamp: new Date().toISOString() });
204
+ return NextResponse.json({ error: "User email not found" }, { status: 400, headers });
205
+ }
206
+
207
+ // Fetch Strapi user by authId (with 5s timeout).
208
+ const authIdController = new AbortController();
209
+ const authIdTimeout = setTimeout(() => authIdController.abort(), 5000);
210
+ const authIdResponse = await fetch(
211
+ `${STRAPI_USER_LIST_API_URL}?filters[authId][$eq]=${encodeURIComponent(authId)}`,
212
+ {
213
+ headers: {
214
+ Authorization: `Bearer ${STRAPI_API_TOKEN}`,
215
+ "Content-Type": "application/json",
216
+ },
217
+ signal: authIdController.signal,
218
+ }
219
+ );
220
+ clearTimeout(authIdTimeout);
221
+
222
+ // If Strapi fetch fails, log and return 500.
223
+ if (!authIdResponse.ok) {
224
+ const errorText = await authIdResponse.text();
225
+ console.error("Failed to fetch Strapi user by authId", {
226
+ requestId,
227
+ status: authIdResponse.status,
228
+ errorText,
229
+ timestamp: new Date().toISOString(),
230
+ });
231
+ return NextResponse.json({ error: "Failed to verify Strapi user" }, { status: 500, headers });
232
+ }
233
+
234
+ // Parse Strapi response.
235
+ const authIdResult = await authIdResponse.json();
236
+ // If user exists, return the sanitized user data (similar to GET).
237
+ if (authIdResult.data && authIdResult.data.length > 0) {
238
+ const existingUser = authIdResult.data[0];
239
+ return NextResponse.json(
240
+ {
241
+ id: existingUser.id,
242
+ username: existingUser.username,
243
+ email: existingUser.email,
244
+ authId: existingUser.authId,
245
+ businessAdminId: existingUser.businessAdminId,
246
+ userRole: existingUser.userRole,
247
+ isAdmin: existingUser.isAdmin || false,
248
+ firstName: existingUser.firstName,
249
+ lastName: existingUser.lastName,
250
+ businessId: existingUser.businessId,
251
+ dateJoined: existingUser.dateJoined,
252
+ businessOwner: existingUser.businessOwner,
253
+ userStatus: existingUser.userStatus,
254
+ timezone: existingUser.timezone,
255
+ language: existingUser.language,
256
+ isVerified: existingUser.isVerified,
257
+ authProvider: existingUser.authProvider,
258
+ businessTitle: existingUser.businessTitle,
259
+ userTitle: existingUser.userTitle,
260
+ number: existingUser.number,
261
+ address: existingUser.address,
262
+ websiteUrl: existingUser.websiteUrl,
263
+ primaryBusinessColor: existingUser.primaryBusinessColor,
264
+ secondaryBusinessColor: existingUser.secondaryBusinessColor,
265
+ logoImage: existingUser.logoImage,
266
+ },
267
+ { status: 200, headers }
268
+ );
269
+ }
270
+
271
+ // If no user found, log warning and return 404.
272
+ console.warn("No Strapi user found for authId", { requestId, authId, timestamp: new Date().toISOString() });
273
+ return NextResponse.json({ error: "User not found in Strapi" }, { status: 404, headers });
274
+ } catch (error) {
275
+ // Catch any errors (e.g., timeouts, network issues), log, and return 500.
276
+ console.error("Error verifying or syncing user", {
277
+ requestId,
278
+ error: error instanceof Error ? error.message : "Unknown error",
279
+ timestamp: new Date().toISOString(),
280
+ });
281
+ return NextResponse.json({ error: "Internal server error" }, { status: 500, headers });
282
+ }
283
+ }
284
+
285
+ // Next.js API route configuration: Enable body parsing for JSON requests.
286
+ export const config = {
287
+ api: {
288
+ bodyParser: true,
289
+ },
290
+ };
291
+
292
+ // Helper function to determine allowedOrigin based on environment
293
+ function getAllowedOrigin(): string {
294
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
295
+
296
+ if (baseUrl) {
297
+ return baseUrl;
298
+ }
299
+
300
+ if (process.env.NODE_ENV === 'development') {
301
+ return 'http://localhost:3000'; // Or '*' for even more flexibility in dev
302
+ }
303
+
304
+ // In production, fail explicitly if not set (prevents accidental '*' usage)
305
+ throw new Error('NEXT_PUBLIC_BASE_URL must be set in production');
306
+ }