@digilogiclabs/create-saas-app 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +975 -891
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/generators/template-generator.d.ts +11 -0
  5. package/dist/generators/template-generator.d.ts.map +1 -1
  6. package/dist/generators/template-generator.js +389 -18
  7. package/dist/generators/template-generator.js.map +1 -1
  8. package/dist/index.js +1837 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/templates/infrastructure/kubernetes/base/template/README.md +253 -0
  11. package/dist/templates/infrastructure/kubernetes/base/template/configmap.yaml +12 -0
  12. package/dist/templates/infrastructure/kubernetes/base/template/deployment.yaml +123 -0
  13. package/dist/templates/infrastructure/kubernetes/base/template/hpa.yaml +45 -0
  14. package/dist/templates/infrastructure/kubernetes/base/template/ingress.yaml +31 -0
  15. package/dist/templates/infrastructure/kubernetes/base/template/kustomization.yaml +25 -0
  16. package/dist/templates/infrastructure/kubernetes/base/template/namespace.yaml +8 -0
  17. package/dist/templates/infrastructure/kubernetes/base/template/networkpolicy.yaml +48 -0
  18. package/dist/templates/infrastructure/kubernetes/base/template/pdb.yaml +14 -0
  19. package/dist/templates/infrastructure/kubernetes/base/template/secret.yaml +17 -0
  20. package/dist/templates/infrastructure/kubernetes/base/template/service.yaml +19 -0
  21. package/dist/templates/infrastructure/kubernetes/base/template/serviceaccount.yaml +9 -0
  22. package/dist/templates/infrastructure/kubernetes/production/template/kustomization.yaml +92 -0
  23. package/dist/templates/infrastructure/terraform/aws/template/README.md +156 -0
  24. package/dist/templates/infrastructure/terraform/aws/template/main.tf +343 -0
  25. package/dist/templates/infrastructure/terraform/aws/template/outputs.tf +66 -0
  26. package/dist/templates/infrastructure/terraform/aws/template/terraform.tfvars.example +28 -0
  27. package/dist/templates/infrastructure/terraform/aws/template/variables.tf +110 -0
  28. package/dist/templates/infrastructure/terraform/gcp/template/README.md +165 -0
  29. package/dist/templates/infrastructure/terraform/gcp/template/main.tf +397 -0
  30. package/dist/templates/infrastructure/terraform/gcp/template/outputs.tf +51 -0
  31. package/dist/templates/infrastructure/terraform/gcp/template/terraform.tfvars.example +29 -0
  32. package/dist/templates/infrastructure/terraform/gcp/template/variables.tf +115 -0
  33. package/dist/templates/shared/admin/web/app/admin/layout.tsx +34 -0
  34. package/dist/templates/shared/admin/web/components/admin-nav.tsx +48 -0
  35. package/dist/templates/shared/audit/web/lib/audit.ts +24 -0
  36. package/dist/templates/shared/auth/keycloak/web/app/api/auth/federated-logout/route.ts +173 -0
  37. package/dist/templates/shared/auth/keycloak/web/auth.config.ts +84 -0
  38. package/dist/templates/shared/auth/keycloak/web/auth.ts +26 -0
  39. package/dist/templates/shared/beta/web/app/api/beta-settings/route.ts +25 -0
  40. package/dist/templates/shared/beta/web/app/api/validate-beta-code/route.ts +67 -0
  41. package/dist/templates/shared/beta/web/lib/beta/settings.ts +31 -0
  42. package/dist/templates/shared/cache/web/lib/cache.ts +44 -0
  43. package/dist/templates/shared/config/web/lib/config.ts +112 -0
  44. package/dist/templates/shared/config/web/next.config.mjs +62 -0
  45. package/dist/templates/shared/contact/web/app/api/contact/route.ts +113 -0
  46. package/dist/templates/shared/contact/web/app/contact/page.tsx +195 -0
  47. package/dist/templates/shared/cookie-consent/web/components/cookie-consent.tsx +54 -0
  48. package/dist/templates/shared/database/postgresql/web/drizzle.config.ts +16 -0
  49. package/dist/templates/shared/database/postgresql/web/lib/db/drizzle.ts +39 -0
  50. package/dist/templates/shared/database/postgresql/web/lib/db/schema.ts +33 -0
  51. package/dist/templates/shared/database/supabase/web/lib/supabase/client.ts +12 -0
  52. package/dist/templates/shared/database/supabase/web/lib/supabase/server.ts +31 -0
  53. package/dist/templates/shared/database/supabase/web/lib/supabase/service.ts +15 -0
  54. package/dist/templates/shared/email/web/lib/email/branding.ts +18 -0
  55. package/dist/templates/shared/email/web/lib/email/client.ts +96 -0
  56. package/dist/templates/shared/error-pages/web/app/error.tsx +70 -0
  57. package/dist/templates/shared/error-pages/web/app/global-error.tsx +102 -0
  58. package/dist/templates/shared/error-pages/web/app/not-found.tsx +39 -0
  59. package/dist/templates/shared/health/web/app/api/health/route.ts +68 -0
  60. package/dist/templates/shared/legal/web/app/(legal)/privacy/page.tsx +205 -0
  61. package/dist/templates/shared/legal/web/app/(legal)/terms/page.tsx +154 -0
  62. package/dist/templates/shared/legal/web/lib/legal-config.ts +50 -0
  63. package/dist/templates/shared/loading/web/app/loading.tsx +5 -0
  64. package/dist/templates/shared/loading/web/components/skeleton.tsx +95 -0
  65. package/dist/templates/shared/middleware/web/middleware.ts +68 -0
  66. package/dist/templates/shared/observability/web/lib/observability.ts +135 -0
  67. package/dist/templates/shared/payments/web/app/api/webhooks/stripe/route.ts +109 -0
  68. package/dist/templates/shared/platform/web/lib/platform.ts +37 -0
  69. package/dist/templates/shared/redis/web/lib/rate-limit-store.ts +18 -0
  70. package/dist/templates/shared/redis/web/lib/redis.ts +48 -0
  71. package/dist/templates/shared/security/web/lib/api-security.ts +318 -0
  72. package/dist/templates/shared/seo/web/app/api/og/route.tsx +97 -0
  73. package/dist/templates/shared/seo/web/app/robots.ts +53 -0
  74. package/dist/templates/shared/seo/web/app/sitemap.ts +53 -0
  75. package/dist/templates/shared/utils/web/lib/api-response.ts +71 -0
  76. package/dist/templates/shared/utils/web/lib/utils.ts +85 -0
  77. package/dist/templates/web/ai-platform/template/.env.example +16 -0
  78. package/dist/templates/web/ai-platform/template/README.md +84 -0
  79. package/dist/templates/web/ai-platform/template/middleware.ts +55 -0
  80. package/dist/templates/web/ai-platform/template/next.config.js +14 -0
  81. package/dist/templates/web/ai-platform/template/package.json +55 -0
  82. package/dist/templates/web/ai-platform/template/src/app/api/chat/route.ts +54 -0
  83. package/dist/templates/web/ai-platform/template/src/app/chat/page.tsx +235 -0
  84. package/dist/templates/web/ai-platform/template/src/app/dashboard/page.tsx +142 -0
  85. package/dist/templates/web/ai-platform/template/src/app/globals.css +34 -0
  86. package/dist/templates/web/ai-platform/template/src/app/layout.tsx +27 -0
  87. package/dist/templates/web/ai-platform/template/src/app/page.tsx +203 -0
  88. package/dist/templates/web/ai-platform/template/src/components/providers/app-providers.tsx +27 -0
  89. package/dist/templates/web/ai-platform/template/src/lib/auth-server.ts +33 -0
  90. package/dist/templates/web/ai-platform/template/src/lib/supabase/client.ts +8 -0
  91. package/dist/templates/web/ai-platform/template/src/lib/supabase/server.ts +27 -0
  92. package/dist/templates/web/ai-platform/template/src/lib/utils.ts +6 -0
  93. package/dist/templates/web/ai-platform/template/tsconfig.json +27 -0
  94. package/dist/templates/web/iot-dashboard/template/.env.example +12 -0
  95. package/dist/templates/web/iot-dashboard/template/README.md +101 -0
  96. package/dist/templates/web/iot-dashboard/template/middleware.ts +56 -0
  97. package/dist/templates/web/iot-dashboard/template/next.config.js +14 -0
  98. package/dist/templates/web/iot-dashboard/template/package.json +49 -0
  99. package/dist/templates/web/iot-dashboard/template/src/app/dashboard/page.tsx +229 -0
  100. package/dist/templates/web/iot-dashboard/template/src/app/globals.css +20 -0
  101. package/dist/templates/web/iot-dashboard/template/src/app/layout.tsx +27 -0
  102. package/dist/templates/web/iot-dashboard/template/src/app/page.tsx +191 -0
  103. package/dist/templates/web/iot-dashboard/template/src/components/providers/app-providers.tsx +24 -0
  104. package/dist/templates/web/iot-dashboard/template/src/lib/auth-server.ts +33 -0
  105. package/dist/templates/web/iot-dashboard/template/src/lib/supabase/client.ts +8 -0
  106. package/dist/templates/web/iot-dashboard/template/src/lib/supabase/server.ts +27 -0
  107. package/dist/templates/web/iot-dashboard/template/src/lib/utils.ts +25 -0
  108. package/dist/templates/web/iot-dashboard/template/tsconfig.json +27 -0
  109. package/dist/templates/web/marketplace/template/.env.example +12 -0
  110. package/dist/templates/web/marketplace/template/README.md +66 -0
  111. package/dist/templates/web/marketplace/template/middleware.ts +56 -0
  112. package/dist/templates/web/marketplace/template/next.config.js +14 -0
  113. package/dist/templates/web/marketplace/template/package.json +51 -0
  114. package/dist/templates/web/marketplace/template/src/app/cart/page.tsx +147 -0
  115. package/dist/templates/web/marketplace/template/src/app/dashboard/page.tsx +149 -0
  116. package/dist/templates/web/marketplace/template/src/app/globals.css +20 -0
  117. package/dist/templates/web/marketplace/template/src/app/layout.tsx +27 -0
  118. package/dist/templates/web/marketplace/template/src/app/page.tsx +167 -0
  119. package/dist/templates/web/marketplace/template/src/app/products/page.tsx +129 -0
  120. package/dist/templates/web/marketplace/template/src/components/providers/app-providers.tsx +27 -0
  121. package/dist/templates/web/marketplace/template/src/lib/auth-server.ts +33 -0
  122. package/dist/templates/web/marketplace/template/src/lib/supabase/client.ts +8 -0
  123. package/dist/templates/web/marketplace/template/src/lib/supabase/server.ts +27 -0
  124. package/dist/templates/web/marketplace/template/src/lib/utils.ts +19 -0
  125. package/dist/templates/web/marketplace/template/tsconfig.json +27 -0
  126. package/dist/templates/web/micro-saas/template/.env.example +10 -0
  127. package/dist/templates/web/micro-saas/template/README.md +63 -0
  128. package/dist/templates/web/micro-saas/template/middleware.ts +53 -0
  129. package/dist/templates/web/micro-saas/template/next.config.js +14 -0
  130. package/dist/templates/web/micro-saas/template/package.json +41 -0
  131. package/dist/templates/web/micro-saas/template/src/app/dashboard/page.tsx +117 -0
  132. package/dist/templates/web/micro-saas/template/src/app/globals.css +20 -0
  133. package/dist/templates/web/micro-saas/template/src/app/layout.tsx +27 -0
  134. package/dist/templates/web/micro-saas/template/src/app/login/page.tsx +87 -0
  135. package/dist/templates/web/micro-saas/template/src/app/page.tsx +137 -0
  136. package/dist/templates/web/micro-saas/template/src/app/signup/page.tsx +108 -0
  137. package/dist/templates/web/micro-saas/template/src/components/providers/app-providers.tsx +24 -0
  138. package/dist/templates/web/micro-saas/template/src/lib/auth-server.ts +33 -0
  139. package/dist/templates/web/micro-saas/template/src/lib/supabase/client.ts +8 -0
  140. package/dist/templates/web/micro-saas/template/src/lib/supabase/server.ts +29 -0
  141. package/dist/templates/web/micro-saas/template/src/lib/utils.ts +6 -0
  142. package/dist/templates/web/micro-saas/template/tsconfig.json +27 -0
  143. package/package.json +5 -4
  144. package/src/templates/infrastructure/kubernetes/base/template/README.md +253 -0
  145. package/src/templates/infrastructure/kubernetes/base/template/configmap.yaml +12 -0
  146. package/src/templates/infrastructure/kubernetes/base/template/deployment.yaml +123 -0
  147. package/src/templates/infrastructure/kubernetes/base/template/hpa.yaml +45 -0
  148. package/src/templates/infrastructure/kubernetes/base/template/ingress.yaml +31 -0
  149. package/src/templates/infrastructure/kubernetes/base/template/kustomization.yaml +25 -0
  150. package/src/templates/infrastructure/kubernetes/base/template/namespace.yaml +8 -0
  151. package/src/templates/infrastructure/kubernetes/base/template/networkpolicy.yaml +48 -0
  152. package/src/templates/infrastructure/kubernetes/base/template/pdb.yaml +14 -0
  153. package/src/templates/infrastructure/kubernetes/base/template/secret.yaml +17 -0
  154. package/src/templates/infrastructure/kubernetes/base/template/service.yaml +19 -0
  155. package/src/templates/infrastructure/kubernetes/base/template/serviceaccount.yaml +9 -0
  156. package/src/templates/infrastructure/kubernetes/production/template/kustomization.yaml +92 -0
  157. package/src/templates/infrastructure/terraform/aws/template/README.md +156 -0
  158. package/src/templates/infrastructure/terraform/aws/template/main.tf +343 -0
  159. package/src/templates/infrastructure/terraform/aws/template/outputs.tf +66 -0
  160. package/src/templates/infrastructure/terraform/aws/template/terraform.tfvars.example +28 -0
  161. package/src/templates/infrastructure/terraform/aws/template/variables.tf +110 -0
  162. package/src/templates/infrastructure/terraform/gcp/template/README.md +165 -0
  163. package/src/templates/infrastructure/terraform/gcp/template/main.tf +397 -0
  164. package/src/templates/infrastructure/terraform/gcp/template/outputs.tf +51 -0
  165. package/src/templates/infrastructure/terraform/gcp/template/terraform.tfvars.example +29 -0
  166. package/src/templates/infrastructure/terraform/gcp/template/variables.tf +115 -0
  167. package/src/templates/shared/admin/web/app/admin/layout.tsx +34 -0
  168. package/src/templates/shared/admin/web/components/admin-nav.tsx +48 -0
  169. package/src/templates/shared/audit/web/lib/audit.ts +24 -0
  170. package/src/templates/shared/auth/keycloak/web/app/api/auth/federated-logout/route.ts +173 -0
  171. package/src/templates/shared/auth/keycloak/web/auth.config.ts +84 -0
  172. package/src/templates/shared/auth/keycloak/web/auth.ts +26 -0
  173. package/src/templates/shared/beta/web/app/api/beta-settings/route.ts +25 -0
  174. package/src/templates/shared/beta/web/app/api/validate-beta-code/route.ts +67 -0
  175. package/src/templates/shared/beta/web/lib/beta/settings.ts +31 -0
  176. package/src/templates/shared/cache/web/lib/cache.ts +44 -0
  177. package/src/templates/shared/config/web/lib/config.ts +112 -0
  178. package/src/templates/shared/config/web/next.config.mjs +62 -0
  179. package/src/templates/shared/contact/web/app/api/contact/route.ts +113 -0
  180. package/src/templates/shared/contact/web/app/contact/page.tsx +195 -0
  181. package/src/templates/shared/cookie-consent/web/components/cookie-consent.tsx +54 -0
  182. package/src/templates/shared/database/postgresql/web/drizzle.config.ts +16 -0
  183. package/src/templates/shared/database/postgresql/web/lib/db/drizzle.ts +39 -0
  184. package/src/templates/shared/database/postgresql/web/lib/db/schema.ts +33 -0
  185. package/src/templates/shared/database/supabase/web/lib/supabase/client.ts +12 -0
  186. package/src/templates/shared/database/supabase/web/lib/supabase/server.ts +31 -0
  187. package/src/templates/shared/database/supabase/web/lib/supabase/service.ts +15 -0
  188. package/src/templates/shared/email/web/lib/email/branding.ts +18 -0
  189. package/src/templates/shared/email/web/lib/email/client.ts +96 -0
  190. package/src/templates/shared/error-pages/web/app/error.tsx +70 -0
  191. package/src/templates/shared/error-pages/web/app/global-error.tsx +102 -0
  192. package/src/templates/shared/error-pages/web/app/not-found.tsx +39 -0
  193. package/src/templates/shared/health/web/app/api/health/route.ts +68 -0
  194. package/src/templates/shared/legal/web/app/(legal)/privacy/page.tsx +205 -0
  195. package/src/templates/shared/legal/web/app/(legal)/terms/page.tsx +154 -0
  196. package/src/templates/shared/legal/web/lib/legal-config.ts +50 -0
  197. package/src/templates/shared/loading/web/app/loading.tsx +5 -0
  198. package/src/templates/shared/loading/web/components/skeleton.tsx +95 -0
  199. package/src/templates/shared/middleware/web/middleware.ts +68 -0
  200. package/src/templates/shared/observability/web/lib/observability.ts +135 -0
  201. package/src/templates/shared/payments/web/app/api/webhooks/stripe/route.ts +109 -0
  202. package/src/templates/shared/platform/web/lib/platform.ts +37 -0
  203. package/src/templates/shared/redis/web/lib/rate-limit-store.ts +18 -0
  204. package/src/templates/shared/redis/web/lib/redis.ts +48 -0
  205. package/src/templates/shared/security/web/lib/api-security.ts +318 -0
  206. package/src/templates/shared/seo/web/app/api/og/route.tsx +97 -0
  207. package/src/templates/shared/seo/web/app/robots.ts +53 -0
  208. package/src/templates/shared/seo/web/app/sitemap.ts +53 -0
  209. package/src/templates/shared/utils/web/lib/api-response.ts +71 -0
  210. package/src/templates/shared/utils/web/lib/utils.ts +85 -0
  211. package/src/templates/web/ai-platform/template/.env.example +16 -0
  212. package/src/templates/web/ai-platform/template/README.md +84 -0
  213. package/src/templates/web/ai-platform/template/middleware.ts +55 -0
  214. package/src/templates/web/ai-platform/template/next.config.js +14 -0
  215. package/src/templates/web/ai-platform/template/package.json +55 -0
  216. package/src/templates/web/ai-platform/template/src/app/api/chat/route.ts +54 -0
  217. package/src/templates/web/ai-platform/template/src/app/chat/page.tsx +235 -0
  218. package/src/templates/web/ai-platform/template/src/app/dashboard/page.tsx +142 -0
  219. package/src/templates/web/ai-platform/template/src/app/globals.css +34 -0
  220. package/src/templates/web/ai-platform/template/src/app/layout.tsx +27 -0
  221. package/src/templates/web/ai-platform/template/src/app/page.tsx +203 -0
  222. package/src/templates/web/ai-platform/template/src/components/providers/app-providers.tsx +27 -0
  223. package/src/templates/web/ai-platform/template/src/lib/auth-server.ts +33 -0
  224. package/src/templates/web/ai-platform/template/src/lib/supabase/client.ts +8 -0
  225. package/src/templates/web/ai-platform/template/src/lib/supabase/server.ts +27 -0
  226. package/src/templates/web/ai-platform/template/src/lib/utils.ts +6 -0
  227. package/src/templates/web/ai-platform/template/tsconfig.json +27 -0
  228. package/src/templates/web/iot-dashboard/template/.env.example +12 -0
  229. package/src/templates/web/iot-dashboard/template/README.md +101 -0
  230. package/src/templates/web/iot-dashboard/template/middleware.ts +56 -0
  231. package/src/templates/web/iot-dashboard/template/next.config.js +14 -0
  232. package/src/templates/web/iot-dashboard/template/package.json +49 -0
  233. package/src/templates/web/iot-dashboard/template/src/app/dashboard/page.tsx +229 -0
  234. package/src/templates/web/iot-dashboard/template/src/app/globals.css +20 -0
  235. package/src/templates/web/iot-dashboard/template/src/app/layout.tsx +27 -0
  236. package/src/templates/web/iot-dashboard/template/src/app/page.tsx +191 -0
  237. package/src/templates/web/iot-dashboard/template/src/components/providers/app-providers.tsx +24 -0
  238. package/src/templates/web/iot-dashboard/template/src/lib/auth-server.ts +33 -0
  239. package/src/templates/web/iot-dashboard/template/src/lib/supabase/client.ts +8 -0
  240. package/src/templates/web/iot-dashboard/template/src/lib/supabase/server.ts +27 -0
  241. package/src/templates/web/iot-dashboard/template/src/lib/utils.ts +25 -0
  242. package/src/templates/web/iot-dashboard/template/tsconfig.json +27 -0
  243. package/src/templates/web/marketplace/template/.env.example +12 -0
  244. package/src/templates/web/marketplace/template/README.md +66 -0
  245. package/src/templates/web/marketplace/template/middleware.ts +56 -0
  246. package/src/templates/web/marketplace/template/next.config.js +14 -0
  247. package/src/templates/web/marketplace/template/package.json +51 -0
  248. package/src/templates/web/marketplace/template/src/app/cart/page.tsx +147 -0
  249. package/src/templates/web/marketplace/template/src/app/dashboard/page.tsx +149 -0
  250. package/src/templates/web/marketplace/template/src/app/globals.css +20 -0
  251. package/src/templates/web/marketplace/template/src/app/layout.tsx +27 -0
  252. package/src/templates/web/marketplace/template/src/app/page.tsx +167 -0
  253. package/src/templates/web/marketplace/template/src/app/products/page.tsx +129 -0
  254. package/src/templates/web/marketplace/template/src/components/providers/app-providers.tsx +27 -0
  255. package/src/templates/web/marketplace/template/src/lib/auth-server.ts +33 -0
  256. package/src/templates/web/marketplace/template/src/lib/supabase/client.ts +8 -0
  257. package/src/templates/web/marketplace/template/src/lib/supabase/server.ts +27 -0
  258. package/src/templates/web/marketplace/template/src/lib/utils.ts +19 -0
  259. package/src/templates/web/marketplace/template/tsconfig.json +27 -0
  260. package/src/templates/web/micro-saas/template/.env.example +10 -0
  261. package/src/templates/web/micro-saas/template/README.md +63 -0
  262. package/src/templates/web/micro-saas/template/middleware.ts +53 -0
  263. package/src/templates/web/micro-saas/template/next.config.js +14 -0
  264. package/src/templates/web/micro-saas/template/package.json +41 -0
  265. package/src/templates/web/micro-saas/template/src/app/dashboard/page.tsx +117 -0
  266. package/src/templates/web/micro-saas/template/src/app/globals.css +20 -0
  267. package/src/templates/web/micro-saas/template/src/app/layout.tsx +27 -0
  268. package/src/templates/web/micro-saas/template/src/app/login/page.tsx +87 -0
  269. package/src/templates/web/micro-saas/template/src/app/page.tsx +137 -0
  270. package/src/templates/web/micro-saas/template/src/app/signup/page.tsx +108 -0
  271. package/src/templates/web/micro-saas/template/src/components/providers/app-providers.tsx +24 -0
  272. package/src/templates/web/micro-saas/template/src/lib/auth-server.ts +33 -0
  273. package/src/templates/web/micro-saas/template/src/lib/supabase/client.ts +8 -0
  274. package/src/templates/web/micro-saas/template/src/lib/supabase/server.ts +29 -0
  275. package/src/templates/web/micro-saas/template/src/lib/utils.ts +6 -0
  276. package/src/templates/web/micro-saas/template/tsconfig.json +27 -0
  277. package/dist/cli/commands/add.d.ts +0 -6
  278. package/dist/cli/commands/add.d.ts.map +0 -1
  279. package/dist/cli/commands/add.js +0 -39
  280. package/dist/cli/commands/add.js.map +0 -1
  281. package/dist/cli/commands/create.d.ts +0 -45
  282. package/dist/cli/commands/create.d.ts.map +0 -1
  283. package/dist/cli/commands/create.js +0 -175
  284. package/dist/cli/commands/create.js.map +0 -1
  285. package/dist/cli/commands/index.d.ts +0 -4
  286. package/dist/cli/commands/index.d.ts.map +0 -1
  287. package/dist/cli/commands/index.js +0 -20
  288. package/dist/cli/commands/index.js.map +0 -1
  289. package/dist/cli/commands/update.d.ts +0 -6
  290. package/dist/cli/commands/update.d.ts.map +0 -1
  291. package/dist/cli/commands/update.js +0 -68
  292. package/dist/cli/commands/update.js.map +0 -1
  293. package/dist/cli/index.d.ts +0 -4
  294. package/dist/cli/index.d.ts.map +0 -1
  295. package/dist/cli/index.js +0 -61
  296. package/dist/cli/index.js.map +0 -1
  297. package/dist/cli/prompts/index.d.ts +0 -2
  298. package/dist/cli/prompts/index.d.ts.map +0 -1
  299. package/dist/cli/prompts/index.js +0 -18
  300. package/dist/cli/prompts/index.js.map +0 -1
  301. package/dist/cli/prompts/project-setup.d.ts +0 -5
  302. package/dist/cli/prompts/project-setup.d.ts.map +0 -1
  303. package/dist/cli/prompts/project-setup.js +0 -316
  304. package/dist/cli/prompts/project-setup.js.map +0 -1
  305. package/dist/cli/utils/git.d.ts +0 -9
  306. package/dist/cli/utils/git.d.ts.map +0 -1
  307. package/dist/cli/utils/git.js +0 -77
  308. package/dist/cli/utils/git.js.map +0 -1
  309. package/dist/cli/utils/index.d.ts +0 -5
  310. package/dist/cli/utils/index.d.ts.map +0 -1
  311. package/dist/cli/utils/index.js +0 -21
  312. package/dist/cli/utils/index.js.map +0 -1
  313. package/dist/cli/utils/logger.d.ts +0 -16
  314. package/dist/cli/utils/logger.d.ts.map +0 -1
  315. package/dist/cli/utils/logger.js +0 -55
  316. package/dist/cli/utils/logger.js.map +0 -1
  317. package/dist/cli/utils/package-manager.d.ts +0 -8
  318. package/dist/cli/utils/package-manager.d.ts.map +0 -1
  319. package/dist/cli/utils/package-manager.js +0 -92
  320. package/dist/cli/utils/package-manager.js.map +0 -1
  321. package/dist/cli/utils/spinner.d.ts +0 -7
  322. package/dist/cli/utils/spinner.d.ts.map +0 -1
  323. package/dist/cli/utils/spinner.js +0 -48
  324. package/dist/cli/utils/spinner.js.map +0 -1
  325. package/dist/cli/validators/dependencies.d.ts +0 -15
  326. package/dist/cli/validators/dependencies.d.ts.map +0 -1
  327. package/dist/cli/validators/dependencies.js +0 -108
  328. package/dist/cli/validators/dependencies.js.map +0 -1
  329. package/dist/cli/validators/index.d.ts +0 -3
  330. package/dist/cli/validators/index.d.ts.map +0 -1
  331. package/dist/cli/validators/index.js +0 -19
  332. package/dist/cli/validators/index.js.map +0 -1
  333. package/dist/cli/validators/project-name.d.ts +0 -5
  334. package/dist/cli/validators/project-name.d.ts.map +0 -1
  335. package/dist/cli/validators/project-name.js +0 -151
  336. package/dist/cli/validators/project-name.js.map +0 -1
  337. package/dist/generators/file-processor.d.ts +0 -28
  338. package/dist/generators/file-processor.d.ts.map +0 -1
  339. package/dist/generators/file-processor.js +0 -224
  340. package/dist/generators/file-processor.js.map +0 -1
  341. package/dist/generators/index.d.ts +0 -4
  342. package/dist/generators/index.d.ts.map +0 -1
  343. package/dist/generators/index.js +0 -20
  344. package/dist/generators/index.js.map +0 -1
  345. package/dist/generators/package-installer.d.ts +0 -29
  346. package/dist/generators/package-installer.d.ts.map +0 -1
  347. package/dist/generators/package-installer.js +0 -177
  348. package/dist/generators/package-installer.js.map +0 -1
