@abtnode/util 1.15.17 → 1.16.0-beta-8ee536d7
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/lib/async-pm2.js +13 -0
- package/lib/axios.js +32 -4
- package/lib/base32.js +14 -0
- package/lib/can-pkg-rw.js +1 -1
- package/lib/check-domain-match.js +8 -3
- package/lib/did-document.js +128 -0
- package/lib/download-file.js +51 -9
- package/lib/ensure-endpoint-healthy.js +1 -1
- package/lib/format-back-slash.js +9 -0
- package/lib/format-context.js +28 -0
- package/lib/format-name.js +5 -0
- package/lib/get-folder-size.js +16 -1
- package/lib/get-ip.js +6 -6
- package/lib/is-ec2.js +9 -8
- package/lib/is-path-prefix-equal.js +3 -0
- package/lib/locate-npm-global-by-binary.js +16 -6
- package/lib/log.js +391 -0
- package/lib/logo-middleware.js +136 -0
- package/lib/nft.js +15 -0
- package/lib/run-script.js +131 -0
- package/lib/security.js +45 -0
- package/lib/sleep.js +1 -0
- package/lib/url-evaluation/check-accessible-browser.js +17 -0
- package/lib/url-evaluation/check-accessible-node.js +14 -0
- package/lib/url-evaluation/index.js +86 -0
- package/lib/user-avatar.js +69 -0
- package/package.json +42 -19
- package/lib/sys-info.js +0 -56
- /package/lib/{get-node-wallet.js → get-app-wallet.js} +0 -0
package/lib/sleep.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// 请求 4xx/5xx 的情况会判定为 accessible = true
|
|
2
|
+
// (基于 script tag 的检测方式, 会将 404 请求判定为 accessible = false)
|
|
3
|
+
// TODO: 存在 Mixed Content 问题
|
|
4
|
+
module.exports = async (url, timeout = 5000) => {
|
|
5
|
+
let timer;
|
|
6
|
+
try {
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
timer = setTimeout(() => controller.abort(), timeout);
|
|
9
|
+
// eslint-disable-next-line no-undef
|
|
10
|
+
await fetch(url, { method: 'HEAD', mode: 'no-cors', signal: controller.signal });
|
|
11
|
+
return true;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
return false;
|
|
14
|
+
} finally {
|
|
15
|
+
clearTimeout(timer);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
3
|
+
module.exports = async (url, timeout = 5000) => {
|
|
4
|
+
try {
|
|
5
|
+
await axios.head(url, {
|
|
6
|
+
timeout,
|
|
7
|
+
// 只要有响应状态码就认为可访问 (包括 4xx/5xx)
|
|
8
|
+
validateStatus: null,
|
|
9
|
+
});
|
|
10
|
+
return true;
|
|
11
|
+
} catch (e) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const checkURLAccessibleInBrowser = require('./check-accessible-browser');
|
|
2
|
+
|
|
3
|
+
const isIpAddress = (hostname) => {
|
|
4
|
+
return /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.test(hostname);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const isIpEcho = (hostname) => {
|
|
8
|
+
return hostname.endsWith('.ip.abtnet.io');
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const isDidDomain = (hostname) => {
|
|
12
|
+
return hostname.endsWith('.did.abtnet.io');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const isCustomDomain = (domain) => {
|
|
16
|
+
return !isIpEcho(domain) && !isDidDomain(domain) && !isIpAddress(domain);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 用于评估 url 可访问性/使用优先级 (考虑 node/browser 两种环境)
|
|
21
|
+
*/
|
|
22
|
+
const evaluateURL = async (url, options = {}) => {
|
|
23
|
+
const { timeout = 5000, checkAccessible = checkURLAccessibleInBrowser } = options;
|
|
24
|
+
const { protocol, port, hostname } = new URL(url);
|
|
25
|
+
// https +1000 分
|
|
26
|
+
let score = protocol === 'https:' ? 1000 : 0;
|
|
27
|
+
// 自定义域名,优先级最高 (即使不可访问)
|
|
28
|
+
if (isCustomDomain(hostname)) {
|
|
29
|
+
score += 30000;
|
|
30
|
+
// 子层越多越靠后
|
|
31
|
+
score -= hostname.split('.').length;
|
|
32
|
+
} else {
|
|
33
|
+
// 优先级高于 ip echo 地址
|
|
34
|
+
if (isDidDomain(hostname)) {
|
|
35
|
+
score += 21;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ip echo, +20
|
|
39
|
+
if (isIpEcho(hostname)) {
|
|
40
|
+
score += 20;
|
|
41
|
+
}
|
|
42
|
+
// 纯 ip 地址
|
|
43
|
+
if (isIpAddress(hostname)) {
|
|
44
|
+
score += 1;
|
|
45
|
+
}
|
|
46
|
+
// 带端口优先权放后
|
|
47
|
+
if (port) {
|
|
48
|
+
score -= 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// 不可访问, 减 20000 分 (这里将 4xx~5xx 也认为是可访问)
|
|
52
|
+
let accessible = false;
|
|
53
|
+
if (checkAccessible) {
|
|
54
|
+
accessible = await checkAccessible(url, timeout);
|
|
55
|
+
if (!accessible) {
|
|
56
|
+
score -= 20000;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { url, score, accessible };
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 用于评估一组 url 的可访问性/使用优先级, 返回一组按 score 从高到低排序的 urls
|
|
64
|
+
*/
|
|
65
|
+
const evaluateURLs = async (urls, options = {}) => {
|
|
66
|
+
const results = await Promise.all(
|
|
67
|
+
urls.map(async (url) => {
|
|
68
|
+
try {
|
|
69
|
+
return await evaluateURL(url, options);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.error(e);
|
|
72
|
+
return { url, score: Number.MIN_SAFE_INTEGER, accessible: false };
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
// 有些情况下可能不需要排序, 可以传入 sort: false
|
|
77
|
+
if (options.sort === false) {
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
return results.sort((a, b) => b.score - a.score);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
module.exports = {
|
|
84
|
+
evaluateURL,
|
|
85
|
+
evaluateURLs,
|
|
86
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
|
|
4
|
+
const { USER_AVATAR_DIR, USER_AVATAR_URL_PREFIX } = require('@abtnode/constant');
|
|
5
|
+
|
|
6
|
+
const md5 = require('./md5');
|
|
7
|
+
|
|
8
|
+
const getAvatarFile = (dataDir, fileName) =>
|
|
9
|
+
path.join(dataDir, USER_AVATAR_DIR, fileName.substring(0, 2), fileName.substring(2));
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* save base64 avatar data to storage and return url
|
|
13
|
+
*/
|
|
14
|
+
const extractUserAvatar = async (avatar, { dataDir } = {}) => {
|
|
15
|
+
if (!avatar) {
|
|
16
|
+
return avatar;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const regex = /^data:image\/(\w+);base64,/;
|
|
20
|
+
const match = regex.exec(avatar);
|
|
21
|
+
|
|
22
|
+
if (!match) {
|
|
23
|
+
return avatar;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const fileData = Buffer.from(avatar.replace(regex, ''), 'base64');
|
|
27
|
+
const fileName = `${md5(fileData)}.${match[1]}`;
|
|
28
|
+
|
|
29
|
+
const avatarFile = getAvatarFile(dataDir, fileName);
|
|
30
|
+
await fs.outputFile(avatarFile, fileData);
|
|
31
|
+
|
|
32
|
+
const url = `${USER_AVATAR_URL_PREFIX}/${fileName}`;
|
|
33
|
+
|
|
34
|
+
return url;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* generate base64 avatar data to from file by url
|
|
39
|
+
*/
|
|
40
|
+
const parseUserAvatar = async (avatar, { dataDir } = {}) => {
|
|
41
|
+
if (!avatar) {
|
|
42
|
+
return avatar;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!avatar.startsWith(USER_AVATAR_URL_PREFIX)) {
|
|
46
|
+
return avatar;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const fileName = avatar.split('/').slice(-1)[0];
|
|
50
|
+
|
|
51
|
+
const avatarFile = getAvatarFile(dataDir, fileName);
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(avatarFile)) {
|
|
54
|
+
return avatar;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const buf = await fs.promises.readFile(avatarFile);
|
|
58
|
+
const extname = path.extname(fileName).substring(1);
|
|
59
|
+
|
|
60
|
+
const data = `data:image/${extname};base64,${buf.toString('base64')}`;
|
|
61
|
+
|
|
62
|
+
return data;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
extractUserAvatar,
|
|
67
|
+
parseUserAvatar,
|
|
68
|
+
getAvatarFile,
|
|
69
|
+
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.16.0-beta-8ee536d7",
|
|
7
7
|
"description": "ArcBlock's JavaScript utility",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -18,34 +18,57 @@
|
|
|
18
18
|
"author": "polunzh <polunzh@gmail.com> (http://github.com/polunzh)",
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@
|
|
22
|
-
"@
|
|
23
|
-
"
|
|
24
|
-
"
|
|
21
|
+
"@abtnode/constant": "1.16.0-beta-8ee536d7",
|
|
22
|
+
"@abtnode/logger": "1.16.0-beta-8ee536d7",
|
|
23
|
+
"@arcblock/jwt": "^1.18.62",
|
|
24
|
+
"@blocklet/constant": "1.16.0-beta-8ee536d7",
|
|
25
|
+
"@ocap/mcrypto": "1.18.62",
|
|
26
|
+
"@ocap/util": "1.18.62",
|
|
27
|
+
"@ocap/wallet": "1.18.62",
|
|
28
|
+
"archiver": "^5.3.1",
|
|
29
|
+
"axios": "^0.27.2",
|
|
30
|
+
"axios-mock-adapter": "^1.21.2",
|
|
25
31
|
"axon": "^2.0.3",
|
|
26
|
-
"
|
|
32
|
+
"cross-spawn": "^7.0.3",
|
|
33
|
+
"dayjs": "^1.11.7",
|
|
34
|
+
"debug": "^4.3.4",
|
|
35
|
+
"fast-glob": "^3.2.12",
|
|
27
36
|
"find-up": "^5.0.0",
|
|
28
37
|
"flush-write-stream": "^2.0.0",
|
|
29
38
|
"folder-walker": "^3.2.0",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
39
|
+
"form-data": "^4.0.0",
|
|
40
|
+
"get-folder-size": "^2.0.1",
|
|
41
|
+
"hasha": "^5.2.2",
|
|
42
|
+
"hpagent": "^1.1.0",
|
|
43
|
+
"internal-ip": "^6.2.0",
|
|
44
|
+
"is-docker": "^2.2.1",
|
|
45
|
+
"json-stable-stringify": "^1.0.1",
|
|
46
|
+
"lodash": "^4.17.21",
|
|
47
|
+
"multiformats": "9.9.0",
|
|
48
|
+
"p-retry": "4.6.1",
|
|
33
49
|
"parallel-transform": "^1.2.0",
|
|
34
|
-
"public-ip": "^4.0.
|
|
50
|
+
"public-ip": "^4.0.4",
|
|
35
51
|
"pump": "^3.0.0",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
52
|
+
"read-last-lines": "^1.8.0",
|
|
53
|
+
"semver": "^7.3.8",
|
|
54
|
+
"semver-sort": "^1.0.0",
|
|
55
|
+
"shelljs": "^0.8.5",
|
|
56
|
+
"slugify": "^1.6.5",
|
|
38
57
|
"stream-to-promise": "^3.0.0",
|
|
39
|
-
"
|
|
58
|
+
"tail": "^2.2.4",
|
|
59
|
+
"tar": "^6.1.11",
|
|
40
60
|
"through2-filter": "^3.0.0",
|
|
41
61
|
"through2-map": "^3.0.0",
|
|
42
|
-
"to-semver": "^3.0.0"
|
|
62
|
+
"to-semver": "^3.0.0",
|
|
63
|
+
"url-join": "^4.0.1",
|
|
64
|
+
"which": "^2.0.2"
|
|
43
65
|
},
|
|
44
66
|
"devDependencies": {
|
|
45
|
-
"detect-port": "^1.
|
|
46
|
-
"express": "^4.
|
|
47
|
-
"fs-extra": "^10.
|
|
48
|
-
"jest": "^27.
|
|
67
|
+
"detect-port": "^1.5.1",
|
|
68
|
+
"express": "^4.18.2",
|
|
69
|
+
"fs-extra": "^10.1.0",
|
|
70
|
+
"jest": "^27.5.1",
|
|
71
|
+
"unzipper": "^0.10.11"
|
|
49
72
|
},
|
|
50
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "57d0c45be311a5fbc1c0fffa2814b62c1a3ee34c"
|
|
51
74
|
}
|
package/lib/sys-info.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const si = require('systeminformation');
|
|
2
|
-
|
|
3
|
-
const systemConfig = {
|
|
4
|
-
cpu: 'cores',
|
|
5
|
-
mem: '*',
|
|
6
|
-
currentLoad: '*',
|
|
7
|
-
fsSize: '*',
|
|
8
|
-
diskLayout: '*',
|
|
9
|
-
osInfo: 'platform',
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const get = async () => {
|
|
13
|
-
const info = await si.get(systemConfig); // prettier-ignore
|
|
14
|
-
|
|
15
|
-
// 物理磁盘。一些VM,或者Docker环境下无
|
|
16
|
-
const physicalDisk = (info.diskLayout || []).filter((x) => Number(x.size) > 0);
|
|
17
|
-
const fsSizeInfo = (info.fsSize || []).filter((x) => Number(x.size) > 0);
|
|
18
|
-
|
|
19
|
-
// HACK 目前我们支持的系统是 Mac Vm Docker 如果支持windows 下面代码可能会有问题
|
|
20
|
-
// 目前强制过滤出苹果文件系统(APFS) 其他的都是 mount 的文件系统
|
|
21
|
-
// 这样有物理磁盘(Mac) 磁盘总计为 physicalDisk + mountFsSize
|
|
22
|
-
// 无物理磁盘的 磁盘总计为 fsSizeInfo
|
|
23
|
-
const mountFsSize = (info.fsSize || []).filter((x) => x.type.toUpperCase() !== 'APFS');
|
|
24
|
-
|
|
25
|
-
const getTotalFn = (data, key) => data.reduce((total, x) => total + Number(x[key]), 0);
|
|
26
|
-
const fsTotalFn = (key) => getTotalFn(fsSizeInfo, key);
|
|
27
|
-
|
|
28
|
-
const baseSystemInfo = {
|
|
29
|
-
cpu: {
|
|
30
|
-
...info.currentLoad,
|
|
31
|
-
...info.cpu,
|
|
32
|
-
},
|
|
33
|
-
mem: info.mem,
|
|
34
|
-
osInfo: info.osInfo,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
if (info.osInfo.platform === 'darwin') {
|
|
38
|
-
return {
|
|
39
|
-
...baseSystemInfo,
|
|
40
|
-
disk: {
|
|
41
|
-
total: getTotalFn(physicalDisk, 'size') + getTotalFn(mountFsSize, 'size'),
|
|
42
|
-
used: fsTotalFn('used'),
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
...baseSystemInfo,
|
|
49
|
-
disk: {
|
|
50
|
-
total: fsTotalFn('size'),
|
|
51
|
-
used: fsTotalFn('used'),
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
module.exports = { get };
|
|
File without changes
|