@abtnode/core 1.16.6-beta-4562aa60 → 1.16.6-beta-eaa4d39d
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/team.js +38 -2
- package/lib/blocklet/manager/disk.js +67 -36
- package/lib/blocklet/manager/helper/install-application-from-dev.js +5 -3
- package/lib/blocklet/manager/helper/install-component-from-url.js +3 -0
- package/lib/blocklet/storage/backup/spaces.js +5 -1
- package/lib/blocklet/storage/restore/spaces.js +5 -1
- package/lib/crons/check-new-version.js +68 -0
- package/lib/{util/disk-monitor.js → crons/monitor-disk-usage.js} +2 -2
- package/lib/crons/rotate-pm2-logs/index.js +41 -0
- package/lib/crons/rotate-pm2-logs/script.js +56 -0
- package/lib/index.js +8 -4
- package/lib/processes/updater.js +160 -0
- package/lib/router/helper.js +4 -7
- package/lib/states/node.js +1 -0
- package/lib/states/user.js +237 -8
- package/lib/util/blocklet.js +67 -10
- package/lib/util/ip.js +0 -6
- package/lib/util/maintain.js +9 -64
- package/lib/util/rotator.js +194 -0
- package/lib/util/rpc.js +69 -8
- package/lib/validators/user.js +44 -0
- package/package.json +32 -27
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const axon = require('axon');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const shell = require('shelljs');
|
|
6
|
+
const semver = require('semver');
|
|
7
|
+
|
|
8
|
+
const VERBOSE = '';
|
|
9
|
+
// const VERBOSE = '--verbose';
|
|
10
|
+
|
|
11
|
+
const BINARY_NAME = process.env.ABT_NODE_BINARY_NAME;
|
|
12
|
+
const COMMAND_NAME = process.env.ABT_NODE_COMMAND_NAME;
|
|
13
|
+
const PACKAGE_NAME = process.env.ABT_NODE_PACKAGE_NAME;
|
|
14
|
+
const VALID_BINARY_NAMES = ['blocklet', 'abtnode'];
|
|
15
|
+
|
|
16
|
+
console.info(`binary name: ${BINARY_NAME}`);
|
|
17
|
+
console.info(`command name: ${COMMAND_NAME}`);
|
|
18
|
+
console.info(`package name: ${PACKAGE_NAME}`);
|
|
19
|
+
|
|
20
|
+
// async and promise style of shelljs.exec
|
|
21
|
+
const runAsync = (command, options) =>
|
|
22
|
+
new Promise((resolve) => {
|
|
23
|
+
console.info(`Run command: ${command}`);
|
|
24
|
+
shell.exec(command, { async: true, ...options }, (code, stdout, stderr) => {
|
|
25
|
+
resolve({ code, stdout, stderr });
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const getInstaller = () => {
|
|
30
|
+
const { stdout: pnpmPath } = shell.which('pnpm') || {};
|
|
31
|
+
if (pnpmPath) {
|
|
32
|
+
const { stdout: expectedDir } = shell.exec('pnpm bin -g', { silent: true });
|
|
33
|
+
const { stdout: binPath } = shell.which(BINARY_NAME);
|
|
34
|
+
if (expectedDir && binPath && expectedDir.trim() === path.dirname(binPath)) {
|
|
35
|
+
return 'pnpm';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { stdout: yarnPath } = shell.which('yarn') || {};
|
|
40
|
+
if (yarnPath) {
|
|
41
|
+
const { stdout: binaryDir } = shell.exec(`yarn global bin ${BINARY_NAME}`, { silent: true });
|
|
42
|
+
const binaryPath = path.join(binaryDir, BINARY_NAME);
|
|
43
|
+
if (fs.existsSync(binaryPath)) {
|
|
44
|
+
return 'yarn';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return 'npm';
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getBinaryDir = () => {
|
|
52
|
+
const installer = getInstaller();
|
|
53
|
+
if (installer === 'pnpm') {
|
|
54
|
+
const { stdout: binaryDir } = shell.exec('pnpm bin -g', { silent: true });
|
|
55
|
+
return binaryDir.trim();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (installer === 'yarn') {
|
|
59
|
+
const { stdout: binaryDir } = shell.exec(`yarn global bin ${BINARY_NAME}`, { silent: true });
|
|
60
|
+
return binaryDir.trim();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { stdout: binaryDir } = shell.exec(`npm bin -g ${BINARY_NAME}`, { silent: true });
|
|
64
|
+
return binaryDir.trim();
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const installBlockletServer = async (version) => {
|
|
68
|
+
if (VALID_BINARY_NAMES.includes(BINARY_NAME) === false) {
|
|
69
|
+
return { code: 1, stderr: 'Abort because you are not using a standard @blocklet/cli setup' };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const installer = getInstaller();
|
|
73
|
+
const commands = {
|
|
74
|
+
pnpm: `pnpm add -g ${PACKAGE_NAME}@${version} ${VERBOSE}`,
|
|
75
|
+
npm: `npm install -g ${PACKAGE_NAME}@${version} ${VERBOSE}`,
|
|
76
|
+
yarn: `yarn global add ${PACKAGE_NAME}@${version} ${VERBOSE}`,
|
|
77
|
+
};
|
|
78
|
+
const command = commands[installer];
|
|
79
|
+
console.info('Installing blocklet server', { version, installer, command });
|
|
80
|
+
const result = await runAsync(command);
|
|
81
|
+
console.info('Installing blocklet server done', { version });
|
|
82
|
+
return result;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const verifyBlockletServer = async (version) => {
|
|
86
|
+
console.info('Verifying blocklet server', { version });
|
|
87
|
+
const result = await runAsync(`${BINARY_NAME} --version`);
|
|
88
|
+
console.info('Verifying blocklet server done', { version });
|
|
89
|
+
|
|
90
|
+
if (result.code === 0) {
|
|
91
|
+
const actual = semver.coerce(result.stdout);
|
|
92
|
+
if (actual && actual.version === version) {
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { code: 1, stderr: `Blocklet server version is not expected ${version}` };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Restart Blocklet Server
|
|
103
|
+
const restartBlockletServer = async (dataDir) => {
|
|
104
|
+
// We put this in the event loop because it will terminate the current node process
|
|
105
|
+
// But we need to wait for the session state to transition from restarting to cleanup
|
|
106
|
+
process.nextTick(async () => {
|
|
107
|
+
const binaryDir = getBinaryDir();
|
|
108
|
+
console.info('Restarting blocklet server', { dataDir, binaryDir });
|
|
109
|
+
|
|
110
|
+
if (!binaryDir) {
|
|
111
|
+
console.error('Can not restart blocklet server because no binaryDir found');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// restart the server
|
|
116
|
+
if (VALID_BINARY_NAMES.includes(BINARY_NAME)) {
|
|
117
|
+
await runAsync(`${binaryDir}/${COMMAND_NAME} start`, { cwd: path.dirname(dataDir) });
|
|
118
|
+
} else {
|
|
119
|
+
await runAsync(`${COMMAND_NAME} start`, { cwd: path.dirname(dataDir) });
|
|
120
|
+
}
|
|
121
|
+
console.info('Restarting blocklet server done');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return dataDir;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Start the server
|
|
128
|
+
const sock = axon.socket('rep');
|
|
129
|
+
const port = Number(process.env.ABT_NODE_UPDATER_PORT);
|
|
130
|
+
sock.bind(port, '127.0.0.1');
|
|
131
|
+
sock.on('message', async (raw, reply) => {
|
|
132
|
+
console.info('receive', { raw });
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const message = JSON.parse(raw);
|
|
136
|
+
if (message.command === 'install') {
|
|
137
|
+
const result = await installBlockletServer(message.version);
|
|
138
|
+
reply(result);
|
|
139
|
+
} else if (message.command === 'verify') {
|
|
140
|
+
const result = await verifyBlockletServer(message.version);
|
|
141
|
+
reply(result);
|
|
142
|
+
} else if (message.command === 'restart') {
|
|
143
|
+
const result = await restartBlockletServer(message.dataDir);
|
|
144
|
+
reply(result);
|
|
145
|
+
} else if (message.command === 'shutdown') {
|
|
146
|
+
reply('Terminate myself after this message');
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}, 500);
|
|
150
|
+
} else if (message.command === 'ping') {
|
|
151
|
+
reply('pong');
|
|
152
|
+
} else {
|
|
153
|
+
reply({ error: 'Unknown command' });
|
|
154
|
+
}
|
|
155
|
+
} catch (err) {
|
|
156
|
+
console.error('failed to process command', { error: err });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
console.info(`Blocklet Server Updater process is ready on port ${port}`);
|
package/lib/router/helper.js
CHANGED
|
@@ -61,13 +61,13 @@ const {
|
|
|
61
61
|
isGatewayCacheEnabled,
|
|
62
62
|
isBlockletSite,
|
|
63
63
|
} = require('../util');
|
|
64
|
-
const { getIpDnsDomainForBlocklet
|
|
64
|
+
const { getIpDnsDomainForBlocklet } = require('../util/get-domain-for-blocklet');
|
|
65
65
|
const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
|
|
66
66
|
|
|
67
67
|
const Router = require('./index');
|
|
68
68
|
const states = require('../states');
|
|
69
69
|
const { getBlockletDomainGroupName, getDidFromDomainGroupName } = require('../util/router');
|
|
70
|
-
const { getBlockletKnownAs } = require('../util/blocklet');
|
|
70
|
+
const { getBlockletKnownAs, getBlockletDidDomainList } = require('../util/blocklet');
|
|
71
71
|
|
|
72
72
|
const isServiceFeDevelopment = process.env.ABT_NODE_SERVICE_FE_PORT;
|
|
73
73
|
|
|
@@ -839,13 +839,10 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
839
839
|
});
|
|
840
840
|
|
|
841
841
|
if (!existSite) {
|
|
842
|
-
const domainAliases =
|
|
843
|
-
|
|
844
|
-
const didDomain = getDidDomainForBlocklet({ appPid: blocklet.appPid, didDomain: nodeInfo.didDomain });
|
|
845
|
-
const ipEchoDnsDomain = getIpDnsDomainForBlocklet(blocklet);
|
|
842
|
+
const domainAliases = getBlockletDidDomainList(blocklet, nodeInfo);
|
|
846
843
|
|
|
847
844
|
// let didDomain in front of ipEchoDnsDomain
|
|
848
|
-
domainAliases.push({ value:
|
|
845
|
+
domainAliases.push({ value: getIpDnsDomainForBlocklet(blocklet), isProtected: true });
|
|
849
846
|
|
|
850
847
|
await routerManager.addRoutingSite(
|
|
851
848
|
{
|
package/lib/states/node.js
CHANGED
package/lib/states/user.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
const pickBy = require('lodash/pickBy');
|
|
2
2
|
const escapeStringRegexp = require('escape-string-regexp');
|
|
3
|
-
|
|
3
|
+
const pick = require('lodash/pick');
|
|
4
|
+
const cloneDeep = require('lodash/cloneDeep');
|
|
4
5
|
const logger = require('@abtnode/logger')('@abtnode/core:states:user');
|
|
5
6
|
const { isValid } = require('@arcblock/did');
|
|
6
7
|
const { PASSPORT_STATUS } = require('@abtnode/constant');
|
|
8
|
+
const { upsertToPassports } = require('@abtnode/auth/lib/passport');
|
|
9
|
+
const { fromAppDid } = require('@arcblock/did-ext');
|
|
10
|
+
const { types } = require('@arcblock/did');
|
|
11
|
+
|
|
7
12
|
const BaseState = require('./base');
|
|
8
13
|
const { validateOwner } = require('../util');
|
|
14
|
+
const { loginSchema } = require('../validators/user');
|
|
9
15
|
|
|
10
16
|
const isNullOrUndefined = (x) => x === undefined || x === null;
|
|
11
17
|
|
|
@@ -39,15 +45,25 @@ const isNullOrUndefined = (x) => x === undefined || x === null;
|
|
|
39
45
|
* WalletAccount
|
|
40
46
|
* @typedef {Object} WalletAccount
|
|
41
47
|
* @property {'wallet'} provider
|
|
42
|
-
* @property {string} did - did for
|
|
43
|
-
* @property {string} pk - pk for
|
|
48
|
+
* @property {string} did - did for wallet account
|
|
49
|
+
* @property {string} pk - pk for wallet account
|
|
50
|
+
* @property {string} lastLoginAt - Last login time new Date().toISOString()
|
|
51
|
+
* @property {string} firstLoginAt - First login time new Date().toISOString()
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* NFTAccount
|
|
56
|
+
* @typedef {Object} NFTAccount
|
|
57
|
+
* @property {'nft'} provider
|
|
58
|
+
* @property {string} did - did for nft account
|
|
59
|
+
* @property {string} owner - owner for nft account
|
|
44
60
|
* @property {string} lastLoginAt - Last login time new Date().toISOString()
|
|
45
61
|
* @property {string} firstLoginAt - First login time new Date().toISOString()
|
|
46
62
|
*/
|
|
47
63
|
|
|
48
64
|
/**
|
|
49
65
|
* ConnectedAccount
|
|
50
|
-
* @typedef {(Auth0Account|WalletAccount)} ConnectedAccount
|
|
66
|
+
* @typedef {(Auth0Account|WalletAccount|NFTAccount)} ConnectedAccount
|
|
51
67
|
*/
|
|
52
68
|
|
|
53
69
|
/**
|
|
@@ -59,7 +75,6 @@ const isNullOrUndefined = (x) => x === undefined || x === null;
|
|
|
59
75
|
* @property {string} avatar - url of user's avatar, eg: bn://avatar/7f8848569405f8cdf8b1b2788ebf7d0f.jpg
|
|
60
76
|
* @property {string} did - user's did -> 实际上变为 uid, 是不可变的;真正的 wallet-did 转移到 extraConfigs 中去了
|
|
61
77
|
* @property {string} pk - user's publicKey
|
|
62
|
-
* @property {('owner'|'admin'|'member'|'guest'|string)} role - user's role
|
|
63
78
|
* @property {UserPassport[]} passports - user's passport list
|
|
64
79
|
* @property {boolean} approved - enable user to login
|
|
65
80
|
* @property {string} locale - locale
|
|
@@ -72,8 +87,36 @@ const isNullOrUndefined = (x) => x === undefined || x === null;
|
|
|
72
87
|
* @property {string} lastLoginIp - lastLoginIp
|
|
73
88
|
* @property {Date} createdAt - createdAt
|
|
74
89
|
* @property {Date} updatedAt - updatedAt
|
|
90
|
+
* @property {('owner'|'admin'|'member'|'guest'|string)} role - user's role deprecated
|
|
75
91
|
*/
|
|
76
92
|
|
|
93
|
+
function updateConnectedAccount(connectedAccounts = [], connectedAccount = {}) {
|
|
94
|
+
const now = new Date().toISOString();
|
|
95
|
+
const filterAccounts = connectedAccounts.filter(Boolean);
|
|
96
|
+
const updated = cloneDeep(filterAccounts);
|
|
97
|
+
const updates = Array.isArray(connectedAccount) ? connectedAccount : [connectedAccount];
|
|
98
|
+
updates.filter(Boolean).forEach((x) => {
|
|
99
|
+
if (x.provider && x.did) {
|
|
100
|
+
const findAccountIndex = updated.findIndex((item) => item.provider === x.provider && item.did === x.did);
|
|
101
|
+
if (findAccountIndex > -1) {
|
|
102
|
+
updated[findAccountIndex] = {
|
|
103
|
+
...filterAccounts[findAccountIndex],
|
|
104
|
+
...x,
|
|
105
|
+
lastLoginAt: now,
|
|
106
|
+
};
|
|
107
|
+
} else {
|
|
108
|
+
updated.push({
|
|
109
|
+
...x,
|
|
110
|
+
firstLoginAt: now,
|
|
111
|
+
lastLoginAt: now,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return updated;
|
|
118
|
+
}
|
|
119
|
+
|
|
77
120
|
class User extends BaseState {
|
|
78
121
|
constructor(baseDir, config = {}) {
|
|
79
122
|
super(baseDir, { filename: 'user.db', ...config });
|
|
@@ -296,14 +339,124 @@ class User extends BaseState {
|
|
|
296
339
|
return this.find(queryParam);
|
|
297
340
|
}
|
|
298
341
|
|
|
342
|
+
/**
|
|
343
|
+
* user 版本标准化,统一将 user 转换为最新的结构,结构参考 core/state/lib/states/user.js L12-L75
|
|
344
|
+
*/
|
|
345
|
+
// 1: 没有 extraConfigs 字段
|
|
346
|
+
// 2: 有 source 和 extraConfigs 字段,oauth 账号的 extraConfigs 中包含 sourceId 和 sourceProvider 字段
|
|
347
|
+
// 3: 只有 extraConfigs 字段,且 extraConfigs.connectedAccounts 中包含 extraConfigs.sourceProvider 的 account
|
|
348
|
+
async normalize(user, { blockletSk }) {
|
|
349
|
+
if (!user) {
|
|
350
|
+
return user;
|
|
351
|
+
}
|
|
352
|
+
let version = 1;
|
|
353
|
+
if (user.extraConfigs) {
|
|
354
|
+
version = 2;
|
|
355
|
+
|
|
356
|
+
const connectedAccounts = user.extraConfigs?.connectedAccounts || [];
|
|
357
|
+
const sourceProvider = user.extraConfigs?.sourceProvider || '';
|
|
358
|
+
if (connectedAccounts.some((item) => item.provider === sourceProvider)) {
|
|
359
|
+
version = 3;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const versionMap = {
|
|
364
|
+
1: async () => {
|
|
365
|
+
const connectedAccounts = [
|
|
366
|
+
{
|
|
367
|
+
provider: 'wallet',
|
|
368
|
+
did: user.did,
|
|
369
|
+
pk: user.pk,
|
|
370
|
+
firstLoginAt: user.firstLoginAt,
|
|
371
|
+
lastLoginAt: user.lastLoginAt,
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
const sourceProvider = 'wallet';
|
|
375
|
+
const updatedUser = await this.update({
|
|
376
|
+
did: user.did,
|
|
377
|
+
pk: user.pk,
|
|
378
|
+
extraConfigs: {
|
|
379
|
+
sourceProvider,
|
|
380
|
+
connectedAccounts,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
return updatedUser;
|
|
384
|
+
},
|
|
385
|
+
2: async () => {
|
|
386
|
+
// eslint-disable-next-line prefer-const
|
|
387
|
+
let { sourceId, sourceProvider, connectedAccounts = [] } = user.extraConfigs || {};
|
|
388
|
+
if (sourceId && sourceProvider) {
|
|
389
|
+
connectedAccounts = [
|
|
390
|
+
{
|
|
391
|
+
provider: sourceProvider,
|
|
392
|
+
id: sourceId,
|
|
393
|
+
did: user.did,
|
|
394
|
+
pk: user.pk,
|
|
395
|
+
firstLoginAt: user.firstLoginAt,
|
|
396
|
+
lastLoginAt: user.lastLoginAt,
|
|
397
|
+
},
|
|
398
|
+
];
|
|
399
|
+
} else {
|
|
400
|
+
sourceProvider = 'wallet';
|
|
401
|
+
connectedAccounts.forEach((account) => {
|
|
402
|
+
if (account.id && blockletSk) {
|
|
403
|
+
const accountWallet = fromAppDid(account.id, blockletSk, types.RoleType.ROLE_ACCOUNT);
|
|
404
|
+
account.did = accountWallet.address;
|
|
405
|
+
account.pk = accountWallet.publicKey;
|
|
406
|
+
account.firstLoginAt = account.firstLoginAt || new Date().toISOString();
|
|
407
|
+
account.lastLoginAt = account.lastLoginAt || new Date().toISOString();
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
connectedAccounts.unshift({
|
|
411
|
+
provider: 'wallet',
|
|
412
|
+
did: user.did,
|
|
413
|
+
pk: user.pk,
|
|
414
|
+
firstLoginAt: user.firstLoginAt,
|
|
415
|
+
lastLoginAt: user.lastLoginAt,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
const updatedUser = await this.update({
|
|
419
|
+
did: user.did,
|
|
420
|
+
pk: user.pk,
|
|
421
|
+
extraConfigs: {
|
|
422
|
+
sourceProvider,
|
|
423
|
+
connectedAccounts,
|
|
424
|
+
},
|
|
425
|
+
source: undefined,
|
|
426
|
+
});
|
|
427
|
+
return updatedUser;
|
|
428
|
+
},
|
|
429
|
+
3: () => {
|
|
430
|
+
return user;
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
return versionMap[version]();
|
|
434
|
+
}
|
|
435
|
+
|
|
299
436
|
/**
|
|
300
437
|
* get user by did
|
|
301
438
|
* @param {string} did user's did
|
|
302
439
|
* @returns {BlockletUser}
|
|
303
440
|
*/
|
|
304
|
-
async getUser(did) {
|
|
305
|
-
|
|
306
|
-
|
|
441
|
+
async getUser(did, { enableConnectedAccout = false, enableNormalize = false, blockletSk } = {}) {
|
|
442
|
+
let user = await this.findOne({ did });
|
|
443
|
+
if (enableConnectedAccout) {
|
|
444
|
+
if (!user) {
|
|
445
|
+
user = await this.findOne({
|
|
446
|
+
'extraConfigs.connectedAccounts.did': did,
|
|
447
|
+
});
|
|
448
|
+
} else if (user.extraConfig?.bindDid) {
|
|
449
|
+
// @deprecated 已经不存在 bindDid 字段
|
|
450
|
+
({ user } = await this.findOne({ did: user.extraConfig.bindDid }));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (enableNormalize) {
|
|
454
|
+
if (!blockletSk) {
|
|
455
|
+
throw new Error('Normalize user must provide blockletSk params');
|
|
456
|
+
}
|
|
457
|
+
return this.normalize(user, { blockletSk });
|
|
458
|
+
}
|
|
459
|
+
return user;
|
|
307
460
|
}
|
|
308
461
|
|
|
309
462
|
/**
|
|
@@ -335,6 +488,82 @@ class User extends BaseState {
|
|
|
335
488
|
|
|
336
489
|
return doc;
|
|
337
490
|
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* 用户登录
|
|
494
|
+
* @param {Object} user
|
|
495
|
+
* @param {string} user.did
|
|
496
|
+
* @param {string} user.pk
|
|
497
|
+
* @param {ConnectedAccount} user.connectedAccount
|
|
498
|
+
* @param {passport} user.passport
|
|
499
|
+
* @param {string} user.fullName - user profile's name
|
|
500
|
+
* @param {string} user.avatar - url of user's avatar, eg: bn://avatar/7f8848569405f8cdf8b1b2788ebf7d0f.jpg
|
|
501
|
+
* @param {string} user.locale - locale
|
|
502
|
+
* @param {Object} [user.extra] - extra data of user
|
|
503
|
+
* @param {string} user.lastLoginIp - lastLoginIp
|
|
504
|
+
* @param {('owner'|'admin'|'member'|'guest'|string)} user.role - deprecated user's role
|
|
505
|
+
*/
|
|
506
|
+
async login(_user) {
|
|
507
|
+
const { error, value: user } = loginSchema.validate(_user);
|
|
508
|
+
if (error) {
|
|
509
|
+
throw new Error(error);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
let updatedUser;
|
|
513
|
+
let action = '';
|
|
514
|
+
const now = new Date().toISOString();
|
|
515
|
+
const oldUser = await this.getUser(user.did, {
|
|
516
|
+
enableConnectedAccout: true,
|
|
517
|
+
});
|
|
518
|
+
const cloneData = cloneDeep(user);
|
|
519
|
+
const passports = upsertToPassports(
|
|
520
|
+
oldUser?.passports || [],
|
|
521
|
+
cloneData.passport && {
|
|
522
|
+
...cloneData.passport,
|
|
523
|
+
lastLoginAt: now,
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
const mergeData = {
|
|
527
|
+
...pick(cloneData, ['type', 'fullName', 'email', 'avatar', 'role', 'locale', 'extra', 'lastLoginIp', 'remark']),
|
|
528
|
+
did: user.did,
|
|
529
|
+
pk: user.pk,
|
|
530
|
+
passports,
|
|
531
|
+
lastLoginAt: now,
|
|
532
|
+
extraConfigs: {
|
|
533
|
+
sourceProvider: 'wallet',
|
|
534
|
+
connectedAccounts: [],
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
if (oldUser) {
|
|
539
|
+
// update
|
|
540
|
+
action = 'update';
|
|
541
|
+
const extraConfigs = {
|
|
542
|
+
sourceProvider: oldUser?.extraConfigs?.sourceProvider || 'wallet',
|
|
543
|
+
connectedAccounts: updateConnectedAccount(
|
|
544
|
+
oldUser?.extraConfigs?.connectedAccounts || [],
|
|
545
|
+
cloneData.connectedAccount
|
|
546
|
+
),
|
|
547
|
+
};
|
|
548
|
+
mergeData.extraConfigs = extraConfigs;
|
|
549
|
+
updatedUser = await this.update(mergeData);
|
|
550
|
+
} else {
|
|
551
|
+
// insert
|
|
552
|
+
action = 'add';
|
|
553
|
+
mergeData.firstLoginAt = now;
|
|
554
|
+
mergeData.approved = true;
|
|
555
|
+
const connectedAccount = Array.isArray(cloneData.connectedAccount)
|
|
556
|
+
? cloneData.connectedAccount.filter(Boolean)[0]
|
|
557
|
+
: cloneData.connectedAccount;
|
|
558
|
+
|
|
559
|
+
mergeData.extraConfigs = {
|
|
560
|
+
sourceProvider: connectedAccount.provider,
|
|
561
|
+
connectedAccounts: updateConnectedAccount([], cloneData.connectedAccount),
|
|
562
|
+
};
|
|
563
|
+
updatedUser = await this.add(mergeData);
|
|
564
|
+
}
|
|
565
|
+
return { ...updatedUser, _action: action };
|
|
566
|
+
}
|
|
338
567
|
}
|
|
339
568
|
|
|
340
569
|
module.exports = User;
|
package/lib/util/blocklet.js
CHANGED
|
@@ -8,6 +8,8 @@ const os = require('os');
|
|
|
8
8
|
const pRetry = require('p-retry');
|
|
9
9
|
const tar = require('tar');
|
|
10
10
|
const get = require('lodash/get');
|
|
11
|
+
const uniq = require('lodash/uniq');
|
|
12
|
+
const toLower = require('lodash/toLower');
|
|
11
13
|
const isEmpty = require('lodash/isEmpty');
|
|
12
14
|
const streamToPromise = require('stream-to-promise');
|
|
13
15
|
const { Throttle } = require('stream-throttle');
|
|
@@ -18,9 +20,10 @@ const isUrl = require('is-url');
|
|
|
18
20
|
const semver = require('semver');
|
|
19
21
|
const axios = require('@abtnode/util/lib/axios');
|
|
20
22
|
const { stableStringify } = require('@arcblock/vc');
|
|
23
|
+
const { chainInfo: chainInfoSchema } = require('@arcblock/did-auth/lib/schema');
|
|
21
24
|
|
|
22
25
|
const { fromSecretKey } = require('@ocap/wallet');
|
|
23
|
-
const { toHex, toBase58, isHex, toDid } = require('@ocap/util');
|
|
26
|
+
const { toHex, toBase58, isHex, toDid, toAddress } = require('@ocap/util');
|
|
24
27
|
const { isValid: isValidDid, isEthereumDid } = require('@arcblock/did');
|
|
25
28
|
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
|
|
26
29
|
const pm2 = require('@abtnode/util/lib/async-pm2');
|
|
@@ -78,6 +81,7 @@ const {
|
|
|
78
81
|
isEnvShareable,
|
|
79
82
|
getBlockletAppIdList,
|
|
80
83
|
isBeforeInstalled,
|
|
84
|
+
getChainInfo,
|
|
81
85
|
} = require('@blocklet/meta/lib/util');
|
|
82
86
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
83
87
|
const { titleSchema, descriptionSchema, logoSchema } = require('@blocklet/meta/lib/schema');
|
|
@@ -294,19 +298,19 @@ const getAppSystemEnvironments = (blocklet, nodeInfo) => {
|
|
|
294
298
|
* 2. 如果没有,再拼接
|
|
295
299
|
*/
|
|
296
300
|
|
|
297
|
-
|
|
298
|
-
|
|
301
|
+
const pidDomain = getDidDomainForBlocklet({ appPid, didDomain: nodeInfo.didDomain });
|
|
299
302
|
const domainAliases = get(blocklet, 'site.domainAliases') || [];
|
|
300
|
-
const didDomainAlias = domainAliases.find(
|
|
301
|
-
(item) => item.value.endsWith(nodeInfo.didDomain) || item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
|
|
302
|
-
);
|
|
303
303
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
304
|
+
let didDomain = domainAliases.find((item) => toLower(item.value) === toLower(pidDomain));
|
|
305
|
+
|
|
306
|
+
if (!didDomain) {
|
|
307
|
+
didDomain = domainAliases.find(
|
|
308
|
+
(item) => item.value.endsWith(nodeInfo.didDomain) || item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
|
|
309
|
+
);
|
|
308
310
|
}
|
|
309
311
|
|
|
312
|
+
const appUrl = didDomain ? prettyURL(didDomain.value, true) : `https://${pidDomain}`;
|
|
313
|
+
|
|
310
314
|
return {
|
|
311
315
|
BLOCKLET_DID: did, // BLOCKLET_DID is always same as BLOCKLET_APP_PID in structV2 application
|
|
312
316
|
BLOCKLET_APP_SK: appSk,
|
|
@@ -884,6 +888,21 @@ const validateBlocklet = (blocklet) =>
|
|
|
884
888
|
validateEngine(getBlockletEngineNameByPlatform(b.meta));
|
|
885
889
|
});
|
|
886
890
|
|
|
891
|
+
const validateBlockletChainInfo = (blocklet) => {
|
|
892
|
+
const chainInfo = getChainInfo({
|
|
893
|
+
CHAIN_TYPE: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_TYPE],
|
|
894
|
+
CHAIN_ID: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_ID],
|
|
895
|
+
CHAIN_HOST: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_HOST],
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
const { error } = chainInfoSchema.validate(chainInfo);
|
|
899
|
+
if (error) {
|
|
900
|
+
throw error;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
return chainInfo;
|
|
904
|
+
};
|
|
905
|
+
|
|
887
906
|
const checkBlockletProcessHealthy = async (blocklet, { minConsecutiveTime, timeout } = {}) => {
|
|
888
907
|
await forEachBlocklet(
|
|
889
908
|
blocklet,
|
|
@@ -1885,6 +1904,41 @@ const updateBlockletFallbackLogo = async (blocklet) => {
|
|
|
1885
1904
|
}
|
|
1886
1905
|
};
|
|
1887
1906
|
|
|
1907
|
+
const ensureAppLogo = async (blocklet, blockletsDir) => {
|
|
1908
|
+
if (!blocklet) {
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
if (
|
|
1913
|
+
blocklet.source === BlockletSource.custom &&
|
|
1914
|
+
(blocklet.children || [])[0]?.meta?.logo &&
|
|
1915
|
+
blocklet.children[0].env.appDir
|
|
1916
|
+
) {
|
|
1917
|
+
const fileName = blocklet.children[0].meta.logo;
|
|
1918
|
+
const src = path.join(blocklet.children[0].env.appDir, fileName);
|
|
1919
|
+
const dist = path.join(getBundleDir(blockletsDir, blocklet.meta), fileName);
|
|
1920
|
+
|
|
1921
|
+
if (fs.existsSync(src)) {
|
|
1922
|
+
await fs.copy(src, dist);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
|
|
1927
|
+
const getBlockletDidDomainList = (blocklet, nodeInfo) => {
|
|
1928
|
+
const domainAliases = [];
|
|
1929
|
+
const alsoKnownAs = getBlockletKnownAs(blocklet);
|
|
1930
|
+
|
|
1931
|
+
const dids = [blocklet.appPid, blocklet.appDid, ...alsoKnownAs].filter(Boolean).map((did) => toAddress(did));
|
|
1932
|
+
|
|
1933
|
+
uniq(dids).forEach((did) => {
|
|
1934
|
+
const domain = getDidDomainForBlocklet({ appPid: did, didDomain: nodeInfo.didDomain });
|
|
1935
|
+
|
|
1936
|
+
domainAliases.push({ value: domain, isProtected: true });
|
|
1937
|
+
});
|
|
1938
|
+
|
|
1939
|
+
return domainAliases;
|
|
1940
|
+
};
|
|
1941
|
+
|
|
1888
1942
|
module.exports = {
|
|
1889
1943
|
updateBlockletFallbackLogo,
|
|
1890
1944
|
consumeServerlessNFT,
|
|
@@ -1897,6 +1951,7 @@ module.exports = {
|
|
|
1897
1951
|
getComponentSystemEnvironments,
|
|
1898
1952
|
getRuntimeEnvironments,
|
|
1899
1953
|
validateBlocklet,
|
|
1954
|
+
validateBlockletChainInfo,
|
|
1900
1955
|
fillBlockletConfigs,
|
|
1901
1956
|
ensureBlockletExpanded,
|
|
1902
1957
|
startBlockletProcess,
|
|
@@ -1941,4 +1996,6 @@ module.exports = {
|
|
|
1941
1996
|
validateBlockletMeta,
|
|
1942
1997
|
getBlockletKnownAs,
|
|
1943
1998
|
getFixedBundleSource,
|
|
1999
|
+
ensureAppLogo,
|
|
2000
|
+
getBlockletDidDomainList,
|
|
1944
2001
|
};
|
package/lib/util/ip.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
const getIp = require('@abtnode/util/lib/get-ip');
|
|
2
2
|
const logger = require('@abtnode/logger')('@abtnode/core:ip');
|
|
3
3
|
|
|
4
|
-
const doRpc = require('./rpc');
|
|
5
|
-
const { memoizeAsync } = require('./index');
|
|
6
|
-
|
|
7
4
|
let cache = null;
|
|
8
5
|
const fetch = async () => {
|
|
9
6
|
try {
|
|
@@ -25,9 +22,6 @@ const cron = {
|
|
|
25
22
|
fn: fetch,
|
|
26
23
|
};
|
|
27
24
|
|
|
28
|
-
const lookup = memoizeAsync((ip) => doRpc({ command: 'lookup', ip }));
|
|
29
|
-
|
|
30
25
|
module.exports.fetch = fetch;
|
|
31
26
|
module.exports.get = get;
|
|
32
27
|
module.exports.cron = cron;
|
|
33
|
-
module.exports.lookup = lookup;
|