create-solostack 1.0.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 +262 -0
- package/bin/cli.js +13 -0
- package/package.json +45 -0
- package/src/constants.js +94 -0
- package/src/generators/auth.js +595 -0
- package/src/generators/base.js +592 -0
- package/src/generators/database.js +365 -0
- package/src/generators/emails.js +404 -0
- package/src/generators/payments.js +541 -0
- package/src/generators/ui.js +368 -0
- package/src/index.js +374 -0
- package/src/utils/files.js +81 -0
- package/src/utils/git.js +69 -0
- package/src/utils/logger.js +62 -0
- package/src/utils/packages.js +75 -0
- package/src/utils/validate.js +17 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { writeFile, ensureDir } from '../utils/files.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates Prisma database configuration
|
|
6
|
+
* @param {string} projectPath - Path where the project is located
|
|
7
|
+
* @param {string} database - Database type (currently only PostgreSQL supported)
|
|
8
|
+
*/
|
|
9
|
+
export async function generateDatabase(projectPath, database) {
|
|
10
|
+
// Create prisma directory
|
|
11
|
+
await ensureDir(path.join(projectPath, 'prisma'));
|
|
12
|
+
|
|
13
|
+
// Generate schema.prisma
|
|
14
|
+
const schemaPrisma = `generator client {
|
|
15
|
+
provider = "prisma-client-js"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
datasource db {
|
|
19
|
+
provider = "postgresql"
|
|
20
|
+
url = env("DATABASE_URL")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
model User {
|
|
24
|
+
id String @id @default(cuid())
|
|
25
|
+
email String @unique
|
|
26
|
+
name String?
|
|
27
|
+
emailVerified DateTime?
|
|
28
|
+
image String?
|
|
29
|
+
password String? // For email/password auth
|
|
30
|
+
role Role @default(USER)
|
|
31
|
+
stripeCustomerId String? @unique
|
|
32
|
+
subscription Subscription?
|
|
33
|
+
createdAt DateTime @default(now())
|
|
34
|
+
updatedAt DateTime @updatedAt
|
|
35
|
+
|
|
36
|
+
accounts Account[]
|
|
37
|
+
sessions Session[]
|
|
38
|
+
payments Payment[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
model Account {
|
|
42
|
+
userId String
|
|
43
|
+
type String
|
|
44
|
+
provider String
|
|
45
|
+
providerAccountId String
|
|
46
|
+
refresh_token String? @db.Text
|
|
47
|
+
access_token String? @db.Text
|
|
48
|
+
expires_at Int?
|
|
49
|
+
token_type String?
|
|
50
|
+
scope String?
|
|
51
|
+
id_token String? @db.Text
|
|
52
|
+
session_state String?
|
|
53
|
+
|
|
54
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
55
|
+
|
|
56
|
+
@@unique([provider, providerAccountId])
|
|
57
|
+
@@index([userId])
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
model Session {
|
|
61
|
+
sessionToken String @unique
|
|
62
|
+
userId String
|
|
63
|
+
expires DateTime
|
|
64
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
65
|
+
|
|
66
|
+
@@index([userId])
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
model VerificationToken {
|
|
70
|
+
identifier String
|
|
71
|
+
token String @unique
|
|
72
|
+
expires DateTime
|
|
73
|
+
|
|
74
|
+
@@unique([identifier, token])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
model Subscription {
|
|
78
|
+
id String @id @default(cuid())
|
|
79
|
+
userId String @unique
|
|
80
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
81
|
+
stripeSubscriptionId String @unique
|
|
82
|
+
stripePriceId String
|
|
83
|
+
status SubscriptionStatus
|
|
84
|
+
currentPeriodStart DateTime
|
|
85
|
+
currentPeriodEnd DateTime
|
|
86
|
+
cancelAtPeriodEnd Boolean @default(false)
|
|
87
|
+
createdAt DateTime @default(now())
|
|
88
|
+
updatedAt DateTime @updatedAt
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
model Payment {
|
|
92
|
+
id String @id @default(cuid())
|
|
93
|
+
userId String
|
|
94
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
95
|
+
stripePaymentId String @unique
|
|
96
|
+
amount Int // In cents
|
|
97
|
+
currency String @default("usd")
|
|
98
|
+
status String
|
|
99
|
+
createdAt DateTime @default(now())
|
|
100
|
+
|
|
101
|
+
@@index([userId])
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
model StripeEvent {
|
|
105
|
+
id String @id @default(cuid())
|
|
106
|
+
eventId String @unique // Stripe event ID
|
|
107
|
+
type String // Event type (e.g., "checkout.session.completed")
|
|
108
|
+
processed Boolean @default(false)
|
|
109
|
+
createdAt DateTime @default(now())
|
|
110
|
+
|
|
111
|
+
@@index([eventId])
|
|
112
|
+
@@index([processed])
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
enum Role {
|
|
116
|
+
USER
|
|
117
|
+
ADMIN
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
enum SubscriptionStatus {
|
|
121
|
+
ACTIVE
|
|
122
|
+
CANCELED
|
|
123
|
+
PAST_DUE
|
|
124
|
+
TRIALING
|
|
125
|
+
INCOMPLETE
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
await writeFile(path.join(projectPath, 'prisma/schema.prisma'), schemaPrisma);
|
|
130
|
+
|
|
131
|
+
// Generate database client
|
|
132
|
+
const dbClient = `import { PrismaClient } from '@prisma/client';
|
|
133
|
+
|
|
134
|
+
const globalForPrisma = globalThis as unknown as {
|
|
135
|
+
prisma: PrismaClient | undefined;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const db = globalForPrisma.prisma ?? new PrismaClient();
|
|
139
|
+
|
|
140
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
await writeFile(path.join(projectPath, 'src/lib/db.ts'), dbClient);
|
|
144
|
+
|
|
145
|
+
// Generate seed script
|
|
146
|
+
const seedScript = `import { PrismaClient } from '@prisma/client';
|
|
147
|
+
import bcrypt from 'bcryptjs';
|
|
148
|
+
|
|
149
|
+
const prisma = new PrismaClient();
|
|
150
|
+
|
|
151
|
+
async function main() {
|
|
152
|
+
console.log('🌱 Seeding database...');
|
|
153
|
+
|
|
154
|
+
// Create a test user
|
|
155
|
+
const hashedPassword = await bcrypt.hash('password123', 10);
|
|
156
|
+
|
|
157
|
+
const user = await prisma.user.upsert({
|
|
158
|
+
where: { email: 'test@example.com' },
|
|
159
|
+
update: {},
|
|
160
|
+
create: {
|
|
161
|
+
email: 'test@example.com',
|
|
162
|
+
name: 'Test User',
|
|
163
|
+
password: hashedPassword,
|
|
164
|
+
emailVerified: new Date(),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
console.log('✅ Created test user:', user.email);
|
|
169
|
+
|
|
170
|
+
// Create an admin user
|
|
171
|
+
const admin = await prisma.user.upsert({
|
|
172
|
+
where: { email: 'admin@example.com' },
|
|
173
|
+
update: {},
|
|
174
|
+
create: {
|
|
175
|
+
email: 'admin@example.com',
|
|
176
|
+
name: 'Admin User',
|
|
177
|
+
password: hashedPassword,
|
|
178
|
+
emailVerified: new Date(),
|
|
179
|
+
role: 'ADMIN',
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
console.log('✅ Created admin user:', admin.email);
|
|
184
|
+
|
|
185
|
+
console.log('🎉 Seeding complete!');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
main()
|
|
189
|
+
.catch((e) => {
|
|
190
|
+
console.error('❌ Seeding failed:', e);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
})
|
|
193
|
+
.finally(async () => {
|
|
194
|
+
await prisma.$disconnect();
|
|
195
|
+
});
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
await writeFile(path.join(projectPath, 'prisma/seed.ts'), seedScript);
|
|
199
|
+
|
|
200
|
+
// Generate database migration guide
|
|
201
|
+
const dbGuide = `# Database Setup Guide
|
|
202
|
+
|
|
203
|
+
## Initial Setup
|
|
204
|
+
|
|
205
|
+
### 1. Set up your PostgreSQL database
|
|
206
|
+
|
|
207
|
+
**Option A: Local PostgreSQL**
|
|
208
|
+
\`\`\`bash
|
|
209
|
+
# Install PostgreSQL (if not already installed)
|
|
210
|
+
# macOS
|
|
211
|
+
brew install postgresql
|
|
212
|
+
brew services start postgresql
|
|
213
|
+
|
|
214
|
+
# Ubuntu/Debian
|
|
215
|
+
sudo apt-get install postgresql
|
|
216
|
+
sudo service postgresql start
|
|
217
|
+
|
|
218
|
+
# Windows
|
|
219
|
+
# Download from https://www.postgresql.org/download/windows/
|
|
220
|
+
\`\`\`
|
|
221
|
+
|
|
222
|
+
Create a database:
|
|
223
|
+
\`\`\`bash
|
|
224
|
+
psql postgres
|
|
225
|
+
CREATE DATABASE your_database_name;
|
|
226
|
+
CREATE USER your_user WITH PASSWORD 'your_password';
|
|
227
|
+
GRANT ALL PRIVILEGES ON DATABASE your_database_name TO your_user;
|
|
228
|
+
\\q
|
|
229
|
+
\`\`\`
|
|
230
|
+
|
|
231
|
+
**Option B: Cloud Database (Recommended for Production)**
|
|
232
|
+
- [Neon](https://neon.tech) - Free tier with PostgreSQL
|
|
233
|
+
- [Supabase](https://supabase.com) - Free tier with PostgreSQL
|
|
234
|
+
- [Railway](https://railway.app) - Easy PostgreSQL deployment
|
|
235
|
+
- [Vercel Postgres](https://vercel.com/storage/postgres) - Serverless PostgreSQL
|
|
236
|
+
|
|
237
|
+
### 2. Update your .env file
|
|
238
|
+
|
|
239
|
+
\`\`\`env
|
|
240
|
+
DATABASE_URL="postgresql://user:password@localhost:5432/dbname"
|
|
241
|
+
# Or for cloud:
|
|
242
|
+
# DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require"
|
|
243
|
+
\`\`\`
|
|
244
|
+
|
|
245
|
+
### 3. Push the schema to your database
|
|
246
|
+
|
|
247
|
+
\`\`\`bash
|
|
248
|
+
npm run db:push
|
|
249
|
+
\`\`\`
|
|
250
|
+
|
|
251
|
+
This command:
|
|
252
|
+
- Creates all tables based on your schema
|
|
253
|
+
- Sets up indexes for performance
|
|
254
|
+
- Applies constraints and relationships
|
|
255
|
+
|
|
256
|
+
### 4. Seed the database (optional)
|
|
257
|
+
|
|
258
|
+
\`\`\`bash
|
|
259
|
+
npm run db:seed
|
|
260
|
+
\`\`\`
|
|
261
|
+
|
|
262
|
+
This creates:
|
|
263
|
+
- Test user: test@example.com (password: password123)
|
|
264
|
+
- Admin user: admin@example.com (password: password123)
|
|
265
|
+
|
|
266
|
+
## Making Schema Changes
|
|
267
|
+
|
|
268
|
+
### 1. Edit prisma/schema.prisma
|
|
269
|
+
|
|
270
|
+
Add or modify models as needed.
|
|
271
|
+
|
|
272
|
+
### 2. Apply changes
|
|
273
|
+
|
|
274
|
+
**For development:**
|
|
275
|
+
\`\`\`bash
|
|
276
|
+
npm run db:push
|
|
277
|
+
\`\`\`
|
|
278
|
+
|
|
279
|
+
**For production (with migrations):**
|
|
280
|
+
\`\`\`bash
|
|
281
|
+
npx prisma migrate dev --name your_migration_name
|
|
282
|
+
\`\`\`
|
|
283
|
+
|
|
284
|
+
This creates a migration file in \`prisma/migrations/\`.
|
|
285
|
+
|
|
286
|
+
### 3. Apply migrations to production
|
|
287
|
+
|
|
288
|
+
\`\`\`bash
|
|
289
|
+
npx prisma migrate deploy
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
## Prisma Studio
|
|
293
|
+
|
|
294
|
+
Inspect and edit your database with a visual interface:
|
|
295
|
+
|
|
296
|
+
\`\`\`bash
|
|
297
|
+
npm run db:studio
|
|
298
|
+
\`\`\`
|
|
299
|
+
|
|
300
|
+
Opens at http://localhost:5555
|
|
301
|
+
|
|
302
|
+
## Common Commands
|
|
303
|
+
|
|
304
|
+
\`\`\`bash
|
|
305
|
+
# Generate Prisma Client after schema changes
|
|
306
|
+
npx prisma generate
|
|
307
|
+
|
|
308
|
+
# Format schema file
|
|
309
|
+
npx prisma format
|
|
310
|
+
|
|
311
|
+
# Check if database is in sync with schema
|
|
312
|
+
npx prisma db pull
|
|
313
|
+
|
|
314
|
+
# Reset database (⚠️ deletes all data)
|
|
315
|
+
npx prisma migrate reset
|
|
316
|
+
|
|
317
|
+
# View database structure
|
|
318
|
+
npx prisma db pull
|
|
319
|
+
\`\`\`
|
|
320
|
+
|
|
321
|
+
## Backup & Restore
|
|
322
|
+
|
|
323
|
+
### Backup
|
|
324
|
+
\`\`\`bash
|
|
325
|
+
pg_dump -U your_user -d your_database > backup.sql
|
|
326
|
+
\`\`\`
|
|
327
|
+
|
|
328
|
+
### Restore
|
|
329
|
+
\`\`\`bash
|
|
330
|
+
psql -U your_user -d your_database < backup.sql
|
|
331
|
+
\`\`\`
|
|
332
|
+
|
|
333
|
+
## Troubleshooting
|
|
334
|
+
|
|
335
|
+
### Connection Issues
|
|
336
|
+
- Verify DATABASE_URL is correct
|
|
337
|
+
- Check if PostgreSQL is running
|
|
338
|
+
- Ensure database exists
|
|
339
|
+
- Check firewall settings for remote connections
|
|
340
|
+
|
|
341
|
+
### Migration Conflicts
|
|
342
|
+
\`\`\`bash
|
|
343
|
+
# Reset migrations (development only)
|
|
344
|
+
npx prisma migrate reset
|
|
345
|
+
|
|
346
|
+
# Mark migration as applied without running
|
|
347
|
+
npx prisma migrate resolve --applied migration_name
|
|
348
|
+
\`\`\`
|
|
349
|
+
|
|
350
|
+
### Performance
|
|
351
|
+
- Add indexes for frequently queried fields
|
|
352
|
+
- Use \`@@index\` in your schema
|
|
353
|
+
- Monitor with \`EXPLAIN ANALYZE\` in PostgreSQL
|
|
354
|
+
|
|
355
|
+
## Best Practices
|
|
356
|
+
|
|
357
|
+
1. **Always backup before major migrations**
|
|
358
|
+
2. **Test migrations on staging first**
|
|
359
|
+
3. **Use migrations (not db push) in production**
|
|
360
|
+
4. **Keep DATABASE_URL in .env, never commit it**
|
|
361
|
+
5. **Use connection pooling for serverless (e.g., Prisma Data Proxy)**
|
|
362
|
+
`;
|
|
363
|
+
|
|
364
|
+
await writeFile(path.join(projectPath, 'prisma/DATABASE_GUIDE.md'), dbGuide);
|
|
365
|
+
}
|