@abtnode/core 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.
Files changed (119) hide show
  1. package/lib/api/node.js +67 -69
  2. package/lib/api/team.js +386 -55
  3. package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
  4. package/lib/blocklet/downloader/bundle-downloader.js +272 -0
  5. package/lib/blocklet/downloader/constants.js +3 -0
  6. package/lib/blocklet/downloader/resolve-download.js +199 -0
  7. package/lib/blocklet/extras.js +83 -26
  8. package/lib/blocklet/hooks.js +18 -65
  9. package/lib/blocklet/manager/base.js +10 -16
  10. package/lib/blocklet/manager/disk.js +1680 -1566
  11. package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
  12. package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
  13. package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
  14. package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
  15. package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
  16. package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
  17. package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
  18. package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
  19. package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
  20. package/lib/blocklet/migration.js +30 -52
  21. package/lib/blocklet/storage/backup/audit-log.js +27 -0
  22. package/lib/blocklet/storage/backup/base.js +62 -0
  23. package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
  24. package/lib/blocklet/storage/backup/blocklet.js +70 -0
  25. package/lib/blocklet/storage/backup/blocklets.js +74 -0
  26. package/lib/blocklet/storage/backup/data.js +19 -0
  27. package/lib/blocklet/storage/backup/logs.js +24 -0
  28. package/lib/blocklet/storage/backup/routing-rule.js +19 -0
  29. package/lib/blocklet/storage/backup/spaces.js +240 -0
  30. package/lib/blocklet/storage/restore/base.js +67 -0
  31. package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
  32. package/lib/blocklet/storage/restore/blocklet.js +56 -0
  33. package/lib/blocklet/storage/restore/blocklets.js +43 -0
  34. package/lib/blocklet/storage/restore/logs.js +21 -0
  35. package/lib/blocklet/storage/restore/spaces.js +156 -0
  36. package/lib/blocklet/storage/utils/hash.js +51 -0
  37. package/lib/blocklet/storage/utils/zip.js +43 -0
  38. package/lib/cert.js +206 -0
  39. package/lib/event.js +237 -64
  40. package/lib/index.js +191 -83
  41. package/lib/migrations/1.0.21-update-config.js +1 -1
  42. package/lib/migrations/1.0.22-max-memory.js +1 -1
  43. package/lib/migrations/1.0.25.js +1 -1
  44. package/lib/migrations/1.0.32-update-config.js +1 -1
  45. package/lib/migrations/1.0.33-blocklets.js +1 -1
  46. package/lib/migrations/1.5.20-registry.js +15 -0
  47. package/lib/migrations/1.6.17-blocklet-children.js +48 -0
  48. package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
  49. package/lib/migrations/1.6.4-security.js +59 -0
  50. package/lib/migrations/1.6.5-security.js +60 -0
  51. package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
  52. package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
  53. package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
  54. package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
  55. package/lib/migrations/1.7.20-blocklet-component.js +41 -0
  56. package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
  57. package/lib/migrations/README.md +1 -1
  58. package/lib/migrations/index.js +6 -2
  59. package/lib/monitor/blocklet-runtime-monitor.js +200 -0
  60. package/lib/monitor/get-history-list.js +37 -0
  61. package/lib/monitor/node-runtime-monitor.js +228 -0
  62. package/lib/router/helper.js +576 -500
  63. package/lib/router/index.js +85 -21
  64. package/lib/router/manager.js +146 -187
  65. package/lib/states/README.md +36 -1
  66. package/lib/states/access-key.js +39 -17
  67. package/lib/states/audit-log.js +462 -0
  68. package/lib/states/base.js +4 -213
  69. package/lib/states/blocklet-extras.js +195 -138
  70. package/lib/states/blocklet.js +371 -110
  71. package/lib/states/cache.js +8 -6
  72. package/lib/states/challenge.js +5 -5
  73. package/lib/states/index.js +19 -36
  74. package/lib/states/migration.js +4 -4
  75. package/lib/states/node.js +135 -46
  76. package/lib/states/notification.js +22 -35
  77. package/lib/states/session.js +17 -9
  78. package/lib/states/site.js +50 -25
  79. package/lib/states/user.js +74 -20
  80. package/lib/states/webhook.js +10 -6
  81. package/lib/team/manager.js +124 -7
  82. package/lib/util/blocklet.js +1223 -246
  83. package/lib/util/chain.js +1 -1
  84. package/lib/util/default-node-config.js +5 -23
  85. package/lib/util/disk-monitor.js +13 -10
  86. package/lib/util/domain-status.js +84 -15
  87. package/lib/util/get-accessible-external-node-ip.js +2 -2
  88. package/lib/util/get-domain-for-blocklet.js +13 -0
  89. package/lib/util/get-meta-from-url.js +33 -0
  90. package/lib/util/index.js +207 -272
  91. package/lib/util/ip.js +6 -0
  92. package/lib/util/maintain.js +233 -0
  93. package/lib/util/public-to-store.js +85 -0
  94. package/lib/util/ready.js +1 -1
  95. package/lib/util/requirement.js +28 -9
  96. package/lib/util/reset-node.js +22 -7
  97. package/lib/util/router.js +13 -0
  98. package/lib/util/rpc.js +16 -0
  99. package/lib/util/store.js +179 -0
  100. package/lib/util/sysinfo.js +44 -0
  101. package/lib/util/ua.js +54 -0
  102. package/lib/validators/blocklet-extra.js +24 -0
  103. package/lib/validators/node.js +25 -12
  104. package/lib/validators/permission.js +16 -1
  105. package/lib/validators/role.js +17 -3
  106. package/lib/validators/router.js +40 -20
  107. package/lib/validators/trusted-passport.js +1 -0
  108. package/lib/validators/util.js +22 -5
  109. package/lib/webhook/index.js +45 -35
  110. package/lib/webhook/sender/index.js +5 -0
  111. package/lib/webhook/sender/slack/index.js +1 -1
  112. package/lib/webhook/sender/wallet/index.js +48 -0
  113. package/package.json +54 -36
  114. package/lib/blocklet/registry.js +0 -205
  115. package/lib/states/https-cert.js +0 -67
  116. package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
  117. package/lib/util/service.js +0 -66
  118. package/lib/util/upgrade.js +0 -178
  119. /package/lib/{queue.js → util/queue.js} +0 -0
