ante-erp-cli 1.7.1 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ante-erp-cli",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "Comprehensive CLI tool for managing ANTE ERP self-hosted installations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -196,28 +196,19 @@ export async function sslEnable(options) {
196
196
 
197
197
  console.log(chalk.bold('\nšŸš€ Starting SSL Configuration...\n'));
198
198
 
199
- // Obtain and configure SSL for each domain
200
- for (const domainConfig of domains) {
201
- console.log(chalk.cyan(`\nšŸ“‹ Configuring SSL for ${domainConfig.domain}...\n`));
199
+ // Step 1: Obtain SSL certificates for all domains
200
+ console.log(chalk.cyan('šŸ“‹ Obtaining SSL certificates...\n'));
202
201
 
202
+ for (const domainConfig of domains) {
203
203
  try {
204
- // Step 1: Obtain SSL certificate
204
+ console.log(chalk.gray(` Obtaining certificate for ${domainConfig.domain}...`));
205
205
  await obtainSSLCertificate({
206
206
  domain: domainConfig.domain,
207
207
  email: options.email,
208
208
  staging: options.staging || false
209
209
  });
210
-
211
- // Step 2: Update NGINX configuration
212
- await updateNginxForSSL({
213
- domain: domainConfig.domain,
214
- port: domainConfig.port
215
- });
216
-
217
- console.log(chalk.green(`āœ“ SSL configured for ${domainConfig.domain}\n`));
218
-
219
210
  } catch (error) {
220
- console.log(chalk.red(`āœ— Failed to configure SSL for ${domainConfig.domain}:`), error.message);
211
+ console.log(chalk.red(`\nāœ— Failed to obtain SSL certificate for ${domainConfig.domain}:`), error.message);
221
212
  console.log(chalk.yellow('\n⚠ Troubleshooting:'));
222
213
  console.log(chalk.gray(' 1. Verify DNS points to this server'));
223
214
  console.log(chalk.gray(' 2. Ensure ports 80 and 443 are open'));
@@ -227,6 +218,31 @@ export async function sslEnable(options) {
227
218
  }
228
219
  }
229
220
 
221
+ console.log(chalk.green('\nāœ“ All SSL certificates obtained\n'));
222
+
223
+ // Step 2: Update NGINX configuration with SSL
224
+ console.log(chalk.cyan('šŸ”§ Configuring NGINX for HTTPS...\n'));
225
+
226
+ try {
227
+ // Extract frontend and API ports
228
+ const frontendPort = frontendUrl.match(/:(\d+)/) ? parseInt(frontendUrl.match(/:(\d+)/)[1]) : 8080;
229
+ const apiPort = apiUrl.match(/:(\d+)/) ? parseInt(apiUrl.match(/:(\d+)/)[1]) : 3001;
230
+
231
+ await updateNginxForSSL({
232
+ frontendDomain: frontendUrl,
233
+ apiDomain: apiUrl,
234
+ frontendPort,
235
+ apiPort
236
+ });
237
+ } catch (error) {
238
+ console.log(chalk.red('\nāœ— Failed to configure NGINX:'), error.message);
239
+ console.log(chalk.yellow('\n⚠ Troubleshooting:'));
240
+ console.log(chalk.gray(' 1. Check NGINX configuration: nginx -t'));
241
+ console.log(chalk.gray(' 2. View NGINX logs: journalctl -u nginx'));
242
+ console.log(chalk.gray(' 3. Restore backup: cp /etc/nginx/sites-enabled/ante.backup /etc/nginx/sites-enabled/ante\n'));
243
+ process.exit(1);
244
+ }
245
+
230
246
  // Setup automatic renewal
231
247
  console.log(chalk.cyan('\nšŸ”„ Setting up automatic certificate renewal...\n'));
232
248
  await setupAutoRenewal();
@@ -50,15 +50,20 @@ export async function installNginx(spinner) {
50
50
  * @param {string} config.apiDomain - API domain (e.g., api.ante.example.com)
51
51
  * @param {number} config.frontendPort - Frontend Docker port (default: 8080)
52
52
  * @param {number} config.apiPort - API Docker port (default: 3001)
53
+ * @param {boolean} config.ssl - Enable SSL configuration (default: false)
53
54
  * @returns {string} NGINX configuration content
54
55
  */
55
56
  export function generateNginxConfig(config) {
56
- const { frontendDomain, apiDomain, frontendPort = 8080, apiPort = 3001 } = config;
57
+ const { frontendDomain, apiDomain, frontendPort = 8080, apiPort = 3001, ssl = false } = config;
57
58
 
58
59
  // Extract domain without protocol
59
60
  const frontendHost = frontendDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
60
61
  const apiHost = apiDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
61
62
 
63
+ if (ssl) {
64
+ return generateSslNginxConfig({ frontendHost, apiHost, frontendPort, apiPort });
65
+ }
66
+
62
67
  return `# ANTE Frontend Configuration
63
68
  server {
64
69
  listen 80;
@@ -127,6 +132,138 @@ server {
127
132
  `;
128
133
  }
129
134
 
135
+ /**
136
+ * Generate NGINX configuration with SSL support
137
+ * @param {Object} config - Configuration object
138
+ * @param {string} config.frontendHost - Frontend hostname
139
+ * @param {string} config.apiHost - API hostname
140
+ * @param {number} config.frontendPort - Frontend port
141
+ * @param {number} config.apiPort - API port
142
+ * @returns {string} NGINX configuration with SSL
143
+ */
144
+ function generateSslNginxConfig(config) {
145
+ const { frontendHost, apiHost, frontendPort, apiPort } = config;
146
+
147
+ return `# ANTE Frontend Configuration (HTTPS)
148
+ server {
149
+ listen 443 ssl;
150
+ listen [::]:443 ssl;
151
+ http2 on;
152
+ server_name ${frontendHost};
153
+
154
+ # SSL Certificate paths
155
+ ssl_certificate /etc/letsencrypt/live/${frontendHost}/fullchain.pem;
156
+ ssl_certificate_key /etc/letsencrypt/live/${frontendHost}/privkey.pem;
157
+
158
+ # SSL Configuration
159
+ ssl_protocols TLSv1.2 TLSv1.3;
160
+ ssl_ciphers HIGH:!aNULL:!MD5;
161
+ ssl_prefer_server_ciphers on;
162
+ ssl_session_cache shared:SSL:10m;
163
+ ssl_session_timeout 10m;
164
+
165
+ # Security headers
166
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
167
+ add_header X-Frame-Options SAMEORIGIN always;
168
+ add_header X-Content-Type-Options nosniff always;
169
+ add_header X-XSS-Protection "1; mode=block" always;
170
+
171
+ # Increase buffer sizes for large headers
172
+ client_header_buffer_size 16k;
173
+ large_client_header_buffers 4 16k;
174
+
175
+ # Increase body size for file uploads
176
+ client_max_body_size 100M;
177
+
178
+ location / {
179
+ proxy_pass http://localhost:${frontendPort};
180
+ proxy_http_version 1.1;
181
+ proxy_set_header Upgrade $http_upgrade;
182
+ proxy_set_header Connection 'upgrade';
183
+ proxy_set_header Host $host;
184
+ proxy_set_header X-Real-IP $remote_addr;
185
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
186
+ proxy_set_header X-Forwarded-Proto $scheme;
187
+ proxy_cache_bypass $http_upgrade;
188
+
189
+ # Timeout settings
190
+ proxy_connect_timeout 60s;
191
+ proxy_send_timeout 60s;
192
+ proxy_read_timeout 60s;
193
+ }
194
+ }
195
+
196
+ # HTTP to HTTPS redirect for ${frontendHost}
197
+ server {
198
+ listen 80;
199
+ listen [::]:80;
200
+ server_name ${frontendHost};
201
+ return 301 https://$server_name$request_uri;
202
+ }
203
+
204
+ # ANTE API Configuration (HTTPS)
205
+ server {
206
+ listen 443 ssl;
207
+ listen [::]:443 ssl;
208
+ http2 on;
209
+ server_name ${apiHost};
210
+
211
+ # SSL Certificate paths
212
+ ssl_certificate /etc/letsencrypt/live/${apiHost}/fullchain.pem;
213
+ ssl_certificate_key /etc/letsencrypt/live/${apiHost}/privkey.pem;
214
+
215
+ # SSL Configuration
216
+ ssl_protocols TLSv1.2 TLSv1.3;
217
+ ssl_ciphers HIGH:!aNULL:!MD5;
218
+ ssl_prefer_server_ciphers on;
219
+ ssl_session_cache shared:SSL:10m;
220
+ ssl_session_timeout 10m;
221
+
222
+ # Security headers
223
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
224
+ add_header X-Frame-Options SAMEORIGIN always;
225
+ add_header X-Content-Type-Options nosniff always;
226
+ add_header X-XSS-Protection "1; mode=block" always;
227
+
228
+ # Increase buffer sizes for large headers
229
+ client_header_buffer_size 16k;
230
+ large_client_header_buffers 4 16k;
231
+
232
+ # Increase body size for file uploads
233
+ client_max_body_size 100M;
234
+
235
+ location / {
236
+ proxy_pass http://localhost:${apiPort};
237
+ proxy_http_version 1.1;
238
+ proxy_set_header Upgrade $http_upgrade;
239
+ proxy_set_header Connection 'upgrade';
240
+ proxy_set_header Host $host;
241
+ proxy_set_header X-Real-IP $remote_addr;
242
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
243
+ proxy_set_header X-Forwarded-Proto $scheme;
244
+ proxy_cache_bypass $http_upgrade;
245
+
246
+ # Timeout settings
247
+ proxy_connect_timeout 60s;
248
+ proxy_send_timeout 60s;
249
+ proxy_read_timeout 60s;
250
+
251
+ # WebSocket support
252
+ proxy_set_header X-Forwarded-Host $host;
253
+ proxy_set_header X-Forwarded-Server $host;
254
+ }
255
+ }
256
+
257
+ # HTTP to HTTPS redirect for ${apiHost}
258
+ server {
259
+ listen 80;
260
+ listen [::]:80;
261
+ server_name ${apiHost};
262
+ return 301 https://$server_name$request_uri;
263
+ }
264
+ `;
265
+ }
266
+
130
267
  /**
131
268
  * Configure NGINX for ANTE domains
132
269
  * @param {Object} config - Configuration object
package/src/utils/ssl.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { execa } from 'execa';
4
- import { readFileSync, writeFileSync, existsSync } from 'fs';
4
+ import { writeFileSync, existsSync, copyFileSync } from 'fs';
5
+ import { generateNginxConfig } from './nginx.js';
5
6
 
6
7
  /**
7
8
  * Check if certbot is installed
@@ -121,16 +122,19 @@ export async function obtainSSLCertificate(config) {
121
122
  /**
122
123
  * Update NGINX configuration to use SSL
123
124
  * @param {Object} config - Configuration object
124
- * @param {string} config.domain - Domain name
125
- * @param {number} config.port - Backend port to proxy to
125
+ * @param {string} config.frontendDomain - Frontend domain URL
126
+ * @param {string} config.apiDomain - API domain URL
127
+ * @param {number} [config.frontendPort=8080] - Frontend port
128
+ * @param {number} [config.apiPort=3001] - API port
126
129
  * @returns {Promise<void>}
127
130
  */
128
131
  export async function updateNginxForSSL(config) {
129
- const { domain, port } = config;
130
- const spinner = ora(`Updating NGINX configuration for ${domain}...`).start();
132
+ const { frontendDomain, apiDomain, frontendPort = 8080, apiPort = 3001 } = config;
133
+ const spinner = ora('Updating NGINX configuration for HTTPS...').start();
131
134
 
132
135
  try {
133
136
  const configPath = '/etc/nginx/sites-enabled/ante';
137
+ const backupPath = `${configPath}.backup`;
134
138
 
135
139
  // Check if config exists
136
140
  if (!existsSync(configPath)) {
@@ -138,112 +142,68 @@ export async function updateNginxForSSL(config) {
138
142
  throw new Error('Please run "ante set-domain" first to configure NGINX');
139
143
  }
140
144
 
141
- // Read existing config
142
- let nginxConfig = readFileSync(configPath, 'utf8');
145
+ // Backup existing configuration
146
+ spinner.text = 'Backing up current NGINX configuration...';
147
+ copyFileSync(configPath, backupPath);
143
148
 
144
- // Find the server block for this domain
145
- const serverBlockRegex = new RegExp(
146
- `server\\s*{[^}]*server_name\\s+${domain.replace('.', '\\.')}[^}]*}`,
147
- 's'
148
- );
149
+ // Generate new SSL-enabled configuration
150
+ spinner.text = 'Generating SSL configuration...';
151
+ const sslConfig = generateNginxConfig({
152
+ frontendDomain,
153
+ apiDomain,
154
+ frontendPort,
155
+ apiPort,
156
+ ssl: true
157
+ });
149
158
 
150
- const match = nginxConfig.match(serverBlockRegex);
151
- if (!match) {
152
- spinner.fail(`No NGINX configuration found for ${domain}`);
153
- throw new Error(`Domain ${domain} not found in NGINX configuration`);
154
- }
155
-
156
- const oldServerBlock = match[0];
157
-
158
- // Create HTTPS server block
159
- const httpsServerBlock = `# HTTPS Configuration for ${domain}
160
- server {
161
- listen 443 ssl http2;
162
- listen [::]:443 ssl http2;
163
- server_name ${domain};
164
-
165
- # SSL Certificate paths
166
- ssl_certificate /etc/letsencrypt/live/${domain}/fullchain.pem;
167
- ssl_certificate_key /etc/letsencrypt/live/${domain}/privkey.pem;
168
-
169
- # SSL Configuration
170
- ssl_protocols TLSv1.2 TLSv1.3;
171
- ssl_ciphers HIGH:!aNULL:!MD5;
172
- ssl_prefer_server_ciphers on;
173
- ssl_session_cache shared:SSL:10m;
174
- ssl_session_timeout 10m;
175
-
176
- # Security headers
177
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
178
- add_header X-Frame-Options SAMEORIGIN always;
179
- add_header X-Content-Type-Options nosniff always;
180
- add_header X-XSS-Protection "1; mode=block" always;
181
-
182
- # Increase buffer sizes for large headers
183
- client_header_buffer_size 16k;
184
- large_client_header_buffers 4 16k;
185
-
186
- # Increase body size for file uploads
187
- client_max_body_size 100M;
188
-
189
- location / {
190
- proxy_pass http://localhost:${port};
191
- proxy_http_version 1.1;
192
- proxy_set_header Upgrade $http_upgrade;
193
- proxy_set_header Connection 'upgrade';
194
- proxy_set_header Host $host;
195
- proxy_set_header X-Real-IP $remote_addr;
196
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
197
- proxy_set_header X-Forwarded-Proto $scheme;
198
- proxy_cache_bypass $http_upgrade;
199
-
200
- # Timeout settings
201
- proxy_connect_timeout 60s;
202
- proxy_send_timeout 60s;
203
- proxy_read_timeout 60s;
204
-
205
- # WebSocket support
206
- proxy_set_header X-Forwarded-Host $host;
207
- proxy_set_header X-Forwarded-Server $host;
208
- }
209
- }
210
-
211
- # HTTP to HTTPS redirect for ${domain}
212
- server {
213
- listen 80;
214
- listen [::]:80;
215
- server_name ${domain};
216
-
217
- # Redirect all HTTP requests to HTTPS
218
- return 301 https://$server_name$request_uri;
219
- }`;
220
-
221
- // Replace the old server block with HTTPS configuration
222
- nginxConfig = nginxConfig.replace(oldServerBlock, httpsServerBlock);
223
-
224
- // Write updated configuration
225
- writeFileSync(configPath, nginxConfig);
226
-
227
- spinner.text = 'Testing NGINX configuration...';
159
+ // Write new configuration
160
+ writeFileSync(configPath, sslConfig);
228
161
 
229
162
  // Test NGINX configuration
163
+ spinner.text = 'Testing NGINX configuration...';
230
164
  try {
231
- await execa('nginx', ['-t'], { stdio: 'pipe' });
165
+ const { stderr } = await execa('nginx', ['-t'], { stdio: 'pipe' });
166
+
167
+ // Check for warnings
168
+ if (stderr && !stderr.includes('syntax is ok')) {
169
+ console.log(chalk.yellow('\n⚠ NGINX warnings:'));
170
+ console.log(chalk.gray(stderr));
171
+ }
232
172
  } catch (error) {
233
173
  spinner.fail('NGINX configuration test failed');
234
- throw new Error(`Invalid NGINX configuration: ${error.message}`);
235
- }
236
174
 
237
- spinner.text = 'Reloading NGINX...';
175
+ // Restore backup
176
+ console.log(chalk.yellow('\n⚠ Restoring previous configuration...'));
177
+ copyFileSync(backupPath, configPath);
178
+
179
+ throw new Error(`Invalid NGINX configuration: ${error.stderr || error.message}`);
180
+ }
238
181
 
239
182
  // Reload NGINX
183
+ spinner.text = 'Reloading NGINX...';
240
184
  try {
241
185
  await execa('systemctl', ['reload', 'nginx'], { stdio: 'pipe' });
242
186
  } catch {
187
+ // If reload fails, try restart
243
188
  await execa('systemctl', ['restart', 'nginx'], { stdio: 'pipe' });
244
189
  }
245
190
 
246
- spinner.succeed(`NGINX configured for HTTPS on ${domain}`);
191
+ // Verify NGINX is running
192
+ const { stdout } = await execa('systemctl', ['is-active', 'nginx'], { stdio: 'pipe' });
193
+ if (stdout.trim() !== 'active') {
194
+ throw new Error('NGINX failed to start after configuration update');
195
+ }
196
+
197
+ spinner.succeed('NGINX configured for HTTPS');
198
+
199
+ // Show configuration info
200
+ const frontendHost = frontendDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
201
+ const apiHost = apiDomain.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
202
+ console.log(chalk.gray('\nšŸ“ NGINX Configuration:'));
203
+ console.log(chalk.gray(` Frontend: https://${frontendHost} → localhost:${frontendPort}`));
204
+ console.log(chalk.gray(` API: https://${apiHost} → localhost:${apiPort}`));
205
+ console.log(chalk.gray(` HTTP redirects to HTTPS: Enabled\n`));
206
+
247
207
  } catch (error) {
248
208
  spinner.fail('Failed to update NGINX configuration');
249
209
  throw error;