@abtnode/blocklet-services 1.16.11-beta-0ae58a71 → 1.16.11-beta-f74663ac
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/api/index.js +11 -2
- package/api/libs/auth/adapters/auth0/management-client.js +1 -1
- package/api/libs/auth/utils.js +1 -40
- package/api/libs/connect/session.js +1 -1
- package/api/libs/jwt.js +1 -1
- package/api/middlewares/check-admin-permission.js +1 -1
- package/api/middlewares/check-running.js +23 -5
- package/api/middlewares/proxy-to-daemon.js +1 -1
- package/api/routes/blocklet.js +94 -58
- package/api/routes/dns-resolver.js +2 -2
- package/api/routes/user.js +29 -20
- package/api/services/auth/connect/bind-wallet.js +2 -2
- package/api/services/auth/connect/exchange-passport.js +1 -1
- package/api/services/auth/connect/login.js +1 -1
- package/api/services/auth/connect/pre-setup.js +1 -1
- package/api/services/auth/connect/receive-transfer-app-owner.js +1 -1
- package/api/services/auth/connect/switch-passport.js +1 -1
- package/api/services/auth/connect/switch-profile.js +1 -1
- package/api/services/auth/session.js +8 -1
- package/api/services/dashboard/index.js +1 -1
- package/api/services/notification/index.js +7 -3
- package/api/services/relay/index.js +1 -1
- package/api/services/static.js +1 -1
- package/api/socket/channel/app.js +2 -2
- package/api/socket/channel/did.js +26 -7
- package/api/socket/util.js +7 -0
- package/api/util/attach-shared-utils.js +2 -2
- package/api/util/index.js +1 -0
- package/build/asset-manifest.json +21 -21
- package/build/index.html +1 -1
- package/build/static/css/{387.bcbdad69.chunk.css → 602.df93b930.chunk.css} +1 -1
- package/build/static/js/158.ca656ebd.chunk.js +2 -0
- package/build/static/js/{189.f876739c.chunk.js → 189.97613e3f.chunk.js} +3 -3
- package/build/static/js/{359.c47779c2.chunk.js → 359.d6450ee2.chunk.js} +2 -2
- package/build/static/js/573.7ab09349.chunk.js +2 -0
- package/build/static/js/602.82badcd8.chunk.js +3 -0
- package/build/static/js/681.e2fe9e19.chunk.js +2 -0
- package/build/static/js/{716.0d2a2d32.chunk.js → 716.b48e64d9.chunk.js} +2 -2
- package/build/static/js/737.ef64812c.chunk.js +2 -0
- package/build/static/js/main.b00547b9.js +3 -0
- package/package.json +20 -21
- package/build/static/js/158.4e4fac79.chunk.js +0 -2
- package/build/static/js/387.9cec9de4.chunk.js +0 -3
- package/build/static/js/573.1be7f42e.chunk.js +0 -2
- package/build/static/js/681.77631131.chunk.js +0 -2
- package/build/static/js/737.38028d6a.chunk.js +0 -2
- package/build/static/js/main.f3fe2438.js +0 -3
- /package/build/static/js/{189.f876739c.chunk.js.LICENSE.txt → 189.97613e3f.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{387.9cec9de4.chunk.js.LICENSE.txt → 602.82badcd8.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{main.f3fe2438.js.LICENSE.txt → main.b00547b9.js.LICENSE.txt} +0 -0
package/api/index.js
CHANGED
|
@@ -13,7 +13,7 @@ const minimatch = require('minimatch');
|
|
|
13
13
|
|
|
14
14
|
const { getAccessLogStream } = require('@abtnode/logger');
|
|
15
15
|
const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES_PREFIX, EVENTS } = require('@abtnode/constant');
|
|
16
|
-
const { BlockletEvents } = require('@blocklet/constant');
|
|
16
|
+
const { BlockletEvents, BlockletInternalEvents } = require('@blocklet/constant');
|
|
17
17
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
18
18
|
const createInvite = require('@abtnode/auth/lib/invitation');
|
|
19
19
|
const eventHub =
|
|
@@ -145,6 +145,13 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
145
145
|
cache.del(cache.keyFns.node());
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
+
[BlockletInternalEvents.componentsUpdated, BlockletInternalEvents.appConfigChanged].forEach((name) => {
|
|
149
|
+
eventHub.on(name, (data) => {
|
|
150
|
+
const { appDid } = data;
|
|
151
|
+
notificationService.sendToApp.exec({ event: name, appDid, data });
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
148
155
|
// Http server
|
|
149
156
|
const server = express();
|
|
150
157
|
|
|
@@ -308,7 +315,7 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
308
315
|
// Simple websocket router like http router
|
|
309
316
|
const httpServer = http.createServer(server);
|
|
310
317
|
server.listen = httpServer.listen.bind(httpServer);
|
|
311
|
-
httpServer.on('upgrade',
|
|
318
|
+
httpServer.on('upgrade', (req, socket, head) => {
|
|
312
319
|
attachSharedUtils({ node, req, options });
|
|
313
320
|
|
|
314
321
|
// find matched handler registered by each service
|
|
@@ -410,6 +417,8 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
410
417
|
|
|
411
418
|
BlockletEventsNotifier.init({ node, notificationService });
|
|
412
419
|
|
|
420
|
+
server.sendToApp = ({ event, appDid, data }) => notificationService.sendToApp.exec({ event, appDid, data });
|
|
421
|
+
|
|
413
422
|
return server;
|
|
414
423
|
};
|
|
415
424
|
|
package/api/libs/auth/utils.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
const md5 = require('md5');
|
|
2
|
-
const axios = require('axios');
|
|
3
|
-
const logger = require('@abtnode/auth/lib/logger');
|
|
4
1
|
const { getPassportStatusEndpoint, getApplicationInfo } = require('@abtnode/auth/lib/auth');
|
|
5
2
|
const { createPassportVC } = require('@abtnode/auth/lib/passport');
|
|
6
3
|
const { VC_TYPE_NODE_PASSPORT, PASSPORT_STATUS } = require('@abtnode/constant');
|
|
7
|
-
const { parseUserAvatar } = require('@abtnode/util/lib/user');
|
|
4
|
+
const { parseUserAvatar, getAvatarByEmail, getAvatarByUrl } = require('@abtnode/util/lib/user');
|
|
8
5
|
const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
9
6
|
const pick = require('lodash/pick');
|
|
10
7
|
const uniq = require('lodash/uniq');
|
|
@@ -12,41 +9,6 @@ const uniqBy = require('lodash/uniqBy');
|
|
|
12
9
|
|
|
13
10
|
const { sendToUser } = require('../notification');
|
|
14
11
|
|
|
15
|
-
function getEmailHash(email = '') {
|
|
16
|
-
const cleanEmail = email.trim().toLowerCase();
|
|
17
|
-
return md5(cleanEmail);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function getAvatarByUrl(url, options = {}) {
|
|
21
|
-
const { verbose = true } = options || {};
|
|
22
|
-
try {
|
|
23
|
-
const { data } = await axios.get(url, {
|
|
24
|
-
responseType: 'arraybuffer',
|
|
25
|
-
});
|
|
26
|
-
const base64Content = Buffer.from(data, 'binary').toString('base64');
|
|
27
|
-
|
|
28
|
-
return `data:image/png;base64,${base64Content}`;
|
|
29
|
-
} catch (error) {
|
|
30
|
-
if (verbose) {
|
|
31
|
-
logger.error(`Fetch avatar failed: ${url}`, { error });
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function getAvatarByEmail(email = '') {
|
|
39
|
-
try {
|
|
40
|
-
const emailHash = getEmailHash(email);
|
|
41
|
-
const gravatarUrl = `https://www.gravatar.com/avatar/${emailHash}`;
|
|
42
|
-
const avatarBase64 = await getAvatarByUrl(gravatarUrl, { verbose: false });
|
|
43
|
-
return avatarBase64;
|
|
44
|
-
} catch (error) {
|
|
45
|
-
logger.error(`Fetch gravatar failed: ${email}`, { error });
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
12
|
// FIXME: @zhanghan 转移通行证目前只能颁发新的,会导致用户数据中产生多余的通行证
|
|
51
13
|
async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo }) {
|
|
52
14
|
if (!fromUser || !toUser) {
|
|
@@ -129,7 +91,6 @@ async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo
|
|
|
129
91
|
}
|
|
130
92
|
|
|
131
93
|
module.exports = {
|
|
132
|
-
getEmailHash,
|
|
133
94
|
getAvatarByUrl,
|
|
134
95
|
getAvatarByEmail,
|
|
135
96
|
transferPassport,
|
|
@@ -637,7 +637,7 @@ module.exports = {
|
|
|
637
637
|
|
|
638
638
|
// 基本流程与 login 一致,但在创建更新用户信息的逻辑不一样
|
|
639
639
|
bindWallet: {
|
|
640
|
-
authPrincipal:
|
|
640
|
+
authPrincipal: ({ email, locale, previousUserDid }) => {
|
|
641
641
|
const user = email || previousUserDid;
|
|
642
642
|
|
|
643
643
|
const message = locale === 'zh' ? `将你的 DID 钱包与账号 ${user} 绑定` : `Connect your DID Wallet with ${user}`;
|
package/api/libs/jwt.js
CHANGED
|
@@ -14,7 +14,7 @@ const getUser = async (node, teamDid, userDid) => {
|
|
|
14
14
|
const initJwt = (node, options) => {
|
|
15
15
|
const ttl = options.sessionTtl || '7d';
|
|
16
16
|
|
|
17
|
-
const createSessionToken =
|
|
17
|
+
const createSessionToken = (did, { role, secret, passport }) =>
|
|
18
18
|
createAuthToken({
|
|
19
19
|
did,
|
|
20
20
|
passport,
|
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
const { BlockletStatus } = require('@blocklet/constant');
|
|
2
2
|
const getBlockletMaintenanceTemplate = require('@abtnode/router-templates/lib/blocklet-maintenance');
|
|
3
|
+
const { findComponentByIdV2 } = require('@blocklet/meta/lib/util');
|
|
3
4
|
|
|
4
5
|
const { shouldGotoStartPage, getRedirectUrl } = require('../util');
|
|
5
6
|
|
|
6
7
|
const checkRunning = async (req, res, next) => {
|
|
7
|
-
const
|
|
8
|
+
const app = await req.getBlocklet();
|
|
9
|
+
const componentId = req.getBlockletComponentId();
|
|
10
|
+
let component = findComponentByIdV2(app, componentId);
|
|
11
|
+
|
|
12
|
+
// for backward compatibility
|
|
13
|
+
component = component || app;
|
|
14
|
+
|
|
8
15
|
if (
|
|
9
16
|
![
|
|
10
17
|
BlockletStatus.running,
|
|
11
18
|
// Waiting, Downloading should be allowed because blocklet is currently being upgrading
|
|
12
19
|
BlockletStatus.waiting,
|
|
13
20
|
BlockletStatus.downloading,
|
|
14
|
-
].includes(
|
|
21
|
+
].includes(component.status)
|
|
15
22
|
) {
|
|
16
|
-
if (shouldGotoStartPage(req,
|
|
17
|
-
|
|
23
|
+
if (shouldGotoStartPage(req, component)) {
|
|
24
|
+
// FIXME: @wangshijun how to validate this token or generate token here?
|
|
25
|
+
if (req.query.setupToken) {
|
|
26
|
+
res.cookie('login_token', req.query.setupToken, { maxAge: 24 * 60 * 60 * 1000 });
|
|
27
|
+
}
|
|
28
|
+
if (app.settings.initialized) {
|
|
18
29
|
res.redirect(getRedirectUrl({ req, pagePath: '/start' }));
|
|
19
30
|
} else {
|
|
20
31
|
res.redirect(getRedirectUrl({ req, pagePath: '/setup' }));
|
|
@@ -28,11 +39,18 @@ const checkRunning = async (req, res, next) => {
|
|
|
28
39
|
if (req.accepts(['html', 'json']) === 'json') {
|
|
29
40
|
res.json({ code: 'error', error: 'blocklet is under maintenance' });
|
|
30
41
|
} else {
|
|
31
|
-
res.send(getBlockletMaintenanceTemplate(
|
|
42
|
+
res.send(getBlockletMaintenanceTemplate(app, nodeInfo, component));
|
|
32
43
|
}
|
|
33
44
|
return;
|
|
34
45
|
}
|
|
35
46
|
|
|
47
|
+
const url = new URL(`http://localhost${req.url}`);
|
|
48
|
+
if (url.searchParams.get('__start__')) {
|
|
49
|
+
url.searchParams.delete('__start__');
|
|
50
|
+
res.redirect(`${url.pathname}${url.search}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
36
54
|
next();
|
|
37
55
|
};
|
|
38
56
|
|
|
@@ -37,7 +37,7 @@ const proxyToDaemon = ({ proxy, pathname, sessionSecret }) => {
|
|
|
37
37
|
return token;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
return
|
|
40
|
+
return (req, res) => {
|
|
41
41
|
req.url = `${pathname}?${qs.stringify(req.query)}`;
|
|
42
42
|
// did returned ty req.getBlockletDid() is blocklet.meta.did
|
|
43
43
|
// the blocklet.meta.did is always the same as appPid in structV2 application
|
package/api/routes/blocklet.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const cloneDeep = require('lodash/cloneDeep');
|
|
4
|
-
const joinUrl = require('url-join');
|
|
5
4
|
const dayjs = require('dayjs');
|
|
6
5
|
const JWT = require('@arcblock/jwt');
|
|
6
|
+
const joinUrl = require('url-join');
|
|
7
7
|
const handleInstanceInStore = require('@abtnode/core/lib/util/public-to-store');
|
|
8
|
+
const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
|
|
9
|
+
const { createPassportVC, createPassport, createUserPassport } = require('@abtnode/auth/lib/passport');
|
|
8
10
|
|
|
9
11
|
const formatContext = require('@abtnode/util/lib/format-context');
|
|
10
12
|
const { parseUserAvatar, getAvatarFile } = require('@abtnode/util/lib/user');
|
|
@@ -19,18 +21,15 @@ const {
|
|
|
19
21
|
cacheError,
|
|
20
22
|
} = require('@abtnode/util/lib/logo-middleware');
|
|
21
23
|
const formatName = require('@abtnode/util/lib/format-name');
|
|
22
|
-
const {
|
|
23
|
-
|
|
24
|
+
const {
|
|
25
|
+
fixBlockletStatus,
|
|
26
|
+
wipeSensitiveData,
|
|
27
|
+
findComponentByIdV2,
|
|
28
|
+
forEachComponentV2Sync,
|
|
29
|
+
} = require('@blocklet/meta/lib/util');
|
|
30
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_PATH_PREFIX, ROLES } = require('@abtnode/constant');
|
|
24
31
|
const logger = require('@abtnode/logger')(require('../../package.json').name);
|
|
25
32
|
|
|
26
|
-
const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
|
|
27
|
-
|
|
28
|
-
const {
|
|
29
|
-
createPassportVC,
|
|
30
|
-
createPassport,
|
|
31
|
-
upsertToPassports,
|
|
32
|
-
createUserPassport,
|
|
33
|
-
} = require('@abtnode/auth/lib/passport');
|
|
34
33
|
const { createDownloadLogStream } = require('@abtnode/util/lib/log');
|
|
35
34
|
|
|
36
35
|
const { BlockletStatus } = require('@blocklet/constant');
|
|
@@ -94,7 +93,7 @@ module.exports = {
|
|
|
94
93
|
onGetBlocklet: async ({ req }) => {
|
|
95
94
|
const blocklet = await req.getBlocklet();
|
|
96
95
|
const dids = req.url.split('?')[0].replace(`${prefix}/blocklet/logo-bundle`, '').split('/').filter(Boolean);
|
|
97
|
-
const component =
|
|
96
|
+
const component = findComponentByIdV2(blocklet, [blocklet.meta.did].concat(dids));
|
|
98
97
|
return component;
|
|
99
98
|
},
|
|
100
99
|
}),
|
|
@@ -137,7 +136,7 @@ module.exports = {
|
|
|
137
136
|
const blocklet = await req.getBlocklet({ useCache: false });
|
|
138
137
|
|
|
139
138
|
const { fromSetup } = req.body;
|
|
140
|
-
const { did: userDid
|
|
139
|
+
const { did: userDid } = req.user;
|
|
141
140
|
|
|
142
141
|
// eslint-disable-next-line no-unreachable
|
|
143
142
|
|
|
@@ -176,8 +175,7 @@ module.exports = {
|
|
|
176
175
|
const user = await node.getUser({ teamDid, user: { did: userDid } });
|
|
177
176
|
user.avatar = await parseUserAvatar(user.avatar, { dataDir: blocklet.env.dataDir });
|
|
178
177
|
|
|
179
|
-
const { pk, locale = 'en'
|
|
180
|
-
const { baseUrl } = extra;
|
|
178
|
+
const { pk, locale = 'en' } = user;
|
|
181
179
|
|
|
182
180
|
await node.setBlockletInitialized({ did: blocklet.meta.did, owner: { did: userDid, pk } });
|
|
183
181
|
|
|
@@ -193,52 +191,57 @@ module.exports = {
|
|
|
193
191
|
}
|
|
194
192
|
}
|
|
195
193
|
|
|
196
|
-
// create vc
|
|
197
|
-
const vc = createPassportVC({
|
|
198
|
-
issuerName: name,
|
|
199
|
-
issuerWallet: wallet,
|
|
200
|
-
ownerDid: userDid,
|
|
201
|
-
passport: await createPassport({
|
|
202
|
-
name: role,
|
|
203
|
-
node,
|
|
204
|
-
teamDid,
|
|
205
|
-
locale,
|
|
206
|
-
endpoint: baseUrl,
|
|
207
|
-
}),
|
|
208
|
-
endpoint: getPassportStatusEndpoint({
|
|
209
|
-
baseUrl: joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
|
|
210
|
-
userDid,
|
|
211
|
-
teamDid,
|
|
212
|
-
}),
|
|
213
|
-
ownerProfile: user,
|
|
214
|
-
preferredColor: passportColor,
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// write passport to db
|
|
218
|
-
const passport = createUserPassport(vc, { role });
|
|
219
|
-
const result = await node.updateUser({
|
|
220
|
-
teamDid,
|
|
221
|
-
user: {
|
|
222
|
-
did: userDid,
|
|
223
|
-
pk,
|
|
224
|
-
passports: upsertToPassports(user.passports || [], passport),
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
await node.createAuditLog(
|
|
228
|
-
{
|
|
229
|
-
action: 'updateUser',
|
|
230
|
-
args: { teamDid, passport, reason: 'setup blocklet' },
|
|
231
|
-
context: formatContext(req),
|
|
232
|
-
result,
|
|
233
|
-
},
|
|
234
|
-
node
|
|
235
|
-
);
|
|
236
|
-
|
|
237
194
|
// send notification to wallet
|
|
238
195
|
const receiver = userDid;
|
|
239
196
|
const token = JWT.sign(wallet.address, wallet.secretKey);
|
|
240
197
|
|
|
241
|
-
if
|
|
198
|
+
// send passport to wallet if no passport for this user
|
|
199
|
+
const role = ROLES.OWNER;
|
|
200
|
+
const hasOwnerPassport = (user.passports || []).some((x) => x.name === role);
|
|
201
|
+
if (hasOwnerPassport === false) {
|
|
202
|
+
const appUrl = blocklet.environments.find((item) => item.key === 'BLOCKLET_APP_URL').value;
|
|
203
|
+
|
|
204
|
+
// create vc
|
|
205
|
+
const vc = createPassportVC({
|
|
206
|
+
issuerName: name,
|
|
207
|
+
issuerWallet: wallet,
|
|
208
|
+
ownerDid: userDid,
|
|
209
|
+
passport: await createPassport({
|
|
210
|
+
name: role,
|
|
211
|
+
node,
|
|
212
|
+
teamDid,
|
|
213
|
+
locale,
|
|
214
|
+
endpoint: appUrl,
|
|
215
|
+
}),
|
|
216
|
+
endpoint: getPassportStatusEndpoint({
|
|
217
|
+
baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
|
|
218
|
+
userDid,
|
|
219
|
+
teamDid,
|
|
220
|
+
}),
|
|
221
|
+
ownerProfile: user,
|
|
222
|
+
preferredColor: passportColor,
|
|
223
|
+
expirationDate: undefined,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// write passport to db
|
|
227
|
+
const passport = createUserPassport(vc, { role });
|
|
228
|
+
const result = await node.updateUser({
|
|
229
|
+
teamDid,
|
|
230
|
+
user: {
|
|
231
|
+
did: userDid,
|
|
232
|
+
passports: [passport],
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
await node.createAuditLog(
|
|
236
|
+
{
|
|
237
|
+
action: 'updateUser',
|
|
238
|
+
args: { teamDid, passport, reason: 'setup blocklet' },
|
|
239
|
+
context: formatContext(req),
|
|
240
|
+
result,
|
|
241
|
+
},
|
|
242
|
+
node
|
|
243
|
+
);
|
|
244
|
+
|
|
242
245
|
// send owner vc
|
|
243
246
|
const notificationText = {
|
|
244
247
|
title: {
|
|
@@ -261,7 +264,7 @@ module.exports = {
|
|
|
261
264
|
type: 'vc',
|
|
262
265
|
data: {
|
|
263
266
|
credential: vc,
|
|
264
|
-
tag:
|
|
267
|
+
tag: ROLES.OWNER,
|
|
265
268
|
},
|
|
266
269
|
},
|
|
267
270
|
],
|
|
@@ -320,10 +323,43 @@ module.exports = {
|
|
|
320
323
|
server.get(`${prefix}/health`, async (req, res) => {
|
|
321
324
|
const blocklet = await req.getBlocklet();
|
|
322
325
|
|
|
326
|
+
if (!blocklet) {
|
|
327
|
+
return res.status(404).json({ message: 'blocklet not found' });
|
|
328
|
+
}
|
|
329
|
+
|
|
323
330
|
if (blocklet.status !== BlockletStatus.running) {
|
|
324
331
|
return res.status(503).json({ message: 'not running' });
|
|
325
332
|
}
|
|
326
333
|
|
|
334
|
+
const components = {};
|
|
335
|
+
forEachComponentV2Sync(blocklet, (component) => {
|
|
336
|
+
components[component.meta.did] = {
|
|
337
|
+
running: component.status === BlockletStatus.running,
|
|
338
|
+
};
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return res.json({ message: 'ok', components });
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
server.get(`${prefix}/health/:componentId`, async (req, res) => {
|
|
345
|
+
const { componentId } = req.params;
|
|
346
|
+
|
|
347
|
+
const blocklet = await req.getBlocklet();
|
|
348
|
+
|
|
349
|
+
if (!blocklet) {
|
|
350
|
+
return res.status(404).json({ message: 'blocklet not found' });
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const component = findComponentByIdV2(blocklet, componentId);
|
|
354
|
+
|
|
355
|
+
if (!component) {
|
|
356
|
+
return res.status(404).json({ message: 'component not found' });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (component.status !== BlockletStatus.running) {
|
|
360
|
+
return res.status(503).json({ message: 'not running' });
|
|
361
|
+
}
|
|
362
|
+
|
|
327
363
|
return res.json({ message: 'ok' });
|
|
328
364
|
});
|
|
329
365
|
|
|
@@ -9,7 +9,7 @@ module.exports = {
|
|
|
9
9
|
maxAge: 5 * 60 * 1000, // cache for 5 minute
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
app.get(`${WELLKNOWN_SERVICE_PATH_PREFIX}/api/dns-resolve/`,
|
|
12
|
+
app.get(`${WELLKNOWN_SERVICE_PATH_PREFIX}/api/dns-resolve/`, (req, res) => {
|
|
13
13
|
const { hostname } = req.query;
|
|
14
14
|
|
|
15
15
|
if (!hostname) {
|
|
@@ -24,7 +24,7 @@ module.exports = {
|
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
dns.lookup(hostname,
|
|
27
|
+
dns.lookup(hostname, (err, address) => {
|
|
28
28
|
if (err) {
|
|
29
29
|
cache.del(hostname);
|
|
30
30
|
res.json({ address: null, error: err.message });
|
package/api/routes/user.js
CHANGED
|
@@ -4,6 +4,7 @@ const { fromAppDid } = require('@arcblock/did-ext');
|
|
|
4
4
|
const { extractUserAvatar } = require('@abtnode/util/lib/user');
|
|
5
5
|
const formatContext = require('@abtnode/util/lib/format-context');
|
|
6
6
|
const logger = require('@abtnode/logger')('blocklet-services:user');
|
|
7
|
+
const { isFromPublicKey } = require('@arcblock/did');
|
|
7
8
|
|
|
8
9
|
const { generateTranslate } = require('../libs/translate');
|
|
9
10
|
const { isInvitedUserOnly } = require('../util');
|
|
@@ -14,6 +15,14 @@ const { loginWalletSchema, loginOAuthSchema } = require('../validators/login');
|
|
|
14
15
|
const verifySig = require('../middlewares/verify-sig');
|
|
15
16
|
const { getAvatarByEmail } = require('../libs/auth/utils');
|
|
16
17
|
|
|
18
|
+
const validateUser = (user) => {
|
|
19
|
+
try {
|
|
20
|
+
return user && user.did && user.pk && isFromPublicKey(user.did, user.pk);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
17
26
|
const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
|
|
18
27
|
|
|
19
28
|
const prefix = `${PREFIX}/user`;
|
|
@@ -22,11 +31,13 @@ const translations = {
|
|
|
22
31
|
needInviteToLogin: '你需要被邀请才可以登录此应用',
|
|
23
32
|
notAllowed: '你没有权限登录该节点',
|
|
24
33
|
needComponentId: '缺少登录参数: componentId',
|
|
34
|
+
userInfoError: '登录用户信息有误',
|
|
25
35
|
},
|
|
26
36
|
en: {
|
|
27
37
|
needInviteToLogin: 'You need to be invited to sign in to this app',
|
|
28
38
|
notAllowed: 'You are not allowed to login to this node',
|
|
29
39
|
needComponentId: 'componentId is required when login user',
|
|
40
|
+
userInfoError: 'Login user info is invalid',
|
|
30
41
|
},
|
|
31
42
|
};
|
|
32
43
|
|
|
@@ -40,7 +51,7 @@ async function checkNeedInvite({ req, node, teamDid, componentId, locale }) {
|
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
53
|
|
|
43
|
-
|
|
54
|
+
function checkUserEnable(user, { locale }) {
|
|
44
55
|
if (!user.approved) {
|
|
45
56
|
throw new ApiError(403, t('notAllowed', locale));
|
|
46
57
|
}
|
|
@@ -62,7 +73,7 @@ async function composeProfileData({ avatar, fullName, email }, { node, req, team
|
|
|
62
73
|
}
|
|
63
74
|
}
|
|
64
75
|
if (avatar) {
|
|
65
|
-
avatarLocal = await getAvatarByUrl(avatar);
|
|
76
|
+
avatarLocal = avatar.startsWith('data:') ? avatar : await getAvatarByUrl(avatar);
|
|
66
77
|
}
|
|
67
78
|
if (avatarLocal) {
|
|
68
79
|
const nodeInfo = await req.getNodeInfo();
|
|
@@ -77,10 +88,7 @@ async function composeProfileData({ avatar, fullName, email }, { node, req, team
|
|
|
77
88
|
return profile;
|
|
78
89
|
}
|
|
79
90
|
|
|
80
|
-
async function loginWallet(
|
|
81
|
-
{ did, pk, avatar, email, fullName },
|
|
82
|
-
{ node, req, locale, componentId, teamDid, blockletWallet }
|
|
83
|
-
) {
|
|
91
|
+
async function loginWallet({ did, pk, avatar, email, fullName }, { node, req, locale, componentId, teamDid }) {
|
|
84
92
|
const provider = 'wallet';
|
|
85
93
|
const { error } = loginWalletSchema.validate({
|
|
86
94
|
provider,
|
|
@@ -101,8 +109,6 @@ async function loginWallet(
|
|
|
101
109
|
},
|
|
102
110
|
options: {
|
|
103
111
|
enableConnectedAccount: true,
|
|
104
|
-
enableNormalize: true,
|
|
105
|
-
blockletSk: blockletWallet.secretKey,
|
|
106
112
|
},
|
|
107
113
|
});
|
|
108
114
|
|
|
@@ -116,17 +122,21 @@ async function loginWallet(
|
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
const lastLoginIp = req.get('x-real-ip');
|
|
119
|
-
const
|
|
125
|
+
const loginParams = {
|
|
120
126
|
teamDid,
|
|
121
127
|
user: {
|
|
122
|
-
did: currentUser?.did ||
|
|
123
|
-
pk: currentUser?.pk ||
|
|
128
|
+
did: currentUser?.did || did,
|
|
129
|
+
pk: currentUser?.pk || pk,
|
|
124
130
|
locale,
|
|
125
131
|
lastLoginIp,
|
|
126
132
|
connectedAccount: { provider, did, pk },
|
|
127
133
|
...profile,
|
|
128
134
|
},
|
|
129
|
-
}
|
|
135
|
+
};
|
|
136
|
+
if (!validateUser(loginParams.user) || !validateUser(loginParams.user.connectedAccount)) {
|
|
137
|
+
throw new ApiError(400, t('userInfoError', locale));
|
|
138
|
+
}
|
|
139
|
+
const doc = await node.loginUser(loginParams);
|
|
130
140
|
|
|
131
141
|
// NOTICE: 使用 provider: wallet 登录的账号,其账号是由 wallet 产生的,所以不需要到链上 declare 了
|
|
132
142
|
return doc;
|
|
@@ -157,8 +167,6 @@ async function loginOAuth(
|
|
|
157
167
|
},
|
|
158
168
|
options: {
|
|
159
169
|
enableConnectedAccount: true,
|
|
160
|
-
enableNormalize: true,
|
|
161
|
-
blockletSk: blockletWallet.secretKey,
|
|
162
170
|
},
|
|
163
171
|
});
|
|
164
172
|
|
|
@@ -173,7 +181,7 @@ async function loginOAuth(
|
|
|
173
181
|
|
|
174
182
|
const lastLoginIp = req.get('x-real-ip');
|
|
175
183
|
|
|
176
|
-
const
|
|
184
|
+
const loginParams = {
|
|
177
185
|
teamDid,
|
|
178
186
|
user: {
|
|
179
187
|
did: currentUser?.did || userDid,
|
|
@@ -183,7 +191,11 @@ async function loginOAuth(
|
|
|
183
191
|
connectedAccount: { provider, id, did: userDid, pk: userPk },
|
|
184
192
|
...profile,
|
|
185
193
|
},
|
|
186
|
-
}
|
|
194
|
+
};
|
|
195
|
+
if (!validateUser(loginParams.user) || !validateUser(loginParams.user.connectedAccount)) {
|
|
196
|
+
throw new ApiError(400, t('userInfoError', locale));
|
|
197
|
+
}
|
|
198
|
+
const doc = await node.loginUser(loginParams);
|
|
187
199
|
return doc;
|
|
188
200
|
}
|
|
189
201
|
|
|
@@ -198,10 +210,7 @@ async function login(req, node, options) {
|
|
|
198
210
|
|
|
199
211
|
let doc;
|
|
200
212
|
if (provider === 'wallet') {
|
|
201
|
-
doc = await loginWallet(
|
|
202
|
-
{ did, pk, avatar, email, fullName },
|
|
203
|
-
{ node, req, locale, componentId, teamDid, blockletWallet }
|
|
204
|
-
);
|
|
213
|
+
doc = await loginWallet({ did, pk, avatar, email, fullName }, { node, req, locale, componentId, teamDid });
|
|
205
214
|
} else {
|
|
206
215
|
doc = await loginOAuth(
|
|
207
216
|
{ provider, id, avatar, email, fullName },
|
|
@@ -9,11 +9,11 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
9
9
|
action: 'bind-wallet',
|
|
10
10
|
authPrincipal: false,
|
|
11
11
|
claims: {
|
|
12
|
-
authPrincipal:
|
|
12
|
+
authPrincipal: ({ extraParams: { locale, previousUserDid, email } }) => {
|
|
13
13
|
return authPrincipal({ locale, previousUserDid, email });
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
|
-
onConnect:
|
|
16
|
+
onConnect: ({ req, userDid, extraParams: { locale, passportId = '', componentId, previousUserDid } }) => {
|
|
17
17
|
return onConnect({
|
|
18
18
|
node,
|
|
19
19
|
request: req,
|
|
@@ -17,7 +17,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
|
|
20
|
-
onConnect:
|
|
20
|
+
onConnect: ({ req, userDid, extraParams: { locale, passportId = '', componentId } }) => {
|
|
21
21
|
return onConnect({ node, request: req, userDid, locale, passportId, componentId, action: 'exchangePassport' });
|
|
22
22
|
},
|
|
23
23
|
|
|
@@ -7,7 +7,7 @@ const { onConnect, onApprove } = login;
|
|
|
7
7
|
module.exports = function createRoutes(node, authenticator, createSessionToken) {
|
|
8
8
|
return {
|
|
9
9
|
action: 'login',
|
|
10
|
-
onConnect:
|
|
10
|
+
onConnect: ({ req, userDid, extraParams: { locale, passportId = '', componentId }, baseUrl }) => {
|
|
11
11
|
return onConnect({ node, request: req, userDid, locale, passportId, componentId, action: 'login', baseUrl });
|
|
12
12
|
},
|
|
13
13
|
|
|
@@ -9,7 +9,7 @@ module.exports = function createRoutes() {
|
|
|
9
9
|
action: 'pre-setup',
|
|
10
10
|
authPrincipal: false,
|
|
11
11
|
claims: getAppDidOwnerClaims(),
|
|
12
|
-
onAuth:
|
|
12
|
+
onAuth: ({ claims, userDid, userPk, extraParams: { locale } }) => {
|
|
13
13
|
const claim = claims.find((x) => x.type === 'signature');
|
|
14
14
|
verifySignature(claim, userDid, userPk, locale);
|
|
15
15
|
logger.info('pre-setup.connect.success', { userDid });
|
|
@@ -131,7 +131,7 @@ module.exports = function createRoutes(node, _, createSessionToken) {
|
|
|
131
131
|
|
|
132
132
|
return getKeyPairClaim(node, { title: blocklet.meta.title, declare: false })(opts);
|
|
133
133
|
},
|
|
134
|
-
profile:
|
|
134
|
+
profile: ({ extraParams: { locale = 'en' } }) => {
|
|
135
135
|
return {
|
|
136
136
|
fields: ['fullName', 'email', 'avatar'],
|
|
137
137
|
description: messages.requestProfile[locale],
|
|
@@ -5,7 +5,7 @@ const { onConnect, onApprove } = switchPassport;
|
|
|
5
5
|
module.exports = function createRoutes(node, authenticator, createSessionToken) {
|
|
6
6
|
return {
|
|
7
7
|
action: 'switch-passport',
|
|
8
|
-
onConnect:
|
|
8
|
+
onConnect: ({ req, userDid, baseUrl, extraParams: { locale, connectedDid } }) => {
|
|
9
9
|
return onConnect({
|
|
10
10
|
node,
|
|
11
11
|
request: req,
|
|
@@ -15,7 +15,7 @@ module.exports = function createRoutes(node) {
|
|
|
15
15
|
});
|
|
16
16
|
},
|
|
17
17
|
|
|
18
|
-
onAuth:
|
|
18
|
+
onAuth: ({ req, claims, userDid, extraParams: { locale } }) => {
|
|
19
19
|
const profile = claims.find((x) => x.type === 'profile');
|
|
20
20
|
return onApprove({
|
|
21
21
|
node,
|