shipd 0.1.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.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1366 -0
- package/docs-template/README.md +255 -0
- package/docs-template/[slug]/[subslug]/page.tsx +1242 -0
- package/docs-template/[slug]/page.tsx +422 -0
- package/docs-template/api/page.tsx +47 -0
- package/docs-template/components/docs/docs-category-page.tsx +162 -0
- package/docs-template/components/docs/docs-code-card.tsx +135 -0
- package/docs-template/components/docs/docs-header.tsx +69 -0
- package/docs-template/components/docs/docs-nav.ts +95 -0
- package/docs-template/components/docs/docs-sidebar.tsx +112 -0
- package/docs-template/components/docs/docs-toc.tsx +38 -0
- package/docs-template/components/ui/badge.tsx +47 -0
- package/docs-template/components/ui/button.tsx +60 -0
- package/docs-template/components/ui/card.tsx +93 -0
- package/docs-template/components/ui/sheet.tsx +140 -0
- package/docs-template/documentation/page.tsx +80 -0
- package/docs-template/layout.tsx +27 -0
- package/docs-template/lib/utils.ts +7 -0
- package/docs-template/page.tsx +360 -0
- package/package.json +66 -0
- package/template/.env.example +45 -0
- package/template/README.md +239 -0
- package/template/app/api/auth/[...all]/route.ts +4 -0
- package/template/app/api/chat/route.ts +16 -0
- package/template/app/api/subscription/route.ts +25 -0
- package/template/app/api/upload-image/route.ts +64 -0
- package/template/app/blog/[slug]/page.tsx +314 -0
- package/template/app/blog/page.tsx +107 -0
- package/template/app/dashboard/_components/chart-interactive.tsx +289 -0
- package/template/app/dashboard/_components/chatbot.tsx +39 -0
- package/template/app/dashboard/_components/mode-toggle.tsx +46 -0
- package/template/app/dashboard/_components/navbar.tsx +84 -0
- package/template/app/dashboard/_components/section-cards.tsx +102 -0
- package/template/app/dashboard/_components/sidebar.tsx +90 -0
- package/template/app/dashboard/_components/subscribe-button.tsx +49 -0
- package/template/app/dashboard/billing/page.tsx +277 -0
- package/template/app/dashboard/chat/page.tsx +73 -0
- package/template/app/dashboard/cli/page.tsx +260 -0
- package/template/app/dashboard/layout.tsx +24 -0
- package/template/app/dashboard/page.tsx +216 -0
- package/template/app/dashboard/payment/_components/manage-subscription.tsx +22 -0
- package/template/app/dashboard/payment/page.tsx +126 -0
- package/template/app/dashboard/settings/page.tsx +613 -0
- package/template/app/dashboard/upload/page.tsx +324 -0
- package/template/app/error.tsx +78 -0
- package/template/app/favicon.ico +0 -0
- package/template/app/globals.css +126 -0
- package/template/app/layout.tsx +135 -0
- package/template/app/not-found.tsx +45 -0
- package/template/app/page.tsx +28 -0
- package/template/app/pricing/_component/pricing-table.tsx +276 -0
- package/template/app/pricing/page.tsx +23 -0
- package/template/app/privacy-policy/page.tsx +280 -0
- package/template/app/robots.txt +12 -0
- package/template/app/sign-in/page.tsx +228 -0
- package/template/app/sign-up/page.tsx +243 -0
- package/template/app/sitemap.ts +62 -0
- package/template/app/success/page.tsx +123 -0
- package/template/app/terms-of-service/page.tsx +212 -0
- package/template/auth-schema.ts +47 -0
- package/template/components/homepage/cli-workflow-section.tsx +138 -0
- package/template/components/homepage/features-section.tsx +150 -0
- package/template/components/homepage/footer.tsx +53 -0
- package/template/components/homepage/hero-section.tsx +112 -0
- package/template/components/homepage/integrations.tsx +124 -0
- package/template/components/homepage/navigation.tsx +116 -0
- package/template/components/homepage/news-section.tsx +82 -0
- package/template/components/homepage/testimonials-section.tsx +34 -0
- package/template/components/logos/BetterAuth.tsx +21 -0
- package/template/components/logos/NeonPostgres.tsx +41 -0
- package/template/components/logos/Nextjs.tsx +72 -0
- package/template/components/logos/Polar.tsx +7 -0
- package/template/components/logos/TailwindCSS.tsx +27 -0
- package/template/components/logos/index.ts +6 -0
- package/template/components/logos/shadcnui.tsx +8 -0
- package/template/components/provider.tsx +8 -0
- package/template/components/ui/avatar.tsx +53 -0
- package/template/components/ui/badge.tsx +46 -0
- package/template/components/ui/button.tsx +59 -0
- package/template/components/ui/card.tsx +92 -0
- package/template/components/ui/chart.tsx +353 -0
- package/template/components/ui/checkbox.tsx +32 -0
- package/template/components/ui/dialog.tsx +135 -0
- package/template/components/ui/dropdown-menu.tsx +257 -0
- package/template/components/ui/form.tsx +167 -0
- package/template/components/ui/input.tsx +21 -0
- package/template/components/ui/label.tsx +24 -0
- package/template/components/ui/progress.tsx +31 -0
- package/template/components/ui/resizable.tsx +56 -0
- package/template/components/ui/select.tsx +185 -0
- package/template/components/ui/separator.tsx +28 -0
- package/template/components/ui/sheet.tsx +139 -0
- package/template/components/ui/skeleton.tsx +13 -0
- package/template/components/ui/sonner.tsx +25 -0
- package/template/components/ui/switch.tsx +31 -0
- package/template/components/ui/tabs.tsx +66 -0
- package/template/components/ui/textarea.tsx +18 -0
- package/template/components/ui/toggle-group.tsx +73 -0
- package/template/components/ui/toggle.tsx +47 -0
- package/template/components/ui/tooltip.tsx +61 -0
- package/template/components/user-profile.tsx +139 -0
- package/template/components.json +21 -0
- package/template/db/drizzle.ts +14 -0
- package/template/db/migrations/0000_worried_rawhide_kid.sql +77 -0
- package/template/db/migrations/meta/0000_snapshot.json +494 -0
- package/template/db/migrations/meta/_journal.json +13 -0
- package/template/db/schema.ts +85 -0
- package/template/drizzle.config.ts +13 -0
- package/template/emails/components/layout.tsx +181 -0
- package/template/emails/password-reset.tsx +67 -0
- package/template/emails/payment-failed.tsx +167 -0
- package/template/emails/subscription-confirmation.tsx +129 -0
- package/template/emails/welcome.tsx +100 -0
- package/template/eslint.config.mjs +16 -0
- package/template/hooks/use-mobile.ts +21 -0
- package/template/lib/auth-client.ts +8 -0
- package/template/lib/auth.ts +276 -0
- package/template/lib/email.ts +118 -0
- package/template/lib/polar-products.ts +49 -0
- package/template/lib/subscription.ts +148 -0
- package/template/lib/upload-image.ts +28 -0
- package/template/lib/utils.ts +6 -0
- package/template/middleware.ts +30 -0
- package/template/next-env.d.ts +5 -0
- package/template/next.config.ts +27 -0
- package/template/package.json +99 -0
- package/template/postcss.config.mjs +5 -0
- package/template/public/add.png +0 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/file.svg +1 -0
- package/template/public/globe.svg +1 -0
- package/template/public/iphone.png +0 -0
- package/template/public/logo.png +0 -0
- package/template/public/next.svg +1 -0
- package/template/public/polar-sh.svg +1 -0
- package/template/public/shadcn-ui.svg +1 -0
- package/template/public/site.webmanifest +21 -0
- package/template/public/vercel.svg +1 -0
- package/template/public/window.svg +1 -0
- package/template/tailwind.config.ts +89 -0
- package/template/template.config.json +138 -0
- package/template/tsconfig.json +27 -0
|
@@ -0,0 +1,1242 @@
|
|
|
1
|
+
import DocsToc from "@/components/docs/docs-toc";
|
|
2
|
+
import ReactMarkdown from "react-markdown";
|
|
3
|
+
import remarkGfm from "remark-gfm";
|
|
4
|
+
|
|
5
|
+
type Section = { id: string; heading: string; body: string };
|
|
6
|
+
type Page = { title: string; updated: string; toc: { id: string; title: string }[]; sections: Section[] };
|
|
7
|
+
|
|
8
|
+
const subpages: Record<string, Page> = {
|
|
9
|
+
"authentication/better-auth": {
|
|
10
|
+
title: "Better Auth",
|
|
11
|
+
updated: "December 18, 2025",
|
|
12
|
+
toc: [
|
|
13
|
+
{ id: "overview", title: "Overview" },
|
|
14
|
+
{ id: "generate-secret", title: "Generate BETTER_AUTH_SECRET" },
|
|
15
|
+
{ id: "google-oauth", title: "Google OAuth Setup" },
|
|
16
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
17
|
+
{ id: "test", title: "Testing" },
|
|
18
|
+
],
|
|
19
|
+
sections: [
|
|
20
|
+
{
|
|
21
|
+
id: "overview",
|
|
22
|
+
heading: "Overview",
|
|
23
|
+
body: "Better Auth is the default authentication provider in the scaffold. It handles email/password authentication, OAuth providers (Google, GitHub, etc.), session management, and integrates with your database for user storage.",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "generate-secret",
|
|
27
|
+
heading: "Generate BETTER_AUTH_SECRET",
|
|
28
|
+
body: `Generate a secure random secret for Better Auth. This is used to encrypt sessions and tokens.
|
|
29
|
+
|
|
30
|
+
**Option 1: Using OpenSSL (recommended)**
|
|
31
|
+
\`\`\`bash
|
|
32
|
+
openssl rand -base64 32
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
**Option 2: Using Node.js**
|
|
36
|
+
\`\`\`bash
|
|
37
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
Copy the generated secret - you'll add it to your \`.env.local\` file in the next step.`,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "google-oauth",
|
|
44
|
+
heading: "Google OAuth Setup",
|
|
45
|
+
body: `To enable Google sign-in, you need to create OAuth credentials in the Google Cloud Console.
|
|
46
|
+
|
|
47
|
+
**Step 1: Create a Google Cloud Project**
|
|
48
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
49
|
+
2. Click "Select a project" → "New Project"
|
|
50
|
+
3. Enter a project name and click "Create"
|
|
51
|
+
|
|
52
|
+
**Step 2: Enable Google+ API**
|
|
53
|
+
1. Navigate to "APIs & Services" → "Library"
|
|
54
|
+
2. Search for "Google+ API" and click "Enable"
|
|
55
|
+
|
|
56
|
+
**Step 3: Create OAuth Credentials**
|
|
57
|
+
1. Go to "APIs & Services" → "Credentials"
|
|
58
|
+
2. Click "Create Credentials" → "OAuth client ID"
|
|
59
|
+
3. If prompted, configure the OAuth consent screen first:
|
|
60
|
+
- Choose "External" user type
|
|
61
|
+
- Fill in app name, support email, developer contact
|
|
62
|
+
- Add scopes: \`email\`, \`profile\`, \`openid\`
|
|
63
|
+
- Add test users if needed
|
|
64
|
+
- Save and continue
|
|
65
|
+
4. Back in "Create OAuth client ID":
|
|
66
|
+
- Application type: "Web application"
|
|
67
|
+
- Name: Your app name
|
|
68
|
+
- Authorized redirect URIs: \`http://localhost:3000/api/auth/callback/google\` (for local dev)
|
|
69
|
+
- For production, add: \`https://yourdomain.com/api/auth/callback/google\`
|
|
70
|
+
5. Click "Create"
|
|
71
|
+
6. Copy the **Client ID** and **Client Secret**`,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "env-variables",
|
|
75
|
+
heading: "Environment Variables",
|
|
76
|
+
body: `Add these variables to your \`.env.local\` file:
|
|
77
|
+
|
|
78
|
+
\`\`\`env
|
|
79
|
+
# Better Auth
|
|
80
|
+
BETTER_AUTH_SECRET="your-generated-secret-from-step-1"
|
|
81
|
+
|
|
82
|
+
# Google OAuth (optional, but recommended)
|
|
83
|
+
GOOGLE_CLIENT_ID="your-google-client-id.apps.googleusercontent.com"
|
|
84
|
+
GOOGLE_CLIENT_SECRET="your-google-client-secret"
|
|
85
|
+
|
|
86
|
+
# App URL (required for OAuth redirects)
|
|
87
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
**Important:**
|
|
91
|
+
- \`BETTER_AUTH_SECRET\` is required - use the secret you generated in step 1
|
|
92
|
+
- \`GOOGLE_CLIENT_ID\` and \`GOOGLE_CLIENT_SECRET\` are optional but needed for Google sign-in
|
|
93
|
+
- For production, update \`NEXT_PUBLIC_APP_URL\` to your production domain`,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "test",
|
|
97
|
+
heading: "Testing",
|
|
98
|
+
body: `1. Restart your dev server: \`npm run dev\`
|
|
99
|
+
2. Navigate to \`http://localhost:3000/sign-up\`
|
|
100
|
+
3. Try creating an account with email/password
|
|
101
|
+
4. Try signing in with Google (if configured)
|
|
102
|
+
5. Verify you can access protected routes like \`/dashboard\`
|
|
103
|
+
|
|
104
|
+
**Troubleshooting:**
|
|
105
|
+
- If Google sign-in fails, check that redirect URIs match exactly
|
|
106
|
+
- If sessions don't persist, verify \`BETTER_AUTH_SECRET\` is set correctly
|
|
107
|
+
- Check browser console and server logs for detailed error messages`,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
"authentication/supabase-auth": {
|
|
112
|
+
title: "Supabase Auth",
|
|
113
|
+
updated: "December 18, 2025",
|
|
114
|
+
toc: [
|
|
115
|
+
{ id: "overview", title: "Overview" },
|
|
116
|
+
{ id: "create-project", title: "Create Supabase Project" },
|
|
117
|
+
{ id: "get-credentials", title: "Get API Credentials" },
|
|
118
|
+
{ id: "configure-auth", title: "Configure Auth Settings" },
|
|
119
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
120
|
+
{ id: "test", title: "Testing" },
|
|
121
|
+
],
|
|
122
|
+
sections: [
|
|
123
|
+
{
|
|
124
|
+
id: "overview",
|
|
125
|
+
heading: "Overview",
|
|
126
|
+
body: "Supabase Auth provides a complete authentication solution with email/password, magic links, OAuth providers, and user management. It's integrated with Supabase's database and real-time features.",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "create-project",
|
|
130
|
+
heading: "Create Supabase Project",
|
|
131
|
+
body: `1. Go to [supabase.com](https://supabase.com) and sign up
|
|
132
|
+
2. Click "New Project"
|
|
133
|
+
3. Fill in project details (name, database password, region)
|
|
134
|
+
4. Click "Create new project"
|
|
135
|
+
5. Wait for provisioning (2-3 minutes)`,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "get-credentials",
|
|
139
|
+
heading: "Get API Credentials",
|
|
140
|
+
body: `1. In your Supabase project dashboard, go to "Settings" → "API"
|
|
141
|
+
2. You'll find these values:
|
|
142
|
+
- **Project URL:** \`https://xxx.supabase.co\` (this is your \`NEXT_PUBLIC_SUPABASE_URL\`)
|
|
143
|
+
- **anon public key:** A long string starting with \`eyJ...\` (this is your \`NEXT_PUBLIC_SUPABASE_ANON_KEY\`)
|
|
144
|
+
- **service_role key:** Another long string (this is your \`SUPABASE_SERVICE_ROLE_KEY\` - keep this secret!)
|
|
145
|
+
3. Copy these values - you'll need them in the next step`,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "configure-auth",
|
|
149
|
+
heading: "Configure Auth Settings",
|
|
150
|
+
body: `1. Go to "Authentication" → "Providers" in your Supabase dashboard
|
|
151
|
+
2. Enable the providers you want (Email, Google, GitHub, etc.)
|
|
152
|
+
3. For Email provider:
|
|
153
|
+
- Enable "Enable email confirmations" if you want email verification
|
|
154
|
+
- Configure email templates in "Email Templates"
|
|
155
|
+
4. For OAuth providers (e.g., Google):
|
|
156
|
+
- Enable the provider
|
|
157
|
+
- Add your OAuth client ID and secret (from Google Cloud Console)
|
|
158
|
+
- Add redirect URL: \`https://yourproject.supabase.co/auth/v1/callback\`
|
|
159
|
+
5. Go to "Authentication" → "URL Configuration"
|
|
160
|
+
6. Add your site URL: \`http://localhost:3000\` (for development)
|
|
161
|
+
7. Add redirect URLs:
|
|
162
|
+
- \`http://localhost:3000/auth/callback\`
|
|
163
|
+
- \`https://yourdomain.com/auth/callback\` (for production)`,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "env-variables",
|
|
167
|
+
heading: "Environment Variables",
|
|
168
|
+
body: `Add these variables to your \`.env.local\` file:
|
|
169
|
+
|
|
170
|
+
\`\`\`env
|
|
171
|
+
# Supabase
|
|
172
|
+
NEXT_PUBLIC_SUPABASE_URL="https://xxx.supabase.co"
|
|
173
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
174
|
+
SUPABASE_SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." # Server-side only
|
|
175
|
+
\`\`\`
|
|
176
|
+
|
|
177
|
+
**Important:**
|
|
178
|
+
- \`NEXT_PUBLIC_SUPABASE_URL\` is your project URL from "Settings" → "API"
|
|
179
|
+
- \`NEXT_PUBLIC_SUPABASE_ANON_KEY\` is the anon/public key (safe to expose in client)
|
|
180
|
+
- \`SUPABASE_SERVICE_ROLE_KEY\` is the service role key (NEVER expose in client - server-side only)
|
|
181
|
+
- All \`NEXT_PUBLIC_*\` variables are exposed to the browser`,
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
id: "test",
|
|
185
|
+
heading: "Testing",
|
|
186
|
+
body: `1. Restart your dev server: \`npm run dev\`
|
|
187
|
+
2. Navigate to \`http://localhost:3000/sign-up\`
|
|
188
|
+
3. Try creating an account with email/password
|
|
189
|
+
4. Check your email for the verification link (if enabled)
|
|
190
|
+
5. Try signing in with OAuth provider (if configured)
|
|
191
|
+
6. Verify user appears in Supabase dashboard → "Authentication" → "Users"
|
|
192
|
+
|
|
193
|
+
**Troubleshooting:**
|
|
194
|
+
- If sign-up fails, check that email provider is enabled in Supabase dashboard
|
|
195
|
+
- For OAuth, verify redirect URLs match exactly in both Supabase and OAuth provider
|
|
196
|
+
- Check browser console and Supabase logs for error details
|
|
197
|
+
- Ensure environment variables are correctly set`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
"authentication/nextauth": {
|
|
202
|
+
title: "NextAuth",
|
|
203
|
+
updated: "December 18, 2025",
|
|
204
|
+
toc: [
|
|
205
|
+
{ id: "overview", title: "Overview" },
|
|
206
|
+
{ id: "generate-secret", title: "Generate NEXTAUTH_SECRET" },
|
|
207
|
+
{ id: "configure-providers", title: "Configure Providers" },
|
|
208
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
209
|
+
{ id: "test", title: "Testing" },
|
|
210
|
+
],
|
|
211
|
+
sections: [
|
|
212
|
+
{
|
|
213
|
+
id: "overview",
|
|
214
|
+
heading: "Overview",
|
|
215
|
+
body: "NextAuth.js (Auth.js) is a popular authentication library for Next.js. It supports email/password, OAuth providers (Google, GitHub, etc.), and database sessions. The scaffold uses NextAuth v5 (Auth.js) with adapters for your database.",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "generate-secret",
|
|
219
|
+
heading: "Generate NEXTAUTH_SECRET",
|
|
220
|
+
body: `Generate a secure random secret for NextAuth. This is used to encrypt sessions and tokens.
|
|
221
|
+
|
|
222
|
+
**Option 1: Using OpenSSL (recommended)**
|
|
223
|
+
\`\`\`bash
|
|
224
|
+
openssl rand -base64 32
|
|
225
|
+
\`\`\`
|
|
226
|
+
|
|
227
|
+
**Option 2: Using Node.js**
|
|
228
|
+
\`\`\`bash
|
|
229
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
230
|
+
\`\`\`
|
|
231
|
+
|
|
232
|
+
Copy the generated secret - you'll add it to your \`.env.local\` file.`,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: "configure-providers",
|
|
236
|
+
heading: "Configure Providers",
|
|
237
|
+
body: `**For Google OAuth:**
|
|
238
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
239
|
+
2. Create a project or select existing
|
|
240
|
+
3. Enable "Google+ API"
|
|
241
|
+
4. Go to "Credentials" → "Create Credentials" → "OAuth client ID"
|
|
242
|
+
5. Configure OAuth consent screen if prompted
|
|
243
|
+
6. Set redirect URI: \`http://localhost:3000/api/auth/callback/google\` (and production URL)
|
|
244
|
+
7. Copy Client ID and Client Secret
|
|
245
|
+
|
|
246
|
+
**For GitHub OAuth:**
|
|
247
|
+
1. Go to GitHub → Settings → Developer settings → OAuth Apps
|
|
248
|
+
2. Click "New OAuth App"
|
|
249
|
+
3. Set Authorization callback URL: \`http://localhost:3000/api/auth/callback/github\`
|
|
250
|
+
4. Copy Client ID and Client Secret
|
|
251
|
+
|
|
252
|
+
**For Email Provider:**
|
|
253
|
+
NextAuth supports magic links via email. You'll need to configure an email service (Resend, SendGrid, etc.) in your NextAuth configuration.`,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: "env-variables",
|
|
257
|
+
heading: "Environment Variables",
|
|
258
|
+
body: `Add these variables to your \`.env.local\` file:
|
|
259
|
+
|
|
260
|
+
\`\`\`env
|
|
261
|
+
# NextAuth
|
|
262
|
+
NEXTAUTH_URL="http://localhost:3000"
|
|
263
|
+
NEXTAUTH_SECRET="your-generated-secret-from-step-1"
|
|
264
|
+
|
|
265
|
+
# Google OAuth (if using Google provider)
|
|
266
|
+
GOOGLE_CLIENT_ID="your-google-client-id.apps.googleusercontent.com"
|
|
267
|
+
GOOGLE_CLIENT_SECRET="your-google-client-secret"
|
|
268
|
+
|
|
269
|
+
# GitHub OAuth (if using GitHub provider)
|
|
270
|
+
GITHUB_CLIENT_ID="your-github-client-id"
|
|
271
|
+
GITHUB_CLIENT_SECRET="your-github-client-secret"
|
|
272
|
+
|
|
273
|
+
# Database (required for sessions)
|
|
274
|
+
DATABASE_URL="your-database-connection-string"
|
|
275
|
+
\`\`\`
|
|
276
|
+
|
|
277
|
+
**Important:**
|
|
278
|
+
- \`NEXTAUTH_URL\` must match your app URL exactly
|
|
279
|
+
- \`NEXTAUTH_SECRET\` is required - use the secret you generated
|
|
280
|
+
- OAuth provider credentials are only needed if you're using those providers
|
|
281
|
+
- Database URL is required for session storage`,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: "test",
|
|
285
|
+
heading: "Testing",
|
|
286
|
+
body: `1. Restart your dev server: \`npm run dev\`
|
|
287
|
+
2. Navigate to \`http://localhost:3000/api/auth/signin\` (NextAuth sign-in page)
|
|
288
|
+
3. Try signing in with your configured provider
|
|
289
|
+
4. Verify you're redirected to the callback URL
|
|
290
|
+
5. Check that session is created (verify in database or check cookies)
|
|
291
|
+
6. Test protected routes to ensure authentication works
|
|
292
|
+
|
|
293
|
+
**Troubleshooting:**
|
|
294
|
+
- If redirect fails, verify \`NEXTAUTH_URL\` matches your app URL exactly
|
|
295
|
+
- Check OAuth redirect URIs match in both NextAuth config and OAuth provider
|
|
296
|
+
- Verify \`NEXTAUTH_SECRET\` is set correctly
|
|
297
|
+
- Check browser console and server logs for detailed errors
|
|
298
|
+
- Ensure database is properly configured for session storage`,
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
"payments/stripe": {
|
|
304
|
+
title: "Stripe",
|
|
305
|
+
updated: "December 18, 2025",
|
|
306
|
+
toc: [
|
|
307
|
+
{ id: "overview", title: "Overview" },
|
|
308
|
+
{ id: "account-setup", title: "Account Setup" },
|
|
309
|
+
{ id: "api-keys", title: "Get API Keys" },
|
|
310
|
+
{ id: "create-products", title: "Create Products" },
|
|
311
|
+
{ id: "webhooks", title: "Configure Webhooks" },
|
|
312
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
313
|
+
{ id: "test", title: "Testing" },
|
|
314
|
+
],
|
|
315
|
+
sections: [
|
|
316
|
+
{
|
|
317
|
+
id: "overview",
|
|
318
|
+
heading: "Overview",
|
|
319
|
+
body: "Stripe is a payment processing platform that supports subscriptions, one-time payments, and complex billing scenarios. It provides robust APIs, webhooks, and a customer portal for subscription management.",
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: "account-setup",
|
|
323
|
+
heading: "Account Setup",
|
|
324
|
+
body: `1. Go to [stripe.com](https://stripe.com) and sign up for an account
|
|
325
|
+
2. Complete your business profile
|
|
326
|
+
3. Add a payment method (for account verification)
|
|
327
|
+
4. Switch between "Test mode" and "Live mode" using the toggle in the dashboard
|
|
328
|
+
5. **Important:** Use Test mode for development, Live mode for production`,
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
id: "api-keys",
|
|
332
|
+
heading: "Get API Keys",
|
|
333
|
+
body: `1. In your Stripe dashboard, go to "Developers" → "API keys"
|
|
334
|
+
2. You'll see two keys:
|
|
335
|
+
- **Publishable key:** Starts with \`pk_test_\` (test) or \`pk_live_\` (live)
|
|
336
|
+
- **Secret key:** Starts with \`sk_test_\` (test) or \`sk_live_\` (live)
|
|
337
|
+
3. Click "Reveal" to see the secret key
|
|
338
|
+
4. Copy both keys - you'll need them in the next step
|
|
339
|
+
5. **Important:** Never expose your secret key in client-side code`,
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
id: "create-products",
|
|
343
|
+
heading: "Create Products",
|
|
344
|
+
body: `1. Go to "Products" in your Stripe dashboard
|
|
345
|
+
2. Click "Add product"
|
|
346
|
+
3. Fill in product details:
|
|
347
|
+
- Name: Your subscription tier (e.g., "Starter")
|
|
348
|
+
- Description: Optional
|
|
349
|
+
- Pricing: Set recurring price (monthly, yearly, etc.)
|
|
350
|
+
4. Click "Save product"
|
|
351
|
+
5. Copy the **Price ID** (starts with \`price_\`) - you'll need this in your code
|
|
352
|
+
6. Repeat for each subscription tier you want to offer`,
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
id: "webhooks",
|
|
356
|
+
heading: "Configure Webhooks",
|
|
357
|
+
body: `Webhooks notify your app when payment events occur (subscription created, canceled, payment succeeded, etc.).
|
|
358
|
+
|
|
359
|
+
**Step 1: Create Webhook Endpoint**
|
|
360
|
+
1. Go to "Developers" → "Webhooks" in your Stripe dashboard
|
|
361
|
+
2. Click "Add endpoint"
|
|
362
|
+
3. Set the endpoint URL:
|
|
363
|
+
- Development: \`http://localhost:3000/api/webhooks/stripe\` (use [ngrok](https://ngrok.com/) for local testing)
|
|
364
|
+
- Production: \`https://yourdomain.com/api/webhooks/stripe\`
|
|
365
|
+
4. Select events to listen to (recommended: all \`customer.subscription.*\` events)
|
|
366
|
+
5. Click "Add endpoint"
|
|
367
|
+
|
|
368
|
+
**Step 2: Get Webhook Secret**
|
|
369
|
+
1. After creating the endpoint, click on it to view details
|
|
370
|
+
2. Click "Reveal" next to "Signing secret"
|
|
371
|
+
3. Copy the webhook secret (starts with \`whsec_\`)
|
|
372
|
+
4. This is your \`STRIPE_WEBHOOK_SECRET\``,
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
id: "env-variables",
|
|
376
|
+
heading: "Environment Variables",
|
|
377
|
+
body: `Add these variables to your \`.env.local\` file:
|
|
378
|
+
|
|
379
|
+
\`\`\`env
|
|
380
|
+
# Stripe (Test mode)
|
|
381
|
+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_xxxxxxxxxxxxx"
|
|
382
|
+
STRIPE_SECRET_KEY="sk_test_xxxxxxxxxxxxx"
|
|
383
|
+
STRIPE_WEBHOOK_SECRET="whsec_xxxxxxxxxxxxx"
|
|
384
|
+
|
|
385
|
+
# Stripe Products
|
|
386
|
+
NEXT_PUBLIC_STRIPE_STARTER_PRICE_ID="price_xxxxxxxxxxxxx"
|
|
387
|
+
|
|
388
|
+
# App URL
|
|
389
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
390
|
+
\`\`\`
|
|
391
|
+
|
|
392
|
+
**For Production:**
|
|
393
|
+
Replace test keys with live keys (starting with \`pk_live_\` and \`sk_live_\`) and update \`NEXT_PUBLIC_APP_URL\` to your production domain.
|
|
394
|
+
|
|
395
|
+
**Important:**
|
|
396
|
+
- \`NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY\` is safe to expose in client code
|
|
397
|
+
- \`STRIPE_SECRET_KEY\` must NEVER be exposed - use server-side only
|
|
398
|
+
- \`STRIPE_WEBHOOK_SECRET\` is used to verify webhook signatures`,
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
id: "test",
|
|
402
|
+
heading: "Testing",
|
|
403
|
+
body: `1. Use Stripe's test mode (toggle in dashboard)
|
|
404
|
+
2. Restart your dev server: \`npm run dev\`
|
|
405
|
+
3. Navigate to your pricing/checkout page
|
|
406
|
+
4. Use Stripe test card: \`4242 4242 4242 4242\`
|
|
407
|
+
- Expiry: Any future date (e.g., 12/34)
|
|
408
|
+
- CVC: Any 3 digits (e.g., 123)
|
|
409
|
+
- ZIP: Any 5 digits (e.g., 12345)
|
|
410
|
+
5. Complete the checkout flow
|
|
411
|
+
6. Verify webhook events are received (check server logs)
|
|
412
|
+
7. Check subscription status updates in your database
|
|
413
|
+
|
|
414
|
+
**Test Cards:**
|
|
415
|
+
- Success: \`4242 4242 4242 4242\`
|
|
416
|
+
- Decline: \`4000 0000 0000 0002\`
|
|
417
|
+
- Requires 3D Secure: \`4000 0027 6000 3184\`
|
|
418
|
+
|
|
419
|
+
**Testing Webhooks Locally:**
|
|
420
|
+
Use [Stripe CLI](https://stripe.com/docs/stripe-cli) to forward webhooks:
|
|
421
|
+
\`\`\`bash
|
|
422
|
+
stripe listen --forward-to localhost:3000/api/webhooks/stripe
|
|
423
|
+
\`\`\`
|
|
424
|
+
This gives you a webhook signing secret to use locally.`,
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
},
|
|
428
|
+
"payments/lemon-squeezy": {
|
|
429
|
+
title: "Lemon Squeezy",
|
|
430
|
+
updated: "December 18, 2025",
|
|
431
|
+
toc: [
|
|
432
|
+
{ id: "overview", title: "Overview" },
|
|
433
|
+
{ id: "account-setup", title: "Account Setup" },
|
|
434
|
+
{ id: "api-keys", title: "Get API Keys" },
|
|
435
|
+
{ id: "create-products", title: "Create Products" },
|
|
436
|
+
{ id: "webhooks", title: "Configure Webhooks" },
|
|
437
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
438
|
+
{ id: "test", title: "Testing" },
|
|
439
|
+
],
|
|
440
|
+
sections: [
|
|
441
|
+
{
|
|
442
|
+
id: "overview",
|
|
443
|
+
heading: "Overview",
|
|
444
|
+
body: "Lemon Squeezy is a payment platform designed for digital products and SaaS subscriptions. It handles VAT, tax compliance, and provides a hosted checkout experience similar to Gumroad.",
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
id: "account-setup",
|
|
448
|
+
heading: "Account Setup",
|
|
449
|
+
body: `1. Go to [lemonsqueezy.com](https://lemonsqueezy.com) and sign up
|
|
450
|
+
2. Complete your store setup
|
|
451
|
+
3. Connect a payment method (Stripe or PayPal)
|
|
452
|
+
4. Verify your account and business details`,
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
id: "api-keys",
|
|
456
|
+
heading: "Get API Keys",
|
|
457
|
+
body: `1. In your Lemon Squeezy dashboard, go to "Settings" → "API"
|
|
458
|
+
2. Click "Generate API Key"
|
|
459
|
+
3. Name it (e.g., "Production" or "Development")
|
|
460
|
+
4. Copy the API key - this is your \`LEMON_SQUEEZY_API_KEY\`
|
|
461
|
+
5. **Important:** Store this securely - you won't be able to see it again
|
|
462
|
+
6. You'll also need your Store ID, found in "Settings" → "Store"`,
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
id: "create-products",
|
|
466
|
+
heading: "Create Products",
|
|
467
|
+
body: `1. Go to "Products" in your Lemon Squeezy dashboard
|
|
468
|
+
2. Click "New Product"
|
|
469
|
+
3. Fill in product details:
|
|
470
|
+
- Name: Your subscription tier
|
|
471
|
+
- Price: Set subscription pricing
|
|
472
|
+
- Billing interval: Monthly, yearly, etc.
|
|
473
|
+
4. Configure product settings (description, images, etc.)
|
|
474
|
+
5. Save the product
|
|
475
|
+
6. Copy the **Variant ID** (you'll need this for checkout links)
|
|
476
|
+
7. Repeat for each subscription tier`,
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
id: "webhooks",
|
|
480
|
+
heading: "Configure Webhooks",
|
|
481
|
+
body: `Webhooks notify your app when subscription events occur.
|
|
482
|
+
|
|
483
|
+
**Step 1: Create Webhook**
|
|
484
|
+
1. Go to "Settings" → "Webhooks" in your Lemon Squeezy dashboard
|
|
485
|
+
2. Click "Create Webhook"
|
|
486
|
+
3. Set the endpoint URL:
|
|
487
|
+
- Development: \`http://localhost:3000/api/webhooks/lemonsqueezy\` (use ngrok for local testing)
|
|
488
|
+
- Production: \`https://yourdomain.com/api/webhooks/lemonsqueezy\`
|
|
489
|
+
4. Select events to listen to:
|
|
490
|
+
- \`subscription_created\`
|
|
491
|
+
- \`subscription_updated\`
|
|
492
|
+
- \`subscription_cancelled\`
|
|
493
|
+
- \`order_created\`
|
|
494
|
+
- \`payment_success\`
|
|
495
|
+
5. Save the webhook
|
|
496
|
+
6. Copy the **Signing Secret** (shown after creation) - this is your \`LEMON_SQUEEZY_WEBHOOK_SECRET\``,
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
id: "env-variables",
|
|
500
|
+
heading: "Environment Variables",
|
|
501
|
+
body: `Add these variables to your \`.env.local\` file:
|
|
502
|
+
|
|
503
|
+
\`\`\`env
|
|
504
|
+
# Lemon Squeezy
|
|
505
|
+
LEMON_SQUEEZY_API_KEY="your-api-key"
|
|
506
|
+
LEMON_SQUEEZY_STORE_ID="your-store-id"
|
|
507
|
+
LEMON_SQUEEZY_WEBHOOK_SECRET="your-webhook-secret"
|
|
508
|
+
|
|
509
|
+
# Product Variant IDs
|
|
510
|
+
NEXT_PUBLIC_LEMON_SQUEEZY_STARTER_VARIANT_ID="variant_xxxxxxxxxxxxx"
|
|
511
|
+
|
|
512
|
+
# App URL
|
|
513
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
514
|
+
\`\`\`
|
|
515
|
+
|
|
516
|
+
**Important:**
|
|
517
|
+
- \`LEMON_SQUEEZY_API_KEY\` comes from "Settings" → "API"
|
|
518
|
+
- \`LEMON_SQUEEZY_STORE_ID\` comes from "Settings" → "Store"
|
|
519
|
+
- \`LEMON_SQUEEZY_WEBHOOK_SECRET\` comes from webhook configuration
|
|
520
|
+
- Variant IDs come from your products`,
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: "test",
|
|
524
|
+
heading: "Testing",
|
|
525
|
+
body: `1. Use Lemon Squeezy's test mode
|
|
526
|
+
2. Restart your dev server: \`npm run dev\`
|
|
527
|
+
3. Navigate to your pricing/checkout page
|
|
528
|
+
4. Click through to Lemon Squeezy checkout
|
|
529
|
+
5. Use test card: \`4242 4242 4242 4242\`
|
|
530
|
+
6. Complete checkout
|
|
531
|
+
7. Verify webhook events are received
|
|
532
|
+
8. Check subscription status in your database
|
|
533
|
+
|
|
534
|
+
**Test Mode:**
|
|
535
|
+
Lemon Squeezy uses Stripe's test mode when your connected Stripe account is in test mode. Use Stripe test cards for testing.
|
|
536
|
+
|
|
537
|
+
**Testing Webhooks Locally:**
|
|
538
|
+
Use [ngrok](https://ngrok.com/) to expose your local server:
|
|
539
|
+
\`\`\`bash
|
|
540
|
+
ngrok http 3000
|
|
541
|
+
\`\`\`
|
|
542
|
+
Update your webhook endpoint URL in Lemon Squeezy dashboard with the ngrok URL.`,
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
"payments/polar": {
|
|
547
|
+
title: "Polar",
|
|
548
|
+
updated: "December 18, 2025",
|
|
549
|
+
toc: [
|
|
550
|
+
{ id: "overview", title: "Overview" },
|
|
551
|
+
{ id: "account-setup", title: "Account Setup" },
|
|
552
|
+
{ id: "create-products", title: "Create Products" },
|
|
553
|
+
{ id: "api-keys", title: "API Keys" },
|
|
554
|
+
{ id: "webhooks", title: "Webhooks" },
|
|
555
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
556
|
+
{ id: "test", title: "Testing" },
|
|
557
|
+
],
|
|
558
|
+
sections: [
|
|
559
|
+
{
|
|
560
|
+
id: "overview",
|
|
561
|
+
heading: "Overview",
|
|
562
|
+
body: "Polar.sh is the default payments provider in the scaffold. It handles subscription management, checkout flows, customer portals, and webhook events for subscription lifecycle changes.",
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
id: "account-setup",
|
|
566
|
+
heading: "Account Setup",
|
|
567
|
+
body: `1. Go to [polar.sh](https://polar.sh) and sign up for an account
|
|
568
|
+
2. Complete your organization profile
|
|
569
|
+
3. Connect a payment method (Stripe account) - Polar uses Stripe as the payment processor
|
|
570
|
+
4. Navigate to your organization dashboard`,
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
id: "create-products",
|
|
574
|
+
heading: "Create Products",
|
|
575
|
+
body: `1. In your Polar dashboard, go to "Products" → "New Product"
|
|
576
|
+
2. Create your subscription tiers (e.g., "Starter" and "Professional")
|
|
577
|
+
3. For each product:
|
|
578
|
+
- Set the name, description, and pricing
|
|
579
|
+
- Choose billing interval (monthly, yearly, etc.)
|
|
580
|
+
- Copy the **Product ID** (you'll need this)
|
|
581
|
+
- Copy the **Product Slug** (you'll need this too)
|
|
582
|
+
4. Make note of at least one product ID and slug for your starter tier`,
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
id: "api-keys",
|
|
586
|
+
heading: "API Keys",
|
|
587
|
+
body: `1. Go to "Settings" → "API Keys" in your Polar dashboard
|
|
588
|
+
2. Click "Generate API Key"
|
|
589
|
+
3. Name it (e.g., "Production API Key" or "Development")
|
|
590
|
+
4. Copy the **Access Token** - this is your \`POLAR_ACCESS_TOKEN\`
|
|
591
|
+
5. **Important:** Store this securely - you won't be able to see it again after closing the dialog`,
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
id: "webhooks",
|
|
595
|
+
heading: "Webhooks",
|
|
596
|
+
body: `Webhooks notify your app when subscription events occur (created, canceled, updated, etc.).
|
|
597
|
+
|
|
598
|
+
**Step 1: Generate Webhook Secret**
|
|
599
|
+
1. Go to "Settings" → "Webhooks" in your Polar dashboard
|
|
600
|
+
2. Click "Add Endpoint" or edit existing endpoint
|
|
601
|
+
3. Set the endpoint URL:
|
|
602
|
+
- Development: \`http://localhost:3000/api/webhooks/polar\` (use ngrok for local testing)
|
|
603
|
+
- Production: \`https://yourdomain.com/api/webhooks/polar\`
|
|
604
|
+
4. Copy the **Webhook Secret** - this is your \`POLAR_WEBHOOK_SECRET\`
|
|
605
|
+
5. Select the events you want to receive (recommended: all subscription events)
|
|
606
|
+
6. Save the endpoint`,
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
id: "env-variables",
|
|
610
|
+
heading: "Environment Variables",
|
|
611
|
+
body: `Add these variables to your \`.env.local\` file:
|
|
612
|
+
|
|
613
|
+
\`\`\`env
|
|
614
|
+
# Polar.sh API
|
|
615
|
+
POLAR_ACCESS_TOKEN="your-polar-access-token"
|
|
616
|
+
|
|
617
|
+
# Polar.sh Webhooks
|
|
618
|
+
POLAR_WEBHOOK_SECRET="your-webhook-secret"
|
|
619
|
+
|
|
620
|
+
# Polar.sh Products (from step 2)
|
|
621
|
+
NEXT_PUBLIC_STARTER_TIER="your-product-id"
|
|
622
|
+
NEXT_PUBLIC_STARTER_SLUG="your-product-slug"
|
|
623
|
+
|
|
624
|
+
# Success URL (where users redirect after successful payment)
|
|
625
|
+
POLAR_SUCCESS_URL="success"
|
|
626
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
627
|
+
\`\`\`
|
|
628
|
+
|
|
629
|
+
**Important:**
|
|
630
|
+
- \`POLAR_ACCESS_TOKEN\` comes from "Settings" → "API Keys"
|
|
631
|
+
- \`POLAR_WEBHOOK_SECRET\` comes from "Settings" → "Webhooks"
|
|
632
|
+
- \`NEXT_PUBLIC_STARTER_TIER\` is the Product ID from your product
|
|
633
|
+
- \`NEXT_PUBLIC_STARTER_SLUG\` is the Product Slug from your product
|
|
634
|
+
- \`POLAR_SUCCESS_URL\` is the relative path where users redirect after checkout`,
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
id: "test",
|
|
638
|
+
heading: "Testing",
|
|
639
|
+
body: `1. Restart your dev server: \`npm run dev\`
|
|
640
|
+
2. Navigate to \`/pricing\` page
|
|
641
|
+
3. Click "Get Started" on a plan
|
|
642
|
+
4. Complete the checkout flow (use Stripe test cards)
|
|
643
|
+
5. Verify webhook events are received (check server logs)
|
|
644
|
+
6. Check that subscription status updates in your database
|
|
645
|
+
|
|
646
|
+
**Test Cards (Stripe):**
|
|
647
|
+
- Success: \`4242 4242 4242 4242\`
|
|
648
|
+
- Decline: \`4000 0000 0000 0002\`
|
|
649
|
+
- Any future expiry date, any 3-digit CVC
|
|
650
|
+
|
|
651
|
+
**Testing Webhooks Locally:**
|
|
652
|
+
Use [ngrok](https://ngrok.com/) to expose your local server:
|
|
653
|
+
\`\`\`bash
|
|
654
|
+
ngrok http 3000
|
|
655
|
+
\`\`\`
|
|
656
|
+
Then use the ngrok URL in your Polar webhook endpoint configuration.`,
|
|
657
|
+
},
|
|
658
|
+
],
|
|
659
|
+
},
|
|
660
|
+
|
|
661
|
+
"emails/resend": {
|
|
662
|
+
title: "Resend",
|
|
663
|
+
updated: "December 18, 2025",
|
|
664
|
+
toc: [
|
|
665
|
+
{ id: "overview", title: "Overview" },
|
|
666
|
+
{ id: "account-setup", title: "Account Setup" },
|
|
667
|
+
{ id: "api-key", title: "Get API Key" },
|
|
668
|
+
{ id: "domain-setup", title: "Domain Setup (Optional)" },
|
|
669
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
670
|
+
{ id: "test", title: "Testing" },
|
|
671
|
+
],
|
|
672
|
+
sections: [
|
|
673
|
+
{
|
|
674
|
+
id: "overview",
|
|
675
|
+
heading: "Overview",
|
|
676
|
+
body: "Resend is a modern transactional email API built for developers. It's simple to use, has great deliverability, and works perfectly for sending transactional emails like welcome emails, password resets, and notifications.",
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
id: "account-setup",
|
|
680
|
+
heading: "Account Setup",
|
|
681
|
+
body: `1. Go to [resend.com](https://resend.com) and sign up for an account
|
|
682
|
+
2. Verify your email address
|
|
683
|
+
3. You'll start with a free tier that includes 3,000 emails/month and unlimited domains`,
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
id: "api-key",
|
|
687
|
+
heading: "Get API Key",
|
|
688
|
+
body: `1. In your Resend dashboard, go to "API Keys"
|
|
689
|
+
2. Click "Create API Key"
|
|
690
|
+
3. Name it (e.g., "Production" or "Development")
|
|
691
|
+
4. Select permissions (for sending emails, you only need "Sending access")
|
|
692
|
+
5. Click "Add"
|
|
693
|
+
6. **Important:** Copy the API key immediately - you won't be able to see it again after closing the dialog
|
|
694
|
+
7. This is your \`RESEND_API_KEY\``,
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
id: "domain-setup",
|
|
698
|
+
heading: "Domain Setup (Optional but Recommended)",
|
|
699
|
+
body: `While you can use Resend's test domain initially, setting up your own domain improves deliverability.
|
|
700
|
+
|
|
701
|
+
**Step 1: Add Domain**
|
|
702
|
+
1. Go to "Domains" in your Resend dashboard
|
|
703
|
+
2. Click "Add Domain"
|
|
704
|
+
3. Enter your domain (e.g., \`mg.yourdomain.com\` - using a subdomain is recommended)
|
|
705
|
+
4. Click "Add"
|
|
706
|
+
|
|
707
|
+
**Step 2: Add DNS Records**
|
|
708
|
+
Resend will show you the DNS records to add. You need to add:
|
|
709
|
+
- **SPF Record:** \`v=spf1 include:resend.com ~all\`
|
|
710
|
+
- **DKIM Records:** Multiple CNAME records (Resend will provide exact values)
|
|
711
|
+
- **DMARC Record (Optional):** \`v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com\`
|
|
712
|
+
|
|
713
|
+
**Step 3: Verify Domain**
|
|
714
|
+
1. Add the DNS records in your domain's DNS provider (Cloudflare, Namecheap, etc.)
|
|
715
|
+
2. Wait for DNS propagation (can take up to 24 hours, usually much faster)
|
|
716
|
+
3. Click "Verify" in Resend dashboard
|
|
717
|
+
4. Once verified, you can send emails from addresses like \`noreply@mg.yourdomain.com\``,
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
id: "env-variables",
|
|
721
|
+
heading: "Environment Variables",
|
|
722
|
+
body: `Add this variable to your \`.env.local\` file:
|
|
723
|
+
|
|
724
|
+
\`\`\`env
|
|
725
|
+
# Resend
|
|
726
|
+
RESEND_API_KEY="re_xxxxxxxxxxxxx"
|
|
727
|
+
\`\`\`
|
|
728
|
+
|
|
729
|
+
**For Domain Setup:**
|
|
730
|
+
If you've set up a custom domain, you'll also need to specify the "From" email address in your email sending code. For example:
|
|
731
|
+
\`\`\`typescript
|
|
732
|
+
from: "noreply@mg.yourdomain.com"
|
|
733
|
+
\`\`\`
|
|
734
|
+
|
|
735
|
+
**Without Domain Setup:**
|
|
736
|
+
You can use Resend's test domain:
|
|
737
|
+
\`\`\`typescript
|
|
738
|
+
from: "onboarding@resend.dev"
|
|
739
|
+
\`\`\`
|
|
740
|
+
Note: Emails from the test domain may have delivery limitations.`,
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
id: "test",
|
|
744
|
+
heading: "Testing",
|
|
745
|
+
body: `1. Add \`RESEND_API_KEY\` to your \`.env.local\`
|
|
746
|
+
2. Restart your dev server: \`npm run dev\`
|
|
747
|
+
3. Trigger an email (e.g., sign up a user, reset password)
|
|
748
|
+
4. Check your email inbox
|
|
749
|
+
5. In Resend dashboard → "Logs", you can see email delivery status
|
|
750
|
+
|
|
751
|
+
**Test Email Script:**
|
|
752
|
+
You can test sending emails directly:
|
|
753
|
+
\`\`\`typescript
|
|
754
|
+
import { Resend } from 'resend';
|
|
755
|
+
|
|
756
|
+
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
757
|
+
|
|
758
|
+
await resend.emails.send({
|
|
759
|
+
from: 'onboarding@resend.dev',
|
|
760
|
+
to: 'your-email@example.com',
|
|
761
|
+
subject: 'Hello World',
|
|
762
|
+
html: '<p>This is a test email!</p>',
|
|
763
|
+
});
|
|
764
|
+
\`\`\`
|
|
765
|
+
|
|
766
|
+
**Troubleshooting:**
|
|
767
|
+
- If emails don't arrive, check the "Logs" in Resend dashboard for error messages
|
|
768
|
+
- Verify your API key is correct
|
|
769
|
+
- For production, ensure domain is verified
|
|
770
|
+
- Check spam folder if using test domain`,
|
|
771
|
+
},
|
|
772
|
+
],
|
|
773
|
+
},
|
|
774
|
+
"emails/mailgun": {
|
|
775
|
+
title: "Mailgun",
|
|
776
|
+
updated: "December 18, 2025",
|
|
777
|
+
toc: [
|
|
778
|
+
{ id: "overview", title: "Overview" },
|
|
779
|
+
{ id: "account-setup", title: "Account Setup" },
|
|
780
|
+
{ id: "api-key", title: "Get API Key" },
|
|
781
|
+
{ id: "domain-setup", title: "Domain Setup" },
|
|
782
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
783
|
+
{ id: "test", title: "Testing" },
|
|
784
|
+
],
|
|
785
|
+
sections: [
|
|
786
|
+
{
|
|
787
|
+
id: "overview",
|
|
788
|
+
heading: "Overview",
|
|
789
|
+
body: "Mailgun is an email service provider that specializes in transactional emails and inbound email processing. It's great for high-volume sending and provides detailed analytics and tracking.",
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
id: "account-setup",
|
|
793
|
+
heading: "Account Setup",
|
|
794
|
+
body: `1. Go to [mailgun.com](https://www.mailgun.com) and sign up
|
|
795
|
+
2. Verify your email address
|
|
796
|
+
3. You'll start with a free tier that includes 5,000 emails/month for the first 3 months
|
|
797
|
+
4. Complete account verification`,
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
id: "api-key",
|
|
801
|
+
heading: "Get API Key",
|
|
802
|
+
body: `1. In your Mailgun dashboard, go to "Sending" → "Domain Settings"
|
|
803
|
+
2. You'll see your API key displayed
|
|
804
|
+
3. Copy the **API Key** - this is your \`MAILGUN_API_KEY\`
|
|
805
|
+
4. Also note your domain (e.g., \`sandboxxxx.mailgun.org\` for testing, or your custom domain)
|
|
806
|
+
5. **Important:** Store the API key securely`,
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: "domain-setup",
|
|
810
|
+
heading: "Domain Setup",
|
|
811
|
+
body: `For production, you need to add and verify your sending domain.
|
|
812
|
+
|
|
813
|
+
**Step 1: Add Domain**
|
|
814
|
+
1. Go to "Sending" → "Domains" in your Mailgun dashboard
|
|
815
|
+
2. Click "Add New Domain"
|
|
816
|
+
3. Enter your domain (e.g., \`mg.yourdomain.com\` - using a subdomain is recommended)
|
|
817
|
+
4. Click "Add Domain"
|
|
818
|
+
|
|
819
|
+
**Step 2: Add DNS Records**
|
|
820
|
+
Mailgun will show you the DNS records to add:
|
|
821
|
+
- **TXT Record (SPF):** \`v=spf1 include:mailgun.org ~all\`
|
|
822
|
+
- **CNAME Records (DKIM):** Multiple CNAME records (Mailgun provides exact values)
|
|
823
|
+
- **TXT Record (DMARC):** \`v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com\`
|
|
824
|
+
- **MX Records (for inbound):** Optional, only if you want to receive emails
|
|
825
|
+
|
|
826
|
+
**Step 3: Verify Domain**
|
|
827
|
+
1. Add the DNS records in your domain's DNS provider
|
|
828
|
+
2. Wait for DNS propagation (can take up to 24 hours, usually faster)
|
|
829
|
+
3. Click "Verify DNS Settings" in Mailgun
|
|
830
|
+
4. Once verified, you can send from addresses like \`noreply@mg.yourdomain.com\`
|
|
831
|
+
|
|
832
|
+
**For Testing:**
|
|
833
|
+
You can use Mailgun's sandbox domain (e.g., \`sandboxxxx.mailgun.org\`) without DNS setup, but emails may have delivery limitations.`,
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
id: "env-variables",
|
|
837
|
+
heading: "Environment Variables",
|
|
838
|
+
body: `Add these variables to your \`.env.local\` file:
|
|
839
|
+
|
|
840
|
+
\`\`\`env
|
|
841
|
+
# Mailgun
|
|
842
|
+
MAILGUN_API_KEY="your-mailgun-api-key"
|
|
843
|
+
MAILGUN_DOMAIN="mg.yourdomain.com" # or sandboxxxx.mailgun.org for testing
|
|
844
|
+
\`\`\`
|
|
845
|
+
|
|
846
|
+
**Important:**
|
|
847
|
+
- \`MAILGUN_API_KEY\` comes from "Sending" → "Domain Settings"
|
|
848
|
+
- \`MAILGUN_DOMAIN\` is your verified domain or sandbox domain
|
|
849
|
+
- For production, use your custom verified domain
|
|
850
|
+
- For development, you can use the sandbox domain`,
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
id: "test",
|
|
854
|
+
heading: "Testing",
|
|
855
|
+
body: `1. Add \`MAILGUN_API_KEY\` and \`MAILGUN_DOMAIN\` to your \`.env.local\`
|
|
856
|
+
2. Restart your dev server: \`npm run dev\`
|
|
857
|
+
3. Trigger an email (e.g., sign up, password reset)
|
|
858
|
+
4. Check your email inbox
|
|
859
|
+
5. In Mailgun dashboard → "Sending" → "Logs", you can see email delivery status
|
|
860
|
+
|
|
861
|
+
**Test Email Script:**
|
|
862
|
+
\`\`\`typescript
|
|
863
|
+
import formData from 'form-data';
|
|
864
|
+
import Mailgun from 'mailgun.js';
|
|
865
|
+
|
|
866
|
+
const mailgun = new Mailgun(formData);
|
|
867
|
+
const mg = mailgun.client({
|
|
868
|
+
username: 'api',
|
|
869
|
+
key: process.env.MAILGUN_API_KEY!,
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
await mg.messages.create(process.env.MAILGUN_DOMAIN!, {
|
|
873
|
+
from: \`noreply@\${process.env.MAILGUN_DOMAIN}\`,
|
|
874
|
+
to: ['your-email@example.com'],
|
|
875
|
+
subject: 'Hello',
|
|
876
|
+
text: 'Testing Mailgun!',
|
|
877
|
+
});
|
|
878
|
+
\`\`\`
|
|
879
|
+
|
|
880
|
+
**Troubleshooting:**
|
|
881
|
+
- If emails don't arrive, check Mailgun logs for error messages
|
|
882
|
+
- Verify API key and domain are correct
|
|
883
|
+
- For production, ensure domain is verified
|
|
884
|
+
- Check spam folder, especially when using sandbox domain
|
|
885
|
+
- Review Mailgun's sending limits and quotas`,
|
|
886
|
+
},
|
|
887
|
+
],
|
|
888
|
+
},
|
|
889
|
+
"emails/inbound-webhooks": {
|
|
890
|
+
title: "Inbound webhooks",
|
|
891
|
+
updated: "December 18, 2025",
|
|
892
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
893
|
+
sections: [{ id: "overview", heading: "Overview", body: "Receive inbound email events and forward to your app endpoint." }],
|
|
894
|
+
},
|
|
895
|
+
"emails/dns": {
|
|
896
|
+
title: "DNS (DKIM/DMARC/SPF)",
|
|
897
|
+
updated: "December 18, 2025",
|
|
898
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
899
|
+
sections: [{ id: "overview", heading: "Overview", body: "Use a subdomain and configure DKIM/DMARC/SPF to avoid spam." }],
|
|
900
|
+
},
|
|
901
|
+
|
|
902
|
+
"database/supabase-postgres": {
|
|
903
|
+
title: "Supabase Postgres",
|
|
904
|
+
updated: "December 18, 2025",
|
|
905
|
+
toc: [
|
|
906
|
+
{ id: "overview", title: "Overview" },
|
|
907
|
+
{ id: "create-project", title: "Create Project" },
|
|
908
|
+
{ id: "get-connection-string", title: "Get Connection String" },
|
|
909
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
910
|
+
{ id: "run-migrations", title: "Run Migrations" },
|
|
911
|
+
{ id: "test", title: "Testing" },
|
|
912
|
+
],
|
|
913
|
+
sections: [
|
|
914
|
+
{
|
|
915
|
+
id: "overview",
|
|
916
|
+
heading: "Overview",
|
|
917
|
+
body: "Supabase provides a fully managed PostgreSQL database with additional features like real-time subscriptions, storage, and auth. It works seamlessly with Drizzle ORM and is a great alternative to Neon.",
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
id: "create-project",
|
|
921
|
+
heading: "Create Project",
|
|
922
|
+
body: `1. Go to [supabase.com](https://supabase.com) and sign up for an account
|
|
923
|
+
2. Click "New Project"
|
|
924
|
+
3. Choose an organization (or create one)
|
|
925
|
+
4. Fill in project details:
|
|
926
|
+
- Name: Your project name
|
|
927
|
+
- Database Password: Create a strong password (save this!)
|
|
928
|
+
- Region: Choose closest to your users
|
|
929
|
+
5. Click "Create new project"
|
|
930
|
+
6. Wait for provisioning (usually 2-3 minutes)`,
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
id: "get-connection-string",
|
|
934
|
+
heading: "Get Connection String",
|
|
935
|
+
body: `1. In your Supabase project dashboard, go to "Settings" → "Database"
|
|
936
|
+
2. Scroll down to "Connection string"
|
|
937
|
+
3. Select "URI" tab (not "JDBC" or "Golang")
|
|
938
|
+
4. Copy the connection string - it looks like: \`postgresql://postgres:[YOUR-PASSWORD]@db.xxx.supabase.co:5432/postgres\`
|
|
939
|
+
5. **Important:** Replace \`[YOUR-PASSWORD]\` with the database password you set when creating the project
|
|
940
|
+
6. Add \`?sslmode=require\` at the end for SSL
|
|
941
|
+
|
|
942
|
+
**Connection Pooling (Recommended for Serverless):**
|
|
943
|
+
Supabase provides connection pooling for better performance with serverless functions. In "Settings" → "Database", look for "Connection pooling" and use the "Transaction" mode connection string.`,
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
id: "env-variables",
|
|
947
|
+
heading: "Environment Variables",
|
|
948
|
+
body: `Add this variable to your \`.env.local\` file:
|
|
949
|
+
|
|
950
|
+
\`\`\`env
|
|
951
|
+
# Supabase Postgres
|
|
952
|
+
DATABASE_URL="postgresql://postgres:your-password@db.xxx.supabase.co:5432/postgres?sslmode=require"
|
|
953
|
+
\`\`\`
|
|
954
|
+
|
|
955
|
+
**Important:**
|
|
956
|
+
- Replace \`your-password\` with your actual database password
|
|
957
|
+
- The connection string should include \`?sslmode=require\` for SSL
|
|
958
|
+
- For production, use environment variables in your hosting platform
|
|
959
|
+
- Never commit \`.env.local\` to git`,
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
id: "run-migrations",
|
|
963
|
+
heading: "Run Migrations",
|
|
964
|
+
body: `After setting \`DATABASE_URL\`, run the database migrations:
|
|
965
|
+
|
|
966
|
+
\`\`\`bash
|
|
967
|
+
# Generate migrations from your schema
|
|
968
|
+
npx drizzle-kit generate
|
|
969
|
+
|
|
970
|
+
# Push migrations to your database
|
|
971
|
+
npx drizzle-kit push
|
|
972
|
+
\`\`\`
|
|
973
|
+
|
|
974
|
+
This will create all the required tables in your Supabase database.
|
|
975
|
+
|
|
976
|
+
**Using Supabase SQL Editor:**
|
|
977
|
+
You can also run SQL directly in Supabase:
|
|
978
|
+
1. Go to "SQL Editor" in your Supabase dashboard
|
|
979
|
+
2. Write and run SQL queries
|
|
980
|
+
3. View results in the interface`,
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
id: "test",
|
|
984
|
+
heading: "Testing",
|
|
985
|
+
body: `1. Restart your dev server: \`npm run dev\`
|
|
986
|
+
2. Navigate to \`http://localhost:3000/sign-up\`
|
|
987
|
+
3. Create a test account
|
|
988
|
+
4. Verify the user was created:
|
|
989
|
+
- Go to Supabase dashboard → "Table Editor"
|
|
990
|
+
- Select the \`user\` table
|
|
991
|
+
- You should see your test user
|
|
992
|
+
5. Sign in and verify sessions work correctly
|
|
993
|
+
|
|
994
|
+
**Troubleshooting:**
|
|
995
|
+
- If connection fails, verify the password in the connection string is correct
|
|
996
|
+
- Check that SSL mode is set to \`require\`
|
|
997
|
+
- Ensure your IP isn't blocked in "Settings" → "Database" → "Connection Pooling"
|
|
998
|
+
- For serverless, use the connection pooling URL instead`,
|
|
999
|
+
},
|
|
1000
|
+
],
|
|
1001
|
+
},
|
|
1002
|
+
"database/neon-postgres": {
|
|
1003
|
+
title: "Neon Postgres",
|
|
1004
|
+
updated: "December 18, 2025",
|
|
1005
|
+
toc: [
|
|
1006
|
+
{ id: "overview", title: "Overview" },
|
|
1007
|
+
{ id: "create-project", title: "Create Project" },
|
|
1008
|
+
{ id: "get-connection-string", title: "Get Connection String" },
|
|
1009
|
+
{ id: "env-variables", title: "Environment Variables" },
|
|
1010
|
+
{ id: "run-migrations", title: "Run Migrations" },
|
|
1011
|
+
{ id: "test", title: "Testing" },
|
|
1012
|
+
],
|
|
1013
|
+
sections: [
|
|
1014
|
+
{
|
|
1015
|
+
id: "overview",
|
|
1016
|
+
heading: "Overview",
|
|
1017
|
+
body: "Neon is a serverless PostgreSQL database that works seamlessly with Drizzle ORM. It's the default database provider in the scaffold, offering automatic scaling, branching, and zero-downtime migrations.",
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
id: "create-project",
|
|
1021
|
+
heading: "Create Project",
|
|
1022
|
+
body: `1. Go to [neon.tech](https://neon.tech) and sign up for an account
|
|
1023
|
+
2. Click "Create Project"
|
|
1024
|
+
3. Choose a project name and region (select closest to your users)
|
|
1025
|
+
4. Select PostgreSQL version (15 or 16 recommended)
|
|
1026
|
+
5. Click "Create Project"
|
|
1027
|
+
6. Wait for the database to be provisioned (usually takes ~30 seconds)`,
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
id: "get-connection-string",
|
|
1031
|
+
heading: "Get Connection String",
|
|
1032
|
+
body: `1. In your Neon project dashboard, you'll see a connection string displayed
|
|
1033
|
+
2. Click "Copy" next to the connection string
|
|
1034
|
+
3. The connection string looks like: \`postgresql://user:password@ep-xxx-xxx.region.aws.neon.tech/dbname?sslmode=require\`
|
|
1035
|
+
4. **Important:** This is your database password - store it securely. You can also reset it in "Settings" → "Password" if needed
|
|
1036
|
+
|
|
1037
|
+
**Connection Pooling (Optional but Recommended):**
|
|
1038
|
+
Neon also provides a pooled connection string for better performance with serverless functions. Look for the "Pooled connection" option in the dashboard.`,
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
id: "env-variables",
|
|
1042
|
+
heading: "Environment Variables",
|
|
1043
|
+
body: `Add this variable to your \`.env.local\` file:
|
|
1044
|
+
|
|
1045
|
+
\`\`\`env
|
|
1046
|
+
# Neon Postgres
|
|
1047
|
+
DATABASE_URL="postgresql://user:password@ep-xxx-xxx.region.aws.neon.tech/dbname?sslmode=require"
|
|
1048
|
+
\`\`\`
|
|
1049
|
+
|
|
1050
|
+
**Important:**
|
|
1051
|
+
- Replace the connection string with your actual Neon connection string
|
|
1052
|
+
- For production, use environment variables in your hosting platform (Vercel, etc.)
|
|
1053
|
+
- Never commit \`.env.local\` to git (it's already in \`.gitignore\`)`,
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
id: "run-migrations",
|
|
1057
|
+
heading: "Run Migrations",
|
|
1058
|
+
body: `After setting \`DATABASE_URL\`, run the database migrations:
|
|
1059
|
+
|
|
1060
|
+
\`\`\`bash
|
|
1061
|
+
# Generate migrations from your schema
|
|
1062
|
+
npx drizzle-kit generate
|
|
1063
|
+
|
|
1064
|
+
# Push migrations to your database
|
|
1065
|
+
npx drizzle-kit push
|
|
1066
|
+
\`\`\`
|
|
1067
|
+
|
|
1068
|
+
This will create all the required tables (users, sessions, accounts, subscriptions, etc.) in your Neon database.
|
|
1069
|
+
|
|
1070
|
+
**Alternative: Use Drizzle Studio (Optional)**
|
|
1071
|
+
View and manage your database with Drizzle Studio:
|
|
1072
|
+
\`\`\`bash
|
|
1073
|
+
npx drizzle-kit studio
|
|
1074
|
+
\`\`\`
|
|
1075
|
+
This opens a local web interface to browse your database tables and data.`,
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
id: "test",
|
|
1079
|
+
heading: "Testing",
|
|
1080
|
+
body: `1. Restart your dev server: \`npm run dev\`
|
|
1081
|
+
2. Navigate to \`http://localhost:3000/sign-up\`
|
|
1082
|
+
3. Create a test account
|
|
1083
|
+
4. Verify the user was created in your database (use Drizzle Studio or Neon dashboard)
|
|
1084
|
+
5. Sign in and verify sessions work correctly
|
|
1085
|
+
|
|
1086
|
+
**Using Neon Dashboard:**
|
|
1087
|
+
1. Go to your Neon project dashboard
|
|
1088
|
+
2. Click "SQL Editor"
|
|
1089
|
+
3. Run: \`SELECT * FROM user;\` to see created users
|
|
1090
|
+
|
|
1091
|
+
**Troubleshooting:**
|
|
1092
|
+
- If connection fails, verify the connection string is correct
|
|
1093
|
+
- Check that SSL mode is set to \`require\` in the connection string
|
|
1094
|
+
- Ensure your IP isn't blocked (Neon allows all IPs by default)
|
|
1095
|
+
- For serverless environments (Vercel), connection pooling is recommended`,
|
|
1096
|
+
},
|
|
1097
|
+
],
|
|
1098
|
+
},
|
|
1099
|
+
"database/mongodb": {
|
|
1100
|
+
title: "MongoDB",
|
|
1101
|
+
updated: "December 18, 2025",
|
|
1102
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1103
|
+
sections: [{ id: "overview", heading: "Overview", body: "Use MongoDB with Mongoose schemas and plugins." }],
|
|
1104
|
+
},
|
|
1105
|
+
"database/schema-migrations": {
|
|
1106
|
+
title: "Schema & migrations",
|
|
1107
|
+
updated: "December 18, 2025",
|
|
1108
|
+
toc: [{ id: "overview", title: "Overview" }, { id: "drizzle", title: "Drizzle migrations" }],
|
|
1109
|
+
sections: [
|
|
1110
|
+
{ id: "overview", heading: "Overview", body: "Keep schema changes tracked and deployable." },
|
|
1111
|
+
{ id: "drizzle", heading: "Drizzle migrations", body: "Use drizzle-kit generate/push to keep Postgres schema in sync." },
|
|
1112
|
+
],
|
|
1113
|
+
},
|
|
1114
|
+
|
|
1115
|
+
"seo/blog": {
|
|
1116
|
+
title: "Blog starter",
|
|
1117
|
+
updated: "December 18, 2025",
|
|
1118
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1119
|
+
sections: [{ id: "overview", heading: "Overview", body: "A blog structure to publish SEO-friendly content quickly." }],
|
|
1120
|
+
},
|
|
1121
|
+
"seo/meta-tags": {
|
|
1122
|
+
title: "Meta tags",
|
|
1123
|
+
updated: "December 18, 2025",
|
|
1124
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1125
|
+
sections: [{ id: "overview", heading: "Overview", body: "Title/description defaults and page overrides." }],
|
|
1126
|
+
},
|
|
1127
|
+
"seo/opengraph": {
|
|
1128
|
+
title: "OpenGraph",
|
|
1129
|
+
updated: "December 18, 2025",
|
|
1130
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1131
|
+
sections: [{ id: "overview", heading: "Overview", body: "OpenGraph tags for social previews." }],
|
|
1132
|
+
},
|
|
1133
|
+
"seo/sitemap": {
|
|
1134
|
+
title: "Sitemap",
|
|
1135
|
+
updated: "December 18, 2025",
|
|
1136
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1137
|
+
sections: [{ id: "overview", heading: "Overview", body: "Automated sitemap generation for faster indexing." }],
|
|
1138
|
+
},
|
|
1139
|
+
"seo/structured-data": {
|
|
1140
|
+
title: "Structured data",
|
|
1141
|
+
updated: "December 18, 2025",
|
|
1142
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1143
|
+
sections: [{ id: "overview", heading: "Overview", body: "Schema.org markup for rich snippets." }],
|
|
1144
|
+
},
|
|
1145
|
+
|
|
1146
|
+
"style/tailwind": {
|
|
1147
|
+
title: "Tailwind CSS",
|
|
1148
|
+
updated: "December 18, 2025",
|
|
1149
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1150
|
+
sections: [{ id: "overview", heading: "Overview", body: "Utility-first styling with sensible defaults." }],
|
|
1151
|
+
},
|
|
1152
|
+
"style/daisyui": {
|
|
1153
|
+
title: "daisyUI themes",
|
|
1154
|
+
updated: "December 18, 2025",
|
|
1155
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1156
|
+
sections: [{ id: "overview", heading: "Overview", body: "Theme packs to re-skin your UI quickly." }],
|
|
1157
|
+
},
|
|
1158
|
+
"style/dark-mode": {
|
|
1159
|
+
title: "Automatic dark mode",
|
|
1160
|
+
updated: "December 18, 2025",
|
|
1161
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1162
|
+
sections: [{ id: "overview", heading: "Overview", body: "Toggle + persisted preference out of the box." }],
|
|
1163
|
+
},
|
|
1164
|
+
"style/ui-components": {
|
|
1165
|
+
title: "UI components",
|
|
1166
|
+
updated: "December 18, 2025",
|
|
1167
|
+
toc: [{ id: "overview", title: "Overview" }],
|
|
1168
|
+
sections: [{ id: "overview", heading: "Overview", body: "A starter component library for common flows and sections." }],
|
|
1169
|
+
},
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
export default async function DocsSubPage({
|
|
1173
|
+
params,
|
|
1174
|
+
}: {
|
|
1175
|
+
params: Promise<{ slug: string; subslug: string }>;
|
|
1176
|
+
}) {
|
|
1177
|
+
const { slug, subslug } = await params;
|
|
1178
|
+
const key = `${slug}/${subslug}`;
|
|
1179
|
+
const page = subpages[key];
|
|
1180
|
+
|
|
1181
|
+
if (!page) {
|
|
1182
|
+
return (
|
|
1183
|
+
<div className="py-10">
|
|
1184
|
+
<div className="text-3xl font-semibold tracking-tight">Not found</div>
|
|
1185
|
+
<p className="mt-3 text-white/70">This docs page doesn’t exist yet.</p>
|
|
1186
|
+
</div>
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
return (
|
|
1191
|
+
<div className="grid grid-cols-1 xl:grid-cols-[minmax(0,1fr)_240px] gap-10">
|
|
1192
|
+
<article className="py-10">
|
|
1193
|
+
<div className="text-[44px] leading-[1.1] font-semibold tracking-tight">{page.title}</div>
|
|
1194
|
+
<div className="mt-4 text-sm text-white/60">Last updated {page.updated}</div>
|
|
1195
|
+
|
|
1196
|
+
<div className="mt-10 border-t border-white/10 pt-10 space-y-10">
|
|
1197
|
+
{page.sections.map((s) => (
|
|
1198
|
+
<section key={s.id} id={s.id} className="space-y-4">
|
|
1199
|
+
<h2 className="text-2xl font-semibold tracking-tight">{s.heading}</h2>
|
|
1200
|
+
<div className="text-white/70 leading-relaxed prose prose-invert prose-p:text-white/70 prose-headings:text-white prose-strong:text-white prose-code:text-orange-500 prose-code:bg-white/10 prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-pre:bg-[#1a1a1a] prose-pre:border prose-pre:border-white/10 max-w-none">
|
|
1201
|
+
<ReactMarkdown
|
|
1202
|
+
remarkPlugins={[remarkGfm]}
|
|
1203
|
+
components={{
|
|
1204
|
+
code: ({ className, children, ...props }: { className?: string; children?: React.ReactNode }) => {
|
|
1205
|
+
const match = /language-(\w+)/.exec(className || "");
|
|
1206
|
+
const isBlock = match;
|
|
1207
|
+
return isBlock ? (
|
|
1208
|
+
<pre className="bg-[#1a1a1a] border border-white/10 rounded-lg p-4 overflow-x-auto my-4">
|
|
1209
|
+
<code className={className} {...props}>
|
|
1210
|
+
{children}
|
|
1211
|
+
</code>
|
|
1212
|
+
</pre>
|
|
1213
|
+
) : (
|
|
1214
|
+
<code className="text-orange-500 bg-white/10 px-1.5 py-0.5 rounded" {...props}>
|
|
1215
|
+
{children}
|
|
1216
|
+
</code>
|
|
1217
|
+
);
|
|
1218
|
+
},
|
|
1219
|
+
a: ({ ...props }) => (
|
|
1220
|
+
<a className="text-orange-500 hover:text-orange-400 underline" {...props} />
|
|
1221
|
+
),
|
|
1222
|
+
ul: ({ ...props }) => <ul className="list-disc list-inside space-y-1 my-4" {...props} />,
|
|
1223
|
+
ol: ({ ...props }) => <ol className="list-decimal list-inside space-y-1 my-4" {...props} />,
|
|
1224
|
+
li: ({ ...props }) => <li className="ml-4" {...props} />,
|
|
1225
|
+
}}
|
|
1226
|
+
>
|
|
1227
|
+
{s.body}
|
|
1228
|
+
</ReactMarkdown>
|
|
1229
|
+
</div>
|
|
1230
|
+
</section>
|
|
1231
|
+
))}
|
|
1232
|
+
</div>
|
|
1233
|
+
</article>
|
|
1234
|
+
|
|
1235
|
+
<aside className="hidden xl:block">
|
|
1236
|
+
<DocsToc items={page.toc} />
|
|
1237
|
+
</aside>
|
|
1238
|
+
</div>
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
|