ante-erp-cli 1.11.52 → 1.11.54

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
@@ -331,6 +331,7 @@ program
331
331
  .option('--facial <url>', 'Facial Web domain/URL (non-interactive mode)')
332
332
  .option('--pos <url>', 'POS App domain/URL (non-interactive mode)')
333
333
  .option('--client <url>', 'Client App domain/URL (non-interactive mode)')
334
+ .option('--backend-client <url>', 'Backend Client API domain/URL (non-interactive mode)')
334
335
  .option('--detect', 'Auto-detect public IP address')
335
336
  .option('--ssl, --enable-ssl', 'Enable SSL certificate (Let\'s Encrypt)')
336
337
  .option('--email <email>', 'Email for SSL certificate notifications')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ante-erp-cli",
3
- "version": "1.11.52",
3
+ "version": "1.11.54",
4
4
  "description": "Comprehensive CLI tool for managing ANTE ERP self-hosted installations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -96,9 +96,9 @@ function getEnvValue(envPath, key) {
96
96
  }
97
97
 
98
98
  /**
99
- * Check if gate-app, guardian-app, facial-web, or pos-app is installed by checking docker-compose.yml
99
+ * Check if gate-app, guardian-app, facial-web, pos-app, client-app, or backend-client is installed by checking docker-compose.yml
100
100
  * @param {string} composeFile - Path to docker-compose.yml
101
- * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean}}
101
+ * @returns {{hasGateApp: boolean, hasGuardianApp: boolean, hasFacialWeb: boolean, hasPosApp: boolean, hasClientApp: boolean, hasBackendClient: boolean}}
102
102
  */
103
103
  function detectInstalledApps(composeFile) {
104
104
  try {
@@ -108,10 +108,11 @@ function detectInstalledApps(composeFile) {
108
108
  hasGuardianApp: composeContent.includes('guardian-app:') || composeContent.includes('container_name: ante-guardian-app'),
109
109
  hasFacialWeb: composeContent.includes('facial-web:') || composeContent.includes('container_name: ante-facial-web'),
110
110
  hasPosApp: composeContent.includes('pos-app:') || composeContent.includes('container_name: ante-pos'),
111
- hasClientApp: composeContent.includes('client-app:') || composeContent.includes('container_name: ante-client')
111
+ hasClientApp: composeContent.includes('client-app:') || composeContent.includes('container_name: ante-client'),
112
+ hasBackendClient: composeContent.includes('backend-client:') || composeContent.includes('container_name: ante-backend-client')
112
113
  };
113
114
  } catch {
114
- return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasClientApp: false };
115
+ return { hasGateApp: false, hasGuardianApp: false, hasFacialWeb: false, hasPosApp: false, hasClientApp: false, hasBackendClient: false };
115
116
  }
116
117
  }
117
118
 
@@ -159,10 +160,10 @@ export async function setDomain(options) {
159
160
  console.log(); // Add spacing
160
161
  }
161
162
 
162
- let frontendUrl, apiUrl, gateAppUrl, guardianAppUrl, facialWebUrl, posAppUrl, clientAppUrl;
163
+ let frontendUrl, apiUrl, gateAppUrl, guardianAppUrl, facialWebUrl, posAppUrl, clientAppUrl, backendClientUrl;
163
164
 
164
165
  // Detect which apps are installed
165
- const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasClientApp } = detectInstalledApps(composeFile);
166
+ const { hasGateApp, hasGuardianApp, hasFacialWeb, hasPosApp, hasClientApp, hasBackendClient } = detectInstalledApps(composeFile);
166
167
 
