@abtnode/core 1.15.17 → 1.16.0-beta-b16cb035
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/api/node.js +67 -69
- package/lib/api/team.js +386 -55
- package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
- package/lib/blocklet/downloader/bundle-downloader.js +272 -0
- package/lib/blocklet/downloader/constants.js +3 -0
- package/lib/blocklet/downloader/resolve-download.js +199 -0
- package/lib/blocklet/extras.js +83 -26
- package/lib/blocklet/hooks.js +18 -65
- package/lib/blocklet/manager/base.js +10 -16
- package/lib/blocklet/manager/disk.js +1679 -1566
- package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
- package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
- package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
- package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
- package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
- package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
- package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
- package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
- package/lib/blocklet/migration.js +30 -52
- package/lib/blocklet/storage/backup/audit-log.js +27 -0
- package/lib/blocklet/storage/backup/base.js +62 -0
- package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
- package/lib/blocklet/storage/backup/blocklet.js +70 -0
- package/lib/blocklet/storage/backup/blocklets.js +74 -0
- package/lib/blocklet/storage/backup/data.js +19 -0
- package/lib/blocklet/storage/backup/logs.js +24 -0
- package/lib/blocklet/storage/backup/routing-rule.js +19 -0
- package/lib/blocklet/storage/backup/spaces.js +240 -0
- package/lib/blocklet/storage/restore/base.js +67 -0
- package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
- package/lib/blocklet/storage/restore/blocklet.js +56 -0
- package/lib/blocklet/storage/restore/blocklets.js +43 -0
- package/lib/blocklet/storage/restore/logs.js +21 -0
- package/lib/blocklet/storage/restore/spaces.js +156 -0
- package/lib/blocklet/storage/utils/hash.js +51 -0
- package/lib/blocklet/storage/utils/zip.js +43 -0
- package/lib/cert.js +206 -0
- package/lib/event.js +237 -64
- package/lib/index.js +191 -83
- package/lib/migrations/1.0.21-update-config.js +1 -1
- package/lib/migrations/1.0.22-max-memory.js +1 -1
- package/lib/migrations/1.0.25.js +1 -1
- package/lib/migrations/1.0.32-update-config.js +1 -1
- package/lib/migrations/1.0.33-blocklets.js +1 -1
- package/lib/migrations/1.5.20-registry.js +15 -0
- package/lib/migrations/1.6.17-blocklet-children.js +48 -0
- package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
- package/lib/migrations/1.6.4-security.js +59 -0
- package/lib/migrations/1.6.5-security.js +60 -0
- package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
- package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
- package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
- package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
- package/lib/migrations/1.7.20-blocklet-component.js +41 -0
- package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
- package/lib/migrations/README.md +1 -1
- package/lib/migrations/index.js +6 -2
- package/lib/monitor/blocklet-runtime-monitor.js +200 -0
- package/lib/monitor/get-history-list.js +37 -0
- package/lib/monitor/node-runtime-monitor.js +228 -0
- package/lib/router/helper.js +572 -497
- package/lib/router/index.js +85 -21
- package/lib/router/manager.js +146 -187
- package/lib/states/README.md +36 -1
- package/lib/states/access-key.js +39 -17
- package/lib/states/audit-log.js +462 -0
- package/lib/states/base.js +4 -213
- package/lib/states/blocklet-extras.js +194 -138
- package/lib/states/blocklet.js +361 -104
- package/lib/states/cache.js +8 -6
- package/lib/states/challenge.js +5 -5
- package/lib/states/index.js +19 -36
- package/lib/states/migration.js +4 -4
- package/lib/states/node.js +135 -46
- package/lib/states/notification.js +22 -35
- package/lib/states/session.js +17 -9
- package/lib/states/site.js +50 -25
- package/lib/states/user.js +74 -20
- package/lib/states/webhook.js +10 -6
- package/lib/team/manager.js +124 -7
- package/lib/util/blocklet.js +1223 -246
- package/lib/util/chain.js +1 -1
- package/lib/util/default-node-config.js +5 -23
- package/lib/util/disk-monitor.js +13 -10
- package/lib/util/domain-status.js +84 -15
- package/lib/util/get-accessible-external-node-ip.js +2 -2
- package/lib/util/get-domain-for-blocklet.js +13 -0
- package/lib/util/get-meta-from-url.js +33 -0
- package/lib/util/index.js +207 -272
- package/lib/util/ip.js +6 -0
- package/lib/util/maintain.js +233 -0
- package/lib/util/public-to-store.js +85 -0
- package/lib/util/ready.js +1 -1
- package/lib/util/requirement.js +28 -9
- package/lib/util/reset-node.js +22 -7
- package/lib/util/router.js +13 -0
- package/lib/util/rpc.js +16 -0
- package/lib/util/store.js +179 -0
- package/lib/util/sysinfo.js +44 -0
- package/lib/util/ua.js +54 -0
- package/lib/validators/blocklet-extra.js +24 -0
- package/lib/validators/node.js +25 -12
- package/lib/validators/permission.js +16 -1
- package/lib/validators/role.js +17 -3
- package/lib/validators/router.js +40 -20
- package/lib/validators/trusted-passport.js +1 -0
- package/lib/validators/util.js +22 -5
- package/lib/webhook/index.js +45 -35
- package/lib/webhook/sender/index.js +5 -0
- package/lib/webhook/sender/slack/index.js +1 -1
- package/lib/webhook/sender/wallet/index.js +48 -0
- package/package.json +54 -36
- package/lib/blocklet/registry.js +0 -205
- package/lib/states/https-cert.js +0 -67
- package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
- package/lib/util/service.js +0 -66
- package/lib/util/upgrade.js +0 -178
- /package/lib/{queue.js → util/queue.js} +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const maxBy = require('lodash/maxBy');
|
|
2
|
+
const SysInfo = require('systeminformation');
|
|
3
|
+
|
|
4
|
+
const getSysInfo = async () => {
|
|
5
|
+
const info = await SysInfo.get({
|
|
6
|
+
cpu: 'physicalCores',
|
|
7
|
+
mem: '*',
|
|
8
|
+
currentLoad: '*',
|
|
9
|
+
fsSize: '*',
|
|
10
|
+
diskLayout: '*',
|
|
11
|
+
osInfo: 'platform',
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let drives = info.fsSize;
|
|
15
|
+
if (info.osInfo.platform === 'darwin') {
|
|
16
|
+
const systemDrives = (info.fsSize || []).filter((x) => x.type === 'APFS');
|
|
17
|
+
const rootDrive = systemDrives.find((x) => x.mount === '/');
|
|
18
|
+
if (rootDrive) {
|
|
19
|
+
const maxUsedDrive = maxBy(systemDrives, (x) => x.used);
|
|
20
|
+
rootDrive.used = maxUsedDrive.used;
|
|
21
|
+
drives = [rootDrive];
|
|
22
|
+
} else {
|
|
23
|
+
drives = [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
cpu: {
|
|
29
|
+
...info.currentLoad,
|
|
30
|
+
...info.cpu,
|
|
31
|
+
},
|
|
32
|
+
mem: info.mem,
|
|
33
|
+
os: info.osInfo,
|
|
34
|
+
disks: drives.map((x) => ({
|
|
35
|
+
device: x.fs,
|
|
36
|
+
mountPoint: x.mount,
|
|
37
|
+
total: x.size,
|
|
38
|
+
used: x.used,
|
|
39
|
+
free: x.size - x.used,
|
|
40
|
+
})),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
module.exports.getSysInfo = getSysInfo;
|
package/lib/util/ua.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const semver = require('semver');
|
|
2
|
+
const parser = require('ua-parser-js');
|
|
3
|
+
const packageJson = require('../../package.json');
|
|
4
|
+
|
|
5
|
+
const { memoizeAsync } = require('./index');
|
|
6
|
+
|
|
7
|
+
const parseWalletUA = (userAgent) => {
|
|
8
|
+
const ua = (userAgent || '').toString().toLowerCase();
|
|
9
|
+
let os = '';
|
|
10
|
+
let version = '';
|
|
11
|
+
if (ua.indexOf('android') > -1) {
|
|
12
|
+
os = 'android';
|
|
13
|
+
} else if (ua.indexOf('darwin') > -1) {
|
|
14
|
+
os = 'ios';
|
|
15
|
+
} else if (ua.indexOf('arcwallet') === 0) {
|
|
16
|
+
os = 'web';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const match = ua.split(/\s+/).find((x) => x.startsWith('arcwallet'));
|
|
20
|
+
if (match) {
|
|
21
|
+
const tmp = match.split('/');
|
|
22
|
+
if (tmp.length > 1 && semver.coerce(tmp[1])) {
|
|
23
|
+
version = semver.coerce(tmp[1]).version;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { os, version };
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const parse = memoizeAsync((ua) => {
|
|
31
|
+
let result = parser(ua);
|
|
32
|
+
if (result.browser.name) {
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
result = parseWalletUA(ua);
|
|
37
|
+
if (result.version) {
|
|
38
|
+
return {
|
|
39
|
+
browser: {
|
|
40
|
+
name: `DID Wallet ${result.os.toUpperCase()}`,
|
|
41
|
+
version: result.version,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
browser: {
|
|
48
|
+
name: 'CLI',
|
|
49
|
+
version: packageJson.version,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
module.exports = { parse };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const JOI = require('joi');
|
|
2
|
+
const { didExtension } = require('@blocklet/meta/lib/extension');
|
|
3
|
+
const { blockletController, createValidator } = require('./util');
|
|
4
|
+
|
|
5
|
+
const Joi = JOI.extend(didExtension);
|
|
6
|
+
|
|
7
|
+
const addMeta = Joi.object({
|
|
8
|
+
did: Joi.ref('meta.did'),
|
|
9
|
+
meta: Joi.object({
|
|
10
|
+
did: Joi.DID().required(),
|
|
11
|
+
name: Joi.string().required(),
|
|
12
|
+
}).required(),
|
|
13
|
+
controller: blockletController.optional(),
|
|
14
|
+
}).required();
|
|
15
|
+
|
|
16
|
+
const updateExpiredInfo = Joi.object({
|
|
17
|
+
did: Joi.DID().required(),
|
|
18
|
+
expiredAt: Joi.string().required(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
validateAddMeta: createValidator(addMeta),
|
|
23
|
+
validateExpiredInfo: createValidator(updateExpiredInfo),
|
|
24
|
+
};
|
package/lib/validators/node.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable newline-per-chained-call */
|
|
2
|
+
const { GATEWAY_REQ_LIMIT } = require('@abtnode/constant');
|
|
2
3
|
const Joi = require('joi');
|
|
3
4
|
const { getMultipleLangParams } = require('./util');
|
|
4
5
|
|
|
@@ -9,14 +10,23 @@ const nodeInfoSchema = Joi.object({
|
|
|
9
10
|
description: Joi.string()
|
|
10
11
|
.required()
|
|
11
12
|
.messages({ zh: { 'string.empty': '描述不能为空' }, en: { 'string.empty': 'Description cannot be empty' } }),
|
|
13
|
+
registerUrl: Joi.string()
|
|
14
|
+
.uri({ scheme: [/https?/] })
|
|
15
|
+
.label('register url')
|
|
16
|
+
.allow('')
|
|
17
|
+
.optional()
|
|
18
|
+
.messages({
|
|
19
|
+
zh: { 'string.uriCustomScheme': '应用启动器必须是合法的 URL' },
|
|
20
|
+
en: { 'string.uriCustomScheme': 'Blocklet Launcher must be a valid URL' },
|
|
21
|
+
}),
|
|
12
22
|
webWalletUrl: Joi.string()
|
|
13
23
|
.uri({ scheme: [/https?/] })
|
|
14
24
|
.label('web wallet url')
|
|
15
25
|
.allow('')
|
|
16
26
|
.optional()
|
|
17
27
|
.messages({
|
|
18
|
-
zh: { 'string.uriCustomScheme': 'Web
|
|
19
|
-
en: { 'string.uriCustomScheme': 'Web
|
|
28
|
+
zh: { 'string.uriCustomScheme': 'Web Wallet 必须是合法的 URL' },
|
|
29
|
+
en: { 'string.uriCustomScheme': 'Web Wallet must be a valid URL' },
|
|
20
30
|
}),
|
|
21
31
|
autoUpgrade: Joi.boolean(),
|
|
22
32
|
enableWelcomePage: Joi.boolean(),
|
|
@@ -31,18 +41,21 @@ const nodeInfoSchema = Joi.object({
|
|
|
31
41
|
'number.max': 'Disk usage alert threshold cannot be higher than 99%',
|
|
32
42
|
},
|
|
33
43
|
}),
|
|
34
|
-
// removed in 1.5.1
|
|
35
|
-
registerUrl: Joi.string()
|
|
36
|
-
.uri({ scheme: [/https?/] })
|
|
37
|
-
.label('register url')
|
|
38
|
-
.allow('')
|
|
39
|
-
.optional()
|
|
40
|
-
.messages({
|
|
41
|
-
zh: { 'string.uriCustomScheme': '注册地址必须是合法的 URL' },
|
|
42
|
-
en: { 'string.uriCustomScheme': 'Registry URL must be a valid URL' },
|
|
43
|
-
}),
|
|
44
44
|
}).options({ stripUnknown: true });
|
|
45
45
|
|
|
46
|
+
const updateGatewaySchema = Joi.object({
|
|
47
|
+
requestLimit: Joi.object({
|
|
48
|
+
enabled: Joi.bool().required(),
|
|
49
|
+
rate: Joi.number()
|
|
50
|
+
.min(GATEWAY_REQ_LIMIT.min)
|
|
51
|
+
.max(GATEWAY_REQ_LIMIT.max)
|
|
52
|
+
.when('requestLimit.enabled', { is: true, then: Joi.required() }),
|
|
53
|
+
ipHeader: Joi.string().allow('').trim(),
|
|
54
|
+
}),
|
|
55
|
+
cacheEnabled: Joi.bool().optional().default(true),
|
|
56
|
+
});
|
|
57
|
+
|
|
46
58
|
module.exports = {
|
|
47
59
|
validateNodeInfo: (entity, context) => nodeInfoSchema.validateAsync(entity, getMultipleLangParams(context)),
|
|
60
|
+
validateUpdateGateway: (entity, context) => updateGatewaySchema.validateAsync(entity, getMultipleLangParams(context)),
|
|
48
61
|
};
|
|
@@ -2,7 +2,22 @@
|
|
|
2
2
|
const JOI = require('joi');
|
|
3
3
|
const { getMultipleLangParams } = require('./util');
|
|
4
4
|
|
|
5
|
-
const nameSchema = JOI.string()
|
|
5
|
+
const nameSchema = JOI.string()
|
|
6
|
+
.trim()
|
|
7
|
+
.max(64)
|
|
8
|
+
.custom((name) => {
|
|
9
|
+
const arr = name.split('_');
|
|
10
|
+
const formatTip = 'The format of permission name should be "{action}_{resource}", e.g. query_data';
|
|
11
|
+
if (arr.length > 2) {
|
|
12
|
+
throw new Error(`Too much "_". ${formatTip}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (arr.length < 2) {
|
|
16
|
+
throw new Error(formatTip);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return name;
|
|
20
|
+
});
|
|
6
21
|
const descriptionSchema = JOI.string().trim().max(600);
|
|
7
22
|
|
|
8
23
|
const createPermissionSchema = JOI.object({
|
package/lib/validators/role.js
CHANGED
|
@@ -2,18 +2,32 @@
|
|
|
2
2
|
const JOI = require('joi');
|
|
3
3
|
const { getMultipleLangParams } = require('./util');
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const roleNameSchema = JOI.string()
|
|
6
|
+
.trim()
|
|
7
|
+
.max(64)
|
|
8
|
+
.custom((value) => {
|
|
9
|
+
if (value.startsWith('blocklet')) {
|
|
10
|
+
throw new Error('role name cannot start with "blocklet"');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (/[^a-zA-z0-9]/.test(value)) {
|
|
14
|
+
throw new Error('role name can include only numbers or letters');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return value;
|
|
18
|
+
});
|
|
19
|
+
|
|
6
20
|
const titleSchema = JOI.string().trim().max(25);
|
|
7
21
|
const descriptionSchema = JOI.string().trim().max(600);
|
|
8
22
|
|
|
9
23
|
const createRoleSchema = JOI.object({
|
|
10
|
-
name:
|
|
24
|
+
name: roleNameSchema.required(),
|
|
11
25
|
title: titleSchema.required(),
|
|
12
26
|
description: descriptionSchema.required(),
|
|
13
27
|
});
|
|
14
28
|
|
|
15
29
|
const updateRoleSchema = JOI.object({
|
|
16
|
-
name:
|
|
30
|
+
name: roleNameSchema.required(),
|
|
17
31
|
title: titleSchema,
|
|
18
32
|
description: descriptionSchema,
|
|
19
33
|
});
|
package/lib/validators/router.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/* eslint-disable newline-per-chained-call */
|
|
2
2
|
const Joi = require('joi');
|
|
3
|
-
const { DOMAIN_FOR_DEFAULT_SITE, ROUTING_RULE_TYPES } = require('@abtnode/constant');
|
|
3
|
+
const { DOMAIN_FOR_DEFAULT_SITE, ROUTING_RULE_TYPES, ROUTER_CACHE_GROUPS } = require('@abtnode/constant');
|
|
4
|
+
const urlFriendly = require('@blocklet/meta/lib/url-friendly').default;
|
|
4
5
|
const { getMultipleLangParams } = require('./util');
|
|
5
6
|
|
|
6
7
|
const WILDCARD_DOMAIN_REGEX = /^\*.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/;
|
|
7
8
|
|
|
9
|
+
const DOMAIN_SCHEMA = Joi.string().domain({ minDomainSegments: 1, tlds: false }).lowercase();
|
|
10
|
+
|
|
8
11
|
const domainMessages = {
|
|
9
12
|
en: {
|
|
10
13
|
'alternatives.match': 'Illegal domain, please enter a domain like arcblock, arcblock.io, *.arcblock.io',
|
|
@@ -25,7 +28,8 @@ const ruleSchema = {
|
|
|
25
28
|
.messages({
|
|
26
29
|
zh: { 'string.empty': 'URL 前缀不能为空', 'string.max': 'URL 前缀的最大长度是 150' },
|
|
27
30
|
en: { 'string.empty': 'URL prefix cannot be empty', 'string.max': 'The maximum length of URL prefix is 150' },
|
|
28
|
-
})
|
|
31
|
+
})
|
|
32
|
+
.custom((value) => urlFriendly(value)),
|
|
29
33
|
groupPathPrefix: Joi.string().trim().min(1).max(150), // path prefix of interface of root blocklet
|
|
30
34
|
header: Joi.any(), // TODO: header does not take effect
|
|
31
35
|
}),
|
|
@@ -39,21 +43,33 @@ const ruleSchema = {
|
|
|
39
43
|
ROUTING_RULE_TYPES.BLOCKLET,
|
|
40
44
|
ROUTING_RULE_TYPES.REDIRECT,
|
|
41
45
|
ROUTING_RULE_TYPES.GENERAL_PROXY,
|
|
46
|
+
ROUTING_RULE_TYPES.DIRECT_RESPONSE,
|
|
42
47
|
ROUTING_RULE_TYPES.NONE
|
|
43
48
|
)
|
|
44
49
|
.required(),
|
|
45
50
|
did: Joi.string().label('did').when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }), // root blocklet did
|
|
46
|
-
realDid: Joi.string().label('real did'), // child blocklet did
|
|
47
51
|
port: Joi.number().label('port').port().when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }),
|
|
48
52
|
url: Joi.string().label('url').when('type', { is: ROUTING_RULE_TYPES.REDIRECT, then: Joi.required() }),
|
|
49
53
|
redirectCode: Joi.alternatives()
|
|
50
|
-
.try(301, 302)
|
|
54
|
+
.try(301, 302, 307, 308)
|
|
51
55
|
.label('redirect code')
|
|
52
56
|
.when('type', { is: ROUTING_RULE_TYPES.REDIRECT, then: Joi.required() }),
|
|
53
57
|
interfaceName: Joi.string() // root interface
|
|
54
58
|
.label('interface name')
|
|
55
59
|
.when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }),
|
|
56
|
-
|
|
60
|
+
response: Joi.object({ status: Joi.number().required(), contentType: Joi.string(), body: Joi.string().required() })
|
|
61
|
+
.label('response')
|
|
62
|
+
.when('type', {
|
|
63
|
+
is: ROUTING_RULE_TYPES.DIRECT_RESPONSE,
|
|
64
|
+
then: Joi.required(),
|
|
65
|
+
}),
|
|
66
|
+
componentId: Joi.string().label('component id'), // component global id
|
|
67
|
+
// FUTURE: blocklets can register routing rules for provider cache
|
|
68
|
+
cacheGroup: Joi.string()
|
|
69
|
+
.label('cache group')
|
|
70
|
+
.valid(...Object.keys(ROUTER_CACHE_GROUPS))
|
|
71
|
+
.allow('')
|
|
72
|
+
.default(''),
|
|
57
73
|
},
|
|
58
74
|
|
|
59
75
|
// List of services that manipulate the request before the upstream blocklet
|
|
@@ -73,11 +89,7 @@ const ruleSchema = {
|
|
|
73
89
|
};
|
|
74
90
|
|
|
75
91
|
const corsSchema = Joi.array()
|
|
76
|
-
.items(
|
|
77
|
-
Joi.string().domain({ minDomainSegments: 1, tlds: false }),
|
|
78
|
-
Joi.string().valid(DOMAIN_FOR_DEFAULT_SITE),
|
|
79
|
-
Joi.string().ip()
|
|
80
|
-
)
|
|
92
|
+
.items(DOMAIN_SCHEMA, Joi.string().valid(DOMAIN_FOR_DEFAULT_SITE, '__none__'), Joi.string().ip())
|
|
81
93
|
.min(1)
|
|
82
94
|
.optional();
|
|
83
95
|
|
|
@@ -85,26 +97,30 @@ const ruleJoiSchema = Joi.object(ruleSchema);
|
|
|
85
97
|
|
|
86
98
|
const addDomainAlias = Joi.alternatives()
|
|
87
99
|
.try(
|
|
88
|
-
|
|
100
|
+
DOMAIN_SCHEMA,
|
|
89
101
|
Joi.string().regex(WILDCARD_DOMAIN_REGEX) // 这种其实是一种特殊的 tld
|
|
90
102
|
)
|
|
91
103
|
.required();
|
|
92
104
|
|
|
105
|
+
const domainAliases = Joi.array().items(
|
|
106
|
+
Joi.object({
|
|
107
|
+
value: addDomainAlias,
|
|
108
|
+
isProtected: Joi.boolean(),
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const updateAliases = domainAliases.required();
|
|
113
|
+
|
|
93
114
|
const addSiteSchema = Joi.object({
|
|
94
115
|
domain: Joi.alternatives()
|
|
95
116
|
.try(
|
|
96
|
-
|
|
117
|
+
DOMAIN_SCHEMA,
|
|
97
118
|
Joi.string().valid('', DOMAIN_FOR_DEFAULT_SITE),
|
|
98
119
|
Joi.string().regex(WILDCARD_DOMAIN_REGEX) // 这种其实是一种特殊的 tld
|
|
99
120
|
)
|
|
100
121
|
.required()
|
|
101
122
|
.messages(domainMessages),
|
|
102
|
-
domainAliases
|
|
103
|
-
Joi.object({
|
|
104
|
-
value: addDomainAlias,
|
|
105
|
-
isProtected: Joi.boolean(),
|
|
106
|
-
})
|
|
107
|
-
),
|
|
123
|
+
domainAliases,
|
|
108
124
|
isProtected: Joi.boolean(),
|
|
109
125
|
rules: Joi.array().items(ruleJoiSchema),
|
|
110
126
|
corsAllowedOrigins: corsSchema,
|
|
@@ -115,12 +131,12 @@ const updateSite = Joi.object({
|
|
|
115
131
|
corsAllowedOrigins: corsSchema,
|
|
116
132
|
domain: Joi.alternatives()
|
|
117
133
|
.try(
|
|
118
|
-
|
|
134
|
+
DOMAIN_SCHEMA,
|
|
119
135
|
Joi.string().regex(WILDCARD_DOMAIN_REGEX) // 这种其实是一种特殊的 tld
|
|
120
136
|
)
|
|
121
137
|
.optional()
|
|
122
138
|
.messages(domainMessages),
|
|
123
|
-
});
|
|
139
|
+
}).unknown();
|
|
124
140
|
|
|
125
141
|
const addRuleSchema = Joi.object({
|
|
126
142
|
id: Joi.string().required(),
|
|
@@ -142,10 +158,14 @@ const validateUpdateSite = (entity, context) => updateSite.validateAsync(entity,
|
|
|
142
158
|
const validateAddRule = (entity, context) => addRuleSchema.validateAsync(entity, getMultipleLangParams(context));
|
|
143
159
|
const validateEditRule = (entity, context) => editRuleSchema.validateAsync(entity, getMultipleLangParams(context));
|
|
144
160
|
|
|
161
|
+
const validateUpdateDomainAliases = (entity, context) =>
|
|
162
|
+
updateAliases.validateAsync(entity, getMultipleLangParams(context));
|
|
163
|
+
|
|
145
164
|
module.exports = {
|
|
146
165
|
validateAddRule,
|
|
147
166
|
validateAddSite,
|
|
148
167
|
validateEditRule,
|
|
149
168
|
validateAddDomainAlias,
|
|
150
169
|
validateUpdateSite,
|
|
170
|
+
validateUpdateDomainAliases,
|
|
151
171
|
};
|
package/lib/validators/util.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
const get = require('lodash/get');
|
|
2
|
+
const JOI = require('joi');
|
|
3
|
+
const { didExtension } = require('@blocklet/meta/lib/extension');
|
|
4
|
+
|
|
5
|
+
const Joi = JOI.extend(didExtension);
|
|
6
|
+
|
|
7
|
+
const getMultipleLangParams = (context) => ({
|
|
8
|
+
errors: {
|
|
9
|
+
language: get(context, 'query.locale', 'en'),
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const blockletController = Joi.object({
|
|
14
|
+
nftId: Joi.DID().required(),
|
|
15
|
+
nftOwner: Joi.DID().required(),
|
|
16
|
+
chainHost: Joi.string().uri().required(),
|
|
17
|
+
appMaxCount: Joi.number().required().min(1),
|
|
18
|
+
}).options({ stripUnknown: true });
|
|
19
|
+
|
|
20
|
+
const createValidator = (schema) => (entity) => schema.validateAsync(entity);
|
|
2
21
|
|
|
3
22
|
module.exports = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
}),
|
|
23
|
+
createValidator,
|
|
24
|
+
getMultipleLangParams,
|
|
25
|
+
blockletController,
|
|
9
26
|
};
|
package/lib/webhook/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
const logger = require('@abtnode/logger')('@abtnode/core:webhook:index');
|
|
2
|
-
const upperFirst = require('lodash/upperFirst');
|
|
3
2
|
|
|
3
|
+
const { evaluateURLs } = require('@abtnode/util/lib/url-evaluation');
|
|
4
|
+
const checkURLAccessible = require('@abtnode/util/lib/url-evaluation/check-accessible-node');
|
|
5
|
+
const { EVENTS } = require('@abtnode/constant');
|
|
4
6
|
const WebHookSender = require('./sender');
|
|
5
|
-
const
|
|
7
|
+
const WalletSender = require('./sender/wallet');
|
|
8
|
+
const createQueue = require('../util/queue');
|
|
6
9
|
const IP = require('../util/ip');
|
|
7
10
|
const states = require('../states');
|
|
8
11
|
const { getBaseUrls } = require('../util');
|
|
9
12
|
|
|
10
|
-
const getSlackUrlInfo = (actionPath = '/notifications', urls) => {
|
|
13
|
+
const getSlackUrlInfo = async (actionPath = '/notifications', urls) => {
|
|
11
14
|
const info = [];
|
|
12
15
|
|
|
13
16
|
if (actionPath && actionPath.startsWith('/blocklet/')) {
|
|
@@ -28,29 +31,30 @@ const getSlackUrlInfo = (actionPath = '/notifications', urls) => {
|
|
|
28
31
|
});
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
type: 'button',
|
|
42
|
-
text: {
|
|
43
|
-
type: 'plain_text',
|
|
44
|
-
text: `${upperFirst(x.type)} Address`,
|
|
45
|
-
},
|
|
46
|
-
style: 'primary',
|
|
47
|
-
value: `${upperFirst(x.type)} Address`,
|
|
48
|
-
url,
|
|
49
|
-
});
|
|
50
|
-
});
|
|
34
|
+
const prioritys = await evaluateURLs(
|
|
35
|
+
urls.map((item) => item.url),
|
|
36
|
+
{ checkAccessble: checkURLAccessible }
|
|
37
|
+
);
|
|
38
|
+
const priorityUrl = prioritys[0].url;
|
|
39
|
+
|
|
40
|
+
const { protocol } = new URL(priorityUrl);
|
|
41
|
+
const normalized = `${priorityUrl}${actionPath}`.replace(`${protocol}//`, '').replace(/\/+/g, '/');
|
|
42
|
+
const url = `${protocol}//${normalized}`;
|
|
43
|
+
|
|
51
44
|
info.push({
|
|
52
45
|
type: 'actions',
|
|
53
|
-
elements
|
|
46
|
+
elements: [
|
|
47
|
+
{
|
|
48
|
+
type: 'button',
|
|
49
|
+
text: {
|
|
50
|
+
type: 'plain_text',
|
|
51
|
+
text: 'Click Me',
|
|
52
|
+
},
|
|
53
|
+
style: 'primary',
|
|
54
|
+
value: 'Click Me',
|
|
55
|
+
url,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
54
58
|
});
|
|
55
59
|
|
|
56
60
|
return info;
|
|
@@ -66,9 +70,14 @@ module.exports = ({ events, dataDirs, instance }) => {
|
|
|
66
70
|
const webhookList = await webhookState.list();
|
|
67
71
|
const nodeInfo = await nodeState.read();
|
|
68
72
|
const { internal, external } = await IP.get();
|
|
69
|
-
const baseUrls = await getBaseUrls(instance,
|
|
73
|
+
const baseUrls = await getBaseUrls(instance, [external, internal]);
|
|
70
74
|
const senderFns = {};
|
|
71
75
|
|
|
76
|
+
// Always send message to wallet
|
|
77
|
+
webhookList.push({
|
|
78
|
+
type: WalletSender.type,
|
|
79
|
+
});
|
|
80
|
+
|
|
72
81
|
if (webhookList.length) {
|
|
73
82
|
for (let i = 0; i < webhookList.length; i++) {
|
|
74
83
|
const item = webhookList[i];
|
|
@@ -76,10 +85,10 @@ module.exports = ({ events, dataDirs, instance }) => {
|
|
|
76
85
|
const senderInstance = WebHookSender.getMessageSender(item.type);
|
|
77
86
|
senderFns[item.type] = senderInstance.send.bind(senderInstance);
|
|
78
87
|
}
|
|
79
|
-
const {
|
|
80
|
-
const options = { ...message, nodeInfo: `*${description}*` };
|
|
88
|
+
const options = { ...message, nodeInfo, node: instance };
|
|
81
89
|
if (item.type === 'slack') {
|
|
82
|
-
|
|
90
|
+
// eslint-disable-next-line
|
|
91
|
+
options.urlInfo = await getSlackUrlInfo(message.action, baseUrls);
|
|
83
92
|
}
|
|
84
93
|
try {
|
|
85
94
|
// eslint-disable-next-line
|
|
@@ -106,15 +115,15 @@ module.exports = ({ events, dataDirs, instance }) => {
|
|
|
106
115
|
},
|
|
107
116
|
});
|
|
108
117
|
|
|
109
|
-
events.on(
|
|
118
|
+
events.on(EVENTS.NOTIFICATION_CREATE, (data) => {
|
|
110
119
|
const { title, description, severity, action, entityType } = data;
|
|
111
120
|
queue.push({ title, description, status: severity, action, entityType });
|
|
112
121
|
});
|
|
113
122
|
|
|
114
|
-
events.on(
|
|
123
|
+
events.on(EVENTS.NODE_STARTED, async (message) => {
|
|
115
124
|
await sendMessage(message);
|
|
116
125
|
});
|
|
117
|
-
events.on(
|
|
126
|
+
events.on(EVENTS.NODE_STOPPED, async (message) => {
|
|
118
127
|
await sendMessage(message);
|
|
119
128
|
});
|
|
120
129
|
|
|
@@ -125,13 +134,14 @@ module.exports = ({ events, dataDirs, instance }) => {
|
|
|
125
134
|
|
|
126
135
|
const createWebhook = async (webhook) => {
|
|
127
136
|
try {
|
|
128
|
-
const
|
|
129
|
-
await sentTextMessage(
|
|
137
|
+
const mockCreateRes = await webhookState.create(webhook, { mock: true });
|
|
138
|
+
await sentTextMessage(mockCreateRes, `A ${mockCreateRes.type} integration is now *successfully added*`);
|
|
130
139
|
|
|
140
|
+
const createRes = await webhookState.create(webhook);
|
|
131
141
|
return createRes;
|
|
132
142
|
} catch (err) {
|
|
133
|
-
logger.error('create webhook
|
|
134
|
-
throw new Error(err.message);
|
|
143
|
+
logger.error('Failed to create webhook', { err });
|
|
144
|
+
throw new Error(`Failed to create webhook: ${err.message}`);
|
|
135
145
|
}
|
|
136
146
|
};
|
|
137
147
|
|
|
@@ -3,6 +3,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:sender');
|
|
|
3
3
|
|
|
4
4
|
const Slack = require('./slack');
|
|
5
5
|
const Api = require('./api');
|
|
6
|
+
const Wallet = require('./wallet');
|
|
6
7
|
|
|
7
8
|
const SenderMap = new Map([
|
|
8
9
|
[Slack.type, Slack],
|
|
@@ -12,6 +13,10 @@ const SenderMap = new Map([
|
|
|
12
13
|
const getSenderNames = () => [...SenderMap.keys()];
|
|
13
14
|
|
|
14
15
|
const getSender = (name) => {
|
|
16
|
+
if (name === Wallet.type) {
|
|
17
|
+
return Wallet;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
if (!SenderMap.has(name)) {
|
|
16
21
|
logger.error(`getSender:sender name [${name}] does not exist`);
|
|
17
22
|
return null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const logger = require('@abtnode/logger')('@abtnode/core:sender:api');
|
|
2
|
+
const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
|
|
3
|
+
const { ROLES, PASSPORT_STATUS } = require('@abtnode/constant');
|
|
4
|
+
const BaseSender = require('../base');
|
|
5
|
+
|
|
6
|
+
class WalletSender extends BaseSender {
|
|
7
|
+
async send(params, data = {}) {
|
|
8
|
+
const { title, description, nodeInfo, node } = data;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const sender = {
|
|
12
|
+
appDid: nodeInfo.did,
|
|
13
|
+
appSk: nodeInfo.sk,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const message = {
|
|
17
|
+
title,
|
|
18
|
+
body: description,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const { users } = await node.getUsers({ teamDid: nodeInfo.did, paging: { pageSize: 100 } });
|
|
22
|
+
const adminUsers = users
|
|
23
|
+
.filter(
|
|
24
|
+
(x) =>
|
|
25
|
+
x.approved &&
|
|
26
|
+
(x.passports || []).some(
|
|
27
|
+
(y) => [ROLES.OWNER, ROLES.ADMIN].includes(y.name) && y.status === PASSPORT_STATUS.VALID
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
.map((x) => x.did);
|
|
31
|
+
|
|
32
|
+
if (!adminUsers.length) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await sendToUser(adminUsers, message, sender, process.env.ABT_NODE_SERVICE_PORT);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
delete error.request;
|
|
39
|
+
delete error.response;
|
|
40
|
+
delete error.config;
|
|
41
|
+
logger.error('failed to push notification to wallet', { error });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
WalletSender.type = 'wallet';
|
|
47
|
+
|
|
48
|
+
module.exports = WalletSender;
|