create-sonicjs 2.0.0-alpha.7 → 2.0.0-beta.4
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/package.json +1 -1
- package/src/cli.js +187 -6
- package/templates/starter/package.json +3 -1
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import validatePackageName from 'validate-npm-package-name'
|
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
13
13
|
|
|
14
14
|
// Version
|
|
15
|
-
const VERSION = '2.0.0-
|
|
15
|
+
const VERSION = '2.0.0-beta.4'
|
|
16
16
|
|
|
17
17
|
// Templates available
|
|
18
18
|
const TEMPLATES = {
|
|
@@ -135,6 +135,32 @@ async function getProjectDetails(initialName) {
|
|
|
135
135
|
})
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// Admin email
|
|
139
|
+
questions.push({
|
|
140
|
+
type: 'text',
|
|
141
|
+
name: 'adminEmail',
|
|
142
|
+
message: 'Admin email:',
|
|
143
|
+
validate: (value) => {
|
|
144
|
+
if (!value) return 'Admin email is required'
|
|
145
|
+
// Basic email validation
|
|
146
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
147
|
+
if (!emailRegex.test(value)) return 'Please enter a valid email address'
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// Admin password
|
|
153
|
+
questions.push({
|
|
154
|
+
type: 'password',
|
|
155
|
+
name: 'adminPassword',
|
|
156
|
+
message: 'Admin password:',
|
|
157
|
+
validate: (value) => {
|
|
158
|
+
if (!value) return 'Admin password is required'
|
|
159
|
+
if (value.length < 8) return 'Password must be at least 8 characters'
|
|
160
|
+
return true
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
138
164
|
// Include example collection (only ask if neither flag is set)
|
|
139
165
|
if (!flags.skipExample && !flags.includeExample) {
|
|
140
166
|
questions.push({
|
|
@@ -176,6 +202,8 @@ async function getProjectDetails(initialName) {
|
|
|
176
202
|
template: flags.template || answers.template,
|
|
177
203
|
databaseName: flags.databaseName || answers.databaseName || `${initialName || answers.projectName}-db`,
|
|
178
204
|
bucketName: flags.bucketName || answers.bucketName || `${initialName || answers.projectName}-media`,
|
|
205
|
+
adminEmail: answers.adminEmail,
|
|
206
|
+
adminPassword: answers.adminPassword,
|
|
179
207
|
includeExample: flags.skipExample ? false : (flags.includeExample ? true : (answers.includeExample !== undefined ? answers.includeExample : true)),
|
|
180
208
|
createResources: flags.skipCloudflare ? false : answers.createResources,
|
|
181
209
|
initGit: flags.skipGit ? false : answers.initGit,
|
|
@@ -189,6 +217,8 @@ async function createProject(answers, flags) {
|
|
|
189
217
|
template,
|
|
190
218
|
databaseName,
|
|
191
219
|
bucketName,
|
|
220
|
+
adminEmail,
|
|
221
|
+
adminPassword,
|
|
192
222
|
includeExample,
|
|
193
223
|
createResources,
|
|
194
224
|
initGit,
|
|
@@ -207,6 +237,8 @@ async function createProject(answers, flags) {
|
|
|
207
237
|
projectName,
|
|
208
238
|
databaseName,
|
|
209
239
|
bucketName,
|
|
240
|
+
adminEmail,
|
|
241
|
+
adminPassword,
|
|
210
242
|
includeExample
|
|
211
243
|
})
|
|
212
244
|
spinner.succeed('Copied template files')
|
|
@@ -282,7 +314,7 @@ async function copyTemplate(templateName, targetDir, options) {
|
|
|
282
314
|
|
|
283
315
|
// Add @sonicjs-cms/core dependency
|
|
284
316
|
packageJson.dependencies = {
|
|
285
|
-
'@sonicjs-cms/core': '^2.0.0-
|
|
317
|
+
'@sonicjs-cms/core': '^2.0.0-beta.3',
|
|
286
318
|
...packageJson.dependencies
|
|
287
319
|
}
|
|
288
320
|
|
|
@@ -302,6 +334,129 @@ async function copyTemplate(templateName, targetDir, options) {
|
|
|
302
334
|
await fs.remove(examplePath)
|
|
303
335
|
}
|
|
304
336
|
}
|
|
337
|
+
|
|
338
|
+
// Create admin seed script with provided credentials
|
|
339
|
+
await createAdminSeedScript(targetDir, {
|
|
340
|
+
email: options.adminEmail,
|
|
341
|
+
password: options.adminPassword
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function createAdminSeedScript(targetDir, { email, password }) {
|
|
346
|
+
const seedScriptContent = `import { createDb, users } from '@sonicjs-cms/core'
|
|
347
|
+
import { eq } from 'drizzle-orm'
|
|
348
|
+
import bcrypt from 'bcryptjs'
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Seed script to create initial admin user
|
|
352
|
+
*
|
|
353
|
+
* Run this script after migrations:
|
|
354
|
+
* npm run db:migrate:local
|
|
355
|
+
* npm run seed
|
|
356
|
+
*
|
|
357
|
+
* Admin credentials:
|
|
358
|
+
* Email: ${email}
|
|
359
|
+
* Password: [as entered during setup]
|
|
360
|
+
*/
|
|
361
|
+
|
|
362
|
+
interface Env {
|
|
363
|
+
DB: D1Database
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function seed() {
|
|
367
|
+
// Get D1 database from Cloudflare environment
|
|
368
|
+
// @ts-ignore - getPlatformProxy is available in wrangler
|
|
369
|
+
const { env } = await import('@cloudflare/workers-types/experimental')
|
|
370
|
+
const platform = (env as any).getPlatformProxy?.() || { env: {} }
|
|
371
|
+
|
|
372
|
+
if (!platform.env?.DB) {
|
|
373
|
+
console.error('❌ Error: DB binding not found')
|
|
374
|
+
console.error('')
|
|
375
|
+
console.error('Make sure you have:')
|
|
376
|
+
console.error('1. Created your D1 database: wrangler d1 create <database-name>')
|
|
377
|
+
console.error('2. Updated wrangler.toml with the database_id')
|
|
378
|
+
console.error('3. Run migrations: npm run db:migrate:local')
|
|
379
|
+
console.error('')
|
|
380
|
+
process.exit(1)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const db = createDb(platform.env.DB)
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
// Check if admin user already exists
|
|
387
|
+
const existingUser = await db
|
|
388
|
+
.select()
|
|
389
|
+
.from(users)
|
|
390
|
+
.where(eq(users.email, '${email}'))
|
|
391
|
+
.get()
|
|
392
|
+
|
|
393
|
+
if (existingUser) {
|
|
394
|
+
console.log('✓ Admin user already exists')
|
|
395
|
+
console.log(\` Email: ${email}\`)
|
|
396
|
+
console.log(\` Role: \${existingUser.role}\`)
|
|
397
|
+
return
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Hash password using bcrypt
|
|
401
|
+
const passwordHash = await bcrypt.hash('${password}', 10)
|
|
402
|
+
|
|
403
|
+
// Create admin user
|
|
404
|
+
await db
|
|
405
|
+
.insert(users)
|
|
406
|
+
.values({
|
|
407
|
+
email: '${email}',
|
|
408
|
+
username: '${email.split('@')[0]}',
|
|
409
|
+
password: passwordHash,
|
|
410
|
+
role: 'admin',
|
|
411
|
+
isActive: 1,
|
|
412
|
+
createdAt: new Date().toISOString(),
|
|
413
|
+
updatedAt: new Date().toISOString()
|
|
414
|
+
})
|
|
415
|
+
.run()
|
|
416
|
+
|
|
417
|
+
console.log('✓ Admin user created successfully')
|
|
418
|
+
console.log(\` Email: ${email}\`)
|
|
419
|
+
console.log(\` Role: admin\`)
|
|
420
|
+
console.log('')
|
|
421
|
+
console.log('You can now login at: http://localhost:8787/auth/login')
|
|
422
|
+
} catch (error) {
|
|
423
|
+
console.error('❌ Error creating admin user:', error)
|
|
424
|
+
process.exit(1)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Run seed
|
|
429
|
+
seed()
|
|
430
|
+
.then(() => {
|
|
431
|
+
console.log('')
|
|
432
|
+
console.log('✓ Seeding complete')
|
|
433
|
+
process.exit(0)
|
|
434
|
+
})
|
|
435
|
+
.catch((error) => {
|
|
436
|
+
console.error('❌ Seeding failed:', error)
|
|
437
|
+
process.exit(1)
|
|
438
|
+
})
|
|
439
|
+
`
|
|
440
|
+
|
|
441
|
+
// Create scripts directory
|
|
442
|
+
const scriptsDir = path.join(targetDir, 'scripts')
|
|
443
|
+
await fs.ensureDir(scriptsDir)
|
|
444
|
+
|
|
445
|
+
// Write seed script
|
|
446
|
+
const seedScriptPath = path.join(scriptsDir, 'seed-admin.ts')
|
|
447
|
+
await fs.writeFile(seedScriptPath, seedScriptContent)
|
|
448
|
+
|
|
449
|
+
// Add seed script to package.json
|
|
450
|
+
const packageJsonPath = path.join(targetDir, 'package.json')
|
|
451
|
+
const packageJson = await fs.readJson(packageJsonPath)
|
|
452
|
+
|
|
453
|
+
if (!packageJson.scripts) {
|
|
454
|
+
packageJson.scripts = {}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
packageJson.scripts.seed = 'tsx scripts/seed-admin.ts'
|
|
458
|
+
|
|
459
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 })
|
|
305
460
|
}
|
|
306
461
|
|
|
307
462
|
async function createCloudflareResources(databaseName, bucketName, targetDir) {
|
|
@@ -315,7 +470,7 @@ async function createCloudflareResources(databaseName, bucketName, targetDir) {
|
|
|
315
470
|
// Create D1 database
|
|
316
471
|
let databaseId
|
|
317
472
|
try {
|
|
318
|
-
const { stdout } = await execa('wrangler', ['d1', 'create', databaseName], {
|
|
473
|
+
const { stdout, stderr } = await execa('wrangler', ['d1', 'create', databaseName], {
|
|
319
474
|
cwd: targetDir
|
|
320
475
|
})
|
|
321
476
|
|
|
@@ -323,9 +478,21 @@ async function createCloudflareResources(databaseName, bucketName, targetDir) {
|
|
|
323
478
|
const match = stdout.match(/database_id\s*=\s*["']([^"']+)["']/)
|
|
324
479
|
if (match) {
|
|
325
480
|
databaseId = match[1]
|
|
481
|
+
} else {
|
|
482
|
+
console.log('')
|
|
483
|
+
console.log(kleur.yellow('⚠ Warning: Could not parse database_id from wrangler output'))
|
|
484
|
+
console.log(kleur.dim(' You may need to manually update wrangler.toml'))
|
|
326
485
|
}
|
|
327
486
|
} catch (error) {
|
|
328
|
-
console.log(
|
|
487
|
+
console.log('')
|
|
488
|
+
console.log(kleur.yellow('⚠ D1 database creation failed:'))
|
|
489
|
+
console.log(kleur.dim(` ${error.message}`))
|
|
490
|
+
if (error.stderr) {
|
|
491
|
+
console.log(kleur.dim(` ${error.stderr}`))
|
|
492
|
+
}
|
|
493
|
+
console.log('')
|
|
494
|
+
console.log(kleur.dim(' Create manually with:'))
|
|
495
|
+
console.log(kleur.dim(` wrangler d1 create ${databaseName}`))
|
|
329
496
|
}
|
|
330
497
|
|
|
331
498
|
// Create R2 bucket
|
|
@@ -334,7 +501,15 @@ async function createCloudflareResources(databaseName, bucketName, targetDir) {
|
|
|
334
501
|
cwd: targetDir
|
|
335
502
|
})
|
|
336
503
|
} catch (error) {
|
|
337
|
-
console.log(
|
|
504
|
+
console.log('')
|
|
505
|
+
console.log(kleur.yellow('⚠ R2 bucket creation failed:'))
|
|
506
|
+
console.log(kleur.dim(` ${error.message}`))
|
|
507
|
+
if (error.stderr) {
|
|
508
|
+
console.log(kleur.dim(` ${error.stderr}`))
|
|
509
|
+
}
|
|
510
|
+
console.log('')
|
|
511
|
+
console.log(kleur.dim(' Create manually with:'))
|
|
512
|
+
console.log(kleur.dim(` wrangler r2 bucket create ${bucketName}`))
|
|
338
513
|
}
|
|
339
514
|
|
|
340
515
|
return databaseId
|
|
@@ -419,13 +594,19 @@ function printSuccessMessage(answers) {
|
|
|
419
594
|
}
|
|
420
595
|
|
|
421
596
|
console.log()
|
|
422
|
-
console.log(kleur.bold('Run migrations:'))
|
|
597
|
+
console.log(kleur.bold('Run migrations and seed admin user:'))
|
|
423
598
|
console.log(kleur.cyan(' npm run db:migrate:local'))
|
|
599
|
+
console.log(kleur.cyan(' npm run seed'))
|
|
424
600
|
|
|
425
601
|
console.log()
|
|
426
602
|
console.log(kleur.bold('Start development:'))
|
|
427
603
|
console.log(kleur.cyan(' npm run dev'))
|
|
428
604
|
|
|
605
|
+
console.log()
|
|
606
|
+
console.log(kleur.bold('Login credentials:'))
|
|
607
|
+
console.log(kleur.cyan(` Email: ${answers.adminEmail}`))
|
|
608
|
+
console.log(kleur.dim(` Password: [as entered]`))
|
|
609
|
+
|
|
429
610
|
console.log()
|
|
430
611
|
console.log(kleur.bold('Visit:'))
|
|
431
612
|
console.log(kleur.cyan(' http://localhost:8787/admin'))
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"db:studio": "drizzle-kit studio",
|
|
13
13
|
"type-check": "tsc --noEmit",
|
|
14
14
|
"test": "vitest --run",
|
|
15
|
-
"test:watch": "vitest"
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"update": "npm install @sonicjs-cms/core@latest",
|
|
17
|
+
"update:beta": "npm install @sonicjs-cms/core@beta"
|
|
16
18
|
},
|
|
17
19
|
"dependencies": {
|
|
18
20
|
},
|