hola-server 1.0.10 → 2.0.1

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.
Files changed (83) hide show
  1. package/README.md +196 -1
  2. package/core/array.js +79 -142
  3. package/core/bash.js +208 -259
  4. package/core/chart.js +26 -16
  5. package/core/cron.js +14 -3
  6. package/core/date.js +15 -44
  7. package/core/encrypt.js +19 -9
  8. package/core/file.js +42 -29
  9. package/core/lhs.js +32 -6
  10. package/core/meta.js +213 -289
  11. package/core/msg.js +20 -7
  12. package/core/number.js +105 -103
  13. package/core/obj.js +15 -12
  14. package/core/random.js +9 -6
  15. package/core/role.js +69 -77
  16. package/core/thread.js +12 -2
  17. package/core/type.js +300 -261
  18. package/core/url.js +20 -12
  19. package/core/validate.js +29 -26
  20. package/db/db.js +297 -227
  21. package/db/entity.js +631 -963
  22. package/db/gridfs.js +120 -166
  23. package/design/add_default_field_attr.md +56 -0
  24. package/http/context.js +22 -8
  25. package/http/cors.js +25 -8
  26. package/http/error.js +27 -9
  27. package/http/express.js +70 -41
  28. package/http/params.js +70 -42
  29. package/http/router.js +51 -40
  30. package/http/session.js +59 -36
  31. package/index.js +85 -9
  32. package/package.json +2 -2
  33. package/router/clone.js +28 -36
  34. package/router/create.js +21 -26
  35. package/router/delete.js +24 -28
  36. package/router/read.js +137 -123
  37. package/router/update.js +38 -56
  38. package/setting.js +22 -6
  39. package/skills/array.md +155 -0
  40. package/skills/bash.md +91 -0
  41. package/skills/chart.md +54 -0
  42. package/skills/code.md +422 -0
  43. package/skills/context.md +177 -0
  44. package/skills/date.md +58 -0
  45. package/skills/express.md +255 -0
  46. package/skills/file.md +60 -0
  47. package/skills/lhs.md +54 -0
  48. package/skills/meta.md +1023 -0
  49. package/skills/msg.md +30 -0
  50. package/skills/number.md +88 -0
  51. package/skills/obj.md +36 -0
  52. package/skills/params.md +206 -0
  53. package/skills/random.md +22 -0
  54. package/skills/role.md +59 -0
  55. package/skills/session.md +281 -0
  56. package/skills/storage.md +743 -0
  57. package/skills/thread.md +22 -0
  58. package/skills/type.md +547 -0
  59. package/skills/url.md +34 -0
  60. package/skills/validate.md +48 -0
  61. package/test/cleanup/close-db.js +5 -0
  62. package/test/core/array.js +226 -0
  63. package/test/core/chart.js +51 -0
  64. package/test/core/file.js +59 -0
  65. package/test/core/lhs.js +44 -0
  66. package/test/core/number.js +167 -12
  67. package/test/core/obj.js +47 -0
  68. package/test/core/random.js +24 -0
  69. package/test/core/thread.js +20 -0
  70. package/test/core/type.js +216 -0
  71. package/test/core/validate.js +67 -0
  72. package/test/db/db-ops.js +99 -0
  73. package/test/db/pipe_test.txt +0 -0
  74. package/test/db/test_case_design.md +528 -0
  75. package/test/db/test_db_class.js +613 -0
  76. package/test/db/test_entity_class.js +414 -0
  77. package/test/db/test_gridfs_class.js +234 -0
  78. package/test/entity/create.js +1 -1
  79. package/test/entity/delete-mixed.js +156 -0
  80. package/test/entity/ref-filter.js +63 -0
  81. package/tool/gen_i18n.js +55 -21
  82. package/test/crud/router.js +0 -99
  83. package/test/router/user.js +0 -17
package/core/bash.js CHANGED
@@ -1,345 +1,294 @@
1
+ /**
2
+ * @fileoverview Bash command execution and SSH utility functions.
3
+ * @module core/bash
4
+ */
5
+
1
6
  const fs = require('fs');
2
7
  const { exec } = require("child_process");
3
8
  const { random_code } = require('./random');
4
9
  const { is_log_debug, is_log_error, log_debug, log_error } = require('../db/db');
5
10
 
6
11
  const LOG_BASH = "bash";
