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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sonicjs",
3
- "version": "2.0.0-alpha.7",
3
+ "version": "2.0.0-beta.4",
4
4
  "description": "Create a new SonicJS application with zero configuration",
5
5
  "type": "module",
6
6
  "bin": {
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-alpha.7'
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-alpha.3',
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(kleur.yellow(' D1 database creation failed'))
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(kleur.yellow(' R2 bucket creation failed'))
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
  },