biz-a-cli 2.3.56 → 2.3.58

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/bin/app.js CHANGED
@@ -3,318 +3,217 @@
3
3
  import yargs from "yargs"
4
4
  import axios from "axios"
5
5
  import fs from "fs"
6
- import { runInNewContext } from "vm"
7
- import uglify from "uglify-js"
8
6
  import * as tar from "tar"
9
7
  import { verify, sign, privateDecrypt, constants as cryptoConstants, randomBytes, createCipheriv } from "node:crypto"
10
8
  import { basename } from "node:path"
11
9
  import { env } from "../envs/env.js"
12
- import * as vm from 'node:vm'
10
+ import { prepareScript, encryptScript } from "./script.js"
13
11
 
14
12
  const keyFolderPath = process.argv[1].substring(0, process.argv[1].lastIndexOf("\\bin")) + "\\key"
15
13
 
16
- yargs(process.argv.slice(2))
17
- .option("s", {
18
- alias: "server",
19
- describe: `Server URL (ex: ${env.BIZA_SERVER_LINK} or http://192.168.1.1 or https://finaapi.imamatek.com)`,
20
- type: "string",
21
- demandOption: true
22
- })
23
- .option("i", {
24
- alias: "dbIndex",
25
- default: 2,
26
- describe: "database index",
27
- type: "number",
28
- demandOption: false
29
- })
30
- .option("sub", {
31
- alias: "subdomain",
32
- describe: "Subdomain",
33
- type: "string",
34
- demandOption: false
35
- })
36
- .option("p", {
37
- alias: "apiPort",
38
- default : 212,
39
- describe: "FINA API Port",
40
- type: "number",
41
- demandOption: false,
42
- })
43
- .command('add', 'Add Biz-A Application',
44
- {
45
- 'd': {
46
- alias: "workingDir",
47
- describe: "Path to templates directory",
48
- type: "string",
49
- demandOption: false,
50
- default: process.cwd(),
51
- },
52
- 'v': {
53
- alias: "verbose",
54
- describe: "Print info to console",
55
- type: "boolean",
56
- demandOption: false,
57
- default: false,
58
- }
14
+ const options = {
15
+ "s" : {
16
+ alias: "server",
17
+ describe: `API or Server URL (ex: ${env.BIZA_SERVER_LINK} or http://192.168.1.1 or https://finaapi.imamatek.com)`,
18
+ type: "string",
19
+ demandOption: true,
20
+ default: 'http://localhost'
59
21
  },
60
- async (options)=>{
61
-
62
- function printInfo(msg){if (options.verbose) {console.log(msg)}}
63
-
64
- const prepareKeys = async ()=>{
65
- const data = Buffer.from(JSON.stringify({issuer: 'CLI', acquirer: 'Client'})).toString('base64')
66
- const privateKey = fs.readFileSync(`${keyFolderPath}\\cliPrivate.pem`)
67
- const signature = sign('sha256', data, {key: privateKey, passphrase: 'Biz-A@cli', padding: cryptoConstants.RSA_PKCS1_PSS_PADDING}).toString('base64')
68
- const res = await axios.get(env.BIZA_SERVER_LINK+'/api/issuerKey', {params: {data, signature}})
69
- if ((res.data.data!=null) && verify('sha256', res.data.data, {key: fs.readFileSync(`${keyFolderPath}\\serverPublic.pem`), padding: cryptoConstants.RSA_PKCS1_PSS_PADDING}, Buffer.from(res.data.signature, 'base64'))) {
70
- const resData = JSON.parse(Buffer.from(res.data.data, 'base64').toString())
71
- const decryptedAESKey = privateDecrypt({key: privateKey, passphrase: 'Biz-A@cli', padding: cryptoConstants.RSA_PKCS1_OAEP_PADDING}, Buffer.from(resData.issuer.key, 'base64')).toString()
72
- const cliSignature = (signedData)=>sign('sha256', signedData, {key: privateKey, passphrase: 'Biz-A@cli', padding: cryptoConstants.RSA_PKCS1_PSS_PADDING}).toString('base64')
73
- const acquirerData = Buffer.from(JSON.stringify(resData.acquirer)).toString('base64')
74
- const signature = cliSignature(acquirerData)
75
- return {encryptKey: decryptedAESKey, metadata: {acquirer: {data: acquirerData, signature}}}
76
- }
77
- else {
78
- return null
79
- }
80
- }
81
-
82
- function replacer(key, value) {
83
- /*
84
- // with line break
85
- if (typeof value == 'function') {
86
- let arr = value.toString().replace(/(\r\n|\n|\r)/gm, "°").split("°");
87
- if (arr.length < 3) throw 'Function must be minimal 3 lines';
88
- return [
89
- 'window.Function',
90
- getParam(arr[0]),
91
- arr.slice(1, arr.length - 1)
92
- ];
93
- } else {
94
- return value;
95
- }
96
- */
97
-
98
- // without line break
99
- if (typeof value == 'function') {
100
- const fnScript = value.toString()
101
- let params, body = ''
102
- if (fnScript.indexOf('{')==-1) {
103
- const arr = fnScript.split('=>')
104
- params = arr[0]
105
- body = arr.slice(1).join()
106
- } else {
107
- params = fnScript.split('{')[0]
108
- body = fnScript.substring(fnScript.indexOf('{')+1, fnScript.lastIndexOf('}'))
109
- }
110
- params = params.replace(/(\s|=>|\(|\)|function)/gim, '')
111
- printInfo(['window.Function', params, body])
112
- return ['window.Function', params, body]
113
- } else {
114
- return value;
115
- }
116
- }
117
-
118
- const minifiedIt = async (fileName) => {
119
- const dataFile = fs.readFileSync(fileName);
120
- printInfo(`===================\n${fileName.toUpperCase()}\n===================`)
121
-
122
- const minifyResult = uglify.minify(dataFile.toString(), {compress: false, mangle: false})
123
- if (minifyResult.error ) {throw (`${fileName}:${minifyResult.error.line}:${minifyResult.error.col}: ` + minifyResult.error)}
124
- let jsMinData = minifyResult.code
125
- jsMinData = jsMinData.replace('module.exports=get;', '') // Ex : at "executeBlockLib.js" of Imamatek App, will throw error. We need this line code for testing or debugging
126
- const minifiedBuffer = Buffer.from(jsMinData)
127
- printInfo(['Minify : \n', minifiedBuffer.toString()].join(''))
128
-
129
- let stringifyData = ''
130
- const isJsonFile = fileName.split('.').pop().toLowerCase().match(/^(json)$/)
131
-
132
- // compile using node.js VM
133
- try {
134
- printInfo('Running script with VM')
135
- if (isJsonFile) {
136
- const vmResult = runInNewContext(minifiedBuffer.toString())
137
- stringifyData = vmResult ? JSON.stringify((typeof vmResult=='function') ? vmResult() : (vmResult || ''), replacer) : ''
138
- } else {
139
- // for handling "get" function in local scope (let, var, const)
140
- const sandbox = vm.createContext({script: ''});
141
- vm.runInContext(
142
- minifiedBuffer.toString() + "\n script = (typeof get=='undefined') ? '' : (typeof get=='function') ? get() : (get||'')",
143
- sandbox
144
- )
145
- stringifyData = JSON.stringify(sandbox.script, replacer)
146
- }
147
- } catch (err){
148
- printInfo(`${fileName} : ` + err) // Do not log it as error, we will try to compile the script using ES6 Import
149
- stringifyData = ''
150
- }
151
-
152
- const isEmptyScript = (scriptText)=>((typeof scriptText=='undefined') || (scriptText===null) || (scriptText.trim()==='""') || (typeof scriptText=='string' && scriptText.trim().length==0))
153
-
154
- // compile using ES6 Import
155
- if (isEmptyScript(stringifyData)) {
156
- try {
157
- printInfo('Running script with Import function')
158
- const lib = await import(`file://${process.cwd()}\\${fileName}`, isJsonFile ? {assert: {type: "json"}} : {})
159
- stringifyData = JSON.stringify((typeof lib.default=='function') ? lib.default() : (lib.default || ''), replacer).replace(/ /g,'') // remove trailing whitespace
160
- // stringifyData = stringifyData.replace(/\\r\\n/g,'') // remove line break. We can not do this because "inline css" still requires line breaks
161
- } catch (error){
162
- throw(`${fileName} : ` + error)
163
- }
164
- }
165
-
166
- if (isEmptyScript(stringifyData)) {
167
- throw(`${fileName} : ` + 'Failed to compile template script.\nPlease make sure the script is correct and not returning empty result')
168
- } else {
169
- printInfo('Array function :')
170
- printInfo(['Stringify : \n', stringifyData].join(''))
171
- }
172
- // console.log('RESULT =>', stringifyData)
173
- printInfo('===================')
174
- return Buffer.from(stringifyData)
22
+ "i" : {
23
+ alias: "dbIndex",
24
+ default: 2,
25
+ describe: "database index",
26
+ type: "number",
27
+ demandOption: false
28
+ },
29
+ "sub": {
30
+ alias: "subdomain",
31
+ describe: "Subdomain",
32
+ type: "string",
33
+ demandOption: false
34
+ },
35
+ "p": {
36
+ alias: "apiPort",
37
+ default : 212,
38
+ describe: "FINA API Port",
39
+ type: "number",
40
+ demandOption: false,
41
+ }
42
+ }
43
+
44
+ const addCommandOptions = {
45
+ 'd': {
46
+ alias: "workingDir",
47
+ describe: "Path to templates directory",
48
+ type: "string",
49
+ demandOption: false,
50
+ default: process.cwd(),
51
+ },
52
+ 'v': {
53
+ alias: "verbose",
54
+ describe: "Print info to console",
55
+ type: "boolean",
56
+ demandOption: false,
57
+ default: false,
58
+ }
59
+ }
60
+
61
+ const removeCommandOptions = {
62
+ 'n': {
63
+ alias: "appName",
64
+ describe: "Application name",
65
+ type: "string",
66
+ demandOption: true,
67
+ default: ""
68
+ }
69
+ }
70
+
71
+ Object
72
+ .keys(options)
73
+ .reduce((app, optKey)=>app = app.option(optKey, options[optKey]), yargs(process.argv.slice(2)))
74
+ .command('add', 'Add Biz-A Application', addCommandOptions, async (options)=>{
75
+
76
+ const prepareKeys = async ()=>{
77
+ const data = Buffer.from(JSON.stringify({issuer: 'CLI', acquirer: 'Client'})).toString('base64')
78
+ const privateKey = fs.readFileSync(`${keyFolderPath}\\cliPrivate.pem`)
79
+ const signature = sign('sha256', data, {key: privateKey, passphrase: 'Biz-A@cli', padding: cryptoConstants.RSA_PKCS1_PSS_PADDING}).toString('base64')
80
+ const res = await axios.get(env.BIZA_SERVER_LINK+'/api/issuerKey', {params: {data, signature}})
81
+ if ((res.data.data!=null) && verify('sha256', res.data.data, {key: fs.readFileSync(`${keyFolderPath}\\serverPublic.pem`), padding: cryptoConstants.RSA_PKCS1_PSS_PADDING}, Buffer.from(res.data.signature, 'base64'))) {
82
+ const resData = JSON.parse(Buffer.from(res.data.data, 'base64').toString())
83
+ const decryptedAESKey = privateDecrypt({key: privateKey, passphrase: 'Biz-A@cli', padding: cryptoConstants.RSA_PKCS1_OAEP_PADDING}, Buffer.from(resData.issuer.key, 'base64')).toString()
84
+ const cliSignature = (signedData)=>sign('sha256', signedData, {key: privateKey, passphrase: 'Biz-A@cli', padding: cryptoConstants.RSA_PKCS1_PSS_PADDING}).toString('base64')
85
+ const acquirerData = Buffer.from(JSON.stringify(resData.acquirer)).toString('base64')
86
+ const signature = cliSignature(acquirerData)
87
+ return {encryptKey: decryptedAESKey, metadata: {acquirer: {data: acquirerData, signature}}}
175
88
  }
176
-
177
- const encryptIt = (data, encryptKey)=>{
178
- const initializeVector = randomBytes(16) // "iv" is unique for each template file
179
- const cipher = createCipheriv('aes-256-cbc', Buffer.from(encryptKey, 'base64'), initializeVector)
180
- return Buffer.concat([initializeVector, cipher.update(data), cipher.final()]) // we put "iv" at beginning of cipherText, seperate it when doing decryption
89
+ else {
90
+ return null
181
91
  }
92
+ }
182
93
 
183
- const compressIt = (fileName, folderPath)=>{
184
- // tar v7.1.0 -> tested 10x times, 2-3x times last compressed file have empty content
185
- // tar v.7.4.0 -> tested 1000x times, all files have content (used app.test.js)
186
- let compressSuccess = false
187
- const maxRetry = 10
188
- let retryCount = 0
189
- do {
190
- tar.c({file: fileName, cwd: folderPath, gzip: {level:9}, strict: true, sync: true}, fs.readdirSync(folderPath))
191
- compressSuccess = true
192
- tar.t({file: fileName, cwd: folderPath, sync: true, onentry: (entry)=>{
193
- if (entry.size==0) {
194
- compressSuccess = false
195
- }
196
- }})
197
- if (compressSuccess==false) {
198
- fs.unlinkSync(fileName)
199
- retryCount++
94
+ const compressIt = (fileName, folderPath)=>{
95
+ // tar v7.1.0 -> tested 10x times, 2-3x times last compressed file have empty content
96
+ // tar v.7.4.0 -> tested 1000x times, all files have content (used app.test.js)
97
+ let compressSuccess = false
98
+ const maxRetry = 10
99
+ let retryCount = 0
100
+ do {
101
+ tar.c({file: fileName, cwd: folderPath, gzip: {level:9}, strict: true, sync: true}, fs.readdirSync(folderPath))
102
+ compressSuccess = true
103
+ tar.t({file: fileName, cwd: folderPath, sync: true, onentry: (entry)=>{
104
+ if (entry.size==0) {
105
+ compressSuccess = false
200
106
  }
107
+ }})
108
+ if (compressSuccess==false) {
109
+ fs.unlinkSync(fileName)
110
+ retryCount++
201
111
  }
202
- while ((compressSuccess==false) && (retryCount<=maxRetry))
203
- }
204
-
205
- const getFileList = ()=>{
206
- return (fs.readdirSync(process.cwd())).filter((fileName)=>{
207
- const stat = fs.statSync(fileName)
208
- return stat.isFile() && (stat.size>0) && ((fileName.split('.').pop().toLowerCase().match(/^(js)$/) || (fileName.toLowerCase()=='menu.json')))
209
- })
210
112
  }
113
+ while ((compressSuccess==false) && (retryCount<=maxRetry))
114
+ }
211
115
 
212
- async function addApp() {
213
- /*
214
- hex => 2 char = 1 bytes => can be encrypted
215
- base64 => 4 char = 3 bytes => can be encrypted. Smaller size compare to Hex
216
- utf8 => 1 char = 1 - 4 bytes => can not be encrypted, encryption need precise bytes per Character. Smallest Size compare to Hex and base64
217
- */
218
- process.chdir(options.workingDir)
219
- try {
220
- const bundlingStart = performance.now()
221
- const rootFolder = './upload/'
222
- const bundleName = basename(process.cwd()).trim().replace(' ', '').toLowerCase()
223
- const bundleFolder = rootFolder+bundleName+'/'
224
- const files = getFileList()
225
- if (files.length>0) {
226
- const keys = await prepareKeys()
227
- let processedFile = 0
228
- fs.rmSync(rootFolder, {force: true, recursive: true})
229
- fs.mkdirSync(bundleFolder, {recursive: true})
230
- for (const file of files) {
231
- const fileName = file.toLowerCase();
232
- // if (['finalib.js', 'solib.js'].indexOf(fileName)==-1) {continue}
233
- const minifiedData = await minifiedIt(fileName);
234
- const encryptedData = encryptIt(minifiedData, keys.encryptKey)
235
- if (fileName=='menu.json') {
236
- keys.metadata['menu'] = encryptedData.toString('base64')
237
- } else {
238
- fs.writeFileSync(bundleFolder+fileName, encryptedData.toString('base64'))
239
- }
240
- processedFile++
241
- }
242
- const bundleFile = `${rootFolder}${bundleName}.tgz`
243
- compressIt(bundleFile, bundleFolder)
244
- console.log(`Finished packing ${processedFile} files into "${bundleFile}" (${((performance.now()-bundlingStart)/1000).toFixed(2)}s)`)
116
+ const getFileList = ()=>{
117
+ return (fs.readdirSync(process.cwd())).filter((fileName)=>{
118
+ const stat = fs.statSync(fileName)
119
+ return stat.isFile() && (stat.size>0) && ((fileName.split('.').pop().toLowerCase().match(/^(js)$/) || (fileName.toLowerCase()=='menu.json')))
120
+ })
121
+ }
245
122
 
246
- // send to FINAPI
247
- const uploadingStart = performance.now()
248
- const data = (fs.readFileSync(bundleFile)).toString('base64') // *.tgz to base64String
249
- const baseUrl = 'fina/rest/TOrmMethod/%22setApp%22'
250
- const url = options.sub ?
251
- `${options.server}/hub/${baseUrl}?subdomain=${options.sub}` :
252
- `${options.server}:${options.apiPort}/${baseUrl}`
253
- const headers = {'Content-Type': 'text/plain'}
254
- const param = { _parameters: [options.dbIndex, bundleName, data, keys.metadata] }
255
- const res = await axios.post(url, param, { headers: headers });
256
- if (res.data.success) {
257
- console.log(`Finished uploading "${bundleFile}" (${((performance.now()-uploadingStart)/1000).toFixed(2)}s)`)
258
- fs.rmSync(rootFolder, {force: true, recursive: true})
123
+ async function addApp() {
124
+ /*
125
+ hex => 2 char = 1 bytes => can be encrypted
126
+ base64 => 4 char = 3 bytes => can be encrypted. Smaller size compare to Hex
127
+ utf8 => 1 char = 1 - 4 bytes => can not be encrypted, encryption need precise bytes per Character. Smallest Size compare to Hex and base64
128
+ */
129
+ process.chdir(options.workingDir)
130
+ try {
131
+ const bundlingStart = performance.now()
132
+ const rootFolder = './upload/'
133
+ const bundleName = basename(process.cwd()).trim().replace(' ', '').toLowerCase()
134
+ const bundleFolder = rootFolder+bundleName+'/'
135
+ const files = getFileList()
136
+ if (files.length>0) {
137
+ const keys = await prepareKeys()
138
+ let processedFile = 0
139
+ fs.rmSync(rootFolder, {force: true, recursive: true})
140
+ fs.mkdirSync(bundleFolder, {recursive: true})
141
+ for (const file of files) {
142
+ const fileName = file.toLowerCase();
143
+ const encryptedScript = encryptScript(await prepareScript(fileName, fs.readFileSync(fileName).toString(), options.verbose), keys.encryptKey)
144
+ if (fileName=='menu.json') {
145
+ keys.metadata['menu'] = encryptedScript.toString('base64')
259
146
  } else {
260
- console.error(res.data.error)
147
+ fs.writeFileSync(bundleFolder+fileName, encryptedScript.toString('base64'))
261
148
  }
262
- } else {
263
- console.error('Nothing to upload. Please recheck your app folder.')
264
- }
265
- } catch (e) {
266
- console.error(e.response?.data ? e.response.data : e)
267
- // console.error({e})
268
- }
269
- }
270
149
 
271
- await addApp()
272
- }
273
- )
274
- .command('remove', 'Remove Biz-A Application',
275
- {
276
- 'n': {
277
- alias: "appName",
278
- describe: "Application name",
279
- type: "string",
280
- demandOption: true,
281
- default: ""
282
- }
283
- },
284
- (options)=>{
285
- (async () => {
286
- try {
287
- const baseUrl = 'fina/rest/TOrmMethod/%22deleteApp%22'
150
+ processedFile++
151
+ }
152
+ const bundleFile = `${rootFolder}${bundleName}.tgz`
153
+ compressIt(bundleFile, bundleFolder)
154
+ console.log(`Finished packing ${processedFile} files into "${bundleFile}" (${((performance.now()-bundlingStart)/1000).toFixed(2)}s)`)
155
+
156
+ // send to API
157
+ const uploadingStart = performance.now()
158
+ const data = (fs.readFileSync(bundleFile)).toString('base64') // *.tgz to base64String
159
+ const baseUrl = 'fina/rest/TOrmMethod/%22setApp%22'
288
160
  const url = options.sub ?
289
161
  `${options.server}/hub/${baseUrl}?subdomain=${options.sub}` :
290
162
  `${options.server}:${options.apiPort}/${baseUrl}`
291
163
  const headers = {'Content-Type': 'text/plain'}
292
- const deleteApps = options.appName.trim().replaceAll(' ', '').toLowerCase()
293
- const param = { _parameters: [options.dbIndex, deleteApps] }
164
+ const param = { _parameters: [options.dbIndex, bundleName, data, keys.metadata] }
294
165
  const res = await axios.post(url, param, { headers: headers });
295
- if (res.data?.success) {
296
- if (deleteApps=='') {
297
- console.log('All apps removed')
298
- } else {
299
- const failedList = (res.data._f && (typeof res.data._f=='string')) ? res.data._f.trim().replaceAll(' ', '').toLowerCase().split(',') : []
300
- const removeList = deleteApps.split(',')
301
- removeList.forEach((app)=>{
302
- console.log(`${app} ${failedList.indexOf(app)==-1 ? 'removed' : 'not found'}`)
303
- })
304
- }
166
+ if (res.data.success) {
167
+ console.log(`Finished uploading "${bundleFile}" (${((performance.now()-uploadingStart)/1000).toFixed(2)}s)`)
168
+ fs.rmSync(rootFolder, {force: true, recursive: true})
305
169
  } else {
306
170
  console.error(res.data.error)
307
171
  }
308
- return res
309
- } catch (e) {
310
- const errMsg = (e.response?.data ? e.response.data : e)
311
- console.error(errMsg)
312
- return errMsg
172
+ } else {
173
+ console.error('Nothing to upload. Please recheck your app folder.')
313
174
  }
314
- })()
175
+ } catch (e) {
176
+ console.error(e.response?.data ? e.response.data : e)
177
+ }
315
178
  }
316
- )
179
+
180
+ await addApp()
181
+ })
182
+ .command('remove', 'Remove Biz-A Application', removeCommandOptions, (options)=>{
183
+ (async () => {
184
+ try {
185
+ const baseUrl = 'fina/rest/TOrmMethod/%22deleteApp%22'
186
+ const url = options.sub ?
187
+ `${options.server}/hub/${baseUrl}?subdomain=${options.sub}` :
188
+ `${options.server}:${options.apiPort}/${baseUrl}`
189
+ const headers = {'Content-Type': 'text/plain'}
190
+ const deleteApps = options.appName.trim().replaceAll(' ', '').toLowerCase()
191
+ const param = { _parameters: [options.dbIndex, deleteApps] }
192
+ const res = await axios.post(url, param, { headers: headers });
193
+ if (res.data?.success) {
194
+ if (deleteApps=='') {
195
+ console.log('All apps removed')
196
+ } else {
197
+ const failedList = (res.data._f && (typeof res.data._f=='string')) ? res.data._f.trim().replaceAll(' ', '').toLowerCase().split(',') : []
198
+ const removeList = deleteApps.split(',')
199
+ removeList.forEach((app)=>{
200
+ console.log(`${app} ${failedList.indexOf(app)==-1 ? 'removed' : 'not found'}`)
201
+ })
202
+ }
203
+ } else {
204
+ console.error(res.data.error)
205
+ }
206
+ return res
207
+ } catch (e) {
208
+ const errMsg = (e.response?.data ? e.response.data : e)
209
+ console.error(errMsg)
210
+ return errMsg
211
+ }
212
+ })()
213
+ })
317
214
  .recommendCommands()
318
215
  .demandCommand(1, 'You need at least one command before moving on')
319
216
  .strict()
320
- .parse();
217
+ .parse();
218
+
219
+ export {options, addCommandOptions, removeCommandOptions}
@@ -0,0 +1,105 @@
1
+ import axios from 'axios';
2
+ import { prepareScript, encryptScript } from './script.js';
3
+ import { IDLE_SOCKET_TIMEOUT_MILLISECONDS } from './hubEvent.js';
4
+ import { CLIENT_ROOM } from './directHubEvent.js';
5
+
6
+ export async function getAppData(apiOpts){
7
+ return await axios.request({
8
+ timeout : IDLE_SOCKET_TIMEOUT_MILLISECONDS,
9
+ baseURL : `${apiOpts['secure']==true ? 'https://' : 'http://'}${apiOpts['hostname']}:${apiOpts['port']}`,
10
+ url : 'fina/rest/TOrmMethod/%22list%22',
11
+ method : 'POST',
12
+ "data" : {
13
+ "dbIndex": apiOpts['dbindex'],
14
+ "object":{
15
+ "columns":[
16
+ {"title":"app", "data":"SYS$APPS.APPNAME"},
17
+ {"title":"metadata", "data":"SYS$APPS.METADATA"}
18
+ ]
19
+ }
20
+ }
21
+ })
22
+ .then(response=>{
23
+ return {error: null, apps: JSON.parse(response.data.data)};
24
+ })
25
+ .catch(err=>{
26
+ return {"error": ((err && (err instanceof Error)) ? err.message : err), apps: []};
27
+ });
28
+ };
29
+
30
+ async function getDeployScript(serverUrl, appList, data){
31
+ let script = await prepareScript(data.fileName, data.script, false);
32
+ if (script) {
33
+ script = await axios.request({
34
+ timeout : IDLE_SOCKET_TIMEOUT_MILLISECONDS,
35
+ baseURL : serverUrl,
36
+ url : 'api/acquirerKey',
37
+ method : 'POST',
38
+ "data" : {"metadata": JSON.stringify(JSON.parse((appList.find((app)=>app['SYS$APPS.APPNAME']===data.appName))['SYS$APPS.METADATA'])['acquirer'])},
39
+ })
40
+ .then(response=>encryptScript(script, response.data).toString('base64'));
41
+ };
42
+ return script;
43
+ };
44
+
45
+ export async function onScriptChangeFromVsCode(sock, serverUrl, appList, data){
46
+ let script;
47
+ try {
48
+ script = await getDeployScript(serverUrl, appList, data);
49
+ if (script) {
50
+ sock.to(CLIENT_ROOM).emit('scriptDeploy', {...data, ...{script}});
51
+ return {error: null, script};
52
+ }
53
+ else {
54
+ throw new Error('Invalid Script');
55
+ };
56
+ }
57
+ catch (err) {
58
+ return {"error": ((err && (err instanceof Error)) ? err.message : err), script};
59
+ };
60
+ };
61
+
62
+ const addAppListListener = (sock,argv)=>{
63
+ sock.on('appList', async (data, cb)=>{
64
+ const result = await getAppData(argv);
65
+ cb(result.error, ((result.error) ? [] : result.apps.map((app)=>app['SYS$APPS.APPNAME'])));
66
+ });
67
+ };
68
+
69
+ const addOnScriptChangeListener = (sock, argv, deployFn)=>{
70
+ sock.on('scriptChange', async (data, cb)=>{
71
+ const result = await getAppData(argv);
72
+ if (result.error) {
73
+ cb(result);
74
+ }
75
+ else {
76
+ const deployResult = await deployFn(result.apps, data);
77
+ cb(deployResult.error, deployResult.script);
78
+ };
79
+ });
80
+ };
81
+
82
+ export const deploymentListenerForVSCode = (socket, argv)=>{
83
+ addAppListListener(socket,argv);
84
+ addOnScriptChangeListener(socket, argv, async (apps, data)=>{
85
+ return (await onScriptChangeFromVsCode(socket, argv['server'], apps, data));
86
+ });
87
+ };
88
+
89
+ export async function onScriptChangeFromHubServer(serverUrl, appList, data){
90
+ let script;
91
+ try {
92
+ script = await getDeployScript(serverUrl, appList, data);
93
+ return {error: (script ? null : 'Invalid Script'), script};
94
+ }
95
+ catch (err) {
96
+ return {"error": ((err && (err instanceof Error)) ? err.message : err), script};
97
+ };
98
+ };
99
+
100
+ export const deploymentListenerForHubServer = (socket, argv)=>{
101
+ addAppListListener(socket, argv);
102
+ addOnScriptChangeListener(socket, argv, async (apps, data)=>{
103
+ return (await onScriptChangeFromHubServer(argv['server'], apps, data));
104
+ });
105
+ };