ante-erp-cli 1.11.60 → 1.11.62

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.60",
3
+ "version": "1.11.62",
4
4
  "description": "Comprehensive CLI tool for managing ANTE ERP self-hosted installations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -914,64 +914,116 @@ export async function setDomain(options) {
914
914
  }
915
915
  }
916
916
 
917
+ // Track successful and failed domains
918
+ const successfulDomains = [];
919
+ const failedDomains = [];
920
+
921
+ // Frontend and API are mandatory - they must succeed
922
+ const mandatoryDomains = [frontendDomain];
923
+ if (apiDomain !== frontendDomain) {
924
+ mandatoryDomains.push(apiDomain);
925
+ }
926
+
917
927
  for (const domain of domains) {
918
928
  console.log(chalk.gray(` Obtaining certificate for ${domain}...`));
919
- await obtainSSLCertificate({
920
- domain,
921
- email: sslEmail,
922
- staging: options.sslStaging || false
923
- });
929
+ try {
930
+ await obtainSSLCertificate({
931
+ domain,
932
+ email: sslEmail,
933
+ staging: options.sslStaging || false
934
+ });
935
+ successfulDomains.push(domain);
936
+ } catch (error) {
937
+ const isMandatory = mandatoryDomains.includes(domain);
938
+ if (isMandatory) {
939
+ // Mandatory domain failed - add to failures but continue
940
+ console.log(chalk.red(`✗ Failed (mandatory): ${domain}`));
941
+ failedDomains.push({ domain, error: error.message, mandatory: true });
942
+ } else {
943
+ // Optional domain failed - continue with others
944
+ console.log(chalk.yellow(`⚠ Skipped (optional): ${domain} - ${error.message}`));
945
+ failedDomains.push({ domain, error: error.message, mandatory: false });
946
+ }
947
+ }
924
948
  }
925
949
 
926
- console.log(chalk.green('\n✓ SSL certificates obtained\n'));
950
+ // Show SSL certificate summary
951
+ console.log(chalk.cyan('\n📋 SSL Certificate Results:\n'));
952
+ successfulDomains.forEach(d => console.log(chalk.green(` ✓ ${d} - Certificate obtained`)));
953
+ failedDomains.forEach(f => console.log(chalk.yellow(` ⚠ ${f.domain} - ${f.error}`)));
927
954
 
928
- // Step 2: Update NGINX for HTTPS
929
- console.log(chalk.cyan('🔧 Configuring NGINX for HTTPS...\n'));
955
+ // Check if any mandatory domains failed
956
+ const mandatoryFailures = failedDomains.filter(f => f.mandatory);
957
+ if (mandatoryFailures.length > 0) {
958
+ console.log(chalk.red('\n✗ Mandatory domains failed to get SSL certificates.'));
959
+ console.log(chalk.yellow(' NGINX will be configured with HTTP for failed domains.\n'));
960
+ }
930
961
 
