@abtnode/core 1.7.9 → 1.7.12
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 +7 -1
- package/lib/api/team.js +2 -2
- package/lib/blocklet/manager/disk.js +153 -91
- package/lib/blocklet/registry.js +6 -2
- package/lib/cert.js +14 -0
- package/lib/event.js +9 -0
- package/lib/index.js +41 -9
- package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
- package/lib/router/index.js +8 -1
- package/lib/router/manager.js +3 -3
- package/lib/states/README.md +31 -1
- package/lib/states/audit-log.js +382 -0
- package/lib/states/blocklet.js +9 -7
- package/lib/states/index.js +3 -0
- package/lib/states/node.js +8 -0
- package/lib/util/blocklet.js +103 -45
- package/lib/util/index.js +37 -7
- package/lib/util/ip.js +6 -0
- package/lib/util/rpc.js +16 -0
- package/lib/util/ua.js +54 -0
- package/lib/util/upgrade.js +3 -15
- package/lib/validators/node.js +13 -0
- package/package.json +21 -21
- package/lib/util/service.js +0 -81
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const md5 = require('@abtnode/util/lib/md5');
|
|
4
|
+
const formatContext = require('@abtnode/util/lib/format-context');
|
|
4
5
|
const Cron = require('@abtnode/cron');
|
|
5
6
|
|
|
6
7
|
const logger = require('@abtnode/logger')('@abtnode/core');
|
|
@@ -10,6 +11,7 @@ const {
|
|
|
10
11
|
fromBlockletSource,
|
|
11
12
|
toBlockletSource,
|
|
12
13
|
} = require('@blocklet/meta/lib/constants');
|
|
14
|
+
const { getServiceMetas } = require('@blocklet/meta/lib/service');
|
|
13
15
|
const { listProviders } = require('@abtnode/router-provider');
|
|
14
16
|
const { DEFAULT_CERTIFICATE_EMAIL, EVENTS } = require('@abtnode/constant');
|
|
15
17
|
|
|
@@ -29,14 +31,13 @@ const IP = require('./util/ip');
|
|
|
29
31
|
const DomainStatus = require('./util/domain-status');
|
|
30
32
|
const Upgrade = require('./util/upgrade');
|
|
31
33
|
const resetNode = require('./util/reset-node');
|
|
32
|
-
const { getServices } = require('./util/service');
|
|
33
34
|
const DiskMonitor = require('./util/disk-monitor');
|
|
34
35
|
const createQueue = require('./queue');
|
|
35
36
|
const createEvents = require('./event');
|
|
36
37
|
const pm2Events = require('./blocklet/manager/pm2-events');
|
|
37
38
|
const { createStateReadyQueue, createStateReadyHandler } = require('./util/ready');
|
|
38
39
|
const { getSysInfo, SysInfoEmitter } = require('./util/sysinfo');
|
|
39
|
-
const { toStatus, fromStatus, ensureDataDirs, getQueueConcurrencyByMem } = require('./util');
|
|
40
|
+
const { toStatus, fromStatus, ensureDataDirs, getQueueConcurrencyByMem, getStateCrons } = require('./util');
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* @param {object} options
|
|
@@ -243,9 +244,9 @@ function ABTNode(options) {
|
|
|
243
244
|
getNodeInfo: nodeAPI.getInfo.bind(nodeAPI),
|
|
244
245
|
getNodeEnv: nodeAPI.getEnv.bind(nodeAPI),
|
|
245
246
|
updateNodeInfo: nodeAPI.updateNodeInfo.bind(nodeAPI),
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
addBlockletStore: nodeAPI.addRegistry.bind(nodeAPI),
|
|
248
|
+
deleteBlockletStore: nodeAPI.deleteRegistry.bind(nodeAPI),
|
|
249
|
+
selectBlockletStore: nodeAPI.selectRegistry.bind(nodeAPI),
|
|
249
250
|
cleanupDirtyUpgradeState: states.node.cleanupDirtyUpgradeState.bind(states.node),
|
|
250
251
|
addNodeOwner: states.node.addNodeOwner.bind(states.node),
|
|
251
252
|
updateNodeRouting,
|
|
@@ -253,6 +254,9 @@ function ABTNode(options) {
|
|
|
253
254
|
resetNode: (params, context) =>
|
|
254
255
|
resetNode({ params, context, blockletManager, routerManager, takeRoutingSnapshot, teamManager, certManager }),
|
|
255
256
|
|
|
257
|
+
// Gateway
|
|
258
|
+
updateGateway: nodeAPI.updateGateway.bind(nodeAPI),
|
|
259
|
+
|
|
256
260
|
// Team && Access control
|
|
257
261
|
|
|
258
262
|
// Invitation
|
|
@@ -314,6 +318,10 @@ function ABTNode(options) {
|
|
|
314
318
|
readNotifications: states.notification.read.bind(states.notification),
|
|
315
319
|
unreadNotifications: states.notification.unread.bind(states.notification),
|
|
316
320
|
|
|
321
|
+
// AuditLog
|
|
322
|
+
createAuditLog: (params) => states.auditLog.create(params, instance),
|
|
323
|
+
getAuditLogs: states.auditLog.find.bind(states.auditLog),
|
|
324
|
+
|
|
317
325
|
// Routing
|
|
318
326
|
routerManager,
|
|
319
327
|
addRoutingSite: routerManager.addRoutingSite.bind(routerManager),
|
|
@@ -339,7 +347,7 @@ function ABTNode(options) {
|
|
|
339
347
|
handleRouting,
|
|
340
348
|
|
|
341
349
|
certManager,
|
|
342
|
-
|
|
350
|
+
updateCertificate: certManager.update.bind(certManager),
|
|
343
351
|
getCertificates,
|
|
344
352
|
addCertificate: certManager.add.bind(certManager),
|
|
345
353
|
deleteCertificate: certManager.remove.bind(certManager),
|
|
@@ -377,7 +385,7 @@ function ABTNode(options) {
|
|
|
377
385
|
endSession: (params, context) => states.session.end(params.id, context),
|
|
378
386
|
|
|
379
387
|
// Services
|
|
380
|
-
getServices: (params, context) =>
|
|
388
|
+
getServices: (params, context) => getServiceMetas({ stringifySchema: true }, context),
|
|
381
389
|
|
|
382
390
|
// Utilities: moved here because some deps require native build
|
|
383
391
|
getSysInfo,
|
|
@@ -397,6 +405,7 @@ function ABTNode(options) {
|
|
|
397
405
|
...getRoutingCrons(),
|
|
398
406
|
...blockletManager.getCrons(),
|
|
399
407
|
DiskMonitor.getCron(),
|
|
408
|
+
...getStateCrons(states),
|
|
400
409
|
],
|
|
401
410
|
onError: (error, name) => {
|
|
402
411
|
states.notification.create({
|
|
@@ -410,22 +419,45 @@ function ABTNode(options) {
|
|
|
410
419
|
});
|
|
411
420
|
};
|
|
412
421
|
|
|
422
|
+
const createCLILog = (action) => {
|
|
423
|
+
instance
|
|
424
|
+
.createAuditLog(
|
|
425
|
+
{
|
|
426
|
+
action,
|
|
427
|
+
args: {},
|
|
428
|
+
context: formatContext({
|
|
429
|
+
user: { fullName: 'CLI', role: 'self', did: options.nodeDid },
|
|
430
|
+
headers: { 'user-agent': 'CLI' },
|
|
431
|
+
}),
|
|
432
|
+
result: null,
|
|
433
|
+
},
|
|
434
|
+
instance
|
|
435
|
+
)
|
|
436
|
+
.catch(console.error);
|
|
437
|
+
};
|
|
438
|
+
|
|
413
439
|
if (options.daemon) {
|
|
414
|
-
|
|
440
|
+
// 启动证书服务
|
|
441
|
+
certManager
|
|
442
|
+
.start()
|
|
443
|
+
.then(() => logger.info('start certificate manager service successfully'))
|
|
444
|
+
.catch((error) => logger.error('start certificate manager service failed', { error }));
|
|
415
445
|
|
|
416
446
|
if (process.env.NODE_ENV === 'development') {
|
|
417
447
|
initCron();
|
|
418
448
|
} else {
|
|
419
449
|
// We should only respond to pm2 events when node is alive
|
|
420
|
-
events.on(EVENTS.NODE_STARTED,
|
|
450
|
+
events.on(EVENTS.NODE_STARTED, () => {
|
|
421
451
|
pm2Events.resume();
|
|
422
452
|
initCron();
|
|
453
|
+
createCLILog('startServer');
|
|
423
454
|
});
|
|
424
455
|
}
|
|
425
456
|
}
|
|
426
457
|
|
|
427
458
|
events.on(EVENTS.NODE_STOPPED, () => {
|
|
428
459
|
pm2Events.pause();
|
|
460
|
+
createCLILog('stopServer');
|
|
429
461
|
});
|
|
430
462
|
|
|
431
463
|
return Object.assign(events, {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* eslint-disable no-continue */
|
|
2
|
+
/* eslint-disable no-await-in-loop */
|
|
3
|
+
/* eslint-disable no-underscore-dangle */
|
|
4
|
+
|
|
5
|
+
module.exports = async ({ states, printInfo }) => {
|
|
6
|
+
printInfo('Try to update blocklet server to 1.7.12...');
|
|
7
|
+
|
|
8
|
+
const blocklets = await states.blocklet.getBlocklets();
|
|
9
|
+
|
|
10
|
+
for (const blocklet of blocklets) {
|
|
11
|
+
let changed = false;
|
|
12
|
+
|
|
13
|
+
if (!blocklet.meta.bundleDid) {
|
|
14
|
+
blocklet.meta.bundleDid = blocklet.meta.did;
|
|
15
|
+
blocklet.meta.bundleName = blocklet.meta.name;
|
|
16
|
+
changed = true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
(blocklet.children || []).forEach((child) => {
|
|
20
|
+
if (!child.meta.bundleDid) {
|
|
21
|
+
child.meta.bundleDid = child.meta.did;
|
|
22
|
+
child.meta.bundleName = child.meta.name;
|
|
23
|
+
changed = true;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (changed) {
|
|
28
|
+
await states.blocklet.updateBlocklet(blocklet.meta.did, { meta: blocklet.meta, children: blocklet.children });
|
|
29
|
+
printInfo(`Blocklet meta in blocklet.db updated: ${blocklet.meta.did}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const blockletExtras = await states.blockletExtras.find({});
|
|
34
|
+
|
|
35
|
+
for (const extra of blockletExtras) {
|
|
36
|
+
let changed = false;
|
|
37
|
+
const children = await states.blockletExtras.getSettings(extra.did, 'children', []);
|
|
38
|
+
(children || []).forEach((child) => {
|
|
39
|
+
if (!child.meta.bundleDid) {
|
|
40
|
+
child.meta.bundleDid = child.meta.did;
|
|
41
|
+
child.meta.bundleName = child.meta.name;
|
|
42
|
+
changed = true;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (changed) {
|
|
47
|
+
await states.blockletExtras.setSettings(extra.did, { children });
|
|
48
|
+
printInfo(`Blocklet dynamic component meta in blocklet_extra.db updated: ${extra.did}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
package/lib/router/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
DEFAULT_IP_DOMAIN,
|
|
9
9
|
BLOCKLET_PROXY_PATH_PREFIX,
|
|
10
10
|
BLOCKLET_SITE_GROUP_SUFFIX,
|
|
11
|
+
GATEWAY_REQ_LIMIT,
|
|
11
12
|
} = require('@abtnode/constant');
|
|
12
13
|
const { BLOCKLET_UI_INTERFACES } = require('@blocklet/meta/lib/constants');
|
|
13
14
|
const logger = require('@abtnode/logger')('@abtnode/core:router');
|
|
@@ -107,12 +108,18 @@ class Router {
|
|
|
107
108
|
logger.info('updateRoutingTable routingTable:', { routingTable: this.routingTable });
|
|
108
109
|
logger.info('updateRoutingTable certificates:', { certificates: certificates.map((item) => item.domain) });
|
|
109
110
|
|
|
111
|
+
const requestLimit = nodeInfo.routing.requestLimit || { enable: false, rate: GATEWAY_REQ_LIMIT.min };
|
|
112
|
+
if (requestLimit.enabled) {
|
|
113
|
+
requestLimit.maxInstantRate = requestLimit.rate >= 20 ? 20 : requestLimit.rate + 20;
|
|
114
|
+
}
|
|
115
|
+
|
|
110
116
|
await this.provider.update({
|
|
111
117
|
routingTable: this.routingTable,
|
|
112
118
|
certificates,
|
|
113
|
-
|
|
119
|
+
commonHeaders: headers,
|
|
114
120
|
services,
|
|
115
121
|
nodeInfo: pick(nodeInfo, ['name', 'version', 'port', 'mode', 'enableWelcomePage', 'routing']),
|
|
122
|
+
requestLimit,
|
|
116
123
|
});
|
|
117
124
|
}
|
|
118
125
|
|
package/lib/router/manager.js
CHANGED
|
@@ -222,7 +222,7 @@ class RouterManager extends EventEmitter {
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
// let custom domain in front of protected domain
|
|
225
|
-
const domainAliases = [{ value: domainAlias, isProtected: false }, ...dbSite.domainAliases];
|
|
225
|
+
const domainAliases = [{ value: domainAlias, isProtected: false }, ...(dbSite.domainAliases || [])];
|
|
226
226
|
|
|
227
227
|
const updateResult = await states.site.update({ _id: id }, { $set: { domainAliases } });
|
|
228
228
|
logger.debug('add domain alias update result', { id, updateResult, domainAlias });
|
|
@@ -420,7 +420,7 @@ class RouterManager extends EventEmitter {
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
async getMatchedCert(domain) {
|
|
423
|
-
const certs = await this.certManager.
|
|
423
|
+
const certs = await this.certManager.getAllNormal();
|
|
424
424
|
const matchedCert = certs.find((cert) => this.isCertMatchedDomain(cert, domain));
|
|
425
425
|
|
|
426
426
|
if (matchedCert) {
|
|
@@ -546,7 +546,7 @@ class RouterManager extends EventEmitter {
|
|
|
546
546
|
getRoutingParams: async () => ({
|
|
547
547
|
sites: await ensureLatestInfo(sites),
|
|
548
548
|
certificates,
|
|
549
|
-
|
|
549
|
+
commonHeaders: get(info, 'routing.headers', {}),
|
|
550
550
|
services: [], // TODO: do we need to add some item here?
|
|
551
551
|
nodeInfo: info,
|
|
552
552
|
}),
|
package/lib/states/README.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
1
|
# State DB
|
|
2
2
|
|
|
3
|
-
All
|
|
3
|
+
All Blocklet Server states are managed by files in this folder.
|
|
4
|
+
|
|
5
|
+
## blocklet
|
|
6
|
+
|
|
7
|
+
- `meta` meta of bundle _defined in @blocklet/meta/schema_
|
|
8
|
+
- `did`, `name`: component id, default from source meta, can be changed before install
|
|
9
|
+
- `title`, `description`: component info, default from source meta, can be changed before install
|
|
10
|
+
- `bundleDdid`, `bundleName`: bundle id, copy from source meta
|
|
11
|
+
- `version`: component version
|
|
12
|
+
- `price`: used for charging
|
|
13
|
+
- `logo`: component logo
|
|
14
|
+
- `logoUrl`: component logo (from store)
|
|
15
|
+
- `interfaces`: for resolving: runtime port; service config; web base prefix; api base prefix
|
|
16
|
+
- `environments`: component environment
|
|
17
|
+
- `scripts`: for hook of component lifecycle
|
|
18
|
+
- `engine`, `timeout`, `group`, `main`: related to process startup
|
|
19
|
+
- `dist`: bundle source
|
|
20
|
+
- `source`: type of bundle source
|
|
21
|
+
- `deployedFrom`: information of bundle source
|
|
22
|
+
- `sourceUrl`: url of bundle source
|
|
23
|
+
- `appDid`: app instance id _app only_
|
|
24
|
+
- `status`, `startedAt`, `installedAt`, `stoppedAt` app status
|
|
25
|
+
- `deletedAt` _component only_
|
|
26
|
+
- `mode` app mode
|
|
27
|
+
- `ports` runtime port resolved from meta
|
|
28
|
+
- `<ENV_NAME>`: `<port number>`
|
|
29
|
+
- `environments[]`: env variables generated by server, e.g. BLOCKLET_APP_SK, BLOCKLET_APP_DID
|
|
30
|
+
- `<key>`: `<value>`
|
|
31
|
+
- `children`: component of app, same structure as parent, can be nested
|
|
32
|
+
- `mountPoint` mount path of web site. _component only_
|
|
33
|
+
- `dynamic` is dynamically added. _component only_
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/* eslint-disable no-async-promise-executor */
|
|
2
|
+
const pick = require('lodash/pick');
|
|
3
|
+
const get = require('lodash/get');
|
|
4
|
+
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
5
|
+
const { BLOCKLET_SITE_GROUP_SUFFIX } = require('@abtnode/constant');
|
|
6
|
+
const logger = require('@abtnode/logger')('@abtnode/core:states:audit-log');
|
|
7
|
+
|
|
8
|
+
const BaseState = require('./base');
|
|
9
|
+
|
|
10
|
+
const { parse } = require('../util/ua');
|
|
11
|
+
const { lookup } = require('../util/ip');
|
|
12
|
+
|
|
13
|
+
const getServerInfo = (info) => `[${info.name}](${info.routing.adminPath}/settings/about)`;
|
|
14
|
+
const getBlockletInfo = (blocklet, info) => `[${getDisplayName(blocklet)} v${blocklet.meta.version}](${info.routing.adminPath}/blocklets/${blocklet.meta.did})`; // prettier-ignore
|
|
15
|
+
const expandTeam = async (teamDid, info, node) => {
|
|
16
|
+
if (!teamDid) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (teamDid === info.did) {
|
|
21
|
+
return getServerInfo(info);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { blocklet } = node.states;
|
|
25
|
+
const doc = await blocklet.getBlocklet(teamDid);
|
|
26
|
+
return doc ? getBlockletInfo(doc, info) : '';
|
|
27
|
+
};
|
|
28
|
+
const expandSite = async (siteId, info, node) => {
|
|
29
|
+
if (!siteId) {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { blocklet, site } = node.states;
|
|
34
|
+
const doc = await site.findOne({ _id: siteId });
|
|
35
|
+
if (!doc) {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (doc.domain === '*') {
|
|
40
|
+
return 'default site';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (doc.domain === '') {
|
|
44
|
+
return getServerInfo(info);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (doc.domain.endsWith(BLOCKLET_SITE_GROUP_SUFFIX)) {
|
|
48
|
+
const tmp = await blocklet.getBlocklet(doc.domain.replace(BLOCKLET_SITE_GROUP_SUFFIX, ''));
|
|
49
|
+
return tmp ? getBlockletInfo(tmp, info) : '';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return '';
|
|
53
|
+
};
|
|
54
|
+
const expandUser = async (teamDid, userDid, passportId, info, node) => {
|
|
55
|
+
if (!teamDid || !userDid) {
|
|
56
|
+
return ['', ''];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const user = await node.getUser({ teamDid, user: { did: userDid } });
|
|
60
|
+
if (!user) {
|
|
61
|
+
return ['', ''];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const passport = user.passports.find((x) => x.id === passportId);
|
|
65
|
+
|
|
66
|
+
if (teamDid === info.did) {
|
|
67
|
+
return [`[${user.fullName}](${info.routing.adminPath}/team/members)`, passport ? passport.name : ''];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return [`[${user.fullName}](${info.routing.adminPath}/blocklets/${teamDid}/members)`, passport ? passport.name : ''];
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create log content in markdown format
|
|
75
|
+
*
|
|
76
|
+
* @param {string} action - GraphQL query/mutation name
|
|
77
|
+
* @param {object} args - GraphQL arguments
|
|
78
|
+
* @param {object} context - request context: user, ip, user-agent, etc.
|
|
79
|
+
* @param {object} result - GraphQL resolve result
|
|
80
|
+
* @param {object} info - server info
|
|
81
|
+
* @param {object} node - server instance
|
|
82
|
+
* @return {string} the generated markdown source
|
|
83
|
+
*/
|
|
84
|
+
const getLogContent = async (action, args, context, result, info, node) => {
|
|
85
|
+
const [team, site, [user, passport]] = await Promise.all([
|
|
86
|
+
expandTeam(args.teamDid, info, node),
|
|
87
|
+
expandSite(args.id, info, node),
|
|
88
|
+
expandUser(args.teamDid, args.userDid || get(args, 'user.did') || args.ownerDid, args.passportId, info, node),
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
switch (action) {
|
|
92
|
+
// blocklets
|
|
93
|
+
case 'installBlocklet':
|
|
94
|
+
return `installed blocklet ${getBlockletInfo(result, info)} from ${result.deployedFrom}`;
|
|
95
|
+
case 'startBlocklet':
|
|
96
|
+
return `started blocklet ${getBlockletInfo(result, info)}`;
|
|
97
|
+
case 'restartBlocklet':
|
|
98
|
+
return `restarted blocklet ${getBlockletInfo(result, info)}`;
|
|
99
|
+
case 'reloadBlocklet':
|
|
100
|
+
return `reloaded blocklet ${getBlockletInfo(result, info)}`;
|
|
101
|
+
case 'stopBlocklet':
|
|
102
|
+
return `stopped blocklet ${getBlockletInfo(result, info)}`;
|
|
103
|
+
case 'resetBlocklet':
|
|
104
|
+
return `reset blocklet ${getBlockletInfo(result, info)} data and config`;
|
|
105
|
+
case 'deleteBlocklet':
|
|
106
|
+
return `removed blocklet ${getBlockletInfo(result, info)} ${args.keepData ? 'but kept its data and config' : 'and its data and config'}`; // prettier-ignore
|
|
107
|
+
case 'installComponent':
|
|
108
|
+
return `added component from ${args.file ? 'Upload' : args.url} at **${args.mountPoint}** to ${getBlockletInfo(result, info)}`; // prettier-ignore
|
|
109
|
+
case 'deleteComponent':
|
|
110
|
+
return `removed component ${args.did} from blocklet ${getBlockletInfo(result, info)}`;
|
|
111
|
+
case 'configBlocklet':
|
|
112
|
+
return `updated following ${args.childDid ? `child ${args.childDid}` : ''} config for blocklet ${getBlockletInfo(result, info)}:\n${args.configs.map(x => `- ${x.key}: ${x.value}\n`)}`; // prettier-ignore
|
|
113
|
+
case 'upgradeBlocklet':
|
|
114
|
+
return `upgraded blocklet ${getBlockletInfo(result, info)} to v${result.meta.version}`;
|
|
115
|
+
case 'updateChildBlocklets':
|
|
116
|
+
return `upgraded components for blocklet ${getBlockletInfo(result, info)}`;
|
|
117
|
+
|
|
118
|
+
// store
|
|
119
|
+
case 'addBlockletStore':
|
|
120
|
+
return `added blocklet store ${args.url}`;
|
|
121
|
+
case 'deleteBlockletStore':
|
|
122
|
+
return `removed blocklet store ${args.url}`;
|
|
123
|
+
case 'selectBlockletStore':
|
|
124
|
+
return `selected blocklet store ${args.url}`;
|
|
125
|
+
|
|
126
|
+
// teams: members/passports
|
|
127
|
+
case 'addUser':
|
|
128
|
+
if (args.passport) {
|
|
129
|
+
return `${args.reason} and received **${args.passport.name}** passport from ${team}`;
|
|
130
|
+
}
|
|
131
|
+
return `joined team ${team} by ${args.reason}`;
|
|
132
|
+
case 'updateUser':
|
|
133
|
+
return `${args.reason} and received **${args.passport.name}** passport from ${team}`;
|
|
134
|
+
case 'configWhoCanAccess':
|
|
135
|
+
return `updated access control policy to **${args.value}** for ${team} when ${args.reason}`;
|
|
136
|
+
case 'configPassportIssuance':
|
|
137
|
+
return `${args.enabled ? 'enabled' : 'disabled'} passport issuance for ${team}`;
|
|
138
|
+
case 'createPassportIssuance':
|
|
139
|
+
return `issued **${args.name}** passport to ${user} for ${team}, issuance id: ${result.id}`;
|
|
140
|
+
case 'processPassportIssuance':
|
|
141
|
+
return `claimed passport **${args.name}** from ${team}, issuance id: ${args.sessionId}`;
|
|
142
|
+
case 'revokeUserPassport':
|
|
143
|
+
return `revoked **${passport}** passport of user ${user} for ${team}`;
|
|
144
|
+
case 'enableUserPassport':
|
|
145
|
+
return `enabled **${passport}** passport of user ${user} for ${team}`;
|
|
146
|
+
case 'updateUserApproval':
|
|
147
|
+
return `${args.user.approved ? 'enabled' : 'disabled'} user ${user} for ${team}`;
|
|
148
|
+
case 'deletePassportIssuance':
|
|
149
|
+
return `removed passport issuance ${args.sessionId} from ${team}`;
|
|
150
|
+
case 'createInvitation':
|
|
151
|
+
return `created member invitation(${result.inviteId}: ${args.remark}) with **${args.role}** passport for ${team}`; // prettier-ignore
|
|
152
|
+
case 'deleteInvitation':
|
|
153
|
+
return `removed unused member invitation(${args.inviteId}) from ${team}`;
|
|
154
|
+
case 'createRole':
|
|
155
|
+
return `created passport ${args.name}(${args.title}) for ${team}`;
|
|
156
|
+
case 'updateRole':
|
|
157
|
+
return `updated passport ${args.role.name}(${args.role.title}) for ${team}`;
|
|
158
|
+
case 'updatePermissionsForRole':
|
|
159
|
+
return `granted following permissions to passport ${args.roleName} for ${team}: \n${args.grantNames.map(x => `- ${x}`).join('\n')}`; // prettier-ignore
|
|
160
|
+
case 'configTrustedPassports':
|
|
161
|
+
if (args.trustedPassports.length === 0) {
|
|
162
|
+
return `removed all trusted passport issuers for ${team}`;
|
|
163
|
+
}
|
|
164
|
+
return `updated trusted passport issuers to following for ${team}: \n${args.trustedPassports.map(x => `- ${x.remark}: ${x.issuerDid}`).join('\n')}`; // prettier-ignore
|
|
165
|
+
|
|
166
|
+
// accessKeys
|
|
167
|
+
case 'createAccessKey':
|
|
168
|
+
return `created access key ${result.accessKeyId} with passport ${args.passport}: ${args.remark}`;
|
|
169
|
+
case 'updateAccessKey':
|
|
170
|
+
return `updated access key ${result.accessKeyId} with following changes:\n - passport: ${args.passport}\n - remark: ${args.remark}`; // prettier-ignore
|
|
171
|
+
case 'deleteAccessKey':
|
|
172
|
+
return `deleted access key ${args.accessKeyId}`; // prettier-ignore
|
|
173
|
+
|
|
174
|
+
// integrations
|
|
175
|
+
case 'createWebHook':
|
|
176
|
+
return `added integration ${result._id}: \n- type: ${args.type}\n${args.params.map(x => `- ${x.name}: ${x.value}`).join('\n')}`; // prettier-ignore
|
|
177
|
+
case 'deleteWebHook':
|
|
178
|
+
return `deleted integration ${args.id}`;
|
|
179
|
+
|
|
180
|
+
// server
|
|
181
|
+
case 'updateNodeInfo':
|
|
182
|
+
return `updated basic server settings: \n${Object.keys(args).map(x => `- ${x}: ${args[x]}`).join('\n')}`; // prettier-ignore
|
|
183
|
+
case 'upgradeNodeVersion':
|
|
184
|
+
return `triggered server upgrade to ${info.nextVersion}`;
|
|
185
|
+
case 'startServer':
|
|
186
|
+
return 'server was started';
|
|
187
|
+
case 'stopServer':
|
|
188
|
+
return 'server was stopped';
|
|
189
|
+
|
|
190
|
+
// certificates
|
|
191
|
+
case 'addCertificate':
|
|
192
|
+
return `added certificate #${args.id}, domain ${result.domain}`;
|
|
193
|
+
case 'deleteCertificate':
|
|
194
|
+
return `deleted certificate #${args.id}`;
|
|
195
|
+
case 'updateCertificate':
|
|
196
|
+
return `updated certificate #${args.id}`;
|
|
197
|
+
case 'issueLetsEncryptCert':
|
|
198
|
+
return `tried to issue lets encrypt certificate for domain **${args.domain}**, ticket: ${result._id}`;
|
|
199
|
+
|
|
200
|
+
// router
|
|
201
|
+
case 'addDomainAlias':
|
|
202
|
+
return `added extra domain **${args.domainAlias}** to ${site}`; // prettier-ignore
|
|
203
|
+
case 'deleteDomainAlias':
|
|
204
|
+
return `removed extra domain **${args.domainAlias}** from ${site}`; // prettier-ignore
|
|
205
|
+
case 'updateGateway': {
|
|
206
|
+
let message = args.requestLimit.enabled ? `status: enabled, rate: ${args.requestLimit.rate}` : 'status: disabled';
|
|
207
|
+
message = `update gateway. ${message}`;
|
|
208
|
+
|
|
209
|
+
return message;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
default:
|
|
213
|
+
return action;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const getLogCategory = (action) => {
|
|
218
|
+
switch (action) {
|
|
219
|
+
// blocklets
|
|
220
|
+
case 'installBlocklet':
|
|
221
|
+
case 'installComponent':
|
|
222
|
+
case 'startBlocklet':
|
|
223
|
+
case 'restartBlocklet':
|
|
224
|
+
case 'reloadBlocklet':
|
|
225
|
+
case 'stopBlocklet':
|
|
226
|
+
case 'resetBlocklet':
|
|
227
|
+
case 'deleteBlocklet':
|
|
228
|
+
case 'deleteComponent':
|
|
229
|
+
case 'configBlocklet':
|
|
230
|
+
case 'upgradeBlocklet':
|
|
231
|
+
case 'updateChildBlocklets':
|
|
232
|
+
return 'blocklet';
|
|
233
|
+
|
|
234
|
+
// store
|
|
235
|
+
case 'addBlockletStore':
|
|
236
|
+
case 'deleteBlockletStore':
|
|
237
|
+
case 'selectBlockletStore':
|
|
238
|
+
return 'server';
|
|
239
|
+
|
|
240
|
+
// teams: members/passports
|
|
241
|
+
case 'addUser':
|
|
242
|
+
case 'updateUser':
|
|
243
|
+
case 'configWhoCanAccess':
|
|
244
|
+
case 'processPassportIssuance':
|
|
245
|
+
case 'configPassportIssuance':
|
|
246
|
+
case 'createPassportIssuance':
|
|
247
|
+
case 'deletePassportIssuance':
|
|
248
|
+
case 'revokeUserPassport':
|
|
249
|
+
case 'enableUserPassport':
|
|
250
|
+
case 'updateUserApproval':
|
|
251
|
+
case 'createInvitation':
|
|
252
|
+
case 'deleteInvitation':
|
|
253
|
+
case 'createRole':
|
|
254
|
+
case 'updateRole':
|
|
255
|
+
case 'updatePermissionsForRole':
|
|
256
|
+
case 'configTrustedPassports':
|
|
257
|
+
return 'team';
|
|
258
|
+
|
|
259
|
+
// accessKeys
|
|
260
|
+
case 'createAccessKey':
|
|
261
|
+
case 'updateAccessKey':
|
|
262
|
+
case 'deleteAccessKey':
|
|
263
|
+
return 'security';
|
|
264
|
+
|
|
265
|
+
// integrations
|
|
266
|
+
case 'createWebHook':
|
|
267
|
+
case 'deleteWebHook':
|
|
268
|
+
return 'integrations';
|
|
269
|
+
|
|
270
|
+
// server
|
|
271
|
+
case 'updateNodeInfo':
|
|
272
|
+
case 'upgradeNodeVersion':
|
|
273
|
+
case 'startServer':
|
|
274
|
+
case 'stopServer':
|
|
275
|
+
return 'server';
|
|
276
|
+
|
|
277
|
+
// certificates
|
|
278
|
+
case 'addCertificate':
|
|
279
|
+
case 'deleteCertificate':
|
|
280
|
+
case 'updateCertificate':
|
|
281
|
+
case 'issueLetsEncryptCert':
|
|
282
|
+
return 'certificates';
|
|
283
|
+
|
|
284
|
+
case 'updateGateway':
|
|
285
|
+
return 'gateway';
|
|
286
|
+
|
|
287
|
+
default:
|
|
288
|
+
return '';
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
class AuditLogState extends BaseState {
|
|
293
|
+
constructor(baseDir, options = {}) {
|
|
294
|
+
super(baseDir, { filename: 'audit-log.db', ...options });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Create new audit log
|
|
299
|
+
*
|
|
300
|
+
* @param {string} action the api name
|
|
301
|
+
* @param {object} args the api args
|
|
302
|
+
* @param {object} result the api result
|
|
303
|
+
* @param {object} context the log context
|
|
304
|
+
* {
|
|
305
|
+
protocol: 'http',
|
|
306
|
+
user: {
|
|
307
|
+
meta: 'profile',
|
|
308
|
+
fullName: 'wangshijun',
|
|
309
|
+
email: 'shijun@arcblock.io',
|
|
310
|
+
type: 'profile',
|
|
311
|
+
did: 'z1jq5bGF64wnZiy29EbRyuQqUxmRHmSdt1Q',
|
|
312
|
+
pk: 'zHTcKwgi9DK8US8QQY9K7wQ3qF79CtrWYn6BYAgTQUeVQ',
|
|
313
|
+
passports: [ [Object] ],
|
|
314
|
+
approved: true,
|
|
315
|
+
locale: 'en',
|
|
316
|
+
firstLoginAt: '2022-04-27T22:55:51.788Z',
|
|
317
|
+
lastLoginAt: '2022-04-27T22:55:51.788Z',
|
|
318
|
+
_id: 'dvOIJJVUJHGxnWBU',
|
|
319
|
+
createdAt: '2022-04-27T22:55:51.789Z',
|
|
320
|
+
updatedAt: '2022-04-27T22:55:51.789Z',
|
|
321
|
+
role: 'owner',
|
|
322
|
+
passportId: 'z2iTzSDhwGQFtYXeya8kqCyXro1tRkwUuwN6K'
|
|
323
|
+
},
|
|
324
|
+
url: '/api/gql?locale=en',
|
|
325
|
+
query: { locale: 'en' },
|
|
326
|
+
hostname: '192.168.123.236',
|
|
327
|
+
port: 0,
|
|
328
|
+
ip: '127.0.0.1'
|
|
329
|
+
}
|
|
330
|
+
* @return {object} new doc
|
|
331
|
+
*/
|
|
332
|
+
create({ action, args, context, result }, node) {
|
|
333
|
+
return new Promise(async (resolve) => {
|
|
334
|
+
// Do not store secure configs in audit log
|
|
335
|
+
if (Array.isArray(args.configs)) {
|
|
336
|
+
args.configs.forEach((x) => {
|
|
337
|
+
if (x.secure) {
|
|
338
|
+
x.value = '******';
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
const { ip, ua, user } = context;
|
|
345
|
+
const [info, geoInfo, uaInfo] = await Promise.all([node.states.node.read(), lookup(ip), parse(ua)]);
|
|
346
|
+
|
|
347
|
+
const data = await this.asyncDB.insert({
|
|
348
|
+
scope: args.teamDid || info.did, // server or blocklet did
|
|
349
|
+
action,
|
|
350
|
+
category: await getLogCategory(action, args, context, result, info, node),
|
|
351
|
+
content: (await getLogContent(action, args, context, result, info, node)).trim(),
|
|
352
|
+
actor: pick(user.actual || user, ['did', 'fullName', 'role']),
|
|
353
|
+
extra: args,
|
|
354
|
+
env: pick(uaInfo, ['browser', 'os', 'device']),
|
|
355
|
+
geo: pick(geoInfo || { country: '', city: '' }, ['country', 'city']),
|
|
356
|
+
ip,
|
|
357
|
+
ua,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
logger.info('create', data);
|
|
361
|
+
return resolve(data);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
logger.error('create error', { error: err, action, args, context });
|
|
364
|
+
return resolve(null);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async find({ scope, category, paging } = {}) {
|
|
370
|
+
const conditions = {};
|
|
371
|
+
if (scope) {
|
|
372
|
+
conditions.scope = scope;
|
|
373
|
+
}
|
|
374
|
+
if (category) {
|
|
375
|
+
conditions.category = category;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return this.paginate(conditions, { createdAt: -1 }, { pageSize: 20, ...paging });
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
module.exports = AuditLogState;
|