create-nextdevtpl 2.6.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 (425) hide show
  1. package/README.md +29 -0
  2. package/catalog.json +385 -0
  3. package/dist/catalog.d.ts +3 -0
  4. package/dist/catalog.js +112 -0
  5. package/dist/catalog.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +115 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/filesystem.d.ts +10 -0
  10. package/dist/filesystem.js +86 -0
  11. package/dist/filesystem.js.map +1 -0
  12. package/dist/generate.d.ts +5 -0
  13. package/dist/generate.js +758 -0
  14. package/dist/generate.js.map +1 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +4 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/selection.d.ts +2 -0
  19. package/dist/selection.js +111 -0
  20. package/dist/selection.js.map +1 -0
  21. package/dist/service-templates.d.ts +4 -0
  22. package/dist/service-templates.js +180 -0
  23. package/dist/service-templates.js.map +1 -0
  24. package/dist/types.d.ts +65 -0
  25. package/dist/types.js +2 -0
  26. package/dist/types.js.map +1 -0
  27. package/package.json +54 -0
  28. package/template/.dockerignore +14 -0
  29. package/template/.env.example +282 -0
  30. package/template/.gitattributes +17 -0
  31. package/template/.node-version +1 -0
  32. package/template/Dockerfile +45 -0
  33. package/template/LICENSE +21 -0
  34. package/template/README.md +267 -0
  35. package/template/biome.json +68 -0
  36. package/template/cloudflare/templates/logger.ts +162 -0
  37. package/template/cloudflare/templates/mail/server.ts +6 -0
  38. package/template/cloudflare/templates/mail/templates.ts +87 -0
  39. package/template/cloudflare/templates/mail/utils.ts +69 -0
  40. package/template/cloudflare/worker.mjs +25 -0
  41. package/template/components.json +22 -0
  42. package/template/compose.yaml +74 -0
  43. package/template/deploy/check-health.mjs +25 -0
  44. package/template/deploy/docker/README.md +14 -0
  45. package/template/deploy/server/README.md +24 -0
  46. package/template/deploy/server/build.sh +24 -0
  47. package/template/deploy/server/nextdevtpl.service +19 -0
  48. package/template/deploy/server/start.sh +19 -0
  49. package/template/deploy/vercel/README.md +16 -0
  50. package/template/deploy/verify-production-health.mjs +86 -0
  51. package/template/drizzle.config.ts +47 -0
  52. package/template/gitignore +62 -0
  53. package/template/manifest.json +35 -0
  54. package/template/messages/en.json +766 -0
  55. package/template/messages/zh.json +762 -0
  56. package/template/next.config.mjs +28 -0
  57. package/template/open-next.config.ts +3 -0
  58. package/template/package.json +125 -0
  59. package/template/pnpm-lock.yaml +15901 -0
  60. package/template/postcss.config.mjs +7 -0
  61. package/template/public/file.svg +1 -0
  62. package/template/public/globe.svg +1 -0
  63. package/template/public/logo.svg +6 -0
  64. package/template/public/next.svg +1 -0
  65. package/template/public/vercel.svg +1 -0
  66. package/template/public/window.svg +1 -0
  67. package/template/sentry.client.config.ts +10 -0
  68. package/template/sentry.edge.config.ts +10 -0
  69. package/template/sentry.server.config.ts +10 -0
  70. package/template/source.config.ts +59 -0
  71. package/template/src/adapters/ai/anthropic.ts +113 -0
  72. package/template/src/adapters/ai/index.ts +6 -0
  73. package/template/src/adapters/ai/openai-compatible.ts +88 -0
  74. package/template/src/adapters/ai/workers-ai.ts +63 -0
  75. package/template/src/adapters/index.ts +10 -0
  76. package/template/src/adapters/jobs/cloudflare-workflows.ts +46 -0
  77. package/template/src/adapters/jobs/index.ts +5 -0
  78. package/template/src/adapters/jobs/inngest/adapter.ts +29 -0
  79. package/template/src/adapters/jobs/inngest/client.ts +8 -0
  80. package/template/src/adapters/jobs/inngest/functions.ts +17 -0
  81. package/template/src/adapters/jobs/inngest/handler.ts +6 -0
  82. package/template/src/adapters/jobs/inngest/index.ts +4 -0
  83. package/template/src/adapters/mail/cloudflare-email.ts +50 -0
  84. package/template/src/adapters/mail/disabled.ts +17 -0
  85. package/template/src/adapters/mail/index.ts +7 -0
  86. package/template/src/adapters/mail/resend.ts +76 -0
  87. package/template/src/adapters/mail/smtp.ts +82 -0
  88. package/template/src/adapters/payment/creem.ts +269 -0
  89. package/template/src/adapters/payment/index.ts +2 -0
  90. package/template/src/adapters/payment/stripe.ts +302 -0
  91. package/template/src/adapters/rate-limit/cloudflare.ts +42 -0
  92. package/template/src/adapters/rate-limit/index.ts +6 -0
  93. package/template/src/adapters/rate-limit/noop.ts +27 -0
  94. package/template/src/adapters/rate-limit/upstash.ts +113 -0
  95. package/template/src/adapters/registry.ts +228 -0
  96. package/template/src/adapters/storage/index.ts +3 -0
  97. package/template/src/adapters/storage/r2-binding.ts +105 -0
  98. package/template/src/adapters/storage/s3-compatible.ts +149 -0
  99. package/template/src/app/[locale]/(admin)/admin/layout.tsx +36 -0
  100. package/template/src/app/[locale]/(admin)/admin/page.tsx +345 -0
  101. package/template/src/app/[locale]/(admin)/admin/tickets/[id]/page.tsx +261 -0
  102. package/template/src/app/[locale]/(admin)/admin/tickets/page.tsx +236 -0
  103. package/template/src/app/[locale]/(admin)/admin/users/page.tsx +557 -0
  104. package/template/src/app/[locale]/(auth)/forgot-password/page.tsx +9 -0
  105. package/template/src/app/[locale]/(auth)/layout.tsx +28 -0
  106. package/template/src/app/[locale]/(auth)/sign-in/page.tsx +9 -0
  107. package/template/src/app/[locale]/(auth)/sign-up/page.tsx +9 -0
  108. package/template/src/app/[locale]/(dashboard)/dashboard/credits/buy/buy-credits-view.tsx +170 -0
  109. package/template/src/app/[locale]/(dashboard)/dashboard/credits/buy/page.tsx +12 -0
  110. package/template/src/app/[locale]/(dashboard)/dashboard/credits/page.tsx +5 -0
  111. package/template/src/app/[locale]/(dashboard)/dashboard/loading.tsx +41 -0
  112. package/template/src/app/[locale]/(dashboard)/dashboard/page.tsx +299 -0
  113. package/template/src/app/[locale]/(dashboard)/dashboard/settings/page.tsx +38 -0
  114. package/template/src/app/[locale]/(dashboard)/dashboard/support/[id]/page.tsx +220 -0
  115. package/template/src/app/[locale]/(dashboard)/dashboard/support/new/page.tsx +183 -0
  116. package/template/src/app/[locale]/(dashboard)/dashboard/support/page.tsx +145 -0
  117. package/template/src/app/[locale]/(dashboard)/layout.tsx +35 -0
  118. package/template/src/app/[locale]/(marketing)/blog/[slug]/page.tsx +178 -0
  119. package/template/src/app/[locale]/(marketing)/blog/blog-post-card.tsx +95 -0
  120. package/template/src/app/[locale]/(marketing)/blog/page.tsx +127 -0
  121. package/template/src/app/[locale]/(marketing)/demo/plan-badges/page.tsx +36 -0
  122. package/template/src/app/[locale]/(marketing)/layout.tsx +15 -0
  123. package/template/src/app/[locale]/(marketing)/legal/[slug]/page.tsx +130 -0
  124. package/template/src/app/[locale]/(marketing)/page.tsx +94 -0
  125. package/template/src/app/[locale]/(marketing)/pseo/[slug]/page.tsx +68 -0
  126. package/template/src/app/[locale]/(marketing)/pseo/page.tsx +137 -0
  127. package/template/src/app/[locale]/docs/[[...slug]]/page.tsx +63 -0
  128. package/template/src/app/[locale]/docs/layout.tsx +39 -0
  129. package/template/src/app/[locale]/layout.tsx +80 -0
  130. package/template/src/app/[locale]/opengraph-image.tsx +20 -0
  131. package/template/src/app/[locale]/twitter-image.tsx +20 -0
  132. package/template/src/app/api/auth/[...all]/route.ts +20 -0
  133. package/template/src/app/api/health/route.ts +45 -0
  134. package/template/src/app/api/inngest/route.ts +12 -0
  135. package/template/src/app/api/jobs/credits/expire/route.ts +102 -0
  136. package/template/src/app/api/search/route.ts +17 -0
  137. package/template/src/app/api/upload/presigned/route.ts +98 -0
  138. package/template/src/app/api/webhooks/payment/route.ts +363 -0
  139. package/template/src/app/globals.css +406 -0
  140. package/template/src/app/icon.svg +6 -0
  141. package/template/src/app/image-proxy/[...path]/route.ts +137 -0
  142. package/template/src/app/layout.tsx +73 -0
  143. package/template/src/app/robots.ts +29 -0
  144. package/template/src/app/sitemap.ts +68 -0
  145. package/template/src/components/motion/reveal.tsx +49 -0
  146. package/template/src/components/seo/json-ld.tsx +87 -0
  147. package/template/src/components/seo/og-image-template.tsx +118 -0
  148. package/template/src/components/ui/accordion.tsx +66 -0
  149. package/template/src/components/ui/alert-dialog.tsx +195 -0
  150. package/template/src/components/ui/avatar.tsx +53 -0
  151. package/template/src/components/ui/badge.tsx +46 -0
  152. package/template/src/components/ui/button.tsx +65 -0
  153. package/template/src/components/ui/card.tsx +92 -0
  154. package/template/src/components/ui/checkbox.tsx +32 -0
  155. package/template/src/components/ui/collapsible.tsx +33 -0
  156. package/template/src/components/ui/dialog.tsx +124 -0
  157. package/template/src/components/ui/dropdown-menu.tsx +257 -0
  158. package/template/src/components/ui/form.tsx +233 -0
  159. package/template/src/components/ui/index.ts +2 -0
  160. package/template/src/components/ui/input.tsx +21 -0
  161. package/template/src/components/ui/label.tsx +24 -0
  162. package/template/src/components/ui/navigation-menu.tsx +167 -0
  163. package/template/src/components/ui/pagination.tsx +125 -0
  164. package/template/src/components/ui/popover.tsx +48 -0
  165. package/template/src/components/ui/progress.tsx +27 -0
  166. package/template/src/components/ui/radio-group.tsx +45 -0
  167. package/template/src/components/ui/select.tsx +190 -0
  168. package/template/src/components/ui/separator.tsx +28 -0
  169. package/template/src/components/ui/sheet.tsx +140 -0
  170. package/template/src/components/ui/switch.tsx +31 -0
  171. package/template/src/components/ui/tabs.tsx +75 -0
  172. package/template/src/components/ui/textarea.tsx +18 -0
  173. package/template/src/config/index.ts +31 -0
  174. package/template/src/config/nav.ts +238 -0
  175. package/template/src/config/payment.ts +282 -0
  176. package/template/src/config/site.ts +53 -0
  177. package/template/src/config/subscription-plan.ts +164 -0
  178. package/template/src/content/blog/en/hello-world.mdx +48 -0
  179. package/template/src/content/blog/zh/hello-world.mdx +48 -0
  180. package/template/src/content/blog/zh/nextdevtpl-linuxdo-intro.mdx +98 -0
  181. package/template/src/content/docs/en/admin-and-support.mdx +65 -0
  182. package/template/src/content/docs/en/authentication.mdx +71 -0
  183. package/template/src/content/docs/en/background-jobs.mdx +53 -0
  184. package/template/src/content/docs/en/configuration.mdx +71 -0
  185. package/template/src/content/docs/en/credits.mdx +69 -0
  186. package/template/src/content/docs/en/customization.mdx +95 -0
  187. package/template/src/content/docs/en/database.mdx +66 -0
  188. package/template/src/content/docs/en/deployment.mdx +62 -0
  189. package/template/src/content/docs/en/email.mdx +48 -0
  190. package/template/src/content/docs/en/index.mdx +48 -0
  191. package/template/src/content/docs/en/internationalization.mdx +78 -0
  192. package/template/src/content/docs/en/meta.json +22 -0
  193. package/template/src/content/docs/en/observability.mdx +69 -0
  194. package/template/src/content/docs/en/payments.mdx +64 -0
  195. package/template/src/content/docs/en/project-structure.mdx +82 -0
  196. package/template/src/content/docs/en/quick-start.mdx +113 -0
  197. package/template/src/content/docs/en/storage.mdx +51 -0
  198. package/template/src/content/docs/zh/admin-and-support.mdx +65 -0
  199. package/template/src/content/docs/zh/authentication.mdx +71 -0
  200. package/template/src/content/docs/zh/background-jobs.mdx +53 -0
  201. package/template/src/content/docs/zh/configuration.mdx +70 -0
  202. package/template/src/content/docs/zh/credits.mdx +69 -0
  203. package/template/src/content/docs/zh/customization.mdx +95 -0
  204. package/template/src/content/docs/zh/database.mdx +66 -0
  205. package/template/src/content/docs/zh/deployment.mdx +61 -0
  206. package/template/src/content/docs/zh/email.mdx +47 -0
  207. package/template/src/content/docs/zh/index.mdx +48 -0
  208. package/template/src/content/docs/zh/internationalization.mdx +78 -0
  209. package/template/src/content/docs/zh/meta.json +22 -0
  210. package/template/src/content/docs/zh/observability.mdx +68 -0
  211. package/template/src/content/docs/zh/payments.mdx +64 -0
  212. package/template/src/content/docs/zh/project-structure.mdx +82 -0
  213. package/template/src/content/docs/zh/quick-start.mdx +110 -0
  214. package/template/src/content/docs/zh/storage.mdx +51 -0
  215. package/template/src/content/legal/en/cookie-policy.mdx +78 -0
  216. package/template/src/content/legal/en/privacy.mdx +131 -0
  217. package/template/src/content/legal/en/terms.mdx +93 -0
  218. package/template/src/content/legal/zh/cookie-policy.mdx +78 -0
  219. package/template/src/content/legal/zh/privacy.mdx +131 -0
  220. package/template/src/content/legal/zh/terms.mdx +92 -0
  221. package/template/src/core/modules/contract.ts +225 -0
  222. package/template/src/core/modules/index.ts +1 -0
  223. package/template/src/core/services/ai.ts +31 -0
  224. package/template/src/core/services/common.ts +112 -0
  225. package/template/src/core/services/index.ts +8 -0
  226. package/template/src/core/services/jobs.ts +24 -0
  227. package/template/src/core/services/mail.ts +35 -0
  228. package/template/src/core/services/manifest.ts +51 -0
  229. package/template/src/core/services/payment.ts +92 -0
  230. package/template/src/core/services/rate-limit.ts +47 -0
  231. package/template/src/core/services/storage.ts +42 -0
  232. package/template/src/db/index.ts +71 -0
  233. package/template/src/db/schema/auth.ts +67 -0
  234. package/template/src/db/schema/credits.ts +94 -0
  235. package/template/src/db/schema/index.ts +5 -0
  236. package/template/src/db/schema/mail.ts +14 -0
  237. package/template/src/db/schema/subscription.ts +21 -0
  238. package/template/src/db/schema/support.ts +56 -0
  239. package/template/src/db/schema.ts +2 -0
  240. package/template/src/features/admin/components/admin-sidebar.tsx +213 -0
  241. package/template/src/features/admin/components/index.ts +2 -0
  242. package/template/src/features/admin/index.ts +3 -0
  243. package/template/src/features/admin/manifest.ts +41 -0
  244. package/template/src/features/analytics/components/analytics.tsx +62 -0
  245. package/template/src/features/analytics/components/index.ts +2 -0
  246. package/template/src/features/analytics/index.ts +3 -0
  247. package/template/src/features/analytics/manifest.ts +13 -0
  248. package/template/src/features/auth/components/auth-brand-panel.tsx +82 -0
  249. package/template/src/features/auth/components/auth-error-alert.tsx +28 -0
  250. package/template/src/features/auth/components/auth-footer.tsx +52 -0
  251. package/template/src/features/auth/components/auth-logo.tsx +30 -0
  252. package/template/src/features/auth/components/forgot-password-form.tsx +146 -0
  253. package/template/src/features/auth/components/index.ts +9 -0
  254. package/template/src/features/auth/components/sign-in-form.tsx +241 -0
  255. package/template/src/features/auth/components/sign-up-form.tsx +307 -0
  256. package/template/src/features/auth/index.ts +3 -0
  257. package/template/src/features/auth/manifest.ts +30 -0
  258. package/template/src/features/blog/components/blog-post-item.tsx +72 -0
  259. package/template/src/features/blog/components/index.ts +1 -0
  260. package/template/src/features/blog/data/mock-posts.ts +62 -0
  261. package/template/src/features/blog/index.ts +3 -0
  262. package/template/src/features/blog/manifest.ts +22 -0
  263. package/template/src/features/credits/account.ts +76 -0
  264. package/template/src/features/credits/actions.ts +427 -0
  265. package/template/src/features/credits/components/credit-balance-badge.tsx +54 -0
  266. package/template/src/features/credits/components/credit-usage-section.tsx +160 -0
  267. package/template/src/features/credits/components/index.ts +7 -0
  268. package/template/src/features/credits/components/transaction-history.tsx +346 -0
  269. package/template/src/features/credits/config.ts +70 -0
  270. package/template/src/features/credits/consume.ts +140 -0
  271. package/template/src/features/credits/core.ts +23 -0
  272. package/template/src/features/credits/errors.ts +26 -0
  273. package/template/src/features/credits/expire.ts +112 -0
  274. package/template/src/features/credits/grant.ts +168 -0
  275. package/template/src/features/credits/index.ts +6 -0
  276. package/template/src/features/credits/manifest.ts +36 -0
  277. package/template/src/features/credits/query.ts +65 -0
  278. package/template/src/features/credits/types.ts +48 -0
  279. package/template/src/features/dashboard/components/index.ts +3 -0
  280. package/template/src/features/dashboard/components/main-wrapper.tsx +100 -0
  281. package/template/src/features/dashboard/components/sidebar.tsx +341 -0
  282. package/template/src/features/dashboard/context/index.ts +2 -0
  283. package/template/src/features/dashboard/context/sidebar-context.tsx +72 -0
  284. package/template/src/features/dashboard/index.ts +4 -0
  285. package/template/src/features/dashboard/manifest.ts +28 -0
  286. package/template/src/features/mail/actions.ts +175 -0
  287. package/template/src/features/mail/index.ts +5 -0
  288. package/template/src/features/mail/manifest.ts +13 -0
  289. package/template/src/features/mail/server.ts +8 -0
  290. package/template/src/features/mail/templates/index.ts +11 -0
  291. package/template/src/features/mail/templates/primary-action-email.tsx +224 -0
  292. package/template/src/features/mail/templates/welcome-email.tsx +121 -0
  293. package/template/src/features/mail/utils.ts +190 -0
  294. package/template/src/features/marketing/components/animated-price.tsx +51 -0
  295. package/template/src/features/marketing/components/cookie-consent.tsx +255 -0
  296. package/template/src/features/marketing/components/cta-section.tsx +50 -0
  297. package/template/src/features/marketing/components/faq-section.tsx +59 -0
  298. package/template/src/features/marketing/components/feature-grid.tsx +80 -0
  299. package/template/src/features/marketing/components/footer.tsx +128 -0
  300. package/template/src/features/marketing/components/header.tsx +255 -0
  301. package/template/src/features/marketing/components/hero-section.tsx +323 -0
  302. package/template/src/features/marketing/components/how-it-works.tsx +115 -0
  303. package/template/src/features/marketing/components/index.ts +14 -0
  304. package/template/src/features/marketing/components/nav-menu.tsx +236 -0
  305. package/template/src/features/marketing/components/pricing-section.tsx +446 -0
  306. package/template/src/features/marketing/components/testimonials.tsx +59 -0
  307. package/template/src/features/marketing/components/use-cases-section.tsx +78 -0
  308. package/template/src/features/marketing/index.ts +3 -0
  309. package/template/src/features/marketing/manifest.ts +35 -0
  310. package/template/src/features/payment/actions.ts +182 -0
  311. package/template/src/features/payment/index.ts +4 -0
  312. package/template/src/features/payment/manifest.ts +18 -0
  313. package/template/src/features/payment/server.ts +7 -0
  314. package/template/src/features/payment/types.ts +202 -0
  315. package/template/src/features/pseo/components/index.ts +6 -0
  316. package/template/src/features/pseo/components/pseo-cta.tsx +51 -0
  317. package/template/src/features/pseo/components/pseo-faq.tsx +40 -0
  318. package/template/src/features/pseo/components/pseo-feature-grid.tsx +74 -0
  319. package/template/src/features/pseo/components/pseo-hero.tsx +106 -0
  320. package/template/src/features/pseo/components/pseo-related.tsx +62 -0
  321. package/template/src/features/pseo/components/pseo-use-cases.tsx +50 -0
  322. package/template/src/features/pseo/data/pseo-pages.json +534 -0
  323. package/template/src/features/pseo/index.ts +4 -0
  324. package/template/src/features/pseo/lib/pseo-data.ts +140 -0
  325. package/template/src/features/pseo/manifest.ts +22 -0
  326. package/template/src/features/settings/actions/delete-account.ts +50 -0
  327. package/template/src/features/settings/actions/index.ts +3 -0
  328. package/template/src/features/settings/actions/update-profile.ts +49 -0
  329. package/template/src/features/settings/components/billing-section.tsx +283 -0
  330. package/template/src/features/settings/components/index.ts +3 -0
  331. package/template/src/features/settings/components/profile-form.tsx +154 -0
  332. package/template/src/features/settings/components/security-section.tsx +158 -0
  333. package/template/src/features/settings/components/settings-profile-view.tsx +506 -0
  334. package/template/src/features/settings/index.ts +5 -0
  335. package/template/src/features/settings/manifest.ts +28 -0
  336. package/template/src/features/settings/schemas/index.ts +2 -0
  337. package/template/src/features/settings/schemas/update-profile.ts +21 -0
  338. package/template/src/features/shared/components/cookie-settings-dialog.tsx +282 -0
  339. package/template/src/features/shared/components/index.ts +5 -0
  340. package/template/src/features/shared/components/language-switcher.tsx +82 -0
  341. package/template/src/features/shared/components/max-width-wrapper.tsx +49 -0
  342. package/template/src/features/shared/components/mode-toggle.tsx +119 -0
  343. package/template/src/features/shared/icons/google-icon.tsx +39 -0
  344. package/template/src/features/shared/icons/index.ts +7 -0
  345. package/template/src/features/shared/index.ts +5 -0
  346. package/template/src/features/shared/manifest.ts +13 -0
  347. package/template/src/features/shared/providers.tsx +59 -0
  348. package/template/src/features/storage/actions.ts +107 -0
  349. package/template/src/features/storage/index.ts +6 -0
  350. package/template/src/features/storage/manifest.ts +22 -0
  351. package/template/src/features/storage/types.ts +143 -0
  352. package/template/src/features/storage/utils.ts +85 -0
  353. package/template/src/features/storage/validation.ts +151 -0
  354. package/template/src/features/subscription/actions/get-user-plan.ts +26 -0
  355. package/template/src/features/subscription/actions/index.ts +5 -0
  356. package/template/src/features/subscription/components/current-plan-badge.tsx +18 -0
  357. package/template/src/features/subscription/components/index.ts +6 -0
  358. package/template/src/features/subscription/components/plan-badge.tsx +98 -0
  359. package/template/src/features/subscription/index.ts +25 -0
  360. package/template/src/features/subscription/manifest.ts +13 -0
  361. package/template/src/features/subscription/services/user-plan.ts +181 -0
  362. package/template/src/features/support/actions/admin-ticket.ts +172 -0
  363. package/template/src/features/support/actions/admin-users.ts +295 -0
  364. package/template/src/features/support/actions/index.ts +22 -0
  365. package/template/src/features/support/actions/ticket.ts +12 -0
  366. package/template/src/features/support/actions/user-ticket.ts +153 -0
  367. package/template/src/features/support/components/admin-ticket-reply-form.tsx +109 -0
  368. package/template/src/features/support/components/admin-ticket-status-select.tsx +119 -0
  369. package/template/src/features/support/components/index.ts +6 -0
  370. package/template/src/features/support/components/ticket-message-form.tsx +99 -0
  371. package/template/src/features/support/components/user-role-select.tsx +107 -0
  372. package/template/src/features/support/index.ts +5 -0
  373. package/template/src/features/support/manifest.ts +53 -0
  374. package/template/src/features/support/schemas/index.ts +15 -0
  375. package/template/src/features/support/schemas/ticket.ts +81 -0
  376. package/template/src/hooks/index.ts +2 -0
  377. package/template/src/i18n/index.ts +12 -0
  378. package/template/src/i18n/request.ts +24 -0
  379. package/template/src/i18n/routing.ts +23 -0
  380. package/template/src/instrumentation.ts +19 -0
  381. package/template/src/lib/ai/index.ts +16 -0
  382. package/template/src/lib/api-logger.ts +24 -0
  383. package/template/src/lib/auth/admin.ts +54 -0
  384. package/template/src/lib/auth/api.ts +17 -0
  385. package/template/src/lib/auth/client.ts +227 -0
  386. package/template/src/lib/auth/edge.ts +84 -0
  387. package/template/src/lib/auth/errors.ts +99 -0
  388. package/template/src/lib/auth/index.ts +159 -0
  389. package/template/src/lib/auth/server.ts +46 -0
  390. package/template/src/lib/auth/session-context.tsx +96 -0
  391. package/template/src/lib/cloudflare/bindings.ts +31 -0
  392. package/template/src/lib/cookie-consent.ts +33 -0
  393. package/template/src/lib/file-utils.ts +45 -0
  394. package/template/src/lib/fingerprint/index.ts +73 -0
  395. package/template/src/lib/health/checks.ts +6 -0
  396. package/template/src/lib/health/core.ts +86 -0
  397. package/template/src/lib/health/index.ts +11 -0
  398. package/template/src/lib/health/types.ts +13 -0
  399. package/template/src/lib/index.ts +2 -0
  400. package/template/src/lib/logger/index.ts +258 -0
  401. package/template/src/lib/monitoring/index.ts +384 -0
  402. package/template/src/lib/rate-limit/anonymous.ts +85 -0
  403. package/template/src/lib/rate-limit/index.ts +111 -0
  404. package/template/src/lib/safe-action.ts +127 -0
  405. package/template/src/lib/seo/json-ld.ts +188 -0
  406. package/template/src/lib/source.ts +122 -0
  407. package/template/src/lib/utils.ts +6 -0
  408. package/template/src/middleware.ts +162 -0
  409. package/template/src/modules/index.ts +10 -0
  410. package/template/src/modules/presets.ts +8 -0
  411. package/template/src/modules/registry.ts +34 -0
  412. package/template/src/modules/selection.ts +45 -0
  413. package/template/src/services/ai.ts +49 -0
  414. package/template/src/services/index.ts +6 -0
  415. package/template/src/services/jobs.ts +3 -0
  416. package/template/src/services/mail.ts +7 -0
  417. package/template/src/services/payment.ts +3 -0
  418. package/template/src/services/rate-limit.ts +12 -0
  419. package/template/src/services/storage.ts +3 -0
  420. package/template/src/types/fumadocs-source.d.ts +14 -0
  421. package/template/src/types/index.ts +2 -0
  422. package/template/tsconfig.json +53 -0
  423. package/template/vercel.json +11 -0
  424. package/template/vitest.config.ts +51 -0
  425. package/template/wrangler.jsonc +30 -0