931
- const sslNginxConfig = {
932
- frontendDomain: frontendUrl,
933
- apiDomain: apiUrl,
934
- frontendPort: 8080,
935
- apiPort: 3001
936
- };
962
+ // Proceed only if at least some domains succeeded
963
+ if (successfulDomains.length > 0) {
964
+ console.log(chalk.green(`\n✓ ${successfulDomains.length} of ${domains.length} SSL certificates obtained\n`));
937
965
 
938
- if (hasGateApp && gateAppUrl) {
939
- sslNginxConfig.gateAppDomain = gateAppUrl;
940
- sslNginxConfig.gateAppPort = 8081;
941
- }
966
+ // Step 2: Update NGINX for HTTPS (with partial SSL support)
967
+ console.log(chalk.cyan('🔧 Configuring NGINX for HTTPS...\n'));
942
968
 
943
- if (hasGuardianApp && guardianAppUrl) {
944
- sslNginxConfig.guardianAppDomain = guardianAppUrl;
945
- sslNginxConfig.guardianAppPort = 8082;
946
- }
969
+ const sslNginxConfig = {
970
+ frontendDomain: frontendUrl,
971
+ apiDomain: apiUrl,
972
+ frontendPort: 8080,
973
+ apiPort: 3001,
974
+ domainsWithCerts: successfulDomains
975
+ };
947
976
 
948
- if (hasFacialWeb && facialWebUrl) {
949
- sslNginxConfig.facialAppDomain = facialWebUrl;
950
- sslNginxConfig.facialWebPort = 8083;
951
- }
977
+ if (hasGateApp && gateAppUrl) {
978
+ sslNginxConfig.gateAppDomain = gateAppUrl;
979
+ sslNginxConfig.gateAppPort = 8081;
980
+ }
952
981
 
953
- if (hasPosApp && posAppUrl) {
954
- sslNginxConfig.posAppDomain = posAppUrl;
955
- sslNginxConfig.posAppPort = 8084;
956
- }
982
+ if (hasGuardianApp && guardianAppUrl) {
983
+ sslNginxConfig.guardianAppDomain = guardianAppUrl;
984
+ sslNginxConfig.guardianAppPort = 8082;
985
+ }
957
986
 
958
- if (hasMlmApp && mlmAppUrl) {
959
- sslNginxConfig.mlmAppDomain = mlmAppUrl;
960
- sslNginxConfig.mlmAppPort = 9005;
961
- }
987
+ if (hasFacialWeb && facialWebUrl) {
988
+ sslNginxConfig.facialAppDomain = facialWebUrl;
989
+ sslNginxConfig.facialWebPort = 8083;
990
+ }
962
991
 
963
- if (hasBackendMlm && backendMlmUrl) {
964
- sslNginxConfig.backendMlmDomain = backendMlmUrl;
965
- sslNginxConfig.backendMlmPort = 4001;
966
- }
992
+ if (hasPosApp && posAppUrl) {
993
+ sslNginxConfig.posAppDomain = posAppUrl;
994
+ sslNginxConfig.posAppPort = 8084;
995
+ }
996
+
997
+ if (hasMlmApp && mlmAppUrl) {
998
+ sslNginxConfig.mlmAppDomain = mlmAppUrl;
999
+ sslNginxConfig.mlmAppPort = 9005;
1000
+ }
1001
+
1002
+ if (hasBackendMlm && backendMlmUrl) {
1003
+ sslNginxConfig.backendMlmDomain = backendMlmUrl;
1004
+ sslNginxConfig.backendMlmPort = 4001;
1005
+ }
967
1006
 
968
- await updateNginxForSSL(sslNginxConfig);
1007
+ await updateNginxForSSL(sslNginxConfig);
969
1008
 
970
- // Step 3: Setup auto-renewal
971
- console.log(chalk.cyan('🔄 Setting up automatic certificate renewal...\n'));
972
- await setupAutoRenewal();
1009
+ // Step 3: Setup auto-renewal
1010
+ console.log(chalk.cyan('🔄 Setting up automatic certificate renewal...\n'));
1011
+ await setupAutoRenewal();
973
1012
 
974
- console.log(chalk.green('✓ SSL/HTTPS configured successfully\n'));
1013
+ if (failedDomains.length > 0) {
1014
+ console.log(chalk.yellow('⚠ SSL/HTTPS partially configured\n'));
1015
+ console.log(chalk.gray(' Failed domains will use HTTP only.'));
1016
+ console.log(chalk.gray(' You can retry failed domains later with:'));
1017
+ console.log(chalk.gray(` ante ssl enable --email ${sslEmail}\n`));
1018
+ } else {
1019
+ console.log(chalk.green('✓ SSL/HTTPS configured successfully\n'));
1020
+ }
1021
+ } else {
1022
+ console.log(chalk.yellow('\n⚠ No SSL certificates were obtained.'));
1023
+ console.log(chalk.gray(' NGINX will use HTTP only.'));
1024
+ console.log(chalk.gray(' You can enable SSL later with:'));
1025
+ console.log(chalk.gray(` ante ssl enable --email ${sslEmail}\n`));
1026
+ }
975
1027
 
