ante-erp-cli 1.11.49 → 1.11.50

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.49",
3
+ "version": "1.11.50",
4
4
  "description": "Comprehensive CLI tool for managing ANTE ERP self-hosted installations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,7 @@ import { generateDockerCompose } from '../templates/docker-compose.yml.js';
15
15
  import { generateEnv } from '../templates/env.js';
16
16
  import { detectPublicIPWithFeedback, buildURL } from '../utils/network.js';
17
17
  import { configureNginx, requiresNginx } from '../utils/nginx.js';
18
+ import { applySysctlSettings } from '../utils/system-config.js';
18
19
 
19
20
  const __filename = fileURLToPath(import.meta.url);
20
21
  const __dirname = dirname(__filename);
@@ -302,6 +303,10 @@ export async function install(options) {
302
303
  }
303
304
 
304
305
  console.log(chalk.green(`✓ ${formatStepTitle(stepSystemCheck, totalSteps, 'System requirements met')}\n`));
306
+
307
+ // Configure system kernel parameters for Redis
308
+ console.log(chalk.cyan(' Configuring system kernel parameters...'));
309
+ await applySysctlSettings();
305
310
  }
306
311
 
307
312
  // Step: Network detection
@@ -9,6 +9,7 @@ import { backup } from './backup.js';
9
9
  import { getCurrentVersion, getLatestVersion } from './update-cli.js';
10
10
  import { generateDockerCompose } from '../templates/docker-compose.yml.js';
11
11
  import { generateSecurePassword } from '../utils/password.js';
12
+ import { verifySysctlSettings, applySysctlSettings } from '../utils/system-config.js';
12
13
 
