lapeh 2.4.12 → 2.6.2
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/index.js +669 -942
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +21 -16
- package/dist/generated/prisma/internal/prismaNamespace.d.ts.map +1 -1
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +3 -3
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -1
- package/dist/lib/bootstrap.d.ts.map +1 -1
- package/dist/lib/bootstrap.js +34 -11
- package/dist/lib/core/database.d.ts.map +1 -1
- package/dist/lib/core/database.js +7 -1
- package/dist/lib/utils/response.d.ts +1 -1
- package/dist/lib/utils/response.d.ts.map +1 -1
- package/dist/src/config/app.d.ts +10 -0
- package/dist/src/config/app.d.ts.map +1 -0
- package/dist/src/config/app.js +12 -0
- package/dist/src/config/cors.d.ts +6 -0
- package/dist/src/config/cors.d.ts.map +1 -0
- package/dist/src/config/cors.js +8 -0
- package/dist/src/modules/Auth/auth.controller.d.ts +11 -0
- package/dist/src/modules/Auth/auth.controller.d.ts.map +1 -0
- package/dist/src/modules/Auth/auth.controller.js +414 -0
- package/dist/src/modules/Pets/pets.controller.d.ts +7 -0
- package/dist/src/modules/Pets/pets.controller.d.ts.map +1 -0
- package/dist/src/modules/Pets/pets.controller.js +163 -0
- package/dist/src/modules/Rbac/rbac.controller.d.ts +16 -0
- package/dist/src/modules/Rbac/rbac.controller.d.ts.map +1 -0
- package/dist/src/modules/Rbac/rbac.controller.js +437 -0
- package/dist/src/routes/auth.js +9 -9
- package/dist/src/routes/pets.js +1 -1
- package/dist/src/routes/rbac.js +15 -15
- package/lib/bootstrap.ts +34 -13
- package/lib/core/database.ts +7 -1
- package/lib/utils/response.ts +4 -4
- package/package.json +4 -5
- package/prisma/base.prisma.template +0 -1
- package/prisma/schema.prisma +0 -1
- package/prisma.config.ts +14 -0
- package/scripts/compile-schema.js +28 -10
- package/scripts/make-module.js +100 -158
- package/src/config/app.ts +9 -0
- package/src/config/cors.ts +5 -0
- package/src/routes/auth.ts +1 -1
- package/src/routes/pets.ts +1 -1
- package/src/routes/rbac.ts +1 -1
- package/storage/logs/.0337f5062fe676994d1dc340156e089444e3d6e0-audit.json +5 -0
- package/storage/logs/lapeh-2025-12-29.log +94 -0
- package/scripts/make-controller.js +0 -205
- package/scripts/make-model.js +0 -42
- /package/src/{controllers/authController.ts → modules/Auth/auth.controller.ts} +0 -0
- /package/src/{models/core.prisma → modules/Auth/auth.prisma} +0 -0
- /package/src/{controllers/petController.ts → modules/Pets/pets.controller.ts} +0 -0
- /package/src/{models → modules/Pets}/pets.prisma +0 -0
- /package/src/{controllers/rbacController.ts → modules/Rbac/rbac.controller.ts} +0 -0
package/bin/index.js
CHANGED
|
@@ -1,943 +1,670 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { execSync } = require('child_process');
|
|
6
|
-
const readline = require('readline');
|
|
7
|
-
|
|
8
|
-
const args = process.argv.slice(2);
|
|
9
|
-
const command = args[0];
|
|
10
|
-
|
|
11
|
-
// Telemetry Logic
|
|
12
|
-
async function sendTelemetry(cmd, errorInfo = null) {
|
|
13
|
-
try {
|
|
14
|
-
const os = require('os');
|
|
15
|
-
|
|
16
|
-
const payload = {
|
|
17
|
-
command: cmd,
|
|
18
|
-
nodeVersion: process.version,
|
|
19
|
-
osPlatform: os.platform(),
|
|
20
|
-
osRelease: os.release(),
|
|
21
|
-
timestamp: new Date().toISOString()
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
if (errorInfo) {
|
|
25
|
-
payload.error = errorInfo.message;
|
|
26
|
-
payload.stack = errorInfo.stack;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const data = JSON.stringify(payload);
|
|
30
|
-
|
|
31
|
-
// Parse URL from env or use default
|
|
32
|
-
const apiUrl = process.env.LAPEH_API_URL || 'https://lapeh-doc.vercel.app/api/telemetry';
|
|
33
|
-
const url = new URL(apiUrl);
|
|
34
|
-
const isHttps = url.protocol === 'https:';
|
|
35
|
-
const client = isHttps ? require('https') : require('http');
|
|
36
|
-
|
|
37
|
-
const options = {
|
|
38
|
-
hostname: url.hostname,
|
|
39
|
-
port: url.port || (isHttps ? 443 : 80),
|
|
40
|
-
path: url.pathname,
|
|
41
|
-
method: 'POST',
|
|
42
|
-
headers: {
|
|
43
|
-
'Content-Type': 'application/json',
|
|
44
|
-
'Content-Length': Buffer.byteLength(data)
|
|
45
|
-
},
|
|
46
|
-
timeout: 2000 // Slightly longer for crash reports
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const req = client.request(options, (res) => {
|
|
50
|
-
res.resume();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
req.on('error', (e) => {
|
|
54
|
-
// Silent fail
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
req.write(data);
|
|
58
|
-
req.end();
|
|
59
|
-
} catch (e) {
|
|
60
|
-
// Silent fail
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Global Error Handler for Crash Reporting
|
|
65
|
-
process.on('uncaughtException', async (err) => {
|
|
66
|
-
console.error('❌ Unexpected Error:', err);
|
|
67
|
-
console.log('📝 Sending crash report...');
|
|
68
|
-
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
env
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
console.
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
'website',
|
|
672
|
-
'init',
|
|
673
|
-
'test-local-run',
|
|
674
|
-
'coverage',
|
|
675
|
-
projectName // Don't copy the destination folder itself if creating inside the template
|
|
676
|
-
];
|
|
677
|
-
|
|
678
|
-
function copyDir(src, dest) {
|
|
679
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
680
|
-
|
|
681
|
-
for (const entry of entries) {
|
|
682
|
-
const srcPath = path.join(src, entry.name);
|
|
683
|
-
const destPath = path.join(dest, entry.name);
|
|
684
|
-
|
|
685
|
-
if (ignoreList.includes(entry.name)) {
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// Explicitly check for prisma/migrations to ensure it's skipped at any depth if logic changes
|
|
690
|
-
if (entry.name === 'migrations' && srcPath.includes('prisma')) {
|
|
691
|
-
continue;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (entry.isDirectory()) {
|
|
695
|
-
fs.mkdirSync(destPath);
|
|
696
|
-
copyDir(srcPath, destPath);
|
|
697
|
-
} else {
|
|
698
|
-
fs.copyFileSync(srcPath, destPath);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
console.log('\n📂 Copying template files...');
|
|
704
|
-
copyDir(templateDir, projectDir);
|
|
705
|
-
|
|
706
|
-
// Rename gitignore.template to .gitignore
|
|
707
|
-
const gitignoreTemplate = path.join(projectDir, 'gitignore.template');
|
|
708
|
-
if (fs.existsSync(gitignoreTemplate)) {
|
|
709
|
-
fs.renameSync(gitignoreTemplate, path.join(projectDir, '.gitignore'));
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// Update package.json
|
|
713
|
-
console.log('📝 Updating package.json...');
|
|
714
|
-
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
715
|
-
const packageJson = require(packageJsonPath);
|
|
716
|
-
|
|
717
|
-
packageJson.name = projectName;
|
|
718
|
-
// Add lapeh framework version to dependencies to track it like react-router
|
|
719
|
-
packageJson.dependencies = packageJson.dependencies || {};
|
|
720
|
-
|
|
721
|
-
// Smart dependency resolution:
|
|
722
|
-
// If running from node_modules (installed via npm), use the version number.
|
|
723
|
-
// If running locally (dev mode), use the absolute file path.
|
|
724
|
-
const frameworkPackageJson = require(path.join(__dirname, '../package.json'));
|
|
725
|
-
|
|
726
|
-
if (__dirname.includes('node_modules')) {
|
|
727
|
-
packageJson.dependencies["lapeh"] = `^${frameworkPackageJson.version}`;
|
|
728
|
-
} else {
|
|
729
|
-
// Local development
|
|
730
|
-
const lapehPath = path.resolve(__dirname, '..').replace(/\\/g, '/');
|
|
731
|
-
packageJson.dependencies["lapeh"] = `file:${lapehPath}`;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// Ensure prisma CLI is available in devDependencies for the new project
|
|
735
|
-
packageJson.devDependencies = packageJson.devDependencies || {};
|
|
736
|
-
packageJson.devDependencies["prisma"] = "5.22.0";
|
|
737
|
-
packageJson.devDependencies["dotenv"] = "^16.4.5"; // Ensure dotenv is available for seed script
|
|
738
|
-
|
|
739
|
-
// Add missing types for dev
|
|
740
|
-
packageJson.devDependencies["@types/express"] = "^5.0.0";
|
|
741
|
-
packageJson.devDependencies["@types/compression"] = "^1.7.5";
|
|
742
|
-
|
|
743
|
-
packageJson.version = '1.0.0';
|
|
744
|
-
packageJson.description = 'Generated by lapeh';
|
|
745
|
-
delete packageJson.bin; // Remove the bin entry from the generated project
|
|
746
|
-
delete packageJson.repository; // Remove repository info if specific to the template
|
|
747
|
-
|
|
748
|
-
// Update scripts to use lapeh binary
|
|
749
|
-
packageJson.scripts = {
|
|
750
|
-
...packageJson.scripts,
|
|
751
|
-
"postinstall": "node scripts/compile-schema.js && prisma generate",
|
|
752
|
-
"dev": "lapeh dev",
|
|
753
|
-
"start": "lapeh start",
|
|
754
|
-
"build": "lapeh build",
|
|
755
|
-
"start:prod": "lapeh start"
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
759
|
-
|
|
760
|
-
// Update tsconfig.json to support framework-as-dependency
|
|
761
|
-
console.log('🔧 Configuring tsconfig.json...');
|
|
762
|
-
const tsconfigPath = path.join(projectDir, 'tsconfig.json');
|
|
763
|
-
if (fs.existsSync(tsconfigPath)) {
|
|
764
|
-
// Use comment-json or just basic parsing if no comments (standard JSON)
|
|
765
|
-
// Since our template tsconfig is standard JSON, require is fine or JSON.parse
|
|
766
|
-
const tsconfig = require(tsconfigPath);
|
|
767
|
-
|
|
768
|
-
// Update paths
|
|
769
|
-
if (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
|
|
770
|
-
tsconfig.compilerOptions.paths["@lapeh/*"] = ["./node_modules/lapeh/lib/*"];
|
|
771
|
-
// Ensure @lapeh/core/database maps correctly to the actual file location
|
|
772
|
-
tsconfig.compilerOptions.paths["@lapeh/core/database"] = ["./node_modules/lapeh/lib/core/database.ts"];
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// Add baseUrl
|
|
776
|
-
tsconfig.compilerOptions.baseUrl = ".";
|
|
777
|
-
|
|
778
|
-
// Add ts-node ignore configuration
|
|
779
|
-
tsconfig["ts-node"] = {
|
|
780
|
-
"ignore": ["node_modules/(?!lapeh)"]
|
|
781
|
-
};
|
|
782
|
-
|
|
783
|
-
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// Configure prisma.config.ts to use tsconfig-paths
|
|
787
|
-
const prismaConfigPath = path.join(projectDir, 'prisma.config.ts');
|
|
788
|
-
if (fs.existsSync(prismaConfigPath)) {
|
|
789
|
-
console.log('🔧 Configuring prisma.config.ts...');
|
|
790
|
-
let prismaConfigContent = fs.readFileSync(prismaConfigPath, 'utf8');
|
|
791
|
-
prismaConfigContent = prismaConfigContent.replace(
|
|
792
|
-
/seed:\s*"ts-node\s+prisma\/seed\.ts"/g,
|
|
793
|
-
'seed: "ts-node -r tsconfig-paths/register prisma/seed.ts"'
|
|
794
|
-
);
|
|
795
|
-
fs.writeFileSync(prismaConfigPath, prismaConfigContent);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
// Configure prisma/seed.ts imports
|
|
799
|
-
const prismaSeedPath = path.join(projectDir, 'prisma', 'seed.ts');
|
|
800
|
-
if (fs.existsSync(prismaSeedPath)) {
|
|
801
|
-
console.log('🔧 Configuring prisma/seed.ts...');
|
|
802
|
-
let seedContent = fs.readFileSync(prismaSeedPath, 'utf8');
|
|
803
|
-
|
|
804
|
-
// Add dotenv config if missing
|
|
805
|
-
if (!seedContent.includes('dotenv.config()')) {
|
|
806
|
-
seedContent = 'import dotenv from "dotenv";\ndotenv.config();\n\n' + seedContent;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// Update import path
|
|
810
|
-
// We want to import from @lapeh/core/database which maps to lib/prisma.ts
|
|
811
|
-
// The alias is configured in tsconfig.json as "@lapeh/core/database": ["./node_modules/lapeh/lib/prisma.ts"]
|
|
812
|
-
|
|
813
|
-
// If the template uses relative path or old alias, replace it.
|
|
814
|
-
// But wait, the template might already have `import { prisma } from "@lapeh/core/database"`.
|
|
815
|
-
// The error is TS2307: Cannot find module. This means ts-node/tsconfig-paths isn't resolving the alias correctly in the seeder context.
|
|
816
|
-
|
|
817
|
-
// Ensure seed content uses the alias
|
|
818
|
-
seedContent = seedContent.replace(
|
|
819
|
-
/import\s+{\s*prisma\s*}\s+from\s+["']\.\.\/src\/prisma["']/,
|
|
820
|
-
'import { prisma } from "@lapeh/core/database"'
|
|
821
|
-
);
|
|
822
|
-
|
|
823
|
-
// Also handle if it was already replaced or in different format
|
|
824
|
-
if (!seedContent.includes('@lapeh/core/database')) {
|
|
825
|
-
seedContent = seedContent.replace(
|
|
826
|
-
/import\s+{\s*prisma\s*}\s+from\s+["'].*prisma["']/,
|
|
827
|
-
'import { prisma } from "@lapeh/core/database"'
|
|
828
|
-
);
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// Remove default demo data (Pets) from seed.ts ONLY if NOT using --full
|
|
832
|
-
// We want to keep users/roles as they are essential, but remove the demo 'Pets' data
|
|
833
|
-
// This matches from "// 6. Seed Pets" up to the completion log message
|
|
834
|
-
if (!isFull) {
|
|
835
|
-
seedContent = seedContent.replace(
|
|
836
|
-
/\/\/ 6\. Seed Pets[\s\S]*?console\.log\("Finished seeding 50,000 pets\."\);/,
|
|
837
|
-
'// 6. Seed Pets (Skipped by default. Use --full to include demo data)'
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
fs.writeFileSync(prismaSeedPath, seedContent);
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
// Create .env from .env.example with correct DB config
|
|
845
|
-
console.log('⚙️ Configuring environment...');
|
|
846
|
-
const envExamplePath = path.join(projectDir, '.env.example');
|
|
847
|
-
const envPath = path.join(projectDir, '.env');
|
|
848
|
-
const prismaBaseFile = path.join(projectDir, "prisma", "base.prisma.template");
|
|
849
|
-
|
|
850
|
-
if (fs.existsSync(envExamplePath)) {
|
|
851
|
-
let envContent = fs.readFileSync(envExamplePath, 'utf8');
|
|
852
|
-
|
|
853
|
-
// Replace DATABASE_URL and DATABASE_PROVIDER
|
|
854
|
-
if (envContent.includes("DATABASE_URL=")) {
|
|
855
|
-
envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL="${dbUrl}"`);
|
|
856
|
-
envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL="${dbUrl}"`);
|
|
857
|
-
} else {
|
|
858
|
-
envContent += `\nDATABASE_URL="${dbUrl}"`;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (envContent.includes("DATABASE_PROVIDER=")) {
|
|
862
|
-
envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
863
|
-
envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
864
|
-
} else {
|
|
865
|
-
envContent += `\nDATABASE_PROVIDER="${dbProvider}"`;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
fs.writeFileSync(envPath, envContent);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
// Update prisma/base.prisma.template
|
|
872
|
-
console.log("📄 Updating prisma/base.prisma.template...");
|
|
873
|
-
if (fs.existsSync(prismaBaseFile)) {
|
|
874
|
-
let baseContent = fs.readFileSync(prismaBaseFile, "utf8");
|
|
875
|
-
// Replace provider in datasource block
|
|
876
|
-
baseContent = baseContent.replace(
|
|
877
|
-
/(datasource\s+db\s+\{[\s\S]*?provider\s*=\s*")[^"]+(")/,
|
|
878
|
-
`$1${dbProvider}$2`
|
|
879
|
-
);
|
|
880
|
-
fs.writeFileSync(prismaBaseFile, baseContent);
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
// Install dependencies
|
|
884
|
-
console.log('📦 Installing dependencies (this might take a while)...');
|
|
885
|
-
try {
|
|
886
|
-
execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
|
|
887
|
-
} catch (error) {
|
|
888
|
-
console.error('❌ Error installing dependencies.');
|
|
889
|
-
process.exit(1);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// Generate JWT Secret
|
|
893
|
-
console.log('🔑 Generating JWT Secret...');
|
|
894
|
-
try {
|
|
895
|
-
execSync('npm run generate:jwt', { cwd: projectDir, stdio: 'inherit' });
|
|
896
|
-
} catch (error) {
|
|
897
|
-
console.warn('⚠️ Could not generate JWT secret automatically.');
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// Generate Prisma Client & Migrate
|
|
901
|
-
console.log('🗄️ Setting up database...');
|
|
902
|
-
try {
|
|
903
|
-
console.log(' Compiling schema...');
|
|
904
|
-
execSync('node scripts/compile-schema.js', { cwd: projectDir, stdio: 'inherit' });
|
|
905
|
-
|
|
906
|
-
// Try to migrate (this will create the DB if it doesn't exist)
|
|
907
|
-
console.log(' Running migration (creates DB if missing)...');
|
|
908
|
-
if (dbProvider === 'mongodb') {
|
|
909
|
-
execSync('npx prisma db push', { cwd: projectDir, stdio: 'inherit' });
|
|
910
|
-
} else {
|
|
911
|
-
execSync('npx prisma migrate dev --name init_setup', { cwd: projectDir, stdio: 'inherit' });
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
// Seed (Users & Roles are mandatory, Pets are demo data)
|
|
915
|
-
console.log(' Seeding mandatory data (Users, Roles, Permissions)...');
|
|
916
|
-
|
|
917
|
-
if (isFull) {
|
|
918
|
-
try {
|
|
919
|
-
execSync('npm run db:seed', { cwd: projectDir, stdio: 'inherit' });
|
|
920
|
-
} catch (error) {
|
|
921
|
-
console.warn('⚠️ Database setup encountered an issue.');
|
|
922
|
-
console.warn(' You may need to check your .env credentials and run:');
|
|
923
|
-
console.warn(` cd ${projectName}`);
|
|
924
|
-
console.warn(' npm run prisma:migrate');
|
|
925
|
-
}
|
|
926
|
-
} else {
|
|
927
|
-
console.log(' ℹ️ Skipping database seeding (use --full to seed default data)...');
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
console.log(`\n✅ Project ${projectName} created successfully!`);
|
|
931
|
-
console.log('\nNext steps:');
|
|
932
|
-
console.log(` cd ${projectName}`);
|
|
933
|
-
console.log(' npm run dev');
|
|
934
|
-
} catch (error) {
|
|
935
|
-
console.error('❌ Error setting up database:', error.message);
|
|
936
|
-
console.log(`\n✅ Project ${projectName} created, but database setup failed.`);
|
|
937
|
-
console.log(' Please check your database credentials in .env and run:');
|
|
938
|
-
console.log(` cd ${projectName}`);
|
|
939
|
-
console.log(' npm run prisma:migrate');
|
|
940
|
-
}
|
|
941
|
-
})();
|
|
942
|
-
}
|
|
943
|
-
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const command = args[0];
|
|
10
|
+
|
|
11
|
+
// Telemetry Logic
|
|
12
|
+
async function sendTelemetry(cmd, errorInfo = null) {
|
|
13
|
+
try {
|
|
14
|
+
const os = require('os');
|
|
15
|
+
|
|
16
|
+
const payload = {
|
|
17
|
+
command: cmd,
|
|
18
|
+
nodeVersion: process.version,
|
|
19
|
+
osPlatform: os.platform(),
|
|
20
|
+
osRelease: os.release(),
|
|
21
|
+
timestamp: new Date().toISOString()
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (errorInfo) {
|
|
25
|
+
payload.error = errorInfo.message;
|
|
26
|
+
payload.stack = errorInfo.stack;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const data = JSON.stringify(payload);
|
|
30
|
+
|
|
31
|
+
// Parse URL from env or use default
|
|
32
|
+
const apiUrl = process.env.LAPEH_API_URL || 'https://lapeh-doc.vercel.app/api/telemetry';
|
|
33
|
+
const url = new URL(apiUrl);
|
|
34
|
+
const isHttps = url.protocol === 'https:';
|
|
35
|
+
const client = isHttps ? require('https') : require('http');
|
|
36
|
+
|
|
37
|
+
const options = {
|
|
38
|
+
hostname: url.hostname,
|
|
39
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
40
|
+
path: url.pathname,
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
'Content-Length': Buffer.byteLength(data)
|
|
45
|
+
},
|
|
46
|
+
timeout: 2000 // Slightly longer for crash reports
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const req = client.request(options, (res) => {
|
|
50
|
+
res.resume();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
req.on('error', (e) => {
|
|
54
|
+
// Silent fail
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
req.write(data);
|
|
58
|
+
req.end();
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// Silent fail
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Global Error Handler for Crash Reporting
|
|
65
|
+
process.on('uncaughtException', async (err) => {
|
|
66
|
+
console.error('❌ Unexpected Error:', err);
|
|
67
|
+
console.log('📝 Sending crash report...');
|
|
68
|
+
try {
|
|
69
|
+
sendTelemetry(command || 'unknown', err);
|
|
70
|
+
|
|
71
|
+
// Give it a moment to send
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}, 1000);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Send telemetry for every command (only if not crashing immediately)
|
|
81
|
+
sendTelemetry(command || 'init');
|
|
82
|
+
|
|
83
|
+
switch (command) {
|
|
84
|
+
case 'dev':
|
|
85
|
+
runDev();
|
|
86
|
+
break;
|
|
87
|
+
case 'start':
|
|
88
|
+
runStart();
|
|
89
|
+
break;
|
|
90
|
+
case 'build':
|
|
91
|
+
runBuild();
|
|
92
|
+
break;
|
|
93
|
+
case 'upgrade':
|
|
94
|
+
(async () => {
|
|
95
|
+
await upgradeProject();
|
|
96
|
+
})();
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
createProject();
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function runDev() {
|
|
104
|
+
console.log('🚀 Starting Lapeh in development mode...');
|
|
105
|
+
try {
|
|
106
|
+
// Generate Prisma Client before starting
|
|
107
|
+
console.log('🔄 Generating Prisma Client...');
|
|
108
|
+
const compileSchemaPath = path.join(process.cwd(), 'scripts/compile-schema.js');
|
|
109
|
+
if (fs.existsSync(compileSchemaPath)) {
|
|
110
|
+
try {
|
|
111
|
+
execSync('node scripts/compile-schema.js', { stdio: 'inherit' });
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.warn('⚠️ Failed to run compile-schema.js', e.message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
execSync('npx prisma generate', { stdio: 'inherit' });
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.warn('⚠️ Failed to run prisma generate. Continuing...', e.message);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const tsNodePath = require.resolve('ts-node/register');
|
|
124
|
+
const tsConfigPathsPath = require.resolve('tsconfig-paths/register');
|
|
125
|
+
|
|
126
|
+
// Resolve bootstrap file
|
|
127
|
+
// 1. Try to find it in the current project's node_modules (preferred)
|
|
128
|
+
const localBootstrapPath = path.join(process.cwd(), 'node_modules/lapeh/lib/bootstrap.ts');
|
|
129
|
+
|
|
130
|
+
// 2. Fallback to relative to this script (if running from source or global cache without local install)
|
|
131
|
+
const fallbackBootstrapPath = path.resolve(__dirname, '../lib/bootstrap.ts');
|
|
132
|
+
|
|
133
|
+
const bootstrapPath = fs.existsSync(localBootstrapPath) ? localBootstrapPath : fallbackBootstrapPath;
|
|
134
|
+
|
|
135
|
+
// We execute a script that requires ts-node to run lib/bootstrap.ts
|
|
136
|
+
// Use JSON.stringify to properly escape paths for the shell command
|
|
137
|
+
const nodeArgs = `-r ${JSON.stringify(tsNodePath)} -r ${JSON.stringify(tsConfigPathsPath)} ${JSON.stringify(bootstrapPath)}`;
|
|
138
|
+
const isWin = process.platform === 'win32';
|
|
139
|
+
|
|
140
|
+
let cmd;
|
|
141
|
+
if (isWin) {
|
|
142
|
+
// On Windows, escape inner quotes
|
|
143
|
+
const escapedArgs = nodeArgs.replace(/"/g, '\\"');
|
|
144
|
+
cmd = `npx nodemon --watch src --watch lib --ext ts,json --exec "node ${escapedArgs}"`;
|
|
145
|
+
} else {
|
|
146
|
+
// On Linux/Mac, use single quotes for the outer wrapper
|
|
147
|
+
cmd = `npx nodemon --watch src --watch lib --ext ts,json --exec 'node ${nodeArgs}'`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
151
|
+
} catch (error) {
|
|
152
|
+
// Ignore error
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function runStart() {
|
|
157
|
+
console.log('🚀 Starting Lapeh production server...');
|
|
158
|
+
|
|
159
|
+
let bootstrapPath;
|
|
160
|
+
try {
|
|
161
|
+
const projectNodeModules = path.join(process.cwd(), 'node_modules');
|
|
162
|
+
const lapehDist = path.join(projectNodeModules, 'lapeh', 'dist', 'lib', 'bootstrap.js');
|
|
163
|
+
const lapehLib = path.join(projectNodeModules, 'lapeh', 'lib', 'bootstrap.js');
|
|
164
|
+
|
|
165
|
+
if (fs.existsSync(lapehDist)) {
|
|
166
|
+
bootstrapPath = lapehDist;
|
|
167
|
+
} else if (fs.existsSync(lapehLib)) {
|
|
168
|
+
bootstrapPath = path.resolve(__dirname, '../lib/bootstrap.js');
|
|
169
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
170
|
+
bootstrapPath = path.resolve(__dirname, '../dist/lib/bootstrap.js');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const frameworkBootstrap = require('../lib/bootstrap');
|
|
175
|
+
frameworkBootstrap.bootstrap();
|
|
176
|
+
return;
|
|
177
|
+
|
|
178
|
+
} catch (e) {
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const possiblePaths = [
|
|
182
|
+
path.join(__dirname, '../lib/bootstrap.js'),
|
|
183
|
+
path.join(__dirname, '../dist/lib/bootstrap.js'),
|
|
184
|
+
path.join(process.cwd(), 'node_modules/lapeh/lib/bootstrap.js')
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
bootstrapPath = possiblePaths.find(p => fs.existsSync(p));
|
|
188
|
+
|
|
189
|
+
if (!bootstrapPath) {
|
|
190
|
+
console.error('❌ Could not find Lapeh bootstrap file.');
|
|
191
|
+
console.error(' Searched in:', possiblePaths);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let cmd;
|
|
196
|
+
if (bootstrapPath.endsWith('.ts')) {
|
|
197
|
+
let tsNodePath;
|
|
198
|
+
let tsConfigPathsPath;
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const projectNodeModules = path.join(process.cwd(), 'node_modules');
|
|
202
|
+
tsNodePath = require.resolve('ts-node/register', { paths: [projectNodeModules, __dirname] });
|
|
203
|
+
tsConfigPathsPath = require.resolve('tsconfig-paths/register', { paths: [projectNodeModules, __dirname] });
|
|
204
|
+
} catch (e) {
|
|
205
|
+
try {
|
|
206
|
+
tsNodePath = require.resolve('ts-node/register');
|
|
207
|
+
tsConfigPathsPath = require.resolve('tsconfig-paths/register');
|
|
208
|
+
} catch (e2) {
|
|
209
|
+
console.warn('⚠️ Could not resolve ts-node/register. Trying npx...');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (tsNodePath && tsConfigPathsPath) {
|
|
214
|
+
const script = `require(${JSON.stringify(bootstrapPath)}).bootstrap()`;
|
|
215
|
+
cmd = `node -r ${JSON.stringify(tsNodePath)} -r ${JSON.stringify(tsConfigPathsPath)} -e ${JSON.stringify(script)}`;
|
|
216
|
+
} else {
|
|
217
|
+
const script = `require(${JSON.stringify(bootstrapPath)}).bootstrap()`;
|
|
218
|
+
cmd = `npx ts-node -r tsconfig-paths/register -e ${JSON.stringify(script)}`;
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
const script = `require(${JSON.stringify(bootstrapPath)}).bootstrap()`;
|
|
222
|
+
cmd = `node -e ${JSON.stringify(script)}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
execSync(cmd, {
|
|
226
|
+
stdio: 'inherit',
|
|
227
|
+
env: { ...process.env, NODE_ENV: 'production' }
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function runBuild() {
|
|
232
|
+
console.log('🛠️ Building Lapeh project...');
|
|
233
|
+
|
|
234
|
+
const compileSchemaPath = path.join(process.cwd(), 'scripts/compile-schema.js');
|
|
235
|
+
if (fs.existsSync(compileSchemaPath)) {
|
|
236
|
+
try {
|
|
237
|
+
execSync('node scripts/compile-schema.js', { stdio: 'inherit' });
|
|
238
|
+
} catch (e) {
|
|
239
|
+
console.error('❌ Failed to compile schema.');
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
execSync('npx prisma generate', { stdio: 'inherit' });
|
|
246
|
+
} catch (e) {
|
|
247
|
+
console.error('❌ Failed to generate prisma client.');
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
execSync('npx tsc -p tsconfig.build.json && npx tsc-alias -p tsconfig.build.json', { stdio: 'inherit' });
|
|
253
|
+
} catch (e) {
|
|
254
|
+
console.error('❌ Build failed.');
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
console.log('✅ Build complete.');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function upgradeProject() {
|
|
262
|
+
const currentDir = process.cwd();
|
|
263
|
+
const templateDir = path.join(__dirname, '..');
|
|
264
|
+
|
|
265
|
+
console.log(`🚀 Upgrading Lapeh project in ${currentDir}...`);
|
|
266
|
+
|
|
267
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
268
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
269
|
+
console.error('❌ No package.json found. Are you in the root of a Lapeh project?');
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const filesToSync = [
|
|
274
|
+
'lib',
|
|
275
|
+
'scripts',
|
|
276
|
+
'docker-compose.yml',
|
|
277
|
+
'.env.example',
|
|
278
|
+
'.vscode',
|
|
279
|
+
'tsconfig.json',
|
|
280
|
+
'README.md',
|
|
281
|
+
'ecosystem.config.js',
|
|
282
|
+
'src/redis.ts',
|
|
283
|
+
'src/prisma.ts',
|
|
284
|
+
'prisma/base.prisma.template', // Sync base template for upgrade
|
|
285
|
+
'prisma.config.ts' // Sync prisma config for upgrade
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
function syncDirectory(src, dest, clean = false) {
|
|
289
|
+
if (!fs.existsSync(src)) return;
|
|
290
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
291
|
+
|
|
292
|
+
const srcEntries = fs.readdirSync(src, { withFileTypes: true });
|
|
293
|
+
const srcEntryNames = new Set();
|
|
294
|
+
|
|
295
|
+
for (const entry of srcEntries) {
|
|
296
|
+
srcEntryNames.add(entry.name);
|
|
297
|
+
const srcPath = path.join(src, entry.name);
|
|
298
|
+
const destPath = path.join(dest, entry.name);
|
|
299
|
+
|
|
300
|
+
if (entry.isDirectory()) {
|
|
301
|
+
syncDirectory(srcPath, destPath, clean);
|
|
302
|
+
} else {
|
|
303
|
+
fs.copyFileSync(srcPath, destPath);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (clean) {
|
|
308
|
+
const destEntries = fs.readdirSync(dest, { withFileTypes: true });
|
|
309
|
+
for (const entry of destEntries) {
|
|
310
|
+
if (!srcEntryNames.has(entry.name)) {
|
|
311
|
+
const destPath = path.join(dest, entry.name);
|
|
312
|
+
console.log(`🗑️ Removing obsolete file/directory: ${destPath}`);
|
|
313
|
+
if (entry.isDirectory()) {
|
|
314
|
+
fs.rmSync(destPath, { recursive: true, force: true });
|
|
315
|
+
} else {
|
|
316
|
+
fs.unlinkSync(destPath);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Rename .model -> .prisma (Legacy migration)
|
|
324
|
+
const modelsDir = path.join(currentDir, 'src', 'models');
|
|
325
|
+
if (fs.existsSync(modelsDir)) {
|
|
326
|
+
console.log('🔄 Checking for legacy .model files...');
|
|
327
|
+
const files = fs.readdirSync(modelsDir);
|
|
328
|
+
let renamedCount = 0;
|
|
329
|
+
files.forEach(file => {
|
|
330
|
+
if (file.endsWith('.model')) {
|
|
331
|
+
const oldPath = path.join(modelsDir, file);
|
|
332
|
+
const newPath = path.join(modelsDir, file.replace('.model', '.prisma'));
|
|
333
|
+
fs.renameSync(oldPath, newPath);
|
|
334
|
+
renamedCount++;
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
if (renamedCount > 0) {
|
|
338
|
+
console.log(`✅ Migrated ${renamedCount} files from .model to .prisma`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
for (const item of filesToSync) {
|
|
343
|
+
const srcPath = path.join(templateDir, item);
|
|
344
|
+
const destPath = path.join(currentDir, item);
|
|
345
|
+
|
|
346
|
+
if (fs.existsSync(srcPath)) {
|
|
347
|
+
const stats = fs.statSync(srcPath);
|
|
348
|
+
if (stats.isDirectory()) {
|
|
349
|
+
console.log(`🔄 Syncing directory ${item}...`);
|
|
350
|
+
syncDirectory(srcPath, destPath, item === 'lib');
|
|
351
|
+
} else {
|
|
352
|
+
console.log(`🔄 Updating file ${item}...`);
|
|
353
|
+
const destDir = path.dirname(destPath);
|
|
354
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
355
|
+
fs.copyFileSync(srcPath, destPath);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
console.log('📝 Updating package.json...');
|
|
361
|
+
const currentPackageJson = require(packageJsonPath);
|
|
362
|
+
const templatePackageJson = require(path.join(templateDir, 'package.json'));
|
|
363
|
+
|
|
364
|
+
currentPackageJson.scripts = {
|
|
365
|
+
...currentPackageJson.scripts,
|
|
366
|
+
...templatePackageJson.scripts,
|
|
367
|
+
"dev": "lapeh dev",
|
|
368
|
+
"start": "lapeh start",
|
|
369
|
+
"build": "lapeh build",
|
|
370
|
+
"start:prod": "lapeh start"
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
currentPackageJson.dependencies = {
|
|
374
|
+
...currentPackageJson.dependencies,
|
|
375
|
+
...templatePackageJson.dependencies
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
currentPackageJson.devDependencies = {
|
|
379
|
+
...currentPackageJson.devDependencies,
|
|
380
|
+
...templatePackageJson.devDependencies
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
currentPackageJson.dependencies["lapeh"] = "file:../";
|
|
384
|
+
|
|
385
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(currentPackageJson, null, 2));
|
|
386
|
+
|
|
387
|
+
console.log('🔧 Configuring tsconfig.json...');
|
|
388
|
+
const tsconfigPath = path.join(currentDir, 'tsconfig.json');
|
|
389
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
390
|
+
const tsconfig = require(tsconfigPath);
|
|
391
|
+
if (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
|
|
392
|
+
tsconfig.compilerOptions.paths["@lapeh/*"] = ["./node_modules/lapeh/lib/*"];
|
|
393
|
+
}
|
|
394
|
+
tsconfig["ts-node"] = {
|
|
395
|
+
"ignore": ["node_modules/(?!lapeh)"]
|
|
396
|
+
};
|
|
397
|
+
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
console.log('📦 Installing updated dependencies...');
|
|
401
|
+
try {
|
|
402
|
+
execSync('npm install', { cwd: currentDir, stdio: 'inherit' });
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.error('❌ Error installing dependencies.');
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
console.log('\n✅ Upgrade completed successfully!');
|
|
409
|
+
console.log(' Please check your .env file against .env.example for any new required variables.');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function createProject() {
|
|
413
|
+
const projectName = args.find(arg => !arg.startsWith('-'));
|
|
414
|
+
const isFull = args.includes('--full');
|
|
415
|
+
const useDefaults = args.includes('--defaults') || args.includes('--default') || args.includes('-y');
|
|
416
|
+
|
|
417
|
+
if (!projectName) {
|
|
418
|
+
console.error('❌ Please specify the project name:');
|
|
419
|
+
console.error(' npx lapeh-cli <project-name> [--full] [--defaults|-y]');
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const currentDir = process.cwd();
|
|
424
|
+
const projectDir = path.join(currentDir, projectName);
|
|
425
|
+
const templateDir = path.join(__dirname, '..');
|
|
426
|
+
|
|
427
|
+
if (fs.existsSync(projectDir)) {
|
|
428
|
+
console.error(`❌ Directory ${projectName} already exists.`);
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const rl = readline.createInterface({
|
|
433
|
+
input: process.stdin,
|
|
434
|
+
output: process.stdout,
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const ask = (query, defaultVal) => {
|
|
438
|
+
return new Promise((resolve) => {
|
|
439
|
+
rl.question(`${query} ${defaultVal ? `[${defaultVal}]` : ""}: `, (answer) => {
|
|
440
|
+
resolve(answer.trim() || defaultVal);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const selectOption = async (query, options) => {
|
|
446
|
+
console.log(query);
|
|
447
|
+
options.forEach((opt, idx) => {
|
|
448
|
+
console.log(` [${opt.key}] ${opt.label}`);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
while (true) {
|
|
452
|
+
const answer = await ask(">", options[0].key);
|
|
453
|
+
const selected = options.find(o => o.key.toLowerCase() === answer.toLowerCase());
|
|
454
|
+
if (selected) return selected;
|
|
455
|
+
|
|
456
|
+
const byLabel = options.find(o => o.label.toLowerCase().includes(answer.toLowerCase()));
|
|
457
|
+
if (byLabel) return byLabel;
|
|
458
|
+
|
|
459
|
+
console.log("Pilihan tidak valid. Silakan coba lagi.");
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
(async () => {
|
|
464
|
+
console.log(`🚀 Creating a new API Lapeh project in ${projectDir}...`);
|
|
465
|
+
fs.mkdirSync(projectDir);
|
|
466
|
+
|
|
467
|
+
console.log("\n--- ORM Configuration ---");
|
|
468
|
+
let usePrisma = true;
|
|
469
|
+
|
|
470
|
+
if (!useDefaults) {
|
|
471
|
+
const ormChoice = await selectOption("Apakah ingin menggunakan ORM (Prisma)?", [
|
|
472
|
+
{ key: "Y", label: "Ya (Disarankan)" },
|
|
473
|
+
{ key: "T", label: "Tidak (Setup Manual)" }
|
|
474
|
+
]);
|
|
475
|
+
usePrisma = ormChoice.key === "Y";
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
let dbType, host, port, user, password, dbName;
|
|
479
|
+
let dbUrl = "";
|
|
480
|
+
let dbProvider = "postgresql";
|
|
481
|
+
|
|
482
|
+
if (usePrisma) {
|
|
483
|
+
if (useDefaults) {
|
|
484
|
+
console.log("ℹ️ Using default database configuration (PostgreSQL)...");
|
|
485
|
+
dbType = { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" };
|
|
486
|
+
host = "localhost";
|
|
487
|
+
port = "5432";
|
|
488
|
+
user = "postgres";
|
|
489
|
+
password = "password";
|
|
490
|
+
dbName = projectName.replace(/-/g, '_');
|
|
491
|
+
} else {
|
|
492
|
+
console.log("\n--- Database Configuration ---");
|
|
493
|
+
dbType = await selectOption("Database apa yang akan digunakan?", [
|
|
494
|
+
{ key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
|
|
495
|
+
{ key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
|
|
496
|
+
]);
|
|
497
|
+
|
|
498
|
+
host = await ask("Database Host", "localhost");
|
|
499
|
+
port = await ask("Database Port", dbType.defaultPort);
|
|
500
|
+
user = await ask("Database User", "root");
|
|
501
|
+
password = await ask("Database Password", "");
|
|
502
|
+
dbName = await ask("Database Name", projectName.replace(/-/g, '_'));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
dbProvider = dbType.provider;
|
|
506
|
+
if (dbType.key === "pgsql") {
|
|
507
|
+
dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
|
|
508
|
+
} else if (dbType.key === "mysql") {
|
|
509
|
+
dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
console.log("ℹ️ Skipping ORM setup. You will need to configure your own database access.");
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const ignoreList = [
|
|
516
|
+
'node_modules', 'dist', '.git', '.env', 'bin', 'lib',
|
|
517
|
+
'package-lock.json', '.DS_Store', 'prisma/migrations',
|
|
518
|
+
'prisma/dev.db', 'prisma/dev.db-journal', 'website',
|
|
519
|
+
'init', 'test-local-run', 'coverage', projectName
|
|
520
|
+
];
|
|
521
|
+
|
|
522
|
+
function copyDir(src, dest) {
|
|
523
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
524
|
+
for (const entry of entries) {
|
|
525
|
+
if (ignoreList.includes(entry.name)) continue;
|
|
526
|
+
const srcPath = path.join(src, entry.name);
|
|
527
|
+
const destPath = path.join(dest, entry.name);
|
|
528
|
+
|
|
529
|
+
if (entry.name === 'migrations' && srcPath.includes('prisma')) continue;
|
|
530
|
+
|
|
531
|
+
if (entry.isDirectory()) {
|
|
532
|
+
fs.mkdirSync(destPath);
|
|
533
|
+
copyDir(srcPath, destPath);
|
|
534
|
+
} else {
|
|
535
|
+
fs.copyFileSync(srcPath, destPath);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
console.log('\n📂 Copying template files...');
|
|
541
|
+
copyDir(templateDir, projectDir);
|
|
542
|
+
|
|
543
|
+
const gitignoreTemplate = path.join(projectDir, 'gitignore.template');
|
|
544
|
+
if (fs.existsSync(gitignoreTemplate)) {
|
|
545
|
+
fs.renameSync(gitignoreTemplate, path.join(projectDir, '.gitignore'));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
console.log('⚙️ Configuring environment...');
|
|
549
|
+
const envExamplePath = path.join(projectDir, '.env.example');
|
|
550
|
+
const envPath = path.join(projectDir, '.env');
|
|
551
|
+
|
|
552
|
+
if (fs.existsSync(envExamplePath)) {
|
|
553
|
+
let envContent = fs.readFileSync(envExamplePath, 'utf8');
|
|
554
|
+
if (usePrisma) {
|
|
555
|
+
envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL="${dbUrl}"`);
|
|
556
|
+
envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL="${dbUrl}"`);
|
|
557
|
+
envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
558
|
+
envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
559
|
+
} else {
|
|
560
|
+
envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL=""`);
|
|
561
|
+
envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL=""`);
|
|
562
|
+
envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="none"`);
|
|
563
|
+
envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="none"`);
|
|
564
|
+
}
|
|
565
|
+
fs.writeFileSync(envPath, envContent);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
console.log('📝 Updating package.json...');
|
|
569
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
570
|
+
const packageJson = require(packageJsonPath);
|
|
571
|
+
packageJson.name = projectName;
|
|
572
|
+
|
|
573
|
+
const frameworkPackageJson = require(path.join(__dirname, '../package.json'));
|
|
574
|
+
if (__dirname.includes('node_modules')) {
|
|
575
|
+
packageJson.dependencies["lapeh"] = `^${frameworkPackageJson.version}`;
|
|
576
|
+
} else {
|
|
577
|
+
const lapehPath = path.resolve(__dirname, '..').replace(/\\/g, '/');
|
|
578
|
+
packageJson.dependencies["lapeh"] = `file:${lapehPath}`;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
packageJson.version = '1.0.0';
|
|
582
|
+
delete packageJson.bin;
|
|
583
|
+
|
|
584
|
+
packageJson.scripts = {
|
|
585
|
+
...packageJson.scripts,
|
|
586
|
+
"dev": "lapeh dev",
|
|
587
|
+
"start": "lapeh start",
|
|
588
|
+
"build": "lapeh build",
|
|
589
|
+
"start:prod": "lapeh start"
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
593
|
+
|
|
594
|
+
// Update tsconfig.json for aliases
|
|
595
|
+
const tsconfigPath = path.join(projectDir, 'tsconfig.json');
|
|
596
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
597
|
+
try {
|
|
598
|
+
const tsconfig = require(tsconfigPath);
|
|
599
|
+
if (!tsconfig.compilerOptions) tsconfig.compilerOptions = {};
|
|
600
|
+
if (!tsconfig.compilerOptions.paths) tsconfig.compilerOptions.paths = {};
|
|
601
|
+
|
|
602
|
+
// Ensure @lapeh/* points to the installed package
|
|
603
|
+
tsconfig.compilerOptions.paths["@lapeh/*"] = ["./node_modules/lapeh/dist/lib/*"];
|
|
604
|
+
|
|
605
|
+
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
|
|
606
|
+
} catch (e) {
|
|
607
|
+
console.warn('⚠️ Failed to update tsconfig.json aliases.');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const prismaBaseFile = path.join(projectDir, "prisma", "base.prisma.template");
|
|
612
|
+
if (usePrisma && fs.existsSync(prismaBaseFile)) {
|
|
613
|
+
let baseContent = fs.readFileSync(prismaBaseFile, "utf8");
|
|
614
|
+
// Update provider
|
|
615
|
+
baseContent = baseContent.replace(
|
|
616
|
+
/(datasource\s+db\s+\{[\s\S]*?provider\s*=\s*")[^"]+(")/,
|
|
617
|
+
`$1${dbProvider}$2`
|
|
618
|
+
);
|
|
619
|
+
fs.writeFileSync(prismaBaseFile, baseContent);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
console.log('📦 Installing dependencies...');
|
|
623
|
+
try {
|
|
624
|
+
execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
|
|
625
|
+
} catch (e) {
|
|
626
|
+
console.error('❌ Error installing dependencies.');
|
|
627
|
+
process.exit(1);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
try {
|
|
631
|
+
execSync('npm run generate:jwt', { cwd: projectDir, stdio: 'inherit' });
|
|
632
|
+
} catch (e) {}
|
|
633
|
+
|
|
634
|
+
if (usePrisma) {
|
|
635
|
+
console.log('🗄️ Setting up database...');
|
|
636
|
+
try {
|
|
637
|
+
execSync('node scripts/compile-schema.js', { cwd: projectDir, stdio: 'inherit' });
|
|
638
|
+
|
|
639
|
+
console.log(' Running migration...');
|
|
640
|
+
if (dbProvider === 'mongodb') {
|
|
641
|
+
execSync('npx prisma db push', { cwd: projectDir, stdio: 'inherit' });
|
|
642
|
+
} else {
|
|
643
|
+
// For Prisma v7, ensure prisma.config.ts is used/detected
|
|
644
|
+
execSync('npx prisma migrate dev --name init_setup', { cwd: projectDir, stdio: 'inherit' });
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
let runSeed = false;
|
|
648
|
+
if (!useDefaults) {
|
|
649
|
+
const seedChoice = await selectOption("Jalankan seeder?", [
|
|
650
|
+
{ key: "Y", label: "Ya" },
|
|
651
|
+
{ key: "T", label: "Tidak" }
|
|
652
|
+
]);
|
|
653
|
+
runSeed = seedChoice.key === "Y";
|
|
654
|
+
} else {
|
|
655
|
+
runSeed = isFull;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (runSeed) {
|
|
659
|
+
console.log(' Seeding database...');
|
|
660
|
+
execSync('npm run db:seed', { cwd: projectDir, stdio: 'inherit' });
|
|
661
|
+
}
|
|
662
|
+
} catch (e) {
|
|
663
|
+
console.warn('⚠️ Database setup failed. Check .env and run manually.');
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
console.log(`\n✅ Project ${projectName} created successfully!`);
|
|
668
|
+
rl.close();
|
|
669
|
+
})();
|
|
670
|
+
}
|