976
1028
  } catch (error) {
977
1029
  console.log(chalk.red('\n✗ SSL setup failed:'), error.message);
@@ -195,18 +195,23 @@ export async function sslEnable(options) {
195
195
 
196
196
  console.log();
197
197
 
198
- const { proceed } = await inquirer.prompt([
199
- {
200
- type: 'confirm',
201
- name: 'proceed',
202
- message: 'Do you want to renew or reconfigure SSL certificates?',
203
- default: false
198
+ // In non-interactive mode, automatically proceed with renewal/reconfiguration
199
+ if (options.interactive !== false) {
200
+ const { proceed } = await inquirer.prompt([
201
+ {
202
+ type: 'confirm',
203
+ name: 'proceed',
204
+ message: 'Do you want to renew or reconfigure SSL certificates?',
205
+ default: false
206
+ }
207
+ ]);
208
+
209
+ if (!proceed) {
210
+ console.log(chalk.gray('\nSSL configuration cancelled.\n'));
211
+ return;
204
212
  }
205
- ]);
206
-
207
- if (!proceed) {
208
- console.log(chalk.gray('\nSSL configuration cancelled.\n'));
209
- return;
213
+ } else {
214
+ console.log(chalk.gray('Non-interactive mode: Proceeding with SSL reconfiguration...\n'));
210
215
  }
211
216
  }
212
217
 
@@ -368,9 +373,21 @@ export async function sslEnable(options) {
368
373
 
369
374
  console.log(chalk.bold('\n🚀 Starting SSL Configuration...\n'));
370
375
 
371
- // Step 1: Obtain SSL certificates for all domains
376
+ // Step 1: Obtain SSL certificates for all domains (with graceful error handling)
372
377
  console.log(chalk.cyan('📋 Obtaining SSL certificates...\n'));
373
378
 
379
+ // Track successful and failed domains
380
+ const successfulDomains = [];
381
+ const failedDomains = [];
382
+
383
+ // Extract domain names for the frontend and API (mandatory)
384
+ const frontendDomainName = frontendUrl.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
385
+ const apiDomainName = apiUrl.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
386
+ const mandatoryDomains = [frontendDomainName];
387
+ if (apiDomainName !== frontendDomainName) {
388
+ mandatoryDomains.push(apiDomainName);
389
+ }
390
+
374
391
  for (const domainConfig of domains) {
375
392
  try {
376
393
  console.log(chalk.gray(` Obtaining certificate for ${domainConfig.domain}...`));
@@ -379,18 +396,36 @@ export async function sslEnable(options) {
379
396
  email: options.email,
380
397
  staging: options.staging || false
381
398
  });
399
+ successfulDomains.push(domainConfig.domain);
382
400
  } catch (error) {
383
- console.log(chalk.red(`\n✗ Failed to obtain SSL certificate for ${domainConfig.domain}:`), error.message);
384
- console.log(chalk.yellow('\n⚠ Troubleshooting:'));
385
- console.log(chalk.gray(' 1. Verify DNS points to this server'));
386
- console.log(chalk.gray(' 2. Ensure ports 80 and 443 are open'));
387
- console.log(chalk.gray(' 3. Check NGINX is running: systemctl status nginx'));
388
- console.log(chalk.gray(' 4. View certbot logs: /var/log/letsencrypt/letsencrypt.log\n'));
389
- process.exit(1);
401
+ const isMandatory = mandatoryDomains.includes(domainConfig.domain);
402
+ if (isMandatory) {
403
+ console.log(chalk.red(`✗ Failed (mandatory): ${domainConfig.domain}`));
404
+ failedDomains.push({ domain: domainConfig.domain, error: error.message, mandatory: true });
405
+ } else {
406
+ console.log(chalk.yellow(`⚠ Skipped (optional): ${domainConfig.domain} - ${error.message}`));
407
+ failedDomains.push({ domain: domainConfig.domain, error: error.message, mandatory: false });
408
+ }
390
409
  }
391
410
  }
392
411
 
393
- console.log(chalk.green('\n✓ All SSL certificates obtained\n'));
412
+ // Show SSL certificate summary
413
+ console.log(chalk.cyan('\n📋 SSL Certificate Results:\n'));
414
+ successfulDomains.forEach(d => console.log(chalk.green(` ✓ ${d} - Certificate obtained`)));
415
+ failedDomains.forEach(f => console.log(chalk.yellow(` ⚠ ${f.domain} - ${f.error}`)));
416
+
417
+ // Check if we can proceed
418
+ if (successfulDomains.length === 0) {
419
+ console.log(chalk.red('\n✗ No SSL certificates were obtained.'));
420
+ console.log(chalk.yellow('\n⚠ Troubleshooting:'));
421
+ console.log(chalk.gray(' 1. Verify DNS points to this server'));
422
+ console.log(chalk.gray(' 2. Ensure ports 80 and 443 are open'));
423
+ console.log(chalk.gray(' 3. Check NGINX is running: systemctl status nginx'));
424
+ console.log(chalk.gray(' 4. View certbot logs: /var/log/letsencrypt/letsencrypt.log\n'));
425
+ process.exit(1);
426
+ }
427
+
428
+ console.log(chalk.green(`\n✓ ${successfulDomains.length} of ${domains.length} SSL certificates obtained\n`));
394
429
 
395
430
  // Step 2: Update NGINX configuration with SSL
396
431
  console.log(chalk.cyan('🔧 Configuring NGINX for HTTPS...\n'));
@@ -404,7 +439,8 @@ export async function sslEnable(options) {
404
439
  frontendDomain: frontendUrl,
405
440
  apiDomain: apiUrl,
406
441
  frontendPort,
407
- apiPort
442
+ apiPort,
443
+ domainsWithCerts: successfulDomains
408
444
  };
409
445
 
410
446
  if (gateAppUrl) {
@@ -452,15 +488,32 @@ export async function sslEnable(options) {
452
488
  await setupAutoRenewal();
453
489
 
454
490
  // Success summary
455
- console.log(chalk.bold.green('\n✓ SSL/HTTPS Configuration Complete!\n'));
491
+ if (failedDomains.length > 0) {
492
+ console.log(chalk.bold.yellow('\n⚠ SSL/HTTPS Configuration Partially Complete!\n'));
493
+ console.log(chalk.yellow('Some domains failed to get SSL certificates:'));
494
+ failedDomains.forEach(f => {
495
+ console.log(chalk.gray(` • ${f.domain}: ${f.error}`));
496
+ });
497
+ console.log(chalk.gray('\nFailed domains will use HTTP only until DNS is configured.'));
498
+ console.log(chalk.gray('You can retry later with: ante ssl enable --email ' + options.email + '\n'));
499
+ } else {
500
+ console.log(chalk.bold.green('\n✓ SSL/HTTPS Configuration Complete!\n'));
501
+ }
456
502
 
457
503
  console.log(chalk.cyan('Secured Domains:'));
458
- domains.forEach(d => {
504
+ domains.filter(d => successfulDomains.includes(d.domain)).forEach(d => {
459
505
  const httpsUrl = d.url.replace('http://', 'https://').replace(/:\d+$/, '');
460
506
  console.log(chalk.white(` • ${d.domain}`));
461
507
  console.log(chalk.gray(` ${httpsUrl}\n`));
462
508
  });
463
509
 
510
+ if (failedDomains.length > 0) {
511
+ console.log(chalk.yellow('Unsecured Domains (HTTP only):'));
512
+ failedDomains.forEach(f => {
513
+ console.log(chalk.gray(` • ${f.domain} - DNS not configured\n`));
514
+ });
515
+ }
516
+
464
517
  console.log(chalk.cyan('Certificate Details:'));
465
518
  console.log(chalk.gray(' Provider: Let\'s Encrypt'));
466
519
  console.log(chalk.gray(' Validity: 90 days'));
@@ -85,7 +85,8 @@ export function generateNginxConfig(config) {
85
85
  mlmAppPort = 9005,
86
86
  backendMlmPort = 4001,
87
87
  ssl = false,
88
- cloudflareSsl = false
88
+ cloudflareSsl = false,
89
+ domainsWithCerts = []
89
90
  } = config;
90
91
 
91
92
  // Extract domain without protocol
@@ -138,7 +139,8 @@ export function generateNginxConfig(config) {
138
139
  facialWebPort,
139
140
  posAppPort,
140
141
  mlmAppPort,
141
- backendMlmPort
142
+ backendMlmPort,
143
+ domainsWithCerts
142
144
  });
143
145
  }
144
146
 
@@ -782,6 +784,7 @@ export async function generateCloudflareCert() {
782
784
  * @param {number} [config.posAppPort] - POS app port (default: 8084)
783
785
  * @param {number} [config.mlmAppPort] - MLM app port (default: 9005)
784
786
  * @param {number} [config.backendMlmPort] - Backend MLM API port (default: 4001)
787
+ * @param {string[]} [config.domainsWithCerts] - List of domains that have valid SSL certificates
785
788
  * @returns {string} NGINX configuration with SSL
786
789
  */
787
790
  function generateSslNginxConfig(config) {
@@ -801,19 +804,26 @@ function generateSslNginxConfig(config) {
801
804
  facialWebPort = 8083,
802
805
  posAppPort = 8084,
803
806
  mlmAppPort = 9005,
804
- backendMlmPort = 4001
807
+ backendMlmPort = 4001,
808
+ domainsWithCerts = []
805
809
  } = config;
806
810
 
807
- return `# ANTE Frontend Configuration (HTTPS)
811
+ // Helper function to check if a domain has a valid SSL certificate
812
+ // If domainsWithCerts is empty, assume all domains have certs (backward compatibility)
813
+ const hasCert = (host) => domainsWithCerts.length === 0 || domainsWithCerts.includes(host);
814
+
815
+ // Helper function to generate SSL server block
816
+ const generateSslServerBlock = (host, port, appName, extraConfig = '') => `
817
+ # ${appName} Configuration (HTTPS)
808
818
  server {
809
819
  listen 443 ssl;
810
820
  listen [::]:443 ssl;
811
821
  http2 on;
812
- server_name ${frontendHost};
822
+ server_name ${host};
813
823
 
814
824
  # SSL Certificate paths
815
- ssl_certificate /etc/letsencrypt/live/${frontendHost}/fullchain.pem;
816
- ssl_certificate_key /etc/letsencrypt/live/${frontendHost}/privkey.pem;
825
+ ssl_certificate /etc/letsencrypt/live/${host}/fullchain.pem;
826
+ ssl_certificate_key /etc/letsencrypt/live/${host}/privkey.pem;
817
827
 
818
828
  # SSL Configuration
819
829
  ssl_protocols TLSv1.2 TLSv1.3;
@@ -834,9 +844,9 @@ server {
834
844
 
835
845
  # Increase body size for file uploads
836
846
  client_max_body_size 100M;
837
-
847
+ ${extraConfig}
838
848
  location / {
839
- proxy_pass http://localhost:${frontendPort};
849
+ proxy_pass http://localhost:${port};
840
850
  proxy_http_version 1.1;
841
851
  proxy_set_header Upgrade $http_upgrade;
842
852
  proxy_set_header Connection 'upgrade';
@@ -853,37 +863,21 @@ server {
853
863
  }
854
864
  }
855
865
 
856
- # HTTP to HTTPS redirect for ${frontendHost}
866
+ # HTTP to HTTPS redirect for ${host}
857
867
  server {
858
868
  listen 80;
859
869
  listen [::]:80;
860
- server_name ${frontendHost};
870
+ server_name ${host};
861
871
  return 301 https://$server_name$request_uri;
862
- }
872
+ }`;
863
873
 
864
- # ANTE API Configuration (HTTPS)
874
+ // Helper function to generate HTTP-only server block (for domains without SSL cert)
875
+ const generateHttpServerBlock = (host, port, appName, extraConfig = '') => `
876
+ # ${appName} Configuration (HTTP only - SSL cert not available)
865
877
  server {
866
- listen 443 ssl;
867
- listen [::]:443 ssl;
868
- http2 on;
869
- server_name ${apiHost};
870
-
871
- # SSL Certificate paths
872
- ssl_certificate /etc/letsencrypt/live/${apiHost}/fullchain.pem;
873
- ssl_certificate_key /etc/letsencrypt/live/${apiHost}/privkey.pem;
874
-
875
- # SSL Configuration
876
- ssl_protocols TLSv1.2 TLSv1.3;
877
- ssl_ciphers HIGH:!aNULL:!MD5;
878
- ssl_prefer_server_ciphers on;
879
- ssl_session_cache shared:SSL:10m;
880
- ssl_session_timeout 10m;
881
-
882
- # Security headers
883
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
884
- add_header X-Frame-Options SAMEORIGIN always;
885
- add_header X-Content-Type-Options nosniff always;
886
- add_header X-XSS-Protection "1; mode=block" always;
878
+ listen 80;
879
+ listen [::]:80;
880
+ server_name ${host};
887
881
 
888
882
  # Increase buffer sizes for large headers
889
883
  client_header_buffer_size 16k;
@@ -891,9 +885,9 @@ server {
891
885
 
892
886
  # Increase body size for file uploads
893
887
  client_max_body_size 100M;
894
-
888
+ ${extraConfig}
895
889
  location / {
896
- proxy_pass http://localhost:${apiPort};
890
+ proxy_pass http://localhost:${port};
897
891
  proxy_http_version 1.1;
898
892
  proxy_set_header Upgrade $http_upgrade;
899
893
  proxy_set_header Connection 'upgrade';
@@ -907,31 +901,40 @@ server {
907
901
  proxy_connect_timeout 60s;
908
902
  proxy_send_timeout 60s;
909
903
  proxy_read_timeout 60s;
904
+ }
905
+ }`;
910
906
 
911
- # WebSocket support
912
- proxy_set_header X-Forwarded-Host $host;
913
- proxy_set_header X-Forwarded-Server $host;
907
+ // Helper function to generate appropriate block based on cert availability
908
+ const generateServerBlock = (host, port, appName, extraConfig = '') => {
909
+ if (hasCert(host)) {
910
+ return generateSslServerBlock(host, port, appName, extraConfig);
914
911
  }
915
- }
912
+ return generateHttpServerBlock(host, port, appName, extraConfig);
913
+ };
916
914
 
917
- # HTTP to HTTPS redirect for ${apiHost}
918
- server {
919
- listen 80;
920
- listen [::]:80;
921
- server_name ${apiHost};
922
- return 301 https://$server_name$request_uri;
923
- }
924
- ${gateAppHost ? `
925
- # ANTE Gate App Configuration (HTTPS)
915
+ // Generate config blocks
916
+ let config_output = '';
917
+
918
+ // Frontend (required)
919
+ config_output += generateServerBlock(frontendHost, frontendPort, 'ANTE Frontend');
920
+
921
+ // API (required) - with WebSocket support
922
+ const apiExtraConfig = `
923
+ # WebSocket support
924
+ proxy_set_header X-Forwarded-Host $host;
925
+ proxy_set_header X-Forwarded-Server $host;`;
926
+ if (hasCert(apiHost)) {
927
+ config_output += `
928
+ # ANTE API Configuration (HTTPS)
926
929
  server {
927
930
  listen 443 ssl;
928
931
  listen [::]:443 ssl;
929
932
  http2 on;
930
- server_name ${gateAppHost};
933
+ server_name ${apiHost};
931
934
 
932
935
  # SSL Certificate paths
933
- ssl_certificate /etc/letsencrypt/live/${gateAppHost}/fullchain.pem;
934
- ssl_certificate_key /etc/letsencrypt/live/${gateAppHost}/privkey.pem;
936
+ ssl_certificate /etc/letsencrypt/live/${apiHost}/fullchain.pem;
937
+ ssl_certificate_key /etc/letsencrypt/live/${apiHost}/privkey.pem;
935
938
 
936
939
  # SSL Configuration
937
940
  ssl_protocols TLSv1.2 TLSv1.3;
@@ -954,7 +957,7 @@ server {
954
957
  client_max_body_size 100M;
955
958
 
956
959
  location / {
957
- proxy_pass http://localhost:${gateAppPort};
960
+ proxy_pass http://localhost:${apiPort};
958
961
  proxy_http_version 1.1;
959
962
  proxy_set_header Upgrade $http_upgrade;
960
963
  proxy_set_header Connection 'upgrade';
@@ -968,40 +971,27 @@ server {
968
971
  proxy_connect_timeout 60s;
969
972
  proxy_send_timeout 60s;
970
973
  proxy_read_timeout 60s;
974
+
975
+ # WebSocket support
976
+ proxy_set_header X-Forwarded-Host $host;
977
+ proxy_set_header X-Forwarded-Server $host;
971
978
  }
972
979
  }
973
980
 
974
- # HTTP to HTTPS redirect for ${gateAppHost}
981
+ # HTTP to HTTPS redirect for ${apiHost}
975
982
  server {
976
983
  listen 80;
977
984
  listen [::]:80;
978
- server_name ${gateAppHost};
985
+ server_name ${apiHost};
979
986
  return 301 https://$server_name$request_uri;
980
- }
981
- ` : ''}${guardianAppHost ? `
982
- # ANTE Guardian App Configuration (HTTPS)
987
+ }`;
988
+ } else {
989
+ config_output += `
990
+ # ANTE API Configuration (HTTP only - SSL cert not available)
983
991
  server {
984
- listen 443 ssl;
985
- listen [::]:443 ssl;
986
- http2 on;
987
- server_name ${guardianAppHost};
988
-
989
- # SSL Certificate paths
990
- ssl_certificate /etc/letsencrypt/live/${guardianAppHost}/fullchain.pem;
991
- ssl_certificate_key /etc/letsencrypt/live/${guardianAppHost}/privkey.pem;
992
-
993
- # SSL Configuration
994
- ssl_protocols TLSv1.2 TLSv1.3;
995
- ssl_ciphers HIGH:!aNULL:!MD5;
996
- ssl_prefer_server_ciphers on;
997
- ssl_session_cache shared:SSL:10m;
998
- ssl_session_timeout 10m;
999
-
1000
- # Security headers
1001
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
1002
- add_header X-Frame-Options SAMEORIGIN always;
1003
- add_header X-Content-Type-Options nosniff always;
1004
- add_header X-XSS-Protection "1; mode=block" always;
992
+ listen 80;
993
+ listen [::]:80;
994
+ server_name ${apiHost};
1005
995
 
1006
996
  # Increase buffer sizes for large headers
1007
997
  client_header_buffer_size 16k;
@@ -1011,7 +1001,7 @@ server {
1011
1001
  client_max_body_size 100M;
1012
1002
 
1013
1003
  location / {
1014
- proxy_pass http://localhost:${guardianAppPort};
1004
+ proxy_pass http://localhost:${apiPort};
1015
1005
  proxy_http_version 1.1;
1016
1006
  proxy_set_header Upgrade $http_upgrade;
1017
1007
  proxy_set_header Connection 'upgrade';
@@ -1025,17 +1015,38 @@ server {
1025
1015
  proxy_connect_timeout 60s;
1026
1016
  proxy_send_timeout 60s;
1027
1017
  proxy_read_timeout 60s;
1018
+
1019
+ # WebSocket support
1020
+ proxy_set_header X-Forwarded-Host $host;
1021
+ proxy_set_header X-Forwarded-Server $host;
1028
1022
  }
1029
- }
1023
+ }`;
1024
+ }
1030
1025
 
1031
- # HTTP to HTTPS redirect for ${guardianAppHost}
1032
- server {
1033
- listen 80;
1034
- listen [::]:80;
1035
- server_name ${guardianAppHost};
1036
- return 301 https://$server_name$request_uri;
1037
- }
1038
- ` : ''}${facialAppHost ? `
1026
+ // Optional apps
1027
+ if (gateAppHost) {
1028
+ config_output += generateServerBlock(gateAppHost, gateAppPort, 'ANTE Gate App');
1029
+ }
1030
+
1031
+ if (guardianAppHost) {
1032
+ config_output += generateServerBlock(guardianAppHost, guardianAppPort, 'ANTE Guardian App');
1033
+ }
1034
+
1035
+ if (facialAppHost) {
1036
+ // Facial app has special config.js caching rules
1037
+ const facialExtraConfig = `
1038
+ # Disable caching for config.js to ensure runtime config is always fresh
1039
+ location = /config.js {
1040
+ proxy_pass http://localhost:${facialWebPort}/config.js;
1041
+ proxy_http_version 1.1;
1042
+ proxy_set_header Host $host;
1043
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
1044
+ add_header Pragma "no-cache" always;
1045
+ add_header Expires "0" always;
1046
+ }
1047
+ `;
1048
+ if (hasCert(facialAppHost)) {
1049
+ config_output += `
1039
1050
  # ANTE Facial Web App Configuration (HTTPS)
1040
1051
  server {
1041
1052
  listen 443 ssl;
@@ -1101,98 +1112,34 @@ server {
1101
1112
  listen [::]:80;
1102
1113
  server_name ${facialAppHost};
1103
1114
  return 301 https://$server_name$request_uri;
1104
- }
1105
- ` : ''}${posAppHost ? `
1106
- # ANTE POS App Configuration (HTTPS)
1115
+ }`;
1116
+ } else {
1117
+ config_output += `
1118
+ # ANTE Facial Web App Configuration (HTTP only - SSL cert not available)
1107
1119
  server {
1108
- listen 443 ssl;
1109
- listen [::]:443 ssl;
1110
- http2 on;
1111
- server_name ${posAppHost};
1112
-
1113
- # SSL Certificate paths
1114
- ssl_certificate /etc/letsencrypt/live/${posAppHost}/fullchain.pem;
1115
- ssl_certificate_key /etc/letsencrypt/live/${posAppHost}/privkey.pem;
1116
-
1117
- # SSL Configuration
1118
- ssl_protocols TLSv1.2 TLSv1.3;
1119
- ssl_ciphers HIGH:!aNULL:!MD5;
1120
- ssl_prefer_server_ciphers on;
1121
- ssl_session_cache shared:SSL:10m;
1122
- ssl_session_timeout 10m;
1123
-
1124
- # Security headers
1125
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
1126
- add_header X-Frame-Options SAMEORIGIN always;
1127
- add_header X-Content-Type-Options nosniff always;
1128
- add_header X-XSS-Protection "1; mode=block" always;
1120
+ listen 80;
1121
+ listen [::]:80;
1122
+ server_name ${facialAppHost};
1129
1123
 
1130
1124
  # Increase buffer sizes for large headers
1131
1125
  client_header_buffer_size 16k;
1132
1126
  large_client_header_buffers 4 16k;
1133
1127
 
1134
- # Increase body size for file uploads
1135
- client_max_body_size 100M;
1128
+ # Increase body size for image uploads
1129
+ client_max_body_size 50M;
1136
1130
 
1137
- location / {
1138
- proxy_pass http://localhost:${posAppPort};
1131
+ # Disable caching for config.js to ensure runtime config is always fresh
1132
+ location = /config.js {
1133
+ proxy_pass http://localhost:${facialWebPort}/config.js;
1139
1134
  proxy_http_version 1.1;
1140
- proxy_set_header Upgrade $http_upgrade;
1141
- proxy_set_header Connection 'upgrade';
1142
1135
  proxy_set_header Host $host;
1143
- proxy_set_header X-Real-IP $remote_addr;
1144
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1145
- proxy_set_header X-Forwarded-Proto $scheme;
1146
- proxy_cache_bypass $http_upgrade;
1147
-
1148
- # Timeout settings
1149
- proxy_connect_timeout 60s;
1150
- proxy_send_timeout 60s;
1151
- proxy_read_timeout 60s;
1136
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
1137
+ add_header Pragma "no-cache" always;
1138
+ add_header Expires "0" always;
1152
1139
  }
1153
- }
1154
-
1155
- # HTTP to HTTPS redirect for ${posAppHost}
1156
- server {
1157
- listen 80;
1158
- listen [::]:80;
1159
- server_name ${posAppHost};
1160
- return 301 https://$server_name$request_uri;
1161
- }
1162
- ` : ''}${mlmAppHost ? `
1163
- # ANTE MLM App Configuration (HTTPS)
1164
- server {
1165
- listen 443 ssl;
1166
- listen [::]:443 ssl;
1167
- http2 on;
1168
- server_name ${mlmAppHost};
1169
-
1170
- # SSL Certificate paths
1171
- ssl_certificate /etc/letsencrypt/live/${mlmAppHost}/fullchain.pem;
1172
- ssl_certificate_key /etc/letsencrypt/live/${mlmAppHost}/privkey.pem;
1173
-
1174
- # SSL Configuration
1175
- ssl_protocols TLSv1.2 TLSv1.3;
1176
- ssl_ciphers HIGH:!aNULL:!MD5;
1177
- ssl_prefer_server_ciphers on;
1178
- ssl_session_cache shared:SSL:10m;
1179
- ssl_session_timeout 10m;
1180
-
1181
- # Security headers
1182
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
1183
- add_header X-Frame-Options SAMEORIGIN always;
1184
- add_header X-Content-Type-Options nosniff always;
1185
- add_header X-XSS-Protection "1; mode=block" always;
1186
-
1187
- # Increase buffer sizes for large headers
1188
- client_header_buffer_size 16k;
1189
- large_client_header_buffers 4 16k;
1190
-
1191
- # Increase body size for file uploads
1192
- client_max_body_size 100M;
1193
1140
 
1194
1141
  location / {
1195
- proxy_pass http://localhost:${mlmAppPort};
1142
+ proxy_pass http://localhost:${facialWebPort};
1196
1143
  proxy_http_version 1.1;
1197
1144
  proxy_set_header Upgrade $http_upgrade;
1198
1145
  proxy_set_header Connection 'upgrade';
@@ -1207,74 +1154,40 @@ server {
1207
1154
  proxy_send_timeout 60s;
1208
1155
  proxy_read_timeout 60s;
1209
1156
  }
1210
- }
1211
-
1212
- # HTTP to HTTPS redirect for ${mlmAppHost}
1213
- server {
1214
- listen 80;
1215
- listen [::]:80;
1216
- server_name ${mlmAppHost};
1217
- return 301 https://$server_name$request_uri;
1218
- }
1219
- ` : ''}${backendMlmHost ? `
1220
- # ANTE Backend MLM API Configuration (HTTPS)
1221
- server {
1222
- listen 443 ssl;
1223
- listen [::]:443 ssl;
1224
- http2 on;
1225
- server_name ${backendMlmHost};
1157
+ }`;
1158
+ }
1159
+ }
1226
1160
 
1227
- # SSL Certificate paths
1228
- ssl_certificate /etc/letsencrypt/live/${backendMlmHost}/fullchain.pem;
1229
- ssl_certificate_key /etc/letsencrypt/live/${backendMlmHost}/privkey.pem;
1161
+ if (posAppHost) {
1162
+ config_output += generateServerBlock(posAppHost, posAppPort, 'ANTE POS App');
1163
+ }
1230
1164
 
1231
- # SSL Configuration
1232
- ssl_protocols TLSv1.2 TLSv1.3;
1233
- ssl_ciphers HIGH:!aNULL:!MD5;
1234
- ssl_prefer_server_ciphers on;
1235
- ssl_session_cache shared:SSL:10m;
1236
- ssl_session_timeout 10m;
1165
+ if (mlmAppHost) {
1166
+ config_output += generateServerBlock(mlmAppHost, mlmAppPort, 'ANTE MLM App');
1167
+ }
1237
1168
 
1238
- # Security headers
1239
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
1240
- add_header X-Frame-Options SAMEORIGIN always;
1241
- add_header X-Content-Type-Options nosniff always;
1242
- add_header X-XSS-Protection "1; mode=block" always;
1169
+ if (backendMlmHost) {
1170
+ config_output += generateServerBlock(backendMlmHost, backendMlmPort, 'ANTE Backend MLM API');
1171
+ }
1243
1172
 
1244
- # Increase buffer sizes for large headers
1245
- client_header_buffer_size 16k;
1246
- large_client_header_buffers 4 16k;
1173
+ return config_output.trim();
1174
+ }
1247
1175
 
1248
- # Increase body size for file uploads
1249
- client_max_body_size 100M;
1176
+ /**
1177
+ * Configure NGINX for ANTE domains - KEPT FOR REFERENCE BUT NOT USED
1178
+ * The generateSslNginxConfig function now handles both SSL and non-SSL domains
1179
+ * based on the domainsWithCerts parameter.
1180
+ */
1250
1181
 
1251
- location / {
1252
- proxy_pass http://localhost:${backendMlmPort};
1253
- proxy_http_version 1.1;
1254
- proxy_set_header Upgrade $http_upgrade;
1255
- proxy_set_header Connection 'upgrade';
1256
- proxy_set_header Host $host;
1257
- proxy_set_header X-Real-IP $remote_addr;
1258
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1259
- proxy_set_header X-Forwarded-Proto $scheme;
1260
- proxy_cache_bypass $http_upgrade;
1182
+ // NOTE: Old generateSslNginxConfigLegacy function removed - functionality merged into generateSslNginxConfig
1261
1183
 
1262
- # Timeout settings
1263
- proxy_connect_timeout 60s;
1264
- proxy_send_timeout 60s;
1265
- proxy_read_timeout 60s;
1266
- }
1267
- }
1184
+ /**
1185
+ * Generate NGINX configuration for Cloudflare SSL - PLACEHOLDER FOR OLD FUNCTION LOCATION
1186
+ * This marks the end of the SSL config generation section
1187
+ */
1268
1188
 
1269
- # HTTP to HTTPS redirect for ${backendMlmHost}
1270
- server {
1271
- listen 80;
1272
- listen [::]:80;
1273
- server_name ${backendMlmHost};
1274
- return 301 https://$server_name$request_uri;
1275
- }
1276
- ` : ''}`;
1277
- }
1189
+ // Previous generateSslNginxConfigLegacy function has been removed.
1190
+ // The new generateSslNginxConfig function handles partial SSL scenarios.
1278
1191
 
1279
1192
  /**
1280
1193
  * Configure NGINX for ANTE domains
package/src/utils/ssl.js CHANGED
@@ -60,10 +60,11 @@ export async function checkDomainDNS(domain) {
60
60
  * @param {string} config.domain - Domain name (e.g., ante.example.com)
61
61
  * @param {string} config.email - Email for certificate notifications
62
62
  * @param {boolean} [config.staging=false] - Use staging environment for testing
63
- * @returns {Promise<void>}
63
+ * @param {boolean} [config.graceful=false] - If true, returns result object instead of throwing
64
+ * @returns {Promise<boolean|Object>} Returns true on success, or result object if graceful mode
64
65
  */
65
66
  export async function obtainSSLCertificate(config) {
66
- const { domain, email, staging = false } = config;
67
+ const { domain, email, staging = false, graceful = false } = config;
67
68
  const spinner = ora(`Obtaining SSL certificate for ${domain}...`).start();
68
69
 
69
70
  try {
@@ -109,12 +110,22 @@ export async function obtainSSLCertificate(config) {
109
110
  spinner.succeed(`SSL certificate obtained for ${domain}`);
110
111
  } catch (error) {
111
112
  spinner.fail(`Failed to obtain SSL certificate for ${domain}`);
112
- throw new Error(`Certbot failed: ${error.message}`);
113
+ const errorMessage = `Certbot failed: ${error.message}`;
114
+ if (graceful) {
115
+ return { success: false, domain, error: errorMessage };
116
+ }
117
+ throw new Error(errorMessage);
113
118
  }
114
119
 
120
+ if (graceful) {
121
+ return { success: true, domain };
122
+ }
115
123
  return true;
116
124
  } catch (error) {
117
125
  spinner.fail('Failed to obtain SSL certificate');
126
+ if (graceful) {
127
+ return { success: false, domain, error: error.message };
128
+ }
118
129
  throw error;
119
130
  }
120
131
  }
@@ -138,6 +149,7 @@ export async function obtainSSLCertificate(config) {
138
149
  * @param {number} [config.posAppPort=8084] - POS App port
139
150
  * @param {number} [config.mlmAppPort=9005] - MLM App port
140
151
  * @param {number} [config.backendMlmPort=4001] - Backend MLM API port
152
+ * @param {string[]} [config.domainsWithCerts=[]] - List of domains that have valid SSL certificates
141
153
  * @returns {Promise<void>}
142
154
  */
143
155
  export async function updateNginxForSSL(config) {
@@ -157,7 +169,8 @@ export async function updateNginxForSSL(config) {
157
169
  facialWebPort = 8083,
158
170
  posAppPort = 8084,
159
171
  mlmAppPort = 9005,
160
- backendMlmPort = 4001
172
+ backendMlmPort = 4001,
173
+ domainsWithCerts = []
161
174
  } = config;
162
175
  const spinner = ora('Updating NGINX configuration for HTTPS...').start();
163
176
 
@@ -194,7 +207,8 @@ export async function updateNginxForSSL(config) {
194
207
  posAppPort,
195
208
  mlmAppPort,
196
209
  backendMlmPort,
197
- ssl: true
210
+ ssl: true,
211
+ domainsWithCerts
198
212
  });
199
213
 
200
214
  // Write new configuration