codebakers 2.0.10 → 2.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/dist/index.js +1123 -247
- package/package.json +1 -1
- package/src/commands/integrate.ts +985 -0
- package/src/index.ts +14 -2
|
@@ -0,0 +1,985 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import * as fs from 'fs-extra';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
import open from 'open';
|
|
7
|
+
import { Config } from '../utils/config.js';
|
|
8
|
+
import { execa } from 'execa';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// INTEGRATION LIBRARY
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
interface Integration {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
category: 'auth' | 'database' | 'payments' | 'email' | 'storage' | 'analytics' | 'ai' | 'cms' | 'messaging' | 'monitoring' | 'deployment' | 'other';
|
|
19
|
+
icon: string;
|
|
20
|
+
authType: 'oauth' | 'apikey' | 'npm' | 'cli';
|
|
21
|
+
oauthUrl?: string;
|
|
22
|
+
scopes?: string[];
|
|
23
|
+
envVars: string[];
|
|
24
|
+
packages?: string[];
|
|
25
|
+
setupFiles?: Array<{ path: string; template: string }>;
|
|
26
|
+
docs: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const INTEGRATIONS: Integration[] = [
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
31
|
+
// AUTH PROVIDERS
|
|
32
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
33
|
+
{
|
|
34
|
+
id: 'clerk',
|
|
35
|
+
name: 'Clerk',
|
|
36
|
+
description: 'Complete user management & authentication',
|
|
37
|
+
category: 'auth',
|
|
38
|
+
icon: '🔐',
|
|
39
|
+
authType: 'oauth',
|
|
40
|
+
oauthUrl: 'https://dashboard.clerk.com/apps/new',
|
|
41
|
+
envVars: ['NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY', 'CLERK_SECRET_KEY'],
|
|
42
|
+
packages: ['@clerk/nextjs'],
|
|
43
|
+
docs: 'https://clerk.com/docs',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'auth0',
|
|
47
|
+
name: 'Auth0',
|
|
48
|
+
description: 'Enterprise authentication platform',
|
|
49
|
+
category: 'auth',
|
|
50
|
+
icon: '🔒',
|
|
51
|
+
authType: 'oauth',
|
|
52
|
+
oauthUrl: 'https://manage.auth0.com/dashboard',
|
|
53
|
+
envVars: ['AUTH0_SECRET', 'AUTH0_BASE_URL', 'AUTH0_ISSUER_BASE_URL', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET'],
|
|
54
|
+
packages: ['@auth0/nextjs-auth0'],
|
|
55
|
+
docs: 'https://auth0.com/docs',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'nextauth',
|
|
59
|
+
name: 'NextAuth.js',
|
|
60
|
+
description: 'Open source authentication for Next.js',
|
|
61
|
+
category: 'auth',
|
|
62
|
+
icon: '🔑',
|
|
63
|
+
authType: 'npm',
|
|
64
|
+
envVars: ['NEXTAUTH_SECRET', 'NEXTAUTH_URL'],
|
|
65
|
+
packages: ['next-auth'],
|
|
66
|
+
docs: 'https://next-auth.js.org',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'supabase-auth',
|
|
70
|
+
name: 'Supabase Auth',
|
|
71
|
+
description: 'Authentication with Supabase',
|
|
72
|
+
category: 'auth',
|
|
73
|
+
icon: '⚡',
|
|
74
|
+
authType: 'oauth',
|
|
75
|
+
oauthUrl: 'https://supabase.com/dashboard/projects',
|
|
76
|
+
envVars: ['NEXT_PUBLIC_SUPABASE_URL', 'NEXT_PUBLIC_SUPABASE_ANON_KEY'],
|
|
77
|
+
packages: ['@supabase/supabase-js', '@supabase/auth-helpers-nextjs'],
|
|
78
|
+
docs: 'https://supabase.com/docs/guides/auth',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'firebase-auth',
|
|
82
|
+
name: 'Firebase Auth',
|
|
83
|
+
description: 'Google Firebase authentication',
|
|
84
|
+
category: 'auth',
|
|
85
|
+
icon: '🔥',
|
|
86
|
+
authType: 'oauth',
|
|
87
|
+
oauthUrl: 'https://console.firebase.google.com',
|
|
88
|
+
envVars: ['NEXT_PUBLIC_FIREBASE_API_KEY', 'NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN', 'NEXT_PUBLIC_FIREBASE_PROJECT_ID'],
|
|
89
|
+
packages: ['firebase'],
|
|
90
|
+
docs: 'https://firebase.google.com/docs/auth',
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
94
|
+
// DATABASES
|
|
95
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
96
|
+
{
|
|
97
|
+
id: 'supabase',
|
|
98
|
+
name: 'Supabase',
|
|
99
|
+
description: 'Postgres database with realtime & auth',
|
|
100
|
+
category: 'database',
|
|
101
|
+
icon: '⚡',
|
|
102
|
+
authType: 'oauth',
|
|
103
|
+
oauthUrl: 'https://supabase.com/dashboard/projects',
|
|
104
|
+
envVars: ['NEXT_PUBLIC_SUPABASE_URL', 'NEXT_PUBLIC_SUPABASE_ANON_KEY', 'SUPABASE_SERVICE_ROLE_KEY'],
|
|
105
|
+
packages: ['@supabase/supabase-js'],
|
|
106
|
+
docs: 'https://supabase.com/docs',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'planetscale',
|
|
110
|
+
name: 'PlanetScale',
|
|
111
|
+
description: 'Serverless MySQL database',
|
|
112
|
+
category: 'database',
|
|
113
|
+
icon: '🪐',
|
|
114
|
+
authType: 'oauth',
|
|
115
|
+
oauthUrl: 'https://app.planetscale.com',
|
|
116
|
+
envVars: ['DATABASE_URL'],
|
|
117
|
+
packages: ['@planetscale/database'],
|
|
118
|
+
docs: 'https://planetscale.com/docs',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'neon',
|
|
122
|
+
name: 'Neon',
|
|
123
|
+
description: 'Serverless Postgres',
|
|
124
|
+
category: 'database',
|
|
125
|
+
icon: '🐘',
|
|
126
|
+
authType: 'oauth',
|
|
127
|
+
oauthUrl: 'https://console.neon.tech',
|
|
128
|
+
envVars: ['DATABASE_URL'],
|
|
129
|
+
packages: ['@neondatabase/serverless'],
|
|
130
|
+
docs: 'https://neon.tech/docs',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'turso',
|
|
134
|
+
name: 'Turso',
|
|
135
|
+
description: 'Edge SQLite database',
|
|
136
|
+
category: 'database',
|
|
137
|
+
icon: '🐢',
|
|
138
|
+
authType: 'oauth',
|
|
139
|
+
oauthUrl: 'https://turso.tech/app',
|
|
140
|
+
envVars: ['TURSO_DATABASE_URL', 'TURSO_AUTH_TOKEN'],
|
|
141
|
+
packages: ['@libsql/client'],
|
|
142
|
+
docs: 'https://docs.turso.tech',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'mongodb',
|
|
146
|
+
name: 'MongoDB Atlas',
|
|
147
|
+
description: 'NoSQL document database',
|
|
148
|
+
category: 'database',
|
|
149
|
+
icon: '🍃',
|
|
150
|
+
authType: 'oauth',
|
|
151
|
+
oauthUrl: 'https://cloud.mongodb.com',
|
|
152
|
+
envVars: ['MONGODB_URI'],
|
|
153
|
+
packages: ['mongodb'],
|
|
154
|
+
docs: 'https://www.mongodb.com/docs',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: 'prisma',
|
|
158
|
+
name: 'Prisma',
|
|
159
|
+
description: 'Type-safe ORM for any database',
|
|
160
|
+
category: 'database',
|
|
161
|
+
icon: '🔷',
|
|
162
|
+
authType: 'npm',
|
|
163
|
+
envVars: ['DATABASE_URL'],
|
|
164
|
+
packages: ['prisma', '@prisma/client'],
|
|
165
|
+
docs: 'https://www.prisma.io/docs',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'drizzle',
|
|
169
|
+
name: 'Drizzle ORM',
|
|
170
|
+
description: 'Lightweight TypeScript ORM',
|
|
171
|
+
category: 'database',
|
|
172
|
+
icon: '💧',
|
|
173
|
+
authType: 'npm',
|
|
174
|
+
envVars: ['DATABASE_URL'],
|
|
175
|
+
packages: ['drizzle-orm', 'drizzle-kit'],
|
|
176
|
+
docs: 'https://orm.drizzle.team',
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
180
|
+
// PAYMENTS
|
|
181
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
182
|
+
{
|
|
183
|
+
id: 'stripe',
|
|
184
|
+
name: 'Stripe',
|
|
185
|
+
description: 'Payment processing & subscriptions',
|
|
186
|
+
category: 'payments',
|
|
187
|
+
icon: '💳',
|
|
188
|
+
authType: 'oauth',
|
|
189
|
+
oauthUrl: 'https://dashboard.stripe.com/apikeys',
|
|
190
|
+
envVars: ['STRIPE_SECRET_KEY', 'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY', 'STRIPE_WEBHOOK_SECRET'],
|
|
191
|
+
packages: ['stripe', '@stripe/stripe-js'],
|
|
192
|
+
docs: 'https://stripe.com/docs',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: 'lemonsqueezy',
|
|
196
|
+
name: 'Lemon Squeezy',
|
|
197
|
+
description: 'Merchant of record for SaaS',
|
|
198
|
+
category: 'payments',
|
|
199
|
+
icon: '🍋',
|
|
200
|
+
authType: 'oauth',
|
|
201
|
+
oauthUrl: 'https://app.lemonsqueezy.com/settings/api',
|
|
202
|
+
envVars: ['LEMONSQUEEZY_API_KEY', 'LEMONSQUEEZY_STORE_ID', 'LEMONSQUEEZY_WEBHOOK_SECRET'],
|
|
203
|
+
packages: ['@lemonsqueezy/lemonsqueezy.js'],
|
|
204
|
+
docs: 'https://docs.lemonsqueezy.com',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: 'paddle',
|
|
208
|
+
name: 'Paddle',
|
|
209
|
+
description: 'Payment infrastructure for SaaS',
|
|
210
|
+
category: 'payments',
|
|
211
|
+
icon: '🏓',
|
|
212
|
+
authType: 'oauth',
|
|
213
|
+
oauthUrl: 'https://vendors.paddle.com/authentication',
|
|
214
|
+
envVars: ['PADDLE_VENDOR_ID', 'PADDLE_API_KEY', 'PADDLE_PUBLIC_KEY'],
|
|
215
|
+
packages: ['@paddle/paddle-js'],
|
|
216
|
+
docs: 'https://developer.paddle.com',
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
220
|
+
// EMAIL
|
|
221
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
222
|
+
{
|
|
223
|
+
id: 'resend',
|
|
224
|
+
name: 'Resend',
|
|
225
|
+
description: 'Modern email API for developers',
|
|
226
|
+
category: 'email',
|
|
227
|
+
icon: '📧',
|
|
228
|
+
authType: 'oauth',
|
|
229
|
+
oauthUrl: 'https://resend.com/api-keys',
|
|
230
|
+
envVars: ['RESEND_API_KEY'],
|
|
231
|
+
packages: ['resend'],
|
|
232
|
+
docs: 'https://resend.com/docs',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: 'sendgrid',
|
|
236
|
+
name: 'SendGrid',
|
|
237
|
+
description: 'Email delivery service',
|
|
238
|
+
category: 'email',
|
|
239
|
+
icon: '📨',
|
|
240
|
+
authType: 'oauth',
|
|
241
|
+
oauthUrl: 'https://app.sendgrid.com/settings/api_keys',
|
|
242
|
+
envVars: ['SENDGRID_API_KEY'],
|
|
243
|
+
packages: ['@sendgrid/mail'],
|
|
244
|
+
docs: 'https://docs.sendgrid.com',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: 'postmark',
|
|
248
|
+
name: 'Postmark',
|
|
249
|
+
description: 'Transactional email service',
|
|
250
|
+
category: 'email',
|
|
251
|
+
icon: '📬',
|
|
252
|
+
authType: 'oauth',
|
|
253
|
+
oauthUrl: 'https://account.postmarkapp.com/servers',
|
|
254
|
+
envVars: ['POSTMARK_API_KEY'],
|
|
255
|
+
packages: ['postmark'],
|
|
256
|
+
docs: 'https://postmarkapp.com/developer',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'mailgun',
|
|
260
|
+
name: 'Mailgun',
|
|
261
|
+
description: 'Email API service',
|
|
262
|
+
category: 'email',
|
|
263
|
+
icon: '📮',
|
|
264
|
+
authType: 'oauth',
|
|
265
|
+
oauthUrl: 'https://app.mailgun.com/app/account/security/api_keys',
|
|
266
|
+
envVars: ['MAILGUN_API_KEY', 'MAILGUN_DOMAIN'],
|
|
267
|
+
packages: ['mailgun.js'],
|
|
268
|
+
docs: 'https://documentation.mailgun.com',
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: 'react-email',
|
|
272
|
+
name: 'React Email',
|
|
273
|
+
description: 'Build emails with React components',
|
|
274
|
+
category: 'email',
|
|
275
|
+
icon: '⚛️',
|
|
276
|
+
authType: 'npm',
|
|
277
|
+
envVars: [],
|
|
278
|
+
packages: ['react-email', '@react-email/components'],
|
|
279
|
+
docs: 'https://react.email/docs',
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
283
|
+
// STORAGE
|
|
284
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
285
|
+
{
|
|
286
|
+
id: 'uploadthing',
|
|
287
|
+
name: 'UploadThing',
|
|
288
|
+
description: 'File uploads for Next.js',
|
|
289
|
+
category: 'storage',
|
|
290
|
+
icon: '📤',
|
|
291
|
+
authType: 'oauth',
|
|
292
|
+
oauthUrl: 'https://uploadthing.com/dashboard',
|
|
293
|
+
envVars: ['UPLOADTHING_SECRET', 'UPLOADTHING_APP_ID'],
|
|
294
|
+
packages: ['uploadthing', '@uploadthing/react'],
|
|
295
|
+
docs: 'https://docs.uploadthing.com',
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: 'cloudinary',
|
|
299
|
+
name: 'Cloudinary',
|
|
300
|
+
description: 'Image & video management',
|
|
301
|
+
category: 'storage',
|
|
302
|
+
icon: '☁️',
|
|
303
|
+
authType: 'oauth',
|
|
304
|
+
oauthUrl: 'https://console.cloudinary.com/settings/api-keys',
|
|
305
|
+
envVars: ['CLOUDINARY_CLOUD_NAME', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET'],
|
|
306
|
+
packages: ['cloudinary'],
|
|
307
|
+
docs: 'https://cloudinary.com/documentation',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: 'aws-s3',
|
|
311
|
+
name: 'AWS S3',
|
|
312
|
+
description: 'Amazon cloud storage',
|
|
313
|
+
category: 'storage',
|
|
314
|
+
icon: '🪣',
|
|
315
|
+
authType: 'oauth',
|
|
316
|
+
oauthUrl: 'https://console.aws.amazon.com/iam/home#/security_credentials',
|
|
317
|
+
envVars: ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION', 'AWS_S3_BUCKET'],
|
|
318
|
+
packages: ['@aws-sdk/client-s3'],
|
|
319
|
+
docs: 'https://docs.aws.amazon.com/s3',
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: 'vercel-blob',
|
|
323
|
+
name: 'Vercel Blob',
|
|
324
|
+
description: 'File storage by Vercel',
|
|
325
|
+
category: 'storage',
|
|
326
|
+
icon: '▲',
|
|
327
|
+
authType: 'oauth',
|
|
328
|
+
oauthUrl: 'https://vercel.com/dashboard/stores',
|
|
329
|
+
envVars: ['BLOB_READ_WRITE_TOKEN'],
|
|
330
|
+
packages: ['@vercel/blob'],
|
|
331
|
+
docs: 'https://vercel.com/docs/storage/vercel-blob',
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
335
|
+
// ANALYTICS
|
|
336
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
337
|
+
{
|
|
338
|
+
id: 'vercel-analytics',
|
|
339
|
+
name: 'Vercel Analytics',
|
|
340
|
+
description: 'Web analytics by Vercel',
|
|
341
|
+
category: 'analytics',
|
|
342
|
+
icon: '📊',
|
|
343
|
+
authType: 'npm',
|
|
344
|
+
envVars: [],
|
|
345
|
+
packages: ['@vercel/analytics'],
|
|
346
|
+
docs: 'https://vercel.com/docs/analytics',
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: 'posthog',
|
|
350
|
+
name: 'PostHog',
|
|
351
|
+
description: 'Product analytics & feature flags',
|
|
352
|
+
category: 'analytics',
|
|
353
|
+
icon: '🦔',
|
|
354
|
+
authType: 'oauth',
|
|
355
|
+
oauthUrl: 'https://app.posthog.com/project/settings',
|
|
356
|
+
envVars: ['NEXT_PUBLIC_POSTHOG_KEY', 'NEXT_PUBLIC_POSTHOG_HOST'],
|
|
357
|
+
packages: ['posthog-js'],
|
|
358
|
+
docs: 'https://posthog.com/docs',
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
id: 'mixpanel',
|
|
362
|
+
name: 'Mixpanel',
|
|
363
|
+
description: 'Event-based analytics',
|
|
364
|
+
category: 'analytics',
|
|
365
|
+
icon: '📈',
|
|
366
|
+
authType: 'oauth',
|
|
367
|
+
oauthUrl: 'https://mixpanel.com/settings/project',
|
|
368
|
+
envVars: ['NEXT_PUBLIC_MIXPANEL_TOKEN'],
|
|
369
|
+
packages: ['mixpanel-browser'],
|
|
370
|
+
docs: 'https://docs.mixpanel.com',
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: 'plausible',
|
|
374
|
+
name: 'Plausible',
|
|
375
|
+
description: 'Privacy-friendly analytics',
|
|
376
|
+
category: 'analytics',
|
|
377
|
+
icon: '📉',
|
|
378
|
+
authType: 'oauth',
|
|
379
|
+
oauthUrl: 'https://plausible.io/sites',
|
|
380
|
+
envVars: ['NEXT_PUBLIC_PLAUSIBLE_DOMAIN'],
|
|
381
|
+
packages: ['next-plausible'],
|
|
382
|
+
docs: 'https://plausible.io/docs',
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
386
|
+
// AI
|
|
387
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
388
|
+
{
|
|
389
|
+
id: 'openai',
|
|
390
|
+
name: 'OpenAI',
|
|
391
|
+
description: 'GPT models & DALL-E',
|
|
392
|
+
category: 'ai',
|
|
393
|
+
icon: '🤖',
|
|
394
|
+
authType: 'oauth',
|
|
395
|
+
oauthUrl: 'https://platform.openai.com/api-keys',
|
|
396
|
+
envVars: ['OPENAI_API_KEY'],
|
|
397
|
+
packages: ['openai'],
|
|
398
|
+
docs: 'https://platform.openai.com/docs',
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
id: 'anthropic',
|
|
402
|
+
name: 'Anthropic',
|
|
403
|
+
description: 'Claude AI models',
|
|
404
|
+
category: 'ai',
|
|
405
|
+
icon: '🧠',
|
|
406
|
+
authType: 'oauth',
|
|
407
|
+
oauthUrl: 'https://console.anthropic.com/settings/keys',
|
|
408
|
+
envVars: ['ANTHROPIC_API_KEY'],
|
|
409
|
+
packages: ['@anthropic-ai/sdk'],
|
|
410
|
+
docs: 'https://docs.anthropic.com',
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
id: 'replicate',
|
|
414
|
+
name: 'Replicate',
|
|
415
|
+
description: 'Run ML models in the cloud',
|
|
416
|
+
category: 'ai',
|
|
417
|
+
icon: '🔄',
|
|
418
|
+
authType: 'oauth',
|
|
419
|
+
oauthUrl: 'https://replicate.com/account/api-tokens',
|
|
420
|
+
envVars: ['REPLICATE_API_TOKEN'],
|
|
421
|
+
packages: ['replicate'],
|
|
422
|
+
docs: 'https://replicate.com/docs',
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
id: 'vercel-ai',
|
|
426
|
+
name: 'Vercel AI SDK',
|
|
427
|
+
description: 'Build AI-powered apps',
|
|
428
|
+
category: 'ai',
|
|
429
|
+
icon: '✨',
|
|
430
|
+
authType: 'npm',
|
|
431
|
+
envVars: [],
|
|
432
|
+
packages: ['ai'],
|
|
433
|
+
docs: 'https://sdk.vercel.ai/docs',
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
id: 'elevenlabs',
|
|
437
|
+
name: 'ElevenLabs',
|
|
438
|
+
description: 'AI voice generation',
|
|
439
|
+
category: 'ai',
|
|
440
|
+
icon: '🎙️',
|
|
441
|
+
authType: 'oauth',
|
|
442
|
+
oauthUrl: 'https://elevenlabs.io/app/settings/api-keys',
|
|
443
|
+
envVars: ['ELEVENLABS_API_KEY'],
|
|
444
|
+
packages: ['elevenlabs'],
|
|
445
|
+
docs: 'https://elevenlabs.io/docs',
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
449
|
+
// CMS
|
|
450
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
451
|
+
{
|
|
452
|
+
id: 'sanity',
|
|
453
|
+
name: 'Sanity',
|
|
454
|
+
description: 'Headless CMS with real-time collaboration',
|
|
455
|
+
category: 'cms',
|
|
456
|
+
icon: '📝',
|
|
457
|
+
authType: 'oauth',
|
|
458
|
+
oauthUrl: 'https://www.sanity.io/manage',
|
|
459
|
+
envVars: ['NEXT_PUBLIC_SANITY_PROJECT_ID', 'NEXT_PUBLIC_SANITY_DATASET', 'SANITY_API_TOKEN'],
|
|
460
|
+
packages: ['@sanity/client', 'next-sanity'],
|
|
461
|
+
docs: 'https://www.sanity.io/docs',
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
id: 'contentful',
|
|
465
|
+
name: 'Contentful',
|
|
466
|
+
description: 'Enterprise headless CMS',
|
|
467
|
+
category: 'cms',
|
|
468
|
+
icon: '📄',
|
|
469
|
+
authType: 'oauth',
|
|
470
|
+
oauthUrl: 'https://app.contentful.com/account/profile/cma_tokens',
|
|
471
|
+
envVars: ['CONTENTFUL_SPACE_ID', 'CONTENTFUL_ACCESS_TOKEN'],
|
|
472
|
+
packages: ['contentful'],
|
|
473
|
+
docs: 'https://www.contentful.com/developers/docs',
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: 'strapi',
|
|
477
|
+
name: 'Strapi',
|
|
478
|
+
description: 'Open-source headless CMS',
|
|
479
|
+
category: 'cms',
|
|
480
|
+
icon: '🚀',
|
|
481
|
+
authType: 'npm',
|
|
482
|
+
envVars: ['STRAPI_URL', 'STRAPI_API_TOKEN'],
|
|
483
|
+
packages: [],
|
|
484
|
+
docs: 'https://docs.strapi.io',
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
488
|
+
// MESSAGING
|
|
489
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
490
|
+
{
|
|
491
|
+
id: 'twilio',
|
|
492
|
+
name: 'Twilio',
|
|
493
|
+
description: 'SMS, voice & WhatsApp',
|
|
494
|
+
category: 'messaging',
|
|
495
|
+
icon: '📱',
|
|
496
|
+
authType: 'oauth',
|
|
497
|
+
oauthUrl: 'https://console.twilio.com/us1/account/keys-credentials/api-keys',
|
|
498
|
+
envVars: ['TWILIO_ACCOUNT_SID', 'TWILIO_AUTH_TOKEN', 'TWILIO_PHONE_NUMBER'],
|
|
499
|
+
packages: ['twilio'],
|
|
500
|
+
docs: 'https://www.twilio.com/docs',
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
id: 'pusher',
|
|
504
|
+
name: 'Pusher',
|
|
505
|
+
description: 'Realtime websockets',
|
|
506
|
+
category: 'messaging',
|
|
507
|
+
icon: '🔔',
|
|
508
|
+
authType: 'oauth',
|
|
509
|
+
oauthUrl: 'https://dashboard.pusher.com',
|
|
510
|
+
envVars: ['PUSHER_APP_ID', 'PUSHER_KEY', 'PUSHER_SECRET', 'NEXT_PUBLIC_PUSHER_KEY'],
|
|
511
|
+
packages: ['pusher', 'pusher-js'],
|
|
512
|
+
docs: 'https://pusher.com/docs',
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
id: 'knock',
|
|
516
|
+
name: 'Knock',
|
|
517
|
+
description: 'Notification infrastructure',
|
|
518
|
+
category: 'messaging',
|
|
519
|
+
icon: '🔔',
|
|
520
|
+
authType: 'oauth',
|
|
521
|
+
oauthUrl: 'https://dashboard.knock.app',
|
|
522
|
+
envVars: ['KNOCK_API_KEY', 'NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY'],
|
|
523
|
+
packages: ['@knocklabs/node', '@knocklabs/react'],
|
|
524
|
+
docs: 'https://docs.knock.app',
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
id: 'stream',
|
|
528
|
+
name: 'Stream',
|
|
529
|
+
description: 'Chat & activity feeds',
|
|
530
|
+
category: 'messaging',
|
|
531
|
+
icon: '💬',
|
|
532
|
+
authType: 'oauth',
|
|
533
|
+
oauthUrl: 'https://dashboard.getstream.io',
|
|
534
|
+
envVars: ['STREAM_API_KEY', 'STREAM_API_SECRET'],
|
|
535
|
+
packages: ['stream-chat', 'stream-chat-react'],
|
|
536
|
+
docs: 'https://getstream.io/docs',
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
540
|
+
// MONITORING
|
|
541
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
542
|
+
{
|
|
543
|
+
id: 'sentry',
|
|
544
|
+
name: 'Sentry',
|
|
545
|
+
description: 'Error tracking & performance',
|
|
546
|
+
category: 'monitoring',
|
|
547
|
+
icon: '🐛',
|
|
548
|
+
authType: 'oauth',
|
|
549
|
+
oauthUrl: 'https://sentry.io/settings/account/api/auth-tokens/',
|
|
550
|
+
envVars: ['SENTRY_DSN', 'SENTRY_AUTH_TOKEN'],
|
|
551
|
+
packages: ['@sentry/nextjs'],
|
|
552
|
+
docs: 'https://docs.sentry.io',
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
id: 'logrocket',
|
|
556
|
+
name: 'LogRocket',
|
|
557
|
+
description: 'Session replay & monitoring',
|
|
558
|
+
category: 'monitoring',
|
|
559
|
+
icon: '🚀',
|
|
560
|
+
authType: 'oauth',
|
|
561
|
+
oauthUrl: 'https://app.logrocket.com/settings/setup',
|
|
562
|
+
envVars: ['NEXT_PUBLIC_LOGROCKET_APP_ID'],
|
|
563
|
+
packages: ['logrocket'],
|
|
564
|
+
docs: 'https://docs.logrocket.com',
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
568
|
+
// DEPLOYMENT
|
|
569
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
570
|
+
{
|
|
571
|
+
id: 'vercel',
|
|
572
|
+
name: 'Vercel',
|
|
573
|
+
description: 'Deploy Next.js apps',
|
|
574
|
+
category: 'deployment',
|
|
575
|
+
icon: '▲',
|
|
576
|
+
authType: 'oauth',
|
|
577
|
+
oauthUrl: 'https://vercel.com/account/tokens',
|
|
578
|
+
envVars: ['VERCEL_TOKEN'],
|
|
579
|
+
packages: ['vercel'],
|
|
580
|
+
docs: 'https://vercel.com/docs',
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
id: 'github',
|
|
584
|
+
name: 'GitHub',
|
|
585
|
+
description: 'Code hosting & CI/CD',
|
|
586
|
+
category: 'deployment',
|
|
587
|
+
icon: '🐙',
|
|
588
|
+
authType: 'oauth',
|
|
589
|
+
oauthUrl: 'https://github.com/settings/tokens',
|
|
590
|
+
envVars: ['GITHUB_TOKEN'],
|
|
591
|
+
packages: ['octokit'],
|
|
592
|
+
docs: 'https://docs.github.com',
|
|
593
|
+
},
|
|
594
|
+
|
|
595
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
596
|
+
// OTHER
|
|
597
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
598
|
+
{
|
|
599
|
+
id: 'crisp',
|
|
600
|
+
name: 'Crisp',
|
|
601
|
+
description: 'Customer support chat',
|
|
602
|
+
category: 'other',
|
|
603
|
+
icon: '💬',
|
|
604
|
+
authType: 'oauth',
|
|
605
|
+
oauthUrl: 'https://app.crisp.chat/settings/website',
|
|
606
|
+
envVars: ['NEXT_PUBLIC_CRISP_WEBSITE_ID'],
|
|
607
|
+
packages: ['crisp-sdk-web'],
|
|
608
|
+
docs: 'https://docs.crisp.chat',
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
id: 'intercom',
|
|
612
|
+
name: 'Intercom',
|
|
613
|
+
description: 'Customer messaging platform',
|
|
614
|
+
category: 'other',
|
|
615
|
+
icon: '💬',
|
|
616
|
+
authType: 'oauth',
|
|
617
|
+
oauthUrl: 'https://app.intercom.com/a/apps/_/developer-hub',
|
|
618
|
+
envVars: ['NEXT_PUBLIC_INTERCOM_APP_ID'],
|
|
619
|
+
packages: ['@intercom/messenger-js-sdk'],
|
|
620
|
+
docs: 'https://developers.intercom.com',
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
id: 'cal',
|
|
624
|
+
name: 'Cal.com',
|
|
625
|
+
description: 'Scheduling infrastructure',
|
|
626
|
+
category: 'other',
|
|
627
|
+
icon: '📅',
|
|
628
|
+
authType: 'oauth',
|
|
629
|
+
oauthUrl: 'https://app.cal.com/settings/developer/api-keys',
|
|
630
|
+
envVars: ['CAL_API_KEY'],
|
|
631
|
+
packages: ['@calcom/embed-react'],
|
|
632
|
+
docs: 'https://cal.com/docs',
|
|
633
|
+
},
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
// ============================================================================
|
|
637
|
+
// MAIN COMMAND
|
|
638
|
+
// ============================================================================
|
|
639
|
+
|
|
640
|
+
export async function integrateCommand(integrationId?: string): Promise<void> {
|
|
641
|
+
const config = new Config();
|
|
642
|
+
|
|
643
|
+
console.log(chalk.cyan(`
|
|
644
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
645
|
+
║ 🔌 ONE-CLICK INTEGRATIONS ║
|
|
646
|
+
║ ║
|
|
647
|
+
║ ${INTEGRATIONS.length} integrations available ║
|
|
648
|
+
║ Browser-based auth • Auto-install • Ready in seconds ║
|
|
649
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
650
|
+
`));
|
|
651
|
+
|
|
652
|
+
// If integration ID provided, install directly
|
|
653
|
+
if (integrationId) {
|
|
654
|
+
const integration = INTEGRATIONS.find(i => i.id === integrationId);
|
|
655
|
+
if (!integration) {
|
|
656
|
+
p.log.error(`Unknown integration: ${integrationId}`);
|
|
657
|
+
console.log(chalk.dim(`Run 'codebakers integrate' to see all available integrations`));
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
await installIntegration(integration, config);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Show category selection
|
|
665
|
+
const categories = [...new Set(INTEGRATIONS.map(i => i.category))];
|
|
666
|
+
|
|
667
|
+
const category = await p.select({
|
|
668
|
+
message: 'Choose a category:',
|
|
669
|
+
options: [
|
|
670
|
+
{ value: 'all', label: '📋 Show all integrations' },
|
|
671
|
+
{ value: 'search', label: '🔍 Search by name' },
|
|
672
|
+
...categories.map(c => ({
|
|
673
|
+
value: c,
|
|
674
|
+
label: getCategoryLabel(c),
|
|
675
|
+
hint: `${INTEGRATIONS.filter(i => i.category === c).length} integrations`,
|
|
676
|
+
})),
|
|
677
|
+
],
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
if (p.isCancel(category)) return;
|
|
681
|
+
|
|
682
|
+
let filteredIntegrations = INTEGRATIONS;
|
|
683
|
+
|
|
684
|
+
if (category === 'search') {
|
|
685
|
+
const query = await p.text({
|
|
686
|
+
message: 'Search integrations:',
|
|
687
|
+
placeholder: 'stripe, auth, email...',
|
|
688
|
+
});
|
|
689
|
+
if (p.isCancel(query)) return;
|
|
690
|
+
|
|
691
|
+
const q = (query as string).toLowerCase();
|
|
692
|
+
filteredIntegrations = INTEGRATIONS.filter(i =>
|
|
693
|
+
i.name.toLowerCase().includes(q) ||
|
|
694
|
+
i.description.toLowerCase().includes(q) ||
|
|
695
|
+
i.id.toLowerCase().includes(q)
|
|
696
|
+
);
|
|
697
|
+
} else if (category !== 'all') {
|
|
698
|
+
filteredIntegrations = INTEGRATIONS.filter(i => i.category === category);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (filteredIntegrations.length === 0) {
|
|
702
|
+
p.log.warn('No integrations found');
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Show integration selection
|
|
707
|
+
const selected = await p.select({
|
|
708
|
+
message: 'Select an integration to install:',
|
|
709
|
+
options: filteredIntegrations.map(i => ({
|
|
710
|
+
value: i.id,
|
|
711
|
+
label: `${i.icon} ${i.name}`,
|
|
712
|
+
hint: i.description,
|
|
713
|
+
})),
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
if (p.isCancel(selected)) return;
|
|
717
|
+
|
|
718
|
+
const integration = INTEGRATIONS.find(i => i.id === selected)!;
|
|
719
|
+
await installIntegration(integration, config);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// ============================================================================
|
|
723
|
+
// INSTALL INTEGRATION
|
|
724
|
+
// ============================================================================
|
|
725
|
+
|
|
726
|
+
async function installIntegration(integration: Integration, config: Config): Promise<void> {
|
|
727
|
+
console.log(chalk.cyan(`\n Installing ${integration.icon} ${integration.name}...\n`));
|
|
728
|
+
|
|
729
|
+
const steps = [];
|
|
730
|
+
|
|
731
|
+
// Step 1: Install packages
|
|
732
|
+
if (integration.packages && integration.packages.length > 0) {
|
|
733
|
+
steps.push({ name: 'Install packages', done: false });
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Step 2: Get credentials (OAuth or API key)
|
|
737
|
+
if (integration.envVars.length > 0) {
|
|
738
|
+
steps.push({ name: 'Configure credentials', done: false });
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Step 3: Create setup files
|
|
742
|
+
steps.push({ name: 'Setup integration', done: false });
|
|
743
|
+
|
|
744
|
+
// Execute steps
|
|
745
|
+
const spinner = p.spinner();
|
|
746
|
+
|
|
747
|
+
// STEP 1: Install packages
|
|
748
|
+
if (integration.packages && integration.packages.length > 0) {
|
|
749
|
+
spinner.start(`Installing ${integration.packages.join(', ')}...`);
|
|
750
|
+
|
|
751
|
+
try {
|
|
752
|
+
await execa('npm', ['install', ...integration.packages], {
|
|
753
|
+
cwd: process.cwd(),
|
|
754
|
+
reject: false,
|
|
755
|
+
});
|
|
756
|
+
spinner.stop(`✓ Packages installed`);
|
|
757
|
+
} catch {
|
|
758
|
+
spinner.stop(`✓ Packages installed (or already present)`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// STEP 2: Get credentials
|
|
763
|
+
if (integration.envVars.length > 0) {
|
|
764
|
+
if (integration.authType === 'oauth' && integration.oauthUrl) {
|
|
765
|
+
// Open browser for OAuth
|
|
766
|
+
console.log(chalk.cyan(`\n Opening ${integration.name} in your browser...`));
|
|
767
|
+
console.log(chalk.dim(` Get your API keys and paste them below.\n`));
|
|
768
|
+
|
|
769
|
+
// Open the OAuth URL
|
|
770
|
+
await open(integration.oauthUrl);
|
|
771
|
+
|
|
772
|
+
// Wait a moment for browser to open
|
|
773
|
+
await sleep(1500);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Collect credentials
|
|
777
|
+
const credentials: Record<string, string> = {};
|
|
778
|
+
|
|
779
|
+
for (const envVar of integration.envVars) {
|
|
780
|
+
const isPublic = envVar.startsWith('NEXT_PUBLIC_');
|
|
781
|
+
const hint = isPublic ? '(public, will be in client bundle)' : '(secret, server-only)';
|
|
782
|
+
|
|
783
|
+
const value = await p.text({
|
|
784
|
+
message: `${envVar} ${chalk.dim(hint)}:`,
|
|
785
|
+
placeholder: 'Paste your key here...',
|
|
786
|
+
validate: (v) => !v ? 'Required' : undefined,
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
if (p.isCancel(value)) return;
|
|
790
|
+
credentials[envVar] = value as string;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Save to .env.local
|
|
794
|
+
await saveEnvVars(credentials);
|
|
795
|
+
console.log(chalk.green(` ✓ Credentials saved to .env.local\n`));
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// STEP 3: Generate setup code
|
|
799
|
+
spinner.start('Generating setup code...');
|
|
800
|
+
|
|
801
|
+
const setupCode = await generateSetupCode(integration);
|
|
802
|
+
|
|
803
|
+
if (setupCode) {
|
|
804
|
+
for (const file of setupCode) {
|
|
805
|
+
await fs.ensureDir(path.dirname(file.path));
|
|
806
|
+
await fs.writeFile(file.path, file.content);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
spinner.stop('✓ Setup complete');
|
|
811
|
+
|
|
812
|
+
// Success message
|
|
813
|
+
console.log(chalk.green(`
|
|
814
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
815
|
+
║ ✓ ${integration.name} installed successfully!
|
|
816
|
+
╠═══════════════════════════════════════════════════════════════╣
|
|
817
|
+
`));
|
|
818
|
+
|
|
819
|
+
if (integration.packages && integration.packages.length > 0) {
|
|
820
|
+
console.log(chalk.dim(` Packages: ${integration.packages.join(', ')}`));
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (integration.envVars.length > 0) {
|
|
824
|
+
console.log(chalk.dim(` Env vars: ${integration.envVars.join(', ')}`));
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
console.log(`
|
|
828
|
+
║ 📚 Docs: ${integration.docs}
|
|
829
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
830
|
+
`);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// ============================================================================
|
|
834
|
+
// HELPERS
|
|
835
|
+
// ============================================================================
|
|
836
|
+
|
|
837
|
+
function getCategoryLabel(category: string): string {
|
|
838
|
+
const labels: Record<string, string> = {
|
|
839
|
+
auth: '🔐 Authentication',
|
|
840
|
+
database: '🗄️ Databases',
|
|
841
|
+
payments: '💳 Payments',
|
|
842
|
+
email: '📧 Email',
|
|
843
|
+
storage: '📦 Storage',
|
|
844
|
+
analytics: '📊 Analytics',
|
|
845
|
+
ai: '🤖 AI & ML',
|
|
846
|
+
cms: '📝 CMS',
|
|
847
|
+
messaging: '💬 Messaging',
|
|
848
|
+
monitoring: '🔍 Monitoring',
|
|
849
|
+
deployment: '🚀 Deployment',
|
|
850
|
+
other: '🔧 Other',
|
|
851
|
+
};
|
|
852
|
+
return labels[category] || category;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async function saveEnvVars(vars: Record<string, string>): Promise<void> {
|
|
856
|
+
const envPath = path.join(process.cwd(), '.env.local');
|
|
857
|
+
let content = '';
|
|
858
|
+
|
|
859
|
+
// Read existing content
|
|
860
|
+
if (await fs.pathExists(envPath)) {
|
|
861
|
+
content = await fs.readFile(envPath, 'utf-8');
|
|
862
|
+
if (!content.endsWith('\n')) {
|
|
863
|
+
content += '\n';
|
|
864
|
+
}
|
|
865
|
+
content += '\n# Added by CodeBakers\n';
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// Add new vars
|
|
869
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
870
|
+
// Check if var already exists
|
|
871
|
+
const regex = new RegExp(`^${key}=`, 'm');
|
|
872
|
+
if (regex.test(content)) {
|
|
873
|
+
// Update existing
|
|
874
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
875
|
+
} else {
|
|
876
|
+
// Add new
|
|
877
|
+
content += `${key}=${value}\n`;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
await fs.writeFile(envPath, content);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
async function generateSetupCode(integration: Integration): Promise<Array<{ path: string; content: string }> | null> {
|
|
885
|
+
// Generate basic setup files based on integration type
|
|
886
|
+
const files: Array<{ path: string; content: string }> = [];
|
|
887
|
+
|
|
888
|
+
switch (integration.id) {
|
|
889
|
+
case 'clerk':
|
|
890
|
+
files.push({
|
|
891
|
+
path: 'src/middleware.ts',
|
|
892
|
+
content: `import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
893
|
+
|
|
894
|
+
export default clerkMiddleware();
|
|
895
|
+
|
|
896
|
+
export const config = {
|
|
897
|
+
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
|
|
898
|
+
};
|
|
899
|
+
`,
|
|
900
|
+
});
|
|
901
|
+
break;
|
|
902
|
+
|
|
903
|
+
case 'stripe':
|
|
904
|
+
files.push({
|
|
905
|
+
path: 'src/lib/stripe.ts',
|
|
906
|
+
content: `import Stripe from 'stripe';
|
|
907
|
+
|
|
908
|
+
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
|
909
|
+
apiVersion: '2023-10-16',
|
|
910
|
+
typescript: true,
|
|
911
|
+
});
|
|
912
|
+
`,
|
|
913
|
+
});
|
|
914
|
+
break;
|
|
915
|
+
|
|
916
|
+
case 'supabase':
|
|
917
|
+
files.push({
|
|
918
|
+
path: 'src/lib/supabase.ts',
|
|
919
|
+
content: `import { createClient } from '@supabase/supabase-js';
|
|
920
|
+
|
|
921
|
+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
|
922
|
+
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
|
923
|
+
|
|
924
|
+
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
|
925
|
+
`,
|
|
926
|
+
});
|
|
927
|
+
break;
|
|
928
|
+
|
|
929
|
+
case 'resend':
|
|
930
|
+
files.push({
|
|
931
|
+
path: 'src/lib/resend.ts',
|
|
932
|
+
content: `import { Resend } from 'resend';
|
|
933
|
+
|
|
934
|
+
export const resend = new Resend(process.env.RESEND_API_KEY);
|
|
935
|
+
`,
|
|
936
|
+
});
|
|
937
|
+
break;
|
|
938
|
+
|
|
939
|
+
case 'openai':
|
|
940
|
+
files.push({
|
|
941
|
+
path: 'src/lib/openai.ts',
|
|
942
|
+
content: `import OpenAI from 'openai';
|
|
943
|
+
|
|
944
|
+
export const openai = new OpenAI({
|
|
945
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
946
|
+
});
|
|
947
|
+
`,
|
|
948
|
+
});
|
|
949
|
+
break;
|
|
950
|
+
|
|
951
|
+
case 'anthropic':
|
|
952
|
+
files.push({
|
|
953
|
+
path: 'src/lib/anthropic.ts',
|
|
954
|
+
content: `import Anthropic from '@anthropic-ai/sdk';
|
|
955
|
+
|
|
956
|
+
export const anthropic = new Anthropic({
|
|
957
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
958
|
+
});
|
|
959
|
+
`,
|
|
960
|
+
});
|
|
961
|
+
break;
|
|
962
|
+
|
|
963
|
+
case 'sentry':
|
|
964
|
+
files.push({
|
|
965
|
+
path: 'sentry.client.config.ts',
|
|
966
|
+
content: `import * as Sentry from '@sentry/nextjs';
|
|
967
|
+
|
|
968
|
+
Sentry.init({
|
|
969
|
+
dsn: process.env.SENTRY_DSN,
|
|
970
|
+
tracesSampleRate: 1.0,
|
|
971
|
+
});
|
|
972
|
+
`,
|
|
973
|
+
});
|
|
974
|
+
break;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return files.length > 0 ? files : null;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function sleep(ms: number): Promise<void> {
|
|
981
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// Export for use elsewhere
|
|
985
|
+
export { INTEGRATIONS, Integration };
|