launchbase 1.0.5 → 1.0.7

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.
Files changed (2) hide show
  1. package/bin/launchbase.js +96 -32
  2. package/package.json +1 -1
package/bin/launchbase.js CHANGED
@@ -1,14 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const path = require('path');
4
+ const net = require('net');
4
5
  const { Command } = require('commander');
5
6
  const crypto = require('crypto');
6
7
  const fs = require('fs-extra');
7
8
  const { execSync, spawn } = require('child_process');
8
9
 
9
- const VERSION = '1.0.5';
10
+ const VERSION = '1.0.7';
10
11
  const program = new Command();
11
12
 
13
+ function findAvailablePort(startPort = 5432, maxAttempts = 100) {
14
+ return new Promise((resolve, reject) => {
15
+ const tryPort = (port, attempts) => {
16
+ if (attempts >= maxAttempts) {
17
+ reject(new Error('Could not find available port'));
18
+ return;
19
+ }
20
+
21
+ const server = net.createServer();
22
+ server.once('error', () => {
23
+ tryPort(port + 1, attempts + 1);
24
+ });
25
+ server.once('listening', () => {
26
+ server.close();
27
+ resolve(port);
28
+ });
29
+ server.listen(port);
30
+ };
31
+
32
+ tryPort(startPort, 0);
33
+ });
34
+ }
35
+
36
+ function findAvailablePorts() {
37
+ return Promise.all([
38
+ findAvailablePort(5432, 100), // Database port
39
+ findAvailablePort(3000, 100), // API port
40
+ findAvailablePort(5173, 100), // Frontend port
41
+ ]);
42
+ }
43
+
12
44
  function replaceInFile(filePath, replacements) {
13
45
  let content = fs.readFileSync(filePath, 'utf8');
14
46
  for (const [from, to] of Object.entries(replacements)) {
@@ -264,32 +296,34 @@ async function startDatabase(projectDir) {
264
296
 
265
297
  console.log('🐳 Starting database with Docker Compose...\n');
266
298
 
267
- try {
268
- // Check if containers are already running
269
- const psOutput = execSync('docker compose ps -q', {
270
- cwd: projectDir,
271
- stdio: 'pipe',
272
- encoding: 'utf8'
273
- }).trim();
274
-
275
- if (psOutput) {
276
- console.log('āœ… Database containers already running\n');
277
- return true;
278
- }
279
- } catch {
280
- // Containers not running, proceed to start
281
- }
299
+ // Get project name from directory
300
+ const projectName = path.basename(projectDir);
282
301
 
283
302
  try {
284
- // Stop any existing containers first to avoid port conflicts
303
+ // First, stop any containers with this project name (from previous runs)
285
304
  try {
286
- execSync('docker compose down', {
305
+ execSync(`docker compose -p ${projectName} down --remove-orphans`, {
287
306
  cwd: projectDir,
288
307
  stdio: 'pipe'
289
308
  });
290
309
  } catch {}
291
310
 
292
- execSync('docker compose up -d --wait', {
311
+ // Also try to remove any containers with similar names
312
+ try {
313
+ const containers = execSync('docker ps -a --format "{{.Names}}"', {
314
+ stdio: 'pipe',
315
+ encoding: 'utf8'
316
+ });
317
+ const containerList = containers.trim().split('\n').filter(c => c.includes(projectName));
318
+ for (const container of containerList) {
319
+ try {
320
+ execSync(`docker rm -f ${container}`, { stdio: 'pipe' });
321
+ } catch {}
322
+ }
323
+ } catch {}
324
+
325
+ // Start fresh containers with explicit project name
326
+ execSync(`docker compose -p ${projectName} up -d --wait --force-recreate`, {
293
327
  cwd: projectDir,
294
328
  stdio: 'inherit'
295
329
  });
@@ -301,10 +335,10 @@ async function startDatabase(projectDir) {
301
335
 
302
336
  // Check for port conflict
303
337
  if (error.message && error.message.includes('port is already allocated')) {
304
- console.log(' Port conflict detected. Another PostgreSQL may be running.\n');
305
- console.log('šŸ’” Solutions:');
306
- console.log(' 1. Stop the other PostgreSQL: docker stop <container>');
307
- console.log(' 2. Or change the port in docker-compose.yml\n');
338
+ console.log(' Port conflict detected. Another service is using this port.\n');
339
+ console.log('šŸ’” Try stopping other databases:');
340
+ console.log(' docker ps # list running containers');
341
+ console.log(' docker stop <container_id> # stop the conflicting container\n');
308
342
  } else {
309
343
  console.log(' Check Docker logs: docker compose logs\n');
310
344
  }
@@ -331,6 +365,11 @@ program
331
365
  console.log('\nšŸš€ LaunchBase CLI v' + VERSION + '\n');
332
366
  console.log('šŸ“ Creating project:', appName);
333
367
 
368
+ // Find available ports first
369
+ console.log('šŸ” Finding available ports...');
370
+ const [dbPort, apiPort, frontendPort] = await findAvailablePorts();
371
+ console.log(` Database: ${dbPort}, API: ${apiPort}, Frontend: ${frontendPort}\n`);
372
+
334
373
  const templateDir = path.resolve(__dirname, '..', 'template');
335
374
  const targetDir = path.resolve(process.cwd(), appName);
336
375
 
@@ -380,13 +419,23 @@ program
380
419
  }
381
420
  });
382
421
 
383
- // Replace placeholders
422
+ // Replace placeholders with dynamic ports
384
423
  const replacements = {
385
424
  '__APP_NAME__': appName,
386
425
  '"name": "launchbase-template"': `"name": "${appName}"`,
426
+ '"PORT=3000"': `"PORT=${apiPort}"`,
427
+ 'PORT=3000': `PORT=${apiPort}`,
428
+ 'localhost:3000': `localhost:${apiPort}`,
429
+ 'localhost:5173': `localhost:${frontendPort}`,
430
+ 'localhost:5433': `localhost:${dbPort}`,
431
+ 'localhost:5432': `localhost:${dbPort}`,
432
+ '"5433:5432"': `"${dbPort}:5432"`,
433
+ '"5432:5432"': `"${dbPort}:5432"`,
434
+ '"3000:3000"': `"${apiPort}:3000"`,
387
435
  };
388
436
 
389
- const filesToReplace = ['package.json', '.env.example', 'README.md'];
437
+ // Update files with port replacements
438
+ const filesToReplace = ['package.json', '.env.example', 'README.md', 'docker-compose.yml', '.env'];
390
439
  for (const rel of filesToReplace) {
391
440
  const fp = path.join(targetDir, rel);
392
441
  if (await fs.pathExists(fp)) {
@@ -402,9 +451,21 @@ program
402
451
  let env = await fs.readFile(envExamplePath, 'utf8');
403
452
  env = env.replace('JWT_ACCESS_SECRET=__CHANGE_ME__', `JWT_ACCESS_SECRET=${randomSecret(32)}`);
404
453
  env = env.replace('JWT_REFRESH_SECRET=__CHANGE_ME__', `JWT_REFRESH_SECRET=${randomSecret(32)}`);
454
+ // Ensure ports are correct
455
+ env = env.replace(/PORT=\d+/, `PORT=${apiPort}`);
456
+ env = env.replace(/localhost:\d+.*__APP_NAME__/g, `localhost:${dbPort}/${appName}`);
405
457
  await fs.writeFile(envPath, env, 'utf8');
406
458
  }
407
459
 
460
+ // Update docker-compose.yml with correct ports
461
+ const dockerComposePath = path.join(targetDir, 'docker-compose.yml');
462
+ if (await fs.pathExists(dockerComposePath)) {
463
+ let compose = await fs.readFile(dockerComposePath, 'utf8');
464
+ compose = compose.replace(/"(\d+):5432"/, `"${dbPort}:5432"`);
465
+ compose = compose.replace(/"(\d+):3000"/, `"${apiPort}:3000"`);
466
+ await fs.writeFile(dockerComposePath, compose, 'utf8');
467
+ }
468
+
408
469
  console.log('āœ… Project files created\n');
409
470
 
410
471
  // Install dependencies
@@ -444,20 +505,22 @@ program
444
505
  // Start dev server
445
506
  console.log('\nšŸš€ Starting development server...\n');
446
507
  console.log('━'.repeat(50));
447
- console.log(' API: http://localhost:3000');
448
- console.log(' Docs: http://localhost:3000/docs');
449
- console.log(' Health: http://localhost:3000/health');
508
+ console.log(` API: http://localhost:${apiPort}`);
509
+ console.log(` Docs: http://localhost:${apiPort}/docs`);
510
+ console.log(` Health: http://localhost:${apiPort}/health`);
450
511
  if (options.template) {
451
- console.log(' Frontend: http://localhost:5173');
512
+ console.log(` Frontend: http://localhost:${frontendPort}`);
452
513
  }
514
+ console.log(` Database: localhost:${dbPort}`);
453
515
  console.log('━'.repeat(50));
454
516
  console.log('\nPress Ctrl+C to stop\n');
455
517
 
456
- // Start backend
518
+ // Start backend with correct port
457
519
  const backend = spawn('npm', ['run', 'start:dev'], {
458
520
  cwd: targetDir,
459
521
  stdio: 'inherit',
460
- shell: true
522
+ shell: true,
523
+ env: { ...process.env, PORT: apiPort.toString() }
461
524
  });
462
525
 
463
526
  // Start frontend after delay if included
@@ -468,7 +531,8 @@ program
468
531
  const frontend = spawn('npm', ['run', 'dev'], {
469
532
  cwd: frontendPath,
470
533
  stdio: 'inherit',
471
- shell: true
534
+ shell: true,
535
+ env: { ...process.env, VITE_API_URL: `http://localhost:${apiPort}`, PORT: frontendPort.toString() }
472
536
  });
473
537
  }, 5000);
474
538
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "launchbase",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Generate production-ready NestJS backends with authentication, multi-tenancy, billing, and deployment in minutes",
5
5
  "author": "LaunchBase",
6
6
  "keywords": [