13
14
  /**
14
15
  * Check if gate-app, guardian-app, facial-web, or pos-app is installed by checking docker-compose.yml
@@ -461,6 +462,16 @@ export async function update(options) {
461
462
  refreshDockerCompose(composeFile, envFile);
462
463
  }
463
464
 
465
+ // Verify and apply sysctl settings if needed (for Redis optimization)
466
+ console.log(chalk.gray('Verifying system kernel parameters...'));
467
+ const sysctlCheck = await verifySysctlSettings();
468
+ if (!sysctlCheck.ok) {
469
+ console.log(chalk.yellow(' Reapplying system kernel parameters...'));
470
+ await applySysctlSettings();
471
+ } else {
472
+ console.log(chalk.gray(' System kernel parameters OK'));
473
+ }
474
+
464
475
  // Pre-calculate step numbers for each task (fixes step numbering bug)
465
476
  let currentStep = 0;
466
477
  const stepPreStart = !options.skipBackup ? ++currentStep : null;
@@ -42,7 +42,7 @@ services:
42
42
  networks:
43
43
  - ante-network
44
44
  healthcheck:
45
- test: ["CMD-SHELL", "pg_isready -U ante"]
45
+ test: ["CMD-SHELL", "pg_isready -U ante -d ante_db"]
46
46
  interval: 10s
47
47
  timeout: 5s
48
48
  retries: 5
@@ -0,0 +1,130 @@
1
+ import { execa } from 'execa';
2
+ import { writeFileSync } from 'fs';
3
+ import chalk from 'chalk';
4
+
5
+ const SYSCTL_CONFIG_FILE = '/etc/sysctl.d/ante-redis.conf';
6
+
7
+ /**
8
+ * Required sysctl settings for optimal Redis performance
9
+ * vm.overcommit_memory=1 prevents Redis background save failures under low memory
10
+ */
11
+ const REQUIRED_SETTINGS = {
12
+ 'vm.overcommit_memory': '1'
13
+ };
14
+
15
+ /**
16
+ * Check if a sysctl setting is currently applied
17
+ * @param {string} key - The sysctl key (e.g., 'vm.overcommit_memory')
18
+ * @returns {Promise<string|null>} Current value or null if error
19
+ */
20
+ export async function checkSysctlSetting(key) {
21
+ try {
22
+ const { stdout } = await execa('sysctl', ['-n', key]);
23
+ return stdout.trim();
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Apply a sysctl setting immediately
31
+ * @param {string} key - The sysctl key
32
+ * @param {string} value - The value to set
33
+ * @returns {Promise<boolean>} Success status
34
+ */
35
+ export async function applySysctlSetting(key, value) {
36
+ try {
37
+ await execa('sudo', ['sysctl', '-w', `${key}=${value}`], { stdio: 'pipe' });
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Persist sysctl settings to config file for reboot survival
46
+ * @returns {Promise<boolean>} Success status
47
+ */
48
+ export async function persistSysctlSettings() {
49
+ const lines = Object.entries(REQUIRED_SETTINGS)
50
+ .map(([key, value]) => `${key}=${value}`)
51
+ .join('\n');
52
+
53
+ const content = `# ANTE ERP Redis Optimization
54
+ # Generated by ante-erp-cli
55
+ # See: https://redis.io/docs/getting-started/faq/#background-saving-fails-with-a-fork-error-on-linux
56
+ ${lines}
57
+ `;
58
+
59
+ try {
60
+ // Write to temp file then move with sudo
61
+ const tempFile = '/tmp/ante-sysctl.conf';
62
+ writeFileSync(tempFile, content);
63
+ await execa('sudo', ['mv', tempFile, SYSCTL_CONFIG_FILE], { stdio: 'pipe' });
64
+ await execa('sudo', ['chmod', '644', SYSCTL_CONFIG_FILE], { stdio: 'pipe' });
65
+ return true;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Apply and persist all required sysctl settings
73
+ * @param {boolean} silent - Suppress console output
74
+ * @returns {Promise<{applied: string[], failed: string[], skipped: string[]}>}
75
+ */
76
+ export async function applySysctlSettings(silent = false) {
77
+ const results = { applied: [], failed: [], skipped: [] };
78
+
79
+ for (const [key, value] of Object.entries(REQUIRED_SETTINGS)) {
80
+ const current = await checkSysctlSetting(key);
81
+
82
+ if (current === value) {
83
+ results.skipped.push(key);
84
+ continue;
85
+ }
86
+
87
+ const success = await applySysctlSetting(key, value);
88
+ if (success) {
89
+ results.applied.push(key);
90
+ } else {
91
+ results.failed.push(key);
92
+ }
93
+ }
94
+
95
+ // Persist to config file for reboot survival
96
+ if (results.applied.length > 0) {
97
+ await persistSysctlSettings();
98
+ }
99
+
100
+ if (!silent) {
101
+ if (results.applied.length > 0) {
102
+ console.log(chalk.green(` ✓ Applied sysctl settings: ${results.applied.join(', ')}`));
103
+ }
104
+ if (results.skipped.length > 0) {
105
+ console.log(chalk.gray(` Sysctl settings already configured: ${results.skipped.join(', ')}`));
106
+ }
107
+ if (results.failed.length > 0) {
108
+ console.log(chalk.yellow(` ⚠ Failed to apply: ${results.failed.join(', ')} (may require root privileges)`));
109
+ }
110
+ }
111
+
112
+ return results;
113
+ }
114
+
115
+ /**
116
+ * Verify sysctl settings are applied (for update command)
117
+ * @returns {Promise<{ok: boolean, missing: Array<{key: string, expected: string, current: string|null}>}>}
118
+ */
119
+ export async function verifySysctlSettings() {
120
+ const missing = [];
121
+
122
+ for (const [key, value] of Object.entries(REQUIRED_SETTINGS)) {
123
+ const current = await checkSysctlSetting(key);
124
+ if (current !== value) {
125
+ missing.push({ key, expected: value, current });
126
+ }
127
+ }
128
+
129
+ return { ok: missing.length === 0, missing };
130
+ }