@@ -0,0 +1,135 @@
1
+ import { getClientIp } from '@digilogiclabs/platform-core/auth';
2
+
3
+ /**
4
+ * Observability helpers — audit logging, error reporting, metrics.
5
+ *
6
+ * All functions are fire-and-forget (no await needed) and fail silently.
7
+ * Uses platform-core adapters when available, falls back to console.
8
+ *
9
+ * Customize the SERVICE_NAME and add app-specific audit actions.
10
+ */
11
+
12
+ const SERVICE_NAME = 'my-app';
13
+
14
+ // ─── Lazy Singletons ────────────────────────────────────────
15
+
16
+ let auditLogPromise: Promise<AuditLogger> | null = null;
17
+
18
+ interface AuditLogger {
19
+ log(event: AuditEvent): Promise<void>;
20
+ }
21
+
22
+ interface AuditEvent {
23
+ action: string;
24
+ actor: { id: string; type: string; email?: string };
25
+ resource?: { type: string; id?: string };
26
+ outcome: 'success' | 'failure' | 'blocked';
27
+ metadata?: Record<string, unknown>;
28
+ category?: string;
29
+ ip?: string;
30
+ userAgent?: string;
31
+ }
32
+
33
+ async function getAuditLog(): Promise<AuditLogger> {
34
+ if (!auditLogPromise) {
35
+ auditLogPromise = (async () => {
36
+ try {
37
+ const { getPlatform } = await import('@/lib/platform');
38
+ const platform = await getPlatform();
39
+ const { DatabaseAuditLog } = await import('@digilogiclabs/platform-core');
40
+ return new DatabaseAuditLog(platform.db, { serviceName: SERVICE_NAME });
41
+ } catch {
42
+ return {
43
+ log: async (event: AuditEvent) => {
44
+ console.log(`[Audit] ${event.action}`, JSON.stringify(event));
45
+ },
46
+ };
47
+ }
48
+ })();
49
+ }
50
+ return auditLogPromise;
51
+ }
52
+
53
+ // ─── Public Helpers ──────────────────────────────────────────
54
+
55
+ /** Get user agent string from request */
56
+ export function getUserAgent(request: Request): string {
57
+ return request.headers.get('user-agent') || 'unknown';
58
+ }
59
+
60
+ // Re-export getClientIp from platform-core
61
+ export { getClientIp } from '@digilogiclabs/platform-core/auth';
62
+
63
+ /** Log an audit event (fire-and-forget) */
64
+ export function auditAction(params: {
65
+ action: string;
66
+ actorId: string;
67
+ actorEmail?: string;
68
+ resourceType?: string;
69
+ resourceId?: string;
70
+ outcome?: 'success' | 'failure' | 'blocked';
71
+ metadata?: Record<string, unknown>;
72
+ request?: Request;
73
+ }): void {
74
+ const category = inferCategory(params.action);
75
+ getAuditLog()
76
+ .then((log) =>
77
+ log.log({
78
+ action: params.action,
79
+ actor: {
80
+ id: params.actorId,
81
+ type: params.actorId === 'system' ? 'system' : 'user',
82
+ email: params.actorEmail,
83
+ },
84
+ resource: params.resourceType
85
+ ? { type: params.resourceType, id: params.resourceId }
86
+ : undefined,
87
+ outcome: params.outcome || 'success',
88
+ metadata: params.metadata,
89
+ category,
90
+ ip: params.request ? getClientIp(params.request) : undefined,
91
+ userAgent: params.request ? getUserAgent(params.request) : undefined,
92
+ })
93
+ )
94
+ .catch(() => {});
95
+ }
96
+
97
+ /** Log an admin action */
98
+ export function auditAdminAction(
99
+ params: Omit<Parameters<typeof auditAction>[0], 'action'> & { action: string }
100
+ ): void {
101
+ auditAction({ ...params, action: `admin.${params.action}` });
102
+ }
103
+
104
+ /** Log a cron job action */
105
+ export function auditCronAction(
106
+ params: Omit<Parameters<typeof auditAction>[0], 'actorId'> & { action: string }
107
+ ): void {
108
+ auditAction({ ...params, actorId: 'system', action: `cron.${params.action}` });
109
+ }
110
+
111
+ /** Capture an error (fire-and-forget) */
112
+ export function captureError(error: unknown, context?: Record<string, unknown>): void {
113
+ try {
114
+ const message = error instanceof Error ? error.message : String(error);
115
+ const stack = error instanceof Error ? error.stack : undefined;
116
+ console.error(`[${SERVICE_NAME}] Error:`, message, context || '');
117
+ if (stack && process.env.NODE_ENV === 'development') {
118
+ console.error(stack);
119
+ }
120
+ } catch {
121
+ // Silent fail — observability must never crash the app
122
+ }
123
+ }
124
+
125
+ // ─── Helpers ─────────────────────────────────────────────────
126
+
127
+ function inferCategory(action: string): string {
128
+ if (action.startsWith('admin.')) return 'admin';
129
+ if (action.startsWith('payment.')) return 'billing';
130
+ if (action.startsWith('account.')) return 'data_mutation';
131
+ if (action.startsWith('auth.')) return 'authentication';
132
+ if (action.startsWith('cron.')) return 'system';
133
+ if (action.startsWith('security.')) return 'security';
134
+ return 'general';
135
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Stripe Webhook Handler
3
+ *
4
+ * Handles Stripe webhook events for subscription lifecycle management.
5
+ * Uses HMAC signature verification and Redis-based idempotency.
6
+ *
7
+ * Required env vars:
8
+ * STRIPE_SECRET_KEY - Stripe API key
9
+ * STRIPE_WEBHOOK_SECRET - Webhook signing secret (whsec_...)
10
+ */
11
+ import { NextRequest, NextResponse } from 'next/server';
12
+ import Stripe from 'stripe';
13
+ import { getRedisClient } from '@/lib/redis';
14
+ import { enforceRateLimit, AppRateLimits } from '@/lib/api-security';
15
+
16
+ export const dynamic = 'force-dynamic';
17
+
18
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
19
+ apiVersion: '2024-12-18.acacia',
20
+ });
21
+
22
+ /**
23
+ * Check if this webhook event has already been processed (idempotency).
24
+ * Uses Redis SET NX + EX pattern — atomic check-and-set with 24h TTL.
25
+ * Returns true if already processed, false if new.
26
+ */
27
+ async function isEventProcessed(eventId: string): Promise<boolean> {
28
+ const redis = getRedisClient();
29
+ if (!redis) return false; // No Redis — allow processing (best-effort)
30
+ try {
31
+ const result = await redis.set(`webhook:${eventId}`, '1', 'EX', 86400, 'NX');
32
+ return result === null; // null = key already existed = already processed
33
+ } catch {
34
+ return false; // Redis error — allow processing
35
+ }
36
+ }
37
+
38
+ export async function POST(request: NextRequest) {
39
+ // Rate limit webhook calls (generous — Stripe retries on failure)
40
+ const rateLimited = await enforceRateLimit(request, 'stripe-webhook', AppRateLimits.webhook);
41
+ if (rateLimited) return rateLimited;
42
+
43
+ const body = await request.text();
44
+ const signature = request.headers.get('stripe-signature');
45
+
46
+ if (!signature) {
47
+ return NextResponse.json({ error: 'Missing signature' }, { status: 400 });
48
+ }
49
+
50
+ let event: Stripe.Event;
51
+ try {
52
+ event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
53
+ } catch (err) {
54
+ console.error('[Stripe Webhook] Signature verification failed:', err);
55
+ return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
56
+ }
57
+
58
+ // Idempotency check — prevent duplicate processing
59
+ if (await isEventProcessed(event.id)) {
60
+ return NextResponse.json({ received: true, duplicate: true });
61
+ }
62
+
63
+ try {
64
+ switch (event.type) {
65
+ case 'checkout.session.completed': {
66
+ const session = event.data.object as Stripe.Checkout.Session;
67
+ console.log('[Stripe Webhook] Checkout completed:', session.id);
68
+ // TODO: Activate subscription, provision access, send welcome email
69
+ break;
70
+ }
71
+
72
+ case 'invoice.paid': {
73
+ const invoice = event.data.object as Stripe.Invoice;
74
+ console.log('[Stripe Webhook] Invoice paid:', invoice.id);
75
+ // TODO: Record payment, extend subscription
76
+ break;
77
+ }
78
+
79
+ case 'invoice.payment_failed': {
80
+ const invoice = event.data.object as Stripe.Invoice;
81
+ console.log('[Stripe Webhook] Payment failed:', invoice.id);
82
+ // TODO: Notify user, start grace period
83
+ break;
84
+ }
85
+
86
+ case 'customer.subscription.updated': {
87
+ const subscription = event.data.object as Stripe.Subscription;
88
+ console.log('[Stripe Webhook] Subscription updated:', subscription.id);
89
+ // TODO: Handle plan changes, cancellation scheduling
90
+ break;
91
+ }
92
+
93
+ case 'customer.subscription.deleted': {
94
+ const subscription = event.data.object as Stripe.Subscription;
95
+ console.log('[Stripe Webhook] Subscription deleted:', subscription.id);
96
+ // TODO: Revoke access, send cancellation email
97
+ break;
98
+ }
99
+
100
+ default:
101
+ console.log(`[Stripe Webhook] Unhandled event type: ${event.type}`);
102
+ }
103
+
104
+ return NextResponse.json({ received: true });
105
+ } catch (error) {
106
+ console.error('[Stripe Webhook] Handler error:', error);
107
+ return NextResponse.json({ error: 'Webhook handler failed' }, { status: 500 });
108
+ }
109
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Platform-Core Singleton
3
+ *
4
+ * Lazy-initialized platform instance with adapter auto-selection from env vars.
5
+ * Uses a promise-based lock to prevent race conditions during initialization.
6
+ *
7
+ * Usage:
8
+ * import { getPlatform } from '@/lib/platform';
9
+ * const platform = await getPlatform();
10
+ * await platform.cache.set('key', value, 3600);
11
+ */
12
+ import 'server-only';
13
+ import { createPlatformAsync, type IPlatform } from '@digilogiclabs/platform-core';
14
+
15
+ let _platform: IPlatform | null = null;
16
+ let _initPromise: Promise<IPlatform> | null = null;
17
+
18
+ /**
19
+ * Get or initialize the platform singleton.
20
+ * Adapters are selected automatically based on environment variables:
21
+ * - DATABASE_URL → PostgreSQL adapter
22
+ * - REDIS_URL → Redis cache adapter
23
+ * - RESEND_API_KEY → Resend email adapter
24
+ * Falls back to memory adapters when env vars are not set.
25
+ */
26
+ export async function getPlatform(): Promise<IPlatform> {
27
+ if (_platform) return _platform;
28
+
29
+ if (!_initPromise) {
30
+ _initPromise = createPlatformAsync().then((p) => {
31
+ _platform = p;
32
+ return p;
33
+ });
34
+ }
35
+
36
+ return _initPromise;
37
+ }
@@ -0,0 +1,18 @@
1
+ import 'server-only';
2
+ import { createRedisRateLimitStore } from '@digilogiclabs/platform-core/auth';
3
+ import type { RateLimitStore } from '@digilogiclabs/platform-core/auth';
4
+ import { getRedisClient } from './redis';
5
+
6
+ let _store: RateLimitStore | null = null;
7
+
8
+ /**
9
+ * Returns a Redis-backed rate limit store, or undefined to fall back to in-memory.
10
+ * The store is created lazily on first call and reused thereafter.
11
+ */
12
+ export function getRateLimitStore(): RateLimitStore | undefined {
13
+ if (_store) return _store;
14
+ const redis = getRedisClient();
15
+ if (!redis) return undefined; // falls back to in-memory in enforceRateLimit
16
+ _store = createRedisRateLimitStore(redis, { keyPrefix: 'rl:' });
17
+ return _store;
18
+ }
@@ -0,0 +1,48 @@
1
+ import 'server-only';
2
+
3
+ /**
4
+ * Minimal Redis client interface matching ioredis.
5
+ * Avoids importing ioredis at type-check time (may not be installed yet).
6
+ */
7
+ interface RedisClient {
8
+ zremrangebyscore(key: string, min: number | string, max: number | string): Promise<number>;
9
+ zadd(key: string, score: number, member: string): Promise<number>;
10
+ zcard(key: string): Promise<number>;
11
+ expire(key: string, seconds: number): Promise<number>;
12
+ get(key: string): Promise<string | null>;
13
+ set(key: string, value: string, ...args: unknown[]): Promise<string | null>;
14
+ ttl(key: string): Promise<number>;
15
+ setex(key: string, seconds: number, value: string): Promise<string>;
16
+ del(...keys: string[]): Promise<number>;
17
+ connect(): Promise<void>;
18
+ disconnect(): Promise<void>;
19
+ }
20
+
21
+ let _client: RedisClient | null = null;
22
+
23
+ /**
24
+ * Lazy Redis client singleton.
25
+ * Returns null if REDIS_URL is not set or ioredis is not installed.
26
+ * Graceful degradation — rate limiting falls back to in-memory.
27
+ */
28
+ export function getRedisClient(): RedisClient | null {
29
+ if (_client) return _client;
30
+ const url = process.env.REDIS_URL;
31
+ if (!url) return null;
32
+
33
+ try {
34
+ // Dynamic require — only loads if ioredis is installed
35
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
36
+ const Redis = require('ioredis');
37
+ _client = new Redis(url, {
38
+ keyPrefix: process.env.REDIS_KEY_PREFIX || 'app:',
39
+ maxRetriesPerRequest: 3,
40
+ lazyConnect: true,
41
+ }) as RedisClient;
42
+ _client.connect().catch(() => {});
43
+ return _client;
44
+ } catch {
45
+ // ioredis not installed — graceful fallback
46
+ return null;
47
+ }
48
+ }
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Shared API security utilities.
3
+ *
4
+ * Built on platform-core/auth building blocks — no local reimplementations.
5
+ * Uses constantTimeEqual, classifyError, rate limiting, and audit from the package.
6
+ *
7
+ * Two usage patterns:
8
+ * 1. Wrappers: withPublicApi / withAuthenticatedApi / withAdminApi (recommended)
9
+ * 2. Primitives: enforceRateLimit, isAdminRequest, errorResponse (manual composition)
10
+ */
11
+ import 'server-only';
12
+ import { NextRequest, NextResponse } from 'next/server';
13
+ import { randomUUID } from 'crypto';
14
+ import {
15
+ // Security primitives
16
+ constantTimeEqual,
17
+ // Rate limiting
18
+ CommonRateLimits,
19
+ type RateLimitRule,
20
+ type RateLimitOptions,
21
+ // Next.js API helpers
22
+ enforceRateLimit as _enforceRateLimit,
23
+ errorResponse,
24
+ zodErrorResponse,
25
+ classifyError,
26
+ } from '@digilogiclabs/platform-core/auth';
27
+
28
+ // Trigger env validation on first import (fail-fast in production)
29
+ import { config } from '@/lib/config';
30
+
31
+ // Re-export for convenience in routes
32
+ export { escapeHtml } from '@digilogiclabs/platform-core/auth';
33
+ export { errorResponse, zodErrorResponse };
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Request ID / Correlation ID
37
+ // ---------------------------------------------------------------------------
38
+
39
+ /** Generate or extract a request ID for correlation. */
40
+ export function getRequestId(request: NextRequest): string {
41
+ return request.headers.get('x-request-id') || randomUUID();
42
+ }
43
+
44
+ /**
45
+ * Rate-limit a request using Redis-backed store (if REDIS_URL is set)
46
+ * or in-memory fallback. Wraps platform-core's enforceRateLimit to
47
+ * automatically inject the store.
48
+ */
49
+ export async function enforceRateLimit(
50
+ request: { headers: { get(name: string): string | null } },
51
+ operation: string,
52
+ rule: RateLimitRule,
53
+ options?: {
54
+ identifier?: string;
55
+ userId?: string;
56
+ rateLimitOptions?: RateLimitOptions;
57
+ }
58
+ ): Promise<Response | null> {
59
+ const { getRateLimitStore } = await import('@/lib/rate-limit-store');
60
+ const store = getRateLimitStore();
61
+ return _enforceRateLimit(request, operation, rule, {
62
+ ...options,
63
+ rateLimitOptions: { ...options?.rateLimitOptions, ...(store ? { store } : {}) },
64
+ });
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Admin / Cron auth helpers
69
+ // ---------------------------------------------------------------------------
70
+
71
+ /** Extract bearer token from Authorization header. */
72
+ function extractBearerToken(request: NextRequest): string | null {
73
+ const header = request.headers.get('authorization');
74
+ if (!header?.startsWith('Bearer ')) return null;
75
+ return header.slice(7);
76
+ }
77
+
78
+ /** Check if request has a valid admin bearer token (ADMIN_SECRET). Timing-safe. */
79
+ export function isAdminRequest(request: NextRequest): boolean {
80
+ const secret = config.adminSecret;
81
+ if (!secret) return false;
82
+ const token = extractBearerToken(request);
83
+ if (!token) return false;
84
+ return constantTimeEqual(token, secret);
85
+ }
86
+
87
+ /** Check if request has a valid cron bearer token (CRON_SECRET). Timing-safe. */
88
+ export function isCronRequest(request: NextRequest): boolean {
89
+ const secret = config.cronSecret;
90
+ if (!secret) return false;
91
+ const token = extractBearerToken(request);
92
+ if (!token) return false;
93
+ return constantTimeEqual(token, secret);
94
+ }
95
+
96
+ // ---------------------------------------------------------------------------
97
+ // Rate limiting presets — tune for your app's traffic patterns
98
+ // ---------------------------------------------------------------------------
99
+
100
+ /** App-specific rate limit presets. Extend or override as needed. */
101
+ export const AppRateLimits = {
102
+ /** Public read endpoints */
103
+ publicRead: { limit: 60, windowSeconds: 60 } satisfies RateLimitRule,
104
+ /** Authenticated mutations */
105
+ authMutation: { limit: 30, windowSeconds: 60 } satisfies RateLimitRule,
106
+ /** Admin endpoints */
107
+ admin: CommonRateLimits.adminAction,
108
+ /** Beta code validation */
109
+ betaValidation: CommonRateLimits.betaValidation,
110
+ /** Webhook endpoints (generous — Stripe retries) */
111
+ webhook: { limit: 100, windowSeconds: 60 } satisfies RateLimitRule,
112
+ } as const;
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // API Wrapper Types
116
+ // ---------------------------------------------------------------------------
117
+
118
+ interface ApiWrapperConfig {
119
+ /** Rate limit rule (defaults to preset based on wrapper type) */
120
+ rateLimit?: RateLimitRule;
121
+ /** Operation name for rate limiting and logging */
122
+ operation?: string;
123
+ }
124
+
125
+ interface AuthenticatedApiContext {
126
+ /** The authenticated session */
127
+ session: { user: { id: string; email: string; roles?: string[] } };
128
+ /** Request correlation ID */
129
+ requestId: string;
130
+ }
131
+
132
+ interface PublicApiContext {
133
+ /** Request correlation ID */
134
+ requestId: string;
135
+ }
136
+
137
+ type PublicApiHandler = (request: NextRequest, context: PublicApiContext) => Promise<NextResponse>;
138
+
139
+ type AuthenticatedApiHandler = (
140
+ request: NextRequest,
141
+ context: AuthenticatedApiContext
142
+ ) => Promise<NextResponse>;
143
+
144
+ type AdminApiHandler = (request: NextRequest, context: PublicApiContext) => Promise<NextResponse>;
145
+
146
+ type CronApiHandler = (request: NextRequest, context: PublicApiContext) => Promise<NextResponse>;
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // API Wrappers — compose auth, rate limiting, error handling automatically
150
+ // ---------------------------------------------------------------------------
151
+
152
+ /**
153
+ * Wrap a public API route with rate limiting and error handling.
154
+ * No authentication required.
155
+ *
156
+ * @example
157
+ * export const GET = withPublicApi({ operation: 'list-items' }, async (req, ctx) => {
158
+ * return NextResponse.json({ items: [] });
159
+ * });
160
+ */
161
+ export function withPublicApi(
162
+ handlerConfig: ApiWrapperConfig,
163
+ handler: PublicApiHandler
164
+ ): (request: NextRequest) => Promise<NextResponse> {
165
+ return async (request: NextRequest) => {
166
+ const requestId = getRequestId(request);
167
+ const operation = handlerConfig.operation || 'public';
168
+
169
+ try {
170
+ // Rate limiting
171
+ const rateLimited = await enforceRateLimit(
172
+ request,
173
+ operation,
174
+ handlerConfig.rateLimit || AppRateLimits.publicRead
175
+ );
176
+ if (rateLimited) return rateLimited as NextResponse;
177
+
178
+ const response = await handler(request, { requestId });
179
+ response.headers.set('x-request-id', requestId);
180
+ return response;
181
+ } catch (error) {
182
+ const { status, body } = classifyError(error, process.env.NODE_ENV === 'development');
183
+ return NextResponse.json({ ...body, requestId }, { status });
184
+ }
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Wrap an authenticated API route with session validation, rate limiting, and error handling.
190
+ * Requires a valid Auth.js session.
191
+ *
192
+ * @example
193
+ * export const POST = withAuthenticatedApi({ operation: 'create-item' }, async (req, ctx) => {
194
+ * const { session, requestId } = ctx;
195
+ * return NextResponse.json({ userId: session.user.id });
196
+ * });
197
+ */
198
+ export function withAuthenticatedApi(
199
+ handlerConfig: ApiWrapperConfig,
200
+ handler: AuthenticatedApiHandler
201
+ ): (request: NextRequest) => Promise<NextResponse> {
202
+ return async (request: NextRequest) => {
203
+ const requestId = getRequestId(request);
204
+ const operation = handlerConfig.operation || 'authenticated';
205
+
206
+ try {
207
+ // Dynamic import to avoid Edge runtime issues
208
+ const { auth } = await import('@/auth');
209
+ const session = await auth();
210
+
211
+ if (!session?.user?.id) {
212
+ return NextResponse.json({ error: 'Unauthorized', requestId }, { status: 401 });
213
+ }
214
+
215
+ // Rate limiting (per-user)
216
+ const rateLimited = await enforceRateLimit(
217
+ request,
218
+ operation,
219
+ handlerConfig.rateLimit || AppRateLimits.authMutation,
220
+ { userId: session.user.id }
221
+ );
222
+ if (rateLimited) return rateLimited as NextResponse;
223
+
224
+ const response = await handler(request, {
225
+ session: session as AuthenticatedApiContext['session'],
226
+ requestId,
227
+ });
228
+ response.headers.set('x-request-id', requestId);
229
+ return response;
230
+ } catch (error) {
231
+ const { status, body } = classifyError(error, process.env.NODE_ENV === 'development');
232
+ return NextResponse.json({ ...body, requestId }, { status });
233
+ }
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Wrap an admin API route with Bearer token auth, rate limiting, and error handling.
239
+ * Requires ADMIN_SECRET Bearer token.
240
+ *
241
+ * @example
242
+ * export const POST = withAdminApi({ operation: 'admin-action' }, async (req, ctx) => {
243
+ * return NextResponse.json({ success: true });
244
+ * });
245
+ */
246
+ export function withAdminApi(
247
+ handlerConfig: ApiWrapperConfig,
248
+ handler: AdminApiHandler
249
+ ): (request: NextRequest) => Promise<NextResponse> {
250
+ return async (request: NextRequest) => {
251
+ const requestId = getRequestId(request);
252
+ const operation = handlerConfig.operation || 'admin';
253
+
254
+ try {
255
+ if (!isAdminRequest(request)) {
256
+ return NextResponse.json({ error: 'Forbidden', requestId }, { status: 403 });
257
+ }
258
+
259
+ // Rate limiting
260
+ const rateLimited = await enforceRateLimit(
261
+ request,
262
+ operation,
263
+ handlerConfig.rateLimit || AppRateLimits.admin
264
+ );
265
+ if (rateLimited) return rateLimited as NextResponse;
266
+
267
+ const response = await handler(request, { requestId });
268
+ response.headers.set('x-request-id', requestId);
269
+ return response;
270
+ } catch (error) {
271
+ const { status, body } = classifyError(error, process.env.NODE_ENV === 'development');
272
+ return NextResponse.json({ ...body, requestId }, { status });
273
+ }
274
+ };
275
+ }
276
+
277
+ /**
278
+ * Wrap a cron/scheduled API route with CRON_SECRET Bearer token auth,
279
+ * falling back to admin session check. Uses generous rate limits since
280
+ * cron jobs are server-to-server.
281
+ *
282
+ * @example
283
+ * export const POST = withCronApi({ operation: 'daily-digest' }, async (req, ctx) => {
284
+ * // Run scheduled task...
285
+ * return NextResponse.json({ processed: 42 });
286
+ * });
287
+ */
288
+ export function withCronApi(
289
+ handlerConfig: ApiWrapperConfig,
290
+ handler: CronApiHandler
291
+ ): (request: NextRequest) => Promise<NextResponse> {
292
+ return async (request: NextRequest) => {
293
+ const requestId = getRequestId(request);
294
+ const operation = handlerConfig.operation || 'cron';
295
+
296
+ try {
297
+ // Accept CRON_SECRET Bearer token or fall back to ADMIN_SECRET
298
+ if (!isCronRequest(request) && !isAdminRequest(request)) {
299
+ return NextResponse.json({ error: 'Forbidden', requestId }, { status: 403 });
300
+ }
301
+
302
+ // Rate limiting (generous — cron jobs are server-to-server)
303
+ const rateLimited = await enforceRateLimit(
304
+ request,
305
+ operation,
306
+ handlerConfig.rateLimit || AppRateLimits.webhook
307
+ );
308
+ if (rateLimited) return rateLimited as NextResponse;
309
+
310
+ const response = await handler(request, { requestId });
311
+ response.headers.set('x-request-id', requestId);
312
+ return response;
313
+ } catch (error) {
314
+ const { status, body } = classifyError(error, process.env.NODE_ENV === 'development');
315
+ return NextResponse.json({ ...body, requestId }, { status });
316
+ }
317
+ };
318
+ }