wad-lab 1.2.1 → 1.2.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 +174 -51
- package/package.json +1 -1
- package/templates/servlet-basic/script.sh +89 -0
- package/templates/servlet-flow/script.sh +89 -0
- package/templates/servlet-jdbc/script.sh +89 -0
- package/templates/servlet-session-tracking/script.sh +89 -0
- package/templates/servlet-voter-eligibility/script.sh +89 -0
package/bin/index.js
CHANGED
|
@@ -41,47 +41,85 @@ const frontendOptions = [
|
|
|
41
41
|
{ name: 'vue-axios-interceptor (Vue 3 + Axios Interceptors + Toast Alerts)', value: 'vue-axios-interceptor' }
|
|
42
42
|
];
|
|
43
43
|
|
|
44
|
+
const featuredTemplates = [
|
|
45
|
+
{
|
|
46
|
+
key: '1',
|
|
47
|
+
value: 'fullstack-springboot-vue',
|
|
48
|
+
name: '[1] fullstack-springboot-vue (Spring Boot 2-Model API + Selectable Vue Frontend)',
|
|
49
|
+
desc: 'Complete stack: 2-model Spring Boot backend + matching Vue frontend (Router/Pinia/Props)'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: '2',
|
|
53
|
+
value: 'springboot-2model-1controller-api',
|
|
54
|
+
name: '[2] springboot-2model-1controller-api (2 Models Dept & Emp, 2 Repos, 1 Service)',
|
|
55
|
+
desc: 'Extensible 2-model Spring Boot architecture (Department + Employee) with full CRUD & PATCH'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: '3',
|
|
59
|
+
value: 'springboot-mongo-aggregation-join',
|
|
60
|
+
name: '[3] springboot-mongo-aggregation-join ($lookup JOIN, @Query, @Aggregation)',
|
|
61
|
+
desc: 'MongoDB $lookup multi-collection JOIN, pipeline stages ($match/$group), and @Query filters'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: '4',
|
|
65
|
+
value: 'vue-axios-router-pinia-props',
|
|
66
|
+
name: '[4] vue-axios-router-pinia-props (Matching Frontend: Router + Pinia + Props/Emits)',
|
|
67
|
+
desc: 'Matching frontend for 2-model backend: Vue Router + Pinia centralized store + Props/Emits'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: '5',
|
|
71
|
+
value: 'vue-axios-router-props',
|
|
72
|
+
name: '[5] vue-axios-router-props (Matching Frontend: Router Views + Props/Emits)',
|
|
73
|
+
desc: 'Matching frontend for 2-model backend: Vue Router pages + reusable components with Props/Emits'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: '6',
|
|
77
|
+
value: 'servlet-jdbc',
|
|
78
|
+
name: '[6] servlet-jdbc (Tomcat 9 + MySQL Database Connection via JDBC)',
|
|
79
|
+
desc: 'Tomcat Servlet with MySQL Driver, JDBC connection, SQL statement execution, and HTML table'
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: '7',
|
|
83
|
+
value: 'servlet-flow',
|
|
84
|
+
name: '[7] servlet-flow (Tomcat 9 Execution Flow with GET/POST Form — No DB)',
|
|
85
|
+
desc: 'Pure servlet without DB: HTML form submission, doGet / doPost processing, and response render'
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
|
|
44
89
|
const categories = [
|
|
45
90
|
{
|
|
46
91
|
id: 'fullstack',
|
|
47
92
|
title: 'Fullstack Web Applications',
|
|
48
|
-
icon: '🌐',
|
|
49
93
|
desc: 'Integrated Spring Boot + MongoDB + Vue 3 apps'
|
|
50
94
|
},
|
|
51
95
|
{
|
|
52
96
|
id: 'springboot',
|
|
53
97
|
title: 'Spring Boot Backend APIs',
|
|
54
|
-
icon: '🍃',
|
|
55
98
|
desc: 'REST APIs, MongoDB, Aggregations, Join & MockMvc'
|
|
56
99
|
},
|
|
57
100
|
{
|
|
58
101
|
id: 'servlet',
|
|
59
102
|
title: 'Java Servlets & Tomcat 9',
|
|
60
|
-
icon: '☕',
|
|
61
103
|
desc: 'Classic Tomcat Servlets, JDBC, Sessions & Web.xml'
|
|
62
104
|
},
|
|
63
105
|
{
|
|
64
106
|
id: 'vue-axios',
|
|
65
107
|
title: 'Vue 3 + Axios HTTP Variants',
|
|
66
|
-
icon: '⚡',
|
|
67
108
|
desc: 'All 10+ architectural combinations of Vue 3 with Axios'
|
|
68
109
|
},
|
|
69
110
|
{
|
|
70
111
|
id: 'vue-core',
|
|
71
112
|
title: 'Vue 3 Core Portals & State',
|
|
72
|
-
icon: '🎨',
|
|
73
113
|
desc: 'Router, Pinia, Native Fetch & Complete UI Portals'
|
|
74
114
|
},
|
|
75
115
|
{
|
|
76
116
|
id: 'games',
|
|
77
117
|
title: 'Interactive Games (JS & Vue)',
|
|
78
|
-
icon: '🎮',
|
|
79
118
|
desc: 'Logic, DOM & State Management Games'
|
|
80
119
|
},
|
|
81
120
|
{
|
|
82
121
|
id: 'html-css',
|
|
83
122
|
title: 'HTML5 & CSS3 Foundations',
|
|
84
|
-
icon: '📄',
|
|
85
123
|
desc: 'Form Validation, DOM Manipulation & 50 CSS Properties'
|
|
86
124
|
}
|
|
87
125
|
];
|
|
@@ -385,7 +423,7 @@ const templates = [
|
|
|
385
423
|
const PAGE_SIZE = 12;
|
|
386
424
|
const templatesRoot = path.join(__dirname, '..', 'templates');
|
|
387
425
|
|
|
388
|
-
let currentView = 'MAIN'; // 'MAIN' | 'CATEGORY' | 'SEARCH' | 'ALL'
|
|
426
|
+
let currentView = 'MAIN'; // 'MAIN' | 'CATEGORY_LIST' | 'CATEGORY' | 'SEARCH' | 'ALL'
|
|
389
427
|
let activeCategory = null;
|
|
390
428
|
let searchQuery = '';
|
|
391
429
|
let selectedIndex = 0;
|
|
@@ -394,19 +432,29 @@ let isPrompting = false;
|
|
|
394
432
|
|
|
395
433
|
function getActiveItemList() {
|
|
396
434
|
if (currentView === 'MAIN') {
|
|
397
|
-
const list =
|
|
435
|
+
const list = featuredTemplates.map(f => ({
|
|
436
|
+
type: 'featured',
|
|
437
|
+
id: f.value,
|
|
438
|
+
name: f.name,
|
|
439
|
+
desc: f.desc
|
|
440
|
+
}));
|
|
441
|
+
list.push({ type: 'action', id: 'categories', name: '[C] Browse by Category (7 Groups, 56 Templates)', desc: 'Explore all 56 templates categorized into 7 groups' });
|
|
442
|
+
list.push({ type: 'action', id: 'search', name: '[S] Search by keyword across all 56 templates', desc: 'Type any keyword to find templates instantly' });
|
|
443
|
+
list.push({ type: 'action', id: 'all_view', name: '[L] View all 56 templates (grouped catalog)', desc: 'Browse the entire catalog with section dividers' });
|
|
444
|
+
list.push({ type: 'action', id: 'unpack_all', name: '[A] Unpack all 56 templates in 1-click', desc: 'Copy every single template into target folder' });
|
|
445
|
+
return list;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (currentView === 'CATEGORY_LIST') {
|
|
449
|
+
return categories.map((cat, idx) => {
|
|
398
450
|
const count = templates.filter(t => t.category === cat.id).length;
|
|
399
451
|
return {
|
|
400
|
-
type: '
|
|
452
|
+
type: 'cat_entry',
|
|
401
453
|
id: cat.id,
|
|
402
|
-
name: `[${idx + 1}] ${cat.
|
|
454
|
+
name: `[${idx + 1}] ${cat.title} (${count} templates)`,
|
|
403
455
|
desc: cat.desc
|
|
404
456
|
};
|
|
405
457
|
});
|
|
406
|
-
list.push({ type: 'action', id: 'search', name: '[S] 🔍 Search templates by keyword...', desc: 'Quickly find templates by typing name or topic' });
|
|
407
|
-
list.push({ type: 'action', id: 'all_view', name: '[L] 📋 View all 56 templates (grouped)', desc: 'Browse the full catalog with section headers' });
|
|
408
|
-
list.push({ type: 'action', id: 'unpack_all', name: '[A] 📦 Unpack all 56 templates in 1-click', desc: 'Copy all 56 templates into target folder' });
|
|
409
|
-
return list;
|
|
410
458
|
}
|
|
411
459
|
|
|
412
460
|
if (currentView === 'CATEGORY') {
|
|
@@ -435,33 +483,59 @@ function renderScreen() {
|
|
|
435
483
|
|
|
436
484
|
if (currentView === 'MAIN') {
|
|
437
485
|
console.log('========================================================');
|
|
438
|
-
console.log('
|
|
439
|
-
console.log('
|
|
486
|
+
console.log(' WAD LAB STARTER TEMPLATES ');
|
|
487
|
+
console.log(' Exam Quick-Start & 56 Ready Templates ');
|
|
440
488
|
console.log('========================================================\n');
|
|
441
|
-
console.log('
|
|
442
|
-
console.log('Press [
|
|
443
|
-
console.log('
|
|
489
|
+
console.log('CORE EXAM STARTERS:');
|
|
490
|
+
console.log(' Press [1-7] for instant pick, or navigate with [UP / DOWN]');
|
|
491
|
+
console.log(' [SPACE] Select | [ENTER] Confirm | [C] Categories | [S] Search | [A] All\n');
|
|
444
492
|
|
|
445
493
|
const items = getActiveItemList();
|
|
446
494
|
items.forEach((item, idx) => {
|
|
447
|
-
if (item.id === '
|
|
448
|
-
console.log('
|
|
495
|
+
if (item.id === 'categories') {
|
|
496
|
+
console.log('\nFULL CATALOG & EXPLORATION:');
|
|
449
497
|
}
|
|
450
498
|
const isCursor = idx === selectedIndex;
|
|
451
|
-
const
|
|
499
|
+
const isChecked = selectedValues.has(item.id);
|
|
500
|
+
const box = item.type === 'featured' ? (isChecked ? '[x] ' : '[ ] ') : '';
|
|
501
|
+
const prefix = isCursor ? ' > ' : ' ';
|
|
502
|
+
console.log(prefix + box + item.name);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
const activeItem = items[selectedIndex];
|
|
506
|
+
if (activeItem && activeItem.desc) {
|
|
507
|
+
console.log('\n Info: ' + activeItem.desc);
|
|
508
|
+
}
|
|
509
|
+
if (selectedValues.size > 0) {
|
|
510
|
+
console.log(`\n Selected: ${selectedValues.size} template(s) [Press ENTER to proceed]`);
|
|
511
|
+
}
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (currentView === 'CATEGORY_LIST') {
|
|
516
|
+
console.log('========================================================');
|
|
517
|
+
console.log(' Browse Templates by Category (56 Total)');
|
|
518
|
+
console.log('========================================================\n');
|
|
519
|
+
console.log(' Press [1-7] or [ENTER] to open category');
|
|
520
|
+
console.log(' [B / ESC] Back to Main Menu\n');
|
|
521
|
+
|
|
522
|
+
const items = getActiveItemList();
|
|
523
|
+
items.forEach((item, idx) => {
|
|
524
|
+
const isCursor = idx === selectedIndex;
|
|
525
|
+
const prefix = isCursor ? ' > ' : ' ';
|
|
452
526
|
console.log(prefix + item.name);
|
|
453
527
|
});
|
|
454
528
|
|
|
455
529
|
const activeItem = items[selectedIndex];
|
|
456
530
|
if (activeItem && activeItem.desc) {
|
|
457
|
-
console.log('\n
|
|
531
|
+
console.log('\n Info: ' + activeItem.desc);
|
|
458
532
|
}
|
|
459
533
|
return;
|
|
460
534
|
}
|
|
461
535
|
|
|
462
536
|
if (currentView === 'CATEGORY') {
|
|
463
537
|
console.log('========================================================');
|
|
464
|
-
console.log(` ${activeCategory.
|
|
538
|
+
console.log(` ${activeCategory.title}`);
|
|
465
539
|
console.log(` ${activeCategory.desc}`);
|
|
466
540
|
console.log('========================================================\n');
|
|
467
541
|
console.log(' [SPACE] Select / Deselect (multi-select supported)');
|
|
@@ -479,20 +553,20 @@ function renderScreen() {
|
|
|
479
553
|
const end = Math.min(start + PAGE_SIZE, total);
|
|
480
554
|
|
|
481
555
|
if (start > 0) {
|
|
482
|
-
console.log(`
|
|
556
|
+
console.log(` ^ (${start} more above)`);
|
|
483
557
|
}
|
|
484
558
|
|
|
485
559
|
for (let i = start; i < end; i++) {
|
|
486
560
|
const t = items[i];
|
|
487
561
|
const isCursor = i === selectedIndex;
|
|
488
562
|
const isChecked = selectedValues.has(t.value);
|
|
489
|
-
const box = isChecked ? '[
|
|
490
|
-
const prefix = isCursor ? '
|
|
563
|
+
const box = isChecked ? '[x]' : '[ ]';
|
|
564
|
+
const prefix = isCursor ? ' > ' : ' ';
|
|
491
565
|
console.log(prefix + box + ' ' + t.name);
|
|
492
566
|
}
|
|
493
567
|
|
|
494
568
|
if (end < total) {
|
|
495
|
-
console.log(`
|
|
569
|
+
console.log(` v (${total - end} more below)`);
|
|
496
570
|
}
|
|
497
571
|
|
|
498
572
|
const selCount = selectedValues.size;
|
|
@@ -502,7 +576,7 @@ function renderScreen() {
|
|
|
502
576
|
|
|
503
577
|
if (currentView === 'SEARCH') {
|
|
504
578
|
console.log('========================================================');
|
|
505
|
-
console.log('
|
|
579
|
+
console.log(' Instant Template Search');
|
|
506
580
|
console.log('========================================================');
|
|
507
581
|
console.log(` Query: "${searchQuery}" (type letters/numbers, Backspace to delete)`);
|
|
508
582
|
console.log(' [SPACE] Select | [ENTER] Confirm | [ESC] Back\n');
|
|
@@ -510,7 +584,7 @@ function renderScreen() {
|
|
|
510
584
|
const items = getActiveItemList();
|
|
511
585
|
if (!searchQuery.trim()) {
|
|
512
586
|
console.log(' Start typing to search across all 56 templates...');
|
|
513
|
-
console.log(' Try keywords: "
|
|
587
|
+
console.log(' Try keywords: "join", "2model", "props", "servlet", "pinia", "mongo"\n');
|
|
514
588
|
return;
|
|
515
589
|
}
|
|
516
590
|
|
|
@@ -528,20 +602,20 @@ function renderScreen() {
|
|
|
528
602
|
const end = Math.min(start + PAGE_SIZE, total);
|
|
529
603
|
|
|
530
604
|
if (start > 0) {
|
|
531
|
-
console.log(`
|
|
605
|
+
console.log(` ^ (${start} more above)`);
|
|
532
606
|
}
|
|
533
607
|
|
|
534
608
|
for (let i = start; i < end; i++) {
|
|
535
609
|
const t = items[i];
|
|
536
610
|
const isCursor = i === selectedIndex;
|
|
537
611
|
const isChecked = selectedValues.has(t.value);
|
|
538
|
-
const box = isChecked ? '[
|
|
539
|
-
const prefix = isCursor ? '
|
|
612
|
+
const box = isChecked ? '[x]' : '[ ]';
|
|
613
|
+
const prefix = isCursor ? ' > ' : ' ';
|
|
540
614
|
console.log(prefix + box + ' ' + t.name);
|
|
541
615
|
}
|
|
542
616
|
|
|
543
617
|
if (end < total) {
|
|
544
|
-
console.log(`
|
|
618
|
+
console.log(` v (${total - end} more below)`);
|
|
545
619
|
}
|
|
546
620
|
|
|
547
621
|
console.log(`\n Matches: ${total} | Selected: ${selectedValues.size}`);
|
|
@@ -550,7 +624,7 @@ function renderScreen() {
|
|
|
550
624
|
|
|
551
625
|
if (currentView === 'ALL') {
|
|
552
626
|
console.log('========================================================');
|
|
553
|
-
console.log('
|
|
627
|
+
console.log(' Full Catalog: All 56 Templates');
|
|
554
628
|
console.log('========================================================\n');
|
|
555
629
|
console.log(' [SPACE] Select / Deselect | [ENTER] Confirm | [ESC] Back\n');
|
|
556
630
|
|
|
@@ -565,7 +639,7 @@ function renderScreen() {
|
|
|
565
639
|
const end = Math.min(start + PAGE_SIZE, total);
|
|
566
640
|
|
|
567
641
|
if (start > 0) {
|
|
568
|
-
console.log(`
|
|
642
|
+
console.log(` ^ (${start} more above)`);
|
|
569
643
|
}
|
|
570
644
|
|
|
571
645
|
let lastCat = null;
|
|
@@ -574,19 +648,19 @@ function renderScreen() {
|
|
|
574
648
|
if (t.category !== lastCat) {
|
|
575
649
|
const catObj = categories.find(c => c.id === t.category);
|
|
576
650
|
if (catObj) {
|
|
577
|
-
console.log(` --- ${catObj.
|
|
651
|
+
console.log(` --- ${catObj.title} ---`);
|
|
578
652
|
}
|
|
579
653
|
lastCat = t.category;
|
|
580
654
|
}
|
|
581
655
|
const isCursor = i === selectedIndex;
|
|
582
656
|
const isChecked = selectedValues.has(t.value);
|
|
583
|
-
const box = isChecked ? '[
|
|
584
|
-
const prefix = isCursor ? '
|
|
657
|
+
const box = isChecked ? '[x]' : '[ ]';
|
|
658
|
+
const prefix = isCursor ? ' > ' : ' ';
|
|
585
659
|
console.log(prefix + box + ' ' + t.name);
|
|
586
660
|
}
|
|
587
661
|
|
|
588
662
|
if (end < total) {
|
|
589
|
-
console.log(`
|
|
663
|
+
console.log(` v (${total - end} more below)`);
|
|
590
664
|
}
|
|
591
665
|
|
|
592
666
|
console.log(`\n [${selectedIndex + 1}/${total}] Selected: ${selectedValues.size}`);
|
|
@@ -650,7 +724,7 @@ function promptFolderName(choices) {
|
|
|
650
724
|
|
|
651
725
|
if (isAll) {
|
|
652
726
|
copyDirRecursive(templatesRoot, destDir);
|
|
653
|
-
console.log('\
|
|
727
|
+
console.log('\nSuccessfully unpacked ALL 56 templates into: ' + destDir);
|
|
654
728
|
} else {
|
|
655
729
|
choices.forEach(c => {
|
|
656
730
|
if (c === 'fullstack-springboot-vue') {
|
|
@@ -680,7 +754,7 @@ function promptFolderName(choices) {
|
|
|
680
754
|
console.log('Unpacked [' + c + '] into: ' + dest);
|
|
681
755
|
}
|
|
682
756
|
});
|
|
683
|
-
console.log('\
|
|
757
|
+
console.log('\nSuccessfully unpacked all chosen templates into: ' + destDir);
|
|
684
758
|
}
|
|
685
759
|
process.exit(0);
|
|
686
760
|
});
|
|
@@ -748,6 +822,10 @@ function startInteractive() {
|
|
|
748
822
|
} else if (key.name === 'down') {
|
|
749
823
|
selectedIndex = (selectedIndex + 1) % items.length;
|
|
750
824
|
renderScreen();
|
|
825
|
+
} else if (str === 'c' || str === 'C') {
|
|
826
|
+
currentView = 'CATEGORY_LIST';
|
|
827
|
+
selectedIndex = 0;
|
|
828
|
+
renderScreen();
|
|
751
829
|
} else if (str === 's' || str === 'S') {
|
|
752
830
|
currentView = 'SEARCH';
|
|
753
831
|
searchQuery = '';
|
|
@@ -761,15 +839,30 @@ function startInteractive() {
|
|
|
761
839
|
renderScreen();
|
|
762
840
|
} else if (str >= '1' && str <= '7') {
|
|
763
841
|
const num = parseInt(str, 10);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
842
|
+
const target = featuredTemplates[num - 1];
|
|
843
|
+
if (target) {
|
|
844
|
+
promptFolderName([target.value]);
|
|
845
|
+
}
|
|
846
|
+
} else if (key.name === 'space') {
|
|
847
|
+
const item = items[selectedIndex];
|
|
848
|
+
if (item.type === 'featured') {
|
|
849
|
+
if (selectedValues.has(item.id)) {
|
|
850
|
+
selectedValues.delete(item.id);
|
|
851
|
+
} else {
|
|
852
|
+
selectedValues.add(item.id);
|
|
853
|
+
}
|
|
854
|
+
renderScreen();
|
|
855
|
+
}
|
|
768
856
|
} else if (key.name === 'return' || key.name === 'enter') {
|
|
769
857
|
const item = items[selectedIndex];
|
|
770
|
-
if (
|
|
771
|
-
|
|
772
|
-
|
|
858
|
+
if (selectedValues.size > 0) {
|
|
859
|
+
promptFolderName(Array.from(selectedValues));
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
if (item.type === 'featured') {
|
|
863
|
+
promptFolderName([item.id]);
|
|
864
|
+
} else if (item.id === 'categories') {
|
|
865
|
+
currentView = 'CATEGORY_LIST';
|
|
773
866
|
selectedIndex = 0;
|
|
774
867
|
renderScreen();
|
|
775
868
|
} else if (item.id === 'search') {
|
|
@@ -788,6 +881,36 @@ function startInteractive() {
|
|
|
788
881
|
return;
|
|
789
882
|
}
|
|
790
883
|
|
|
884
|
+
// CATEGORY_LIST VIEW INPUT
|
|
885
|
+
if (currentView === 'CATEGORY_LIST') {
|
|
886
|
+
if (key.name === 'escape' || key.name === 'left' || str === 'b' || str === 'B') {
|
|
887
|
+
currentView = 'MAIN';
|
|
888
|
+
selectedIndex = 0;
|
|
889
|
+
renderScreen();
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (key.name === 'up') {
|
|
893
|
+
selectedIndex = (selectedIndex - 1 + items.length) % items.length;
|
|
894
|
+
renderScreen();
|
|
895
|
+
} else if (key.name === 'down') {
|
|
896
|
+
selectedIndex = (selectedIndex + 1) % items.length;
|
|
897
|
+
renderScreen();
|
|
898
|
+
} else if (str >= '1' && str <= '7') {
|
|
899
|
+
const num = parseInt(str, 10);
|
|
900
|
+
activeCategory = categories[num - 1];
|
|
901
|
+
currentView = 'CATEGORY';
|
|
902
|
+
selectedIndex = 0;
|
|
903
|
+
renderScreen();
|
|
904
|
+
} else if (key.name === 'return' || key.name === 'enter') {
|
|
905
|
+
const item = items[selectedIndex];
|
|
906
|
+
activeCategory = categories.find(c => c.id === item.id);
|
|
907
|
+
currentView = 'CATEGORY';
|
|
908
|
+
selectedIndex = 0;
|
|
909
|
+
renderScreen();
|
|
910
|
+
}
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
|
|
791
914
|
// SEARCH VIEW INPUT
|
|
792
915
|
if (currentView === 'SEARCH') {
|
|
793
916
|
if (key.name === 'escape') {
|
|
@@ -863,7 +986,7 @@ function startInteractive() {
|
|
|
863
986
|
// CATEGORY / ALL VIEW INPUT
|
|
864
987
|
if (currentView === 'CATEGORY' || currentView === 'ALL') {
|
|
865
988
|
if (key.name === 'escape' || key.name === 'left' || str === 'b' || str === 'B') {
|
|
866
|
-
currentView = 'MAIN';
|
|
989
|
+
currentView = currentView === 'CATEGORY' ? 'CATEGORY_LIST' : 'MAIN';
|
|
867
990
|
selectedIndex = 0;
|
|
868
991
|
renderScreen();
|
|
869
992
|
return;
|
package/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
|
|
3
|
+
# Usage:
|
|
4
|
+
# ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
|
|
5
|
+
# ./script.sh -d (runs in background daemon mode)
|
|
6
|
+
# ./script.sh [app-dir] [-d]
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
DAEMON_MODE=false
|
|
10
|
+
APP_ARG=""
|
|
11
|
+
for arg in "$@"; do
|
|
12
|
+
if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
|
|
13
|
+
DAEMON_MODE=true
|
|
14
|
+
elif [ -z "$APP_ARG" ]; then
|
|
15
|
+
APP_ARG="$arg"
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
|
|
21
|
+
APP_NAME="$(basename "$APP_DIR")"
|
|
22
|
+
|
|
23
|
+
# ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
|
|
24
|
+
TOMCAT_HOME="${CATALINA_HOME:-}"
|
|
25
|
+
if [ -z "$TOMCAT_HOME" ]; then
|
|
26
|
+
for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
|
|
27
|
+
if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
|
|
28
|
+
TOMCAT_HOME="$d"
|
|
29
|
+
break
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
[ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
|
|
35
|
+
export CATALINA_HOME="$TOMCAT_HOME"
|
|
36
|
+
chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
|
|
37
|
+
echo "Tomcat : $TOMCAT_HOME"
|
|
38
|
+
echo "App : $APP_NAME ($APP_DIR)"
|
|
39
|
+
|
|
40
|
+
# ---- stop Tomcat if it's already running (ignore errors)
|
|
41
|
+
"$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
|
|
42
|
+
sleep 2
|
|
43
|
+
|
|
44
|
+
# ---- copy the app into webapps (fresh copy, remove old exploded copy)
|
|
45
|
+
DEST="$TOMCAT_HOME/webapps/$APP_NAME"
|
|
46
|
+
rm -rf "$DEST"
|
|
47
|
+
mkdir -p "$DEST"
|
|
48
|
+
cp -r "$APP_DIR"/. "$DEST"/
|
|
49
|
+
|
|
50
|
+
# ---- compile servlets (if any .java files exist)
|
|
51
|
+
SRC_DIR="$DEST/WEB-INF/src"
|
|
52
|
+
[ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
|
|
53
|
+
mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
|
|
54
|
+
if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
|
|
55
|
+
mkdir -p "$DEST/WEB-INF/classes"
|
|
56
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
57
|
+
CP_PARTS=()
|
|
58
|
+
for j in "$TOMCAT_HOME"/lib/*.jar; do
|
|
59
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
60
|
+
done
|
|
61
|
+
if [ -d "$DEST/WEB-INF/lib" ]; then
|
|
62
|
+
for j in "$DEST"/WEB-INF/lib/*.jar; do
|
|
63
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
64
|
+
done
|
|
65
|
+
fi
|
|
66
|
+
CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
|
|
67
|
+
else
|
|
68
|
+
CP="$TOMCAT_HOME/lib/*"
|
|
69
|
+
[ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
|
|
70
|
+
fi
|
|
71
|
+
echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
|
|
72
|
+
javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
|
|
73
|
+
else
|
|
74
|
+
echo "No .java files found, skipping compile."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ---- start Tomcat
|
|
78
|
+
if [ "$DAEMON_MODE" = true ]; then
|
|
79
|
+
"$TOMCAT_HOME/bin/startup.sh"
|
|
80
|
+
echo
|
|
81
|
+
echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
|
|
82
|
+
echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
|
|
83
|
+
else
|
|
84
|
+
echo
|
|
85
|
+
echo "Started. Open: http://localhost:8080/$APP_NAME/"
|
|
86
|
+
echo "Press Ctrl+C to stop."
|
|
87
|
+
echo
|
|
88
|
+
exec "$TOMCAT_HOME/bin/catalina.sh" run
|
|
89
|
+
fi
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
|
|
3
|
+
# Usage:
|
|
4
|
+
# ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
|
|
5
|
+
# ./script.sh -d (runs in background daemon mode)
|
|
6
|
+
# ./script.sh [app-dir] [-d]
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
DAEMON_MODE=false
|
|
10
|
+
APP_ARG=""
|
|
11
|
+
for arg in "$@"; do
|
|
12
|
+
if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
|
|
13
|
+
DAEMON_MODE=true
|
|
14
|
+
elif [ -z "$APP_ARG" ]; then
|
|
15
|
+
APP_ARG="$arg"
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
|
|
21
|
+
APP_NAME="$(basename "$APP_DIR")"
|
|
22
|
+
|
|
23
|
+
# ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
|
|
24
|
+
TOMCAT_HOME="${CATALINA_HOME:-}"
|
|
25
|
+
if [ -z "$TOMCAT_HOME" ]; then
|
|
26
|
+
for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
|
|
27
|
+
if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
|
|
28
|
+
TOMCAT_HOME="$d"
|
|
29
|
+
break
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
[ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
|
|
35
|
+
export CATALINA_HOME="$TOMCAT_HOME"
|
|
36
|
+
chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
|
|
37
|
+
echo "Tomcat : $TOMCAT_HOME"
|
|
38
|
+
echo "App : $APP_NAME ($APP_DIR)"
|
|
39
|
+
|
|
40
|
+
# ---- stop Tomcat if it's already running (ignore errors)
|
|
41
|
+
"$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
|
|
42
|
+
sleep 2
|
|
43
|
+
|
|
44
|
+
# ---- copy the app into webapps (fresh copy, remove old exploded copy)
|
|
45
|
+
DEST="$TOMCAT_HOME/webapps/$APP_NAME"
|
|
46
|
+
rm -rf "$DEST"
|
|
47
|
+
mkdir -p "$DEST"
|
|
48
|
+
cp -r "$APP_DIR"/. "$DEST"/
|
|
49
|
+
|
|
50
|
+
# ---- compile servlets (if any .java files exist)
|
|
51
|
+
SRC_DIR="$DEST/WEB-INF/src"
|
|
52
|
+
[ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
|
|
53
|
+
mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
|
|
54
|
+
if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
|
|
55
|
+
mkdir -p "$DEST/WEB-INF/classes"
|
|
56
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
57
|
+
CP_PARTS=()
|
|
58
|
+
for j in "$TOMCAT_HOME"/lib/*.jar; do
|
|
59
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
60
|
+
done
|
|
61
|
+
if [ -d "$DEST/WEB-INF/lib" ]; then
|
|
62
|
+
for j in "$DEST"/WEB-INF/lib/*.jar; do
|
|
63
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
64
|
+
done
|
|
65
|
+
fi
|
|
66
|
+
CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
|
|
67
|
+
else
|
|
68
|
+
CP="$TOMCAT_HOME/lib/*"
|
|
69
|
+
[ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
|
|
70
|
+
fi
|
|
71
|
+
echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
|
|
72
|
+
javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
|
|
73
|
+
else
|
|
74
|
+
echo "No .java files found, skipping compile."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ---- start Tomcat
|
|
78
|
+
if [ "$DAEMON_MODE" = true ]; then
|
|
79
|
+
"$TOMCAT_HOME/bin/startup.sh"
|
|
80
|
+
echo
|
|
81
|
+
echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
|
|
82
|
+
echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
|
|
83
|
+
else
|
|
84
|
+
echo
|
|
85
|
+
echo "Started. Open: http://localhost:8080/$APP_NAME/"
|
|
86
|
+
echo "Press Ctrl+C to stop."
|
|
87
|
+
echo
|
|
88
|
+
exec "$TOMCAT_HOME/bin/catalina.sh" run
|
|
89
|
+
fi
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
|
|
3
|
+
# Usage:
|
|
4
|
+
# ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
|
|
5
|
+
# ./script.sh -d (runs in background daemon mode)
|
|
6
|
+
# ./script.sh [app-dir] [-d]
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
DAEMON_MODE=false
|
|
10
|
+
APP_ARG=""
|
|
11
|
+
for arg in "$@"; do
|
|
12
|
+
if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
|
|
13
|
+
DAEMON_MODE=true
|
|
14
|
+
elif [ -z "$APP_ARG" ]; then
|
|
15
|
+
APP_ARG="$arg"
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
|
|
21
|
+
APP_NAME="$(basename "$APP_DIR")"
|
|
22
|
+
|
|
23
|
+
# ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
|
|
24
|
+
TOMCAT_HOME="${CATALINA_HOME:-}"
|
|
25
|
+
if [ -z "$TOMCAT_HOME" ]; then
|
|
26
|
+
for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
|
|
27
|
+
if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
|
|
28
|
+
TOMCAT_HOME="$d"
|
|
29
|
+
break
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
[ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
|
|
35
|
+
export CATALINA_HOME="$TOMCAT_HOME"
|
|
36
|
+
chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
|
|
37
|
+
echo "Tomcat : $TOMCAT_HOME"
|
|
38
|
+
echo "App : $APP_NAME ($APP_DIR)"
|
|
39
|
+
|
|
40
|
+
# ---- stop Tomcat if it's already running (ignore errors)
|
|
41
|
+
"$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
|
|
42
|
+
sleep 2
|
|
43
|
+
|
|
44
|
+
# ---- copy the app into webapps (fresh copy, remove old exploded copy)
|
|
45
|
+
DEST="$TOMCAT_HOME/webapps/$APP_NAME"
|
|
46
|
+
rm -rf "$DEST"
|
|
47
|
+
mkdir -p "$DEST"
|
|
48
|
+
cp -r "$APP_DIR"/. "$DEST"/
|
|
49
|
+
|
|
50
|
+
# ---- compile servlets (if any .java files exist)
|
|
51
|
+
SRC_DIR="$DEST/WEB-INF/src"
|
|
52
|
+
[ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
|
|
53
|
+
mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
|
|
54
|
+
if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
|
|
55
|
+
mkdir -p "$DEST/WEB-INF/classes"
|
|
56
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
57
|
+
CP_PARTS=()
|
|
58
|
+
for j in "$TOMCAT_HOME"/lib/*.jar; do
|
|
59
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
60
|
+
done
|
|
61
|
+
if [ -d "$DEST/WEB-INF/lib" ]; then
|
|
62
|
+
for j in "$DEST"/WEB-INF/lib/*.jar; do
|
|
63
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
64
|
+
done
|
|
65
|
+
fi
|
|
66
|
+
CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
|
|
67
|
+
else
|
|
68
|
+
CP="$TOMCAT_HOME/lib/*"
|
|
69
|
+
[ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
|
|
70
|
+
fi
|
|
71
|
+
echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
|
|
72
|
+
javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
|
|
73
|
+
else
|
|
74
|
+
echo "No .java files found, skipping compile."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ---- start Tomcat
|
|
78
|
+
if [ "$DAEMON_MODE" = true ]; then
|
|
79
|
+
"$TOMCAT_HOME/bin/startup.sh"
|
|
80
|
+
echo
|
|
81
|
+
echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
|
|
82
|
+
echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
|
|
83
|
+
else
|
|
84
|
+
echo
|
|
85
|
+
echo "Started. Open: http://localhost:8080/$APP_NAME/"
|
|
86
|
+
echo "Press Ctrl+C to stop."
|
|
87
|
+
echo
|
|
88
|
+
exec "$TOMCAT_HOME/bin/catalina.sh" run
|
|
89
|
+
fi
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
|
|
3
|
+
# Usage:
|
|
4
|
+
# ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
|
|
5
|
+
# ./script.sh -d (runs in background daemon mode)
|
|
6
|
+
# ./script.sh [app-dir] [-d]
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
DAEMON_MODE=false
|
|
10
|
+
APP_ARG=""
|
|
11
|
+
for arg in "$@"; do
|
|
12
|
+
if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
|
|
13
|
+
DAEMON_MODE=true
|
|
14
|
+
elif [ -z "$APP_ARG" ]; then
|
|
15
|
+
APP_ARG="$arg"
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
|
|
21
|
+
APP_NAME="$(basename "$APP_DIR")"
|
|
22
|
+
|
|
23
|
+
# ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
|
|
24
|
+
TOMCAT_HOME="${CATALINA_HOME:-}"
|
|
25
|
+
if [ -z "$TOMCAT_HOME" ]; then
|
|
26
|
+
for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
|
|
27
|
+
if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
|
|
28
|
+
TOMCAT_HOME="$d"
|
|
29
|
+
break
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
[ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
|
|
35
|
+
export CATALINA_HOME="$TOMCAT_HOME"
|
|
36
|
+
chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
|
|
37
|
+
echo "Tomcat : $TOMCAT_HOME"
|
|
38
|
+
echo "App : $APP_NAME ($APP_DIR)"
|
|
39
|
+
|
|
40
|
+
# ---- stop Tomcat if it's already running (ignore errors)
|
|
41
|
+
"$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
|
|
42
|
+
sleep 2
|
|
43
|
+
|
|
44
|
+
# ---- copy the app into webapps (fresh copy, remove old exploded copy)
|
|
45
|
+
DEST="$TOMCAT_HOME/webapps/$APP_NAME"
|
|
46
|
+
rm -rf "$DEST"
|
|
47
|
+
mkdir -p "$DEST"
|
|
48
|
+
cp -r "$APP_DIR"/. "$DEST"/
|
|
49
|
+
|
|
50
|
+
# ---- compile servlets (if any .java files exist)
|
|
51
|
+
SRC_DIR="$DEST/WEB-INF/src"
|
|
52
|
+
[ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
|
|
53
|
+
mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
|
|
54
|
+
if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
|
|
55
|
+
mkdir -p "$DEST/WEB-INF/classes"
|
|
56
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
57
|
+
CP_PARTS=()
|
|
58
|
+
for j in "$TOMCAT_HOME"/lib/*.jar; do
|
|
59
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
60
|
+
done
|
|
61
|
+
if [ -d "$DEST/WEB-INF/lib" ]; then
|
|
62
|
+
for j in "$DEST"/WEB-INF/lib/*.jar; do
|
|
63
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
64
|
+
done
|
|
65
|
+
fi
|
|
66
|
+
CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
|
|
67
|
+
else
|
|
68
|
+
CP="$TOMCAT_HOME/lib/*"
|
|
69
|
+
[ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
|
|
70
|
+
fi
|
|
71
|
+
echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
|
|
72
|
+
javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
|
|
73
|
+
else
|
|
74
|
+
echo "No .java files found, skipping compile."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ---- start Tomcat
|
|
78
|
+
if [ "$DAEMON_MODE" = true ]; then
|
|
79
|
+
"$TOMCAT_HOME/bin/startup.sh"
|
|
80
|
+
echo
|
|
81
|
+
echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
|
|
82
|
+
echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
|
|
83
|
+
else
|
|
84
|
+
echo
|
|
85
|
+
echo "Started. Open: http://localhost:8080/$APP_NAME/"
|
|
86
|
+
echo "Press Ctrl+C to stop."
|
|
87
|
+
echo
|
|
88
|
+
exec "$TOMCAT_HOME/bin/catalina.sh" run
|
|
89
|
+
fi
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Deploy a servlet/web app folder to Tomcat 9, compile servlets, and start Tomcat.
|
|
3
|
+
# Usage:
|
|
4
|
+
# ./script.sh (runs in foreground, displays logs, press Ctrl+C to stop)
|
|
5
|
+
# ./script.sh -d (runs in background daemon mode)
|
|
6
|
+
# ./script.sh [app-dir] [-d]
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
DAEMON_MODE=false
|
|
10
|
+
APP_ARG=""
|
|
11
|
+
for arg in "$@"; do
|
|
12
|
+
if [ "$arg" = "-d" ] || [ "$arg" = "--daemon" ]; then
|
|
13
|
+
DAEMON_MODE=true
|
|
14
|
+
elif [ -z "$APP_ARG" ]; then
|
|
15
|
+
APP_ARG="$arg"
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
APP_DIR="$(cd "${APP_ARG:-$SCRIPT_DIR}" && pwd)"
|
|
21
|
+
APP_NAME="$(basename "$APP_DIR")"
|
|
22
|
+
|
|
23
|
+
# ---- locate Tomcat 9 (CATALINA_HOME, Desktop, Home, WSL /mnt/c, Git Bash /c, or /opt)
|
|
24
|
+
TOMCAT_HOME="${CATALINA_HOME:-}"
|
|
25
|
+
if [ -z "$TOMCAT_HOME" ]; then
|
|
26
|
+
for d in "$HOME"/Desktop/apache-tomcat-9* "$HOME"/Desktop/*tomcat*9* "$HOME"/Desktop/*omcat* "$HOME"/apache-tomcat-9* "$HOME"/*tomcat* /mnt/c/apache-tomcat-9* /mnt/c/apache-tomcat* /c/apache-tomcat-9* /c/apache-tomcat* /opt/apache-tomcat-9* /opt/apache-tomcat* /usr/local/apache-tomcat* "$APP_DIR"/../*tomcat*; do
|
|
27
|
+
if [ -x "$d/bin/catalina.sh" ] || [ -f "$d/bin/catalina.sh" ]; then
|
|
28
|
+
TOMCAT_HOME="$d"
|
|
29
|
+
break
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
[ -n "$TOMCAT_HOME" ] || { echo "Tomcat not found in Desktop, Home, /mnt/c, or /opt. Set CATALINA_HOME=/path/to/tomcat and retry."; exit 1; }
|
|
35
|
+
export CATALINA_HOME="$TOMCAT_HOME"
|
|
36
|
+
chmod +x "$TOMCAT_HOME"/bin/*.sh 2>/dev/null || true
|
|
37
|
+
echo "Tomcat : $TOMCAT_HOME"
|
|
38
|
+
echo "App : $APP_NAME ($APP_DIR)"
|
|
39
|
+
|
|
40
|
+
# ---- stop Tomcat if it's already running (ignore errors)
|
|
41
|
+
"$TOMCAT_HOME/bin/shutdown.sh" >/dev/null 2>&1 || true
|
|
42
|
+
sleep 2
|
|
43
|
+
|
|
44
|
+
# ---- copy the app into webapps (fresh copy, remove old exploded copy)
|
|
45
|
+
DEST="$TOMCAT_HOME/webapps/$APP_NAME"
|
|
46
|
+
rm -rf "$DEST"
|
|
47
|
+
mkdir -p "$DEST"
|
|
48
|
+
cp -r "$APP_DIR"/. "$DEST"/
|
|
49
|
+
|
|
50
|
+
# ---- compile servlets (if any .java files exist)
|
|
51
|
+
SRC_DIR="$DEST/WEB-INF/src"
|
|
52
|
+
[ -d "$SRC_DIR" ] || SRC_DIR="$DEST/WEB-INF"
|
|
53
|
+
mapfile -t JAVA_FILES < <(find "$SRC_DIR" -name '*.java' 2>/dev/null || true)
|
|
54
|
+
if [ "${#JAVA_FILES[@]}" -gt 0 ]; then
|
|
55
|
+
mkdir -p "$DEST/WEB-INF/classes"
|
|
56
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
57
|
+
CP_PARTS=()
|
|
58
|
+
for j in "$TOMCAT_HOME"/lib/*.jar; do
|
|
59
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
60
|
+
done
|
|
61
|
+
if [ -d "$DEST/WEB-INF/lib" ]; then
|
|
62
|
+
for j in "$DEST"/WEB-INF/lib/*.jar; do
|
|
63
|
+
[ -f "$j" ] && CP_PARTS+=("$(cygpath -w "$j")")
|
|
64
|
+
done
|
|
65
|
+
fi
|
|
66
|
+
CP="$(IFS=";"; echo "${CP_PARTS[*]}")"
|
|
67
|
+
else
|
|
68
|
+
CP="$TOMCAT_HOME/lib/*"
|
|
69
|
+
[ -d "$DEST/WEB-INF/lib" ] && CP="$CP:$DEST/WEB-INF/lib/*"
|
|
70
|
+
fi
|
|
71
|
+
echo "Compiling ${#JAVA_FILES[@]} Java file(s)..."
|
|
72
|
+
javac -cp "$CP" -d "$DEST/WEB-INF/classes" "${JAVA_FILES[@]}"
|
|
73
|
+
else
|
|
74
|
+
echo "No .java files found, skipping compile."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ---- start Tomcat
|
|
78
|
+
if [ "$DAEMON_MODE" = true ]; then
|
|
79
|
+
"$TOMCAT_HOME/bin/startup.sh"
|
|
80
|
+
echo
|
|
81
|
+
echo "Started in background. Open: http://localhost:8080/$APP_NAME/"
|
|
82
|
+
echo "Logs : tail -f $TOMCAT_HOME/logs/catalina.out"
|
|
83
|
+
else
|
|
84
|
+
echo
|
|
85
|
+
echo "Started. Open: http://localhost:8080/$APP_NAME/"
|
|
86
|
+
echo "Press Ctrl+C to stop."
|
|
87
|
+
echo
|
|
88
|
+
exec "$TOMCAT_HOME/bin/catalina.sh" run
|
|
89
|
+
fi
|