biz-a-cli 2.3.73 → 2.3.74
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 +23 -0
- package/bin/hub.js +42 -2
- package/bin/hubEvent.js +100 -35
- package/bin/migrate.js +169 -0
- package/callbackController.js +58 -36
- package/db/db.js +37 -0
- package/db/ds.js +189 -0
- package/engine/bpm/routes.js +61 -0
- package/engine/bpm/workflow-runtime.js +1136 -0
- package/engine/bpm/workflow.js +235 -0
- package/migrations/1777727873750__apps.sql +16 -0
- package/migrations/1777727892577__bpm.sql +274 -0
- package/package.json +17 -2
- package/scheduler/datalib.js +266 -182
- package/worker/cliScriptWorker.js +55 -22
- package/worker/cliWorkerPool.js +63 -10
- package/.editorconfig +0 -16
- package/log/debug.log +0 -0
- package/log/error.log +0 -0
- package/log/exception.log +0 -0
- package/log/info.log +0 -0
- package/tests/app.test.js +0 -1208
- package/tests/callback.test.js +0 -42
- package/tests/config.test.js +0 -39
- package/tests/converter.test.js +0 -106
- package/tests/data.test.js +0 -487
- package/tests/deployment.test.js +0 -339
- package/tests/hub.test.js +0 -998
- package/tests/hubPublish.test.js +0 -231
- package/tests/mailCtl.test.js +0 -44
- package/tests/timer.test.js +0 -187
- package/tests/watcher.test.js +0 -352
- package/tests/watcherCtl.test.js +0 -124
package/bin/app.js
CHANGED
|
@@ -14,6 +14,7 @@ import path, { basename } from "node:path";
|
|
|
14
14
|
import { env } from "../envs/env.js";
|
|
15
15
|
import { prepareScript, encryptScript } from "./script.js";
|
|
16
16
|
import { spawn } from "node:child_process";
|
|
17
|
+
import { newMigration, runMigrations } from "./migrate.js";
|
|
17
18
|
|
|
18
19
|
const getKeyFolderPath = () => {
|
|
19
20
|
const scriptPath =
|
|
@@ -654,6 +655,28 @@ const buildCli = () =>
|
|
|
654
655
|
})();
|
|
655
656
|
},
|
|
656
657
|
)
|
|
658
|
+
.command(
|
|
659
|
+
"migrate:new",
|
|
660
|
+
"Create a new blank migration file with an epoch timestamp",
|
|
661
|
+
(yargs) => {
|
|
662
|
+
return yargs.option("n", {
|
|
663
|
+
alias: "name",
|
|
664
|
+
describe:
|
|
665
|
+
'Name of the migration feature (e.g., "BPM", "Domain", "new feature_name")',
|
|
666
|
+
type: "string",
|
|
667
|
+
demandOption: true,
|
|
668
|
+
});
|
|
669
|
+
},
|
|
670
|
+
(commandOptions) => newMigration(commandOptions),
|
|
671
|
+
)
|
|
672
|
+
.command(
|
|
673
|
+
"migrate:run",
|
|
674
|
+
"Run pending DDL migrations from the /migrations folder",
|
|
675
|
+
{},
|
|
676
|
+
async (commandOptions) => {
|
|
677
|
+
await runMigrations(commandOptions);
|
|
678
|
+
},
|
|
679
|
+
)
|
|
657
680
|
.recommendCommands()
|
|
658
681
|
.demandCommand(1, "You need at least one command before moving on")
|
|
659
682
|
.strict();
|
package/bin/hub.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
createSocketServer,
|
|
21
21
|
} from "./directHubEvent.js";
|
|
22
22
|
import { env } from "../envs/env.js";
|
|
23
|
+
import { setGlobalConfig } from "../db/ds.js";
|
|
23
24
|
|
|
24
25
|
const logger = createLogger({
|
|
25
26
|
level: "info",
|
|
@@ -52,7 +53,7 @@ const argv = yargs(process.argv.slice(2))
|
|
|
52
53
|
.usage("Usage: $0 [options]")
|
|
53
54
|
.options("s", {
|
|
54
55
|
alias: "server",
|
|
55
|
-
default: env.BIZA_SERVER_LINK,
|
|
56
|
+
// default: env.BIZA_SERVER_LINK, // handle by serverMode
|
|
56
57
|
describe: "(Required) Tunnel server endpoint",
|
|
57
58
|
type: "string",
|
|
58
59
|
demandOption: false,
|
|
@@ -106,12 +107,48 @@ const argv = yargs(process.argv.slice(2))
|
|
|
106
107
|
})
|
|
107
108
|
.options("hs", {
|
|
108
109
|
alias: "hubServer",
|
|
109
|
-
default: `${env.BIZA_HUB_SERVER_LINK}`,
|
|
110
|
+
// default: `${env.BIZA_HUB_SERVER_LINK}`, // handle by serverMode
|
|
110
111
|
describe: "BizA hub",
|
|
111
112
|
type: "string",
|
|
112
113
|
demandOption: false,
|
|
114
|
+
})
|
|
115
|
+
.options("serverMode", {
|
|
116
|
+
alias: "sMode",
|
|
117
|
+
describe: "Backend server mode: prod, dev, or backup",
|
|
118
|
+
type: "string",
|
|
119
|
+
// choices: ["prod", "dev", "backup"],
|
|
120
|
+
choices: ["prod", "dev"],
|
|
121
|
+
default: "prod",
|
|
113
122
|
}).argv;
|
|
114
123
|
|
|
124
|
+
// Server endpoint mapping by mode
|
|
125
|
+
const SERVER_ENDPOINTS = {
|
|
126
|
+
prod: {
|
|
127
|
+
server: env.BIZA_SERVER_LINK,
|
|
128
|
+
hub: env.BIZA_HUB_SERVER_LINK,
|
|
129
|
+
},
|
|
130
|
+
dev: {
|
|
131
|
+
server: "https://devServer.biz-a.id",
|
|
132
|
+
hub: "https://devHub.biz-a.id",
|
|
133
|
+
},
|
|
134
|
+
// backup: {
|
|
135
|
+
// server: "https://backupServer.biz-a.id",
|
|
136
|
+
// hub: "https://backupHub.biz-a.id",
|
|
137
|
+
// },
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (!argv.server) {
|
|
141
|
+
argv.server =
|
|
142
|
+
SERVER_ENDPOINTS[argv.serverMode]?.server ||
|
|
143
|
+
SERVER_ENDPOINTS.prod.server;
|
|
144
|
+
}
|
|
145
|
+
if (!argv.hubServer) {
|
|
146
|
+
argv.hubServer =
|
|
147
|
+
SERVER_ENDPOINTS[argv.serverMode]?.hub || SERVER_ENDPOINTS.prod.hub;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
setGlobalConfig(argv);
|
|
151
|
+
|
|
115
152
|
if (argv.help) {
|
|
116
153
|
yargs().showHelp();
|
|
117
154
|
process.exit();
|
|
@@ -137,6 +174,7 @@ import fs from "fs";
|
|
|
137
174
|
import http from "http";
|
|
138
175
|
import https from "https";
|
|
139
176
|
import path from "node:path";
|
|
177
|
+
import bpmRoutes from "../engine/bpm/routes.js";
|
|
140
178
|
|
|
141
179
|
app.use(compression());
|
|
142
180
|
app.use(cors());
|
|
@@ -154,6 +192,8 @@ app.use("/status", (req, res) => {
|
|
|
154
192
|
res.status(200).json(status(argv));
|
|
155
193
|
});
|
|
156
194
|
|
|
195
|
+
app.use("/bpm", bpmRoutes);
|
|
196
|
+
|
|
157
197
|
// create HTTP(s) Server
|
|
158
198
|
const keyFile = path.join(import.meta.dirname, "../cert/key.pem");
|
|
159
199
|
const certFile = path.join(import.meta.dirname, "../cert/cert.pem");
|
package/bin/hubEvent.js
CHANGED
|
@@ -11,7 +11,7 @@ const packageJson = require("../package.json");
|
|
|
11
11
|
import { deploymentListenerForHubServer } from "./deployEvent.js";
|
|
12
12
|
import { execCLIScriptWorker, workerStats } from "../worker/cliWorkerPool.js";
|
|
13
13
|
|
|
14
|
-
export const IDLE_SOCKET_TIMEOUT_MILLISECONDS = 1000 *
|
|
14
|
+
export const IDLE_SOCKET_TIMEOUT_MILLISECONDS = 1000 * 28;
|
|
15
15
|
export const RECONNECT_SOCKET_DELAY = 60 * 1000;
|
|
16
16
|
const DISCONNECT_REASON_BY_SOCKET_SERVER = "io server disconnect";
|
|
17
17
|
|
|
@@ -131,15 +131,37 @@ export const streamEvent = async (socket, argv) =>
|
|
|
131
131
|
});
|
|
132
132
|
};
|
|
133
133
|
|
|
134
|
+
// const cliReqCb = async (data, callback) => {
|
|
135
|
+
// const { path, method, ...remainData } = data;
|
|
136
|
+
|
|
137
|
+
// const result = await axios.request({
|
|
138
|
+
// method: data.method,
|
|
139
|
+
// url: `${process.env.HOST || "http://localhost"}:${argv.serverport}/cb${path || ""}`,
|
|
140
|
+
// data: remainData,
|
|
141
|
+
// });
|
|
142
|
+
// callback(result.data);
|
|
143
|
+
// };
|
|
144
|
+
|
|
134
145
|
const cliReqCb = async (data, callback) => {
|
|
135
146
|
const { path, method, ...remainData } = data;
|
|
136
147
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
try {
|
|
149
|
+
const result = await axios.request({
|
|
150
|
+
method: data.method,
|
|
151
|
+
url: `${process.env.HOST || "http://localhost"}:${argv.serverport}/cb${path || ""}`,
|
|
152
|
+
data: remainData,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
callback(result.data);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error.response) {
|
|
158
|
+
callback(error.response.data);
|
|
159
|
+
} else {
|
|
160
|
+
callback(
|
|
161
|
+
`Proxy Error: Local CLI unreachable. ${error.message}`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
143
165
|
};
|
|
144
166
|
|
|
145
167
|
const publishReqCb = async (data, callback) => {
|
|
@@ -212,6 +234,7 @@ export const streamEvent = async (socket, argv) =>
|
|
|
212
234
|
socket.on("incomingClient", incomingHubCb);
|
|
213
235
|
socket.on("cli-req", cliReqCb);
|
|
214
236
|
socket.on("publish-req", publishReqCb);
|
|
237
|
+
socket.on("cliCommand", (data, cb) => handleCliCommand(data, cb, argv));
|
|
215
238
|
|
|
216
239
|
socket.on("disconnect", (reason) => {
|
|
217
240
|
console.log(
|
|
@@ -336,6 +359,75 @@ export const status = (argv) => {
|
|
|
336
359
|
};
|
|
337
360
|
};
|
|
338
361
|
|
|
362
|
+
const EngineRegistry = {
|
|
363
|
+
// ZERO-MEMORY LAZY REGISTRY. Lazy load the module into memory (Only impacts performance on the very first call)
|
|
364
|
+
"bpm/workflow": () => import("../engine/bpm/workflow.js"),
|
|
365
|
+
"bpm/workflowRT": () => import("../engine/bpm/workflow-runtime.js"),
|
|
366
|
+
// 'domain/system': () => import('../engine/domain/system.js') // example for Domain Engine
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const handleCliCommand = async (data, cb, argv) => {
|
|
370
|
+
try {
|
|
371
|
+
const command = data.command.trim().toLowerCase();
|
|
372
|
+
switch (command) {
|
|
373
|
+
case "status":
|
|
374
|
+
cb(null, status(argv));
|
|
375
|
+
break;
|
|
376
|
+
case "runcliscript":
|
|
377
|
+
const liveTriggerPayload = {
|
|
378
|
+
arguments: argv,
|
|
379
|
+
body: data.scriptData ?? null,
|
|
380
|
+
path: "/live-trigger",
|
|
381
|
+
query: { scriptName: data.scriptName },
|
|
382
|
+
headers: { "user-agent": "biz-a-client-socket" },
|
|
383
|
+
};
|
|
384
|
+
cb(
|
|
385
|
+
null,
|
|
386
|
+
await execCLIScriptWorker(
|
|
387
|
+
{
|
|
388
|
+
url: `${argv["secure"] == true ? "https://" : "http://"}${argv["hostname"]}:${argv["port"]}`,
|
|
389
|
+
dbindex: argv["dbindex"],
|
|
390
|
+
subdomain: argv["subdomain"],
|
|
391
|
+
},
|
|
392
|
+
data.scriptName,
|
|
393
|
+
liveTriggerPayload,
|
|
394
|
+
),
|
|
395
|
+
);
|
|
396
|
+
break;
|
|
397
|
+
case "engine": {
|
|
398
|
+
// Ex: Extracts "bpm/instance" and "initiate" from "bpm/instance/initiate"
|
|
399
|
+
const targetParts = data.target.split("/");
|
|
400
|
+
const methodName = targetParts.pop();
|
|
401
|
+
const modulePath = targetParts.join("/");
|
|
402
|
+
|
|
403
|
+
if (!EngineRegistry[modulePath]) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`Engine target '${modulePath}' is not registered on this CLI.`,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const engineModule = await EngineRegistry[modulePath]();
|
|
410
|
+
|
|
411
|
+
if (typeof engineModule[methodName] !== "function") {
|
|
412
|
+
throw new Error(
|
|
413
|
+
`Method '${methodName}' does not exist on target '${modulePath}'.`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const result = await engineModule[methodName](data.payload);
|
|
418
|
+
|
|
419
|
+
cb(null, { success: true, data: result });
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
default:
|
|
424
|
+
cb(`Unknown CLI command '${command}'`, null);
|
|
425
|
+
}
|
|
426
|
+
} catch (err) {
|
|
427
|
+
cb(err.message || err, null);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
339
431
|
export const clientListener = (socket, argv) => {
|
|
340
432
|
socket
|
|
341
433
|
.on("apiRequest", (reqData, resCB) => {
|
|
@@ -406,33 +498,6 @@ export const clientListener = (socket, argv) => {
|
|
|
406
498
|
}
|
|
407
499
|
})
|
|
408
500
|
.on("cliCommand", async (data, cb) => {
|
|
409
|
-
|
|
410
|
-
const command = data.command.trim().toLowerCase();
|
|
411
|
-
switch (
|
|
412
|
-
command //ensure case insensitive
|
|
413
|
-
) {
|
|
414
|
-
case "status":
|
|
415
|
-
cb(null, status(argv));
|
|
416
|
-
break;
|
|
417
|
-
case "runcliscript":
|
|
418
|
-
cb(
|
|
419
|
-
null,
|
|
420
|
-
await execCLIScriptWorker(
|
|
421
|
-
{
|
|
422
|
-
url: `${argv["secure"] == true ? "https://" : "http://"}${argv["hostname"]}:${argv["port"]}`,
|
|
423
|
-
dbindex: argv["dbindex"],
|
|
424
|
-
subdomain: argv["subdomain"],
|
|
425
|
-
},
|
|
426
|
-
data.scriptName,
|
|
427
|
-
data.scriptData,
|
|
428
|
-
),
|
|
429
|
-
);
|
|
430
|
-
break;
|
|
431
|
-
default:
|
|
432
|
-
cb(`Unknown CLI command '${command}'`, null);
|
|
433
|
-
}
|
|
434
|
-
} catch (err) {
|
|
435
|
-
cb(err.message || err, null);
|
|
436
|
-
}
|
|
501
|
+
handleCliCommand(data, cb, argv);
|
|
437
502
|
});
|
|
438
503
|
};
|
package/bin/migrate.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { queryData, save as dbSave, executeBlock } from "../db/db.js";
|
|
4
|
+
|
|
5
|
+
const flattenSql = (sqlString) => {
|
|
6
|
+
return sqlString
|
|
7
|
+
.replace(/--.*$/gm, "") // 1. Strip all SQL comments
|
|
8
|
+
.split(/\r?\n/) // 2. Split into array of individual lines
|
|
9
|
+
.map((line) => line.trim()) // 3. Trim leading/trailing indentation
|
|
10
|
+
.filter((line) => line.length > 0) // 4. Remove empty lines entirely
|
|
11
|
+
.join(" "); // 5. Join safely with exactly one space
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const newMigration = (argv) => {
|
|
15
|
+
const dir = path.resolve(process.cwd(), "migrations");
|
|
16
|
+
if (!fs.existsSync(dir)) {
|
|
17
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const safeName = argv.name
|
|
21
|
+
.trim()
|
|
22
|
+
.replace(/[\s-]+/g, "_")
|
|
23
|
+
.toLowerCase();
|
|
24
|
+
const filename = `${Date.now()}__${safeName}.sql`;
|
|
25
|
+
|
|
26
|
+
// Defensive Template: Bypasses are commented out by default.
|
|
27
|
+
// DDL must use `execute statement` inside the block to bypass compile-time checks.
|
|
28
|
+
const template = [
|
|
29
|
+
"-- To safely bypass this script if a main table already exists, uncomment the line below and replace MAIN_TABLE_NAME",
|
|
30
|
+
"-- -- @RunOnlyIfMissing: MAIN_TABLE_NAME",
|
|
31
|
+
"",
|
|
32
|
+
"EXECUTE BLOCK AS",
|
|
33
|
+
"BEGIN",
|
|
34
|
+
" -- Write your DDL/SQL here using execute statement",
|
|
35
|
+
" -- execute statement 'CREATE TABLE YOUR_TABLE(ID INT NOT NULL PRIMARY KEY);';",
|
|
36
|
+
"END",
|
|
37
|
+
"",
|
|
38
|
+
].join("\n");
|
|
39
|
+
|
|
40
|
+
fs.writeFileSync(path.join(dir, filename), template);
|
|
41
|
+
console.log(`[Success] Created new migration file: migrations/${filename}`);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const runMigrations = async (argv) => {
|
|
45
|
+
const migrationsDir = path.resolve(process.cwd(), "migrations");
|
|
46
|
+
|
|
47
|
+
if (!fs.existsSync(migrationsDir)) {
|
|
48
|
+
console.log(
|
|
49
|
+
`[Migrate] No 'migrations' folder found at ${migrationsDir}`,
|
|
50
|
+
);
|
|
51
|
+
return { success: false, error: "Missing migrations folder" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const apiConfig = {
|
|
55
|
+
url: `${argv.server}:${argv.apiPort}`,
|
|
56
|
+
dbindex: argv.dbIndex,
|
|
57
|
+
subdomain: argv.subdomain,
|
|
58
|
+
timeout: 30000,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const bootstrapMigrationTable = async () => {
|
|
62
|
+
const bootstrapSql = `
|
|
63
|
+
EXECUTE BLOCK AS
|
|
64
|
+
BEGIN
|
|
65
|
+
IF (NOT EXISTS(SELECT 1 FROM RDB$RELATIONS WHERE UPPER(RDB$RELATION_NAME) = 'SYS$MIGRATIONS')) THEN
|
|
66
|
+
BEGIN
|
|
67
|
+
EXECUTE STATEMENT 'CREATE GENERATOR SYS$MIGRATIONS_GEN;';
|
|
68
|
+
EXECUTE STATEMENT 'CREATE TABLE SYS$MIGRATIONS(ID INTEGER NOT NULL PRIMARY KEY, FILE_NAME VARCHAR(255) NOT NULL, EXECUTED_AT TIMESTAMP NOT NULL, CONSTRAINT UNQ_SYS$MIGRATIONS_FILE UNIQUE (FILE_NAME));';
|
|
69
|
+
EXECUTE STATEMENT 'CREATE TRIGGER SYS$MIGRATIONS_BI FOR SYS$MIGRATIONS ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.ID IS NULL) THEN NEW.ID = GEN_ID(SYS$MIGRATIONS_GEN, 1); IF (NEW.EXECUTED_AT IS NULL) THEN NEW.EXECUTED_AT = ''NOW''; END';
|
|
70
|
+
END
|
|
71
|
+
END
|
|
72
|
+
`;
|
|
73
|
+
await executeBlock(flattenSql(bootstrapSql), apiConfig);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const files = fs
|
|
78
|
+
.readdirSync(migrationsDir)
|
|
79
|
+
.filter((f) => f.endsWith(".sql"))
|
|
80
|
+
.sort();
|
|
81
|
+
|
|
82
|
+
if (files.length === 0) {
|
|
83
|
+
console.log(`[Migrate] Migrations folder is empty. Nothing to do.`);
|
|
84
|
+
return { success: true };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(`[Migrate] Checking SYS$MIGRATIONS table...`);
|
|
88
|
+
|
|
89
|
+
await bootstrapMigrationTable();
|
|
90
|
+
|
|
91
|
+
const appliedParam = {
|
|
92
|
+
length: -1,
|
|
93
|
+
columns: [{ data: "SYS$MIGRATIONS.FILE_NAME", key: "file" }],
|
|
94
|
+
};
|
|
95
|
+
const appliedRes = await queryData(appliedParam, apiConfig, true);
|
|
96
|
+
const appliedFiles = Array.isArray(appliedRes)
|
|
97
|
+
? appliedRes.map((r) => r.file.trim())
|
|
98
|
+
: [];
|
|
99
|
+
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
if (appliedFiles.includes(file)) continue;
|
|
102
|
+
|
|
103
|
+
console.log(`[Migrate] Executing ${file}...`);
|
|
104
|
+
const sqlContent = fs.readFileSync(
|
|
105
|
+
path.join(migrationsDir, file),
|
|
106
|
+
"utf8",
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// BYPASS LOGIC: Evaluates strict start-of-line tag
|
|
110
|
+
const baselineMatch = sqlContent.match(
|
|
111
|
+
/^--\s*@RunOnlyIfMissing:\s*([A-Za-z0-9_$]+)/im,
|
|
112
|
+
);
|
|
113
|
+
if (baselineMatch) {
|
|
114
|
+
const targetTable = baselineMatch[1].toUpperCase();
|
|
115
|
+
|
|
116
|
+
const bypassCheckParam = {
|
|
117
|
+
length: 1,
|
|
118
|
+
filter: [
|
|
119
|
+
{
|
|
120
|
+
junction: "",
|
|
121
|
+
column: "RDB$RELATIONS.RDB$RELATION_NAME",
|
|
122
|
+
operator: "=",
|
|
123
|
+
value1: `'${targetTable}'`,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
columns: [
|
|
127
|
+
{
|
|
128
|
+
data: "RDB$RELATIONS.RDB$RELATION_NAME",
|
|
129
|
+
key: "name",
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const bypassCheckRes = await queryData(
|
|
135
|
+
bypassCheckParam,
|
|
136
|
+
apiConfig,
|
|
137
|
+
true,
|
|
138
|
+
);
|
|
139
|
+
if (bypassCheckRes && bypassCheckRes.length > 0) {
|
|
140
|
+
console.log(
|
|
141
|
+
`[Migrate] Bypass Triggered: ${targetTable} exists. Marking ${file} as done.`,
|
|
142
|
+
);
|
|
143
|
+
await dbSave(
|
|
144
|
+
{ SYS$MIGRATIONS: { id: null, file_name: file } },
|
|
145
|
+
apiConfig,
|
|
146
|
+
);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// mark migration current file as "Applied"
|
|
152
|
+
const trackerSql = `\n INSERT INTO SYS$MIGRATIONS (FILE_NAME) VALUES ('${file}');\n`;
|
|
153
|
+
let rawSql = sqlContent.replace(/END\s*$/i, trackerSql + "END");
|
|
154
|
+
|
|
155
|
+
await executeBlock(flattenSql(rawSql), apiConfig);
|
|
156
|
+
|
|
157
|
+
console.log(`[Migrate] Success: ${file}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log(`[Migrate] Database is up to date.`);
|
|
161
|
+
return { success: true };
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error(
|
|
164
|
+
`[Migrate] CRITICAL ERROR:`,
|
|
165
|
+
error?.response?.data?.error || error.message,
|
|
166
|
+
);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
};
|
package/callbackController.js
CHANGED
|
@@ -1,42 +1,64 @@
|
|
|
1
|
-
import { loadCliScript, extractFunctionScript } from "./scheduler/datalib.js";
|
|
2
|
-
|
|
1
|
+
// import { loadCliScript, extractFunctionScript } from "./scheduler/datalib.js";
|
|
2
|
+
import { execCLIScriptWorker } from "./worker/cliWorkerPool.js";
|
|
3
3
|
|
|
4
4
|
export function getInputScript(req) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
const args = req.app.settings.args;
|
|
6
|
+
return {
|
|
7
|
+
data: {
|
|
8
|
+
arguments: args,
|
|
9
|
+
body: req.body.body,
|
|
10
|
+
path: req.path,
|
|
11
|
+
query: req.body.query,
|
|
12
|
+
headers: req.body.headers,
|
|
13
|
+
// socket: req.app.settings.socket, // seem never used, and it can not be serialized to worker thread
|
|
14
|
+
},
|
|
15
|
+
selectedConfig: {
|
|
16
|
+
url: `http://${args.hostname}:${args.port}`,
|
|
17
|
+
dbindex: args.dbindex,
|
|
18
|
+
subdomain: args.subdomain,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// export async function runCliScript(req, res) {
|
|
24
|
+
// try {
|
|
25
|
+
// const { data, selectedConfig } = getInputScript(req);
|
|
26
|
+
// let script = await loadCliScript(selectedConfig, 'ID', req.body.query.scriptid);
|
|
27
|
+
// let functions = await extractFunctionScript(selectedConfig, script);
|
|
28
|
+
// if (functions) {
|
|
29
|
+
// console.info(`## Callback ID ${req.body.query.scriptid} Starts. ##`);
|
|
30
|
+
|
|
31
|
+
// let respon = await functions.onInit(data);
|
|
32
|
+
// res.send(respon);
|
|
33
|
+
|
|
34
|
+
// console.info(`## Callback ID ${req.body.query.scriptid} Finish. ##`);
|
|
35
|
+
// } else {
|
|
36
|
+
// res.send('CLI Script does not exist.');
|
|
37
|
+
// }
|
|
38
|
+
// } catch (error) {
|
|
39
|
+
// console.error(error);
|
|
40
|
+
// throw new Error(error);
|
|
41
|
+
// }
|
|
42
|
+
// }
|
|
43
|
+
|
|
23
44
|
export async function runCliScript(req, res) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
try {
|
|
46
|
+
const { data, selectedConfig } = getInputScript(req);
|
|
47
|
+
|
|
48
|
+
console.info(
|
|
49
|
+
`## Callback ID ${req.body.query.scriptid} Starts. ##`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
let respon = await execCLIScriptWorker(
|
|
53
|
+
selectedConfig,
|
|
54
|
+
{ searchBy: "ID", searchValue: req.body.query.scriptid },
|
|
55
|
+
data,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
res.send(respon);
|
|
59
|
+
console.info(`## Callback ID ${req.body.query.scriptid} Finish. ##`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(error);
|
|
62
|
+
res.status(500).send(error.message || error);
|
|
63
|
+
}
|
|
42
64
|
}
|
package/db/db.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as adapter from "./ds.js";
|
|
2
|
+
|
|
3
|
+
export const queryData = async (param, config = {}) => {
|
|
4
|
+
return await adapter.queryData(param, config);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} payload - The data to save.
|
|
10
|
+
* @param {Object} [config] - Optional configuration overrides.
|
|
11
|
+
* @param {string} [tableName] - Optional. If provided, wraps the payload for standard ORM (sendModel).
|
|
12
|
+
* If omitted, sends the raw payload (for Sibling/ACID orchestration) (sendORMPayload).
|
|
13
|
+
*/
|
|
14
|
+
export const save = async (payload, config = {}) => {
|
|
15
|
+
return await adapter.save(payload, config);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const deleteMD = async (id, masterTable, detailTables, config = {}) => {
|
|
19
|
+
const payload = {
|
|
20
|
+
id: id,
|
|
21
|
+
master: masterTable,
|
|
22
|
+
detail: detailTables,
|
|
23
|
+
};
|
|
24
|
+
return await adapter.removeMD(payload, config);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const remove = async (payload, config = {}) => {
|
|
28
|
+
return await adapter.remove(payload, config);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const list = async (payload, config = {}) => {
|
|
32
|
+
return await adapter.list(payload, config);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const executeBlock = async (sqlString, config = {}) => {
|
|
36
|
+
return await adapter.executeBlock(sqlString, config);
|
|
37
|
+
};
|