167
168
  if (options.interactive !== false) {
168
169
  // Show current values
@@ -173,6 +174,7 @@ export async function setDomain(options) {
173
174
  const currentFacialWebUrl = getEnvValue(envPath, 'FACIAL_WEB_URL');
174
175
  const currentPosAppUrl = getEnvValue(envPath, 'POS_APP_URL');
175
176
  const currentClientAppUrl = getEnvValue(envPath, 'CLIENT_APP_URL');
177
+ const currentBackendClientUrl = getEnvValue(envPath, 'CLIENT_API_URL');
176
178
 
177
179
  if (currentFrontendUrl) {
178
180
  console.log(chalk.gray(`Current Frontend URL: ${currentFrontendUrl}`));
@@ -192,6 +194,9 @@ export async function setDomain(options) {
192
194
  if (currentClientAppUrl) {
193
195
  console.log(chalk.gray(`Current Client App URL: ${currentClientAppUrl}`));
194
196
  }
197
+ if (currentBackendClientUrl) {
198
+ console.log(chalk.gray(`Current Backend-Client API URL: ${currentBackendClientUrl}`));
199
+ }
195
200
  if (currentApiUrl) {
196
201
  console.log(chalk.gray(`Current API URL: ${currentApiUrl}\n`));
197
202
  }
@@ -478,6 +483,7 @@ export async function setDomain(options) {
478
483
  if (hasFacialWeb) facialWebUrl = `https://${subdomain}-fr.ante.ph`;
479
484
  if (hasPosApp) posAppUrl = `https://${subdomain}-pos.ante.ph`;
480
485
  if (hasClientApp) clientAppUrl = `https://${subdomain}-client.ante.ph`;
486
+ if (hasBackendClient) backendClientUrl = `https://${subdomain}-api-client.ante.ph`;
481
487
 
482
488
  // Display generated domains for confirmation
483
489
  console.log(chalk.cyan('\nšŸ“‹ Generated Domain Configuration:\n'));
@@ -488,6 +494,7 @@ export async function setDomain(options) {
488
494
  if (hasFacialWeb) console.log(chalk.gray(` Facial Web: ${facialWebUrl}`));
489
495
  if (hasPosApp) console.log(chalk.gray(` POS App: ${posAppUrl}`));
490
496
  if (hasClientApp) console.log(chalk.gray(` Client App: ${clientAppUrl}`));
497
+ if (hasBackendClient) console.log(chalk.gray(` Backend-Client: ${backendClientUrl}`));
491
498
  console.log('');
492
499
 
493
500
  const confirmDomains = await inquirer.prompt([
@@ -565,6 +572,16 @@ export async function setDomain(options) {
565
572
  });
566
573
  }
567
574
 
575
+ if (hasBackendClient) {
576
+ domainPrompts.push({
577
+ type: 'input',
578
+ name: 'backendClientUrl',
579
+ message: 'Backend-Client API URL:\n Examples: https://staging-api-client.ante.ph or http://143.198.91.153:4001\n Enter URL',
580
+ default: currentBackendClientUrl || 'http://localhost:4001',
581
+ validate: validateUrl
582
+ });
583
+ }
584
+
568
585
  domainPrompts.push({
569
586
  type: 'input',
570
587
  name: 'apiUrl',
@@ -583,6 +600,7 @@ export async function setDomain(options) {
583
600
  if (hasFacialWeb) facialWebUrl = sanitizeUrl(answers.facialWebUrl);
584
601
  if (hasPosApp) posAppUrl = sanitizeUrl(answers.posAppUrl);
585
602
  if (hasClientApp) clientAppUrl = sanitizeUrl(answers.clientAppUrl);
603
+ if (hasBackendClient) backendClientUrl = sanitizeUrl(answers.backendClientUrl);
586
604
  }
587
605
  }
588
606
  } else {
@@ -668,6 +686,17 @@ export async function setDomain(options) {
668
686
  }
669
687
  }
670
688
 
689
+ if (hasBackendClient) {
690
+ backendClientUrl = options.backendClient ? sanitizeUrl(options.backendClient) : getEnvValue(envPath, 'CLIENT_API_URL');
691
+ if (backendClientUrl) {
692
+ const validation = validateUrl(backendClientUrl);
693
+ if (validation !== true) {
694
+ console.log(chalk.red(`Error: Invalid Backend-Client API URL - ${validation}`));
695
+ process.exit(1);
696
+ }
697
+ }
698
+ }
699
+
671
700
  // Show what will be configured in non-interactive mode
672
701
  console.log(chalk.cyan('\nšŸ“‹ Non-interactive mode configuration:\n'));
673
702
  console.log(chalk.gray(` Frontend: ${frontendUrl}`));
@@ -677,6 +706,7 @@ export async function setDomain(options) {
677
706
  if (hasFacialWeb && facialWebUrl) console.log(chalk.gray(` Facial Web: ${facialWebUrl}`));
678
707
  if (hasPosApp && posAppUrl) console.log(chalk.gray(` POS App: ${posAppUrl}`));
679
708
  if (hasClientApp && clientAppUrl) console.log(chalk.gray(` Client App: ${clientAppUrl}`));
709
+ if (hasBackendClient && backendClientUrl) console.log(chalk.gray(` Backend-Client: ${backendClientUrl}`));
680
710
  console.log('');
681
711
  }
682
712
 
@@ -709,6 +739,10 @@ export async function setDomain(options) {
709
739
  envUpdates.CLIENT_APP_URL = clientAppUrl;
710
740
  }
711
741
 
742
+ if (hasBackendClient && backendClientUrl) {
743
+ envUpdates.CLIENT_API_URL = backendClientUrl;
744
+ }
745
+
712
746
  updateEnvFile(envPath, envUpdates);
713
747
 
714
748
  console.log(chalk.green('āœ“ Configuration updated'));
@@ -751,6 +785,11 @@ export async function setDomain(options) {
751
785
  nginxConfig.clientAppPort = 9005;
752
786
  }
753
787
 
788
+ if (hasBackendClient && backendClientUrl) {
789
+ nginxConfig.backendClientDomain = backendClientUrl;
790
+ nginxConfig.backendClientPort = 4001;
791
+ }
792
+
754
793
  await configureNginx(nginxConfig);
755
794
  } catch (error) {
756
795
  console.log(chalk.yellow('\n⚠ NGINX configuration failed:', error.message));
@@ -855,6 +894,12 @@ export async function setDomain(options) {
855
894
  domains.push(clientAppDomain);
856
895
  }
857
896
  }
897
+ if (hasBackendClient && backendClientUrl && backendClientUrl.startsWith('https://')) {
898
+ const backendClientDomain = extractDomain(backendClientUrl);
899
+ if (backendClientDomain !== 'localhost' && !backendClientDomain.match(/^\d+\.\d+\.\d+\.\d+$/) && !domains.includes(backendClientDomain)) {
900
+ domains.push(backendClientDomain);
901
+ }
902
+ }
858
903
 
859
904
  for (const domain of domains) {
860
905
  console.log(chalk.gray(` Obtaining certificate for ${domain}...`));
@@ -902,6 +947,11 @@ export async function setDomain(options) {
902
947
  sslNginxConfig.clientAppPort = 9005;
903
948
  }
904
949
 
950
+ if (hasBackendClient && backendClientUrl) {
951
+ sslNginxConfig.backendClientDomain = backendClientUrl;
952
+ sslNginxConfig.backendClientPort = 4001;
953
+ }
954
+
905
955
  await updateNginxForSSL(sslNginxConfig);
906
956
 
907
957
  // Step 3: Setup auto-renewal
@@ -53,6 +53,7 @@ export async function installNginx(spinner) {
53
53
  * @param {string} [config.facialAppDomain] - Facial web app domain (optional)
54
54
  * @param {string} [config.posAppDomain] - POS app domain (optional)
55
55
  * @param {string} [config.clientAppDomain] - Client app domain (optional)
56
+ * @param {string} [config.backendClientDomain] - Backend client API domain (optional)
56
57
  * @param {number} config.frontendPort - Frontend Docker port (default: 8080)
57
58
  * @param {number} config.apiPort - API Docker port (default: 3001)
58
59
  * @param {number} [config.gateAppPort] - Gate app Docker port (default: 8081)
@@ -60,6 +61,7 @@ export async function installNginx(spinner) {
60
61
  * @param {number} [config.facialWebPort] - Facial web app Docker port (default: 8083)
61
62
  * @param {number} [config.posAppPort] - POS app Docker port (default: 8084)
62
63
  * @param {number} [config.clientAppPort] - Client app Docker port (default: 9005)
64
+ * @param {number} [config.backendClientPort] - Backend client API Docker port (default: 4001)
63
65
  * @param {boolean} config.ssl - Enable SSL configuration (default: false)
64
66
  * @returns {string} NGINX configuration content
65
67
  */
@@ -72,6 +74,7 @@ export function generateNginxConfig(config) {
72
74
  facialAppDomain,
73
75
  posAppDomain,
74
76
  clientAppDomain,
77
+ backendClientDomain,
75
78
  frontendPort = 8080,
76
79
  apiPort = 3001,
77
80
  gateAppPort = 8081,
@@ -79,6 +82,7 @@ export function generateNginxConfig(config) {
79
82
  facialWebPort = 8083,
80
83
  posAppPort = 8084,
81
84
  clientAppPort = 9005,
85
+ backendClientPort = 4001,
82
86
  ssl = false
83
87
  } = config;
84
88
 
@@ -90,6 +94,7 @@ export function generateNginxConfig(config) {
90
94
  const facialAppHost = facialAppDomain ? facialAppDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
91
95
  const posAppHost = posAppDomain ? posAppDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
92
96
  const clientAppHost = clientAppDomain ? clientAppDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
97
+ const backendClientHost = backendClientDomain ? backendClientDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '') : null;
93
98
 
94
99
  if (ssl) {
95
100
  return generateSslNginxConfig({
@@ -100,13 +105,15 @@ export function generateNginxConfig(config) {
100
105
  facialAppHost,
101
106
  posAppHost,
102
107
  clientAppHost,
108
+ backendClientHost,
103
109
  frontendPort,
104
110
  apiPort,
105
111
  gateAppPort,
106
112
  guardianAppPort,
107
113
  facialWebPort,
108
114
  posAppPort,
109
- clientAppPort
115
+ clientAppPort,
116
+ backendClientPort
110
117
  });
111
118
  }
112
119
 
@@ -330,6 +337,37 @@ server {
330
337
  proxy_read_timeout 60s;
331
338
  }
332
339
  }
340
+ ` : ''}${backendClientHost ? `
341
+ # ANTE Backend Client API Configuration
342
+ server {
343
+ listen 80;
344
+ listen [::]:80;
345
+ server_name ${backendClientHost};
346
+
347
+ # Increase buffer sizes for large headers
348
+ client_header_buffer_size 16k;
349
+ large_client_header_buffers 4 16k;
350
+
351
+ # Increase body size for file uploads
352
+ client_max_body_size 100M;
353
+
354
+ location / {
355
+ proxy_pass http://localhost:${backendClientPort};
356
+ proxy_http_version 1.1;
357
+ proxy_set_header Upgrade $http_upgrade;
358
+ proxy_set_header Connection 'upgrade';
359
+ proxy_set_header Host $host;
360
+ proxy_set_header X-Real-IP $remote_addr;
361
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
362
+ proxy_set_header X-Forwarded-Proto $scheme;
363
+ proxy_cache_bypass $http_upgrade;
364
+
365
+ # Timeout settings
366
+ proxy_connect_timeout 60s;
367
+ proxy_send_timeout 60s;
368
+ proxy_read_timeout 60s;
369
+ }
370
+ }
333
371
  ` : ''}`;
334
372
  }
335
373
 
@@ -343,6 +381,7 @@ server {
343
381
  * @param {string} [config.facialAppHost] - Facial web app hostname (optional)
344
382
  * @param {string} [config.posAppHost] - POS app hostname (optional)
345
383
  * @param {string} [config.clientAppHost] - Client app hostname (optional)
384
+ * @param {string} [config.backendClientHost] - Backend client API hostname (optional)
346
385
  * @param {number} config.frontendPort - Frontend port
347
386
  * @param {number} config.apiPort - API port
348
387
  * @param {number} [config.gateAppPort] - Gate app port (default: 8081)
@@ -350,6 +389,7 @@ server {
350
389
  * @param {number} [config.facialWebPort] - Facial web app port (default: 8083)
351
390
  * @param {number} [config.posAppPort] - POS app port (default: 8084)
352
391
  * @param {number} [config.clientAppPort] - Client app port (default: 9005)
392
+ * @param {number} [config.backendClientPort] - Backend client API port (default: 4001)
353
393
  * @returns {string} NGINX configuration with SSL
354
394
  */
355
395
  function generateSslNginxConfig(config) {
@@ -361,13 +401,15 @@ function generateSslNginxConfig(config) {
361
401
  facialAppHost,
362
402
  posAppHost,
363
403
  clientAppHost,
404
+ backendClientHost,
364
405
  frontendPort,
365
406
  apiPort,
366
407
  gateAppPort = 8081,
367
408
  guardianAppPort = 8082,
368
409
  facialWebPort = 8083,
369
410
  posAppPort = 8084,
370
- clientAppPort = 9005
411
+ clientAppPort = 9005,
412
+ backendClientPort = 4001
371
413
  } = config;
372
414
 
373
415
  return `# ANTE Frontend Configuration (HTTPS)
@@ -782,6 +824,63 @@ server {
782
824
  server_name ${clientAppHost};
783
825
  return 301 https://$server_name$request_uri;
784
826
  }
827
+ ` : ''}${backendClientHost ? `
828
+ # ANTE Backend Client API Configuration (HTTPS)
829
+ server {
830
+ listen 443 ssl;
831
+ listen [::]:443 ssl;
832
+ http2 on;
833
+ server_name ${backendClientHost};
834
+
835
+ # SSL Certificate paths
836
+ ssl_certificate /etc/letsencrypt/live/${backendClientHost}/fullchain.pem;
837
+ ssl_certificate_key /etc/letsencrypt/live/${backendClientHost}/privkey.pem;
838
+
839
+ # SSL Configuration
840
+ ssl_protocols TLSv1.2 TLSv1.3;
841
+ ssl_ciphers HIGH:!aNULL:!MD5;
842
+ ssl_prefer_server_ciphers on;
843
+ ssl_session_cache shared:SSL:10m;
844
+ ssl_session_timeout 10m;
845
+
846
+ # Security headers
847
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
848
+ add_header X-Frame-Options SAMEORIGIN always;
849
+ add_header X-Content-Type-Options nosniff always;
850
+ add_header X-XSS-Protection "1; mode=block" always;
851
+
852
+ # Increase buffer sizes for large headers
853
+ client_header_buffer_size 16k;
854
+ large_client_header_buffers 4 16k;
855
+
856
+ # Increase body size for file uploads
857
+ client_max_body_size 100M;
858
+
859
+ location / {
860
+ proxy_pass http://localhost:${backendClientPort};
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
+
877
+ # HTTP to HTTPS redirect for ${backendClientHost}
878
+ server {
879
+ listen 80;
880
+ listen [::]:80;
881
+ server_name ${backendClientHost};
882
+ return 301 https://$server_name$request_uri;
883
+ }
785
884
  ` : ''}`;
786
885
  }
787
886