@@ -0,0 +1,255 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+ import { useCallback, useEffect, useState } from "react";
5
+
6
+ import { Button } from "@/components/ui/button";
7
+ import { Switch } from "@/components/ui/switch";
8
+ import { Link } from "@/i18n/routing";
9
+ import {
10
+ COOKIE_CONSENT_CHANGE_EVENT,
11
+ COOKIE_CONSENT_KEY,
12
+ COOKIE_PREFERENCES_KEY,
13
+ type CookieConsentType,
14
+ type CookiePreferences,
15
+ } from "@/lib/cookie-consent";
16
+ import { cn } from "@/lib/utils";
17
+
18
+ /**
19
+ * 默认 Cookie 偏好
20
+ */
21
+ const DEFAULT_PREFERENCES: CookiePreferences = {
22
+ analytics: true,
23
+ marketing: true,
24
+ };
25
+
26
+ /**
27
+ * Cookie Consent Banner 组件
28
+ *
29
+ * 功能:
30
+ * - 首次访问时显示 Cookie 同意横幅
31
+ * - 用户可选择接受全部、仅必要或管理偏好
32
+ * - 选择后存储到 localStorage
33
+ * - 支持动画过渡效果
34
+ * - 跟踪用户的具体偏好设置
35
+ * - 支持多语言 (中英文)
36
+ */
37
+ export function CookieConsent() {
38
+ const t = useTranslations("Cookie");
39
+
40
+ // 是否显示横幅
41
+ const [isVisible, setIsVisible] = useState(false);
42
+ // 是否显示详细设置面板
43
+ const [showDetails, setShowDetails] = useState(false);
44
+ // Cookie 偏好设置
45
+ const [preferences, setPreferences] =
46
+ useState<CookiePreferences>(DEFAULT_PREFERENCES);
47
+
48
+ /**
49
+ * 检查是否已有 Cookie 同意记录
50
+ */
51
+ useEffect(() => {
52
+ const consent = localStorage.getItem(COOKIE_CONSENT_KEY);
53
+ if (!consent) {
54
+ // 延迟显示,避免页面加载时闪烁
55
+ const timer = setTimeout(() => setIsVisible(true), 1000);
56
+ return () => clearTimeout(timer);
57
+ }
58
+ // 加载已保存的偏好设置
59
+ const savedPreferences = localStorage.getItem(COOKIE_PREFERENCES_KEY);
60
+ if (savedPreferences) {
61
+ try {
62
+ setPreferences(JSON.parse(savedPreferences));
63
+ } catch {
64
+ // 忽略解析错误
65
+ }
66
+ }
67
+ return undefined;
68
+ }, []);
69
+
70
+ /**
71
+ * 保存 Cookie 同意设置
72
+ */
73
+ const saveConsent = useCallback(
74
+ (consent: CookieConsentType, prefs?: CookiePreferences) => {
75
+ localStorage.setItem(COOKIE_CONSENT_KEY, consent || "");
76
+ if (prefs) {
77
+ localStorage.setItem(COOKIE_PREFERENCES_KEY, JSON.stringify(prefs));
78
+ }
79
+ // 触发自定义事件通知 Analytics 组件
80
+ window.dispatchEvent(new CustomEvent(COOKIE_CONSENT_CHANGE_EVENT));
81
+ setIsVisible(false);
82
+ },
83
+ []
84
+ );
85
+
86
+ /**
87
+ * 处理接受全部 Cookie
88
+ */
89
+ const handleAcceptAll = useCallback(() => {
90
+ const allPreferences: CookiePreferences = {
91
+ analytics: true,
92
+ marketing: true,
93
+ };
94
+ setPreferences(allPreferences);
95
+ saveConsent("all", allPreferences);
96
+ }, [saveConsent]);
97
+
98
+ /**
99
+ * 处理仅接受必要 Cookie
100
+ */
101
+ const handleRejectAll = useCallback(() => {
102
+ const essentialPreferences: CookiePreferences = {
103
+ analytics: false,
104
+ marketing: false,
105
+ };
106
+ setPreferences(essentialPreferences);
107
+ saveConsent("essential", essentialPreferences);
108
+ }, [saveConsent]);
109
+
110
+ /**
111
+ * 处理保存当前偏好设置
112
+ */
113
+ const handleSavePreferences = useCallback(() => {
114
+ // 根据偏好确定同意类型
115
+ const consentType: CookieConsentType =
116
+ preferences.analytics || preferences.marketing ? "all" : "essential";
117
+ saveConsent(consentType, preferences);
118
+ }, [preferences, saveConsent]);
119
+
120
+ /**
121
+ * 更新偏好设置
122
+ */
123
+ const updatePreference = useCallback(
124
+ (key: keyof CookiePreferences, value: boolean) => {
125
+ setPreferences((prev) => ({ ...prev, [key]: value }));
126
+ },
127
+ []
128
+ );
129
+
130
+ // 不显示时返回 null
131
+ if (!isVisible) return null;
132
+
133
+ return (
134
+ <div
135
+ className={cn(
136
+ "fixed bottom-0 left-0 right-0 z-50 p-4",
137
+ "animate-in slide-in-from-bottom-5 duration-300"
138
+ )}
139
+ >
140
+ <div className="container">
141
+ <div className="rounded-lg border bg-background p-6 shadow-lg">
142
+ {!showDetails ? (
143
+ // 简洁视图
144
+ <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
145
+ <div className="flex-1">
146
+ <h3 className="text-lg font-semibold">{t("title")}</h3>
147
+ <p className="mt-1 text-sm text-muted-foreground">
148
+ {t("description")}
149
+ <Link
150
+ href="/legal/privacy"
151
+ className="ml-1 underline underline-offset-4 hover:text-foreground"
152
+ >
153
+ {t("privacyPolicy")}
154
+ </Link>
155
+
156
+ </p>
157
+ </div>
158
+ <div className="flex flex-wrap items-center gap-2">
159
+ <Button
160
+ variant="outline"
161
+ size="sm"
162
+ onClick={() => setShowDetails(true)}
163
+ >
164
+ {t("managePreferences")}
165
+ </Button>
166
+ <Button variant="outline" size="sm" onClick={handleRejectAll}>
167
+ {t("rejectAll")}
168
+ </Button>
169
+ <Button size="sm" onClick={handleAcceptAll}>
170
+ {t("acceptAll")}
171
+ </Button>
172
+ </div>
173
+ </div>
174
+ ) : (
175
+ // 详细设置视图
176
+ <div className="space-y-4">
177
+ <div className="flex items-center justify-between">
178
+ <h3 className="text-lg font-semibold">
179
+ {t("preferencesTitle")}
180
+ </h3>
181
+ <Button
182
+ variant="ghost"
183
+ size="sm"
184
+ onClick={() => setShowDetails(false)}
185
+ >
186
+ {t("back")}
187
+ </Button>
188
+ </div>
189
+
190
+ <div className="space-y-3">
191
+ {/* 必要 Cookie */}
192
+ <div className="flex items-start justify-between rounded-md border p-4">
193
+ <div className="flex-1">
194
+ <h4 className="font-medium">{t("essential.title")}</h4>
195
+ <p className="mt-1 text-sm text-muted-foreground">
196
+ {t("essential.description")}
197
+ </p>
198
+ </div>
199
+ <div className="ml-4 text-sm text-muted-foreground">
200
+ {t("alwaysEnabled")}
201
+ </div>
202
+ </div>
203
+
204
+ {/* 分析 Cookie */}
205
+ <div className="flex items-start justify-between rounded-md border p-4">
206
+ <div className="flex-1">
207
+ <h4 className="font-medium">{t("analytics.title")}</h4>
208
+ <p className="mt-1 text-sm text-muted-foreground">
209
+ {t("analytics.description")}
210
+ </p>
211
+ </div>
212
+ <div className="ml-4">
213
+ <Switch
214
+ checked={preferences.analytics}
215
+ onCheckedChange={(checked) =>
216
+ updatePreference("analytics", checked)
217
+ }
218
+ />
219
+ </div>
220
+ </div>
221
+
222
+ {/* 营销 Cookie */}
223
+ <div className="flex items-start justify-between rounded-md border p-4">
224
+ <div className="flex-1">
225
+ <h4 className="font-medium">{t("marketing.title")}</h4>
226
+ <p className="mt-1 text-sm text-muted-foreground">
227
+ {t("marketing.description")}
228
+ </p>
229
+ </div>
230
+ <div className="ml-4">
231
+ <Switch
232
+ checked={preferences.marketing}
233
+ onCheckedChange={(checked) =>
234
+ updatePreference("marketing", checked)
235
+ }
236
+ />
237
+ </div>
238
+ </div>
239
+ </div>
240
+
241
+ <div className="flex justify-end gap-2 pt-2">
242
+ <Button variant="outline" size="sm" onClick={handleRejectAll}>
243
+ {t("rejectAll")}
244
+ </Button>
245
+ <Button size="sm" onClick={handleSavePreferences}>
246
+ {t("saveSettings")}
247
+ </Button>
248
+ </div>
249
+ </div>
250
+ )}
251
+ </div>
252
+ </div>
253
+ </div>
254
+ );
255
+ }
@@ -0,0 +1,50 @@
1
+ "use client";
2
+
3
+ import { ArrowRight } from "lucide-react";
4
+ import Link from "next/link";
5
+ import { useTranslations } from "next-intl";
6
+
7
+ import { Reveal } from "@/components/motion/reveal";
8
+ import { Button } from "@/components/ui/button";
9
+
10
+ export function CTASection() {
11
+ const t = useTranslations("CTA");
12
+
13
+ return (
14
+ <section className="border-t py-24">
15
+ <div className="container">
16
+ <Reveal>
17
+ {/* spec-sheet 风格盒子:卡片底 + 顶部主色光晕 + 网格 + 颗粒 */}
18
+ <div className="aura grain relative overflow-hidden rounded-2xl border bg-card px-6 py-16 text-center shadow-soft md:px-16 md:py-20">
19
+ <div
20
+ aria-hidden
21
+ className="bg-grid pointer-events-none absolute inset-0 opacity-60 [mask-image:radial-gradient(60%_60%_at_50%_0%,black,transparent)]"
22
+ />
23
+ <div className="relative mx-auto max-w-2xl">
24
+ <span className="eyebrow justify-center">{t("badge")}</span>
25
+
26
+ <h2 className="mt-5 text-balance text-3xl font-extrabold tracking-tight text-foreground md:text-4xl">
27
+ {t("title")}
28
+ </h2>
29
+ <p className="mx-auto mt-4 max-w-xl text-muted-foreground">
30
+ {t("subtitle")}
31
+ </p>
32
+
33
+ <div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
34
+ <Button size="lg" asChild className="group">
35
+ <Link href="/sign-up">
36
+ {t("getStarted")}
37
+ <ArrowRight className="transition-transform group-hover:translate-x-0.5" />
38
+ </Link>
39
+ </Button>
40
+ <Button size="lg" variant="outline" asChild>
41
+ <Link href="/dashboard/generate">{t("seeDemo")}</Link>
42
+ </Button>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </Reveal>
47
+ </div>
48
+ </section>
49
+ );
50
+ }
@@ -0,0 +1,59 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+
5
+ import { Reveal } from "@/components/motion/reveal";
6
+ import {
7
+ Accordion,
8
+ AccordionContent,
9
+ AccordionItem,
10
+ AccordionTrigger,
11
+ } from "@/components/ui/accordion";
12
+
13
+ export function FAQSection() {
14
+ const t = useTranslations("FAQ");
15
+
16
+ const faqItems = [
17
+ {
18
+ question: t("items.0.question"),
19
+ answer: t("items.0.answer"),
20
+ },
21
+ {
22
+ question: t("items.1.question"),
23
+ answer: t("items.1.answer"),
24
+ },
25
+ {
26
+ question: t("items.2.question"),
27
+ answer: t("items.2.answer"),
28
+ },
29
+ ];
30
+
31
+ return (
32
+ <section className="border-t py-24">
33
+ <div className="container max-w-3xl">
34
+ <Reveal>
35
+ <div className="mb-12 text-center">
36
+ <span className="eyebrow justify-center">{t("label")}</span>
37
+ <h2 className="mt-4 text-balance text-3xl font-extrabold tracking-tight text-foreground md:text-4xl">
38
+ {t("title")}
39
+ </h2>
40
+ </div>
41
+ </Reveal>
42
+
43
+ <Reveal delay={0.08}>
44
+ <Accordion type="single" collapsible className="w-full">
45
+ {faqItems.map((faq, index) => (
46
+ // biome-ignore lint/suspicious/noArrayIndexKey: static FAQ list
47
+ <AccordionItem key={index} value={`item-${index}`}>
48
+ <AccordionTrigger className="text-left">
49
+ {faq.question}
50
+ </AccordionTrigger>
51
+ <AccordionContent>{faq.answer}</AccordionContent>
52
+ </AccordionItem>
53
+ ))}
54
+ </Accordion>
55
+ </Reveal>
56
+ </div>
57
+ </section>
58
+ );
59
+ }
@@ -0,0 +1,80 @@
1
+ "use client";
2
+
3
+ import {
4
+ Coins,
5
+ CreditCard,
6
+ Languages,
7
+ LayoutGrid,
8
+ ShieldCheck,
9
+ Zap,
10
+ } from "lucide-react";
11
+ import { useTranslations } from "next-intl";
12
+
13
+ import { Reveal } from "@/components/motion/reveal";
14
+
15
+ /**
16
+ * 功能模块配置
17
+ * - icon 与卡片内容匹配(认证/支付/积分/异步/国际化/管理后台)
18
+ * - path 是展示用的"工程化"代码路径标签,不需要翻译
19
+ */
20
+ const featureConfig = [
21
+ { key: "ai" as const, icon: ShieldCheck, path: "/auth" },
22
+ { key: "multiSource" as const, icon: CreditCard, path: "/payment" },
23
+ { key: "outline" as const, icon: Coins, path: "/credits" },
24
+ { key: "export" as const, icon: Zap, path: "/jobs" },
25
+ { key: "batch" as const, icon: Languages, path: "/i18n" },
26
+ { key: "multilingual" as const, icon: LayoutGrid, path: "/admin" },
27
+ ];
28
+
29
+ export function FeatureGrid() {
30
+ const t = useTranslations("Features");
31
+
32
+ return (
33
+ <section id="features" className="border-t py-24">
34
+ <div className="container">
35
+ <Reveal>
36
+ <div className="mb-14 flex flex-col gap-5 md:flex-row md:items-end md:justify-between">
37
+ <div>
38
+ <span className="eyebrow">{t("label")}</span>
39
+ <h2 className="mt-4 text-balance text-3xl font-extrabold tracking-tight text-foreground md:text-4xl">
40
+ {t("title")}
41
+ </h2>
42
+ </div>
43
+ <p className="max-w-md text-muted-foreground">{t("subtitle")}</p>
44
+ </div>
45
+ </Reveal>
46
+
47
+ <Reveal delay={0.08}>
48
+ <div className="grid gap-px overflow-hidden rounded-xl border bg-border md:grid-cols-2 lg:grid-cols-3">
49
+ {featureConfig.map((feature) => {
50
+ const Icon = feature.icon;
51
+ return (
52
+ <div
53
+ key={feature.key}
54
+ className="group relative bg-card p-7 transition-colors hover:bg-muted/40"
55
+ >
56
+ {/* 右上角代码路径索引 */}
57
+ <span className="absolute top-6 right-6 font-mono text-[10px] font-semibold tracking-[0.14em] text-muted-foreground/60 uppercase">
58
+ {feature.path}
59
+ </span>
60
+
61
+ {/* 图标:hover 时反色填充 */}
62
+ <div className="mb-5 flex h-10 w-10 items-center justify-center rounded-md bg-primary/10 text-primary ring-1 ring-primary/20 transition-colors group-hover:bg-primary group-hover:text-primary-foreground">
63
+ <Icon className="h-5 w-5" />
64
+ </div>
65
+
66
+ <h3 className="mb-2 font-semibold tracking-tight">
67
+ {t(`items.${feature.key}.title`)}
68
+ </h3>
69
+ <p className="text-sm leading-relaxed text-muted-foreground">
70
+ {t(`items.${feature.key}.description`)}
71
+ </p>
72
+ </div>
73
+ );
74
+ })}
75
+ </div>
76
+ </Reveal>
77
+ </div>
78
+ </section>
79
+ );
80
+ }
@@ -0,0 +1,128 @@
1
+ import { Github, Twitter } from "lucide-react";
2
+ import Link from "next/link";
3
+ import { useTranslations } from "next-intl";
4
+
5
+ import { footerNav, siteConfig } from "@/config";
6
+
7
+ export function Footer() {
8
+ const t = useTranslations("Footer");
9
+
10
+ return (
11
+ <footer className="border-t bg-background">
12
+ <div className="container py-16">
13
+ <div className="grid gap-12 lg:grid-cols-[1fr_1fr]">
14
+ {/* 品牌区 */}
15
+ <div>
16
+ <Link
17
+ href="/"
18
+ className="mb-4 inline-flex items-center gap-2 text-xl font-bold"
19
+ >
20
+ {/* biome-ignore lint/a11y/noSvgWithoutTitle: decorative logo */}
21
+ <svg
22
+ className="h-6 w-6 text-primary"
23
+ viewBox="0 0 24 24"
24
+ fill="none"
25
+ stroke="currentColor"
26
+ strokeWidth={2.4}
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ >
30
+ <path d="M3 17 L9 7 L13 14 L21 4" />
31
+ <circle
32
+ cx="9"
33
+ cy="7"
34
+ r="1.4"
35
+ fill="currentColor"
36
+ stroke="none"
37
+ />
38
+ <circle
39
+ cx="21"
40
+ cy="4"
41
+ r="1.4"
42
+ fill="currentColor"
43
+ stroke="none"
44
+ />
45
+ </svg>
46
+ {siteConfig.name}
47
+ </Link>
48
+ <p className="max-w-sm text-sm text-muted-foreground">
49
+ {t("description")}
50
+ </p>
51
+ </div>
52
+
53
+ {/* 链接区 */}
54
+ <div className="grid grid-cols-2 gap-8">
55
+ {/* 产品 */}
56
+ <div>
57
+ <h3 className="mb-4 text-sm font-semibold">{t("product")}</h3>
58
+ <ul className="space-y-3">
59
+ {footerNav.product.map((link) => (
60
+ <li key={link.href}>
61
+ <Link
62
+ href={link.href}
63
+ {...(link.external
64
+ ? { target: "_blank", rel: "noopener noreferrer" }
65
+ : {})}
66
+ className="text-sm text-muted-foreground transition-colors hover:text-foreground"
67
+ >
68
+ {link.title}
69
+ </Link>
70
+ </li>
71
+ ))}
72
+ </ul>
73
+ </div>
74
+
75
+ {/* 法律 */}
76
+ <div>
77
+ <h3 className="mb-4 text-sm font-semibold">{t("legal")}</h3>
78
+ <ul className="space-y-3">
79
+ {footerNav.legal.map((link) => (
80
+ <li key={link.href}>
81
+ <Link
82
+ href={link.href}
83
+ className="text-sm text-muted-foreground transition-colors hover:text-foreground"
84
+ >
85
+ {link.title}
86
+ </Link>
87
+ </li>
88
+ ))}
89
+ </ul>
90
+ </div>
91
+ </div>
92
+ </div>
93
+
94
+ {/* 底部栏 */}
95
+ <div className="mt-12 flex flex-col items-center justify-between gap-4 border-t pt-8 sm:flex-row">
96
+ <p className="font-mono text-xs tracking-wide text-muted-foreground">
97
+ {t("copyright", {
98
+ year: new Date().getFullYear(),
99
+ name: siteConfig.name,
100
+ })}
101
+ </p>
102
+
103
+ {/* 社交链接 */}
104
+ <div className="flex items-center gap-4">
105
+ <Link
106
+ href={siteConfig.links.twitter}
107
+ target="_blank"
108
+ rel="noopener noreferrer"
109
+ className="text-muted-foreground transition-colors hover:text-foreground"
110
+ >
111
+ <Twitter className="h-5 w-5" />
112
+ <span className="sr-only">{t("twitter")}</span>
113
+ </Link>
114
+ <Link
115
+ href={siteConfig.links.github}
116
+ target="_blank"
117
+ rel="noopener noreferrer"
118
+ className="text-muted-foreground transition-colors hover:text-foreground"
119
+ >
120
+ <Github className="h-5 w-5" />
121
+ <span className="sr-only">{t("github")}</span>
122
+ </Link>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </footer>
127
+ );
128
+ }