verteilen-core 1.0.0
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/LICENSE +21 -0
- package/README.md +2 -0
- package/bun.lock +730 -0
- package/index.ts +1 -0
- package/jest.config.js +8 -0
- package/package.json +42 -0
- package/src/client/analysis.ts +377 -0
- package/src/client/client.ts +230 -0
- package/src/client/cluster.ts +125 -0
- package/src/client/execute.ts +210 -0
- package/src/client/http.ts +45 -0
- package/src/client/javascript.ts +535 -0
- package/src/client/job_execute.ts +216 -0
- package/src/client/job_parameter.ts +41 -0
- package/src/client/os.ts +210 -0
- package/src/client/parameter.ts +58 -0
- package/src/client/resource.ts +121 -0
- package/src/client/shell.ts +147 -0
- package/src/interface/base.ts +82 -0
- package/src/interface/bus.ts +144 -0
- package/src/interface/enum.ts +181 -0
- package/src/interface/execute.ts +47 -0
- package/src/interface/record.ts +131 -0
- package/src/interface/server.ts +91 -0
- package/src/interface/struct.ts +292 -0
- package/src/interface/table.ts +34 -0
- package/src/interface/ui.ts +35 -0
- package/src/interface.ts +50 -0
- package/src/lan/en.json +395 -0
- package/src/lan/zh_TW.json +395 -0
- package/src/plugins/i18n.ts +20 -0
- package/src/script/console_manager.ts +135 -0
- package/src/script/console_server_manager.ts +46 -0
- package/src/script/execute/base.ts +309 -0
- package/src/script/execute/feedback.ts +212 -0
- package/src/script/execute/region_job.ts +14 -0
- package/src/script/execute/region_project.ts +23 -0
- package/src/script/execute/region_subtask.ts +14 -0
- package/src/script/execute/region_task.ts +23 -0
- package/src/script/execute/runner.ts +339 -0
- package/src/script/execute/util_parser.ts +175 -0
- package/src/script/execute_manager.ts +348 -0
- package/src/script/socket_manager.ts +329 -0
- package/src/script/webhook_manager.ts +6 -0
- package/src/script/webhook_server_manager.ts +102 -0
- package/src/util/server/console_handle.ts +248 -0
- package/src/util/server/log_handle.ts +194 -0
- package/test/TEST.ts +63 -0
- package/test/client/execute.test.ts +54 -0
- package/test/client/javascript.test.ts +78 -0
- package/test/client/server.test.ts +26 -0
- package/test/client/task.test.ts +136 -0
- package/test/script/parser.test.ts +110 -0
- package/test/script/socket.test.ts +27 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import WebSocket from "ws";
|
|
7
|
+
import { Job, JobCategory, JobType, JobType2, JobType2Text, JobTypeText, Libraries, Messager, Messager_log, OnePath, Parameter, PluginList, TwoPath } from "../interface";
|
|
8
|
+
import { i18n } from "../plugins/i18n";
|
|
9
|
+
import { ClientJavascript } from "./javascript";
|
|
10
|
+
import { ClientJobParameter } from "./job_parameter";
|
|
11
|
+
import { ClientOS } from "./os";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The job execute worker\
|
|
15
|
+
* This class should spawn by the cluster thread to prevent heavy calculation on the main thread
|
|
16
|
+
*/
|
|
17
|
+
export class ClientJobExecute {
|
|
18
|
+
/**
|
|
19
|
+
* Project parameters for references
|
|
20
|
+
*/
|
|
21
|
+
parameter:Parameter | undefined
|
|
22
|
+
/**
|
|
23
|
+
* User library for scripts
|
|
24
|
+
*/
|
|
25
|
+
libraries:Libraries | undefined
|
|
26
|
+
/**
|
|
27
|
+
* The job uuid\
|
|
28
|
+
* This will put in the prefix of message
|
|
29
|
+
*/
|
|
30
|
+
tag: string
|
|
31
|
+
runtime:string
|
|
32
|
+
|
|
33
|
+
private messager:Messager
|
|
34
|
+
private messager_log:Messager_log
|
|
35
|
+
private javascript:ClientJavascript
|
|
36
|
+
private os:ClientOS
|
|
37
|
+
private para:ClientJobParameter
|
|
38
|
+
private job:Job
|
|
39
|
+
private plugin:PluginList
|
|
40
|
+
|
|
41
|
+
constructor(_messager:Messager, _messager_log:Messager_log, _job:Job, _source:WebSocket | undefined, _plugin:PluginList){
|
|
42
|
+
this.messager = _messager
|
|
43
|
+
this.messager_log = _messager_log
|
|
44
|
+
this.tag = _job.uuid
|
|
45
|
+
this.runtime = _job.runtime_uuid || ''
|
|
46
|
+
this.job = _job
|
|
47
|
+
this.plugin = _plugin
|
|
48
|
+
this.para = new ClientJobParameter()
|
|
49
|
+
this.os = new ClientOS(() => this.tag, () => this.job.runtime_uuid || '', _messager, _messager_log)
|
|
50
|
+
this.javascript = new ClientJavascript(_messager, _messager_log, () => this.job)
|
|
51
|
+
this.parameter = process.env.parameter != undefined ? JSON.parse(process.env.parameter) : undefined
|
|
52
|
+
this.libraries = process.env.libraries != undefined ? JSON.parse(process.env.libraries) : undefined
|
|
53
|
+
|
|
54
|
+
ClientJavascript.Init(_messager, _messager_log, this.os, this.para,
|
|
55
|
+
() => this.libraries,
|
|
56
|
+
() => this.parameter,
|
|
57
|
+
() => this.job
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The entry function to execute the job container
|
|
63
|
+
* @param job Target job
|
|
64
|
+
*/
|
|
65
|
+
execute = () => {
|
|
66
|
+
// Output the job type message to let user know what is going on
|
|
67
|
+
this.messager_log(`[Execute] ${this.job.uuid} ${this.job.category == JobCategory.Execution ? i18n.global.t(JobTypeText[this.job.type]) : i18n.global.t(JobType2Text[this.job.type])}`, this.tag, this.runtime)
|
|
68
|
+
const child = this.job.category == JobCategory.Execution ? this.execute_job_exe() : this.execute_job_con()
|
|
69
|
+
return child
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
stop_all = () => {
|
|
73
|
+
this.os.stopall()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Execute the job that classify as run
|
|
78
|
+
* @param job Target job
|
|
79
|
+
* @returns Promise instance
|
|
80
|
+
*/
|
|
81
|
+
private execute_job_exe = () => {
|
|
82
|
+
return new Promise<string>(async (resolve, reject) => {
|
|
83
|
+
switch(this.job.type as JobType){
|
|
84
|
+
case JobType.COPY_FILE:
|
|
85
|
+
{
|
|
86
|
+
const data:TwoPath = { from: this.job.string_args[0], to: this.job.string_args[1] }
|
|
87
|
+
this.os.file_copy(data)
|
|
88
|
+
resolve(`Copy file successfully, ${data.from}, ${data.to}`)
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
case JobType.COPY_DIR:
|
|
92
|
+
{
|
|
93
|
+
const data:TwoPath = { from: this.job.string_args[0], to: this.job.string_args[1] }
|
|
94
|
+
this.os.dir_copy(data)
|
|
95
|
+
resolve(`Copy dir successfully, ${data.from}, ${data.to}`)
|
|
96
|
+
break
|
|
97
|
+
}
|
|
98
|
+
case JobType.DELETE_FILE:
|
|
99
|
+
{
|
|
100
|
+
const data:OnePath = { path: this.job.string_args[0] }
|
|
101
|
+
this.os.file_delete(data)
|
|
102
|
+
resolve(`Delete file successfully, ${data.path}`)
|
|
103
|
+
break
|
|
104
|
+
}
|
|
105
|
+
case JobType.DELETE_DIR:
|
|
106
|
+
{
|
|
107
|
+
const data:OnePath = { path: this.job.string_args[0] }
|
|
108
|
+
this.os.dir_delete(data)
|
|
109
|
+
resolve(`Delete folder successfully, ${data.path}`)
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
case JobType.CREATE_DIR:
|
|
113
|
+
{
|
|
114
|
+
const data:OnePath = { path: this.job.string_args[0] }
|
|
115
|
+
this.os.dir_create(data)
|
|
116
|
+
resolve(`Create dir successfully, ${data.path}`)
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
case JobType.CREATE_FILE:
|
|
120
|
+
{
|
|
121
|
+
const data:TwoPath = { from: this.job.string_args[0], to: this.job.string_args[1] }
|
|
122
|
+
this.os.file_write(data)
|
|
123
|
+
resolve(`Create file successfully, ${data.from} ${data.to}`)
|
|
124
|
+
break
|
|
125
|
+
}
|
|
126
|
+
case JobType.RENAME:
|
|
127
|
+
{
|
|
128
|
+
const data:TwoPath = { from: this.job.string_args[0], to: this.job.string_args[1] }
|
|
129
|
+
this.os.rename(data)
|
|
130
|
+
resolve(`Rename successfully, ${data.from} ${data.to}`)
|
|
131
|
+
break
|
|
132
|
+
}
|
|
133
|
+
case JobType.JAVASCRIPT:
|
|
134
|
+
{
|
|
135
|
+
await this.javascript.JavascriptExecuteWithLib(this.job.script, this.job.string_args).then(() => {
|
|
136
|
+
resolve(`Execute Javascript successfully`)
|
|
137
|
+
}).catch(k => {
|
|
138
|
+
reject(k)
|
|
139
|
+
})
|
|
140
|
+
break
|
|
141
|
+
}
|
|
142
|
+
case JobType.COMMAND:
|
|
143
|
+
{
|
|
144
|
+
this.os.command(this.job.string_args[1], this.job.string_args[2], this.job.string_args[0]).then(m => {
|
|
145
|
+
resolve(m)
|
|
146
|
+
}).catch(err => {
|
|
147
|
+
reject(err)
|
|
148
|
+
})
|
|
149
|
+
break
|
|
150
|
+
}
|
|
151
|
+
case JobType.LIB_COMMAND:
|
|
152
|
+
{
|
|
153
|
+
const target = this.plugin.plugins.find(x => x.name == this.job.string_args[0])
|
|
154
|
+
if(target == undefined){
|
|
155
|
+
reject("Cannot find plugin " + this.job.string_args[0])
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const archTarget = target.contents.find(x => x.arch == process.arch && x.platform == process.platform)
|
|
160
|
+
if(archTarget == undefined){
|
|
161
|
+
reject({
|
|
162
|
+
code: 1,
|
|
163
|
+
message: "Cannot find plugin match arch " + this.job.string_args[0] + " " + process.arch
|
|
164
|
+
})
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.os.lib_command(archTarget.filename, this.job.string_args[1]).then(m => {
|
|
169
|
+
resolve(m)
|
|
170
|
+
}).catch(err => {
|
|
171
|
+
reject(err)
|
|
172
|
+
})
|
|
173
|
+
break
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Execute the job that classify as condition
|
|
181
|
+
* @param job Target job
|
|
182
|
+
* @returns Promise instance
|
|
183
|
+
*/
|
|
184
|
+
private execute_job_con = () => {
|
|
185
|
+
return new Promise<string>(async (resolve, reject) => {
|
|
186
|
+
switch(this.job.type as JobType2){
|
|
187
|
+
case JobType2.CHECK_PATH:
|
|
188
|
+
{
|
|
189
|
+
const data:OnePath = { path: this.job.string_args[0] }
|
|
190
|
+
if(this.os.fs_exist(data)){
|
|
191
|
+
resolve(`Path exist ${data.path}`)
|
|
192
|
+
}else{
|
|
193
|
+
reject({
|
|
194
|
+
code: 2,
|
|
195
|
+
message: `Path not exist ${data.path}`
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
case JobType2.JAVASCRIPT:
|
|
201
|
+
{
|
|
202
|
+
const r = await this.javascript.JavascriptExecuteWithLib(this.job.script, this.job.string_args)
|
|
203
|
+
if(r != undefined && r == 0){
|
|
204
|
+
resolve(`Execute Javascript successfully`)
|
|
205
|
+
}else{
|
|
206
|
+
reject({
|
|
207
|
+
code: 3,
|
|
208
|
+
message: `Execute Javascript failed`
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
break
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import { Header, Setter } from "../interface"
|
|
7
|
+
|
|
8
|
+
export class ClientJobParameter {
|
|
9
|
+
/**
|
|
10
|
+
* Update parameter number on the cluster server
|
|
11
|
+
* @param data Target KeyValue
|
|
12
|
+
*/
|
|
13
|
+
feedbacknumber = (data:Setter) => {
|
|
14
|
+
this.feedback("feedbacknumber", data)
|
|
15
|
+
}
|
|
16
|
+
feedbackboolean = (data:Setter) => {
|
|
17
|
+
this.feedback("feedbackboolean", data)
|
|
18
|
+
}
|
|
19
|
+
feedbackstring = (data:Setter) => {
|
|
20
|
+
this.feedback("feedbackstring", data)
|
|
21
|
+
}
|
|
22
|
+
feedbackobject = (data:Setter) => {
|
|
23
|
+
this.feedback("feedbackobject", data)
|
|
24
|
+
}
|
|
25
|
+
feedbacklist = (data:Setter) => {
|
|
26
|
+
this.feedback("feedbacklist", data)
|
|
27
|
+
}
|
|
28
|
+
feedbackselect = (data:Setter) => {
|
|
29
|
+
this.feedback("feedbackselect", data)
|
|
30
|
+
}
|
|
31
|
+
private feedback = (title:string, data:Setter) => {
|
|
32
|
+
const p:Header = {
|
|
33
|
+
name: title,
|
|
34
|
+
data: {
|
|
35
|
+
key: data.key,
|
|
36
|
+
value: data.value
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
console.log(JSON.stringify(p))
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/client/os.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import { ChildProcess, exec, spawn } from 'child_process';
|
|
7
|
+
import tkill from 'tree-kill'
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import * as os from "os";
|
|
11
|
+
import { DATA_FOLDER, Messager, Messager_log, OnePath, TwoPath } from "../interface";
|
|
12
|
+
|
|
13
|
+
type getstring = ()=>string
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The operation system related actions utility\
|
|
17
|
+
* If you want to do something related to things below
|
|
18
|
+
* * File operation
|
|
19
|
+
* * Folder checker
|
|
20
|
+
* * Writing a file
|
|
21
|
+
* * Call a exe file
|
|
22
|
+
*
|
|
23
|
+
* Please get a instance of this, and call the methods instead using fs youself
|
|
24
|
+
*/
|
|
25
|
+
export class ClientOS {
|
|
26
|
+
private messager:Messager
|
|
27
|
+
private messager_log:Messager_log
|
|
28
|
+
private tag:getstring
|
|
29
|
+
private runtime:getstring
|
|
30
|
+
private children:Array<ChildProcess> = []
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @param _tag The tag getter that put in the prefix of the message
|
|
35
|
+
* @param _messager Message method
|
|
36
|
+
* @param _messager_log Message method with output on the screen feature
|
|
37
|
+
*/
|
|
38
|
+
constructor(_tag:getstring, _runtime:getstring, _messager:Messager, _messager_log:Messager_log){
|
|
39
|
+
this.tag = _tag
|
|
40
|
+
this.runtime = _runtime
|
|
41
|
+
this.messager = _messager
|
|
42
|
+
this.messager_log = _messager_log
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
file_copy = (data:TwoPath) => {
|
|
46
|
+
this.messager(`[OS Action] File copy, ${data.from} => ${data.to}`, this.tag())
|
|
47
|
+
fs.copyFileSync(data.from, data.to)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
dir_copy = (data:TwoPath) => {
|
|
51
|
+
this.messager(`[OS Action] Folder copy, ${data.from} => ${data.to}`, this.tag())
|
|
52
|
+
fs.cpSync(data.from, data.to, { recursive: true, force: true })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
file_delete = (data:OnePath) => {
|
|
56
|
+
this.messager(`[OS Action] File delete, ${data.path}`, this.tag())
|
|
57
|
+
fs.rmSync(data.path);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
dir_delete = (data:OnePath) => {
|
|
61
|
+
this.messager(`[OS Action] Folder delete, ${data.path}`, this.tag())
|
|
62
|
+
fs.rmSync(data.path, { recursive: true, force: true })
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
rename = (data:TwoPath) => {
|
|
66
|
+
this.messager(`[OS Action] File or dir rename, ${data.from} => ${data.to}`, this.tag())
|
|
67
|
+
fs.renameSync(data.from, data.to)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fs_exist = (data:OnePath):boolean => {
|
|
71
|
+
const v = fs.existsSync(data.path)
|
|
72
|
+
this.messager(`[OS Action] Check path exists, ${data.path}`, this.tag())
|
|
73
|
+
return v
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fs_dir_exist = (data:OnePath):boolean => {
|
|
77
|
+
const p = this.fs_exist(data)
|
|
78
|
+
if(!p) return false
|
|
79
|
+
const stat = fs.statSync(data.path)
|
|
80
|
+
return stat.isDirectory()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fs_file_exist = (data:OnePath):boolean => {
|
|
84
|
+
const p = this.fs_exist(data)
|
|
85
|
+
if(!p) return false
|
|
86
|
+
const stat = fs.statSync(data.path)
|
|
87
|
+
return stat.isFile()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
dir_files = (data:OnePath):Array<string> => {
|
|
91
|
+
const r = fs.readdirSync(data.path, { withFileTypes: true }).filter(x => x.isFile()).map(x => x.name)
|
|
92
|
+
return r as string[]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
dir_dirs = (data:OnePath):Array<string> => {
|
|
96
|
+
const r = fs.readdirSync(data.path, { withFileTypes: true }).filter(x => x.isDirectory()).map(x => x.name)
|
|
97
|
+
return r as string[]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
dir_create = (data:OnePath) => {
|
|
101
|
+
this.messager(`[OS Action] Create folder, ${data.path}`, this.tag())
|
|
102
|
+
fs.mkdirSync(data.path, {recursive: true})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
file_write = (data:TwoPath) => {
|
|
106
|
+
this.messager(`[OS Action] Create file, ${data.from}`, this.tag())
|
|
107
|
+
fs.writeFileSync(data.from, data.to)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
file_read = (data:OnePath) => {
|
|
111
|
+
return fs.readFileSync(data.path).toString()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Kill all current running processes
|
|
116
|
+
*/
|
|
117
|
+
stopall = () => {
|
|
118
|
+
this.children.forEach(x => {
|
|
119
|
+
x.stdin!.write('q')
|
|
120
|
+
x.stdin!.end()
|
|
121
|
+
tkill(x.pid!, 'SIGKILL')
|
|
122
|
+
})
|
|
123
|
+
this.children = []
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
lib_command = async (command:string, args:string):Promise<string> => {
|
|
127
|
+
const cc = process.platform == "win32" ? command : "./" + command
|
|
128
|
+
return this.command(cc, args, path.join(os.homedir(), DATA_FOLDER, "exe"))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Call command on terminal
|
|
133
|
+
* @param cwd The system location
|
|
134
|
+
* @param command Command name, Or you can put filename here
|
|
135
|
+
* @param args Arguments, It will split by space afterward
|
|
136
|
+
* @returns
|
|
137
|
+
*/
|
|
138
|
+
command = async (command:string, args:string, cwd?:string):Promise<string> => {
|
|
139
|
+
this.messager_log(`[OS Action] Command cwd: ${cwd}`, this.tag())
|
|
140
|
+
this.messager_log(`[OS Action] Command command: ${command}`, this.tag())
|
|
141
|
+
this.messager_log(`[OS Action] Command args: ${args}`, this.tag())
|
|
142
|
+
return new Promise<string>((resolve, reject) => {
|
|
143
|
+
const child = spawn(command, args.split(' '),
|
|
144
|
+
{
|
|
145
|
+
cwd: cwd,
|
|
146
|
+
shell: true,
|
|
147
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
148
|
+
})
|
|
149
|
+
child.stdin.setDefaultEncoding('utf8')
|
|
150
|
+
// The kill process detecter
|
|
151
|
+
child.on('spawn', () => {
|
|
152
|
+
this.children.push(child)
|
|
153
|
+
this.messager_log(`[Command] Spawn process`, this.tag())
|
|
154
|
+
})
|
|
155
|
+
child.on('error', (err) => {
|
|
156
|
+
this.messager_log(`[Command] Error: ${err}`, this.tag())
|
|
157
|
+
reject(`Error ${err}`)
|
|
158
|
+
})
|
|
159
|
+
child.on('exit', (code, signal) => {
|
|
160
|
+
this.messager_log(`[Command] Process Exit: ${code}`, this.tag())
|
|
161
|
+
})
|
|
162
|
+
child.on('message', (message, sendHandle) => {
|
|
163
|
+
this.messager_log(`[Command] : ${message.toString()}`, this.tag())
|
|
164
|
+
})
|
|
165
|
+
child.on('close', (code, signal) => {
|
|
166
|
+
this.messager_log(`[Command] Process Close: ${code}`, this.tag())
|
|
167
|
+
const index = this.children.findIndex(x => x.pid == child.pid)
|
|
168
|
+
if(index != -1) this.children.splice(index, 1)
|
|
169
|
+
resolve(`Successfully ${code}`)
|
|
170
|
+
})
|
|
171
|
+
child.stdout.setEncoding('utf8');
|
|
172
|
+
child.stdout.on('data', (chunk) => {
|
|
173
|
+
this.messager_log(`[Command Info] : ${chunk.toString()}`, this.tag())
|
|
174
|
+
})
|
|
175
|
+
child.stderr.setEncoding('utf8');
|
|
176
|
+
child.stderr.on('data', (chunk) => {
|
|
177
|
+
this.messager_log(`[Command Error] : ${chunk.toString()}`, this.tag())
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
command_sync = async (command:string, args:string, cwd?:string):Promise<string> => {
|
|
183
|
+
return this.command(command, args, cwd)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
command_exec = (command:string, args:string, cwd?:string) => {
|
|
187
|
+
this.messager_log(`[OS Action] Command cwd: ${cwd}`, this.tag())
|
|
188
|
+
this.messager_log(`[OS Action] Command command: ${command}`, this.tag())
|
|
189
|
+
this.messager_log(`[OS Action] Command args: ${args}`, this.tag())
|
|
190
|
+
const child = exec(`${command} ${args}`, {
|
|
191
|
+
cwd: cwd
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
child.on('spawn', () => {
|
|
195
|
+
this.messager_log(`[Command] Spawn process`, this.tag())
|
|
196
|
+
})
|
|
197
|
+
child.on('error', (err) => {
|
|
198
|
+
this.messager_log(`[Command] Error: ${err}`, this.tag())
|
|
199
|
+
})
|
|
200
|
+
child.on('exit', (code, signal) => {
|
|
201
|
+
this.messager_log(`[Command] Process Exit: ${code}`, this.tag())
|
|
202
|
+
})
|
|
203
|
+
child.on('message', (message, sendHandle) => {
|
|
204
|
+
this.messager_log(`[Command] : ${message.toString()}`, this.tag())
|
|
205
|
+
})
|
|
206
|
+
child.on('close', (code, signal) => {
|
|
207
|
+
this.messager_log(`[Command] Process Close: ${code}`, this.tag())
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import WebSocket from "ws";
|
|
7
|
+
import { Header, Setter } from "../interface";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The parameter feedback helper\
|
|
11
|
+
* Update the main parameter container on the cluster server
|
|
12
|
+
*/
|
|
13
|
+
export class ClientParameter {
|
|
14
|
+
private source:WebSocket | undefined
|
|
15
|
+
|
|
16
|
+
constructor(_source:WebSocket | undefined){
|
|
17
|
+
this.source = _source
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Update parameter number on the cluster server
|
|
22
|
+
* @param data Target KeyValue
|
|
23
|
+
*/
|
|
24
|
+
feedbacknumber = (data:Setter) => {
|
|
25
|
+
this.feedback("feedback_number", data)
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Update parameter boolean on the cluster server
|
|
29
|
+
* @param data Target KeyValue
|
|
30
|
+
*/
|
|
31
|
+
feedbackboolean = (data:Setter) => {
|
|
32
|
+
this.feedback("feedback_boolean", data)
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Update parameter string on the cluster server
|
|
36
|
+
* @param data Target KeyValue
|
|
37
|
+
*/
|
|
38
|
+
feedbackstring = (data:Setter) => {
|
|
39
|
+
this.feedback("feedback_string", data)
|
|
40
|
+
}
|
|
41
|
+
feedbackobject = (data:Setter) => {
|
|
42
|
+
this.feedback("feedback_object", data)
|
|
43
|
+
}
|
|
44
|
+
feedbacklist = (data:Setter) => {
|
|
45
|
+
this.feedback("feedback_list", data)
|
|
46
|
+
}
|
|
47
|
+
feedbackselect = (data:Setter) => {
|
|
48
|
+
this.feedback("feedback_select", data)
|
|
49
|
+
}
|
|
50
|
+
private feedback = (title:string, data:Setter) => {
|
|
51
|
+
if(this.source == undefined) return
|
|
52
|
+
const p:Header = {
|
|
53
|
+
name: title,
|
|
54
|
+
data: data
|
|
55
|
+
}
|
|
56
|
+
this.source.send(JSON.stringify(p, null, 2))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import si from "systeminformation"
|
|
7
|
+
import { ResourceType } from "../interface"
|
|
8
|
+
import { SystemLoad } from "../interface/struct"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The resource query helper
|
|
12
|
+
*/
|
|
13
|
+
export class ClientResource {
|
|
14
|
+
is_query = false
|
|
15
|
+
|
|
16
|
+
Query = async (src:SystemLoad | undefined = undefined, type:ResourceType = ResourceType.ALL):Promise<SystemLoad> => {
|
|
17
|
+
this.is_query = true
|
|
18
|
+
const result:SystemLoad = src != undefined ? src : this.create_new()
|
|
19
|
+
|
|
20
|
+
const _system = (type & ResourceType.SYSTEM ) == ResourceType.SYSTEM ? si.system() : undefined
|
|
21
|
+
const _cpu = si.cpu()
|
|
22
|
+
const _ram = si.mem()
|
|
23
|
+
const _battery = si.battery()
|
|
24
|
+
const _load = si.currentLoad()
|
|
25
|
+
const _os = si.osInfo()
|
|
26
|
+
const _gpu = si.graphics()
|
|
27
|
+
const _disk = si.fsSize()
|
|
28
|
+
const _net = si.networkStats()
|
|
29
|
+
this.is_query = false
|
|
30
|
+
|
|
31
|
+
return await Promise.all([_system, _cpu, _ram, _battery, _load, _os, _gpu, _disk, _net]).then(x => {
|
|
32
|
+
const system = x[0]
|
|
33
|
+
const cpu = x[1]
|
|
34
|
+
const ram = x[2]
|
|
35
|
+
const battery = x[3]
|
|
36
|
+
const load = x[4]
|
|
37
|
+
const os = x[5]
|
|
38
|
+
const gpu = x[6]
|
|
39
|
+
const disk = x[7]
|
|
40
|
+
const net = x[8]
|
|
41
|
+
|
|
42
|
+
if(system != undefined){
|
|
43
|
+
result.system_name = `${system.manufacturer} ${system.model}`
|
|
44
|
+
result.virtual = system.virtual
|
|
45
|
+
}
|
|
46
|
+
if(os != undefined){
|
|
47
|
+
result.platform = process.platform
|
|
48
|
+
result.arch = process.arch
|
|
49
|
+
result.hostname = os.hostname
|
|
50
|
+
}
|
|
51
|
+
if(cpu != undefined){
|
|
52
|
+
result.cpu_name = `${cpu.manufacturer} ${cpu.brand} ${cpu.speed}`
|
|
53
|
+
result.cpu_core = cpu.cores
|
|
54
|
+
}
|
|
55
|
+
if(load != undefined){
|
|
56
|
+
result.cpu_usage = load.currentLoadGuest + load.currentLoadIrq + load.currentLoadSystem + load.currentLoad + load.currentLoadSteal + load.currentLoadNice
|
|
57
|
+
}
|
|
58
|
+
if(ram != undefined){
|
|
59
|
+
result.ram_usage = ram.used
|
|
60
|
+
result.ram_free = ram.free
|
|
61
|
+
result.ram_total = ram.total
|
|
62
|
+
}
|
|
63
|
+
if(battery != undefined){
|
|
64
|
+
result.battery = battery.hasBattery ? battery.percent : 1
|
|
65
|
+
result.charging = battery.isCharging
|
|
66
|
+
}
|
|
67
|
+
if(gpu != undefined){
|
|
68
|
+
result.gpu = gpu.controllers.map(x => ({
|
|
69
|
+
gpu_name: `${x.vendor} ${x.model}`
|
|
70
|
+
}))
|
|
71
|
+
}
|
|
72
|
+
if(disk != undefined){
|
|
73
|
+
result.disk = disk.map(x => ({
|
|
74
|
+
disk_name: x.fs,
|
|
75
|
+
disk_type: x.type,
|
|
76
|
+
disk_free: x.available,
|
|
77
|
+
disk_total: x.size,
|
|
78
|
+
disk_usage: x.used,
|
|
79
|
+
disk_percentage: x.use,
|
|
80
|
+
}))
|
|
81
|
+
}
|
|
82
|
+
if(net != undefined){
|
|
83
|
+
result.net = net.map(x => ({
|
|
84
|
+
net_name: x.iface,
|
|
85
|
+
download: x.rx_sec,
|
|
86
|
+
upload: x.tx_sec
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
result.pid_usage = process.pid
|
|
91
|
+
return result
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private create_new = ():SystemLoad => {
|
|
96
|
+
return {
|
|
97
|
+
system_name: '',
|
|
98
|
+
virtual: false,
|
|
99
|
+
platform: '',
|
|
100
|
+
arch: '',
|
|
101
|
+
hostname: '',
|
|
102
|
+
|
|
103
|
+
cpu_name: '',
|
|
104
|
+
cpu_core: 0,
|
|
105
|
+
cpu_usage: 0,
|
|
106
|
+
|
|
107
|
+
ram_usage: 0,
|
|
108
|
+
ram_free: 0,
|
|
109
|
+
ram_total: 0,
|
|
110
|
+
|
|
111
|
+
battery: 0,
|
|
112
|
+
charging: false,
|
|
113
|
+
|
|
114
|
+
gpu: [],
|
|
115
|
+
disk: [],
|
|
116
|
+
net: [],
|
|
117
|
+
|
|
118
|
+
pid_usage: 0
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|