7
-
8
- const get_log_file = async () => {
9
- const home = await run_simple_local_cmd("echo ~");
10
- const log_dir = `${home}/.hola/ssh`;
11
- await run_local_cmd(`mkdir -p ${log_dir}`);
12
- return `${log_dir}/l_${random_code()}.log`;
13
- }
12
+ const SSH_OPTIONS = "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
14
13
 
15
14
  /**
16
- * Run script and get stdout
17
- * @param {host info,contains user,ip and password} host
18
- * @param {commands to run} script
19
- * @returns
15
+ * Execute command with unified logging and error handling.
16
+ * @param {string} cmd - Command to execute.
17
+ * @param {Object} options - Exec options.
18
+ * @param {string} error_msg - Error message prefix.
19
+ * @param {string} success_msg - Success message prefix.
20
+ * @param {Object} [log_extra] - Additional logging context.
21
+ * @returns {Promise<{stdout: string, err?: string}>} Command result.
20
22
  */
21
- const run_script = async (host, script, log_extra) => {
23
+ const exec_with_logging = (cmd, options, error_msg, success_msg, log_extra) => {
22
24
  return new Promise((resolve) => {
23
- exec(`ssh ${host.auth} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${host.port} ${host.user}@${host.ip} /bin/bash <<'EOT'\n ${script} \nEOT\n`, { maxBuffer: 1024 * 150000 }, (error, stdout) => {
25
+ exec(cmd, options, (error, stdout) => {
24
26
  if (error) {
25
- if (is_log_error()) {
26
- log_error(LOG_BASH, "error running on host:" + host.name + " the script:" + script + ",error:" + error, log_extra);
27
- }
28
- resolve({ stdout: stdout, err: "error running the script:" + script + ",error:" + error });
27
+ if (is_log_error()) log_error(LOG_BASH, `${error_msg}, error:${error}`, log_extra);
28
+ resolve({ stdout, err: `${error_msg}, error:${error}` });
29
29
  } else {
30
- if (is_log_debug()) {
31
- log_debug(LOG_BASH, "executing on host:" + host.name + ", script:" + script + ",stdout:" + stdout, log_extra);
32
- }
33
- resolve({ stdout: stdout });
30
+ if (is_log_debug()) log_debug(LOG_BASH, `${success_msg}, stdout:${stdout}`, log_extra);
31
+ resolve({ stdout });
34
32
  }
35
33
  });
36
34
  });
37
35
  };
38
36
 
39
37
  /**
40
- * Run script and get stdout and this use file redirect to avoid progress bar issue
41
- * @param {host info,contains user,ip and password} host
42
- * @param {commands to run} script
43
- * @returns
38
+ * Get temporary log file path.
39
+ * @returns {Promise<string>} Path to temporary log file.
40
+ */
41
+ const get_log_file = async () => {
42
+ const home = await run_simple_local_cmd("echo ~");
43
+ const log_dir = `${home}/.hola/ssh`;
44
+ await run_local_cmd(`mkdir -p ${log_dir}`);
45
+ return `${log_dir}/l_${random_code()}.log`;
46
+ };
47
+
48
+ /**
49
+ * Build SSH command prefix.
50
+ * @param {Object} host - Host configuration.
51
+ * @returns {string} SSH command prefix.
52
+ */
53
+ const build_ssh_prefix = (host) => `ssh ${host.auth} ${SSH_OPTIONS} -p ${host.port} ${host.user}@${host.ip}`;
54
+
55
+ /**
56
+ * Build SCP command.
57
+ * @param {Object} host - Host configuration.
58
+ * @param {string} src - Source path.
59
+ * @param {string} dest - Destination path.
60
+ * @param {boolean} to_remote - True if copying to remote.
61
+ * @returns {string} SCP command.
62
+ */
63
+ const build_scp_cmd = (host, src, dest, to_remote) => {
64
+ const remote_path = `${host.user}@${host.ip}:${to_remote ? dest : src}`;
65
+ const local_path = to_remote ? src : dest;
66
+ const [first, second] = to_remote ? [local_path, remote_path] : [remote_path, local_path];
67
+ return `scp ${host.auth} ${SSH_OPTIONS} -P ${host.port} -q ${first} ${second}`;
68
+ };
69
+
70
+ /**
71
+ * Run script on remote host via SSH.
72
+ * @param {Object} host - Host info with user, ip, port, auth, name.
73
+ * @param {string} script - Commands to run.
74
+ * @param {Object} [log_extra] - Additional logging context.
75
+ * @returns {Promise<{stdout: string, err?: string}>} Command result.
76
+ */
77
+ const run_script = (host, script, log_extra) => {
78
+ const cmd = `${build_ssh_prefix(host)} /bin/bash <<'EOT'\n ${script} \nEOT\n`;
79
+ return exec_with_logging(cmd, { maxBuffer: 1024 * 150000 },
80
+ `error running on host:${host.name} script:${script}`,
81
+ `executing on host:${host.name}, script:${script}`, log_extra);
82
+ };
83
+
84
+ /**
85
+ * Run script on remote host with file redirect to avoid progress bar issues.
86
+ * @param {Object} host - Host info with user, ip, port, auth, name.
87
+ * @param {string} script - Commands to run.
88
+ * @param {Object} [log_extra] - Additional logging context.
89
+ * @returns {Promise<{stdout: string, err?: string}>} Command result.
44
90
  */
45
91
  const run_script_extra = async (host, script, log_extra) => {
46
92
  const log_file = await get_log_file();
93
+ const cmd = `${build_ssh_prefix(host)} /bin/bash <<'EOT' > ${log_file} \n ${script} \nEOT\n`;
47
94
 
48
95
  return new Promise((resolve) => {
49
- exec(`ssh ${host.auth} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${host.port} ${host.user}@${host.ip} /bin/bash <<'EOT' > ${log_file} \n ${script} \nEOT\n`, { maxBuffer: 1024 * 150000 }, (error, stdout) => {
96
+ exec(cmd, { maxBuffer: 1024 * 150000 }, (error, stdout) => {
50
97
  if (error) {
51
- if (is_log_error()) {
52
- log_error(LOG_BASH, "error running on host:" + host.name + " the script:" + script + ",error:" + error, log_extra);
53
- }
54
- resolve({ stdout: stdout, err: "error running the script:" + script + ",error:" + error });
98
+ if (is_log_error()) log_error(LOG_BASH, `error running on host:${host.name} script:${script}, error:${error}`, log_extra);
99
+ resolve({ stdout, err: `error running script:${script}, error:${error}` });
55
100
  } else {
56
101
  const output = fs.readFileSync(log_file, { encoding: 'utf8', flag: 'r' });
57
- if (is_log_debug()) {
58
- log_debug(LOG_BASH, "executing on host:" + host.name + ", script:" + script + ",stdout:" + output, log_extra);
59
- }
60
- resolve({ stdout: output });
102
+ if (is_log_debug()) log_debug(LOG_BASH, `executing on host:${host.name}, script:${script}, stdout:${output}`, log_extra);
61
103
  fs.unlinkSync(log_file);
104
+ resolve({ stdout: output });
62
105
  }
63
106
  });
64
107
  });
65
108
  };
66
109
 
67
- const run_script_file = async (host, script_file, log_extra) => {
68
- return new Promise((resolve) => {
69
- exec(`ssh ${host.auth} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${host.port} ${host.user}@${host.ip} /bin/bash < ${script_file}`, (error, stdout) => {
70
- if (error) {
71
- if (is_log_error()) {
72
- log_error(LOG_BASH, "error running on host:" + host.name + " the script_file:" + script_file + ",error:" + error, log_extra);
73
- }
74
- resolve({ stdout: stdout, err: "error running the script:" + script_file + ",error:" + error });
75
- } else {
76
- if (is_log_debug()) {
77
- log_debug(LOG_BASH, "executing on host:" + host.name + ", script_file:" + script_file + ",stdout:" + stdout, log_extra);
78
- }
79
- resolve({ stdout: stdout });
80
- }
81
- });
82
- });
110
+ /**
111
+ * Run script file on remote host.
112
+ * @param {Object} host - Host info.
113
+ * @param {string} script_file - Path to local script file.
114
+ * @param {Object} [log_extra] - Additional logging context.
115
+ * @returns {Promise<{stdout: string, err?: string}>} Command result.
116
+ */
117
+ const run_script_file = (host, script_file, log_extra) => {
118
+ const cmd = `${build_ssh_prefix(host)} /bin/bash < ${script_file}`;
119
+ return exec_with_logging(cmd, {},
120
+ `error running on host:${host.name} script_file:${script_file}`,
121
+ `executing on host:${host.name}, script_file:${script_file}`, log_extra);
83
122
  };
84
123
 
85
124
  /**
86
- * Run command in local host
87
- * @param {command to execute} cmd
88
- * @returns
125
+ * Run command on local host.
126
+ * @param {string} cmd - Command to execute.
127
+ * @param {Object} [log_extra] - Additional logging context.
128
+ * @returns {Promise<{stdout: string, err?: string}>} Command result.
89
129
  */
90
- const run_local_cmd = async (cmd, log_extra) => {
91
- return new Promise((resolve) => {
92
- exec(cmd, { maxBuffer: 1024 * 15000000 }, (error, stdout) => {
93
- if (error) {
94
- if (is_log_error()) {
95
- log_error(LOG_BASH, "error running on local host with cmd:" + cmd + ",error:" + error, log_extra);
96
- }
97
- resolve({ stdout: stdout, err: "error running the cmd:" + cmd + ",error:" + error }, log_extra);
98
- } else {
99
- if (is_log_debug()) {
100
- log_debug(LOG_BASH, "executing on local host with cmd:" + cmd + ",stdout:" + stdout, log_extra);
101
- }
102
- resolve({ stdout: stdout });
103
- }
104
- });
105
- });
130
+ const run_local_cmd = (cmd, log_extra) => {
131
+ return exec_with_logging(cmd, { maxBuffer: 1024 * 15000000 },
132
+ `error running on local host cmd:${cmd}`,
133
+ `executing on local host cmd:${cmd}`, log_extra);
106
134
  };
107
135
 
108
136
  /**
109
- * Scp remote file to local file
110
- * @param {remote host} host
111
- * @param {remote file path} remote_file
112
- * @param {local file path} locale_file
113
- * @returns
137
+ * SCP remote file to local.
138
+ * @param {Object} host - Remote host info.
139
+ * @param {string} remote_file - Remote file path.
140
+ * @param {string} local_file - Local file path.
141
+ * @param {Object} [log_extra] - Additional logging context.
142
+ * @returns {Promise<{stdout: string, err?: string}>} Command result.
114
143
  */
115
- const scp = async (host, remote_file, local_file, log_extra) => {
116
- return new Promise((resolve) => {
117
- exec(`scp ${host.auth} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P ${host.port} -q ${host.user}@${host.ip}:${remote_file} ${local_file}`, (error, stdout) => {
118
- if (error) {
119
- if (is_log_error()) {
120
- log_error(LOG_BASH, "error scp on host:" + host.name + " remote:" + remote_file + ",local:" + local_file + ",error:" + error, log_extra);
121
- }
122
- resolve({ stdout: stdout, err: "error scp:" + remote_file + " to locale:" + local_file + ",err:" + error });
123
- } else {
124
- if (is_log_debug()) {
125
- log_debug(LOG_BASH, "executing scp on host:" + host.name + ", remote:" + remote_file + ",local:" + local_file + ",stdout:" + stdout, log_extra);
126
- }
127
- resolve({ stdout: stdout });
128
- }
129
- });
130
- });
144
+ const scp = (host, remote_file, local_file, log_extra) => {
145
+ const cmd = build_scp_cmd(host, remote_file, local_file, false);
146
+ return exec_with_logging(cmd, {},
147
+ `error scp on host:${host.name} remote:${remote_file}, local:${local_file}`,
148
+ `executing scp on host:${host.name}, remote:${remote_file}, local:${local_file}`, log_extra);
131
149
  };
132
150
 
133
151
  /**
134
- * Scp local file to remote file
135
- * @param {remote host} host
136
- * @param {local file path} locale_file
137
- * @param {remote file path} remote_file
138
- * @returns
152
+ * SCP local file to remote.
153
+ * @param {Object} host - Remote host info.
154
+ * @param {string} local_file - Local file path.
155
+ * @param {string} remote_file - Remote file path.
156
+ * @param {Object} [log_extra] - Additional logging context.
157
+ * @returns {Promise<{stdout: string, err?: string}>} Command result.
139
158
  */
140
- const scpr = async (host, local_file, remote_file, log_extra) => {
141
- return new Promise((resolve) => {
142
- exec(`scp ${host.auth} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P ${host.port} -q ${local_file} ${host.user}@${host.ip}:${remote_file}`, (error, stdout) => {
143
- if (error) {
144
- if (is_log_error()) {
145
- log_error(LOG_BASH, "error scpr on host:" + host.name + " remote:" + remote_file + ",local:" + local_file + ",error:" + error, log_extra);
146
- }
147
- resolve({ stdout: stdout, err: "error scp:" + remote_file + " to locale:" + local_file + ",err:" + error });
148
- } else {
149
- if (is_log_debug()) {
150
- log_debug(LOG_BASH, "executing scpr on host:" + host.name + ", remote:" + remote_file + ",local:" + local_file + ",stdout:" + stdout, log_extra);
151
- }
152
- resolve({ stdout: stdout });
153
- }
154
- });
155
- });
159
+ const scpr = (host, local_file, remote_file, log_extra) => {
160
+ const cmd = build_scp_cmd(host, local_file, remote_file, true);
161
+ return exec_with_logging(cmd, {},
162
+ `error scpr on host:${host.name} remote:${remote_file}, local:${local_file}`,
163
+ `executing scpr on host:${host.name}, remote:${remote_file}, local:${local_file}`, log_extra);
156
164
  };
157
165
 
158
166
  /**
159
- *Just run simple command and get result directly
160
- * @param {host info,contains user,ip and password} host
161
- * @param {command to run} cmd
162
- * @returns
167
+ * Run simple command on remote host and get trimmed result.
168
+ * @param {Object} host - Host info.
169
+ * @param {string} cmd - Command to run.
170
+ * @param {Object} [log_extra] - Additional logging context.
171
+ * @returns {Promise<string|null>} Trimmed output or null on error.
163
172
  */
164
173
  const run_simple_cmd = async (host, cmd, log_extra) => {
165
174
  const { err, stdout } = await run_script(host, cmd, log_extra);
166
- if (err) {
167
- return null;
168
- } else {
169
- return stdout.trim();
170
- }
171
- }
175
+ return err ? null : stdout.trim();
176
+ };
172
177
 
173
178
  /**
174
- *Just run simple command and get result directly
175
- * @param {command to run} cmd
176
- * @returns
179
+ * Run simple command locally and get trimmed result.
180
+ * @param {string} cmd - Command to run.
181
+ * @param {Object} [log_extra] - Additional logging context.
182
+ * @returns {Promise<string|null>} Trimmed output or null on error.
177
183
  */
178
184
  const run_simple_local_cmd = async (cmd, log_extra) => {
179
185
  const { err, stdout } = await run_local_cmd(cmd, log_extra);
180
- if (err) {
181
- return null;
182
- } else {
183
- return stdout.trim();
184
- }
185
- }
186
+ return err ? null : stdout.trim();
187
+ };
186
188
 
187
189
  /**
188
- * Use regular expression to match the pattern to get some part of value
189
- Speed: Unknown
190
- Manufacturer: NO DIMM
191
- Serial Number: NO DIMM
192
- Asset Tag:
193
- Part Number: NO DIMM
194
- Rank: Unknown
195
- Configured Memory Speed: Unknown
196
- Minimum Voltage: 1.2 V
197
- Maximum Voltage: 1.2 V
198
- Configured Voltage: 1.2 V
199
-
200
- * @param {stdout of the run_cmd} stdout
201
- * @param {key to retrieve info} key
202
- * @returns
190
+ * Extract info from stdout using regex pattern matching.
191
+ * @param {string} stdout - Command output.
192
+ * @param {string} key - Key to search for.
193
+ * @param {Object} [log_extra] - Additional logging context.
194
+ * @returns {string[]} Array of matched values.
203
195
  */
204
196
  const get_info = (stdout, key, log_extra) => {
205
197
  const word_key = key.split(" ").join("\\s+");
206
198
  const regex = new RegExp(`\n\\s?${word_key}\\s?:(.*)\\s`, 'g');
207
- if (is_log_debug()) {
208
- log_debug(LOG_BASH, "get_info and regex:" + JSON.stringify(regex), log_extra);
209
- }
199
+ if (is_log_debug()) log_debug(LOG_BASH, `get_info regex:${JSON.stringify(regex)}`, log_extra);
210
200
 
211
- const results = stdout.matchAll(regex);
212
-
213
- const matched = [];
214
- for (let result of results) {
215
- matched.push(result[1].trim());
216
- }
217
-
218
- if (is_log_debug()) {
219
- log_debug(LOG_BASH, "get_info and matched:" + JSON.stringify(matched), log_extra);
220
- }
201
+ const matched = [...stdout.matchAll(regex)].map(result => result[1].trim());
202
+ if (is_log_debug()) log_debug(LOG_BASH, `get_info matched:${JSON.stringify(matched)}`, log_extra);
221
203
  return matched;
222
- }
223
-
224
- /**
225
- * Get the lines of array
226
- * @param {array of lines} array
227
- * @param {array of line number to get,use range to define the lines} lines
228
- * @returns
229
- */
230
- const get_lines = (array, lines) => {
231
- const result = [];
232
- lines.forEach(line => {
233
- result.push(array[line]);
234
- });
235
- return result;
236
- }
204
+ };
237
205
 
238
206
  /**
239
- * Read all the lines to object such as lscpu to retrieve all the cpu info
240
- Core(s) per socket: 28
241
- Socket(s): 2
242
- NUMA node(s): 2
243
- Vendor ID: GenuineIntel
244
- CPU family: 6
245
- Model: 85
246
- Model name: Intel(R) Xeon(R) Gold 6258R CPU @ 2.70GHz
247
- Stepping: 7
248
- CPU MHz: 2965.749
249
- CPU max MHz: 4000.0000
250
- CPU min MHz: 1000.0000
251
- BogoMIPS: 5400.00
252
- Virtualization: VT-x
253
- L1d cache: 32K
254
- L1i cache: 32K
255
-
256
- * @param {stdout of the run_cmd} stdout
257
- * @param {delimiter to split} delimiter
258
- * @param {lines to retrieve} lines
259
- * @param {only retrieve the value that has the same property in config} config
260
- * @returns
207
+ * Read key-value pairs from stdout.
208
+ * @param {string} stdout - Command output.
209
+ * @param {string} [delimiter=":"] - Key-value delimiter.
210
+ * @param {number[]} [lines] - Specific lines to read.
211
+ * @param {Object} [config] - Filter config for keys.
212
+ * @param {boolean} [exclude_mode] - If true, exclude keys in config.
213
+ * @returns {Object} Parsed key-value object.
261
214
  */
262
215
  const read_key_value_line = (stdout, delimiter = ":", lines, config, exclude_mode) => {
263
- const obj = {};
264
- if (!stdout) {
265
- return obj;
266
- }
216
+ if (!stdout) return {};
267
217
 
268
218
  const contents = stdout.toString().split(/(?:\r\n|\r|\n)/g);
269
- const line_texts = lines ? get_lines(contents, lines) : contents;
270
- line_texts.forEach(content => {
219
+ const line_texts = lines ? lines.map(i => contents[i]) : contents;
220
+
221
+ return line_texts.reduce((obj, content) => {
271
222
  const key_value = content.split(delimiter);
272
- if (key_value.length == 2) {
273
- const key = key_value[0].trim();
274
- if (key && !config || (exclude_mode != true && config && config[key]) || exclude_mode == true && config && !config[`!${key}`]) {
275
- obj[key] = key_value[1].trim();
276
- }
277
- }
278
- });
279
- return obj;
280
- }
223
+ if (key_value.length !== 2) return obj;
281
224
 
225
+ const key = key_value[0].trim();
226
+ const should_include = !config ||
227
+ (exclude_mode !== true && config[key]) ||
228
+ (exclude_mode === true && !config[`!${key}`]);
282
229
 
283
- /**
284
- * Use delimeter to split the line and retrieve the info
285
- *
286
- MODEL SIZE
287
- INTEL SSDSC2KB03 3.5T
288
- INTEL SSDPE2KE032T7 2.9T
230
+ if (key && should_include) obj[key] = key_value[1].trim();
231
+ return obj;
232
+ }, {});
233
+ };
289
234
 
290
- * @param {stdout of the run_cmd} stdout
291
- * @param {the attribute keys} keys
292
- * @param {the lines to ignore} ignore
293
- * @param {delimiter to split the line} delimiter
294
- * @returns
235
+ /**
236
+ * Read structured lines into array of objects.
237
+ * @param {string} stdout - Command output.
238
+ * @param {string[]} keys - Attribute keys for columns.
239
+ * @param {number} [ignore=1] - Lines to skip at start.
240
+ * @param {string} [delimiter=" "] - Column delimiter.
241
+ * @returns {Object[]} Array of parsed objects.
295
242
  */
296
243
  const read_obj_line = (stdout, keys, ignore = 1, delimiter = " ") => {
297
- const results = [];
298
- if (!stdout) {
299
- return results;
300
- }
244
+ if (!stdout) return [];
301
245
 
302
- const contents = stdout.toString().split(/(?:\r\n|\r|\n)/g);
303
- const line_texts = contents.slice(ignore);
304
- line_texts.forEach(content => {
305
- const values = content.split(delimiter);
306
- if (values && values.length > 0 && values[0].trim().length > 0) {
307
- const obj = {};
308
- const attrs = values.filter(f => f.trim().length > 0);
309
- for (let i = 0; i < keys.length; i++) {
310
- const key = keys[i];
311
- const value = attrs[i];
312
- value && (obj[key] = value.trim());
313
- }
314
- results.push(obj);
315
- }
316
- });
317
- return results;
318
- }
246
+ return stdout.toString().split(/(?:\r\n|\r|\n)/g)
247
+ .slice(ignore)
248
+ .filter(content => content.trim().length > 0)
249
+ .map(content => {
250
+ const attrs = content.split(delimiter).filter(f => f.trim().length > 0);
251
+ return keys.reduce((obj, key, i) => {
252
+ if (attrs[i]) obj[key] = attrs[i].trim();
253
+ return obj;
254
+ }, {});
255
+ });
256
+ };
319
257
 
320
258
  /**
321
- * Retrive attributes value and put it to obj
322
- * @param {host info,contains user,ip and password} host
323
- * @param {*} attrs {name:"attr name",cmd:"command to get the attr"}
259
+ * Get multiple system attributes from remote host.
260
+ * @param {Object} host - Host info.
261
+ * @param {Array<{name: string, cmd: string}>} attrs - Attributes to fetch.
262
+ * @param {Object} [log_extra] - Additional logging context.
263
+ * @returns {Promise<Object>} Object with attribute values.
324
264
  */
325
265
  const get_system_attributes = async (host, attrs, log_extra) => {
326
- const obj = {};
327
- for (let i = 0; i < attrs.length; i++) {
328
- const attr = attrs[i];
329
- const value = await run_simple_cmd(host, attr.cmd, log_extra);
330
- value && (obj[attr.name] = value);
331
- }
332
- return obj;
333
- }
266
+ const results = await Promise.all(attrs.map(attr => run_simple_cmd(host, attr.cmd, log_extra)));
267
+ return attrs.reduce((obj, attr, i) => {
268
+ if (results[i]) obj[attr.name] = results[i];
269
+ return obj;
270
+ }, {});
271
+ };
334
272
 
273
+ /**
274
+ * Stop process on remote host if running.
275
+ * @param {Object} host - Host info.
276
+ * @param {string} process_name - Process name to stop.
277
+ * @param {string} stop_cmd - Command to stop the process.
278
+ * @param {boolean} [using_full] - Use pgrep -f for full command match.
279
+ * @param {Object} [log_extra] - Additional logging context.
280
+ * @returns {Promise<boolean>} True if process was running and stopped.
281
+ */
335
282
  const stop_process = async (host, process_name, stop_cmd, using_full, log_extra) => {
336
- const grep_cmd = using_full == true ? `pgrep -f "${process_name}" | wc -l` : `pgrep ${process_name} | wc -l`;
283
+ const grep_cmd = using_full ? `pgrep -f "${process_name}" | wc -l` : `pgrep ${process_name} | wc -l`;
337
284
  const { stdout } = await run_script(host, grep_cmd, log_extra);
338
285
  const has_process = stdout && parseInt(stdout) > 0;
339
- if (has_process) {
340
- await run_script(host, stop_cmd, log_extra);
341
- }
286
+ if (has_process) await run_script(host, stop_cmd, log_extra);
342
287
  return has_process;
343
- }
288
+ };
344
289
 
345
- module.exports = { stop_process, scp, scpr, run_script, run_script_extra, run_script_file, run_simple_cmd, run_local_cmd, run_simple_local_cmd, get_info, get_system_attributes, read_key_value_line, read_obj_line };
290
+ module.exports = {
291
+ stop_process, scp, scpr, run_script, run_script_extra, run_script_file,
292
+ run_simple_cmd, run_local_cmd, run_simple_local_cmd, get_info,
293
+ get_system_attributes, read_key_value_line, read_obj_line
294
+ };
package/core/chart.js CHANGED
@@ -1,36 +1,46 @@
1
+ /**
2
+ * @fileoverview Chart data processing utility functions.
3
+ * @module core/chart
4
+ */
5
+
1
6
  const { has_value } = require("./validate");
2
7
 
3
- const set_chart_header = (arr1, prefix) => {
4
- if (!arr1 || arr1.length < 1) {
5
- return;
6
- }
7
- const headers = arr1[0];
8
- if (!headers || headers.length < 1) {
9
- return;
10
- }
8
+ /**
9
+ * Set chart header prefix for all columns except the first.
10
+ * @param {Array[]} arr - 2D array with headers in first row.
11
+ * @param {string} prefix - Prefix to add to header names.
12
+ */
13
+ const set_chart_header = (arr, prefix) => {
14
+ if (!arr || arr.length < 1) return;
15
+ const headers = arr[0];
16
+ if (!headers || headers.length < 1) return;
11
17
  for (let i = 1; i < headers.length; i++) {
12
18
  headers[i] = `${prefix}${headers[i]}`;
13
19
  }
14
- }
20
+ };
15
21
 
22
+ /**
23
+ * Merge two chart data arrays by combining columns.
24
+ * @param {Array[]} arr1 - First 2D data array (will be modified).
25
+ * @param {Array[]} arr2 - Second 2D data array to merge.
26
+ */
16
27
  const merge_chart_data = (arr1, arr2) => {
17
- if (!arr1 || arr1.length < 2 || !arr2 || arr2.length < 2) {
18
- return;
19
- }
28
+ if (!arr1 || arr1.length < 2 || !arr2 || arr2.length < 2) return;
20
29
 
21
30
  const max = Math.max(arr1.length, arr2.length);
22
31
  const arr1_cols = arr1[0].length;
23
32
  const arr2_cols = arr2[0].length;
33
+
24
34
  for (let i = 0; i < max; i++) {
25
35
  if (!has_value(arr1[i])) {
26
- arr1[i] = [...new Array(arr1_cols)].map(o => "");
36
+ arr1[i] = [...new Array(arr1_cols)].map(() => "");
27
37
  arr1[i][0] = arr2[i][0];
28
38
  }
29
39
  if (!has_value(arr2[i])) {
30
- arr2[i] = [...new Array(arr2_cols)].map(o => "");
40
+ arr2[i] = [...new Array(arr2_cols)].map(() => "");
31
41
  }
32
42
  arr1[i] = [...arr1[i], ...arr2[i].splice(1)];
33
43
  }
34
- }
44
+ };
35
45
 
36
- module.exports = { set_chart_header, merge_chart_data }
46
+ module.exports = { set_chart_header, merge_chart_data };
package/core/cron.js CHANGED
@@ -1,10 +1,21 @@
1
+ /**
2
+ * @fileoverview Cron job scheduling utility functions.
3
+ * @module core/cron
4
+ */
5
+
1
6
  const node_cron = require('node-cron');
2
7
 
3
- const init_cron = async (crons, tasks) => {
8
+ /**
9
+ * Initialize and start cron jobs.
10
+ * @param {string[]} crons - Array of cron expressions.
11
+ * @param {Function[]} tasks - Array of task functions to execute.
12
+ * @param {string} [timezone="Asia/Shanghai"] - Timezone for scheduling.
13
+ */
14
+ const init_cron = async (crons, tasks, timezone = "Asia/Shanghai") => {
4
15
  crons.forEach((cron, index) => {
5
- const schedule = node_cron.schedule(cron, () => { tasks[index](); }, { scheduled: true, timezone: "Asia/Shanghai" });
16
+ const schedule = node_cron.schedule(cron, () => tasks[index](), { scheduled: true, timezone });
6
17
  schedule.start();
7
18
  });
8
- }
19
+ };
9
20
 
10
21
  module.exports = { init_cron };