next-ts-cli 1.0.3

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +194 -0
  3. package/dist/index.js +94 -0
  4. package/package.json +97 -0
  5. package/template/base/.cusror/mpc.json +8 -0
  6. package/template/base/.husky/commit-msg +1 -0
  7. package/template/base/.husky/pre-commit +1 -0
  8. package/template/base/LICENSE +21 -0
  9. package/template/base/README.md +0 -0
  10. package/template/base/app/apple-icon.png +0 -0
  11. package/template/base/app/favicon.ico +0 -0
  12. package/template/base/app/globals.css +9 -0
  13. package/template/base/app/layout.tsx +61 -0
  14. package/template/base/app/loading.tsx +7 -0
  15. package/template/base/app/manifest.ts +33 -0
  16. package/template/base/app/opengraph-image.png +0 -0
  17. package/template/base/app/page.tsx +7 -0
  18. package/template/base/app/robots.ts +28 -0
  19. package/template/base/app/sitemap.ts +17 -0
  20. package/template/base/app/twitter-image.png +0 -0
  21. package/template/base/biome.jsonc +272 -0
  22. package/template/base/commitlint.config.ts +25 -0
  23. package/template/base/hooks/use-hydration.tsx +16 -0
  24. package/template/base/jest.config.js +18 -0
  25. package/template/base/jest.setup.js +2 -0
  26. package/template/base/lib/fonts.ts +14 -0
  27. package/template/base/lib/indexing.ts +14 -0
  28. package/template/base/lib/microdata.ts +51 -0
  29. package/template/base/lib/utils.ts +6 -0
  30. package/template/base/next.config.ts +7 -0
  31. package/template/base/package-lock.json +9296 -0
  32. package/template/base/package.json +59 -0
  33. package/template/base/postcss.config.js +5 -0
  34. package/template/base/providers/MicrodataScript.tsx +18 -0
  35. package/template/base/public/.gitkeep +3 -0
  36. package/template/base/test/index.test.tsx +9 -0
  37. package/template/base/tsconfig.json +38 -0
  38. package/template/extras/better-auth/api/auth/[...all]/route.ts +7 -0
  39. package/template/extras/better-auth/base-auth.ts +12 -0
  40. package/template/extras/better-auth/with-drizzle-auth.ts +31 -0
  41. package/template/extras/clerk/layout.tsx +89 -0
  42. package/template/extras/clerk/proxy.ts +21 -0
  43. package/template/extras/docker/.dockerignore +60 -0
  44. package/template/extras/docker/Dockerfile +51 -0
  45. package/template/extras/docker/docker-compose.prod.yml +34 -0
  46. package/template/extras/drizzle/db/index.ts +9 -0
  47. package/template/extras/drizzle/db/schema.ts +7 -0
  48. package/template/extras/drizzle/drizzle.config.ts +15 -0
  49. package/template/extras/neon/index.ts +10 -0
  50. package/template/extras/shadcnui/components.json +21 -0
  51. package/template/extras/shadcnui/globals.css +71 -0
  52. package/template/extras/stripe/checkout_session/route.ts +60 -0
  53. package/template/extras/stripe/stripe.ts +17 -0
  54. package/template/extras/stripe/webhook/stripe/route.ts +89 -0
  55. package/template/extras/supabase/client.ts +8 -0
  56. package/template/extras/supabase/getAuth.ts +50 -0
  57. package/template/extras/supabase/proxy.ts +70 -0
  58. package/template/extras/supabase/server.ts +34 -0
  59. package/template/extras/supabase/storage.ts +90 -0
  60. package/template/extras/vercel-ai/route.ts +12 -0
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "next-ts",
3
+ "version": "1.0.0",
4
+ "scripts": {
5
+ "dev": "next dev",
6
+ "build": "next build",
7
+ "start": "next start",
8
+ "lint": "biome check",
9
+ "format": "biome format --write",
10
+ "test": "jest",
11
+ "test:watch": "jest --watch",
12
+ "test:ci": "jest --ci --passWithNoTests",
13
+ "docker:build": "docker compose -f docker-compose.prod.yml build",
14
+ "docker:up": "docker compose -f docker-compose.prod.yml up -d",
15
+ "docker:down": "docker compose -f docker-compose.prod.yml down",
16
+ "docker:logs": "docker compose -f docker-compose.prod.yml logs -f",
17
+ "docker:exec": "docker compose -f docker-compose.prod.yml exec app sh",
18
+ "docker:exec:bash": "docker compose -f docker-compose.prod.yml exec app bash",
19
+ "docker:exec:sh": "docker compose -f docker-compose.prod.yml exec app sh",
20
+ "docker:exec:npm": "docker compose -f docker-compose.prod.yml exec app npm"
21
+ },
22
+ "lint-staged": {
23
+ "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
24
+ "biome check --write --no-errors-on-unmatched"
25
+ ]
26
+ },
27
+ "dependencies": {
28
+ "@next/third-parties": "^16.1.1",
29
+ "clsx": "^2.1.1",
30
+ "framer-motion": "^12.23.6",
31
+ "husky": "^9.1.7",
32
+ "lint-staged": "^16.2.7",
33
+ "next": "^16.1.1",
34
+ "react": "^19.2.3",
35
+ "react-dom": "^19.2.3",
36
+ "schema-dts": "^1.1.5",
37
+ "sharp": "^0.34.5",
38
+ "tailwind-merge": "^3.4.0"
39
+ },
40
+ "devDependencies": {
41
+ "@biomejs/biome": "2.3.10",
42
+ "@commitlint/cli": "^20.2.0",
43
+ "@commitlint/config-conventional": "^20.2.0",
44
+ "@tailwindcss/postcss": "^4.1.11",
45
+ "@testing-library/jest-dom": "^6.9.1",
46
+ "@testing-library/react": "^16.3.0",
47
+ "@testing-library/user-event": "^14.6.1",
48
+ "@types/jest": "^30.0.0",
49
+ "@types/node": "^25.0.3",
50
+ "@types/react": "^19.2.7",
51
+ "@types/react-dom": "^19.2.3",
52
+ "autoprefixer": "^10.4.21",
53
+ "jest": "^30.2.0",
54
+ "jest-environment-jsdom": "^30.2.0",
55
+ "postcss": "^8.5.6",
56
+ "tailwindcss": "^4.1.11",
57
+ "typescript": "^5.9.3"
58
+ }
59
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
@@ -0,0 +1,18 @@
1
+ import Script from "next/script";
2
+ import type { WithContext } from "schema-dts";
3
+ import type { SchemaType, SchemaTypeMap } from "@/lib/microdata";
4
+
5
+ interface MicrodataScriptProps {
6
+ id: string;
7
+ microdata: WithContext<SchemaTypeMap[SchemaType]>;
8
+ }
9
+
10
+ export function MicrodataScript({ id, microdata }: MicrodataScriptProps) {
11
+ return (
12
+ <Script
13
+ id={id}
14
+ type="application/ld+json"
15
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(microdata) }}
16
+ />
17
+ );
18
+ }
@@ -0,0 +1,3 @@
1
+ # This file keeps the public directory in git
2
+ # Add your static assets here (images, fonts, etc.)
3
+
@@ -0,0 +1,9 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import Home from "@/app/page";
3
+
4
+ describe("Home Page", () => {
5
+ it("renders home page", () => {
6
+ render(<Home />);
7
+ expect(screen.getByText("next-ts")).toBeDefined();
8
+ });
9
+ });
@@ -0,0 +1,38 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["dom", "dom.iterable", "esnext"],
4
+ "allowJs": true,
5
+ "skipLibCheck": true,
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "esModuleInterop": true,
9
+ "module": "esnext",
10
+ "moduleResolution": "bundler",
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "jsx": "react-jsx",
14
+ "incremental": true,
15
+ "plugins": [
16
+ {
17
+ "name": "next"
18
+ }
19
+ ],
20
+ "paths": {
21
+ "@/*": ["./*"],
22
+ "@/components/*": ["./components/*"],
23
+ "@/hooks/*": ["./hooks/*"],
24
+ "@/lib/*": ["./lib/*"],
25
+ "@/public/*": ["./public/*"],
26
+ "@/providers/*": ["./providers/*"]
27
+ },
28
+ "target": "ES2017"
29
+ },
30
+ "include": [
31
+ "next-env.d.ts",
32
+ "**/*.ts",
33
+ "**/*.tsx",
34
+ ".next/types/**/*.ts",
35
+ ".next/dev/types/**/*.ts"
36
+ ],
37
+ "exclude": ["node_modules"]
38
+ }
@@ -0,0 +1,7 @@
1
+ import { toNodeHandler } from "better-auth/node"
2
+ import { auth } from "@/lib/auth"
3
+
4
+ // Disallow body parsing, we will parse it manually
5
+ export const config = { api: { bodyParser: false } }
6
+
7
+ export default toNodeHandler(auth.handler)
@@ -0,0 +1,12 @@
1
+ import { betterAuth } from "better-auth";
2
+ import { nextCookies } from "better-auth/next-js";
3
+
4
+ export const auth = betterAuth({
5
+ emailAndPassword: {
6
+ enabled: true,
7
+ },
8
+ // Make sure nextCookies() is the last plugin in the array
9
+ plugins: [nextCookies()],
10
+ });
11
+
12
+ export type Session = typeof auth.$Infer.Session;
@@ -0,0 +1,31 @@
1
+ import "dotenv/config";
2
+ import { betterAuth } from "better-auth";
3
+ import { drizzleAdapter } from "better-auth/adapters/drizzle";
4
+ import { nextCookies } from "better-auth/next-js";
5
+
6
+ /* Here imports have errors because they are related to the final directory structure */
7
+ import { db } from "@/lib/db";
8
+
9
+ if (!process.env.BETTER_AUTH_GITHUB_CLIENT_ID || !process.env.BETTER_AUTH_GITHUB_CLIENT_SECRET) {
10
+ throw new Error("BETTER_AUTH_GITHUB_CLIENT_ID and BETTER_AUTH_GITHUB_CLIENT_SECRET must be set");
11
+ }
12
+
13
+ export const auth = betterAuth({
14
+ database: drizzleAdapter(db, {
15
+ provider: "pg",
16
+ }),
17
+ emailAndPassword: {
18
+ enabled: true,
19
+ },
20
+ socialProviders: {
21
+ github: {
22
+ clientId: process.env.BETTER_AUTH_GITHUB_CLIENT_ID,
23
+ clientSecret: process.env.BETTER_AUTH_GITHUB_CLIENT_SECRET,
24
+ redirectURI: "http://localhost:3000/api/auth/callback/github",
25
+ },
26
+ },
27
+ // Make sure nextCookies() is the last plugin in the array
28
+ plugins: [nextCookies()],
29
+ });
30
+
31
+ export type Session = typeof auth.$Infer.Session;
@@ -0,0 +1,89 @@
1
+ import type { Metadata } from "next";
2
+ import { GoogleAnalytics } from "@next/third-parties/google";
3
+ import { barrio, dmSans } from "@/lib/fonts";
4
+ import { allowIndexing } from "@/lib/indexing";
5
+ import { microdata } from "@/lib/microdata";
6
+ import { cn } from "@/lib/utils";
7
+ import { MicrodataScript } from "@/providers/MicrodataScript";
8
+ import {
9
+ ClerkProvider,
10
+ SignInButton,
11
+ SignUpButton,
12
+ SignedIn,
13
+ SignedOut,
14
+ UserButton,
15
+ } from '@clerk/nextjs'
16
+ import "./globals.css";
17
+
18
+ const homePageMicrodata = microdata("WebSite", {
19
+ name: "next-ts",
20
+ url: process.env.NEXT_PUBLIC_BASE_URL,
21
+ description:
22
+ "next-ts is a Production-Ready and Scalable Next.js Template Starter. Stop wasting time setting up your _next_ big project, with next-ts it's all ready to go!",
23
+ author: "next-ts",
24
+ publisher: "next-ts",
25
+ inLanguage: "en_US",
26
+ isAccessibleForFree: true,
27
+ image: `${process.env.NEXT_PUBLIC_BASE_URL}/public/myimage.png`,
28
+ mainEntityOfPage: {
29
+ "@type": "WebSite",
30
+ "@id": process.env.NEXT_PUBLIC_BASE_URL,
31
+ },
32
+ });
33
+
34
+ export const metaData: Metadata = {
35
+ metadataBase: new URL(process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"),
36
+ title: {
37
+ default: "next-ts",
38
+ template: "next-ts - %s",
39
+ },
40
+ description:
41
+ "next-ts is a Production-Ready and Scalable Next.js Template Starter. Stop wasting time setting up your _next_ big project, with next-ts it's all ready to go!",
42
+ keywords: ["next-ts"],
43
+ authors: [{ name: "next-ts" }],
44
+ creator: "next-ts",
45
+ publisher: "next-ts",
46
+ formatDetection: {
47
+ email: false,
48
+ address: false,
49
+ telephone: false,
50
+ },
51
+ alternates: {
52
+ canonical: process.env.NEXT_PUBLIC_BASE_URL,
53
+ },
54
+ ...allowIndexing(),
55
+ };
56
+
57
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
58
+ return (
59
+ <ClerkProvider>
60
+ <html lang="en">
61
+ <body className={cn("bg-background", barrio.variable, dmSans.variable)}>
62
+ <header className="flex fixed top-0 left-0 right-0 justify-between items-center py-8 max-w-4xl mx-auto gap-4 h-16">
63
+ <div className="flex items-center gap-4">
64
+ My SaaS App
65
+ </div>
66
+ <div className="flex items-center gap-4">
67
+ <SignedOut>
68
+ <SignInButton />
69
+ <SignUpButton>
70
+ <button className="bg-[#6c47ff] text-white rounded-full font-medium text-sm px-4 py-2 cursor-pointer">
71
+ Sign Up
72
+ </button>
73
+ </SignUpButton>
74
+ </SignedOut>
75
+ <SignedIn>
76
+ <UserButton />
77
+ </SignedIn>
78
+ </div>
79
+ </header>
80
+ {children}
81
+ <MicrodataScript id="home-microdata" microdata={homePageMicrodata} />
82
+ {process.env.GOOGLE_ANALYTICS_TAG && (
83
+ <GoogleAnalytics gaId={process.env.GOOGLE_ANALYTICS_TAG} />
84
+ )}
85
+ </body>
86
+ </html>
87
+ </ClerkProvider>
88
+ );
89
+ }
@@ -0,0 +1,21 @@
1
+ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
2
+
3
+
4
+ const protectedRoutes = createRouteMatcher([
5
+ // Add your protected routes here, example: /dashboard
6
+ ])
7
+
8
+ export default clerkMiddleware(async (auth,req) =>{
9
+ if(protectedRoutes(req)){
10
+ await auth.protect()
11
+ }
12
+ })
13
+
14
+ export const config = {
15
+ matcher: [
16
+ // Skip Next.js internals and all static files, unless found in search params
17
+ '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
18
+ // Always run for API routes
19
+ '/(api|trpc)(.*)',
20
+ ],
21
+ }
@@ -0,0 +1,60 @@
1
+ # ============================================
2
+ # Docker Ignore File
3
+ # ============================================
4
+ # Excludes files from the Docker build context
5
+ # for faster builds and smaller images
6
+ # ============================================
7
+
8
+ # Dependencies
9
+ node_modules
10
+ .pnp
11
+ .pnp.js
12
+
13
+ # Build outputs
14
+ .next
15
+ out
16
+ build
17
+ dist
18
+
19
+ # Testing
20
+ coverage
21
+ .nyc_output
22
+
23
+ # Debug logs
24
+ npm-debug.log*
25
+ yarn-debug.log*
26
+ yarn-error.log*
27
+ .pnpm-debug.log*
28
+
29
+ # Environment files (add to context only what you need)
30
+ .env
31
+ .env.*
32
+ !.env.example
33
+
34
+ # IDE & Editor
35
+ .idea
36
+ .vscode
37
+ *.swp
38
+ *.swo
39
+ .DS_Store
40
+
41
+ # Git
42
+ .git
43
+ .gitignore
44
+
45
+ # Docker
46
+ Dockerfile*
47
+ docker-compose*
48
+ .dockerignore
49
+
50
+ # Documentation
51
+ README.md
52
+ LICENSE
53
+ CHANGELOG.md
54
+ docs/
55
+
56
+ # Misc
57
+ *.md
58
+ .husky
59
+ .github
60
+
@@ -0,0 +1,51 @@
1
+ # 1. Base Image
2
+ FROM node:22-alpine AS base
3
+
4
+ # 2. Dependencies Stage
5
+ FROM base AS deps
6
+ RUN apk add --no-cache libc6-compat
7
+ WORKDIR /app
8
+
9
+ COPY package.json package-lock.json* ./
10
+ RUN npm ci
11
+
12
+ # 3. Builder Stage
13
+ FROM base AS builder
14
+ WORKDIR /app
15
+ COPY --from=deps /app/node_modules ./node_modules
16
+ COPY . .
17
+
18
+ ARG NEXT_PUBLIC_BASE_URL
19
+ ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
20
+
21
+ # Disabilita la telemetria durante la build
22
+ ENV NEXT_TELEMETRY_DISABLED 1
23
+
24
+ RUN npm run build
25
+
26
+ # 4. Runner Stage (Production)
27
+ FROM base AS runner
28
+ WORKDIR /app
29
+
30
+ ENV NODE_ENV production
31
+ ENV NEXT_TELEMETRY_DISABLED 1
32
+
33
+ RUN addgroup --system --gid 1001 nodejs
34
+ RUN adduser --system --uid 1001 nextjs
35
+
36
+ COPY --from=builder /app/public ./public
37
+
38
+ RUN mkdir .next
39
+ RUN chown nextjs:nodejs .next
40
+
41
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
42
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
43
+
44
+ USER nextjs
45
+
46
+ EXPOSE 3000
47
+
48
+ ENV PORT 3000
49
+ ENV HOSTNAME "0.0.0.0"
50
+
51
+ CMD ["node", "server.js"]
@@ -0,0 +1,34 @@
1
+ # ============================================
2
+ # Next.js SEO Starter - Docker Compose (Production)
3
+ # ============================================
4
+ #
5
+ # Usage:
6
+ # Build & Start: docker compose -f docker-compose.prod.yml up --build -d
7
+ # Stop: docker compose -f docker-compose.prod.yml down
8
+ #
9
+ # ============================================
10
+
11
+ services:
12
+ app:
13
+ build:
14
+ context: .
15
+ target: runner
16
+ args:
17
+ NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL}
18
+ container_name: nextjs-seo-web
19
+ volumes: [] # No volume mounts in production
20
+ environment:
21
+ - NODE_ENV=production
22
+ - NEXT_TELEMETRY_DISABLED=1
23
+ env_file:
24
+ - path: .env
25
+ restart: always
26
+ # Resource limits (adjust based on your needs)
27
+ deploy:
28
+ resources:
29
+ limits:
30
+ cpus: "1"
31
+ memory: 512M
32
+ reservations:
33
+ cpus: "0.25"
34
+ memory: 256M
@@ -0,0 +1,9 @@
1
+ import { drizzle } from 'drizzle-orm/postgres-js'
2
+
3
+ if (!process.env.DATABASE_URL) {
4
+ throw new Error("DATABASE_URL is not set");
5
+ }
6
+
7
+ const db = drizzle(process.env.DATABASE_URL!);
8
+
9
+ export { db };
@@ -0,0 +1,7 @@
1
+ import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
2
+ export const usersTable = pgTable("users", {
3
+ id: integer().primaryKey().generatedAlwaysAsIdentity(),
4
+ name: varchar({ length: 255 }).notNull(),
5
+ age: integer().notNull(),
6
+ email: varchar({ length: 255 }).notNull().unique(),
7
+ });
@@ -0,0 +1,15 @@
1
+ import "dotenv/config";
2
+ import { defineConfig } from "drizzle-kit";
3
+
4
+ if (!process.env.DATABASE_URL) {
5
+ throw new Error("DATABASE_URL is not set");
6
+ }
7
+
8
+ export default defineConfig({
9
+ out: "./drizzle",
10
+ schema: "./lib/db/schema.ts",
11
+ dialect: "postgresql",
12
+ dbCredentials: {
13
+ url: process.env.DATABASE_URL,
14
+ },
15
+ });
@@ -0,0 +1,10 @@
1
+ /* Drizzle configuration for Neon */
2
+ import { drizzle } from "drizzle-orm/neon-http";
3
+ import { neon } from "@neondatabase/serverless";
4
+
5
+ if (!process.env.DATABASE_URL) {
6
+ throw new Error("DATABASE_URL is not set");
7
+ }
8
+
9
+ const sql = neon(process.env.DATABASE_URL);
10
+ export const db = drizzle({ client: sql });
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
@@ -0,0 +1,71 @@
1
+ @import "tailwindcss";
2
+ @custom-variant dark (&:is(.dark *));
3
+
4
+ :root {
5
+ --radius: 0.625rem;
6
+ --background: oklch(1 0 0);
7
+ --foreground: oklch(0.145 0 0);
8
+ --card: oklch(1 0 0);
9
+ --card-foreground: oklch(0.145 0 0);
10
+ --popover: oklch(1 0 0);
11
+ --popover-foreground: oklch(0.145 0 0);
12
+ --primary: oklch(0.205 0 0);
13
+ --primary-foreground: oklch(0.985 0 0);
14
+ --secondary: oklch(0.97 0 0);
15
+ --secondary-foreground: oklch(0.205 0 0);
16
+ --muted: oklch(0.97 0 0);
17
+ --muted-foreground: oklch(0.556 0 0);
18
+ --accent: oklch(0.97 0 0);
19
+ --accent-foreground: oklch(0.205 0 0);
20
+ --destructive: oklch(0.577 0.245 27.325);
21
+ --border: oklch(0.922 0 0);
22
+ --input: oklch(0.922 0 0);
23
+ --ring: oklch(0.708 0 0);
24
+ --chart-1: oklch(0.646 0.222 41.116);
25
+ --chart-2: oklch(0.6 0.118 184.704);
26
+ --chart-3: oklch(0.398 0.07 227.392);
27
+ --chart-4: oklch(0.828 0.189 84.429);
28
+ --chart-5: oklch(0.769 0.188 70.08);
29
+ --sidebar: oklch(0.985 0 0);
30
+ --sidebar-foreground: oklch(0.145 0 0);
31
+ --sidebar-primary: oklch(0.205 0 0);
32
+ --sidebar-primary-foreground: oklch(0.985 0 0);
33
+ --sidebar-accent: oklch(0.97 0 0);
34
+ --sidebar-accent-foreground: oklch(0.205 0 0);
35
+ --sidebar-border: oklch(0.922 0 0);
36
+ --sidebar-ring: oklch(0.708 0 0);
37
+ }
38
+
39
+ .dark {
40
+ --background: oklch(0.145 0 0);
41
+ --foreground: oklch(0.985 0 0);
42
+ --card: oklch(0.205 0 0);
43
+ --card-foreground: oklch(0.985 0 0);
44
+ --popover: oklch(0.205 0 0);
45
+ --popover-foreground: oklch(0.985 0 0);
46
+ --primary: oklch(0.922 0 0);
47
+ --primary-foreground: oklch(0.205 0 0);
48
+ --secondary: oklch(0.269 0 0);
49
+ --secondary-foreground: oklch(0.985 0 0);
50
+ --muted: oklch(0.269 0 0);
51
+ --muted-foreground: oklch(0.708 0 0);
52
+ --accent: oklch(0.269 0 0);
53
+ --accent-foreground: oklch(0.985 0 0);
54
+ --destructive: oklch(0.704 0.191 22.216);
55
+ --border: oklch(1 0 0 / 10%);
56
+ --input: oklch(1 0 0 / 15%);
57
+ --ring: oklch(0.556 0 0);
58
+ --chart-1: oklch(0.488 0.243 264.376);
59
+ --chart-2: oklch(0.696 0.17 162.48);
60
+ --chart-3: oklch(0.769 0.188 70.08);
61
+ --chart-4: oklch(0.627 0.265 303.9);
62
+ --chart-5: oklch(0.645 0.246 16.439);
63
+ --sidebar: oklch(0.205 0 0);
64
+ --sidebar-foreground: oklch(0.985 0 0);
65
+ --sidebar-primary: oklch(0.488 0.243 264.376);
66
+ --sidebar-primary-foreground: oklch(0.985 0 0);
67
+ --sidebar-accent: oklch(0.269 0 0);
68
+ --sidebar-accent-foreground: oklch(0.985 0 0);
69
+ --sidebar-border: oklch(1 0 0 / 10%);
70
+ --sidebar-ring: oklch(0.556 0 0);
71
+ }
@@ -0,0 +1,60 @@
1
+ import { NextResponse } from "next/server";
2
+ /* Here imports are set up for the final project structure, so it's fine */
3
+ import { stripe } from "@/lib/stripe";
4
+
5
+
6
+ const TRIAL_PERIOD_DAYS = 3;
7
+
8
+
9
+ export async function GET(req: Request) {
10
+ try {
11
+
12
+ // Usually you would check for authentication first
13
+ const fakeUser = {
14
+ id: "123",
15
+ email: "test@test.com",
16
+ }
17
+
18
+ const url = new URL(req.url);
19
+ const priceId = url.searchParams.get("priceId");
20
+
21
+ if (!priceId) {
22
+ return new NextResponse("Price ID is required", { status: 400 });
23
+ }
24
+
25
+ // Set subscription data with trial period, remove if not wanted
26
+ const subscriptionData: {
27
+ trial_period_days?: number;
28
+ } = {
29
+ trial_period_days: TRIAL_PERIOD_DAYS,
30
+ };
31
+
32
+ // Create Stripe checkout session
33
+ const checkoutSession = await stripe().checkout.sessions.create({
34
+ mode: "subscription",
35
+ payment_method_types: ["card", "paypal"],
36
+ line_items: [
37
+ {
38
+ price: priceId,
39
+ quantity: 1,
40
+ },
41
+ ],
42
+ subscription_data: subscriptionData,
43
+ // Metadata will be used to identify the user in the webhook
44
+ metadata: {
45
+ userId: fakeUser.id,
46
+ },
47
+ customer_email: fakeUser.email,
48
+ success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
49
+ cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/`,
50
+ });
51
+
52
+ if (!checkoutSession.url) {
53
+ return new NextResponse("Checkout session URL is missing", { status: 500 });
54
+ }
55
+
56
+ return NextResponse.json({ url: checkoutSession.url });
57
+ } catch (_) {
58
+ return new NextResponse("Internal Error", { status: 500 });
59
+ }
60
+ }
@@ -0,0 +1,17 @@
1
+ import Stripe from "stripe";
2
+
3
+ let stripeInstance: Stripe | null = null;
4
+
5
+ const getStripe = () => {
6
+ if (!stripeInstance) {
7
+ if (!process.env.STRIPE_SECRET_KEY) {
8
+ throw new Error("STRIPE_SECRET_KEY is not set");
9
+ }
10
+ stripeInstance = new Stripe(process.env.STRIPE_SECRET_KEY, {
11
+ typescript: true,
12
+ });
13
+ }
14
+ return stripeInstance;
15
+ };
16
+
17
+ export const stripe = getStripe;