ante-erp-cli 1.11.66 → 1.11.67

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ante-erp-cli",
3
- "version": "1.11.66",
3
+ "version": "1.11.67",
4
4
  "description": "Comprehensive CLI tool for managing ANTE ERP self-hosted installations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -78,6 +78,8 @@ function showWelcome() {
78
78
  function showSuccess(installDir, credentials, config) {
79
79
  const frontendUrl = config.frontendDomain || 'http://localhost:8080';
80
80
  const apiUrl = config.apiDomain || 'http://localhost:3001';
81
+ const frontendCoreUrl = config.frontendCoreDomain || 'http://localhost:8085';
82
+ const backendCoreUrl = config.backendCoreDomain || 'http://localhost:4002';
81
83
  const gateAppUrl = config.gateAppDomain || 'http://localhost:8081';
82
84
  const guardianAppUrl = config.guardianAppDomain || 'http://localhost:8082';
83
85
  const facialWebUrl = config.facialAppDomain || 'http://localhost:8083';
@@ -86,7 +88,8 @@ function showSuccess(installDir, credentials, config) {
86
88
 
87
89
  let accessInfo = chalk.white('Access Information:') + '\n' +
88
90
  chalk.gray('━'.repeat(40)) + '\n' +
89
- chalk.cyan('Frontend Main: ') + chalk.white(frontendUrl) + '\n';
91
+ chalk.cyan('Frontend Main: ') + chalk.white(frontendUrl) + '\n' +
92
+ chalk.cyan('Frontend Core: ') + chalk.white(frontendCoreUrl) + '\n';
90
93
 
91
94
  if (config.installGate) {
92
95
  accessInfo += chalk.cyan('Gate App: ') + chalk.white(gateAppUrl) + '\n';
@@ -105,10 +108,11 @@ function showSuccess(installDir, credentials, config) {
105
108
  }
106
109
 
107
110
  if (config.installMlm) {
108
- accessInfo += chalk.cyan('MLM App: ') + chalk.white(mlmAppUrl) + '\n';
111
+ accessInfo += chalk.cyan('MLM App: ') + chalk.white(mlmAppUrl) + '\n';
109
112
  }
110
113
 
111
114
  accessInfo += chalk.cyan('Backend: ') + chalk.white(apiUrl) + '\n' +
115
+ chalk.cyan('Backend Core: ') + chalk.white(backendCoreUrl) + '\n' +
112
116
  chalk.cyan('WebSocket: ') + chalk.white(apiUrl) + '\n\n';
113
117
 
114
118
  console.log(
@@ -319,6 +323,8 @@ export async function install(options) {
319
323
  const frontendPort = parseInt(options.port) || 8080;
320
324
  const apiPort = 3001;
321
325
  const backendMlmPort = 4001;
326
+ const backendCorePort = 4002;
327
+ const frontendCorePort = 8085;
322
328
  const gateAppPort = 8081;
323
329
  const guardianAppPort = 8082;
324
330
  const facialWebPort = 8083;
@@ -331,6 +337,8 @@ export async function install(options) {
331
337
  frontendDomain: buildURL(host, frontendPort),
332
338
  apiDomain: buildURL(host, apiPort),
333
339
  mlmApiDomain: buildURL(host, backendMlmPort),
340
+ backendCoreDomain: buildURL(host, backendCorePort),
341
+ frontendCoreDomain: buildURL(host, frontendCorePort),
334
342
  gateAppDomain: buildURL(host, gateAppPort),
335
343
  guardianAppDomain: buildURL(host, guardianAppPort),
336
344
  facialAppDomain: buildURL(host, facialWebPort),
@@ -339,6 +347,8 @@ export async function install(options) {
339
347
  frontendPort,
340
348
  apiPort,
341
349
  backendMlmPort,
350
+ backendCorePort,
351
+ frontendCorePort,
342
352
  gateAppPort,
343
353
  guardianAppPort,
344
354
  facialWebPort,
@@ -350,7 +360,7 @@ export async function install(options) {
350
360
  installFacial: true,
351
361
  installPos: true,
352
362
  installMlm: true,
353
- frontends: ['main', 'gate', 'guardian', 'facial', 'pos', 'mlm']
363
+ frontends: ['main', 'gate', 'guardian', 'facial', 'pos', 'mlm', 'core']
354
364
  };
355
365
 
356
366
  console.log(chalk.green(`✓ ${formatStepTitle(stepNetworkDetect, totalSteps, `Network detected: ${host}`)}\n`));
@@ -359,12 +369,14 @@ export async function install(options) {
359
369
  console.log(chalk.bold('📋 Installation Configuration:\n'));
360
370
  console.log(chalk.cyan(' Directory:'), chalk.white(config.installDir));
361
371
  console.log(chalk.cyan(' Frontend Main:'), chalk.white(config.frontendDomain));
372
+ console.log(chalk.cyan(' Frontend Core:'), chalk.white(config.frontendCoreDomain));
362
373
  console.log(chalk.cyan(' Gate App:'), chalk.white(config.gateAppDomain));
363
374
  console.log(chalk.cyan(' Guardian App:'), chalk.white(config.guardianAppDomain));
364
375
  console.log(chalk.cyan(' Facial Web:'), chalk.white(config.facialAppDomain));
365
376
  console.log(chalk.cyan(' POS App:'), chalk.white(config.posAppDomain));
366
377
  console.log(chalk.cyan(' MLM App:'), chalk.white(config.mlmAppDomain));
367
378
  console.log(chalk.cyan(' API:'), chalk.white(config.apiDomain));
379
+ console.log(chalk.cyan(' Backend Core:'), chalk.white(config.backendCoreDomain));
368
380
  console.log(chalk.cyan(' MLM API:'), chalk.white(config.mlmApiDomain));
369
381
  console.log();
370
382
 
@@ -442,6 +454,8 @@ export async function install(options) {
442
454
  frontendPort: config.frontendPort,
443
455
  backendPort: config.apiPort,
444
456
  backendMlmPort: config.backendMlmPort,
457
+ backendCorePort: config.backendCorePort,
458
+ frontendCorePort: config.frontendCorePort,
445
459
  gateAppPort: config.gateAppPort,
446
460
  guardianAppPort: config.guardianAppPort,
447
461
  facialWebPort: config.facialWebPort,
@@ -463,6 +477,8 @@ export async function install(options) {
463
477
  frontendPort: config.frontendPort,
464
478
  backendPort: config.apiPort,
465
479
  backendMlmPort: config.backendMlmPort,
480
+ backendCorePort: config.backendCorePort,
481
+ frontendCorePort: config.frontendCorePort,
466
482
  gateAppPort: config.gateAppPort,
467
483
  guardianAppPort: config.guardianAppPort,
468
484
  facialWebPort: config.facialWebPort,
@@ -474,6 +490,8 @@ export async function install(options) {
474
490
  posAppUrl: config.posAppDomain,
475
491
  mlmAppUrl: config.mlmAppDomain,
476
492
  mlmApiUrl: config.mlmApiDomain,
493
+ frontendCoreUrl: config.frontendCoreDomain,
494
+ backendCoreUrl: config.backendCoreDomain,
477
495
  companyId: config.companyId,
478
496
  installGate: config.installGate,
479
497
  installGuardian: config.installGuardian,
@@ -74,11 +74,13 @@ export async function regenerateCompose() {
74
74
  const installed = detectInstalledApps(composeFile);
75
75
  console.log(chalk.cyan('Detected Configuration:'));
76
76
  console.log(chalk.gray(` Frontend Main: ${installed.hasMain ? '✓ Installed' : '✗ Not installed'}`));
77
+ console.log(chalk.gray(` Backend Core: ✓ Always Included`));
78
+ console.log(chalk.gray(` Frontend Core: ✓ Always Included`));
77
79
  console.log(chalk.gray(` Gate App: ${installed.hasGateApp ? '✓ Installed' : '✗ Not installed'}`));
78
80
  console.log(chalk.gray(` Guardian App: ${installed.hasGuardianApp ? '✓ Installed' : '✗ Not installed'}`));
79
81
  console.log(chalk.gray(` Facial Web: ${installed.hasFacialWeb ? '✓ Installed' : '✗ Not installed'}`));
80
82
  console.log(chalk.gray(` POS App: ${installed.hasPosApp ? '✓ Installed' : '✗ Not installed'}`));
81
- console.log(chalk.gray(` MLM App: ${installed.hasMlmApp ? '✓ Installed' : '✗ Not installed'}\n`));
83
+ console.log(chalk.gray(` MLM App: ${installed.hasMlmApp ? '✓ Installed' : '✗ Not installed'}\n`));
82
84
 
83
85
  // Parse .env file
84
86
  const envConfig = parseEnvFile(envFile);
@@ -87,6 +89,8 @@ export async function regenerateCompose() {
87
89
  const frontendPort = parseInt(envConfig.FRONTEND_PORT) || 8080;
88
90
  const backendPort = parseInt(envConfig.BACKEND_PORT) || 3001;
89
91
  const backendMlmPort = parseInt(envConfig.BACKEND_MLM_PORT) || 4001;
92
+ const backendCorePort = parseInt(envConfig.BACKEND_CORE_PORT) || 4002;
93
+ const frontendCorePort = parseInt(envConfig.FRONTEND_CORE_PORT) || 8085;
90
94
  const gateAppPort = parseInt(envConfig.GATE_APP_PORT) || 8081;
91
95
  const guardianAppPort = parseInt(envConfig.GUARDIAN_APP_PORT) || 8082;
92
96
  const facialWebPort = parseInt(envConfig.FACIAL_WEB_PORT) || 8083;
@@ -95,25 +99,27 @@ export async function regenerateCompose() {
95
99
  const companyId = parseInt(envConfig.COMPANY_ID) || 1;
96
100
 
97
101
  console.log(chalk.cyan('Port Configuration:'));
98
- console.log(chalk.gray(` Frontend: ${frontendPort}`));
99
- console.log(chalk.gray(` Backend: ${backendPort}`));
102
+ console.log(chalk.gray(` Frontend: ${frontendPort}`));
103
+ console.log(chalk.gray(` Backend: ${backendPort}`));
104
+ console.log(chalk.gray(` Backend Core: ${backendCorePort}`));
105
+ console.log(chalk.gray(` Frontend Core: ${frontendCorePort}`));
100
106
  if (installed.hasGateApp) {
101
- console.log(chalk.gray(` Gate App: ${gateAppPort}`));
107
+ console.log(chalk.gray(` Gate App: ${gateAppPort}`));
102
108
  }
103
109
  if (installed.hasGuardianApp) {
104
- console.log(chalk.gray(` Guardian: ${guardianAppPort}`));
110
+ console.log(chalk.gray(` Guardian: ${guardianAppPort}`));
105
111
  }
106
112
  if (installed.hasFacialWeb) {
107
- console.log(chalk.gray(` Facial Web: ${facialWebPort}`));
113
+ console.log(chalk.gray(` Facial Web: ${facialWebPort}`));
108
114
  }
109
115
  if (installed.hasPosApp) {
110
- console.log(chalk.gray(` POS App: ${posAppPort}`));
116
+ console.log(chalk.gray(` POS App: ${posAppPort}`));
111
117
  }
112
118
  if (installed.hasMlmApp) {
113
- console.log(chalk.gray(` MLM App: ${mlmAppPort}`));
114
- console.log(chalk.gray(` Backend MLM: ${backendMlmPort}`));
119
+ console.log(chalk.gray(` MLM App: ${mlmAppPort}`));
120
+ console.log(chalk.gray(` Backend MLM: ${backendMlmPort}`));
115
121
  }
116
- console.log(chalk.gray(` Company ID: ${companyId}\n`));
122
+ console.log(chalk.gray(` Company ID: ${companyId}\n`));
117
123
 
118
124
  // Backup existing docker-compose.yml
119
125
  const timestamp = new Date().toISOString().split('.')[0].replace(/:/g, '-').replace('T', '_');
@@ -129,6 +135,8 @@ export async function regenerateCompose() {
129
135
  frontendPort,
130
136
  backendPort,
131
137
  backendMlmPort,
138
+ backendCorePort,
139
+ frontendCorePort,
132
140
  gateAppPort,
133
141
  guardianAppPort,
134
142
  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, or backend-mlm is installed by checking docker-compose.yml
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
101
101
  * @param {string} composeFile - Path to docker-compose.yml
102
- * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasMlmApp: boolean, hasBackendMlm: boolean}}
102
+ * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasMlmApp: boolean, hasBackendMlm: boolean, hasFrontendCore: boolean, hasBackendCore: boolean}}
103
103
  */
104
104
  function detectInstalledApps(composeFile) {
105
105
  try {
@@ -110,10 +110,12 @@ function detectInstalledApps(composeFile) {
110
110
  hasFacialWeb: composeContent.includes('facial-web:') || composeContent.includes('container_name: ante-facial-web'),
111
111
  hasPosApp: composeContent.includes('pos-app:') || composeContent.includes('container_name: ante-pos'),
112
112
  hasMlmApp: composeContent.includes('mlm-app:') || composeContent.includes('container_name: ante-mlm'),
113
- hasBackendMlm: composeContent.includes('backend-mlm:') || composeContent.includes('container_name: ante-backend-mlm')
113
+ hasBackendMlm: composeContent.includes('backend-mlm:') || composeContent.includes('container_name: ante-backend-mlm'),
114
+ hasFrontendCore: composeContent.includes('frontend-core:') || composeContent.includes('container_name: ante-frontend-core'),
115
+ hasBackendCore: composeContent.includes('backend-core:') || composeContent.includes('container_name: ante-backend-core')
114
116
  };
115
117
  } catch {
116
- return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false };
118
+ return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false, hasFrontendCore: false, hasBackendCore: false };
117
119
  }
118
120
  }
119
121
 
@@ -172,10 +174,10 @@ export async function setDomain(options) {
172
174
  console.log(); // Add spacing
173
175
  }
174
176
 
175
- let frontendUrl, apiUrl, gateAppUrl, guardianAppUrl, facialWebUrl, posAppUrl, mlmAppUrl, backendMlmUrl;
177
+ let frontendUrl, apiUrl, gateAppUrl, guardianAppUrl, facialWebUrl, posAppUrl, mlmAppUrl, backendMlmUrl, frontendCoreUrl, backendCoreUrl;
176
178
 
177
179
  // Detect which apps are installed
178
- const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm } = detectInstalledApps(composeFile);
180
+ const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasBackendCore } = detectInstalledApps(composeFile);
179
181
 
180
182
  if (options.interactive !== false) {
181
183
  // Show current values
@@ -187,6 +189,8 @@ export async function setDomain(options) {
187
189
  const currentPosAppUrl = getEnvValue(envPath, 'POS_APP_URL');
188
190
  const currentClientAppUrl = getEnvValue(envPath, 'MLM_APP_URL');
189
191
  const currentBackendClientUrl = getEnvValue(envPath, 'MLM_API_URL');
192
+ const currentFrontendCoreUrl = getEnvValue(envPath, 'FRONTEND_CORE_URL');
193
+ const currentBackendCoreUrl = getEnvValue(envPath, 'BACKEND_CORE_URL');
190
194
 
191
195
  if (currentFrontendUrl) {
192
196
  console.log(chalk.gray(`Current Frontend URL: ${currentFrontendUrl}`));
@@ -209,6 +213,12 @@ export async function setDomain(options) {
209
213
  if (currentBackendClientUrl) {
210
214
  console.log(chalk.gray(`Current Backend-MLM API URL: ${currentBackendClientUrl}`));
211
215
  }
216
+ if (currentFrontendCoreUrl) {
217
+ console.log(chalk.gray(`Current Frontend Core URL: ${currentFrontendCoreUrl}`));
218
+ }
219
+ if (currentBackendCoreUrl) {
220
+ console.log(chalk.gray(`Current Backend Core API URL: ${currentBackendCoreUrl}`));
221
+ }
212
222
  if (currentApiUrl) {
213
223
  console.log(chalk.gray(`Current API URL: ${currentApiUrl}\n`));
214
224
  }
@@ -496,6 +506,8 @@ export async function setDomain(options) {
496
506
  if (hasPosApp) posAppUrl = `https://${subdomain}-pos.ante.ph`;
497
507
  if (hasMlmApp) mlmAppUrl = `https://${subdomain}-mlm.ante.ph`;
498
508
  if (hasBackendMlm) backendMlmUrl = `https://${subdomain}-api-mlm.ante.ph`;
509
+ if (hasFrontendCore) frontendCoreUrl = `https://${subdomain}-core.ante.ph`;
510
+ if (hasBackendCore) backendCoreUrl = `https://${subdomain}-api-core.ante.ph`;
499
511
 
500
512
  // Display generated domains for confirmation
501
513
  console.log(chalk.cyan('\n📋 Generated Domain Configuration:\n'));
@@ -507,6 +519,8 @@ export async function setDomain(options) {
507
519
  if (hasPosApp) console.log(chalk.gray(` POS App: ${posAppUrl}`));
508
520
  if (hasMlmApp) console.log(chalk.gray(` MLM App: ${mlmAppUrl}`));
509
521
  if (hasBackendMlm) console.log(chalk.gray(` Backend-MLM: ${backendMlmUrl}`));
522
+ if (hasFrontendCore) console.log(chalk.gray(` Frontend Core: ${frontendCoreUrl}`));
523
+ if (hasBackendCore) console.log(chalk.gray(` Backend Core: ${backendCoreUrl}`));
510
524
  console.log('');
511
525
 
512
526
  const confirmDomains = await inquirer.prompt([
@@ -594,6 +608,26 @@ export async function setDomain(options) {
594
608
  });
595
609
  }
596
610
 
611
+ if (hasFrontendCore) {
612
+ domainPrompts.push({
613
+ type: 'input',
614
+ name: 'frontendCoreUrl',
615
+ message: 'Frontend Core URL:\n Examples: https://staging-core.ante.ph or http://143.198.91.153:8085\n Enter URL',
616
+ default: currentFrontendCoreUrl || 'http://localhost:8085',
617
+ validate: validateUrl
618
+ });
619
+ }
620
+
621
+ if (hasBackendCore) {
622
+ domainPrompts.push({
623
+ type: 'input',
624
+ name: 'backendCoreUrl',
625
+ message: 'Backend Core API URL:\n Examples: https://staging-api-core.ante.ph or http://143.198.91.153:4002\n Enter URL',
626
+ default: currentBackendCoreUrl || 'http://localhost:4002',
627
+ validate: validateUrl
628
+ });
629
+ }
630
+
597
631
  domainPrompts.push({
598
632
  type: 'input',
599
633
  name: 'apiUrl',
@@ -613,6 +647,8 @@ export async function setDomain(options) {
613
647
  if (hasPosApp) posAppUrl = sanitizeUrl(answers.posAppUrl);
614
648
  if (hasMlmApp) mlmAppUrl = sanitizeUrl(answers.mlmAppUrl);
615
649
  if (hasBackendMlm) backendMlmUrl = sanitizeUrl(answers.backendMlmUrl);
650
+ if (hasFrontendCore) frontendCoreUrl = sanitizeUrl(answers.frontendCoreUrl);
651
+ if (hasBackendCore) backendCoreUrl = sanitizeUrl(answers.backendCoreUrl);
616
652
  }
617
653
  }
618
654
  } else {
@@ -755,6 +791,14 @@ export async function setDomain(options) {
755
791
  envUpdates.MLM_API_URL = backendMlmUrl;
756
792
  }
757
793
 
794
+ if (hasFrontendCore && frontendCoreUrl) {
795
+ envUpdates.FRONTEND_CORE_URL = frontendCoreUrl;
796
+ }
797
+
798
+ if (hasBackendCore && backendCoreUrl) {
799
+ envUpdates.BACKEND_CORE_URL = backendCoreUrl;
800
+ }
801
+
758
802
  updateEnvFile(envPath, envUpdates);
759
803
 
760
804
  console.log(chalk.green('✓ Configuration updated'));
@@ -813,6 +857,16 @@ export async function setDomain(options) {
813
857
  nginxConfig.backendMlmPort = 4001;
814
858
  }
815
859
 
860
+ if (hasFrontendCore && frontendCoreUrl) {
861
+ nginxConfig.frontendCoreDomain = frontendCoreUrl;
862
+ nginxConfig.frontendCorePort = 8085;
863
+ }
864
+
865
+ if (hasBackendCore && backendCoreUrl) {
866
+ nginxConfig.backendCoreDomain = backendCoreUrl;
867
+ nginxConfig.backendCorePort = 4002;
868
+ }
869
+
816
870
  await configureNginx(nginxConfig);
817
871
  } catch (error) {
818
872
  console.log(chalk.yellow('\n⚠ NGINX configuration failed:', error.message));
@@ -925,6 +979,18 @@ export async function setDomain(options) {
925
979
  domains.push(backendMlmDomain);
926
980
  }
927
981
  }
982
+ if (hasFrontendCore && frontendCoreUrl && frontendCoreUrl.startsWith('https://')) {
983
+ const frontendCoreDomain = extractDomain(frontendCoreUrl);
984
+ if (frontendCoreDomain !== 'localhost' && !frontendCoreDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.includes(frontendCoreDomain)) {
985
+ domains.push(frontendCoreDomain);
986
+ }
987
+ }
988
+ if (hasBackendCore && backendCoreUrl && backendCoreUrl.startsWith('https://')) {
989
+ const backendCoreDomain = extractDomain(backendCoreUrl);
990
+ if (backendCoreDomain !== 'localhost' && !backendCoreDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.includes(backendCoreDomain)) {
991
+ domains.push(backendCoreDomain);
992
+ }
993
+ }
928
994
 
929
995
  // Track successful and failed domains
930
996
  const successfulDomains = [];
@@ -1016,6 +1082,16 @@ export async function setDomain(options) {
1016
1082
  sslNginxConfig.backendMlmPort = 4001;
1017
1083
  }
1018
1084
 
1085
+ if (hasFrontendCore && frontendCoreUrl) {
1086
+ sslNginxConfig.frontendCoreDomain = frontendCoreUrl;
1087
+ sslNginxConfig.frontendCorePort = 8085;
1088
+ }
1089
+
1090
+ if (hasBackendCore && backendCoreUrl) {
1091
+ sslNginxConfig.backendCoreDomain = backendCoreUrl;
1092
+ sslNginxConfig.backendCorePort = 4002;
1093
+ }
1094
+
1019
1095
  await updateNginxForSSL(sslNginxConfig);
1020
1096
 
1021
1097
  // Step 3: Setup auto-renewal
@@ -1176,6 +1252,12 @@ export async function setDomain(options) {
1176
1252
  if (hasMlmApp && mlmAppUrl) {
1177
1253
  console.log(chalk.cyan('MLM App: '), chalk.white(mlmAppUrl));
1178
1254
  }
1255
+ if (hasFrontendCore && frontendCoreUrl) {
1256
+ console.log(chalk.cyan('Frontend Core: '), chalk.white(frontendCoreUrl));
1257
+ }
1258
+ if (hasBackendCore && backendCoreUrl) {
1259
+ console.log(chalk.cyan('Backend Core: '), chalk.white(backendCoreUrl));
1260
+ }
1179
1261
  console.log(chalk.cyan('API: '), chalk.white(apiUrl));
1180
1262
  console.log(chalk.cyan('WebSocket: '), chalk.white(apiUrl));
1181
1263
 
@@ -1193,6 +1275,12 @@ export async function setDomain(options) {
1193
1275
  if (hasMlmApp && mlmAppUrl) {
1194
1276
  console.log(chalk.white(` MLM App: ${mlmAppUrl}`));
1195
1277
  }
1278
+ if (hasFrontendCore && frontendCoreUrl) {
1279
+ console.log(chalk.white(` Frontend Core: ${frontendCoreUrl}`));
1280
+ }
1281
+ if (hasBackendCore && backendCoreUrl) {
1282
+ console.log(chalk.white(` Backend Core: ${backendCoreUrl}`));
1283
+ }
1196
1284
  console.log();
1197
1285
 
1198
1286
  } catch (error) {
@@ -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}}
51
+ * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasMlmApp: boolean, hasBackendMlm: boolean, hasFrontendCore: boolean, hasBackendCore: boolean}}
52
52
  */
53
53
  function detectInstalledApps(composeFile) {
54
54
  try {
@@ -59,10 +59,12 @@ function detectInstalledApps(composeFile) {
59
59
  hasFacialWeb: composeContent.includes('facial-web:') || composeContent.includes('container_name: ante-facial-web'),
60
60
  hasPosApp: composeContent.includes('pos-app:') || composeContent.includes('container_name: ante-pos'),
61
61
  hasMlmApp: composeContent.includes('mlm-app:') || composeContent.includes('container_name: ante-mlm'),
62
- hasBackendMlm: composeContent.includes('backend-mlm:') || composeContent.includes('container_name: ante-backend-mlm')
62
+ hasBackendMlm: composeContent.includes('backend-mlm:') || composeContent.includes('container_name: ante-backend-mlm'),
63
+ hasFrontendCore: composeContent.includes('frontend-core:') || composeContent.includes('container_name: ante-frontend-core'),
64
+ hasBackendCore: composeContent.includes('backend-core:') || composeContent.includes('container_name: ante-backend-core')
63
65
  };
64
66
  } catch {
65
- return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false };
67
+ return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasMlmApp: false, hasBackendMlm: false, hasFrontendCore: true, hasBackendCore: true };
66
68
  }
67
69
  }
68
70
 
@@ -90,7 +92,7 @@ export async function sslEnable(options) {
90
92
  console.log(chalk.gray(`Installation: ${installDir}\n`));
91
93
 
92
94
  // Detect which apps are installed
93
- const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm } = detectInstalledApps(composeFile);
95
+ const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasBackendCore } = detectInstalledApps(composeFile);
94
96
 
95
97
  // Read current configuration
96
98
  const frontendUrl = getEnvValue(envPath, 'FRONTEND_URL');
@@ -101,6 +103,8 @@ export async function sslEnable(options) {
101
103
  const posAppUrl = hasPosApp ? getEnvValue(envPath, 'POS_APP_URL') : '';
102
104
  const mlmAppUrl = hasMlmApp ? getEnvValue(envPath, 'MLM_APP_URL') : '';
103
105
  const backendMlmUrl = hasBackendMlm ? getEnvValue(envPath, 'MLM_API_URL') : '';
106
+ const frontendCoreUrl = hasFrontendCore ? getEnvValue(envPath, 'FRONTEND_CORE_URL') : '';
107
+ const backendCoreUrl = hasBackendCore ? getEnvValue(envPath, 'BACKEND_CORE_URL') : '';
104
108
 
105
109
  if (!frontendUrl || !apiUrl) {
106
110
  console.log(chalk.red('✗ Domain configuration not found'));
@@ -120,17 +124,21 @@ export async function sslEnable(options) {
120
124
  (!facialWebUrl || facialWebUrl.startsWith('https://')) &&
121
125
  (!posAppUrl || posAppUrl.startsWith('https://')) &&
122
126
  (!mlmAppUrl || mlmAppUrl.startsWith('https://')) &&
123
- (!backendMlmUrl || backendMlmUrl.startsWith('https://'));
127
+ (!backendMlmUrl || backendMlmUrl.startsWith('https://')) &&
128
+ (!frontendCoreUrl || frontendCoreUrl.startsWith('https://')) &&
129
+ (!backendCoreUrl || backendCoreUrl.startsWith('https://'));
124
130
 
125
131
  if (allHttps) {
126
132
  console.log(chalk.yellow('⚠ Domains are already configured with HTTPS:\n'));
127
133
  console.log(chalk.gray(` Frontend: ${frontendUrl}`));
134
+ if (frontendCoreUrl) console.log(chalk.gray(` Frontend Core: ${frontendCoreUrl}`));
128
135
  if (gateAppUrl) console.log(chalk.gray(` Gate App: ${gateAppUrl}`));
129
136
  if (guardianAppUrl) console.log(chalk.gray(` Guardian: ${guardianAppUrl}`));
130
137
  if (facialWebUrl) console.log(chalk.gray(` Facial Web: ${facialWebUrl}`));
131
138
  if (posAppUrl) console.log(chalk.gray(` POS App: ${posAppUrl}`));
132
139
  if (mlmAppUrl) console.log(chalk.gray(` MLM App: ${mlmAppUrl}`));
133
140
  if (backendMlmUrl) console.log(chalk.gray(` Backend MLM: ${backendMlmUrl}`));
141
+ if (backendCoreUrl) console.log(chalk.gray(` Backend Core: ${backendCoreUrl}`));
134
142
  console.log(chalk.gray(` API: ${apiUrl}\n`));
135
143
 
136
144
  // Check certificate status
@@ -189,6 +197,22 @@ export async function sslEnable(options) {
189
197
  }
190
198
  }
191
199
 
200
+ if (frontendCoreUrl) {
201
+ const frontendCoreDomain = extractDomain(frontendCoreUrl);
202
+ const frontendCoreStatus = await checkSSLStatus(frontendCoreDomain);
203
+ if (frontendCoreStatus.installed) {
204
+ console.log(chalk.green(`✓ Frontend Core SSL: Valid (expires in ${frontendCoreStatus.daysUntilExpiry} days)`));
205
+ }
206
+ }
207
+
208
+ if (backendCoreUrl) {
209
+ const backendCoreDomain = extractDomain(backendCoreUrl);
210
+ const backendCoreStatus = await checkSSLStatus(backendCoreDomain);
211
+ if (backendCoreStatus.installed) {
212
+ console.log(chalk.green(`✓ Backend Core SSL: Valid (expires in ${backendCoreStatus.daysUntilExpiry} days)`));
213
+ }
214
+ }
215
+
192
216
  if (apiStatus.installed) {
193
217
  console.log(chalk.green(`✓ API SSL: Valid (expires in ${apiStatus.daysUntilExpiry} days)`));
194
218
  }
@@ -218,12 +242,14 @@ export async function sslEnable(options) {
218
242
  // Show current configuration
219
243
  console.log(chalk.cyan('Current Configuration:'));
220
244
  console.log(chalk.gray(` Frontend: ${frontendUrl}`));
245
+ if (frontendCoreUrl) console.log(chalk.gray(` Frontend Core: ${frontendCoreUrl}`));
221
246
  if (gateAppUrl) console.log(chalk.gray(` Gate App: ${gateAppUrl}`));
222
247
  if (guardianAppUrl) console.log(chalk.gray(` Guardian: ${guardianAppUrl}`));
223
248
  if (facialWebUrl) console.log(chalk.gray(` Facial Web: ${facialWebUrl}`));
224
249
  if (posAppUrl) console.log(chalk.gray(` POS App: ${posAppUrl}`));
225
250
  if (mlmAppUrl) console.log(chalk.gray(` MLM App: ${mlmAppUrl}`));
226
251
  if (backendMlmUrl) console.log(chalk.gray(` Backend MLM: ${backendMlmUrl}`));
252
+ if (backendCoreUrl) console.log(chalk.gray(` Backend Core: ${backendCoreUrl}`));
227
253
  console.log(chalk.gray(` API: ${apiUrl}\n`));
228
254
 
229
255
  // Detect domains to configure
@@ -310,6 +336,30 @@ export async function sslEnable(options) {
310
336
  }
311
337
  }
312
338
 
339
+ if (frontendCoreUrl) {
340
+ const frontendCoreDomain = extractDomain(frontendCoreUrl);
341
+ if (frontendCoreDomain !== 'localhost' && !frontendCoreDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.find(d => d.domain === frontendCoreDomain)) {
342
+ domains.push({
343
+ name: 'Frontend Core',
344
+ domain: frontendCoreDomain,
345
+ port: frontendCoreUrl.match(/:(\d+)/) ? frontendCoreUrl.match(/:(\d+)/)[1] : 8085,
346
+ url: frontendCoreUrl
347
+ });
348
+ }
349
+ }
350
+
351
+ if (backendCoreUrl) {
352
+ const backendCoreDomain = extractDomain(backendCoreUrl);
353
+ if (backendCoreDomain !== 'localhost' && !backendCoreDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.find(d => d.domain === backendCoreDomain)) {
354
+ domains.push({
355
+ name: 'Backend Core',
356
+ domain: backendCoreDomain,
357
+ port: backendCoreUrl.match(/:(\d+)/) ? backendCoreUrl.match(/:(\d+)/)[1] : 4002,
358
+ url: backendCoreUrl
359
+ });
360
+ }
361
+ }
362
+
313
363
  if (apiDomain !== 'localhost' && !apiDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && apiDomain !== frontendDomain) {
314
364
  domains.push({
315
365
  name: 'API',
@@ -473,6 +523,16 @@ export async function sslEnable(options) {
473
523
  sslConfig.backendMlmPort = backendMlmUrl.match(/:(\d+)/) ? parseInt(backendMlmUrl.match(/:(\d+)/)[1]) : 4001;
474
524
  }
475
525
 
526
+ if (frontendCoreUrl) {
527
+ sslConfig.frontendCoreDomain = frontendCoreUrl;
528
+ sslConfig.frontendCorePort = frontendCoreUrl.match(/:(\d+)/) ? parseInt(frontendCoreUrl.match(/:(\d+)/)[1]) : 8085;
529
+ }
530
+
531
+ if (backendCoreUrl) {
532
+ sslConfig.backendCoreDomain = backendCoreUrl;
533
+ sslConfig.backendCorePort = backendCoreUrl.match(/:(\d+)/) ? parseInt(backendCoreUrl.match(/:(\d+)/)[1]) : 4002;
534
+ }
535
+
476
536
  await updateNginxForSSL(sslConfig);
477
537
  } catch (error) {
478
538
  console.log(chalk.red('\n✗ Failed to configure NGINX:'), error.message);
@@ -559,7 +619,7 @@ export async function sslStatus() {
559
619
  const composeFile = join(installDir, 'docker-compose.yml');
560
620
 
561
621
  // Detect which apps are installed
562
- const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm } = detectInstalledApps(composeFile);
622
+ const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasMlmApp, hasBackendMlm, hasFrontendCore, hasBackendCore } = detectInstalledApps(composeFile);
563
623
 
564
624
  const frontendUrl = getEnvValue(envPath, 'FRONTEND_URL');
565
625
  const apiUrl = getEnvValue(envPath, 'API_URL');
@@ -569,6 +629,8 @@ export async function sslStatus() {
569
629
  const posAppUrl = hasPosApp ? getEnvValue(envPath, 'POS_APP_URL') : '';
570
630
  const mlmAppUrl = hasMlmApp ? getEnvValue(envPath, 'MLM_APP_URL') : '';
571
631
  const backendMlmUrl = hasBackendMlm ? getEnvValue(envPath, 'MLM_API_URL') : '';
632
+ const frontendCoreUrl = hasFrontendCore ? getEnvValue(envPath, 'FRONTEND_CORE_URL') : '';
633
+ const backendCoreUrl = hasBackendCore ? getEnvValue(envPath, 'BACKEND_CORE_URL') : '';
572
634
 
573
635
  if (!frontendUrl || !apiUrl) {
574
636
  console.log(chalk.red('✗ Domain configuration not found\n'));
@@ -612,6 +674,16 @@ export async function sslStatus() {
612
674
  domains.push({ name: 'Backend MLM', domain: backendMlmDomain, url: backendMlmUrl });
613
675
  }
614
676
 
677
+ if (frontendCoreUrl) {
678
+ const frontendCoreDomain = extractDomain(frontendCoreUrl);
679
+ domains.push({ name: 'Frontend Core', domain: frontendCoreDomain, url: frontendCoreUrl });
680
+ }
681
+
682
+ if (backendCoreUrl) {
683
+ const backendCoreDomain = extractDomain(backendCoreUrl);
684
+ domains.push({ name: 'Backend Core', domain: backendCoreDomain, url: backendCoreUrl });
685
+ }
686
+
615
687
  domains.push({ name: 'API', domain: apiDomain, url: apiUrl });
616
688
 
617
689
  for (const { name, domain, url } of domains) {
@@ -82,6 +82,8 @@ function updateDockerCompose(composeFile, envFile, newServices) {
82
82
  frontendPort: parseInt(envConfig.FRONTEND_PORT) || 8080,
83
83
  backendPort: parseInt(envConfig.BACKEND_PORT) || 3001,
84
84
  backendMlmPort: parseInt(envConfig.BACKEND_MLM_PORT) || 4001,
85
+ backendCorePort: parseInt(envConfig.BACKEND_CORE_PORT) || 4002,
86
+ frontendCorePort: parseInt(envConfig.FRONTEND_CORE_PORT) || 8085,
85
87
  gateAppPort: parseInt(envConfig.GATE_APP_PORT) || 8081,
86
88
  guardianAppPort: parseInt(envConfig.GUARDIAN_APP_PORT) || 8082,
87
89
  facialWebPort: parseInt(envConfig.FACIAL_WEB_PORT) || 8083,
@@ -347,6 +349,8 @@ function refreshDockerCompose(composeFile, envFile) {
347
349
  frontendPort: parseInt(envConfig.FRONTEND_PORT) || 8080,
348
350
  backendPort: parseInt(envConfig.BACKEND_PORT) || 3001,
349
351
  backendMlmPort: parseInt(envConfig.BACKEND_MLM_PORT) || 4001,
352
+ backendCorePort: parseInt(envConfig.BACKEND_CORE_PORT) || 4002,
353
+ frontendCorePort: parseInt(envConfig.FRONTEND_CORE_PORT) || 8085,
350
354
  gateAppPort: parseInt(envConfig.GATE_APP_PORT) || 8081,
351
355
  guardianAppPort: parseInt(envConfig.GUARDIAN_APP_PORT) || 8082,
352
356
  facialWebPort: parseInt(envConfig.FACIAL_WEB_PORT) || 8083,
@@ -396,6 +400,8 @@ function getRequiredPorts(composeFile, envFile) {
396
400
  // Core services - always needed
397
401
  ports.push(parseInt(envConfig.BACKEND_PORT) || 3001); // Backend
398
402
  ports.push(parseInt(envConfig.FRONTEND_PORT) || 8080); // Frontend
403
+ ports.push(parseInt(envConfig.BACKEND_CORE_PORT) || 4002); // Backend Core (always included)
404
+ ports.push(parseInt(envConfig.FRONTEND_CORE_PORT) || 8085); // Frontend Core (always included)
399
405
 
400
406
  // Optional services - only add if installed
401
407
  if (installed.hasGateApp) {
@@ -8,6 +8,8 @@ export function generateDockerCompose(options = {}) {
8
8
  frontendPort = 8080,
9
9
  backendPort = 3001,
10
10
  backendMlmPort = 4001,
11
+ backendCorePort = 4002,
12
+ frontendCorePort = 8085,
11
13
  gateAppPort = 8081,
12
14
  guardianAppPort = 8082,
13
15
  facialWebPort = 8083,
@@ -193,6 +195,68 @@ services:
193
195
  options:
194
196
  max-size: "10m"
195
197
  max-file: "3"
198
+
199
+ # ANTE Backend Core - Express.js API for Frontend Core
200
+ backend-core:
201
+ image: ghcr.io/gtplusnet/ante-self-hosted-backend-core:latest
202
+ container_name: ante-backend-core
203
+ restart: unless-stopped
204
+ depends_on:
205
+ backend:
206
+ condition: service_healthy
207
+ environment:
208
+ NODE_ENV: production
209
+ BACKEND_CORE_PORT: 4002
210
+ DATABASE_URL: postgresql://ante:\${DB_PASSWORD}@postgres:5432/ante_db?schema=public&connection_limit=10
211
+ DIRECT_URL: postgresql://ante:\${DB_PASSWORD}@postgres:5432/ante_db?schema=public
212
+ REDIS_HOST: redis
213
+ REDIS_PORT: 6379
214
+ REDIS_PASSWORD: \${REDIS_PASSWORD}
215
+ JWT_SECRET: \${JWT_SECRET}
216
+ TZ: \${TZ:-Asia/Manila}
217
+ ports:
218
+ - "${backendCorePort}:4002"
219
+ networks:
220
+ - ante-network
221
+ healthcheck:
222
+ test: ["CMD", "curl", "-f", "http://localhost:4002/health"]
223
+ interval: 30s
224
+ timeout: 10s
225
+ retries: 3
226
+ start_period: 30s
227
+ logging:
228
+ driver: "json-file"
229
+ options:
230
+ max-size: "10m"
231
+ max-file: "3"
232
+
233
+ # ANTE Frontend Core - React Application
234
+ frontend-core:
235
+ image: ghcr.io/gtplusnet/ante-self-hosted-frontend-core:latest
236
+ container_name: ante-frontend-core
237
+ restart: unless-stopped
238
+ depends_on:
239
+ backend-core:
240
+ condition: service_healthy
241
+ environment:
242
+ - VITE_API_URL=\${CORE_API_URL:-http://localhost:${backendCorePort}}
243
+ - VITE_APP_NAME=ANTE Frontend Core
244
+ - VITE_APP_VERSION=\${VERSION:-1.0.0}
245
+ ports:
246
+ - "${frontendCorePort}:8085"
247
+ networks:
248
+ - ante-network
249
+ healthcheck:
250
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8085/"]
251
+ interval: 30s
252
+ timeout: 10s
253
+ retries: 3
254
+ start_period: 30s
255
+ logging:
256
+ driver: "json-file"
257
+ options:
258
+ max-size: "10m"
259
+ max-file: "3"
196
260
  ${installGate ? `
197
261
  # ANTE Gate App
198
262
  gate-app:
@@ -12,6 +12,8 @@ export function generateEnv(credentials, options = {}) {
12
12
  frontendPort = 8080,
13
13
  backendPort = 3001,
14
14
  backendMlmPort = 4001,
15
+ backendCorePort = 4002,
16
+ frontendCorePort = 8085,
15
17
  gateAppPort = 8081,
16
18
  guardianAppPort = 8082,
17
19
  facialWebPort = 8083,
@@ -23,6 +25,8 @@ export function generateEnv(credentials, options = {}) {
23
25
  posAppUrl = 'http://localhost:8084',
24
26
  mlmAppUrl = 'http://localhost:9005',
25
27
  mlmApiUrl = 'http://localhost:4001',
28
+ frontendCoreUrl = 'http://localhost:8085',
29
+ backendCoreUrl = 'http://localhost:4002',
26
30
  companyId = 1,
27
31
  installGate = false,
28
32
  installGuardian = false,
@@ -74,6 +78,8 @@ SOCKET_URL=${socketUrl}
74
78
  # ------------------------------------------------------------------------------
75
79
  FRONTEND_PORT=${frontendPort}
76
80
  BACKEND_PORT=${backendPort}
81
+ BACKEND_CORE_PORT=${backendCorePort}
82
+ FRONTEND_CORE_PORT=${frontendCorePort}
77
83
  ${installMlm ? `BACKEND_MLM_PORT=${backendMlmPort}` : '# BACKEND_MLM_PORT=4001'}
78
84
  ${installGate ? `GATE_APP_PORT=${gateAppPort}` : '# GATE_APP_PORT=8081'}
79
85
  ${installGuardian ? `GUARDIAN_APP_PORT=${guardianAppPort}` : '# GUARDIAN_APP_PORT=8082'}
@@ -82,6 +88,13 @@ ${installPos ? `POS_APP_PORT=${posAppPort}` : '# POS_APP_PORT=8084'}
82
88
  ${installMlm ? `MLM_APP_PORT=${mlmAppPort}` : '# MLM_APP_PORT=9005'}
83
89
  # Note: WebSocket runs on the same port as backend (BACKEND_PORT)
84
90
 
91
+ # ------------------------------------------------------------------------------
92
+ # CORE SERVICES CONFIGURATION (Always Included)
93
+ # ------------------------------------------------------------------------------
94
+ FRONTEND_CORE_URL=${frontendCoreUrl}
95
+ BACKEND_CORE_URL=${backendCoreUrl}
96
+ CORE_API_URL=${backendCoreUrl}
97
+
85
98
  ${(installGate || installGuardian || installFacial || installPos || installMlm) ? `# ------------------------------------------------------------------------------
86
99
  # FRONTEND APP CONFIGURATION
87
100
  # ------------------------------------------------------------------------------
@@ -54,6 +54,8 @@ export async function installNginx(spinner) {
54
54
  * @param {string} [config.posAppDomain] - POS app domain (optional)
55
55
  * @param {string} [config.mlmAppDomain] - MLM app domain (optional)
56
56
  * @param {string} [config.backendMlmDomain] - Backend MLM API domain (optional)
57
+ * @param {string} [config.frontendCoreDomain] - Frontend Core app domain (optional)
58
+ * @param {string} [config.backendCoreDomain] - Backend Core API domain (optional)
57
59
  * @param {number} config.frontendPort - Frontend Docker port (default: 8080)
58
60
  * @param {number} config.apiPort - API Docker port (default: 3001)
59
61
  * @param {number} [config.gateAppPort] - Gate app Docker port (default: 8081)
@@ -62,6 +64,8 @@ export async function installNginx(spinner) {
62
64
  * @param {number} [config.posAppPort] - POS app Docker port (default: 8084)
63
65
  * @param {number} [config.mlmAppPort] - MLM app Docker port (default: 9005)
64
66
  * @param {number} [config.backendMlmPort] - Backend MLM API Docker port (default: 4001)
67
+ * @param {number} [config.frontendCorePort] - Frontend Core Docker port (default: 8085)
68
+ * @param {number} [config.backendCorePort] - Backend Core API Docker port (default: 4002)
65
69
  * @param {boolean} config.ssl - Enable SSL configuration with Let's Encrypt (default: false)
66
70
  * @param {boolean} config.cloudflareSsl - Enable Cloudflare SSL mode with self-signed certs (default: false)
67
71
  * @returns {string} NGINX configuration content
@@ -76,6 +80,8 @@ export function generateNginxConfig(config) {
76
80
  posAppDomain,
77
81
  mlmAppDomain,
78
82
  backendMlmDomain,
83
+ frontendCoreDomain,
84
+ backendCoreDomain,
79
85
  frontendPort = 8080,
80
86
  apiPort = 3001,
81
87
  gateAppPort = 8081,
@@ -84,6 +90,8 @@ export function generateNginxConfig(config) {
84
90
  posAppPort = 8084,
85
91
  mlmAppPort = 9005,
86
92
  backendMlmPort = 4001,
93
+ frontendCorePort = 8085,
94
+ backendCorePort = 4002,
87
95
  ssl = false,
88
96
  cloudflareSsl = false,
89
97
  domainsWithCerts = []
@@ -98,6 +106,8 @@ export function generateNginxConfig(config) {
98
106
  const posAppHost = posAppDomain ? posAppDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
99
107
  const mlmAppHost = mlmAppDomain ? mlmAppDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
100
108
  const backendMlmHost = backendMlmDomain ? backendMlmDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
109
+ const frontendCoreHost = frontendCoreDomain ? frontendCoreDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
110
+ const backendCoreHost = backendCoreDomain ? backendCoreDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
101
111
 
102
112
  // Cloudflare SSL mode - uses self-signed certs for Cloudflare "Full" mode
103
113
  if (cloudflareSsl) {
@@ -110,6 +120,8 @@ export function generateNginxConfig(config) {
110
120
  posAppHost,
111
121
  mlmAppHost,
112
122
  backendMlmHost,
123
+ frontendCoreHost,
124
+ backendCoreHost,
113
125
  frontendPort,
114
126
  apiPort,
115
127
  gateAppPort,
@@ -117,7 +129,9 @@ export function generateNginxConfig(config) {
117
129
  facialWebPort,
118
130
  posAppPort,
119
131
  mlmAppPort,
120
- backendMlmPort
132
+ backendMlmPort,
133
+ frontendCorePort,
134
+ backendCorePort
121
135
  });
122
136
  }
123
137
 
@@ -132,6 +146,8 @@ export function generateNginxConfig(config) {
132
146
  posAppHost,
133
147
  mlmAppHost,
134
148
  backendMlmHost,
149
+ frontendCoreHost,
150
+ backendCoreHost,
135
151
  frontendPort,
136
152
  apiPort,
137
153
  gateAppPort,
@@ -140,6 +156,8 @@ export function generateNginxConfig(config) {
140
156
  posAppPort,
141
157
  mlmAppPort,
142
158
  backendMlmPort,
159
+ frontendCorePort,
160
+ backendCorePort,
143
161
  domainsWithCerts
144
162
  });
145
163
  }
@@ -395,6 +413,82 @@ server {
395
413
  proxy_read_timeout 60s;
396
414
  }
397
415
  }
416
+ ` : ''}${frontendCoreHost ? `
417
+ # ANTE Frontend Core Configuration
418
+ server {
419
+ listen 80;
420
+ listen [::]:80;
421
+ server_name ${frontendCoreHost};
422
+
423
+ # Increase buffer sizes for large headers
424
+ client_header_buffer_size 16k;
425
+ large_client_header_buffers 4 16k;
426
+
427
+ # Increase body size for file uploads
428
+ client_max_body_size 100M;
429
+
430
+ # Disable caching for config.js to ensure runtime config is always fresh
431
+ location = /config.js {
432
+ proxy_pass http://localhost:${frontendCorePort}/config.js;
433
+ proxy_http_version 1.1;
434
+ proxy_set_header Host $host;
435
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
436
+ add_header Pragma "no-cache" always;
437
+ add_header Expires "0" always;
438
+ }
439
+
440
+ location / {
441
+ proxy_pass http://localhost:${frontendCorePort};
442
+ proxy_http_version 1.1;
443
+ proxy_set_header Upgrade $http_upgrade;
444
+ proxy_set_header Connection 'upgrade';
445
+ proxy_set_header Host $host;
446
+ proxy_set_header X-Real-IP $remote_addr;
447
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
448
+ proxy_set_header X-Forwarded-Proto $scheme;
449
+ proxy_cache_bypass $http_upgrade;
450
+
451
+ # Timeout settings
452
+ proxy_connect_timeout 60s;
453
+ proxy_send_timeout 60s;
454
+ proxy_read_timeout 60s;
455
+ }
456
+ }
457
+ ` : ''}${backendCoreHost ? `
458
+ # ANTE Backend Core API Configuration
459
+ server {
460
+ listen 80;
461
+ listen [::]:80;
462
+ server_name ${backendCoreHost};
463
+
464
+ # Increase buffer sizes for large headers
465
+ client_header_buffer_size 16k;
466
+ large_client_header_buffers 4 16k;
467
+
468
+ # Increase body size for file uploads
469
+ client_max_body_size 100M;
470
+
471
+ location / {
472
+ proxy_pass http://localhost:${backendCorePort};
473
+ proxy_http_version 1.1;
474
+ proxy_set_header Upgrade $http_upgrade;
475
+ proxy_set_header Connection 'upgrade';
476
+ proxy_set_header Host $host;
477
+ proxy_set_header X-Real-IP $remote_addr;
478
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
479
+ proxy_set_header X-Forwarded-Proto $scheme;
480
+ proxy_cache_bypass $http_upgrade;
481
+
482
+ # Timeout settings
483
+ proxy_connect_timeout 60s;
484
+ proxy_send_timeout 60s;
485
+ proxy_read_timeout 60s;
486
+
487
+ # WebSocket support
488
+ proxy_set_header X-Forwarded-Host $host;
489
+ proxy_set_header X-Forwarded-Server $host;
490
+ }
491
+ }
398
492
  ` : ''}`;
399
493
  }
400
494
 
@@ -415,6 +509,8 @@ function generateCloudflareNginxConfig(config) {
415
509
  posAppHost,
416
510
  mlmAppHost,
417
511
  backendMlmHost,
512
+ frontendCoreHost,
513
+ backendCoreHost,
418
514
  frontendPort,
419
515
  apiPort,
420
516
  gateAppPort = 8081,
@@ -422,7 +518,9 @@ function generateCloudflareNginxConfig(config) {
422
518
  facialWebPort = 8083,
423
519
  posAppPort = 8084,
424
520
  mlmAppPort = 9005,
425
- backendMlmPort = 4001
521
+ backendMlmPort = 4001,
522
+ frontendCorePort = 8085,
523
+ backendCorePort = 4002
426
524
  } = config;
427
525
 
428
526
  // SSL certificate paths for Cloudflare self-signed certs
@@ -728,6 +826,94 @@ server {
728
826
  proxy_read_timeout 60s;
729
827
  }
730
828
  }
829
+ ` : ''}${frontendCoreHost ? `
830
+ # ANTE Frontend Core Configuration (Cloudflare SSL)
831
+ server {
832
+ listen 80;
833
+ listen 443 ssl;
834
+ listen [::]:80;
835
+ listen [::]:443 ssl;
836
+ server_name ${frontendCoreHost};
837
+
838
+ # SSL Certificate (self-signed for Cloudflare Full mode)
839
+ ssl_certificate ${sslCert};
840
+ ssl_certificate_key ${sslKey};
841
+
842
+ # Increase buffer sizes for large headers
843
+ client_header_buffer_size 16k;
844
+ large_client_header_buffers 4 16k;
845
+
846
+ # Increase body size for file uploads
847
+ client_max_body_size 100M;
848
+
849
+ # Disable caching for config.js to ensure runtime config is always fresh
850
+ location = /config.js {
851
+ proxy_pass http://localhost:${frontendCorePort}/config.js;
852
+ proxy_http_version 1.1;
853
+ proxy_set_header Host $host;
854
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
855
+ add_header Pragma "no-cache" always;
856
+ add_header Expires "0" always;
857
+ }
858
+
859
+ location / {
860
+ proxy_pass http://localhost:${frontendCorePort};
861
+ proxy_http_version 1.1;
862
+ proxy_set_header Upgrade $http_upgrade;
863
+ proxy_set_header Connection 'upgrade';
864
+ proxy_set_header Host $host;
865
+ proxy_set_header X-Real-IP $remote_addr;
866
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
867
+ proxy_set_header X-Forwarded-Proto $scheme;
868
+ proxy_cache_bypass $http_upgrade;
869
+
870
+ # Timeout settings
871
+ proxy_connect_timeout 60s;
872
+ proxy_send_timeout 60s;
873
+ proxy_read_timeout 60s;
874
+ }
875
+ }
876
+ ` : ''}${backendCoreHost ? `
877
+ # ANTE Backend Core API Configuration (Cloudflare SSL)
878
+ server {
879
+ listen 80;
880
+ listen 443 ssl;
881
+ listen [::]:80;
882
+ listen [::]:443 ssl;
883
+ server_name ${backendCoreHost};
884
+
885
+ # SSL Certificate (self-signed for Cloudflare Full mode)
886
+ ssl_certificate ${sslCert};
887
+ ssl_certificate_key ${sslKey};
888
+
889
+ # Increase buffer sizes for large headers
890
+ client_header_buffer_size 16k;
891
+ large_client_header_buffers 4 16k;
892
+
893
+ # Increase body size for file uploads
894
+ client_max_body_size 100M;
895
+
896
+ location / {
897
+ proxy_pass http://localhost:${backendCorePort};
898
+ proxy_http_version 1.1;
899
+ proxy_set_header Upgrade $http_upgrade;
900
+ proxy_set_header Connection 'upgrade';
901
+ proxy_set_header Host $host;
902
+ proxy_set_header X-Real-IP $remote_addr;
903
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
904
+ proxy_set_header X-Forwarded-Proto $scheme;
905
+ proxy_cache_bypass $http_upgrade;
906
+
907
+ # Timeout settings
908
+ proxy_connect_timeout 60s;
909
+ proxy_send_timeout 60s;
910
+ proxy_read_timeout 60s;
911
+
912
+ # WebSocket support
913
+ proxy_set_header X-Forwarded-Host $host;
914
+ proxy_set_header X-Forwarded-Server $host;
915
+ }
916
+ }
731
917
  ` : ''}`;
732
918
  }
733
919
 
@@ -776,6 +962,8 @@ export async function generateCloudflareCert() {
776
962
  * @param {string} [config.posAppHost] - POS app hostname (optional)
777
963
  * @param {string} [config.mlmAppHost] - MLM app hostname (optional)
778
964
  * @param {string} [config.backendMlmHost] - Backend MLM API hostname (optional)
965
+ * @param {string} [config.frontendCoreHost] - Frontend Core hostname (optional)
966
+ * @param {string} [config.backendCoreHost] - Backend Core API hostname (optional)
779
967
  * @param {number} config.frontendPort - Frontend port
780
968
  * @param {number} config.apiPort - API port
781
969
  * @param {number} [config.gateAppPort] - Gate app port (default: 8081)
@@ -784,6 +972,8 @@ export async function generateCloudflareCert() {
784
972
  * @param {number} [config.posAppPort] - POS app port (default: 8084)
785
973
  * @param {number} [config.mlmAppPort] - MLM app port (default: 9005)
786
974
  * @param {number} [config.backendMlmPort] - Backend MLM API port (default: 4001)
975
+ * @param {number} [config.frontendCorePort] - Frontend Core port (default: 8085)
976
+ * @param {number} [config.backendCorePort] - Backend Core API port (default: 4002)
787
977
  * @param {string[]} [config.domainsWithCerts] - List of domains that have valid SSL certificates
788
978
  * @returns {string} NGINX configuration with SSL
789
979
  */
@@ -797,6 +987,8 @@ function generateSslNginxConfig(config) {
797
987
  posAppHost,
798
988
  mlmAppHost,
799
989
  backendMlmHost,
990
+ frontendCoreHost,
991
+ backendCoreHost,
800
992
  frontendPort,
801
993
  apiPort,
802
994
  gateAppPort = 8081,
@@ -805,6 +997,8 @@ function generateSslNginxConfig(config) {
805
997
  posAppPort = 8084,
806
998
  mlmAppPort = 9005,
807
999
  backendMlmPort = 4001,
1000
+ frontendCorePort = 8085,
1001
+ backendCorePort = 4002,
808
1002
  domainsWithCerts = []
809
1003
  } = config;
810
1004
 
@@ -1170,6 +1364,125 @@ server {
1170
1364
  config_output += generateServerBlock(backendMlmHost, backendMlmPort, 'ANTE Backend MLM API');
1171
1365
  }
1172
1366
 
1367
+ if (frontendCoreHost) {
1368
+ // Frontend Core has special config.js caching rules
1369
+ if (hasCert(frontendCoreHost)) {
1370
+ config_output += `
1371
+ # ANTE Frontend Core Configuration (HTTPS)
1372
+ server {
1373
+ listen 443 ssl;
1374
+ listen [::]:443 ssl;
1375
+ http2 on;
1376
+ server_name ${frontendCoreHost};
1377
+
1378
+ # SSL Certificate paths
1379
+ ssl_certificate /etc/letsencrypt/live/${frontendCoreHost}/fullchain.pem;
1380
+ ssl_certificate_key /etc/letsencrypt/live/${frontendCoreHost}/privkey.pem;
1381
+
1382
+ # SSL Configuration
1383
+ ssl_protocols TLSv1.2 TLSv1.3;
1384
+ ssl_ciphers HIGH:!aNULL:!MD5;
1385
+ ssl_prefer_server_ciphers on;
1386
+ ssl_session_cache shared:SSL:10m;
1387
+ ssl_session_timeout 10m;
1388
+
1389
+ # Security headers
1390
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
1391
+ add_header X-Frame-Options SAMEORIGIN always;
1392
+ add_header X-Content-Type-Options nosniff always;
1393
+ add_header X-XSS-Protection "1; mode=block" always;
1394
+
1395
+ # Increase buffer sizes for large headers
1396
+ client_header_buffer_size 16k;
1397
+ large_client_header_buffers 4 16k;
1398
+
1399
+ # Increase body size for file uploads
1400
+ client_max_body_size 100M;
1401
+
1402
+ # Disable caching for config.js to ensure runtime config is always fresh
1403
+ location = /config.js {
1404
+ proxy_pass http://localhost:${frontendCorePort}/config.js;
1405
+ proxy_http_version 1.1;
1406
+ proxy_set_header Host $host;
1407
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
1408
+ add_header Pragma "no-cache" always;
1409
+ add_header Expires "0" always;
1410
+ }
1411
+
1412
+ location / {
1413
+ proxy_pass http://localhost:${frontendCorePort};
1414
+ proxy_http_version 1.1;
1415
+ proxy_set_header Upgrade $http_upgrade;
1416
+ proxy_set_header Connection 'upgrade';
1417
+ proxy_set_header Host $host;
1418
+ proxy_set_header X-Real-IP $remote_addr;
1419
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1420
+ proxy_set_header X-Forwarded-Proto $scheme;
1421
+ proxy_cache_bypass $http_upgrade;
1422
+
1423
+ # Timeout settings
1424
+ proxy_connect_timeout 60s;
1425
+ proxy_send_timeout 60s;
1426
+ proxy_read_timeout 60s;
1427
+ }
1428
+ }
1429
+
1430
+ # HTTP to HTTPS redirect for ${frontendCoreHost}
1431
+ server {
1432
+ listen 80;
1433
+ listen [::]:80;
1434
+ server_name ${frontendCoreHost};
1435
+ return 301 https://$server_name$request_uri;
1436
+ }`;
1437
+ } else {
1438
+ config_output += `
1439
+ # ANTE Frontend Core Configuration (HTTP only - SSL cert not available)
1440
+ server {
1441
+ listen 80;
1442
+ listen [::]:80;
1443
+ server_name ${frontendCoreHost};
1444
+
1445
+ # Increase buffer sizes for large headers
1446
+ client_header_buffer_size 16k;
1447
+ large_client_header_buffers 4 16k;
1448
+
1449
+ # Increase body size for file uploads
1450
+ client_max_body_size 100M;
1451
+
1452
+ # Disable caching for config.js to ensure runtime config is always fresh
1453
+ location = /config.js {
1454
+ proxy_pass http://localhost:${frontendCorePort}/config.js;
1455
+ proxy_http_version 1.1;
1456
+ proxy_set_header Host $host;
1457
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
1458
+ add_header Pragma "no-cache" always;
1459
+ add_header Expires "0" always;
1460
+ }
1461
+
1462
+ location / {
1463
+ proxy_pass http://localhost:${frontendCorePort};
1464
+ proxy_http_version 1.1;
1465
+ proxy_set_header Upgrade $http_upgrade;
1466
+ proxy_set_header Connection 'upgrade';
1467
+ proxy_set_header Host $host;
1468
+ proxy_set_header X-Real-IP $remote_addr;
1469
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1470
+ proxy_set_header X-Forwarded-Proto $scheme;
1471
+ proxy_cache_bypass $http_upgrade;
1472
+
1473
+ # Timeout settings
1474
+ proxy_connect_timeout 60s;
1475
+ proxy_send_timeout 60s;
1476
+ proxy_read_timeout 60s;
1477
+ }
1478
+ }`;
1479
+ }
1480
+ }
1481
+
1482
+ if (backendCoreHost) {
1483
+ config_output += generateServerBlock(backendCoreHost, backendCorePort, 'ANTE Backend Core API');
1484
+ }
1485
+
1173
1486
  return config_output.trim();
1174
1487
  }
1175
1488
 
@@ -1283,6 +1596,12 @@ export async function configureNginx(config) {
1283
1596
  if (config.mlmAppDomain) {
1284
1597
  console.log(chalk.gray(` Client App: ${config.mlmAppDomain} → localhost:${config.mlmAppPort || 9005}`));
1285
1598
  }
1599
+ if (config.frontendCoreDomain) {
1600
+ console.log(chalk.gray(` Frontend Core: ${config.frontendCoreDomain} → localhost:${config.frontendCorePort || 8085}`));
1601
+ }
1602
+ if (config.backendCoreDomain) {
1603
+ console.log(chalk.gray(` Backend Core API: ${config.backendCoreDomain} → localhost:${config.backendCorePort || 4002}`));
1604
+ }
1286
1605
  console.log(chalk.gray(` API: ${config.apiDomain} → localhost:${config.apiPort || 3001}`));
1287
1606
 
1288
1607
  } catch (error) {
package/src/utils/ssl.js CHANGED
@@ -141,6 +141,8 @@ export async function obtainSSLCertificate(config) {
141
141
  * @param {string} [config.posAppDomain] - POS App domain URL (optional)
142
142
  * @param {string} [config.mlmAppDomain] - MLM App domain URL (optional)
143
143
  * @param {string} [config.backendMlmDomain] - Backend MLM API domain URL (optional)
144
+ * @param {string} [config.frontendCoreDomain] - Frontend Core domain URL (optional)
145
+ * @param {string} [config.backendCoreDomain] - Backend Core API domain URL (optional)
144
146
  * @param {number} [config.frontendPort=8080] - Frontend port
145
147
  * @param {number} [config.apiPort=3001] - API port
146
148
  * @param {number} [config.gateAppPort=8081] - Gate App port
@@ -149,6 +151,8 @@ export async function obtainSSLCertificate(config) {
149
151
  * @param {number} [config.posAppPort=8084] - POS App port
150
152
  * @param {number} [config.mlmAppPort=9005] - MLM App port
151
153
  * @param {number} [config.backendMlmPort=4001] - Backend MLM API port
154
+ * @param {number} [config.frontendCorePort=8085] - Frontend Core port
155
+ * @param {number} [config.backendCorePort=4002] - Backend Core API port
152
156
  * @param {string[]} [config.domainsWithCerts=[]] - List of domains that have valid SSL certificates
153
157
  * @returns {Promise<void>}
154
158
  */
@@ -162,6 +166,8 @@ export async function updateNginxForSSL(config) {
162
166
  posAppDomain,
163
167
  mlmAppDomain,
164
168
  backendMlmDomain,
169
+ frontendCoreDomain,
170
+ backendCoreDomain,
165
171
  frontendPort = 8080,
166
172
  apiPort = 3001,
167
173
  gateAppPort = 8081,
@@ -170,6 +176,8 @@ export async function updateNginxForSSL(config) {
170
176
  posAppPort = 8084,
171
177
  mlmAppPort = 9005,
172
178
  backendMlmPort = 4001,
179
+ frontendCorePort = 8085,
180
+ backendCorePort = 4002,
173
181
  domainsWithCerts = []
174
182
  } = config;
175
183
  const spinner = ora('Updating NGINX configuration for HTTPS...').start();
@@ -199,6 +207,8 @@ export async function updateNginxForSSL(config) {
199
207
  posAppDomain,
200
208
  mlmAppDomain,
201
209
  backendMlmDomain,
210
+ frontendCoreDomain,
211
+ backendCoreDomain,
202
212
  frontendPort,
203
213
  apiPort,
204
214
  gateAppPort,
@@ -207,6 +217,8 @@ export async function updateNginxForSSL(config) {
207
217
  posAppPort,
208
218
  mlmAppPort,
209
219
  backendMlmPort,
220
+ frontendCorePort,
221
+ backendCorePort,
210
222
  ssl: true,
211
223
  domainsWithCerts
212
224
  });
@@ -228,7 +228,8 @@ export async function checkPort(port) {
228
228
  * @returns {number[]} Array of ports to check
229
229
  */
230
230
  function detectPortsFromEnv(installDir = './ante-erp') {
231
- const defaultPorts = [8080, 3001, 4001];
231
+ // Default ports now include core services (always included)
232
+ const defaultPorts = [8080, 3001, 4001, 4002, 8085];
232
233
 
233
234
  try {
234
235
  const envPath = join(installDir, '.env');
@@ -258,6 +259,24 @@ function detectPortsFromEnv(installDir = './ante-erp') {
258
259
  }
259
260
  }
260
261
 
262
+ // Check for BACKEND_CORE_PORT (in case it's customized)
263
+ const backendCorePortMatch = envContent.match(/^BACKEND_CORE_PORT=(\d+)/m);
264
+ if (backendCorePortMatch) {
265
+ const backendCorePort = parseInt(backendCorePortMatch[1]);
266
+ if (!ports.includes(backendCorePort)) {
267
+ ports.push(backendCorePort);
268
+ }
269
+ }
270
+
271
+ // Check for FRONTEND_CORE_PORT (in case it's customized)
272
+ const frontendCorePortMatch = envContent.match(/^FRONTEND_CORE_PORT=(\d+)/m);
273
+ if (frontendCorePortMatch) {
274
+ const frontendCorePort = parseInt(frontendCorePortMatch[1]);
275
+ if (!ports.includes(frontendCorePort)) {
276
+ ports.push(frontendCorePort);
277
+ }
278
+ }
279
+
261
280
  return ports;
262
281
  } catch (error) {
263
282
  return defaultPorts;