@abtnode/core 1.16.33 → 1.16.34-beta-20241120-080738-bbbe036c
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/README.md +1 -1
- package/lib/blocklet/manager/disk.js +23 -5
- package/lib/blocklet/manager/helper/install-application-from-backup.js +7 -0
- package/lib/blocklet/migration-dist/migration.cjs +1 -1
- package/lib/index.js +8 -1
- package/lib/router/helper.js +36 -1
- package/lib/util/blocklet.js +8 -3
- package/lib/util/docker/docker-exec-chown.js +2 -1
- package/lib/util/docker/parse-docker-options-from-pm2.js +33 -8
- package/lib/util/monitor.js +159 -0
- package/lib/util/rotator.js +8 -2
- package/package.json +21 -21
package/README.md
CHANGED
|
@@ -216,7 +216,11 @@ const statusLock = new Lock('blocklet-status-lock');
|
|
|
216
216
|
const getHookArgs = (blocklet) => ({
|
|
217
217
|
output: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '' : path.join(blocklet.env.logsDir, 'output.log'),
|
|
218
218
|
error: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '' : path.join(blocklet.env.logsDir, 'error.log'),
|
|
219
|
-
timeout:
|
|
219
|
+
timeout:
|
|
220
|
+
Math.max(
|
|
221
|
+
get(blocklet, 'meta.timeout.script', 120),
|
|
222
|
+
...(blocklet?.children || []).map((child) => child.meta?.timeout?.script || 0)
|
|
223
|
+
) * 1000,
|
|
220
224
|
});
|
|
221
225
|
|
|
222
226
|
const pm2StatusMap = {
|
|
@@ -906,6 +910,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
906
910
|
env: nextEnv,
|
|
907
911
|
hookName: 'preStop',
|
|
908
912
|
nodeInfo,
|
|
913
|
+
timeout: getHookArgs(b).timeout,
|
|
909
914
|
});
|
|
910
915
|
}
|
|
911
916
|
return null;
|
|
@@ -1124,6 +1129,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1124
1129
|
script: b.meta.scripts.preUninstall,
|
|
1125
1130
|
hookName: 'preUninstall',
|
|
1126
1131
|
nodeInfo,
|
|
1132
|
+
timeout: getHookArgs(b).timeout,
|
|
1127
1133
|
});
|
|
1128
1134
|
}
|
|
1129
1135
|
return null;
|
|
@@ -1178,7 +1184,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1178
1184
|
const nodeInfo = await states.node.read();
|
|
1179
1185
|
// 如果删除因为权限问题失败,尝试用 docker 用户去调整用户组为宿主机当前用户
|
|
1180
1186
|
if (checkDockerRunHistory(nodeInfo) && error.code === 'EACCES') {
|
|
1181
|
-
await dockerExecChown(`${did}-${childDid}`, dirs);
|
|
1187
|
+
await dockerExecChown(`${did}-${childDid}-remove-catch`, dirs);
|
|
1182
1188
|
for (const dir of dirs) {
|
|
1183
1189
|
fs.removeSync(dir);
|
|
1184
1190
|
logger.info(`removed blocklet ${did} ${childDid}: ${dir}`);
|
|
@@ -1246,7 +1252,12 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1246
1252
|
|
|
1247
1253
|
async deleteComponent({ did, rootDid, keepData, keepState }, context) {
|
|
1248
1254
|
logger.info('delete blocklet component', { did, rootDid, keepData });
|
|
1249
|
-
|
|
1255
|
+
const nodeInfo = await states.node.read();
|
|
1256
|
+
if (checkDockerRunHistory(nodeInfo)) {
|
|
1257
|
+
await dockerExecChown(`${did}-${rootDid}-deleteComponent`, [
|
|
1258
|
+
path.join(process.env.ABT_NODE_DATA_DIR, 'data', rootDid),
|
|
1259
|
+
]);
|
|
1260
|
+
}
|
|
1250
1261
|
const app = await this.getBlocklet(rootDid);
|
|
1251
1262
|
|
|
1252
1263
|
const child = app.children.find((x) => x.meta.did === did);
|
|
@@ -1602,7 +1613,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1602
1613
|
nodeInfo,
|
|
1603
1614
|
env: nextEnv,
|
|
1604
1615
|
script: blocklet.meta.scripts.preConfig,
|
|
1605
|
-
timeout:
|
|
1616
|
+
timeout: getHookArgs(blocklet).timeout,
|
|
1606
1617
|
retry: 0,
|
|
1607
1618
|
hookName: 'preConfig',
|
|
1608
1619
|
});
|
|
@@ -4121,6 +4132,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4121
4132
|
const dataDir = path.join(this.dataDirs.data, name);
|
|
4122
4133
|
const logsDir = path.join(this.dataDirs.logs, name);
|
|
4123
4134
|
const cacheDir = path.join(this.dataDirs.cache, name);
|
|
4135
|
+
const nodeInfo = await states.node.read();
|
|
4136
|
+
if (checkDockerRunHistory(nodeInfo)) {
|
|
4137
|
+
await dockerExecChown(`${blocklet.meta.did}-clean-data`, [dataDir, logsDir, cacheDir]);
|
|
4138
|
+
}
|
|
4124
4139
|
|
|
4125
4140
|
logger.info(`clean blocklet ${blocklet.meta.did} data`, { keepData, keepLogsDir, keepConfigs });
|
|
4126
4141
|
|
|
@@ -4580,7 +4595,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4580
4595
|
// eslint-disable-next-line no-unused-vars
|
|
4581
4596
|
async _backupToDisk({ blocklet }, context) {
|
|
4582
4597
|
const { appDid } = blocklet;
|
|
4583
|
-
|
|
4598
|
+
const nodeInfo = await states.node.read();
|
|
4599
|
+
if (checkDockerRunHistory(nodeInfo)) {
|
|
4600
|
+
await dockerExecChown(`${appDid}-backup-to-disk`, [path.join(process.env.ABT_NODE_DATA_DIR, 'data', appDid)]);
|
|
4601
|
+
}
|
|
4584
4602
|
const diskBackup = new DiskBackup({ appDid, event: this });
|
|
4585
4603
|
await diskBackup.backup();
|
|
4586
4604
|
|
|
@@ -67,6 +67,9 @@ const installApplicationFromBackup = async ({
|
|
|
67
67
|
);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/** @type {import('@abtnode/client').RoutingSite} */
|
|
71
|
+
const routingSite = omit(fs.readJSONSync(path.join(dir, 'routing_rule.json')));
|
|
72
|
+
|
|
70
73
|
const { meta } = blockletState;
|
|
71
74
|
const { did, name: appName } = meta;
|
|
72
75
|
|
|
@@ -135,6 +138,10 @@ const installApplicationFromBackup = async ({
|
|
|
135
138
|
let blocklet = null;
|
|
136
139
|
|
|
137
140
|
try {
|
|
141
|
+
// 还原 routing rule
|
|
142
|
+
await states.site.remove({ id: routingSite.id });
|
|
143
|
+
await states.site.insert(routingSite);
|
|
144
|
+
|
|
138
145
|
// copy extra
|
|
139
146
|
const existExtra = await states.blockletExtras.find({ did });
|
|
140
147
|
if (existExtra) {
|
|
@@ -38532,7 +38532,7 @@ module.exports = require("zlib");
|
|
|
38532
38532
|
/***/ ((module) => {
|
|
38533
38533
|
|
|
38534
38534
|
"use strict";
|
|
38535
|
-
module.exports = JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.
|
|
38535
|
+
module.exports = JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.33","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.33","@abtnode/auth":"1.16.33","@abtnode/certificate-manager":"1.16.33","@abtnode/constant":"1.16.33","@abtnode/cron":"1.16.33","@abtnode/logger":"1.16.33","@abtnode/models":"1.16.33","@abtnode/queue":"1.16.33","@abtnode/rbac":"1.16.33","@abtnode/router-provider":"1.16.33","@abtnode/static-server":"1.16.33","@abtnode/timemachine":"1.16.33","@abtnode/util":"1.16.33","@arcblock/did":"1.18.139","@arcblock/did-auth":"1.18.139","@arcblock/did-ext":"^1.18.139","@arcblock/did-motif":"^1.1.13","@arcblock/did-util":"1.18.139","@arcblock/event-hub":"1.18.139","@arcblock/jwt":"^1.18.139","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"^1.18.139","@arcblock/vc":"1.18.139","@blocklet/constant":"1.16.33","@blocklet/did-space-js":"^0.5.65","@blocklet/env":"1.16.33","@blocklet/meta":"1.16.33","@blocklet/resolver":"1.16.33","@blocklet/sdk":"1.16.33","@blocklet/store":"1.16.33","@fidm/x509":"^1.2.1","@ocap/mcrypto":"1.18.139","@ocap/util":"1.18.139","@ocap/wallet":"1.18.139","@slack/webhook":"^5.0.4","archiver":"^7.0.1","axios":"^1.7.5","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","lru-cache":"^6.0.0","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"^2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^9.0.1","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"expand-tilde":"^2.0.2","express":"^4.18.2","jest":"^29.7.0","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
|
|
38536
38536
|
|
|
38537
38537
|
/***/ }),
|
|
38538
38538
|
|
package/lib/index.js
CHANGED
|
@@ -273,7 +273,14 @@ function ABTNode(options) {
|
|
|
273
273
|
deleteRoutingRule,
|
|
274
274
|
addDomainAlias,
|
|
275
275
|
deleteDomainAlias,
|
|
276
|
-
} = getRouterHelpers({
|
|
276
|
+
} = getRouterHelpers({
|
|
277
|
+
dataDirs,
|
|
278
|
+
routingSnapshot,
|
|
279
|
+
routerManager,
|
|
280
|
+
blockletManager,
|
|
281
|
+
certManager,
|
|
282
|
+
daemon: options.daemon,
|
|
283
|
+
});
|
|
277
284
|
|
|
278
285
|
blockletManager.resetSiteByDid = resetSiteByDid;
|
|
279
286
|
|
package/lib/router/helper.js
CHANGED
|
@@ -74,6 +74,7 @@ const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-acces
|
|
|
74
74
|
|
|
75
75
|
const Router = require('./index');
|
|
76
76
|
const states = require('../states');
|
|
77
|
+
const monitor = require('../util/monitor');
|
|
77
78
|
const { getBlockletDomainGroupName, getDidFromDomainGroupName, createProviderInstance } = require('../util/router');
|
|
78
79
|
const {
|
|
79
80
|
getBlockletDidDomainList,
|
|
@@ -679,7 +680,14 @@ const getDidDomainCertDownloadUrl = (baseUrl) => joinCertDownUrl(baseUrl, 'did-a
|
|
|
679
680
|
const getDownloadCertBaseUrl = (info) =>
|
|
680
681
|
process.env.ABT_NODE_WILDCARD_CERT_HOST || get(info, 'routing.wildcardCertHost', '');
|
|
681
682
|
|
|
682
|
-
module.exports = function getRouterHelpers({
|
|
683
|
+
module.exports = function getRouterHelpers({
|
|
684
|
+
dataDirs,
|
|
685
|
+
routingSnapshot,
|
|
686
|
+
routerManager,
|
|
687
|
+
blockletManager,
|
|
688
|
+
certManager,
|
|
689
|
+
daemon = false,
|
|
690
|
+
}) {
|
|
683
691
|
const nodeState = states.node;
|
|
684
692
|
const blockletState = states.blocklet;
|
|
685
693
|
const siteState = states.site;
|
|
@@ -1317,6 +1325,20 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1317
1325
|
await providers[providerName].start();
|
|
1318
1326
|
}
|
|
1319
1327
|
|
|
1328
|
+
if (daemon && process.env.ABT_NODE_MONITOR_GATEWAY_5XX) {
|
|
1329
|
+
monitor.stopLogWatcher();
|
|
1330
|
+
const logs = providers[providerName].getLogFilesForToday();
|
|
1331
|
+
monitor.startLogWatcher(logs.access, (logEntries) => {
|
|
1332
|
+
notification.create({
|
|
1333
|
+
title: 'Server 5xx Alert',
|
|
1334
|
+
description: `5xx request detected: ${JSON.stringify(logEntries, null, 2)}`,
|
|
1335
|
+
entityType: 'server',
|
|
1336
|
+
severity: 'error',
|
|
1337
|
+
sticky: true,
|
|
1338
|
+
});
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1320
1342
|
logger.info('done handle routing', { cost: Date.now() - now });
|
|
1321
1343
|
};
|
|
1322
1344
|
|
|
@@ -1325,7 +1347,20 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1325
1347
|
const providerName = get(info, 'routing.provider', null);
|
|
1326
1348
|
|
|
1327
1349
|
if (providerName && providers[providerName] && typeof providers[providerName].rotateLogs === 'function') {
|
|
1350
|
+
monitor.stopLogWatcher();
|
|
1328
1351
|
await providers[providerName].rotateLogs({ retain: LOG_RETAIN_IN_DAYS });
|
|
1352
|
+
if (daemon && process.env.ABT_NODE_MONITOR_GATEWAY_5XX) {
|
|
1353
|
+
const logs = providers[providerName].getLogFilesForToday();
|
|
1354
|
+
monitor.startLogWatcher(logs.access, (logEntries) => {
|
|
1355
|
+
notification.create({
|
|
1356
|
+
title: 'Server 5xx Alert',
|
|
1357
|
+
description: `5xx request detected: ${JSON.stringify(logEntries, null, 2)}`,
|
|
1358
|
+
entityType: 'server',
|
|
1359
|
+
severity: 'error',
|
|
1360
|
+
sticky: true,
|
|
1361
|
+
});
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1329
1364
|
}
|
|
1330
1365
|
};
|
|
1331
1366
|
|
package/lib/util/blocklet.js
CHANGED
|
@@ -524,8 +524,11 @@ const getHealthyCheckTimeout = (blocklet, { checkHealthImmediately } = {}) => {
|
|
|
524
524
|
}
|
|
525
525
|
|
|
526
526
|
// Let's wait for at least 1 minute for the blocklet to go live
|
|
527
|
-
|
|
528
|
-
|
|
527
|
+
const startTimeout =
|
|
528
|
+
Math.max(
|
|
529
|
+
get(blocklet, 'meta.timeout.start', process.env.NODE_ENV === 'test' ? 10 : 60),
|
|
530
|
+
...(blocklet?.children || []).map((child) => child.meta?.timeout?.start || 0)
|
|
531
|
+
) * 1000;
|
|
529
532
|
|
|
530
533
|
return {
|
|
531
534
|
startTimeout,
|
|
@@ -970,8 +973,10 @@ const expandTarball = async ({ source, dest, strip = 1 }) => {
|
|
|
970
973
|
};
|
|
971
974
|
|
|
972
975
|
const verifyIntegrity = async ({ file, integrity: expected }) => {
|
|
973
|
-
const
|
|
976
|
+
const stream = fs.createReadStream(file);
|
|
977
|
+
const result = await ssri.checkStream(stream, ssri.parse(expected));
|
|
974
978
|
logger.debug('verify integrity result', { result });
|
|
979
|
+
stream.destroy();
|
|
975
980
|
return true;
|
|
976
981
|
};
|
|
977
982
|
|
|
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const logger = require('@abtnode/logger')('@abtnode/docker-exec-chown');
|
|
4
4
|
const debianDockerfile = require('./debian-dockerfile');
|
|
5
5
|
const promiseSpawn = require('../promise-spawn');
|
|
6
|
+
const parseDockerName = require('./parse-docker-name');
|
|
6
7
|
|
|
7
8
|
async function dockerExecChown(name, targetDirs) {
|
|
8
9
|
const uid = process.getuid();
|
|
@@ -15,7 +16,7 @@ async function dockerExecChown(name, targetDirs) {
|
|
|
15
16
|
const command = dirs
|
|
16
17
|
.map((dir) => `chown -R ${uid}:${gid} ${path.join(baseDir, dir.replace(process.env.ABT_NODE_DATA_DIR, ''))}`)
|
|
17
18
|
.join(' && ');
|
|
18
|
-
const realName = `blocklet-chown-${name.replace(/\\/g, '_')}
|
|
19
|
+
const realName = parseDockerName(`blocklet-chown-${name.replace(/\\/g, '_')}`);
|
|
19
20
|
const startTime = Date.now();
|
|
20
21
|
try {
|
|
21
22
|
await promiseSpawn(
|
|
@@ -7,6 +7,7 @@ const fs = require('fs');
|
|
|
7
7
|
const os = require('os');
|
|
8
8
|
const logger = require('@abtnode/logger')('@abtnode/docker');
|
|
9
9
|
const { existsSync, writeFileSync } = require('fs-extra');
|
|
10
|
+
const { NODE_MODES } = require('@abtnode/constant');
|
|
10
11
|
|
|
11
12
|
const parseDockerName = require('./parse-docker-name');
|
|
12
13
|
const { createDockerImage } = require('./create-docker-image');
|
|
@@ -35,6 +36,8 @@ async function parseDockerOptionsFromPm2({ options, nodeInfo, meta, ports, overr
|
|
|
35
36
|
return options;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
const isServerless = nodeInfo.mode === NODE_MODES.SERVERLESS;
|
|
40
|
+
|
|
38
41
|
const dockerInfo = await createDockerImage({
|
|
39
42
|
appDir: options.env.BLOCKLET_APP_DIR,
|
|
40
43
|
dataDir: options.env.BLOCKLET_DATA_DIR,
|
|
@@ -46,13 +49,30 @@ async function parseDockerOptionsFromPm2({ options, nodeInfo, meta, ports, overr
|
|
|
46
49
|
nextOptions.env = { ...nextOptions.env };
|
|
47
50
|
const name = parseDockerName(options.name);
|
|
48
51
|
|
|
52
|
+
const defaultCpus = '2';
|
|
53
|
+
const defaultMemory = '2g';
|
|
54
|
+
const defaultDiskSize = '0g';
|
|
55
|
+
|
|
56
|
+
const cpus = isServerless
|
|
57
|
+
? process.env.ABT_NODE_DOCKER_CPUS
|
|
58
|
+
: options.env.DOCKER_CPUS || process.env.ABT_NODE_DOCKER_CPUS;
|
|
59
|
+
|
|
60
|
+
const memory = isServerless
|
|
61
|
+
? process.env.ABT_NODE_DOCKER_MEMORY
|
|
62
|
+
: options.env.DOCKER_MEMORY || process.env.ABT_NODE_DOCKER_MEMORY;
|
|
63
|
+
|
|
64
|
+
const diskSize = isServerless
|
|
65
|
+
? process.env.ABT_NODE_DOCKER_DISK_SIZE
|
|
66
|
+
: options.env.DOCKER_DISK_SIZE || process.env.ABT_NODE_DOCKER_DISK_SIZE;
|
|
67
|
+
|
|
49
68
|
// Ensure environment variables are properly set within the Docker container
|
|
50
69
|
const envDefaults = {
|
|
51
|
-
BLOCKLET_DOCKER_CPUS:
|
|
52
|
-
BLOCKLET_DOCKER_MEMORY:
|
|
53
|
-
BLOCKLET_DOCKER_DISK_SIZE:
|
|
70
|
+
BLOCKLET_DOCKER_CPUS: cpus || defaultCpus,
|
|
71
|
+
BLOCKLET_DOCKER_MEMORY: memory || defaultMemory,
|
|
72
|
+
BLOCKLET_DOCKER_DISK_SIZE: diskSize || defaultDiskSize,
|
|
54
73
|
BLOCKLET_DOCKER_NAME: name,
|
|
55
74
|
INTERNAL_HOST: 'host.docker.internal',
|
|
75
|
+
NODE_MODES: options.env.NODE_MODES || (isServerless ? NODE_MODES.SERVERLESS : NODE_MODES.PRODUCTION),
|
|
56
76
|
HTTP_PROXY: process.env.DOCKER_HTTP_PROXY || '',
|
|
57
77
|
HTTPS_PROXY: process.env.DOCKER_HTTPS_PROXY || '',
|
|
58
78
|
};
|
|
@@ -163,11 +183,13 @@ async function parseDockerOptionsFromPm2({ options, nodeInfo, meta, ports, overr
|
|
|
163
183
|
target: path.join(dockerEnv.BLOCKLET_APP_DATA_DIR, '.assets'),
|
|
164
184
|
canEdit: false,
|
|
165
185
|
},
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
186
|
+
options.env.SHARE_DOCKER_APP_DATA_DIR
|
|
187
|
+
? {
|
|
188
|
+
source: nextOptions.env.BLOCKLET_APP_DATA_DIR,
|
|
189
|
+
target: dockerEnv.BLOCKLET_APP_DATA_DIR,
|
|
190
|
+
canEdit: false,
|
|
191
|
+
}
|
|
192
|
+
: null,
|
|
171
193
|
{ source: nextOptions.env.BLOCKLET_APP_DIR, target: dockerEnv.BLOCKLET_APP_DIR, canEdit: true },
|
|
172
194
|
{ source: nextOptions.env.BLOCKLET_DATA_DIR, target: dockerEnv.BLOCKLET_DATA_DIR, canEdit: true },
|
|
173
195
|
{ source: nextOptions.env.BLOCKLET_LOG_DIR, target: dockerEnv.BLOCKLET_LOG_DIR, canEdit: true },
|
|
@@ -188,6 +210,9 @@ async function parseDockerOptionsFromPm2({ options, nodeInfo, meta, ports, overr
|
|
|
188
210
|
canEdit: true,
|
|
189
211
|
},
|
|
190
212
|
].filter((v) => {
|
|
213
|
+
if (!v) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
191
216
|
if (targetDirs.has(v.target)) {
|
|
192
217
|
return false;
|
|
193
218
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
const THROTTLE_DELAY = 1000; // 1 second throttle
|
|
5
|
+
|
|
6
|
+
// Function to parse the log entry and extract relevant information
|
|
7
|
+
function parseLogEntry(line) {
|
|
8
|
+
const regex =
|
|
9
|
+
/^(\S+) - (\S+) \[([^\]]+)\] (\S+) "([^"]+)" "([^"]+)" (\d{3}) (\d+) "(.*?)" "(.*?)" "(.*?)" rt="(\S+)" uid="(.+?)" uos="(.+?)" uct="(\S+)" uht="(\S+)" urt="(\S+)"/;
|
|
10
|
+
const match = line.match(regex);
|
|
11
|
+
if (match) {
|
|
12
|
+
return {
|
|
13
|
+
ip: match[1],
|
|
14
|
+
remoteUser: match[2],
|
|
15
|
+
timeIso8601: match[3],
|
|
16
|
+
requestId: match[4],
|
|
17
|
+
host: match[5],
|
|
18
|
+
request: match[6],
|
|
19
|
+
status: parseInt(match[7], 10),
|
|
20
|
+
bodyBytesSent: parseInt(match[8], 10),
|
|
21
|
+
referer: match[9],
|
|
22
|
+
userAgent: match[10],
|
|
23
|
+
forwardedFor: match[11],
|
|
24
|
+
requestTime: parseFloat(match[12]),
|
|
25
|
+
connectedDid: match[13],
|
|
26
|
+
connectedWalletOs: match[14],
|
|
27
|
+
upstreamConnectTime: parseFloat(match[15]),
|
|
28
|
+
upstreamHeaderTime: parseFloat(match[16]),
|
|
29
|
+
upstreamResponseTime: parseFloat(match[17]),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function readLogFile(filePath, startPosition = 0) {
|
|
36
|
+
let fd = null;
|
|
37
|
+
try {
|
|
38
|
+
if (!fs.existsSync(filePath)) {
|
|
39
|
+
return { lines: [], fileSize: 0 };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const realPath = await fs.promises.realpath(filePath);
|
|
43
|
+
fd = await fs.promises.open(realPath, 'r');
|
|
44
|
+
const stats = await fd.stat();
|
|
45
|
+
const fileSize = stats.size;
|
|
46
|
+
|
|
47
|
+
if (startPosition < 0) {
|
|
48
|
+
// eslint-disable-next-line no-param-reassign
|
|
49
|
+
startPosition = Math.max(0, fileSize + startPosition);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (startPosition >= fileSize) {
|
|
53
|
+
return { lines: [], fileSize };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const readSize = fileSize - startPosition;
|
|
57
|
+
if (readSize <= 0) {
|
|
58
|
+
return { lines: [], fileSize };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const buffer = Buffer.alloc(readSize);
|
|
62
|
+
const { bytesRead } = await fd.read(buffer, 0, readSize, startPosition);
|
|
63
|
+
const content = buffer.slice(0, bytesRead).toString();
|
|
64
|
+
|
|
65
|
+
const lines = content
|
|
66
|
+
.split('\n')
|
|
67
|
+
.map((line) => line.trim())
|
|
68
|
+
.filter(Boolean);
|
|
69
|
+
|
|
70
|
+
return { lines, fileSize };
|
|
71
|
+
} finally {
|
|
72
|
+
if (fd) {
|
|
73
|
+
await fd.close();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let watcher;
|
|
79
|
+
process.on('SIGINT', () => {
|
|
80
|
+
watcher?.close();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
process.on('SIGTERM', () => {
|
|
84
|
+
watcher?.close();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
function startLogWatcher(logFilePath, onLogEntry, initialBufferSize = 10240) {
|
|
88
|
+
if (!fs.existsSync(logFilePath)) {
|
|
89
|
+
console.error(`Log file ${logFilePath} does not exist`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.warn(`Start watching ${logFilePath} for 5xx requests...`);
|
|
94
|
+
|
|
95
|
+
let isProcessing = false;
|
|
96
|
+
let isFirstRun = true;
|
|
97
|
+
let timeoutId = null;
|
|
98
|
+
let lastProcessedPosition = 0;
|
|
99
|
+
|
|
100
|
+
async function processLogChanges() {
|
|
101
|
+
isProcessing = true;
|
|
102
|
+
try {
|
|
103
|
+
const { lines, fileSize } = await readLogFile(
|
|
104
|
+
logFilePath,
|
|
105
|
+
isFirstRun ? -initialBufferSize : lastProcessedPosition
|
|
106
|
+
);
|
|
107
|
+
const entries = [];
|
|
108
|
+
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
const logEntry = parseLogEntry(line);
|
|
111
|
+
if (logEntry && logEntry.status >= 500 && logEntry.status <= 599) {
|
|
112
|
+
console.warn(`5xx request detected: ${logEntry.host}`, line);
|
|
113
|
+
entries.push(logEntry);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (entries.length > 0) {
|
|
118
|
+
onLogEntry(entries);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
lastProcessedPosition = fileSize;
|
|
122
|
+
isFirstRun = false;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(`Error reading log file ${logFilePath}`, error);
|
|
125
|
+
} finally {
|
|
126
|
+
isProcessing = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
watcher = fs.watch(logFilePath, { persistent: true }, async (eventType, filename) => {
|
|
131
|
+
if (!filename || eventType !== 'change') return;
|
|
132
|
+
|
|
133
|
+
// Clear any pending timeout
|
|
134
|
+
if (timeoutId) {
|
|
135
|
+
clearTimeout(timeoutId);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If already processing, schedule for later
|
|
139
|
+
if (isProcessing) {
|
|
140
|
+
timeoutId = setTimeout(() => {
|
|
141
|
+
processLogChanges();
|
|
142
|
+
}, THROTTLE_DELAY);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await processLogChanges();
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function stopLogWatcher() {
|
|
151
|
+
watcher?.close();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
parseLogEntry,
|
|
156
|
+
readLogFile,
|
|
157
|
+
startLogWatcher,
|
|
158
|
+
stopLogWatcher,
|
|
159
|
+
};
|
package/lib/util/rotator.js
CHANGED
|
@@ -112,6 +112,12 @@ module.exports = class LogRotate {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const onError = (err) => {
|
|
115
|
+
if (GZIP) {
|
|
116
|
+
GZIP.close();
|
|
117
|
+
}
|
|
118
|
+
readStream?.close();
|
|
119
|
+
writeStream?.close();
|
|
120
|
+
|
|
115
121
|
logError(err);
|
|
116
122
|
callback(err);
|
|
117
123
|
};
|
|
@@ -128,8 +134,8 @@ module.exports = class LogRotate {
|
|
|
128
134
|
if (GZIP) {
|
|
129
135
|
GZIP.close();
|
|
130
136
|
}
|
|
131
|
-
readStream
|
|
132
|
-
writeStream
|
|
137
|
+
readStream?.close();
|
|
138
|
+
writeStream?.close();
|
|
133
139
|
|
|
134
140
|
fs.truncateSync(file);
|
|
135
141
|
logInfo('rotated file:', finalName);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.
|
|
6
|
+
"version": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,19 +19,19 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/analytics": "1.16.
|
|
23
|
-
"@abtnode/auth": "1.16.
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.
|
|
25
|
-
"@abtnode/constant": "1.16.
|
|
26
|
-
"@abtnode/cron": "1.16.
|
|
27
|
-
"@abtnode/logger": "1.16.
|
|
28
|
-
"@abtnode/models": "1.16.
|
|
29
|
-
"@abtnode/queue": "1.16.
|
|
30
|
-
"@abtnode/rbac": "1.16.
|
|
31
|
-
"@abtnode/router-provider": "1.16.
|
|
32
|
-
"@abtnode/static-server": "1.16.
|
|
33
|
-
"@abtnode/timemachine": "1.16.
|
|
34
|
-
"@abtnode/util": "1.16.
|
|
22
|
+
"@abtnode/analytics": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
23
|
+
"@abtnode/auth": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
25
|
+
"@abtnode/constant": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
26
|
+
"@abtnode/cron": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
27
|
+
"@abtnode/logger": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
28
|
+
"@abtnode/models": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
29
|
+
"@abtnode/queue": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
30
|
+
"@abtnode/rbac": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
31
|
+
"@abtnode/router-provider": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
32
|
+
"@abtnode/static-server": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
33
|
+
"@abtnode/timemachine": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
34
|
+
"@abtnode/util": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
35
35
|
"@arcblock/did": "1.18.139",
|
|
36
36
|
"@arcblock/did-auth": "1.18.139",
|
|
37
37
|
"@arcblock/did-ext": "^1.18.139",
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
"@arcblock/pm2-events": "^0.0.5",
|
|
43
43
|
"@arcblock/validator": "^1.18.139",
|
|
44
44
|
"@arcblock/vc": "1.18.139",
|
|
45
|
-
"@blocklet/constant": "1.16.
|
|
45
|
+
"@blocklet/constant": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
46
46
|
"@blocklet/did-space-js": "^0.5.65",
|
|
47
|
-
"@blocklet/env": "1.16.
|
|
48
|
-
"@blocklet/meta": "1.16.
|
|
49
|
-
"@blocklet/resolver": "1.16.
|
|
50
|
-
"@blocklet/sdk": "1.16.
|
|
51
|
-
"@blocklet/store": "1.16.
|
|
47
|
+
"@blocklet/env": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
48
|
+
"@blocklet/meta": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
49
|
+
"@blocklet/resolver": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
50
|
+
"@blocklet/sdk": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
51
|
+
"@blocklet/store": "1.16.34-beta-20241120-080738-bbbe036c",
|
|
52
52
|
"@fidm/x509": "^1.2.1",
|
|
53
53
|
"@ocap/mcrypto": "1.18.139",
|
|
54
54
|
"@ocap/util": "1.18.139",
|
|
@@ -106,5 +106,5 @@
|
|
|
106
106
|
"jest": "^29.7.0",
|
|
107
107
|
"unzipper": "^0.10.11"
|
|
108
108
|
},
|
|
109
|
-
"gitHead": "
|
|
109
|
+
"gitHead": "4bc345b51743e28a95bf09f7e0c0740838b39ad3"
|
|
110
110
|
}
|