launchbase 1.0.2 ā 1.0.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/bin/launchbase.js
CHANGED
|
@@ -6,7 +6,7 @@ const crypto = require('crypto');
|
|
|
6
6
|
const fs = require('fs-extra');
|
|
7
7
|
const { execSync, spawn } = require('child_process');
|
|
8
8
|
|
|
9
|
-
const VERSION = '1.0.
|
|
9
|
+
const VERSION = '1.0.4';
|
|
10
10
|
const program = new Command();
|
|
11
11
|
|
|
12
12
|
function replaceInFile(filePath, replacements) {
|
|
@@ -52,12 +52,431 @@ function checkRenderCLI() {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function checkDocker() {
|
|
56
|
+
try {
|
|
57
|
+
execSync('docker --version', { stdio: 'pipe' });
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function checkDockerRunning() {
|
|
65
|
+
try {
|
|
66
|
+
execSync('docker info', { stdio: 'pipe' });
|
|
67
|
+
return true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getPlatform() {
|
|
74
|
+
return process.platform; // 'win32', 'darwin', 'linux'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function installDocker() {
|
|
78
|
+
const platform = getPlatform();
|
|
79
|
+
console.log('\nš¦ Docker not found. Installing...\n');
|
|
80
|
+
|
|
81
|
+
if (platform === 'win32') {
|
|
82
|
+
console.log('š Docker Desktop for Windows\n');
|
|
83
|
+
console.log('Option 1: Install via winget (recommended)');
|
|
84
|
+
console.log(' winget install Docker.DockerDesktop\n');
|
|
85
|
+
console.log('Option 2: Download manually');
|
|
86
|
+
console.log(' https://www.docker.com/products/docker-desktop\n');
|
|
87
|
+
console.log('After installation, restart your terminal and run the command again.\n');
|
|
88
|
+
|
|
89
|
+
// Try winget install
|
|
90
|
+
const useWinget = await askYesNo('Install via winget now?');
|
|
91
|
+
if (useWinget) {
|
|
92
|
+
console.log('\nā³ Installing Docker Desktop via winget...');
|
|
93
|
+
console.log(' (This may take a few minutes and require UAC prompt)\n');
|
|
94
|
+
try {
|
|
95
|
+
execSync('winget install Docker.DockerDesktop --accept-package-agreements --accept-source-agreements', {
|
|
96
|
+
stdio: 'inherit'
|
|
97
|
+
});
|
|
98
|
+
console.log('\nā
Docker Desktop installed!');
|
|
99
|
+
console.log('ā ļø Please restart Docker Desktop and run the command again.\n');
|
|
100
|
+
process.exit(0);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.log('\nā winget install failed. Please install manually from:');
|
|
103
|
+
console.log(' https://www.docker.com/products/docker-desktop\n');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} else if (platform === 'darwin') {
|
|
108
|
+
console.log('š Docker Desktop for macOS\n');
|
|
109
|
+
console.log('Option 1: Install via Homebrew (recommended)');
|
|
110
|
+
console.log(' brew install --cask docker\n');
|
|
111
|
+
console.log('Option 2: Download manually');
|
|
112
|
+
console.log(' https://www.docker.com/products/docker-desktop\n');
|
|
113
|
+
|
|
114
|
+
// Try brew install
|
|
115
|
+
try {
|
|
116
|
+
execSync('brew --version', { stdio: 'pipe' });
|
|
117
|
+
const useBrew = await askYesNo('Install via Homebrew now?');
|
|
118
|
+
if (useBrew) {
|
|
119
|
+
console.log('\nā³ Installing Docker Desktop via Homebrew...\n');
|
|
120
|
+
execSync('brew install --cask docker', { stdio: 'inherit' });
|
|
121
|
+
console.log('\nā
Docker Desktop installed!');
|
|
122
|
+
console.log('ā ļø Open Docker Desktop and run the command again.\n');
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
console.log('Homebrew not found. Install manually from:');
|
|
127
|
+
console.log(' https://www.docker.com/products/docker-desktop\n');
|
|
128
|
+
}
|
|
129
|
+
} else if (platform === 'linux') {
|
|
130
|
+
console.log('ļæ½ Docker for Linux\n');
|
|
131
|
+
console.log('Running official Docker install script...\n');
|
|
132
|
+
try {
|
|
133
|
+
execSync('curl -fsSL https://get.docker.com | sh', { stdio: 'inherit' });
|
|
134
|
+
console.log('\nā
Docker installed!');
|
|
135
|
+
console.log('ā ļø Run: sudo usermod -aG docker $USER && newgrp docker\n');
|
|
136
|
+
process.exit(0);
|
|
137
|
+
} catch {
|
|
138
|
+
console.log('\nā Install failed. See: https://docs.docker.com/engine/install/\n');
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function askYesNo(question) {
|
|
147
|
+
const readline = require('readline');
|
|
148
|
+
const rl = readline.createInterface({
|
|
149
|
+
input: process.stdin,
|
|
150
|
+
output: process.stdout
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
rl.question(`${question} (y/n): `, (answer) => {
|
|
155
|
+
rl.close();
|
|
156
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function ensureDocker() {
|
|
162
|
+
// Check if Docker is installed
|
|
163
|
+
if (!checkDocker()) {
|
|
164
|
+
await installDocker();
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Check if Docker daemon is running
|
|
169
|
+
if (!checkDockerRunning()) {
|
|
170
|
+
const platform = getPlatform();
|
|
171
|
+
|
|
172
|
+
console.log('\nā ļø Docker is installed but not running.');
|
|
173
|
+
|
|
174
|
+
// Try to start Docker Desktop automatically on Windows/macOS
|
|
175
|
+
if (platform === 'win32') {
|
|
176
|
+
console.log('š Attempting to start Docker Desktop...\n');
|
|
177
|
+
try {
|
|
178
|
+
// Try to start Docker Desktop via PowerShell
|
|
179
|
+
execSync('Start-Process "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"', {
|
|
180
|
+
shell: 'powershell.exe',
|
|
181
|
+
stdio: 'pipe'
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
console.log('ā³ Waiting for Docker to start');
|
|
185
|
+
|
|
186
|
+
// Wait for Docker to be ready (up to 60 seconds)
|
|
187
|
+
let retries = 60;
|
|
188
|
+
while (retries > 0) {
|
|
189
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
190
|
+
try {
|
|
191
|
+
execSync('docker info', { stdio: 'pipe' });
|
|
192
|
+
console.log('\nā
Docker is now running!\n');
|
|
193
|
+
return true;
|
|
194
|
+
} catch {
|
|
195
|
+
process.stdout.write('.');
|
|
196
|
+
retries--;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log('\n\nā ļø Docker did not start in time. Please start Docker Desktop manually.\n');
|
|
201
|
+
process.exit(1);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.log('ā Could not start Docker Desktop automatically.\n');
|
|
204
|
+
console.log('š” Please start Docker Desktop from your Start menu and run the command again.\n');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
} else if (platform === 'darwin') {
|
|
208
|
+
console.log('š Attempting to start Docker Desktop...\n');
|
|
209
|
+
try {
|
|
210
|
+
execSync('open -a Docker', { stdio: 'pipe' });
|
|
211
|
+
|
|
212
|
+
console.log('ā³ Waiting for Docker to start');
|
|
213
|
+
|
|
214
|
+
// Wait for Docker to be ready (up to 60 seconds)
|
|
215
|
+
let retries = 60;
|
|
216
|
+
while (retries > 0) {
|
|
217
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
218
|
+
try {
|
|
219
|
+
execSync('docker info', { stdio: 'pipe' });
|
|
220
|
+
console.log('\nā
Docker is now running!\n');
|
|
221
|
+
return true;
|
|
222
|
+
} catch {
|
|
223
|
+
process.stdout.write('.');
|
|
224
|
+
retries--;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
console.log('\n\nā ļø Docker did not start in time. Please start Docker Desktop manually.\n');
|
|
229
|
+
process.exit(1);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.log('ā Could not start Docker Desktop automatically.\n');
|
|
232
|
+
console.log('š” Please start Docker Desktop from your Applications and run the command again.\n');
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
// Linux - try systemctl
|
|
237
|
+
console.log('š Attempting to start Docker daemon...\n');
|
|
238
|
+
try {
|
|
239
|
+
execSync('sudo systemctl start docker', { stdio: 'inherit' });
|
|
240
|
+
|
|
241
|
+
// Wait briefly and check
|
|
242
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
243
|
+
if (checkDockerRunning()) {
|
|
244
|
+
console.log('ā
Docker is now running!\n');
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
} catch {}
|
|
248
|
+
|
|
249
|
+
console.log('š” Please start Docker manually and run the command again.\n');
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function startDatabase(projectDir) {
|
|
258
|
+
const dockerComposePath = path.join(projectDir, 'docker-compose.yml');
|
|
259
|
+
|
|
260
|
+
if (!await fs.pathExists(dockerComposePath)) {
|
|
261
|
+
console.log('ā ļø No docker-compose.yml found, skipping database setup.\n');
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
console.log('š³ Starting database with Docker Compose...\n');
|
|
266
|
+
|
|
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
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
execSync('docker compose up -d', {
|
|
285
|
+
cwd: projectDir,
|
|
286
|
+
stdio: 'inherit'
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
console.log('\nā³ Waiting for database to be ready...');
|
|
290
|
+
|
|
291
|
+
// Wait for database to be ready
|
|
292
|
+
let retries = 30;
|
|
293
|
+
while (retries > 0) {
|
|
294
|
+
try {
|
|
295
|
+
execSync('docker compose exec -T db pg_isready -U postgres', {
|
|
296
|
+
cwd: projectDir,
|
|
297
|
+
stdio: 'pipe'
|
|
298
|
+
});
|
|
299
|
+
console.log('ā
Database is ready!\n');
|
|
300
|
+
return true;
|
|
301
|
+
} catch {
|
|
302
|
+
process.stdout.write('.');
|
|
303
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
304
|
+
retries--;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
console.log('\nā ļø Database may not be ready yet. Check logs: docker compose logs\n');
|
|
309
|
+
return true;
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.log('\nā Failed to start database. Check Docker logs.\n');
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
55
316
|
program
|
|
56
317
|
.name('launchbase')
|
|
57
|
-
.description('
|
|
318
|
+
.description('�� Generate production-ready NestJS backends with authentication, multi-tenancy, billing, and deployment')
|
|
58
319
|
.version(VERSION);
|
|
59
320
|
|
|
60
|
-
//
|
|
321
|
+
// New command - one command to create and start everything
|
|
322
|
+
program
|
|
323
|
+
.command('new')
|
|
324
|
+
.description('š Create new project and start development (one command experience)')
|
|
325
|
+
.argument('<appName>', 'Project name')
|
|
326
|
+
.option('-t, --template', 'Include frontend React template')
|
|
327
|
+
.option('-s, --sdk', 'Include TypeScript SDK')
|
|
328
|
+
.option('--no-docker', 'Skip Docker/database setup')
|
|
329
|
+
.option('--no-cicd', 'Skip CI/CD workflow')
|
|
330
|
+
.action(async (appName, options) => {
|
|
331
|
+
console.log('\nš LaunchBase CLI v' + VERSION + '\n');
|
|
332
|
+
console.log('š Creating project:', appName);
|
|
333
|
+
|
|
334
|
+
const templateDir = path.resolve(__dirname, '..', 'template');
|
|
335
|
+
const targetDir = path.resolve(process.cwd(), appName);
|
|
336
|
+
|
|
337
|
+
// Check if directory exists
|
|
338
|
+
if (await fs.pathExists(targetDir)) {
|
|
339
|
+
console.error(`ā Target directory already exists: ${targetDir}`);
|
|
340
|
+
console.log(' Use a different name or remove the existing directory.');
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Copy template files with filtering
|
|
345
|
+
await fs.copy(templateDir, targetDir, {
|
|
346
|
+
filter: (src) => {
|
|
347
|
+
const relativePath = path.relative(templateDir, src);
|
|
348
|
+
|
|
349
|
+
// Skip node_modules, dist, etc.
|
|
350
|
+
if (relativePath.includes('node_modules') || relativePath.includes('dist') || relativePath.includes('.next')) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Skip frontend if not requested
|
|
355
|
+
if (relativePath.startsWith('frontend') && !options.template) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Skip SDK if not requested
|
|
360
|
+
if (relativePath.startsWith('sdk') && !options.sdk) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Skip Docker files if not requested
|
|
365
|
+
if (options.noDocker) {
|
|
366
|
+
if (relativePath.includes('Dockerfile') ||
|
|
367
|
+
relativePath.includes('docker-compose') ||
|
|
368
|
+
relativePath.includes('nginx.conf') ||
|
|
369
|
+
relativePath.includes('certbot')) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Skip CI/CD if not requested
|
|
375
|
+
if (options.noCicd && relativePath.startsWith('.github')) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Replace placeholders
|
|
384
|
+
const replacements = {
|
|
385
|
+
'__APP_NAME__': appName,
|
|
386
|
+
'"name": "launchbase-template"': `"name": "${appName}"`,
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const filesToReplace = ['package.json', '.env.example', 'README.md'];
|
|
390
|
+
for (const rel of filesToReplace) {
|
|
391
|
+
const fp = path.join(targetDir, rel);
|
|
392
|
+
if (await fs.pathExists(fp)) {
|
|
393
|
+
replaceInFile(fp, replacements);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Generate .env with secrets
|
|
398
|
+
const envExamplePath = path.join(targetDir, '.env.example');
|
|
399
|
+
const envPath = path.join(targetDir, '.env');
|
|
400
|
+
|
|
401
|
+
if (await fs.pathExists(envExamplePath)) {
|
|
402
|
+
let env = await fs.readFile(envExamplePath, 'utf8');
|
|
403
|
+
env = env.replace('JWT_ACCESS_SECRET=__CHANGE_ME__', `JWT_ACCESS_SECRET=${randomSecret(32)}`);
|
|
404
|
+
env = env.replace('JWT_REFRESH_SECRET=__CHANGE_ME__', `JWT_REFRESH_SECRET=${randomSecret(32)}`);
|
|
405
|
+
await fs.writeFile(envPath, env, 'utf8');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
console.log('ā
Project files created\n');
|
|
409
|
+
|
|
410
|
+
// Install dependencies
|
|
411
|
+
console.log('š¦ Installing dependencies...\n');
|
|
412
|
+
runCommand('npm install', { cwd: targetDir });
|
|
413
|
+
|
|
414
|
+
if (options.template) {
|
|
415
|
+
const frontendPath = path.join(targetDir, 'frontend');
|
|
416
|
+
if (await fs.pathExists(frontendPath)) {
|
|
417
|
+
console.log('\nš¦ Installing frontend dependencies...\n');
|
|
418
|
+
runCommand('npm install', { cwd: frontendPath });
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Setup Docker and database
|
|
423
|
+
if (!options.noDocker) {
|
|
424
|
+
console.log('\nš³ Setting up database...\n');
|
|
425
|
+
|
|
426
|
+
if (await ensureDocker()) {
|
|
427
|
+
if (await startDatabase(targetDir)) {
|
|
428
|
+
// Wait a moment for DB to be fully ready
|
|
429
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
430
|
+
|
|
431
|
+
// Run migrations
|
|
432
|
+
console.log('š Running database migrations...\n');
|
|
433
|
+
runCommand('npx prisma migrate dev --name init', { cwd: targetDir });
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Start dev server
|
|
439
|
+
console.log('\nš Starting development server...\n');
|
|
440
|
+
console.log('ā'.repeat(50));
|
|
441
|
+
console.log(' API: http://localhost:3000');
|
|
442
|
+
console.log(' Docs: http://localhost:3000/docs');
|
|
443
|
+
console.log(' Health: http://localhost:3000/health');
|
|
444
|
+
if (options.template) {
|
|
445
|
+
console.log(' Frontend: http://localhost:5173');
|
|
446
|
+
}
|
|
447
|
+
console.log('ā'.repeat(50));
|
|
448
|
+
console.log('\nPress Ctrl+C to stop\n');
|
|
449
|
+
|
|
450
|
+
// Start backend
|
|
451
|
+
const backend = spawn('npm', ['run', 'start:dev'], {
|
|
452
|
+
cwd: targetDir,
|
|
453
|
+
stdio: 'inherit',
|
|
454
|
+
shell: true
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
// Start frontend after delay if included
|
|
458
|
+
if (options.template) {
|
|
459
|
+
const frontendPath = path.join(targetDir, 'frontend');
|
|
460
|
+
if (await fs.pathExists(frontendPath)) {
|
|
461
|
+
setTimeout(() => {
|
|
462
|
+
const frontend = spawn('npm', ['run', 'dev'], {
|
|
463
|
+
cwd: frontendPath,
|
|
464
|
+
stdio: 'inherit',
|
|
465
|
+
shell: true
|
|
466
|
+
});
|
|
467
|
+
}, 5000);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Handle shutdown
|
|
472
|
+
process.on('SIGINT', () => {
|
|
473
|
+
console.log('\n\nš Shutting down...');
|
|
474
|
+
backend.kill();
|
|
475
|
+
process.exit(0);
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Default command: create (scaffold only)
|
|
61
480
|
program
|
|
62
481
|
.argument('[appName]', 'Destination folder name', 'my-app')
|
|
63
482
|
.option('-t, --template', 'Include frontend React template')
|
|
@@ -173,7 +592,7 @@ program
|
|
|
173
592
|
console.log('\n');
|
|
174
593
|
});
|
|
175
594
|
|
|
176
|
-
// Dev command -
|
|
595
|
+
// Dev command - start development environment (for existing projects)
|
|
177
596
|
program
|
|
178
597
|
.command('dev')
|
|
179
598
|
.description('š Start development environment (backend + frontend + db)')
|
|
@@ -184,16 +603,13 @@ program
|
|
|
184
603
|
console.log('\nš LaunchBase Dev\n');
|
|
185
604
|
|
|
186
605
|
const projectDir = process.cwd();
|
|
187
|
-
const { spawn } = require('child_process');
|
|
188
606
|
|
|
189
607
|
// Check if this is a LaunchBase project
|
|
190
608
|
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
191
609
|
if (!await fs.pathExists(packageJsonPath)) {
|
|
192
610
|
console.error('ā No package.json found. Run this command from your project directory.');
|
|
193
611
|
console.log('\nš” Create a new project first:');
|
|
194
|
-
console.log(' npx launchbase my-app');
|
|
195
|
-
console.log(' cd my-app');
|
|
196
|
-
console.log(' npx launchbase dev\n');
|
|
612
|
+
console.log(' npx launchbase new my-app\n');
|
|
197
613
|
process.exit(1);
|
|
198
614
|
}
|
|
199
615
|
|
|
@@ -216,16 +632,18 @@ program
|
|
|
216
632
|
|
|
217
633
|
// Setup database if not skipped
|
|
218
634
|
if (!options.skipDb && !options.frontendOnly) {
|
|
219
|
-
console.log('
|
|
635
|
+
console.log('ļæ½ Setting up database...\n');
|
|
220
636
|
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
637
|
+
// Ensure Docker is available
|
|
638
|
+
if (await ensureDocker()) {
|
|
639
|
+
if (await startDatabase(projectDir)) {
|
|
640
|
+
// Generate Prisma client
|
|
641
|
+
runCommand('npx prisma generate', { cwd: projectDir });
|
|
642
|
+
|
|
643
|
+
// Run migrations
|
|
644
|
+
console.log('\nš Running migrations...\n');
|
|
645
|
+
runCommand('npx prisma migrate dev', { cwd: projectDir });
|
|
646
|
+
}
|
|
229
647
|
}
|
|
230
648
|
}
|
|
231
649
|
|
|
@@ -234,7 +652,7 @@ program
|
|
|
234
652
|
|
|
235
653
|
if (!options.frontendOnly) {
|
|
236
654
|
// Start backend
|
|
237
|
-
console.log('š§ Starting backend on http://localhost:3000');
|
|
655
|
+
console.log('\nš§ Starting backend on http://localhost:3000');
|
|
238
656
|
const backend = spawn('npm', ['run', 'start:dev'], {
|
|
239
657
|
cwd: projectDir,
|
|
240
658
|
stdio: 'inherit',
|
package/package.json
CHANGED
package/template/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "tsc -p tsconfig.build.json",
|
|
7
|
+
"build": "prisma generate && tsc -p tsconfig.build.json",
|
|
8
8
|
"start": "node dist/src/main.js",
|
|
9
9
|
"start:dev": "ts-node-dev --respawn --transpile-only src/main.ts",
|
|
10
10
|
"prisma:generate": "prisma generate",
|
|
@@ -4,7 +4,7 @@ import LaunchBase from '@launchbasex/sdk'
|
|
|
4
4
|
|
|
5
5
|
@Injectable()
|
|
6
6
|
export class LaunchBaseService implements OnModuleInit {
|
|
7
|
-
private client
|
|
7
|
+
private client!: LaunchBase // Definite assignment assertion - initialized in onModuleInit
|
|
8
8
|
|
|
9
9
|
constructor(private configService: ConfigService) {}
|
|
10
10
|
|