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,178 @@
1
+ import type { Metadata } from "next";
2
+ import { notFound } from "next/navigation";
3
+ import { ArticleJsonLd, BreadcrumbJsonLd } from "@/components/seo/json-ld";
4
+ import { siteConfig } from "@/config";
5
+ import { Link } from "@/i18n/routing";
6
+ import { getAllBlogSlugs, getBlogPost } from "@/lib/source";
7
+
8
+ /**
9
+ * 生成静态参数
10
+ */
11
+ export function generateStaticParams() {
12
+ return getAllBlogSlugs();
13
+ }
14
+
15
+ /**
16
+ * 生成页面元数据 (enhanced with article metadata)
17
+ */
18
+ export async function generateMetadata({
19
+ params,
20
+ }: {
21
+ params: Promise<{ locale: string; slug: string }>;
22
+ }): Promise<Metadata> {
23
+ const { locale, slug } = await params;
24
+ const post = getBlogPost(slug, locale);
25
+
26
+ if (!post) {
27
+ return {
28
+ title: "Post Not Found",
29
+ };
30
+ }
31
+
32
+ const publishedDate =
33
+ typeof post.date === "string" ? post.date : post.date.toISOString();
34
+
35
+ const url = `${siteConfig.url}/${locale}/blog/${slug}`;
36
+
37
+ return {
38
+ title: post.title,
39
+ description: post.description,
40
+ authors: post.author ? [{ name: post.author }] : undefined,
41
+ openGraph: {
42
+ title: post.title,
43
+ description: post.description,
44
+ type: "article",
45
+ url,
46
+ siteName: siteConfig.name,
47
+ publishedTime: publishedDate,
48
+ authors: post.author ? [post.author] : undefined,
49
+ tags: post.tags,
50
+ images: [
51
+ {
52
+ url: `${siteConfig.url}${siteConfig.ogImage}`,
53
+ width: 1200,
54
+ height: 630,
55
+ alt: post.title,
56
+ },
57
+ ],
58
+ },
59
+ twitter: {
60
+ card: "summary_large_image",
61
+ title: post.title,
62
+ description: post.description,
63
+ },
64
+ alternates: {
65
+ canonical: url,
66
+ languages: {
67
+ en: `${siteConfig.url}/en/blog/${slug}`,
68
+ zh: `${siteConfig.url}/zh/blog/${slug}`,
69
+ },
70
+ },
71
+ };
72
+ }
73
+
74
+ /**
75
+ * 博客文章详情页面
76
+ */
77
+ export default async function BlogPostPage({
78
+ params,
79
+ }: {
80
+ params: Promise<{ locale: string; slug: string }>;
81
+ }) {
82
+ const { locale, slug } = await params;
83
+ const post = getBlogPost(slug, locale);
84
+
85
+ if (!post) {
86
+ notFound();
87
+ }
88
+
89
+ // 获取 MDX 内容组件
90
+ const MDXContent = post.body;
91
+
92
+ // 格式化日期
93
+ const formattedDate =
94
+ typeof post.date === "string"
95
+ ? post.date
96
+ : post.date.toLocaleDateString(locale === "zh" ? "zh-CN" : "en-US", {
97
+ year: "numeric",
98
+ month: "long",
99
+ day: "numeric",
100
+ });
101
+
102
+ const isoDate =
103
+ typeof post.date === "string" ? post.date : post.date.toISOString();
104
+
105
+ return (
106
+ <article className="container mx-auto max-w-3xl py-20">
107
+ {/* JSON-LD Structured Data */}
108
+ <ArticleJsonLd
109
+ title={post.title}
110
+ description={post.description || ""}
111
+ slug={slug}
112
+ locale={locale as "en" | "zh"}
113
+ publishedAt={isoDate}
114
+ {...(post.author && { author: post.author })}
115
+ {...(post.tags && { tags: post.tags })}
116
+ />
117
+ <BreadcrumbJsonLd
118
+ items={[
119
+ { name: "Home", url: `/${locale}` },
120
+ {
121
+ name: locale === "zh" ? "博客" : "Blog",
122
+ url: `/${locale}/blog`,
123
+ },
124
+ { name: post.title, url: `/${locale}/blog/${slug}` },
125
+ ]}
126
+ />
127
+
128
+ {/* 返回链接 */}
129
+ <Link
130
+ href="/blog"
131
+ className="mb-8 inline-flex items-center text-sm text-muted-foreground hover:text-foreground"
132
+ >
133
+ ← {locale === "zh" ? "返回博客" : "Back to Blog"}
134
+ </Link>
135
+
136
+ {/* 文章头部 */}
137
+ <header className="mb-12">
138
+ {/* 标签 */}
139
+ {post.tags && post.tags.length > 0 && (
140
+ <div className="mb-4 flex flex-wrap gap-2">
141
+ {post.tags.map((tag) => (
142
+ <span
143
+ key={tag}
144
+ className="rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary"
145
+ >
146
+ {tag}
147
+ </span>
148
+ ))}
149
+ </div>
150
+ )}
151
+
152
+ {/* 标题 */}
153
+ <h1 className="mb-4 text-4xl font-bold tracking-tight md:text-5xl">
154
+ {post.title}
155
+ </h1>
156
+
157
+ {/* 描述 */}
158
+ {post.description && (
159
+ <p className="mb-6 text-xl text-muted-foreground">
160
+ {post.description}
161
+ </p>
162
+ )}
163
+
164
+ {/* 元数据 */}
165
+ <div className="flex items-center gap-4 text-sm text-muted-foreground">
166
+ {post.author && <span>{post.author}</span>}
167
+ <span>•</span>
168
+ <time dateTime={formattedDate}>{formattedDate}</time>
169
+ </div>
170
+ </header>
171
+
172
+ {/* 文章内容 */}
173
+ <div className="prose prose-lg dark:prose-invert max-w-none prose-headings:font-bold prose-a:text-primary prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-pre:bg-muted">
174
+ <MDXContent />
175
+ </div>
176
+ </article>
177
+ );
178
+ }
@@ -0,0 +1,95 @@
1
+ import { Link } from "@/i18n/routing";
2
+
3
+ /**
4
+ * 博客文章卡片属性
5
+ */
6
+ interface BlogPostCardProps {
7
+ slug: string;
8
+ title: string;
9
+ description?: string | undefined;
10
+ date: string;
11
+ author?: string | undefined;
12
+ tags?: string[] | undefined;
13
+ }
14
+
15
+ /**
16
+ * 博客文章卡片组件
17
+ *
18
+ * 用于在博客列表页面显示文章摘要
19
+ */
20
+ export function BlogPostCard({
21
+ slug,
22
+ title,
23
+ description,
24
+ date,
25
+ author,
26
+ tags,
27
+ }: BlogPostCardProps) {
28
+ return (
29
+ <article className="group">
30
+ <Link
31
+ href={`/blog/${slug}`}
32
+ className="flex flex-col gap-8 md:flex-row md:items-start"
33
+ >
34
+ {/* 文本内容 */}
35
+ <div className="flex-1 space-y-4">
36
+ {/* 标签 */}
37
+ {tags && tags.length > 0 && (
38
+ <div className="flex flex-wrap gap-2">
39
+ {tags.map((tag) => (
40
+ <span
41
+ key={tag}
42
+ className="text-xs font-medium uppercase tracking-wider text-muted-foreground"
43
+ >
44
+ {tag}
45
+ </span>
46
+ ))}
47
+ </div>
48
+ )}
49
+
50
+ {/* 标题 */}
51
+ <h2 className="text-2xl font-bold tracking-tight transition-colors group-hover:text-primary md:text-3xl">
52
+ {title}
53
+ </h2>
54
+
55
+ {/* 描述 */}
56
+ {description && (
57
+ <p className="leading-relaxed text-muted-foreground">
58
+ {description}
59
+ </p>
60
+ )}
61
+
62
+ {/* 元数据 */}
63
+ <p className="text-sm text-muted-foreground">
64
+ {author && `${author} • `}
65
+ {date}
66
+ </p>
67
+ </div>
68
+
69
+ {/* 图片占位符 */}
70
+ <div className="relative aspect-video w-full shrink-0 overflow-hidden rounded-xl border bg-muted md:w-[380px]">
71
+ <div className="flex h-full items-center justify-center">
72
+ <div className="grid grid-cols-2 gap-3 p-6">
73
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-background shadow-sm">
74
+ <span className="text-lg font-bold">N</span>
75
+ </div>
76
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-background shadow-sm">
77
+ <span className="text-lg font-bold text-primary">⚛</span>
78
+ </div>
79
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-background shadow-sm">
80
+ <span className="text-lg font-bold text-muted-foreground">
81
+ TS
82
+ </span>
83
+ </div>
84
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-background shadow-sm">
85
+ <span className="text-lg font-bold text-muted-foreground">
86
+ 🌊
87
+ </span>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </Link>
93
+ </article>
94
+ );
95
+ }
@@ -0,0 +1,127 @@
1
+ import type { Metadata } from "next";
2
+ import { BreadcrumbJsonLd } from "@/components/seo/json-ld";
3
+ import { Separator } from "@/components/ui/separator";
4
+ import { siteConfig } from "@/config";
5
+ import { getBlogPosts } from "@/lib/source";
6
+
7
+ import { BlogPostCard } from "./blog-post-card";
8
+
9
+ /**
10
+ * 生成博客列表页 Metadata
11
+ */
12
+ export async function generateMetadata({
13
+ params,
14
+ }: {
15
+ params: Promise<{ locale: string }>;
16
+ }): Promise<Metadata> {
17
+ const { locale } = await params;
18
+ const isZh = locale === "zh";
19
+
20
+ const title = isZh ? "博客文章" : "Blog Posts";
21
+ const description = isZh
22
+ ? "发现 NextDevTpl 团队的最新见解、教程和更新。了解产品最新动态。"
23
+ : "Discover the latest insights, tutorials, and updates from the NextDevTpl team. Learn about the latest product news.";
24
+
25
+ return {
26
+ title,
27
+ description,
28
+ openGraph: {
29
+ title,
30
+ description,
31
+ type: "website",
32
+ url: `${siteConfig.url}/${locale}/blog`,
33
+ siteName: siteConfig.name,
34
+ },
35
+ alternates: {
36
+ canonical: `${siteConfig.url}/${locale}/blog`,
37
+ languages: {
38
+ en: `${siteConfig.url}/en/blog`,
39
+ zh: `${siteConfig.url}/zh/blog`,
40
+ },
41
+ },
42
+ };
43
+ }
44
+
45
+ /**
46
+ * 博客列表页面
47
+ *
48
+ * 显示当前语言的所有博客文章
49
+ */
50
+ export default async function BlogPage({
51
+ params,
52
+ }: {
53
+ params: Promise<{ locale: string }>;
54
+ }) {
55
+ const { locale } = await params;
56
+ const posts = getBlogPosts(locale);
57
+
58
+ // 按日期排序(最新的在前)
59
+ const sortedPosts = posts.sort((a, b) => {
60
+ const dateA = new Date(a.date);
61
+ const dateB = new Date(b.date);
62
+ return dateB.getTime() - dateA.getTime();
63
+ });
64
+
65
+ return (
66
+ <div className="container mx-auto max-w-5xl py-20">
67
+ {/* Breadcrumb JSON-LD */}
68
+ <BreadcrumbJsonLd
69
+ items={[
70
+ { name: "Home", url: `/${locale}` },
71
+ {
72
+ name: locale === "zh" ? "博客" : "Blog",
73
+ url: `/${locale}/blog`,
74
+ },
75
+ ]}
76
+ />
77
+
78
+ {/* Header */}
79
+ <div className="mb-16 text-center">
80
+ <h1 className="text-4xl font-bold tracking-tight md:text-5xl">
81
+ {locale === "zh" ? "博客文章" : "Blog Posts"}
82
+ </h1>
83
+ <p className="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground">
84
+ {locale === "zh"
85
+ ? "发现 NextDevTpl 团队的最新见解、教程和更新。了解产品最新动态。"
86
+ : "Discover the latest insights, tutorials, and updates from the NextDevTpl team. Learn about the latest product news."}
87
+ </p>
88
+ </div>
89
+
90
+ {/* Posts List */}
91
+ {sortedPosts.length > 0 ? (
92
+ <div className="space-y-12">
93
+ {sortedPosts.map((post, index) => {
94
+ // 从路径中提取 slug
95
+ const pathParts = post.info.path.split("/");
96
+ const fileName = pathParts[pathParts.length - 1] ?? "";
97
+ const slug = fileName.replace(/\.mdx$/, "");
98
+
99
+ return (
100
+ <div key={post.info.path}>
101
+ <BlogPostCard
102
+ slug={slug}
103
+ title={post.title}
104
+ description={post.description}
105
+ date={
106
+ typeof post.date === "string"
107
+ ? post.date
108
+ : (post.date.toISOString().split("T")[0] ?? "")
109
+ }
110
+ author={post.author}
111
+ tags={post.tags}
112
+ />
113
+ {index < sortedPosts.length - 1 && (
114
+ <Separator className="mt-12" />
115
+ )}
116
+ </div>
117
+ );
118
+ })}
119
+ </div>
120
+ ) : (
121
+ <div className="text-center text-muted-foreground">
122
+ {locale === "zh" ? "暂无博客文章" : "No blog posts yet"}
123
+ </div>
124
+ )}
125
+ </div>
126
+ );
127
+ }
@@ -0,0 +1,36 @@
1
+ import { PlanBadge, type PlanType } from "@/features/subscription";
2
+
3
+ const plans: PlanType[] = ["free", "starter", "pro", "ultra"];
4
+ const sizes: Array<"xs" | "sm" | "md" | "lg"> = ["xs", "sm", "md", "lg"];
5
+
6
+ export default function PlanBadgesDemoPage() {
7
+ return (
8
+ <section className="container py-16">
9
+ <div className="mx-auto flex max-w-4xl flex-col gap-10">
10
+ <header className="space-y-3">
11
+ <h1 className="text-3xl font-semibold tracking-tight">
12
+ Plan Badge Demo
13
+ </h1>
14
+ <p className="text-muted-foreground">
15
+ Preview the subscription badge styles across plans and sizes.
16
+ </p>
17
+ </header>
18
+
19
+ <div className="space-y-8">
20
+ {sizes.map((size) => (
21
+ <div key={size} className="space-y-3 rounded-xl border bg-card p-6">
22
+ <div className="text-sm font-medium uppercase tracking-wide text-muted-foreground">
23
+ Size: {size.toUpperCase()}
24
+ </div>
25
+ <div className="flex flex-wrap items-center gap-4">
26
+ {plans.map((plan) => (
27
+ <PlanBadge key={`${plan}-${size}`} plan={plan} size={size} />
28
+ ))}
29
+ </div>
30
+ </div>
31
+ ))}
32
+ </div>
33
+ </div>
34
+ </section>
35
+ );
36
+ }
@@ -0,0 +1,15 @@
1
+ import { Footer, Header } from "@/features/marketing";
2
+
3
+ export default function MarketingLayout({
4
+ children,
5
+ }: {
6
+ children: React.ReactNode;
7
+ }) {
8
+ return (
9
+ <div className="flex min-h-screen flex-col">
10
+ <Header />
11
+ <main className="flex-1">{children}</main>
12
+ <Footer />
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,130 @@
1
+ import type { Metadata } from "next";
2
+ import { notFound } from "next/navigation";
3
+
4
+ import { Link } from "@/i18n/routing";
5
+ import { getAllLegalSlugs, getLegalDoc } from "@/lib/source";
6
+
7
+ /**
8
+ * 生成静态参数
9
+ * 用于预渲染所有法律文档页面
10
+ */
11
+ export function generateStaticParams() {
12
+ return getAllLegalSlugs();
13
+ }
14
+
15
+ /**
16
+ * 生成页面元数据
17
+ */
18
+ export async function generateMetadata({
19
+ params,
20
+ }: {
21
+ params: Promise<{ locale: string; slug: string }>;
22
+ }): Promise<Metadata> {
23
+ const { locale, slug } = await params;
24
+ const doc = getLegalDoc(slug, locale);
25
+
26
+ if (!doc) {
27
+ return {
28
+ title: "Document Not Found",
29
+ };
30
+ }
31
+
32
+ return {
33
+ title: doc.title,
34
+ description: doc.description,
35
+ };
36
+ }
37
+
38
+ /**
39
+ * 法律文档详情页面
40
+ *
41
+ * 路由: /legal/[slug]
42
+ * 支持: /legal/terms, /legal/privacy, /legal/cookie-policy
43
+ */
44
+ export default async function LegalPage({
45
+ params,
46
+ }: {
47
+ params: Promise<{ locale: string; slug: string }>;
48
+ }) {
49
+ const { locale, slug } = await params;
50
+ const doc = getLegalDoc(slug, locale);
51
+
52
+ // 文档不存在时返回 404
53
+ if (!doc) {
54
+ notFound();
55
+ }
56
+
57
+ // 获取 MDX 内容组件
58
+ const MDXContent = doc.body;
59
+
60
+ // 格式化日期
61
+ const formattedDate =
62
+ typeof doc.date === "string"
63
+ ? doc.date
64
+ : doc.date.toLocaleDateString(locale === "zh" ? "zh-CN" : "en-US", {
65
+ year: "numeric",
66
+ month: "long",
67
+ day: "numeric",
68
+ });
69
+
70
+ return (
71
+ <article className="container mx-auto max-w-3xl py-12">
72
+ {/* 返回链接 */}
73
+ <Link
74
+ href="/"
75
+ className="mb-8 inline-flex items-center text-sm text-muted-foreground hover:text-foreground"
76
+ >
77
+ ← {locale === "zh" ? "返回首页" : "Back to Home"}
78
+ </Link>
79
+
80
+ {/* 文档头部 */}
81
+ <header className="mb-8 border-b pb-8">
82
+ {/* 标题 */}
83
+ <h1 className="mb-4 text-3xl font-bold tracking-tight md:text-4xl">
84
+ {doc.title}
85
+ </h1>
86
+
87
+ {/* 最后更新日期 */}
88
+ <p className="text-sm text-muted-foreground">
89
+ {locale === "zh" ? "最后更新:" : "Last Updated: "}
90
+ <time dateTime={formattedDate}>{formattedDate}</time>
91
+ </p>
92
+ </header>
93
+
94
+ {/* 文档内容 - 使用 Tailwind Typography 样式 */}
95
+ <div className="prose prose-neutral dark:prose-invert max-w-none prose-headings:font-semibold prose-h2:text-xl prose-h2:mt-8 prose-h3:text-lg prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-table:text-sm prose-th:bg-muted prose-th:p-2 prose-td:p-2 prose-td:border">
96
+ <MDXContent />
97
+ </div>
98
+
99
+ {/* 底部导航 */}
100
+ <footer className="mt-12 border-t pt-8">
101
+ <div className="flex flex-wrap gap-4 text-sm">
102
+ <span className="text-muted-foreground">
103
+ {locale === "zh" ? "其他法律文档:" : "Other Legal Documents:"}
104
+ </span>
105
+ {slug !== "terms" && (
106
+ <Link href="/legal/terms" className="text-primary hover:underline">
107
+ {locale === "zh" ? "服务条款" : "Terms of Service"}
108
+ </Link>
109
+ )}
110
+ {slug !== "privacy" && (
111
+ <Link
112
+ href="/legal/privacy"
113
+ className="text-primary hover:underline"
114
+ >
115
+ {locale === "zh" ? "隐私政策" : "Privacy Policy"}
116
+ </Link>
117
+ )}
118
+ {slug !== "cookie-policy" && (
119
+ <Link
120
+ href="/legal/cookie-policy"
121
+ className="text-primary hover:underline"
122
+ >
123
+ {locale === "zh" ? "Cookie 政策" : "Cookie Policy"}
124
+ </Link>
125
+ )}
126
+ </div>
127
+ </footer>
128
+ </article>
129
+ );
130
+ }
@@ -0,0 +1,94 @@
1
+ import type { Metadata } from "next";
2
+ import { SiteJsonLd, SoftwareAppJsonLd } from "@/components/seo/json-ld";
3
+ import { siteConfig } from "@/config";
4
+ import {
5
+ CTASection,
6
+ FAQSection,
7
+ FeatureGrid,
8
+ HeroSection,
9
+ HowItWorks,
10
+ PricingSection,
11
+ Testimonials,
12
+ UseCasesSection,
13
+ } from "@/features/marketing";
14
+
15
+ /**
16
+ * 生成首页 Metadata
17
+ */
18
+ export async function generateMetadata({
19
+ params,
20
+ }: {
21
+ params: Promise<{ locale: string }>;
22
+ }): Promise<Metadata> {
23
+ const { locale } = await params;
24
+ const isZh = locale === "zh";
25
+
26
+ const title = isZh
27
+ ? "NextDevTpl - 生产就绪的 Next.js SaaS 模板"
28
+ : "NextDevTpl - Production-ready Next.js SaaS Template";
29
+
30
+ const description = isZh
31
+ ? "生产就绪的 Next.js SaaS 模板,内置认证、支付、积分系统、后台任务、国际化与管理后台。克隆、定制、即可上线。"
32
+ : "Production-ready Next.js SaaS template with authentication, payments, a credits system, background jobs, i18n, and an admin panel. Clone, customize, and ship.";
33
+
34
+ return {
35
+ title,
36
+ description,
37
+ keywords: [
38
+ "Next.js SaaS template",
39
+ "SaaS boilerplate",
40
+ "Next.js starter kit",
41
+ "Better Auth",
42
+ "Drizzle ORM",
43
+ "TypeScript SaaS",
44
+ ...(isZh ? ["Next.js SaaS 模板", "SaaS 脚手架", "SaaS 样板"] : []),
45
+ ],
46
+ openGraph: {
47
+ title,
48
+ description,
49
+ type: "website",
50
+ url: `${siteConfig.url}/${locale}`,
51
+ siteName: siteConfig.name,
52
+ images: [
53
+ {
54
+ url: `${siteConfig.url}${siteConfig.ogImage}`,
55
+ width: 1200,
56
+ height: 630,
57
+ alt: siteConfig.name,
58
+ },
59
+ ],
60
+ },
61
+ twitter: {
62
+ card: "summary_large_image",
63
+ title,
64
+ description,
65
+ images: [`${siteConfig.url}${siteConfig.ogImage}`],
66
+ },
67
+ };
68
+ }
69
+
70
+ export default async function HomePage({
71
+ params,
72
+ }: {
73
+ params: Promise<{ locale: string }>;
74
+ }) {
75
+ const { locale } = await params;
76
+
77
+ return (
78
+ <>
79
+ {/* JSON-LD Structured Data */}
80
+ <SiteJsonLd locale={locale as "en" | "zh"} />
81
+ <SoftwareAppJsonLd locale={locale as "en" | "zh"} />
82
+
83
+ {/* Page Sections */}
84
+ <HeroSection />
85
+ <FeatureGrid />
86
+ <HowItWorks />
87
+ <UseCasesSection />
88
+ <Testimonials />
89
+ <PricingSection />
90
+ <FAQSection />
91
+ <CTASection />
92
+ </>
93
+ );
94
+ }