ante-erp-cli 1.11.69 → 1.11.70

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/ante-cli.js CHANGED
@@ -332,6 +332,7 @@ program
332
332
  .option('--pos <url>', 'POS App domain/URL (non-interactive mode)')
333
333
  .option('--mlm <url>', 'MLM App domain/URL (non-interactive mode)')
334
334
  .option('--backend-mlm <url>', 'Backend MLM API domain/URL (non-interactive mode)')
335
+ .option('--frontend-core-mobile <url>', 'Frontend Core Mobile domain/URL (non-interactive mode)')
335
336
  .option('--detect', 'Auto-detect public IP address')
336
337
  .option('--ssl, --enable-ssl', 'Enable SSL certificate (Let\'s Encrypt)')
337
338
  .option('--cloudflare-ssl', 'Enable Cloudflare SSL mode (self-signed cert for Full mode)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ante-erp-cli",
3
- "version": "1.11.69",
3
+ "version": "1.11.70",
4
4
  "description": "Comprehensive CLI tool for managing ANTE ERP self-hosted installations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -79,6 +79,7 @@ function showSuccess(installDir, credentials, config) {
79
79
  const frontendUrl = config.frontendDomain || 'http://localhost:8080';
80
80
  const apiUrl = config.apiDomain || 'http://localhost:3001';
81
81
  const frontendCoreUrl = config.frontendCoreDomain || 'http://localhost:8085';
82
+ const frontendCoreMobileUrl = config.frontendCoreMobileDomain || 'http://localhost:8086';
82
83
  const backendCoreUrl = config.backendCoreDomain || 'http://localhost:4002';
83
84
  const gateAppUrl = config.gateAppDomain || 'http://localhost:8081';
84
85
  const guardianAppUrl = config.guardianAppDomain || 'http://localhost:8082';
@@ -89,7 +90,8 @@ function showSuccess(installDir, credentials, config) {
89
90
  let accessInfo = chalk.white('Access Information:') + '\n' +
90
91
  chalk.gray('━'.repeat(40)) + '\n' +
91
92
  chalk.cyan('Frontend Main: ') + chalk.white(frontendUrl) + '\n' +
92
- chalk.cyan('Frontend Core: ') + chalk.white(frontendCoreUrl) + '\n';
93
+ chalk.cyan('Frontend Core: ') + chalk.white(frontendCoreUrl) + '\n' +
94
+ chalk.cyan('Frontend Core Mobile: ') + chalk.white(frontendCoreMobileUrl) + '\n';
93
95
 
94
96
  if (config.installGate) {
95
97
  accessInfo += chalk.cyan('Gate App: ') + chalk.white(gateAppUrl) + '\n';
@@ -325,6 +327,7 @@ export async function install(options) {
325
327
  const backendMlmPort = 4001;
326
328
  const backendCorePort = 4002;
327
329
  const frontendCorePort = 8085;
330
+ const frontendCoreMobilePort = 8086;
328
331
  const gateAppPort = 8081;
329
332
  const guardianAppPort = 8082;
330
333
  const facialWebPort = 8083;
@@ -339,6 +342,7 @@ export async function install(options) {
339
342
  mlmApiDomain: buildURL(host, backendMlmPort),
340
343
  backendCoreDomain: buildURL(host, backendCorePort),
341
344
  frontendCoreDomain: buildURL(host, frontendCorePort),
345
+ frontendCoreMobileDomain: buildURL(host, frontendCoreMobilePort),
342
346
  gateAppDomain: buildURL(host, gateAppPort),
343
347
  guardianAppDomain: buildURL(host, guardianAppPort),
344
348
  facialAppDomain: buildURL(host, facialWebPort),
@@ -349,6 +353,7 @@ export async function install(options) {
349
353
  backendMlmPort,
350
354
  backendCorePort,
351
355
  frontendCorePort,
356
+ frontendCoreMobilePort,
352
357
  gateAppPort,
353
358
  guardianAppPort,
354
359
  facialWebPort,
@@ -360,7 +365,7 @@ export async function install(options) {
360
365
  installFacial: true,
361
366
  installPos: true,
362
367
  installMlm: true,
363
- frontends: ['main', 'gate', 'guardian', 'facial', 'pos', 'mlm', 'core']
368
+ frontends: ['main', 'gate', 'guardian', 'facial', 'pos', 'mlm', 'core', 'core-mobile']
364
369
  };
365
370
 
366
371
  console.log(chalk.green(`✓ ${formatStepTitle(stepNetworkDetect, totalSteps, `Network detected: ${host}`)}\n`));
@@ -370,6 +375,7 @@ export async function install(options) {
370
375
  console.log(chalk.cyan(' Directory:'), chalk.white(config.installDir));
371
376
  console.log(chalk.cyan(' Frontend Main:'), chalk.white(config.frontendDomain));
372
377
  console.log(chalk.cyan(' Frontend Core:'), chalk.white(config.frontendCoreDomain));
378
+ console.log(chalk.cyan(' Frontend Core Mobile:'), chalk.white(config.frontendCoreMobileDomain));
373
379
  console.log(chalk.cyan(' Gate App:'), chalk.white(config.gateAppDomain));
374
380
  console.log(chalk.cyan(' Guardian App:'), chalk.white(config.guardianAppDomain));
375
381
  console.log(chalk.cyan(' Facial Web:'), chalk.white(config.facialAppDomain));
@@ -456,6 +462,7 @@ export async function install(options) {
456
462
  backendMlmPort: config.backendMlmPort,
457
463
  backendCorePort: config.backendCorePort,
458
464
  frontendCorePort: config.frontendCorePort,
465
+ frontendCoreMobilePort: config.frontendCoreMobilePort,
459
466
  gateAppPort: config.gateAppPort,
460
467
  guardianAppPort: config.guardianAppPort,
461
468
  facialWebPort: config.facialWebPort,
@@ -479,6 +486,7 @@ export async function install(options) {
479
486
  backendMlmPort: config.backendMlmPort,
480
487
  backendCorePort: config.backendCorePort,
481
488
  frontendCorePort: config.frontendCorePort,
489
+ frontendCoreMobilePort: config.frontendCoreMobilePort,
482
490
  gateAppPort: config.gateAppPort,
483
491
  guardianAppPort: config.guardianAppPort,
484
492
  facialWebPort: config.facialWebPort,
@@ -491,6 +499,7 @@ export async function install(options) {
491
499
  mlmAppUrl: config.mlmAppDomain,
492
500
  mlmApiUrl: config.mlmApiDomain,
493
501
  frontendCoreUrl: config.frontendCoreDomain,
502
+ frontendCoreMobileUrl: config.frontendCoreMobileDomain,
494
503
  backendCoreUrl: config.backendCoreDomain,
495
504
  companyId: config.companyId,
496
505
  installGate: config.installGate,
@@ -76,6 +76,7 @@ export async function regenerateCompose() {
76
76
  console.log(chalk.gray(` Frontend Main: ${installed.hasMain ? '✓ Installed' : '✗ Not installed'}`));
77
77
  console.log(chalk.gray(` Backend Core: ✓ Always Included`));
78
78
  console.log(chalk.gray(` Frontend Core: ✓ Always Included`));
79
+ console.log(chalk.gray(` Frontend Core Mobile: ✓ Always Included`));
79
80
  console.log(chalk.gray(` Gate App: ${installed.hasGateApp ? '✓ Installed' : '✗ Not installed'}`));
80
81
  console.log(chalk.gray(` Guardian App: ${installed.hasGuardianApp ? '✓ Installed' : '✗ Not installed'}`));
81
82
  console.log(chalk.gray(` Facial Web: ${installed.hasFacialWeb ? '✓ Installed' : '✗ Not installed'}`));
@@ -91,6 +92,7 @@ export async function regenerateCompose() {
91
92
  const backendMlmPort = parseInt(envConfig.BACKEND_MLM_PORT) || 4001;
92
93
  const backendCorePort = parseInt(envConfig.BACKEND_CORE_PORT) || 4002;
93
94
  const frontendCorePort = parseInt(envConfig.FRONTEND_CORE_PORT) || 8085;
95
+ const frontendCoreMobilePort = parseInt(envConfig.FRONTEND_CORE_MOBILE_PORT) || 8086;
94
96
  const gateAppPort = parseInt(envConfig.GATE_APP_PORT) || 8081;
95
97
  const guardianAppPort = parseInt(envConfig.GUARDIAN_APP_PORT) || 8082;
96
98
  const facialWebPort = parseInt(envConfig.FACIAL_WEB_PORT) || 8083;
@@ -103,6 +105,7 @@ export async function regenerateCompose() {
103
105
  console.log(chalk.gray(` Backend: ${backendPort}`));
104
106
  console.log(chalk.gray(` Backend Core: ${backendCorePort}`));
105
107
  console.log(chalk.gray(` Frontend Core: ${frontendCorePort}`));
108
+ console.log(chalk.gray(` Frontend Core Mobile: ${frontendCoreMobilePort}`));
106
109
  if (installed.hasGateApp) {
107
110
  console.log(chalk.gray(` Gate App: ${gateAppPort}`));
108
111
  }
@@ -137,6 +140,7 @@ export async function regenerateCompose() {
137
140
  backendMlmPort,
138
141
  backendCorePort,
139
142
  frontendCorePort,
143
+ frontendCoreMobilePort,
140
144
  gateAppPort,
141
145
  guardianAppPort,
142
146
  facialWebPort,
@@ -97,9 +97,9 @@ function getEnvValue(envPath, key) {
97
97
  }
98
98
 
99
99
  /**
100
- * Check if gate-app, guardian-app, facial-web, pos-app, mlm-app, backend-mlm, frontend-core, or backend-core is installed by checking docker-compose.yml
100
+ * Check if gate-app, guardian-app, facial-web, pos-app, mlm-app, backend-mlm, frontend-core, frontend-core-mobile, or backend-core is installed by checking docker-compose.yml
101
101
  * @param {string} composeFile - Path to docker-compose.yml
102
- * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasMlmApp: boolean, hasBackendMlm: boolean, hasFrontendCore: boolean, hasBackendCore: boolean}}
102
+ * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasMlmApp: boolean, hasBackendMlm: boolean, hasFrontendCore: boolean, hasFrontendCoreMobile: boolean, hasBackendCore: boolean}}
103
103
  */
104
104
  function detectInstalledApps(composeFile) {
105
105
  try {
@@ -112,10 +112,11 @@ function detectInstalledApps(composeFile) {
112
112
  hasMlmApp: composeContent.includes('mlm-app:') || composeContent.includes('container_name: ante-mlm'),
113
113
  hasBackendMlm: composeContent.includes('backend-mlm:') || composeContent.includes('container_name: ante-backend-mlm'),
114
114
  hasFrontendCore: composeContent.includes('frontend-core:') || composeContent.includes('container_name: ante-frontend-core'),
115
+ hasFrontendCoreMobile: composeContent.includes('frontend-core-mobile:') || composeContent.includes('container_name: ante-frontend-core-mobile'),
115
116
  hasBackendCore: composeContent.includes('backend-core:') || composeContent.includes('container_name: ante-backend-core')
116
117
  };
117
118
  } catch {
118
- return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false, hasFrontendCore: false, hasBackendCore: false };
119
+ return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false, hasFrontendCore: false, hasFrontendCoreMobile: true, hasBackendCore: false };
119
120
  }
120
121
  }
121
122
 
@@ -174,10 +175,10 @@ export async function setDomain(options) {
174
175
  console.log(); // Add spacing
175
176
  }
176
177
 
177
- let frontendUrl, apiUrl, gateAppUrl, guardianAppUrl, facialWebUrl, posAppUrl, mlmAppUrl, backendMlmUrl, frontendCoreUrl, backendCoreUrl;
178
+ let frontendUrl, apiUrl, gateAppUrl, guardianAppUrl, facialWebUrl, posAppUrl, mlmAppUrl, backendMlmUrl, frontendCoreUrl, frontendCoreMobileUrl, backendCoreUrl;
178
179
 
179
180
  // Detect which apps are installed
180
- const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasBackendCore } = detectInstalledApps(composeFile);
181
+ const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasFrontendCoreMobile, hasBackendCore } = detectInstalledApps(composeFile);
181
182
 
182
183
  if (options.interactive !== false) {
183
184
  // Show current values
@@ -190,6 +191,7 @@ export async function setDomain(options) {
190
191
  const currentClientAppUrl = getEnvValue(envPath, 'MLM_APP_URL');
191
192
  const currentBackendClientUrl = getEnvValue(envPath, 'MLM_API_URL');
192
193
  const currentFrontendCoreUrl = getEnvValue(envPath, 'FRONTEND_CORE_URL');
194
+ const currentFrontendCoreMobileUrl = getEnvValue(envPath, 'FRONTEND_CORE_MOBILE_URL');
193
195
  const currentBackendCoreUrl = getEnvValue(envPath, 'BACKEND_CORE_URL');
194
196
 
195
197
  if (currentFrontendUrl) {
@@ -216,6 +218,9 @@ export async function setDomain(options) {
216
218
  if (currentFrontendCoreUrl) {
217
219
  console.log(chalk.gray(`Current Frontend Core URL: ${currentFrontendCoreUrl}`));
218
220
  }
221
+ if (currentFrontendCoreMobileUrl) {
222
+ console.log(chalk.gray(`Current Frontend Core Mobile URL: ${currentFrontendCoreMobileUrl}`));
223
+ }
219
224
  if (currentBackendCoreUrl) {
220
225
  console.log(chalk.gray(`Current Backend Core API URL: ${currentBackendCoreUrl}`));
221
226
  }
@@ -507,6 +512,7 @@ export async function setDomain(options) {
507
512
  if (hasMlmApp) mlmAppUrl = `https://${subdomain}-mlm.ante.ph`;
508
513
  if (hasBackendMlm) backendMlmUrl = `https://${subdomain}-api-mlm.ante.ph`;
509
514
  if (hasFrontendCore) frontendCoreUrl = `https://${subdomain}-core.ante.ph`;
515
+ if (hasFrontendCoreMobile) frontendCoreMobileUrl = `https://${subdomain}-mobile.ante.ph`;
510
516
  if (hasBackendCore) backendCoreUrl = `https://${subdomain}-api-core.ante.ph`;
511
517
 
512
518
  // Display generated domains for confirmation
@@ -520,6 +526,7 @@ export async function setDomain(options) {
520
526
  if (hasMlmApp) console.log(chalk.gray(` MLM App: ${mlmAppUrl}`));
521
527
  if (hasBackendMlm) console.log(chalk.gray(` Backend-MLM: ${backendMlmUrl}`));
522
528
  if (hasFrontendCore) console.log(chalk.gray(` Frontend Core: ${frontendCoreUrl}`));
529
+ if (hasFrontendCoreMobile) console.log(chalk.gray(` Frontend Core Mobile: ${frontendCoreMobileUrl}`));
523
530
  if (hasBackendCore) console.log(chalk.gray(` Backend Core: ${backendCoreUrl}`));
524
531
  console.log('');
525
532
 
@@ -618,6 +625,16 @@ export async function setDomain(options) {
618
625
  });
619
626
  }
620
627
 
628
+ if (hasFrontendCoreMobile) {
629
+ domainPrompts.push({
630
+ type: 'input',
631
+ name: 'frontendCoreMobileUrl',
632
+ message: 'Frontend Core Mobile URL:\n Examples: https://staging-mobile.ante.ph or http://143.198.91.153:8086\n Enter URL',
633
+ default: currentFrontendCoreMobileUrl || 'http://localhost:8086',
634
+ validate: validateUrl
635
+ });
636
+ }
637
+
621
638
  if (hasBackendCore) {
622
639
  domainPrompts.push({
623
640
  type: 'input',
@@ -648,6 +665,7 @@ export async function setDomain(options) {
648
665
  if (hasMlmApp) mlmAppUrl = sanitizeUrl(answers.mlmAppUrl);
649
666
  if (hasBackendMlm) backendMlmUrl = sanitizeUrl(answers.backendMlmUrl);
650
667
  if (hasFrontendCore) frontendCoreUrl = sanitizeUrl(answers.frontendCoreUrl);
668
+ if (hasFrontendCoreMobile) frontendCoreMobileUrl = sanitizeUrl(answers.frontendCoreMobileUrl);
651
669
  if (hasBackendCore) backendCoreUrl = sanitizeUrl(answers.backendCoreUrl);
652
670
  }
653
671
  }
@@ -745,6 +763,17 @@ export async function setDomain(options) {
745
763
  }
746
764
  }
747
765
 
766
+ if (hasFrontendCoreMobile) {
767
+ frontendCoreMobileUrl = options.frontendCoreMobile ? sanitizeUrl(options.frontendCoreMobile) : getEnvValue(envPath, 'FRONTEND_CORE_MOBILE_URL');
768
+ if (frontendCoreMobileUrl) {
769
+ const validation = validateUrl(frontendCoreMobileUrl);
770
+ if (validation !== true) {
771
+ console.log(chalk.red(`Error: Invalid Frontend Core Mobile URL - ${validation}`));
772
+ process.exit(1);
773
+ }
774
+ }
775
+ }
776
+
748
777
  // Show what will be configured in non-interactive mode
749
778
  console.log(chalk.cyan('\n📋 Non-interactive mode configuration:\n'));
750
779
  console.log(chalk.gray(` Frontend: ${frontendUrl}`));
@@ -755,6 +784,7 @@ export async function setDomain(options) {
755
784
  if (hasPosApp && posAppUrl) console.log(chalk.gray(` POS App: ${posAppUrl}`));
756
785
  if (hasMlmApp && mlmAppUrl) console.log(chalk.gray(` MLM App: ${mlmAppUrl}`));
757
786
  if (hasBackendMlm && backendMlmUrl) console.log(chalk.gray(` Backend-MLM: ${backendMlmUrl}`));
787
+ if (hasFrontendCoreMobile && frontendCoreMobileUrl) console.log(chalk.gray(` Frontend Core Mobile: ${frontendCoreMobileUrl}`));
758
788
  console.log('');
759
789
  }
760
790
 
@@ -795,6 +825,10 @@ export async function setDomain(options) {
795
825
  envUpdates.FRONTEND_CORE_URL = frontendCoreUrl;
796
826
  }
797
827
 
828
+ if (hasFrontendCoreMobile && frontendCoreMobileUrl) {
829
+ envUpdates.FRONTEND_CORE_MOBILE_URL = frontendCoreMobileUrl;
830
+ }
831
+
798
832
  if (hasBackendCore && backendCoreUrl) {
799
833
  envUpdates.BACKEND_CORE_URL = backendCoreUrl;
800
834
  }
@@ -862,6 +896,11 @@ export async function setDomain(options) {
862
896
  nginxConfig.frontendCorePort = 8085;
863
897
  }
864
898
 
899
+ if (hasFrontendCoreMobile && frontendCoreMobileUrl) {
900
+ nginxConfig.frontendCoreMobileDomain = frontendCoreMobileUrl;
901
+ nginxConfig.frontendCoreMobilePort = 8086;
902
+ }
903
+
865
904
  if (hasBackendCore && backendCoreUrl) {
866
905
  nginxConfig.backendCoreDomain = backendCoreUrl;
867
906
  nginxConfig.backendCorePort = 4002;
@@ -985,6 +1024,12 @@ export async function setDomain(options) {
985
1024
  domains.push(frontendCoreDomain);
986
1025
  }
987
1026
  }
1027
+ if (hasFrontendCoreMobile && frontendCoreMobileUrl && frontendCoreMobileUrl.startsWith('https://')) {
1028
+ const frontendCoreMobileDomain = extractDomain(frontendCoreMobileUrl);
1029
+ if (frontendCoreMobileDomain !== 'localhost' && !frontendCoreMobileDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.includes(frontendCoreMobileDomain)) {
1030
+ domains.push(frontendCoreMobileDomain);
1031
+ }
1032
+ }
988
1033
  if (hasBackendCore && backendCoreUrl && backendCoreUrl.startsWith('https://')) {
989
1034
  const backendCoreDomain = extractDomain(backendCoreUrl);
990
1035
  if (backendCoreDomain !== 'localhost' && !backendCoreDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.includes(backendCoreDomain)) {
@@ -1087,6 +1132,11 @@ export async function setDomain(options) {
1087
1132
  sslNginxConfig.frontendCorePort = 8085;
1088
1133
  }
1089
1134
 
1135
+ if (hasFrontendCoreMobile && frontendCoreMobileUrl) {
1136
+ sslNginxConfig.frontendCoreMobileDomain = frontendCoreMobileUrl;
1137
+ sslNginxConfig.frontendCoreMobilePort = 8086;
1138
+ }
1139
+
1090
1140
  if (hasBackendCore && backendCoreUrl) {
1091
1141
  sslNginxConfig.backendCoreDomain = backendCoreUrl;
1092
1142
  sslNginxConfig.backendCorePort = 4002;
@@ -1255,6 +1305,9 @@ export async function setDomain(options) {
1255
1305
  if (hasFrontendCore && frontendCoreUrl) {
1256
1306
  console.log(chalk.cyan('Frontend Core: '), chalk.white(frontendCoreUrl));
1257
1307
  }
1308
+ if (hasFrontendCoreMobile && frontendCoreMobileUrl) {
1309
+ console.log(chalk.cyan('Frontend Core Mobile: '), chalk.white(frontendCoreMobileUrl));
1310
+ }
1258
1311
  if (hasBackendCore && backendCoreUrl) {
1259
1312
  console.log(chalk.cyan('Backend Core: '), chalk.white(backendCoreUrl));
1260
1313
  }
@@ -1278,6 +1331,9 @@ export async function setDomain(options) {
1278
1331
  if (hasFrontendCore && frontendCoreUrl) {
1279
1332
  console.log(chalk.white(` Frontend Core: ${frontendCoreUrl}`));
1280
1333
  }
1334
+ if (hasFrontendCoreMobile && frontendCoreMobileUrl) {
1335
+ console.log(chalk.white(` Frontend Core Mobile: ${frontendCoreMobileUrl}`));
1336
+ }
1281
1337
  if (hasBackendCore && backendCoreUrl) {
1282
1338
  console.log(chalk.white(` Backend Core: ${backendCoreUrl}`));
1283
1339
  }
@@ -48,7 +48,7 @@ function getEnvValue(envPath, key) {
48
48
  /**
49
49
  * Check if optional apps are installed by checking docker-compose.yml
50
50
  * @param {string} composeFile - Path to docker-compose.yml
51
- * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasMlmApp: boolean, hasBackendMlm: boolean, hasFrontendCore: boolean, hasBackendCore: boolean}}
51
+ * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasMlmApp: boolean, hasBackendMlm: boolean, hasFrontendCore: boolean, hasFrontendCoreMobile: boolean, hasBackendCore: boolean}}
52
52
  */
53
53
  function detectInstalledApps(composeFile) {
54
54
  try {
@@ -61,10 +61,11 @@ function detectInstalledApps(composeFile) {
61
61
  hasMlmApp: composeContent.includes('mlm-app:') || composeContent.includes('container_name: ante-mlm'),
62
62
  hasBackendMlm: composeContent.includes('backend-mlm:') || composeContent.includes('container_name: ante-backend-mlm'),
63
63
  hasFrontendCore: composeContent.includes('frontend-core:') || composeContent.includes('container_name: ante-frontend-core'),
64
+ hasFrontendCoreMobile: composeContent.includes('frontend-core-mobile:') || composeContent.includes('container_name: ante-frontend-core-mobile'),
64
65
  hasBackendCore: composeContent.includes('backend-core:') || composeContent.includes('container_name: ante-backend-core')
65
66
  };
66
67
  } catch {
67
- return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false, hasFrontendCore: true, hasBackendCore: true };
68
+ return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false, hasFrontendCore: true, hasFrontendCoreMobile: true, hasBackendCore: true };
68
69
  }
69
70
  }
70
71
 
@@ -92,7 +93,7 @@ export async function sslEnable(options) {
92
93
  console.log(chalk.gray(`Installation: ${installDir}\n`));
93
94
 
94
95
  // Detect which apps are installed
95
- const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasBackendCore } = detectInstalledApps(composeFile);
96
+ const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasFrontendCoreMobile, hasBackendCore } = detectInstalledApps(composeFile);
96
97
 
97
98
  // Read current configuration
98
99
  const frontendUrl = getEnvValue(envPath, 'FRONTEND_URL');
@@ -104,6 +105,7 @@ export async function sslEnable(options) {
104
105
  const mlmAppUrl = hasMlmApp ? getEnvValue(envPath, 'MLM_APP_URL') : '';
105
106
  const backendMlmUrl = hasBackendMlm ? getEnvValue(envPath, 'MLM_API_URL') : '';
106
107
  const frontendCoreUrl = hasFrontendCore ? getEnvValue(envPath, 'FRONTEND_CORE_URL') : '';
108
+ const frontendCoreMobileUrl = hasFrontendCoreMobile ? getEnvValue(envPath, 'FRONTEND_CORE_MOBILE_URL') : '';
107
109
  const backendCoreUrl = hasBackendCore ? getEnvValue(envPath, 'BACKEND_CORE_URL') : '';
108
110
 
109
111
  if (!frontendUrl || !apiUrl) {
@@ -126,12 +128,14 @@ export async function sslEnable(options) {
126
128
  (!mlmAppUrl || mlmAppUrl.startsWith('https://')) &&
127
129
  (!backendMlmUrl || backendMlmUrl.startsWith('https://')) &&
128
130
  (!frontendCoreUrl || frontendCoreUrl.startsWith('https://')) &&
131
+ (!frontendCoreMobileUrl || frontendCoreMobileUrl.startsWith('https://')) &&
129
132
  (!backendCoreUrl || backendCoreUrl.startsWith('https://'));
130
133
 
131
134
  if (allHttps) {
132
135
  console.log(chalk.yellow('⚠ Domains are already configured with HTTPS:\n'));
133
136
  console.log(chalk.gray(` Frontend: ${frontendUrl}`));
134
137
  if (frontendCoreUrl) console.log(chalk.gray(` Frontend Core: ${frontendCoreUrl}`));
138
+ if (frontendCoreMobileUrl) console.log(chalk.gray(` Frontend Core Mobile: ${frontendCoreMobileUrl}`));
135
139
  if (gateAppUrl) console.log(chalk.gray(` Gate App: ${gateAppUrl}`));
136
140
  if (guardianAppUrl) console.log(chalk.gray(` Guardian: ${guardianAppUrl}`));
137
141
  if (facialWebUrl) console.log(chalk.gray(` Facial Web: ${facialWebUrl}`));
@@ -205,6 +209,14 @@ export async function sslEnable(options) {
205
209
  }
206
210
  }
207
211
 
212
+ if (frontendCoreMobileUrl) {
213
+ const frontendCoreMobileDomain = extractDomain(frontendCoreMobileUrl);
214
+ const frontendCoreMobileStatus = await checkSSLStatus(frontendCoreMobileDomain);
215
+ if (frontendCoreMobileStatus.installed) {
216
+ console.log(chalk.green(`✓ Frontend Core Mobile SSL: Valid (expires in ${frontendCoreMobileStatus.daysUntilExpiry} days)`));
217
+ }
218
+ }
219
+
208
220
  if (backendCoreUrl) {
209
221
  const backendCoreDomain = extractDomain(backendCoreUrl);
210
222
  const backendCoreStatus = await checkSSLStatus(backendCoreDomain);
@@ -243,6 +255,7 @@ export async function sslEnable(options) {
243
255
  console.log(chalk.cyan('Current Configuration:'));
244
256
  console.log(chalk.gray(` Frontend: ${frontendUrl}`));
245
257
  if (frontendCoreUrl) console.log(chalk.gray(` Frontend Core: ${frontendCoreUrl}`));
258
+ if (frontendCoreMobileUrl) console.log(chalk.gray(` Frontend Core Mobile: ${frontendCoreMobileUrl}`));
246
259
  if (gateAppUrl) console.log(chalk.gray(` Gate App: ${gateAppUrl}`));
247
260
  if (guardianAppUrl) console.log(chalk.gray(` Guardian: ${guardianAppUrl}`));
248
261
  if (facialWebUrl) console.log(chalk.gray(` Facial Web: ${facialWebUrl}`));
@@ -348,6 +361,18 @@ export async function sslEnable(options) {
348
361
  }
349
362
  }
350
363
 
364
+ if (frontendCoreMobileUrl) {
365
+ const frontendCoreMobileDomain = extractDomain(frontendCoreMobileUrl);
366
+ if (frontendCoreMobileDomain !== 'localhost' && !frontendCoreMobileDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.find(d => d.domain === frontendCoreMobileDomain)) {
367
+ domains.push({
368
+ name: 'Frontend Core Mobile',
369
+ domain: frontendCoreMobileDomain,
370
+ port: frontendCoreMobileUrl.match(/:(\d+)/) ? frontendCoreMobileUrl.match(/:(\d+)/)[1] : 8086,
371
+ url: frontendCoreMobileUrl
372
+ });
373
+ }
374
+ }
375
+
351
376
  if (backendCoreUrl) {
352
377
  const backendCoreDomain = extractDomain(backendCoreUrl);
353
378
  if (backendCoreDomain !== 'localhost' && !backendCoreDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.find(d => d.domain === backendCoreDomain)) {
@@ -528,6 +553,11 @@ export async function sslEnable(options) {
528
553
  sslConfig.frontendCorePort = frontendCoreUrl.match(/:(\d+)/) ? parseInt(frontendCoreUrl.match(/:(\d+)/)[1]) : 8085;
529
554
  }
530
555
 
556
+ if (frontendCoreMobileUrl) {
557
+ sslConfig.frontendCoreMobileDomain = frontendCoreMobileUrl;
558
+ sslConfig.frontendCoreMobilePort = frontendCoreMobileUrl.match(/:(\d+)/) ? parseInt(frontendCoreMobileUrl.match(/:(\d+)/)[1]) : 8086;
559
+ }
560
+
531
561
  if (backendCoreUrl) {
532
562
  sslConfig.backendCoreDomain = backendCoreUrl;
533
563
  sslConfig.backendCorePort = backendCoreUrl.match(/:(\d+)/) ? parseInt(backendCoreUrl.match(/:(\d+)/)[1]) : 4002;
@@ -619,7 +649,7 @@ export async function sslStatus() {
619
649
  const composeFile = join(installDir, 'docker-compose.yml');
620
650
 
621
651
  // Detect which apps are installed
622
- const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasBackendCore } = detectInstalledApps(composeFile);
652
+ const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasFrontendCoreMobile, hasBackendCore } = detectInstalledApps(composeFile);
623
653
 
624
654
  const frontendUrl = getEnvValue(envPath, 'FRONTEND_URL');
625
655
  const apiUrl = getEnvValue(envPath, 'API_URL');
@@ -630,6 +660,7 @@ export async function sslStatus() {
630
660
  const mlmAppUrl = hasMlmApp ? getEnvValue(envPath, 'MLM_APP_URL') : '';
631
661
  const backendMlmUrl = hasBackendMlm ? getEnvValue(envPath, 'MLM_API_URL') : '';
632
662
  const frontendCoreUrl = hasFrontendCore ? getEnvValue(envPath, 'FRONTEND_CORE_URL') : '';
663
+ const frontendCoreMobileUrl = hasFrontendCoreMobile ? getEnvValue(envPath, 'FRONTEND_CORE_MOBILE_URL') : '';
633
664
  const backendCoreUrl = hasBackendCore ? getEnvValue(envPath, 'BACKEND_CORE_URL') : '';
634
665
 
635
666
  if (!frontendUrl || !apiUrl) {
@@ -679,6 +710,11 @@ export async function sslStatus() {
679
710
  domains.push({ name: 'Frontend Core', domain: frontendCoreDomain, url: frontendCoreUrl });
680
711
  }
681
712
 
713
+ if (frontendCoreMobileUrl) {
714
+ const frontendCoreMobileDomain = extractDomain(frontendCoreMobileUrl);
715
+ domains.push({ name: 'Frontend Core Mobile', domain: frontendCoreMobileDomain, url: frontendCoreMobileUrl });
716
+ }
717
+
682
718
  if (backendCoreUrl) {
683
719
  const backendCoreDomain = extractDomain(backendCoreUrl);
684
720
  domains.push({ name: 'Backend Core', domain: backendCoreDomain, url: backendCoreUrl });
@@ -84,6 +84,7 @@ function updateDockerCompose(composeFile, envFile, newServices) {
84
84
  backendMlmPort: parseInt(envConfig.BACKEND_MLM_PORT) || 4001,
85
85
  backendCorePort: parseInt(envConfig.BACKEND_CORE_PORT) || 4002,
86
86
  frontendCorePort: parseInt(envConfig.FRONTEND_CORE_PORT) || 8085,
87
+ frontendCoreMobilePort: parseInt(envConfig.FRONTEND_CORE_MOBILE_PORT) || 8086,
87
88
  gateAppPort: parseInt(envConfig.GATE_APP_PORT) || 8081,
88
89
  guardianAppPort: parseInt(envConfig.GUARDIAN_APP_PORT) || 8082,
89
90
  facialWebPort: parseInt(envConfig.FACIAL_WEB_PORT) || 8083,
@@ -351,6 +352,7 @@ function refreshDockerCompose(composeFile, envFile) {
351
352
  backendMlmPort: parseInt(envConfig.BACKEND_MLM_PORT) || 4001,
352
353
  backendCorePort: parseInt(envConfig.BACKEND_CORE_PORT) || 4002,
353
354
  frontendCorePort: parseInt(envConfig.FRONTEND_CORE_PORT) || 8085,
355
+ frontendCoreMobilePort: parseInt(envConfig.FRONTEND_CORE_MOBILE_PORT) || 8086,
354
356
  gateAppPort: parseInt(envConfig.GATE_APP_PORT) || 8081,
355
357
  guardianAppPort: parseInt(envConfig.GUARDIAN_APP_PORT) || 8082,
356
358
  facialWebPort: parseInt(envConfig.FACIAL_WEB_PORT) || 8083,
@@ -402,6 +404,7 @@ function getRequiredPorts(composeFile, envFile) {
402
404
  ports.push(parseInt(envConfig.FRONTEND_PORT) || 8080); // Frontend
403
405
  ports.push(parseInt(envConfig.BACKEND_CORE_PORT) || 4002); // Backend Core (always included)
404
406
  ports.push(parseInt(envConfig.FRONTEND_CORE_PORT) || 8085); // Frontend Core (always included)
407
+ ports.push(parseInt(envConfig.FRONTEND_CORE_MOBILE_PORT) || 8086); // Frontend Core Mobile (always included)
405
408
 
406
409
  // Optional services - only add if installed
407
410
  if (installed.hasGateApp) {
@@ -10,6 +10,7 @@ export function generateDockerCompose(options = {}) {
10
10
  backendMlmPort = 4001,
11
11
  backendCorePort = 4002,
12
12
  frontendCorePort = 8085,
13
+ frontendCoreMobilePort = 8086,
13
14
  gateAppPort = 8081,
14
15
  guardianAppPort = 8082,
15
16
  facialWebPort = 8083,
@@ -258,6 +259,34 @@ services:
258
259
  options:
259
260
  max-size: "10m"
260
261
  max-file: "3"
262
+
263
+ # ANTE Frontend Core Mobile - React Mobile Application
264
+ frontend-core-mobile:
265
+ image: ghcr.io/gtplusnet/ante-self-hosted-frontend-core-mobile:\${IMAGE_TAG:-latest}
266
+ container_name: ante-frontend-core-mobile
267
+ restart: unless-stopped
268
+ depends_on:
269
+ backend-core:
270
+ condition: service_healthy
271
+ environment:
272
+ - VITE_API_URL=\${BACKEND_CORE_URL:-http://localhost:${backendCorePort}}
273
+ - VITE_APP_NAME=ANTE Frontend Core Mobile
274
+ - VITE_APP_VERSION=\${VERSION:-1.0.0}
275
+ ports:
276
+ - "${frontendCoreMobilePort}:8086"
277
+ networks:
278
+ - ante-network
279
+ healthcheck:
280
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8086/"]
281
+ interval: 30s
282
+ timeout: 10s
283
+ retries: 3
284
+ start_period: 30s
285
+ logging:
286
+ driver: "json-file"
287
+ options:
288
+ max-size: "10m"
289
+ max-file: "3"
261
290
  ${installGate ? `
262
291
  # ANTE Gate App
263
292
  gate-app:
@@ -14,6 +14,7 @@ export function generateEnv(credentials, options = {}) {
14
14
  backendMlmPort = 4001,
15
15
  backendCorePort = 4002,
16
16
  frontendCorePort = 8085,
17
+ frontendCoreMobilePort = 8086,
17
18
  gateAppPort = 8081,
18
19
  guardianAppPort = 8082,
19
20
  facialWebPort = 8083,
@@ -26,6 +27,7 @@ export function generateEnv(credentials, options = {}) {
26
27
  mlmAppUrl = 'http://localhost:9005',
27
28
  mlmApiUrl = 'http://localhost:4001',
28
29
  frontendCoreUrl = 'http://localhost:8085',
30
+ frontendCoreMobileUrl = 'http://localhost:8086',
29
31
  backendCoreUrl = 'http://localhost:4002',
30
32
  companyId = 1,
31
33
  installGate = false,
@@ -80,6 +82,7 @@ FRONTEND_PORT=${frontendPort}
80
82
  BACKEND_PORT=${backendPort}
81
83
  BACKEND_CORE_PORT=${backendCorePort}
82
84
  FRONTEND_CORE_PORT=${frontendCorePort}
85
+ FRONTEND_CORE_MOBILE_PORT=${frontendCoreMobilePort}
83
86
  ${installMlm ? `BACKEND_MLM_PORT=${backendMlmPort}` : '# BACKEND_MLM_PORT=4001'}
84
87
  ${installGate ? `GATE_APP_PORT=${gateAppPort}` : '# GATE_APP_PORT=8081'}
85
88
  ${installGuardian ? `GUARDIAN_APP_PORT=${guardianAppPort}` : '# GUARDIAN_APP_PORT=8082'}
@@ -92,6 +95,7 @@ ${installMlm ? `MLM_APP_PORT=${mlmAppPort}` : '# MLM_APP_PORT=9005'}
92
95
  # CORE SERVICES CONFIGURATION (Always Included)
93
96
  # ------------------------------------------------------------------------------
94
97
  FRONTEND_CORE_URL=${frontendCoreUrl}
98
+ FRONTEND_CORE_MOBILE_URL=${frontendCoreMobileUrl}
95
99
  BACKEND_CORE_URL=${backendCoreUrl}
96
100
  CORE_API_URL=${backendCoreUrl}
97
101
 
@@ -55,6 +55,7 @@ export async function installNginx(spinner) {
55
55
  * @param {string} [config.mlmAppDomain] - MLM app domain (optional)
56
56
  * @param {string} [config.backendMlmDomain] - Backend MLM API domain (optional)
57
57
  * @param {string} [config.frontendCoreDomain] - Frontend Core app domain (optional)
58
+ * @param {string} [config.frontendCoreMobileDomain] - Frontend Core Mobile app domain (optional)
58
59
  * @param {string} [config.backendCoreDomain] - Backend Core API domain (optional)
59
60
  * @param {number} config.frontendPort - Frontend Docker port (default: 8080)
60
61
  * @param {number} config.apiPort - API Docker port (default: 3001)
@@ -65,6 +66,7 @@ export async function installNginx(spinner) {
65
66
  * @param {number} [config.mlmAppPort] - MLM app Docker port (default: 9005)
66
67
  * @param {number} [config.backendMlmPort] - Backend MLM API Docker port (default: 4001)
67
68
  * @param {number} [config.frontendCorePort] - Frontend Core Docker port (default: 8085)
69
+ * @param {number} [config.frontendCoreMobilePort] - Frontend Core Mobile Docker port (default: 8086)
68
70
  * @param {number} [config.backendCorePort] - Backend Core API Docker port (default: 4002)
69
71
  * @param {boolean} config.ssl - Enable SSL configuration with Let's Encrypt (default: false)
70
72
  * @param {boolean} config.cloudflareSsl - Enable Cloudflare SSL mode with self-signed certs (default: false)
@@ -81,6 +83,7 @@ export function generateNginxConfig(config) {
81
83
  mlmAppDomain,
82
84
  backendMlmDomain,
83
85
  frontendCoreDomain,
86
+ frontendCoreMobileDomain,
84
87
  backendCoreDomain,
85
88
  frontendPort = 8080,
86
89
  apiPort = 3001,
@@ -91,6 +94,7 @@ export function generateNginxConfig(config) {
91
94
  mlmAppPort = 9005,
92
95
  backendMlmPort = 4001,
93
96
  frontendCorePort = 8085,
97
+ frontendCoreMobilePort = 8086,
94
98
  backendCorePort = 4002,
95
99
  ssl = false,
96
100
  cloudflareSsl = false,
@@ -107,6 +111,7 @@ export function generateNginxConfig(config) {
107
111
  const mlmAppHost = mlmAppDomain ? mlmAppDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
108
112
  const backendMlmHost = backendMlmDomain ? backendMlmDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
109
113
  const frontendCoreHost = frontendCoreDomain ? frontendCoreDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
114
+ const frontendCoreMobileHost = frontendCoreMobileDomain ? frontendCoreMobileDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
110
115
  const backendCoreHost = backendCoreDomain ? backendCoreDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
111
116
 
112
117
  // Cloudflare SSL mode - uses self-signed certs for Cloudflare "Full" mode
@@ -121,6 +126,7 @@ export function generateNginxConfig(config) {
121
126
  mlmAppHost,
122
127
  backendMlmHost,
123
128
  frontendCoreHost,
129
+ frontendCoreMobileHost,
124
130
  backendCoreHost,
125
131
  frontendPort,
126
132
  apiPort,
@@ -131,6 +137,7 @@ export function generateNginxConfig(config) {
131
137
  mlmAppPort,
132
138
  backendMlmPort,
133
139
  frontendCorePort,
140
+ frontendCoreMobilePort,
134
141
  backendCorePort
135
142
  });
136
143
  }
@@ -147,6 +154,7 @@ export function generateNginxConfig(config) {
147
154
  mlmAppHost,
148
155
  backendMlmHost,
149
156
  frontendCoreHost,
157
+ frontendCoreMobileHost,
150
158
  backendCoreHost,
151
159
  frontendPort,
152
160
  apiPort,
@@ -157,6 +165,7 @@ export function generateNginxConfig(config) {
157
165
  mlmAppPort,
158
166
  backendMlmPort,
159
167
  frontendCorePort,
168
+ frontendCoreMobilePort,
160
169
  backendCorePort,
161
170
  domainsWithCerts
162
171
  });
@@ -454,6 +463,47 @@ server {
454
463
  proxy_read_timeout 60s;
455
464
  }
456
465
  }
466
+ ` : ''}${frontendCoreMobileHost ? `
467
+ # ANTE Frontend Core Mobile Configuration
468
+ server {
469
+ listen 80;
470
+ listen [::]:80;
471
+ server_name ${frontendCoreMobileHost};
472
+
473
+ # Increase buffer sizes for large headers
474
+ client_header_buffer_size 16k;
475
+ large_client_header_buffers 4 16k;
476
+
477
+ # Increase body size for file uploads
478
+ client_max_body_size 100M;
479
+
480
+ # Disable caching for config.js to ensure runtime config is always fresh
481
+ location = /config.js {
482
+ proxy_pass http://localhost:${frontendCoreMobilePort}/config.js;
483
+ proxy_http_version 1.1;
484
+ proxy_set_header Host $host;
485
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
486
+ add_header Pragma "no-cache" always;
487
+ add_header Expires "0" always;
488
+ }
489
+
490
+ location / {
491
+ proxy_pass http://localhost:${frontendCoreMobilePort};
492
+ proxy_http_version 1.1;
493
+ proxy_set_header Upgrade $http_upgrade;
494
+ proxy_set_header Connection 'upgrade';
495
+ proxy_set_header Host $host;
496
+ proxy_set_header X-Real-IP $remote_addr;
497
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
498
+ proxy_set_header X-Forwarded-Proto $scheme;
499
+ proxy_cache_bypass $http_upgrade;
500
+
501
+ # Timeout settings
502
+ proxy_connect_timeout 60s;
503
+ proxy_send_timeout 60s;
504
+ proxy_read_timeout 60s;
505
+ }
506
+ }
457
507
  ` : ''}${backendCoreHost ? `
458
508
  # ANTE Backend Core API Configuration
459
509
  server {
@@ -510,6 +560,7 @@ function generateCloudflareNginxConfig(config) {
510
560
  mlmAppHost,
511
561
  backendMlmHost,
512
562
  frontendCoreHost,
563
+ frontendCoreMobileHost,
513
564
  backendCoreHost,
514
565
  frontendPort,
515
566
  apiPort,
@@ -520,6 +571,7 @@ function generateCloudflareNginxConfig(config) {
520
571
  mlmAppPort = 9005,
521
572
  backendMlmPort = 4001,
522
573
  frontendCorePort = 8085,
574
+ frontendCoreMobilePort = 8086,
523
575
  backendCorePort = 4002
524
576
  } = config;
525
577
 
@@ -873,6 +925,53 @@ server {
873
925
  proxy_read_timeout 60s;
874
926
  }
875
927
  }
928
+ ` : ''}${frontendCoreMobileHost ? `
929
+ # ANTE Frontend Core Mobile Configuration (Cloudflare SSL)
930
+ server {
931
+ listen 80;
932
+ listen 443 ssl;
933
+ listen [::]:80;
934
+ listen [::]:443 ssl;
935
+ server_name ${frontendCoreMobileHost};
936
+
937
+ # SSL Certificate (self-signed for Cloudflare Full mode)
938
+ ssl_certificate ${sslCert};
939
+ ssl_certificate_key ${sslKey};
940
+
941
+ # Increase buffer sizes for large headers
942
+ client_header_buffer_size 16k;
943
+ large_client_header_buffers 4 16k;
944
+
945
+ # Increase body size for file uploads
946
+ client_max_body_size 100M;
947
+
948
+ # Disable caching for config.js to ensure runtime config is always fresh
949
+ location = /config.js {
950
+ proxy_pass http://localhost:${frontendCoreMobilePort}/config.js;
951
+ proxy_http_version 1.1;
952
+ proxy_set_header Host $host;
953
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
954
+ add_header Pragma "no-cache" always;
955
+ add_header Expires "0" always;
956
+ }
957
+
958
+ location / {
959
+ proxy_pass http://localhost:${frontendCoreMobilePort};
960
+ proxy_http_version 1.1;
961
+ proxy_set_header Upgrade $http_upgrade;
962
+ proxy_set_header Connection 'upgrade';
963
+ proxy_set_header Host $host;
964
+ proxy_set_header X-Real-IP $remote_addr;
965
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
966
+ proxy_set_header X-Forwarded-Proto $scheme;
967
+ proxy_cache_bypass $http_upgrade;
968
+
969
+ # Timeout settings
970
+ proxy_connect_timeout 60s;
971
+ proxy_send_timeout 60s;
972
+ proxy_read_timeout 60s;
973
+ }
974
+ }
876
975
  ` : ''}${backendCoreHost ? `
877
976
  # ANTE Backend Core API Configuration (Cloudflare SSL)
878
977
  server {
@@ -963,6 +1062,7 @@ export async function generateCloudflareCert() {
963
1062
  * @param {string} [config.mlmAppHost] - MLM app hostname (optional)
964
1063
  * @param {string} [config.backendMlmHost] - Backend MLM API hostname (optional)
965
1064
  * @param {string} [config.frontendCoreHost] - Frontend Core hostname (optional)
1065
+ * @param {string} [config.frontendCoreMobileHost] - Frontend Core Mobile hostname (optional)
966
1066
  * @param {string} [config.backendCoreHost] - Backend Core API hostname (optional)
967
1067
  * @param {number} config.frontendPort - Frontend port
968
1068
  * @param {number} config.apiPort - API port
@@ -973,6 +1073,7 @@ export async function generateCloudflareCert() {
973
1073
  * @param {number} [config.mlmAppPort] - MLM app port (default: 9005)
974
1074
  * @param {number} [config.backendMlmPort] - Backend MLM API port (default: 4001)
975
1075
  * @param {number} [config.frontendCorePort] - Frontend Core port (default: 8085)
1076
+ * @param {number} [config.frontendCoreMobilePort] - Frontend Core Mobile port (default: 8086)
976
1077
  * @param {number} [config.backendCorePort] - Backend Core API port (default: 4002)
977
1078
  * @param {string[]} [config.domainsWithCerts] - List of domains that have valid SSL certificates
978
1079
  * @returns {string} NGINX configuration with SSL
@@ -988,6 +1089,7 @@ function generateSslNginxConfig(config) {
988
1089
  mlmAppHost,
989
1090
  backendMlmHost,
990
1091
  frontendCoreHost,
1092
+ frontendCoreMobileHost,
991
1093
  backendCoreHost,
992
1094
  frontendPort,
993
1095
  apiPort,
@@ -998,6 +1100,7 @@ function generateSslNginxConfig(config) {
998
1100
  mlmAppPort = 9005,
999
1101
  backendMlmPort = 4001,
1000
1102
  frontendCorePort = 8085,
1103
+ frontendCoreMobilePort = 8086,
1001
1104
  backendCorePort = 4002,
1002
1105
  domainsWithCerts = []
1003
1106
  } = config;
@@ -1479,6 +1582,121 @@ server {
1479
1582
  }
1480
1583
  }
1481
1584
 
1585
+ if (frontendCoreMobileHost) {
1586
+ // Frontend Core Mobile has special config.js caching rules
1587
+ if (hasCert(frontendCoreMobileHost)) {
1588
+ config_output += `
1589
+ # ANTE Frontend Core Mobile Configuration (HTTPS)
1590
+ server {
1591
+ listen 443 ssl;
1592
+ listen [::]:443 ssl;
1593
+ http2 on;
1594
+ server_name ${frontendCoreMobileHost};
1595
+
1596
+ # SSL Certificate paths
1597
+ ssl_certificate /etc/letsencrypt/live/${frontendCoreMobileHost}/fullchain.pem;
1598
+ ssl_certificate_key /etc/letsencrypt/live/${frontendCoreMobileHost}/privkey.pem;
1599
+
1600
+ # SSL Configuration
1601
+ ssl_protocols TLSv1.2 TLSv1.3;
1602
+ ssl_ciphers HIGH:!aNULL:!MD5;
1603
+ ssl_prefer_server_ciphers on;
1604
+ ssl_session_cache shared:SSL:10m;
1605
+ ssl_session_timeout 10m;
1606
+
1607
+ # Security headers
1608
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
1609
+ add_header X-Frame-Options SAMEORIGIN always;
1610
+ add_header X-Content-Type-Options nosniff always;
1611
+ add_header X-XSS-Protection "1; mode=block" always;
1612
+
1613
+ # Increase buffer sizes for large headers
1614
+ client_header_buffer_size 16k;
1615
+ large_client_header_buffers 4 16k;
1616
+
1617
+ # Increase body size for file uploads
1618
+ client_max_body_size 100M;
1619
+
1620
+ # Disable caching for config.js to ensure runtime config is always fresh
1621
+ location = /config.js {
1622
+ proxy_pass http://localhost:${frontendCoreMobilePort}/config.js;
1623
+ proxy_http_version 1.1;
1624
+ proxy_set_header Host $host;
1625
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
1626
+ add_header Pragma "no-cache" always;
1627
+ add_header Expires "0" always;
1628
+ }
1629
+
1630
+ location / {
1631
+ proxy_pass http://localhost:${frontendCoreMobilePort};
1632
+ proxy_http_version 1.1;
1633
+ proxy_set_header Upgrade $http_upgrade;
1634
+ proxy_set_header Connection 'upgrade';
1635
+ proxy_set_header Host $host;
1636
+ proxy_set_header X-Real-IP $remote_addr;
1637
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1638
+ proxy_set_header X-Forwarded-Proto $scheme;
1639
+ proxy_cache_bypass $http_upgrade;
1640
+
1641
+ # Timeout settings
1642
+ proxy_connect_timeout 60s;
1643
+ proxy_send_timeout 60s;
1644
+ proxy_read_timeout 60s;
1645
+ }
1646
+ }
1647
+
1648
+ # HTTP to HTTPS redirect for ${frontendCoreMobileHost}
1649
+ server {
1650
+ listen 80;
1651
+ listen [::]:80;
1652
+ server_name ${frontendCoreMobileHost};
1653
+ return 301 https://$server_name$request_uri;
1654
+ }`;
1655
+ } else {
1656
+ config_output += `
1657
+ # ANTE Frontend Core Mobile Configuration (HTTP only - SSL cert not available)
1658
+ server {
1659
+ listen 80;
1660
+ listen [::]:80;
1661
+ server_name ${frontendCoreMobileHost};
1662
+
1663
+ # Increase buffer sizes for large headers
1664
+ client_header_buffer_size 16k;
1665
+ large_client_header_buffers 4 16k;
1666
+
1667
+ # Increase body size for file uploads
1668
+ client_max_body_size 100M;
1669
+
1670
+ # Disable caching for config.js to ensure runtime config is always fresh
1671
+ location = /config.js {
1672
+ proxy_pass http://localhost:${frontendCoreMobilePort}/config.js;
1673
+ proxy_http_version 1.1;
1674
+ proxy_set_header Host $host;
1675
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
1676
+ add_header Pragma "no-cache" always;
1677
+ add_header Expires "0" always;
1678
+ }
1679
+
1680
+ location / {
1681
+ proxy_pass http://localhost:${frontendCoreMobilePort};
1682
+ proxy_http_version 1.1;
1683
+ proxy_set_header Upgrade $http_upgrade;
1684
+ proxy_set_header Connection 'upgrade';
1685
+ proxy_set_header Host $host;
1686
+ proxy_set_header X-Real-IP $remote_addr;
1687
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1688
+ proxy_set_header X-Forwarded-Proto $scheme;
1689
+ proxy_cache_bypass $http_upgrade;
1690
+
1691
+ # Timeout settings
1692
+ proxy_connect_timeout 60s;
1693
+ proxy_send_timeout 60s;
1694
+ proxy_read_timeout 60s;
1695
+ }
1696
+ }`;
1697
+ }
1698
+ }
1699
+
1482
1700
  if (backendCoreHost) {
1483
1701
  config_output += generateServerBlock(backendCoreHost, backendCorePort, 'ANTE Backend Core API');
1484
1702
  }
@@ -1599,6 +1817,9 @@ export async function configureNginx(config) {
1599
1817
  if (config.frontendCoreDomain) {
1600
1818
  console.log(chalk.gray(` Frontend Core: ${config.frontendCoreDomain} → localhost:${config.frontendCorePort || 8085}`));
1601
1819
  }
1820
+ if (config.frontendCoreMobileDomain) {
1821
+ console.log(chalk.gray(` Frontend Core Mobile: ${config.frontendCoreMobileDomain} → localhost:${config.frontendCoreMobilePort || 8086}`));
1822
+ }
1602
1823
  if (config.backendCoreDomain) {
1603
1824
  console.log(chalk.gray(` Backend Core API: ${config.backendCoreDomain} → localhost:${config.backendCorePort || 4002}`));
1604
1825
  }
package/src/utils/ssl.js CHANGED
@@ -142,6 +142,7 @@ export async function obtainSSLCertificate(config) {
142
142
  * @param {string} [config.mlmAppDomain] - MLM App domain URL (optional)
143
143
  * @param {string} [config.backendMlmDomain] - Backend MLM API domain URL (optional)
144
144
  * @param {string} [config.frontendCoreDomain] - Frontend Core domain URL (optional)
145
+ * @param {string} [config.frontendCoreMobileDomain] - Frontend Core Mobile domain URL (optional)
145
146
  * @param {string} [config.backendCoreDomain] - Backend Core API domain URL (optional)
146
147
  * @param {number} [config.frontendPort=8080] - Frontend port
147
148
  * @param {number} [config.apiPort=3001] - API port
@@ -152,6 +153,7 @@ export async function obtainSSLCertificate(config) {
152
153
  * @param {number} [config.mlmAppPort=9005] - MLM App port
153
154
  * @param {number} [config.backendMlmPort=4001] - Backend MLM API port
154
155
  * @param {number} [config.frontendCorePort=8085] - Frontend Core port
156
+ * @param {number} [config.frontendCoreMobilePort=8086] - Frontend Core Mobile port
155
157
  * @param {number} [config.backendCorePort=4002] - Backend Core API port
156
158
  * @param {string[]} [config.domainsWithCerts=[]] - List of domains that have valid SSL certificates
157
159
  * @returns {Promise<void>}
@@ -167,6 +169,7 @@ export async function updateNginxForSSL(config) {
167
169
  mlmAppDomain,
168
170
  backendMlmDomain,
169
171
  frontendCoreDomain,
172
+ frontendCoreMobileDomain,
170
173
  backendCoreDomain,
171
174
  frontendPort = 8080,
172
175
  apiPort = 3001,
@@ -177,6 +180,7 @@ export async function updateNginxForSSL(config) {
177
180
  mlmAppPort = 9005,
178
181
  backendMlmPort = 4001,
179
182
  frontendCorePort = 8085,
183
+ frontendCoreMobilePort = 8086,
180
184
  backendCorePort = 4002,
181
185
  domainsWithCerts = []
182
186
  } = config;
@@ -208,6 +212,7 @@ export async function updateNginxForSSL(config) {
208
212
  mlmAppDomain,
209
213
  backendMlmDomain,
210
214
  frontendCoreDomain,
215
+ frontendCoreMobileDomain,
211
216
  backendCoreDomain,
212
217
  frontendPort,
213
218
  apiPort,
@@ -218,6 +223,7 @@ export async function updateNginxForSSL(config) {
218
223
  mlmAppPort,
219
224
  backendMlmPort,
220
225
  frontendCorePort,
226
+ frontendCoreMobilePort,
221
227
  backendCorePort,
222
228
  ssl: true,
223
229
  domainsWithCerts
@@ -229,7 +229,7 @@ export async function checkPort(port) {
229
229
  */
230
230
  function detectPortsFromEnv(installDir = './ante-erp') {
231
231
  // Default ports now include core services (always included)
232
- const defaultPorts = [8080, 3001, 4001, 4002, 8085];
232
+ const defaultPorts = [8080, 3001, 4001, 4002, 8085, 8086];
233
233
 
234
234
  try {
235
235
  const envPath = join(installDir, '.env');
@@ -277,6 +277,15 @@ function detectPortsFromEnv(installDir = './ante-erp') {
277
277
  }
278
278
  }
279
279
 
280
+ // Check for FRONTEND_CORE_MOBILE_PORT (in case it's customized)
281
+ const frontendCoreMobilePortMatch = envContent.match(/^FRONTEND_CORE_MOBILE_PORT=(\d+)/m);
282
+ if (frontendCoreMobilePortMatch) {
283
+ const frontendCoreMobilePort = parseInt(frontendCoreMobilePortMatch[1]);
284
+ if (!ports.includes(frontendCoreMobilePort)) {
285
+ ports.push(frontendCoreMobilePort);
286
+ }
287
+ }
288
+
280
289
  return ports;
281
290
  } catch (error) {
282
291
  return defaultPorts;