@@ -1,3 +1,4 @@
1
+ const get = require('lodash/get');
1
2
  const { fromRandom } = require('@ocap/wallet');
2
3
  const { toBase58 } = require('@ocap/util');
3
4
  const logger = require('@abtnode/logger')('@abtnode/core:access-key');
@@ -15,20 +16,23 @@ const validatePassport = (passport) => {
15
16
  }
16
17
  };
17
18
 
18
- class AccessKeyState extends BaseState {
19
- constructor(baseDir, options = {}) {
20
- super(baseDir, { filename: 'access_key.db', ...options });
19
+ const getUserName = (context) => get(context, 'user.fullName', '');
21
20
 
22
- this.db.ensureIndex({ fieldName: 'accessKeyId', unique: true }, (error) => {
23
- if (error) {
24
- logger.error('ensure unique index failed', { error });
25
- }
21
+ class AccessKeyState extends BaseState {
22
+ constructor(baseDir, config = {}) {
23
+ super(baseDir, { filename: 'access_key.db', ...config });
24
+
25
+ this.onReady(() => {
26
+ this.ensureIndex({ fieldName: 'accessKeyId', unique: true }, (error) => {
27
+ if (error) {
28
+ logger.error('ensure unique index failed', { error });
29
+ }
30
+ });
26
31
  });
27
32
  }
28
33
 
29
- // eslint-disable-next-line no-unused-vars
30
- async create(input = {}, context) {
31
- const { remark, passport } = input;
34
+ async create(input, context) {
35
+ const { remark, passport, tag } = input || {};
32
36
 
33
37
  validateRemark(remark);
34
38
  validatePassport(passport);
@@ -39,16 +43,33 @@ class AccessKeyState extends BaseState {
39
43
  accessKeyPublic: wallet.publicKey,
40
44
  passport,
41
45
  };
46
+
42
47
  if (remark) {
43
48
  data.remark = remark;
44
49
  }
45
- const doc = await this.asyncDB.insert(data);
50
+
51
+ if (tag) {
52
+ data.tag = tag;
53
+ }
54
+
55
+ data.createdBy = getUserName(context);
56
+ data.updatedBy = getUserName(context);
57
+
58
+ const doc = await this.insert(data);
46
59
  return {
47
60
  ...doc,
48
61
  accessKeySecret: toBase58(wallet.secretKey),
49
62
  };
50
63
  }
51
64
 
65
+ async getAccessKeyByTag({ tag } = {}) {
66
+ if (!tag) {
67
+ throw new Error('tag should not be empty');
68
+ }
69
+
70
+ return this.findOne({ tag });
71
+ }
72
+
52
73
  // eslint-disable-next-line no-unused-vars
53
74
  async list(params, context) {
54
75
  const res = await this.paginate({}, { createdAt: -1 }, { pageSize: 100 });
@@ -61,7 +82,7 @@ class AccessKeyState extends BaseState {
61
82
  if (!accessKeyId) {
62
83
  throw new Error('accessKeyId should not be empty');
63
84
  }
64
- const doc = await this.asyncDB.findOne({ accessKeyId });
85
+ const doc = await this.findOne({ accessKeyId });
65
86
  return doc;
66
87
  }
67
88
 
@@ -76,7 +97,7 @@ class AccessKeyState extends BaseState {
76
97
  if (!accessKeyId) {
77
98
  throw new Error('accessKeyId should not be empty');
78
99
  }
79
- const doc = await this.asyncDB.findOne({ accessKeyId });
100
+ const doc = await this.findOne({ accessKeyId });
80
101
  if (!doc) {
81
102
  throw new Error(`Access Key Id ${accessKeyId} does not exist`);
82
103
  }
@@ -84,8 +105,9 @@ class AccessKeyState extends BaseState {
84
105
  doc.remark = remark;
85
106
  }
86
107
  doc.passport = passport;
108
+ doc.updatedBy = getUserName(context);
87
109
 
88
- await this.asyncDB.update({ accessKeyId }, { $set: doc });
110
+ await super.update({ accessKeyId }, { $set: doc });
89
111
  return doc;
90
112
  }
91
113
 
@@ -94,12 +116,12 @@ class AccessKeyState extends BaseState {
94
116
  if (!accessKeyId) {
95
117
  throw new Error('accessKeyId should not be empty');
96
118
  }
97
- const doc = await this.asyncDB.findOne({ accessKeyId });
119
+ const doc = await this.findOne({ accessKeyId });
98
120
  if (!doc) {
99
121
  throw new Error(`Access Key Id ${accessKeyId} does not exist`);
100
122
  }
101
123
  doc.lastUsedAt = new Date();
102
- await this.asyncDB.update({ accessKeyId }, { $set: doc });
124
+ await super.update({ accessKeyId }, { $set: doc });
103
125
  return doc;
104
126
  }
105
127
 
@@ -109,7 +131,7 @@ class AccessKeyState extends BaseState {
109
131
  if (!accessKeyId) {
110
132
  throw new Error('accessKeyId should not be empty');
111
133
  }
112
- const num = await this.asyncDB.remove({ accessKeyId });
134
+ const num = await super.remove({ accessKeyId });
113
135
  if (num <= 0) {
114
136
  throw new Error(`Access Key Id ${accessKeyId} does not exist`);
115
137
  }
@@ -0,0 +1,462 @@
1
+ /* eslint-disable no-async-promise-executor */
2
+ const pick = require('lodash/pick');
3
+ const get = require('lodash/get');
4
+ const joinUrl = require('url-join');
5
+ const { getDisplayName } = require('@blocklet/meta/lib/util');
6
+ const { BLOCKLET_SITE_GROUP_SUFFIX, NODE_SERVICES } = require('@abtnode/constant');
7
+ const logger = require('@abtnode/logger')('@abtnode/core:states:audit-log');
8
+
9
+ const BaseState = require('./base');
10
+
11
+ const { parse } = require('../util/ua');
12
+
13
+ const getServerInfo = (info) => `[${info.name}](${joinUrl(info.routing.adminPath, '/settings/about')})`;
14
+ const getBlockletInfo = (blocklet, info) => `[${getDisplayName(blocklet)} v${blocklet.meta.version}](${joinUrl(info.routing.adminPath, '/blocklets/', blocklet.meta.did, '/overview')})`; // 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}](${joinUrl(info.routing.adminPath, '/team/members')})`, passport ? passport.name : ''];
68
+ }
69
+
70
+ return [`[${user.fullName}](${joinUrl(info.routing.adminPath, '/blocklets/', teamDid, '/members')})`, passport ? passport.name : '']; // prettier-ignore
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)} ${args.reason || ''}`;
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 config for blocklet ${getBlockletInfo(result, info)}:\n${args.configs.map(x => `- ${x.key}: ${x.value}\n`)}`; // prettier-ignore
113
+ case 'upgradeBlocklet':
114
+ if (result.resultStatus === 'failed') {
115
+ return `upgrade blocklet failed: ${getBlockletInfo(result, info)}`;
116
+ }
117
+ return `upgraded blocklet ${getBlockletInfo(result, info)} to v${result.meta.version}`;
118
+ case 'upgradeComponents':
119
+ return `upgraded components for blocklet ${getBlockletInfo(result, info)}`;
120
+ case 'configPublicToStore':
121
+ if (args.publicToStore) {
122
+ return `set publicToStore to true for blocklet ${getBlockletInfo(result, info)}`;
123
+ }
124
+ return `set publicToStore to false for blocklet ${getBlockletInfo(result, info)}`;
125
+ case 'configNavigations':
126
+ // eslint-disable-next-line prettier/prettier
127
+ return `updated following navigations for blocklet ${getBlockletInfo(result, info)}:\n${args.navigations.map(
128
+ (x) => `- ${x.title}: ${x.link}\n`
129
+ )}`;
130
+ case 'updateComponentTitle':
131
+ return `update component title to **${args.title}** for blocklet ${getBlockletInfo(result, info)}`;
132
+ case 'updateComponentMountPoint':
133
+ return `update component mount point to **${args.mountPoint}** for blocklet ${getBlockletInfo(result, info)}`;
134
+ // store
135
+ case 'addBlockletStore':
136
+ return `added blocklet store ${args.url}`;
137
+ case 'deleteBlockletStore':
138
+ return `removed blocklet store ${args.url}`;
139
+ case 'selectBlockletStore':
140
+ return `selected blocklet store ${args.url}`;
141
+
142
+ // teams: members/passports
143
+ case 'addUser':
144
+ if (args.passport) {
145
+ return `${args.reason} and received **${args.passport.name}** passport from ${team}`;
146
+ }
147
+ return `joined team ${team} by ${args.reason}`;
148
+ case 'updateUser':
149
+ return `${args.reason} and received **${args.passport.name}** passport from ${team}`;
150
+ case 'switchProfile':
151
+ return `switched profile to ${args.profile.fullName} for ${team}`;
152
+ case 'switchPassport':
153
+ return `switched passport to ${args.passport.name} for ${team}`;
154
+ case 'login':
155
+ return `${user} logged in to ${team} with passport ${args.passport.name}`;
156
+ case 'updateWhoCanAccess':
157
+ return `updated access control policy to **${args.whoCanAccess}**`;
158
+ case 'configPassportIssuance':
159
+ return `${args.enabled ? 'enabled' : 'disabled'} passport issuance for ${team}`;
160
+ case 'createPassportIssuance':
161
+ return `issued **${args.name}** passport to ${user} for ${team}, issuance id: ${result.id}`;
162
+ case 'processPassportIssuance':
163
+ return `claimed passport **${args.name}** from ${team}, issuance id: ${args.sessionId}`;
164
+ case 'revokeUserPassport':
165
+ return `revoked **${passport}** passport of user ${user} for ${team}`;
166
+ case 'enableUserPassport':
167
+ return `enabled **${passport}** passport of user ${user} for ${team}`;
168
+ case 'updateUserApproval':
169
+ return `${args.user.approved ? 'enabled' : 'disabled'} user ${user} for ${team}`;
170
+ case 'deletePassportIssuance':
171
+ return `removed passport issuance ${args.sessionId} from ${team}`;
172
+ case 'createMemberInvitation':
173
+ return `created member invitation(${result.inviteId}: ${args.remark}) with **${args.role}** passport for ${team}`; // prettier-ignore
174
+ case 'deleteInvitation':
175
+ return `removed unused member invitation(${args.inviteId}) from ${team}`;
176
+ case 'createRole':
177
+ return `created passport ${args.name}(${args.title}) for ${team}`;
178
+ case 'updateRole':
179
+ return `updated passport ${args.role.name}(${args.role.title}) for ${team}`;
180
+ case 'updatePermissionsForRole':
181
+ return `granted following permissions to passport ${args.roleName} for ${team}: \n${args.grantNames.map(x => `- ${x}`).join('\n')}`; // prettier-ignore
182
+ case 'configTrustedPassports':
183
+ if (args.trustedPassports.length === 0) {
184
+ return `removed all trusted passport issuers for ${team}`;
185
+ }
186
+ return `updated trusted passport issuers to following for ${team}: \n${args.trustedPassports.map(x => `- ${x.remark}: ${x.issuerDid}`).join('\n')}`; // prettier-ignore
187
+ case 'delegateTransferNFT':
188
+ return `${args.owner} ${args.reason}`;
189
+ case 'issuePassportToUser':
190
+ return `issued **${args.role}** passport to ${user}`;
191
+
192
+ // accessKeys
193
+ case 'createAccessKey':
194
+ return `created access key ${result.accessKeyId} with passport ${args.passport}: ${args.remark}`;
195
+ case 'updateAccessKey':
196
+ return `updated access key ${result.accessKeyId} with following changes:\n - passport: ${args.passport}\n - remark: ${args.remark}`; // prettier-ignore
197
+ case 'deleteAccessKey':
198
+ return `deleted access key ${args.accessKeyId}`; // prettier-ignore
199
+
200
+ // integrations
201
+ case 'createWebHook':
202
+ return `added integration ${result._id}: \n- type: ${args.type}\n${args.params.map(x => `- ${x.name}: ${x.value}`).join('\n')}`; // prettier-ignore
203
+ case 'deleteWebHook':
204
+ return `deleted integration ${args.id}`;
205
+
206
+ // server
207
+ case 'updateNodeInfo':
208
+ return `updated basic server settings: \n${Object.keys(args).map(x => `- ${x}: ${args[x]}`).join('\n')}`; // prettier-ignore
209
+ case 'upgradeNodeVersion':
210
+ return `upgrade server to ${info.nextVersion}`;
211
+ case 'restartServer':
212
+ return 'server was restarted';
213
+ case 'startServer':
214
+ return 'server was started';
215
+ case 'stopServer':
216
+ return 'server was stopped';
217
+
218
+ // certificates
219
+ case 'addCertificate':
220
+ return `added certificate #${args.id}, domain ${result.domain}`;
221
+ case 'deleteCertificate':
222
+ return `deleted certificate #${args.id}`;
223
+ case 'updateCertificate':
224
+ return `updated certificate #${args.id}`;
225
+ case 'issueLetsEncryptCert':
226
+ return `tried to issue lets encrypt certificate for domain **${args.domain}**, ticket: ${result._id}`;
227
+
228
+ // router
229
+ case 'addDomainAlias':
230
+ return `added extra domain **${args.domainAlias}** to ${site}`; // prettier-ignore
231
+ case 'deleteDomainAlias':
232
+ return `removed extra domain **${args.domainAlias}** from ${site}`; // prettier-ignore
233
+ case 'updateRoutingSite':
234
+ return `updated site from ${site}`; // prettier-ignore
235
+ case 'addRoutingRule':
236
+ return `added routing rule **${args.rule?.from?.pathPrefix}** from ${site}`; // prettier-ignore
237
+ case 'updateRoutingRule':
238
+ return `updated routing rule **${args.rule?.from?.pathPrefix}** from ${site}`; // prettier-ignore
239
+ case 'deleteRoutingRule':
240
+ return `deleted routing rule from ${site}`; // prettier-ignore
241
+ case 'updateGateway': {
242
+ const changes = [
243
+ args.requestLimit.enabled ? `rate limit: enabled, rate: ${args.requestLimit.rate}` : 'rate limit: disabled',
244
+ args.cacheEnabled ? `global cache: enabled, rate: ${args.cacheEnabled}` : 'global cache: disabled',
245
+ ];
246
+ const message = `update gateway: ${changes.join('; ')}`;
247
+ return message;
248
+ }
249
+ case 'createTransferInvitation':
250
+ return `created a transfer node invitation(${result.inviteId})`;
251
+
252
+ default:
253
+ return action;
254
+ }
255
+ };
256
+
257
+ const getLogCategory = (action) => {
258
+ switch (action) {
259
+ // blocklets
260
+ case 'installBlocklet':
261
+ case 'installComponent':
262
+ case 'startBlocklet':
263
+ case 'restartBlocklet':
264
+ case 'reloadBlocklet':
265
+ case 'stopBlocklet':
266
+ case 'resetBlocklet':
267
+ case 'deleteBlocklet':
268
+ case 'deleteComponent':
269
+ case 'configBlocklet':
270
+ case 'upgradeBlocklet':
271
+ case 'upgradeComponents':
272
+ case 'configPublicToStore':
273
+ case 'configNavigations':
274
+ case 'updateComponentTitle':
275
+ case 'updateComponentMountPoint':
276
+ return 'blocklet';
277
+
278
+ // store
279
+ case 'addBlockletStore':
280
+ case 'deleteBlockletStore':
281
+ case 'selectBlockletStore':
282
+ return 'server';
283
+
284
+ // teams: members/passports
285
+ case 'addUser':
286
+ case 'updateUser':
287
+ case 'switchProfile':
288
+ case 'switchPassport':
289
+ case 'login':
290
+ case 'configWhoCanAccess':
291
+ case 'processPassportIssuance':
292
+ case 'configPassportIssuance':
293
+ case 'createPassportIssuance':
294
+ case 'deletePassportIssuance':
295
+ case 'revokeUserPassport':
296
+ case 'enableUserPassport':
297
+ case 'updateUserApproval':
298
+ case 'createMemberInvitation':
299
+ case 'deleteInvitation':
300
+ case 'createRole':
301
+ case 'updateRole':
302
+ case 'updatePermissionsForRole':
303
+ case 'configTrustedPassports':
304
+ case 'delegateTransferNFT':
305
+ case 'createTransferInvitation':
306
+ return 'team';
307
+
308
+ // accessKeys
309
+ case 'createAccessKey':
310
+ case 'updateAccessKey':
311
+ case 'deleteAccessKey':
312
+ return 'security';
313
+
314
+ // integrations
315
+ case 'createWebHook':
316
+ case 'deleteWebHook':
317
+ return 'integrations';
318
+
319
+ // server
320
+ case 'updateNodeInfo':
321
+ case 'upgradeNodeVersion':
322
+ case 'startServer':
323
+ case 'stopServer':
324
+ return 'server';
325
+
326
+ // certificates
327
+ case 'addCertificate':
328
+ case 'deleteCertificate':
329
+ case 'updateCertificate':
330
+ case 'issueLetsEncryptCert':
331
+ return 'certificates';
332
+
333
+ case 'updateGateway':
334
+ return 'gateway';
335
+
336
+ default:
337
+ return '';
338
+ }
339
+ };
340
+
341
+ const getScope = (args = {}) => {
342
+ // this param usually means mutating an application (server or blocklet)
343
+ if (args.teamDid) {
344
+ return args.teamDid;
345
+ }
346
+
347
+ // this param usually means mutating a child component
348
+ if (args.rootDid) {
349
+ return args.rootDid;
350
+ }
351
+
352
+ // this param usually means mutating an blockle application
353
+ if (args.did) {
354
+ // this param usually means mutating a nested child component
355
+ if (Array.isArray(args.did)) {
356
+ return args.did[0];
357
+ }
358
+
359
+ return args.did;
360
+ }
361
+
362
+ return null;
363
+ };
364
+
365
+ const fixActor = (actor) => {
366
+ if ([NODE_SERVICES.AUTH_SERVICE, 'blocklet'].includes(actor?.role)) {
367
+ actor.role = '';
368
+ }
369
+ };
370
+
371
+ class AuditLogState extends BaseState {
372
+ constructor(baseDir, config = {}) {
373
+ super(baseDir, { filename: 'audit-log.db', ...config });
374
+ }
375
+
376
+ /**
377
+ * Create new audit log
378
+ *
379
+ * @param {string} action the api name
380
+ * @param {object} args the api args
381
+ * @param {object} result the api result
382
+ * @param {object} context the log context
383
+ * {
384
+ protocol: 'http',
385
+ user: {
386
+ meta: 'profile',
387
+ fullName: 'wangshijun',
388
+ email: 'shijun@arcblock.io',
389
+ type: 'profile',
390
+ did: 'z1jq5bGF64wnZiy29EbRyuQqUxmRHmSdt1Q',
391
+ pk: 'zHTcKwgi9DK8US8QQY9K7wQ3qF79CtrWYn6BYAgTQUeVQ',
392
+ passports: [ [Object] ],
393
+ approved: true,
394
+ locale: 'en',
395
+ firstLoginAt: '2022-04-27T22:55:51.788Z',
396
+ lastLoginAt: '2022-04-27T22:55:51.788Z',
397
+ _id: 'dvOIJJVUJHGxnWBU',
398
+ createdAt: '2022-04-27T22:55:51.789Z',
399
+ updatedAt: '2022-04-27T22:55:51.789Z',
400
+ role: 'owner',
401
+ passportId: 'z2iTzSDhwGQFtYXeya8kqCyXro1tRkwUuwN6K'
402
+ },
403
+ url: '/api/gql?locale=en',
404
+ query: { locale: 'en' },
405
+ hostname: '192.168.123.236',
406
+ port: 0,
407
+ ip: '127.0.0.1'
408
+ }
409
+ * @return {object} new doc
410
+ */
411
+ create({ action, args = {}, context = {}, result = {} }, node) {
412
+ return new Promise(async (resolve) => {
413
+ // Do not store secure configs in audit log
414
+ if (Array.isArray(args.configs)) {
415
+ args.configs.forEach((x) => {
416
+ if (x.secure) {
417
+ x.value = '******';
418
+ }
419
+ });
420
+ }
421
+
422
+ try {
423
+ const { ip, ua, user = {} } = context;
424
+ const [info, uaInfo] = await Promise.all([node.states.node.read(), parse(ua)]);
425
+
426
+ fixActor(user);
427
+
428
+ const data = await this.insert({
429
+ scope: getScope(args) || info.did, // server or blocklet did
430
+ action,
431
+ category: await getLogCategory(action, args, context, result, info, node),
432
+ content: (await getLogContent(action, args, context, result, info, node)).trim(),
433
+ actor: pick(user.actual || user, ['did', 'fullName', 'role']),
434
+ extra: args,
435
+ env: pick(uaInfo, ['browser', 'os', 'device']),
436
+ ip,
437
+ ua,
438
+ });
439
+
440
+ logger.info('create', data);
441
+ return resolve(data);
442
+ } catch (err) {
443
+ logger.error('create error', { error: err, action, args, context });
444
+ return resolve(null);
445
+ }
446
+ });
447
+ }
448
+
449
+ async findPaginated({ scope, category, paging } = {}) {
450
+ const conditions = {};
451
+ if (scope) {
452
+ conditions.scope = scope;
453
+ }
454
+ if (category) {
455
+ conditions.category = category;
456
+ }
457
+
458
+ return super.paginate(conditions, { createdAt: -1 }, { pageSize: 20, ...paging });
459
+ }
460
+ }
461
+
462
+ module.exports = AuditLogState;