biz-a-cli 2.3.42 → 2.3.52
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/directHubEvent.js +57 -0
- package/bin/hub.js +64 -21
- package/bin/hubEvent.js +71 -20
- package/mailController.js +1 -1
- package/package.json +5 -4
- package/readme.md +22 -1
- package/scheduler/datalib.js +2 -8
- package/tests/data.test.js +86 -19
- package/tests/mailCtl.test.js +4 -4
- package/bin/log/debug.log +0 -20
- package/bin/log/error.log +0 -20
- package/bin/log/exception.log +0 -0
- package/bin/log/info.log +0 -20
- package/bin/node +0 -0
- package/log/debug.log +0 -0
- package/log/error.log +0 -0
- package/log/exception.log +0 -0
- package/log/info.log +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { IDLE_SOCKET_TIMEOUT_MILLISECONDS } from './hubEvent.js'
|
|
3
|
+
import { Tunnel as QuickTunnel } from 'cloudflared'
|
|
4
|
+
|
|
5
|
+
export async function localhostTunnel(port){
|
|
6
|
+
function notifyUser(){
|
|
7
|
+
console.log(`${new Date()}: Direct hub only available within local LAN. Please restart CLI to try again.`)
|
|
8
|
+
}
|
|
9
|
+
const qt = QuickTunnel.quick('127.0.0.1:'+port)
|
|
10
|
+
qt.on('Disconnected', ()=>{console.log('DISCONNECTED');notifyUser()})
|
|
11
|
+
let url = await new Promise((resolve)=>{
|
|
12
|
+
let tunnelURL = ''
|
|
13
|
+
qt.once('url', (qtUrl)=>{console.log('URL obtained');tunnelURL=qtUrl})
|
|
14
|
+
qt.once('connected', (conn)=>{console.log('CONNECTED', conn); resolve(tunnelURL)})
|
|
15
|
+
qt.once('exit', (code)=>{resolve('')})
|
|
16
|
+
qt.once('error', (err)=>{resolve('')})
|
|
17
|
+
})
|
|
18
|
+
if (!url) {
|
|
19
|
+
notifyUser()
|
|
20
|
+
}
|
|
21
|
+
return url
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function directHubEvent(serverSocket, argv){
|
|
25
|
+
serverSocket.on('connection', (clientSocket) => {
|
|
26
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
27
|
+
console.log('BizA Client Socket', clientSocket.id)
|
|
28
|
+
clientSocket.on('disconnect', (reason)=>{console.log(`Socket ${clientSocket.id} disconnected. Reason : ${reason}`)})
|
|
29
|
+
}
|
|
30
|
+
clientSocket.on('APISocket', (reqData, resCB)=>{
|
|
31
|
+
const socketResponse = (resp)=>{return {status: resp.status, statusText: resp.statusText, headers: resp.headers, body: resp.data, url: `${argv['hostname']}:${argv['port']+resp.config.url}`}}
|
|
32
|
+
if (argv['subdomain'].localeCompare(reqData.subDomain)==0) {
|
|
33
|
+
const apiAddress = `${argv['secure']==true ? 'https://' : 'http://'}${argv['hostname']}:${argv['port']}`
|
|
34
|
+
axios.request({
|
|
35
|
+
timeout: IDLE_SOCKET_TIMEOUT_MILLISECONDS,
|
|
36
|
+
baseURL: apiAddress,
|
|
37
|
+
url: reqData.path,
|
|
38
|
+
method: reqData.method,
|
|
39
|
+
headers: reqData.headers,
|
|
40
|
+
data: reqData.body,
|
|
41
|
+
// decompress: false, // if we need to interfered default Agent compression
|
|
42
|
+
responseType: reqData.responseType,
|
|
43
|
+
maxContentLength: Infinity,
|
|
44
|
+
})
|
|
45
|
+
.then(response=>{
|
|
46
|
+
resCB(null, socketResponse(response))
|
|
47
|
+
})
|
|
48
|
+
.catch(error=>{
|
|
49
|
+
resCB(error, null)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
resCB({status: 401, statusText: 'bad subdomain', url: `${argv['hostname']}:${argv['port']+reqData.path}`}, null)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
}
|
package/bin/hub.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import yargs from 'yargs';
|
|
4
|
-
import { io as
|
|
4
|
+
import { io as ioClient } from "socket.io-client";
|
|
5
5
|
import hubEvent from './hubEvent.js'
|
|
6
6
|
import { createLogger, transports, format } from "winston";
|
|
7
|
+
import { Server as ioServer } from 'socket.io'
|
|
8
|
+
import os from 'node:os'
|
|
9
|
+
import { directHubEvent, localhostTunnel }from './directHubEvent.js'
|
|
7
10
|
|
|
8
11
|
const logger = createLogger({
|
|
9
12
|
level: 'info',
|
|
@@ -27,7 +30,7 @@ process.on('unhandledRejection', (err) => { //debug
|
|
|
27
30
|
logger.error('Unhandled Rejection:', err)
|
|
28
31
|
});
|
|
29
32
|
|
|
30
|
-
const
|
|
33
|
+
const defaultPort = 3002;
|
|
31
34
|
const argv = yargs(process.argv.slice(2))
|
|
32
35
|
.usage('Usage: $0 [options]')
|
|
33
36
|
.options('s', {
|
|
@@ -39,34 +42,46 @@ const argv = yargs(process.argv.slice(2))
|
|
|
39
42
|
})
|
|
40
43
|
.options('sub', {
|
|
41
44
|
alias: 'subdomain',
|
|
42
|
-
describe: '
|
|
45
|
+
describe: 'Public URL the tunnel server is forwarding to us',
|
|
43
46
|
type: 'string',
|
|
44
47
|
demandOption: true
|
|
45
48
|
})
|
|
46
49
|
.options('h', {
|
|
47
50
|
alias: 'hostname',
|
|
48
51
|
default: '127.0.0.1',
|
|
49
|
-
describe: '
|
|
52
|
+
describe: 'Address of API server for forwarding over socket-tunnel. Please emit "HTTP" or "HTTPS" from address',
|
|
50
53
|
type: 'string',
|
|
51
54
|
demandOption: true
|
|
52
55
|
})
|
|
53
|
-
.options('d', {
|
|
54
|
-
alias: 'dbindex',
|
|
55
|
-
default: 2,
|
|
56
|
-
describe: '(Required) Biz-A Database Index (Callback Feature)',
|
|
57
|
-
type: 'number',
|
|
58
|
-
demandOption: false
|
|
59
|
-
})
|
|
60
56
|
.options('p', {
|
|
61
57
|
alias: 'port',
|
|
62
58
|
default: 212,
|
|
63
|
-
describe: 'Port of
|
|
59
|
+
describe: 'Port of API server for forwarding over socket-tunnel',
|
|
60
|
+
type: 'number',
|
|
61
|
+
demandOption: false
|
|
62
|
+
})
|
|
63
|
+
.options('secure', {
|
|
64
|
+
default: false,
|
|
65
|
+
describe: 'Is API server using ssl (HTTPS) ?',
|
|
66
|
+
type: 'boolean',
|
|
67
|
+
demandOption: false
|
|
68
|
+
})
|
|
69
|
+
.options('publish', {
|
|
70
|
+
default: true,
|
|
71
|
+
describe: 'Will the CLI be published to the internet??',
|
|
72
|
+
type: 'boolean',
|
|
73
|
+
demandOption: false
|
|
74
|
+
})
|
|
75
|
+
.options('d', {
|
|
76
|
+
alias: 'dbindex',
|
|
77
|
+
default: 2,
|
|
78
|
+
describe: 'Biz-A Database Index (Callback Feature)',
|
|
64
79
|
type: 'number',
|
|
65
80
|
demandOption: false
|
|
66
81
|
})
|
|
67
82
|
.options('sp', {
|
|
68
83
|
alias: 'serverport',
|
|
69
|
-
default:
|
|
84
|
+
default: defaultPort,
|
|
70
85
|
describe: 'Express Port (Callback Feature)',
|
|
71
86
|
type: 'number',
|
|
72
87
|
demandOption: false
|
|
@@ -89,12 +104,15 @@ if (!argv['server'] || !argv['subdomain'] || !argv['port']) {
|
|
|
89
104
|
}
|
|
90
105
|
}
|
|
91
106
|
|
|
92
|
-
//
|
|
93
107
|
import express from 'express';
|
|
94
108
|
import compression from 'compression';
|
|
95
109
|
import cors from 'cors';
|
|
96
110
|
const app = express();
|
|
97
111
|
import { runCliScript } from '../callbackController.js'
|
|
112
|
+
import fs from "fs"
|
|
113
|
+
import http from 'http'
|
|
114
|
+
import https from 'https'
|
|
115
|
+
import path from 'node:path'
|
|
98
116
|
|
|
99
117
|
app.use(compression());
|
|
100
118
|
app.use(cors());
|
|
@@ -102,17 +120,42 @@ app.use(express.json({ limit: '100mb' }));
|
|
|
102
120
|
|
|
103
121
|
app.set('args', argv);
|
|
104
122
|
|
|
105
|
-
|
|
106
123
|
app.use('/cb', runCliScript);
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
|
|
125
|
+
|
|
126
|
+
// create HTTP(s) Server
|
|
127
|
+
const keyFile = path.join(import.meta.dirname, "../cert/key.pem")
|
|
128
|
+
const certFile = path.join(import.meta.dirname, "../cert/cert.pem")
|
|
129
|
+
const rootFile = path.join(import.meta.dirname, "../cert/root.pem")
|
|
130
|
+
const isHttps = (fs.existsSync(keyFile) && fs.existsSync(certFile) && fs.existsSync(rootFile))
|
|
131
|
+
const getProtocol = ()=>(isHttps ? 'Https://' : 'Http://')
|
|
132
|
+
let server = isHttps ? https.createServer({key: fs.readFileSync(keyFile), cert: fs.readFileSync(certFile), ca: fs.readFileSync(rootFile),}, app) : http.createServer(app)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
// publish CLI with tunnel
|
|
136
|
+
argv.publicUrl = (argv.publish==true) ? await localhostTunnel(argv.serverport) : ''
|
|
137
|
+
|
|
138
|
+
// prepare CLI Address
|
|
139
|
+
argv.cliAddress = ()=>{
|
|
140
|
+
const ip = Object.values(os.networkInterfaces()).flat().reduce((ip, {family, address, internal})=> ip || !internal && family==='IPv4' && address, undefined)
|
|
141
|
+
return {ip, port: argv.serverport, address: `${ip}:${argv.serverport}`, publicUrl: argv.publicUrl}
|
|
142
|
+
}
|
|
143
|
+
const cliAddress = argv.cliAddress()
|
|
144
|
+
server.listen(argv.serverport, () => {
|
|
145
|
+
console.log(`${new Date()}: CLI is listening at ${getProtocol() + (process.env.HOST || cliAddress.ip || 'localhost')}:${cliAddress.port} `);
|
|
110
146
|
});
|
|
111
|
-
//
|
|
112
147
|
|
|
113
|
-
let socket = ioc(argv['server']);
|
|
114
|
-
hubEvent(socket, argv);
|
|
115
148
|
|
|
116
|
-
|
|
149
|
+
// BizA CLI as socket client to BizA SERVER
|
|
150
|
+
await hubEvent(ioClient(argv['server'], {query: {cliAddress: cliAddress.address }}), argv);
|
|
151
|
+
console.log(`${new Date()}: CLI connected to BizA Server using sub domain "${argv['subdomain']}"`)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
// BizA CLI as socket server for BizA CLIENT
|
|
155
|
+
const serverCORSOrigin = ['https://biz-a.id', 'https://test.biz-a.id', /\.biz-a\.id$/].concat((process.env.NODE_ENV === 'production') ? [] : [`http://${cliAddress.ip}:4200`, 'http://localhost:4200'])
|
|
156
|
+
// console.log('Allowed Origins', serverCORSOrigin)
|
|
157
|
+
directHubEvent(new ioServer(server, {cors: {origin: serverCORSOrigin}}), argv)
|
|
158
|
+
console.log(`${new Date()}: CLI is listening at "${cliAddress.publicUrl ? cliAddress.publicUrl : cliAddress.address}"`)
|
|
159
|
+
|
|
117
160
|
|
|
118
161
|
export { app }
|
package/bin/hubEvent.js
CHANGED
|
@@ -1,62 +1,112 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import net from 'node:net';
|
|
3
|
+
import tls from 'node:tls';
|
|
3
4
|
import { createRequire } from "module";
|
|
4
5
|
const require = createRequire(import.meta.url);
|
|
5
6
|
const ss = require('socket.io-stream'); //SCY: Temporary, next will be replaced with import
|
|
7
|
+
import { Transform } from 'node:stream'
|
|
8
|
+
// import { pipeline } from 'node:stream'
|
|
6
9
|
|
|
7
|
-
const IDLE_SOCKET_TIMEOUT_MILLISECONDS = 1000 * 30;
|
|
10
|
+
export const IDLE_SOCKET_TIMEOUT_MILLISECONDS = 1000 * 30;
|
|
11
|
+
|
|
12
|
+
export const socketAgent = isUsingHttps=>(isUsingHttps==true) ? tls : net
|
|
8
13
|
|
|
9
14
|
export default async (socket, argv) => new Promise((resolve, reject) => {
|
|
15
|
+
|
|
10
16
|
const connectCb = () => {
|
|
11
|
-
console.log(new Date() + ': connected to
|
|
12
|
-
console.log(new Date() + ': requesting subdomain ' + argv['subdomain'] + ' via ' + argv['server']);
|
|
17
|
+
// console.log(new Date() + ': connected to BizA Server');
|
|
18
|
+
// console.log(new Date() + ': requesting subdomain ' + argv['subdomain'] + ' via ' + argv['server']);
|
|
13
19
|
|
|
14
20
|
socket.emit('createTunnel', argv['subdomain'], (err) => {
|
|
15
21
|
if (err) {
|
|
16
22
|
console.log(new Date() + ': [error] ' + err);
|
|
17
23
|
reject(err);
|
|
18
24
|
} else {
|
|
19
|
-
console.log(new Date() + ': registered with server successfully');
|
|
25
|
+
// console.log(new Date() + ': registered with server successfully');
|
|
20
26
|
|
|
21
|
-
// clean and concat requested url
|
|
22
|
-
let url;
|
|
23
|
-
// let subdomain = argv['subdomain'].toString();
|
|
24
|
-
let server = argv['server'].toString();
|
|
27
|
+
// // clean and concat requested url
|
|
28
|
+
// let url;
|
|
29
|
+
// // let subdomain = argv['subdomain'].toString();
|
|
30
|
+
// let server = argv['server'].toString();
|
|
25
31
|
|
|
26
|
-
url = server;
|
|
32
|
+
// url = server;
|
|
27
33
|
|
|
28
|
-
// resolve promise with requested URL
|
|
29
|
-
resolve(url);
|
|
34
|
+
// // resolve promise with requested URL
|
|
35
|
+
// resolve(url);
|
|
36
|
+
|
|
37
|
+
resolve(argv['server'].toString());
|
|
30
38
|
}
|
|
31
39
|
});
|
|
32
40
|
}
|
|
41
|
+
|
|
33
42
|
const incomingHubCb = (clientId) => {
|
|
34
|
-
console.log(clientId, 'incoming clientId')
|
|
35
|
-
|
|
43
|
+
// console.log(clientId, 'incoming clientId')
|
|
44
|
+
|
|
45
|
+
let addCLIAddressAsResponseHeader = new Transform({
|
|
46
|
+
transform(chunk, encoding, next){
|
|
47
|
+
const apiResponse = chunk.toString().toLowerCase()
|
|
48
|
+
const cliAddress = argv.cliAddress()
|
|
49
|
+
if ( (apiResponse.indexOf('200 ok') > -1) && (apiResponse.indexOf('server: datasnaphttpservice') > -1) && (cliAddress.publicUrl || cliAddress.address)) {
|
|
50
|
+
// don't use string to insert additional headers, chunk can have mixed content of string and binary data
|
|
51
|
+
const response = Buffer.from(chunk)
|
|
52
|
+
const delimiter = '\r\n\r\n'
|
|
53
|
+
const delimiterPos = response.indexOf(delimiter)
|
|
54
|
+
const header = Buffer.concat([
|
|
55
|
+
Buffer.copyBytesFrom(response, 0, delimiterPos),
|
|
56
|
+
Buffer.from(
|
|
57
|
+
'\r\n' +
|
|
58
|
+
'Access-Control-Expose-Headers: biza-cli-address\r\n' +
|
|
59
|
+
`biza-cli-address: ${cliAddress.publicUrl ? cliAddress.publicUrl : cliAddress.address}\r\n` +
|
|
60
|
+
'\r\n'
|
|
61
|
+
)
|
|
62
|
+
])
|
|
63
|
+
const body = (response.length>delimiterPos+delimiter.length) ? Buffer.copyBytesFrom(response, delimiterPos+4) : Buffer.from('')
|
|
64
|
+
this.push(body.length>0 ? Buffer.concat([header, body]) : header)
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.push(chunk)
|
|
68
|
+
}
|
|
69
|
+
next()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
36
72
|
|
|
73
|
+
// let client = net.connect(argv['port'], argv['hostname']);
|
|
74
|
+
let client = socketAgent(argv['secure']).connect(argv['port'], argv['hostname']);
|
|
37
75
|
client.on('connect', () => {
|
|
38
|
-
console.log(`client connected to ${argv['hostname']}:${argv['port']}`)
|
|
76
|
+
// console.log(`client connected to ${argv['hostname']}:${argv['port']}`)
|
|
39
77
|
let s = ss.createStream();
|
|
40
|
-
|
|
78
|
+
|
|
79
|
+
// s.pipe(client).pipe(s);
|
|
80
|
+
s.pipe(client).pipe(addCLIAddressAsResponseHeader).pipe(s)
|
|
81
|
+
|
|
82
|
+
// use pipeline for better performance, back pressure, memory management, error handling and clean up
|
|
83
|
+
// pipeline(s, client, addCLIAddressAsResponseHeader, s, (err)=>{if (err) {console.error(err)}}) // Not work well with heroku
|
|
41
84
|
|
|
42
85
|
s.on('end', () => {
|
|
43
|
-
client.destroy()
|
|
44
|
-
})
|
|
86
|
+
client.destroy()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
socket.once(clientId, ()=>{ // hub server shall notify us to end pipeline as soon as possible
|
|
90
|
+
client.end()
|
|
91
|
+
})
|
|
92
|
+
|
|
45
93
|
ss(socket).emit(clientId, s);
|
|
46
94
|
})
|
|
47
95
|
|
|
48
96
|
client.setTimeout(IDLE_SOCKET_TIMEOUT_MILLISECONDS);
|
|
49
|
-
client.on('timeout', () => {
|
|
50
|
-
client.end()
|
|
97
|
+
client.on('timeout', () => { // in case client not notify to end pipeline, then inactivity timeout will end it
|
|
98
|
+
client.end()
|
|
51
99
|
});
|
|
52
100
|
|
|
53
|
-
client.on('error', () => {
|
|
101
|
+
client.on('error', (err) => {
|
|
54
102
|
// handle connection refusal (create a stream and immediately close it)
|
|
103
|
+
console.error('API Error : ', err)
|
|
55
104
|
let s = ss.createStream();
|
|
56
105
|
ss(socket).emit(clientId, s);
|
|
57
106
|
s.end();
|
|
58
107
|
});
|
|
59
108
|
}
|
|
109
|
+
|
|
60
110
|
const cliReqCb = async (data, callback) => {
|
|
61
111
|
const { path, method, ...remainData } = data;
|
|
62
112
|
|
|
@@ -67,6 +117,7 @@ export default async (socket, argv) => new Promise((resolve, reject) => {
|
|
|
67
117
|
})
|
|
68
118
|
callback(result.data);
|
|
69
119
|
}
|
|
120
|
+
|
|
70
121
|
socket.on('connect', connectCb);
|
|
71
122
|
socket.on('incomingClient', incomingHubCb)
|
|
72
123
|
socket.on('cli-req', cliReqCb);
|
package/mailController.js
CHANGED
|
@@ -6,7 +6,7 @@ function createEmailTransport(req, config) {
|
|
|
6
6
|
);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export async function
|
|
9
|
+
export async function sendMailCliScript(req) {
|
|
10
10
|
try {
|
|
11
11
|
const transporter = createEmailTransport(req, req.body.config);
|
|
12
12
|
const sendResult = await transporter.sendMail(req.body.mailOptions);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "biz-a-cli",
|
|
3
3
|
"nameDev": "biz-a-cli-dev",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.52",
|
|
5
5
|
"versionDev": "0.0.30",
|
|
6
6
|
"description": "",
|
|
7
7
|
"main": "bin/index.js",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"biza": "bin/app.js"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"axios": "^1.
|
|
31
|
+
"axios": "^1.7.8",
|
|
32
|
+
"cloudflared": "^0.6.0",
|
|
32
33
|
"compression": "^1.7.5",
|
|
33
34
|
"cors": "^2.8.5",
|
|
34
35
|
"dayjs": "^1.11.10",
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
"mongodb": "^6.5.0",
|
|
37
38
|
"net": "^1.0.2",
|
|
38
39
|
"nodemailer": "^6.9.12",
|
|
40
|
+
"socket.io": "^4.7.5",
|
|
39
41
|
"socket.io-client": "^4.7.5",
|
|
40
42
|
"socket.io-stream": "^0.9.1",
|
|
41
43
|
"tar": "^7.4.0",
|
|
@@ -45,8 +47,7 @@
|
|
|
45
47
|
"yargs": "^17.7.2"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|
|
48
|
-
"jest": "^29.7.0"
|
|
49
|
-
"socket.io": "^4.7.5"
|
|
50
|
+
"jest": "^29.7.0"
|
|
50
51
|
},
|
|
51
52
|
"jest": {
|
|
52
53
|
"transform": {},
|
package/readme.md
CHANGED
|
@@ -61,4 +61,25 @@
|
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
> [!CAUTION]
|
|
64
|
-
> **The *uploadBizA*, *uploadApp*, *deleteApp* commands are deprecated and will be removed in the future.**
|
|
64
|
+
> **The *uploadBizA*, *uploadApp*, *deleteApp* commands are deprecated and will be removed in the future.**
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## III. Hub
|
|
68
|
+
|
|
69
|
+
### a. Using HTTP
|
|
70
|
+
hub --server [BizA Hub Server] --sub [subdomain] --hostname [ip_API] --port [port_API]
|
|
71
|
+
|
|
72
|
+
Example :
|
|
73
|
+
hub --server https://biz-a.herokuapp.com --sub imamatek --hostname localhost --port 212
|
|
74
|
+
|
|
75
|
+
### b. Using HTTPs (SSL)
|
|
76
|
+
hub --server [BizA Hub Server] --sub [subdomain] --hostname [ip_API] --port [port_API] --secure
|
|
77
|
+
|
|
78
|
+
Example :
|
|
79
|
+
hub --server https://biz-a.herokuapp.com --sub imamatek --hostname localhost --port 212 --secure
|
|
80
|
+
|
|
81
|
+
### c. Publish Hub
|
|
82
|
+
hub --server [BizA Hub Server] --sub [subdomain] --hostname [ip_API] --port [port_API] --publish [true]
|
|
83
|
+
|
|
84
|
+
Example :
|
|
85
|
+
hub --server https://biz-a.herokuapp.com --sub imamatek --hostname localhost --port 212 --publish=false
|
package/scheduler/datalib.js
CHANGED
|
@@ -221,13 +221,7 @@ export function getInputData(config, trigger) {
|
|
|
221
221
|
dbindex: config.dbindex,
|
|
222
222
|
finaDbIndex: config.finaDbIndex,
|
|
223
223
|
subdomain: config.subdomain,
|
|
224
|
-
smtp:
|
|
225
|
-
user: config.smtp.auth.user,
|
|
226
|
-
pass: config.smtp.auth.pass,
|
|
227
|
-
host: config.smtp.host,
|
|
228
|
-
port: config.smtp.port,
|
|
229
|
-
secure: config.smtp.secure
|
|
230
|
-
}
|
|
224
|
+
smtp: config.smtp
|
|
231
225
|
},
|
|
232
226
|
body: trigger.data
|
|
233
227
|
}
|
|
@@ -235,7 +229,7 @@ export function getInputData(config, trigger) {
|
|
|
235
229
|
|
|
236
230
|
export async function scheduleSubscription(config, data, trigger, needSetHistory, isTest = false) {
|
|
237
231
|
try {
|
|
238
|
-
let functions = extractFunctionScript(config, data);
|
|
232
|
+
let functions = await extractFunctionScript(config, data);
|
|
239
233
|
|
|
240
234
|
if (functions) {
|
|
241
235
|
if (!isTest) { // next, change isTest to test better
|
package/tests/data.test.js
CHANGED
|
@@ -22,7 +22,8 @@ const {
|
|
|
22
22
|
getUrlAndParam,
|
|
23
23
|
getTableObj,
|
|
24
24
|
options,
|
|
25
|
-
setLibrary
|
|
25
|
+
setLibrary,
|
|
26
|
+
extractFunctionScript
|
|
26
27
|
} = await import('../scheduler/datalib.js');
|
|
27
28
|
|
|
28
29
|
describe('data test', () => {
|
|
@@ -44,7 +45,7 @@ describe('data test', () => {
|
|
|
44
45
|
" }" +
|
|
45
46
|
"}";
|
|
46
47
|
|
|
47
|
-
test('check schedule subscription no history', () => {
|
|
48
|
+
test('check schedule subscription no history', async () => {
|
|
48
49
|
const config = {
|
|
49
50
|
_id: 'ffffffff2ae49fab9ea654e1',
|
|
50
51
|
url: 'http://localhost:212',
|
|
@@ -59,11 +60,11 @@ describe('data test', () => {
|
|
|
59
60
|
name: 'New Watcher 1',
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
scheduleSubscription(config, data, trigger, false, true);
|
|
63
|
+
await scheduleSubscription(config, data, trigger, false, true);
|
|
63
64
|
expect(mockInsertHistory).toBeCalledTimes(0);
|
|
64
65
|
});
|
|
65
66
|
|
|
66
|
-
test('check schedule subscription with history', () => {
|
|
67
|
+
test('check schedule subscription with history', async () => {
|
|
67
68
|
const config = {
|
|
68
69
|
_id: 'ffffffff2ae49fab9ea654e1',
|
|
69
70
|
url: 'http://localhost:212',
|
|
@@ -79,7 +80,7 @@ describe('data test', () => {
|
|
|
79
80
|
name: 'New Watcher 1'
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
scheduleSubscription(config, data, trigger, true, true);
|
|
83
|
+
await scheduleSubscription(config, data, trigger, true, true);
|
|
83
84
|
expect(mockInsertHistory).toBeCalledTimes(1);
|
|
84
85
|
});
|
|
85
86
|
|
|
@@ -111,21 +112,12 @@ describe('data test', () => {
|
|
|
111
112
|
arguments: {
|
|
112
113
|
hostname: 'localhost',
|
|
113
114
|
port: 212,
|
|
114
|
-
dbindex:
|
|
115
|
-
finaDbIndex:
|
|
116
|
-
subdomain:
|
|
117
|
-
smtp:
|
|
118
|
-
user: 'abc',
|
|
119
|
-
pass: '123',
|
|
120
|
-
host: 'mail.imamatek.com',
|
|
121
|
-
port: 465,
|
|
122
|
-
secure: true
|
|
123
|
-
}
|
|
115
|
+
dbindex: config.dbindex,
|
|
116
|
+
finaDbIndex: config.finaDbIndex,
|
|
117
|
+
subdomain: config.subdomain,
|
|
118
|
+
smtp: config.smtp
|
|
124
119
|
},
|
|
125
|
-
body:
|
|
126
|
-
_id: 1,
|
|
127
|
-
name: 'New Watcher 1'
|
|
128
|
-
}
|
|
120
|
+
body: trigger.data
|
|
129
121
|
});
|
|
130
122
|
});
|
|
131
123
|
|
|
@@ -346,6 +338,81 @@ describe('data test', () => {
|
|
|
346
338
|
expect(result.libCb.zzz()).toStrictEqual({ "defA": "A", "defB": "B" });
|
|
347
339
|
});
|
|
348
340
|
|
|
341
|
+
test('should return undefined when data has error', async () => {
|
|
342
|
+
const config = {};
|
|
343
|
+
const data = { error: 'error' };
|
|
344
|
+
|
|
345
|
+
const actual = await extractFunctionScript(config, data);
|
|
346
|
+
const expected = undefined;
|
|
347
|
+
|
|
348
|
+
expect(actual).toStrictEqual(expected);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('should return undefined when no data', async () => {
|
|
352
|
+
const config = {};
|
|
353
|
+
const data = [];
|
|
354
|
+
|
|
355
|
+
const actual = await extractFunctionScript(config, data);
|
|
356
|
+
const expected = undefined;
|
|
357
|
+
|
|
358
|
+
expect(actual).toStrictEqual(expected);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test('should return undefined when no functions', async () => {
|
|
362
|
+
const config = {};
|
|
363
|
+
const data = [{
|
|
364
|
+
script: 'get = function () {\n' +
|
|
365
|
+
' return {}\n' +
|
|
366
|
+
'}'
|
|
367
|
+
}];
|
|
368
|
+
|
|
369
|
+
const actual = await extractFunctionScript(config, data);
|
|
370
|
+
const expected = undefined;
|
|
371
|
+
|
|
372
|
+
expect(actual).toStrictEqual(expected);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test('should return onInit', async () => {
|
|
376
|
+
const config = {};
|
|
377
|
+
const data = [{
|
|
378
|
+
script: 'get = function () {\n' +
|
|
379
|
+
' return {\n' +
|
|
380
|
+
' functions: {\n' +
|
|
381
|
+
' onInit: function (data) {\n' +
|
|
382
|
+
" return 'OK'\n" +
|
|
383
|
+
' }\n' +
|
|
384
|
+
' }\n' +
|
|
385
|
+
' }\n' +
|
|
386
|
+
'}'
|
|
387
|
+
}];
|
|
388
|
+
|
|
389
|
+
const actual = await extractFunctionScript(config, data);
|
|
390
|
+
const expected = 'OK';
|
|
391
|
+
|
|
392
|
+
expect(actual.onInit()).toStrictEqual(expected);
|
|
393
|
+
});
|
|
349
394
|
|
|
395
|
+
// test('should return useLibrary', async () => { // not work, cannot mock useLibrary. Try later
|
|
396
|
+
// const config = {};
|
|
397
|
+
// const data = [{
|
|
398
|
+
// script: 'get = function () {\n' +
|
|
399
|
+
// ' return {\n' +
|
|
400
|
+
// ' functions: {\n' +
|
|
401
|
+
// ' onInit: function (data) {\n' +
|
|
402
|
+
// " return 'OK'\n" +
|
|
403
|
+
// ' },\n' +
|
|
404
|
+
// ' useLibrary: function () {\n' +
|
|
405
|
+
// ' return ["lib"];\n' +
|
|
406
|
+
// ' }\n' +
|
|
407
|
+
// ' }\n' +
|
|
408
|
+
// ' }\n' +
|
|
409
|
+
// '}'
|
|
410
|
+
// }];
|
|
411
|
+
|
|
412
|
+
// const actual = await extractFunctionScript(config, data);
|
|
413
|
+
// const expected = 'OK';
|
|
414
|
+
|
|
415
|
+
// expect(actual.onInit()).toStrictEqual(expected);
|
|
416
|
+
// });
|
|
350
417
|
})
|
|
351
418
|
|
package/tests/mailCtl.test.js
CHANGED
|
@@ -11,12 +11,12 @@ jest.unstable_mockModule('nodemailer', () => ({
|
|
|
11
11
|
createTransport: (data) => mockCreateTransport(data)
|
|
12
12
|
}))
|
|
13
13
|
|
|
14
|
-
const {
|
|
14
|
+
const { sendMailCliScript } = await import('../mailController.js');
|
|
15
15
|
|
|
16
16
|
describe('Mail Controller', () => {
|
|
17
17
|
let req;
|
|
18
18
|
|
|
19
|
-
test('transporter.
|
|
19
|
+
test('transporter.sendMailCliScript is called', async () => {
|
|
20
20
|
req = {
|
|
21
21
|
body: {
|
|
22
22
|
companyname: 'abc',
|
|
@@ -27,12 +27,12 @@ describe('Mail Controller', () => {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
mockSendMail.mockResolvedValue('OK');
|
|
30
|
-
expect(await
|
|
30
|
+
expect(await sendMailCliScript(req)).toEqual('OK');
|
|
31
31
|
expect(mockSendMail).toBeCalledTimes(1);
|
|
32
32
|
expect(mockCreateTransport).toHaveBeenCalledWith({ smtp: 'test' });
|
|
33
33
|
|
|
34
34
|
mockSendMail.mockRejectedValue({ message: 'error' });
|
|
35
|
-
expect(await
|
|
35
|
+
expect(await sendMailCliScript(req)).toEqual('error');
|
|
36
36
|
|
|
37
37
|
expect(mockSendMail).toBeCalledTimes(2);
|
|
38
38
|
})
|