@abtnode/blocklet-services 1.6.30 → 1.7.1
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 +27 -15
- package/api/libs/auth.js +1 -1
- package/api/libs/jwt.js +4 -4
- package/api/middlewares/check-running.js +7 -11
- package/api/middlewares/proxy-to-daemon.js +42 -0
- package/api/routes/blocklet.js +101 -0
- package/api/routes/dns-resolver.js +39 -0
- package/api/routes/env.js +15 -5
- package/api/services/auth/connect/invite.js +4 -4
- package/api/services/auth/connect/login.js +68 -45
- package/api/services/auth/connect/setup.js +102 -0
- package/api/services/auth/index.js +23 -17
- package/api/services/notification/index.js +15 -19
- package/api/services/static.js +6 -26
- package/api/state/message.js +1 -1
- package/api/util/attach-shared-utils.js +21 -10
- package/api/util/get-dynamic-service-config.js +22 -0
- package/api/util/index.js +28 -2
- package/build/asset-manifest.json +34 -29
- package/build/index.html +1 -1
- package/build/precache-manifest.fc03dfbbf370fcf983cbef3f121b7584.js +186 -0
- package/build/service-worker.js +1 -1
- package/build/static/css/4.634341e0.chunk.css +2 -0
- package/build/static/css/4.634341e0.chunk.css.map +1 -0
- package/build/static/js/0.b6b3b23c.chunk.js +3 -0
- package/build/static/js/{3.38949d38.chunk.js.LICENSE.txt → 0.b6b3b23c.chunk.js.LICENSE.txt} +0 -9
- package/build/static/js/0.b6b3b23c.chunk.js.map +1 -0
- package/build/static/js/1.0e83f63d.chunk.js +2 -0
- package/build/static/js/1.0e83f63d.chunk.js.map +1 -0
- package/build/static/js/10.4199ec3c.chunk.js +2 -0
- package/build/static/js/10.4199ec3c.chunk.js.map +1 -0
- package/build/static/js/4.0046a465.chunk.js +3 -0
- package/build/static/js/{2.ca22e0d7.chunk.js.LICENSE.txt → 4.0046a465.chunk.js.LICENSE.txt} +9 -0
- package/build/static/js/4.0046a465.chunk.js.map +1 -0
- package/build/static/js/5.4304d38f.chunk.js +2 -0
- package/build/static/js/5.4304d38f.chunk.js.map +1 -0
- package/build/static/js/6.08043dea.chunk.js +2 -0
- package/build/static/js/6.08043dea.chunk.js.map +1 -0
- package/build/static/js/7.165c3091.chunk.js +2 -0
- package/build/static/js/7.165c3091.chunk.js.map +1 -0
- package/build/static/js/8.49993466.chunk.js +2 -0
- package/build/static/js/8.49993466.chunk.js.map +1 -0
- package/build/static/js/9.69df4f05.chunk.js +2 -0
- package/build/static/js/9.69df4f05.chunk.js.map +1 -0
- package/build/static/js/main.af31d0af.chunk.js +2 -0
- package/build/static/js/main.af31d0af.chunk.js.map +1 -0
- package/build/static/js/runtime-main.aa0aaf4a.js +2 -0
- package/build/static/js/runtime-main.aa0aaf4a.js.map +1 -0
- package/build/static/media/lato-all-400-normal.a82dcb33.woff +0 -0
- package/build/static/media/lato-all-700-normal.8190ee0e.woff +0 -0
- package/build/static/media/lato-latin-400-normal.e1b3b590.woff2 +0 -0
- package/build/static/media/lato-latin-700-normal.de69cf9e.woff2 +0 -0
- package/build/static/media/lato-latin-ext-400-normal.4bde07f9.woff2 +0 -0
- package/build/static/media/lato-latin-ext-700-normal.a48b0f04.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-400-normal.49f3ce37.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-500-normal.0c1bd2ab.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-600-normal.b76031ea.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-ext-400-normal.d3902986.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-ext-500-normal.7e493ee0.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-ext-600-normal.f4b6c519.woff2 +0 -0
- package/build/static/media/rubik-hebrew-400-normal.0ebef856.woff2 +0 -0
- package/build/static/media/rubik-hebrew-500-normal.3664f2b5.woff2 +0 -0
- package/build/static/media/rubik-hebrew-600-normal.6207349a.woff2 +0 -0
- package/build/static/media/rubik-latin-400-normal.b836d2ed.woff2 +0 -0
- package/build/static/media/rubik-latin-500-normal.18984747.woff2 +0 -0
- package/build/static/media/rubik-latin-600-normal.b1216018.woff2 +0 -0
- package/build/static/media/rubik-latin-ext-400-normal.69a88832.woff2 +0 -0
- package/build/static/media/rubik-latin-ext-500-normal.21b63491.woff2 +0 -0
- package/build/static/media/rubik-latin-ext-600-normal.d0d90e83.woff2 +0 -0
- package/package.json +28 -22
- package/build/precache-manifest.f8d516b676d205d1f6e2c4e869fbf297.js +0 -154
- package/build/static/css/2.d49e994f.chunk.css +0 -2
- package/build/static/css/2.d49e994f.chunk.css.map +0 -1
- package/build/static/js/2.ca22e0d7.chunk.js +0 -3
- package/build/static/js/2.ca22e0d7.chunk.js.map +0 -1
- package/build/static/js/3.38949d38.chunk.js +0 -3
- package/build/static/js/3.38949d38.chunk.js.map +0 -1
- package/build/static/js/4.97f8d423.chunk.js +0 -2
- package/build/static/js/4.97f8d423.chunk.js.map +0 -1
- package/build/static/js/5.8149df59.chunk.js +0 -2
- package/build/static/js/5.8149df59.chunk.js.map +0 -1
- package/build/static/js/6.001b8434.chunk.js +0 -2
- package/build/static/js/6.001b8434.chunk.js.map +0 -1
- package/build/static/js/7.f30a5254.chunk.js +0 -2
- package/build/static/js/7.f30a5254.chunk.js.map +0 -1
- package/build/static/js/8.e354cbf1.chunk.js +0 -2
- package/build/static/js/8.e354cbf1.chunk.js.map +0 -1
- package/build/static/js/main.561b39e2.chunk.js +0 -2
- package/build/static/js/main.561b39e2.chunk.js.map +0 -1
- package/build/static/js/runtime-main.8ddd5828.js +0 -2
- package/build/static/js/runtime-main.8ddd5828.js.map +0 -1
- package/build/static/media/rubik-cyrillic-400-normal.aa383bbd.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-500-normal.27a1ebd4.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-600-normal.74d5cdba.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-ext-400-normal.b8647475.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-ext-500-normal.860932d9.woff2 +0 -0
- package/build/static/media/rubik-cyrillic-ext-600-normal.942f240f.woff2 +0 -0
- package/build/static/media/rubik-hebrew-400-normal.2c9e3c2a.woff2 +0 -0
- package/build/static/media/rubik-hebrew-500-normal.08d6e502.woff2 +0 -0
- package/build/static/media/rubik-hebrew-600-normal.bfa32e44.woff2 +0 -0
- package/build/static/media/rubik-latin-400-normal.b8fd53c5.woff2 +0 -0
- package/build/static/media/rubik-latin-500-normal.595f1a98.woff2 +0 -0
- package/build/static/media/rubik-latin-600-normal.5f06934f.woff2 +0 -0
- package/build/static/media/rubik-latin-ext-400-normal.3c5c378e.woff2 +0 -0
- package/build/static/media/rubik-latin-ext-500-normal.5663c731.woff2 +0 -0
- package/build/static/media/rubik-latin-ext-600-normal.ff159cb8.woff2 +0 -0
package/api/index.js
CHANGED
|
@@ -29,7 +29,10 @@ const { init: initAuth } = require('./services/auth');
|
|
|
29
29
|
const StaticService = require('./services/static');
|
|
30
30
|
const createEnvRoutes = require('./routes/env');
|
|
31
31
|
const createBlockletRoutes = require('./routes/blocklet');
|
|
32
|
+
const createDnsResolver = require('./routes/dns-resolver');
|
|
32
33
|
const checkRunning = require('./middlewares/check-running');
|
|
34
|
+
const checkAdminPermission = require('./middlewares/check-admin-permission');
|
|
35
|
+
const proxyToDaemon = require('./middlewares/proxy-to-daemon');
|
|
33
36
|
const attachSharedUtils = require('./util/attach-shared-utils');
|
|
34
37
|
|
|
35
38
|
const logFileGenerator = (time, index) => {
|
|
@@ -52,7 +55,7 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
52
55
|
sessionSecret: process.env.ABT_NODE_SESSION_SECRET,
|
|
53
56
|
sessionTtl: process.env.ABT_NODE_SESSION_TTL,
|
|
54
57
|
webWalletUrl: 'https://web.abtwallet.io',
|
|
55
|
-
|
|
58
|
+
sessionTokenKey: 'login_token',
|
|
56
59
|
...serverOptions,
|
|
57
60
|
isProduction,
|
|
58
61
|
isE2E,
|
|
@@ -74,6 +77,15 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
74
77
|
|
|
75
78
|
// Proxy engine
|
|
76
79
|
const proxy = httpProxy.createProxyServer({});
|
|
80
|
+
proxy.safeWeb = (req, res, opts = {}) => {
|
|
81
|
+
proxy.web(req, res, opts, (error) => {
|
|
82
|
+
if (error) {
|
|
83
|
+
logger.error('http proxy error', { error });
|
|
84
|
+
res.status(502).send(`Can not proxy to upstream target: ${opts.target}`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
77
89
|
// eslint-disable-next-line no-unused-vars
|
|
78
90
|
proxy.on('proxyReq', (proxyReq, req, res) => {
|
|
79
91
|
if (req.rawBody) {
|
|
@@ -175,6 +187,12 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
175
187
|
authRoutes.createPassportRoutes.init(server, node, options);
|
|
176
188
|
authRoutes.createSessionRoutes.init(server, node, options);
|
|
177
189
|
|
|
190
|
+
// API: gql
|
|
191
|
+
server.use(`${WELLKNOWN_SERVICE_PATH_PREFIX}/api/gql`, checkAdminPermission, proxyToDaemon({ proxy, ...options }));
|
|
192
|
+
|
|
193
|
+
// API: dns resolver
|
|
194
|
+
createDnsResolver.init(server, node, options);
|
|
195
|
+
|
|
178
196
|
// Web Page
|
|
179
197
|
server.get(`${WELLKNOWN_SERVICE_PATH_PREFIX}/**`, (req, res) => {
|
|
180
198
|
res.sendWebPage();
|
|
@@ -202,20 +220,9 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
202
220
|
const { target } = ensureProxyUrl(req);
|
|
203
221
|
|
|
204
222
|
if (target) {
|
|
205
|
-
proxy.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
{
|
|
209
|
-
target,
|
|
210
|
-
},
|
|
211
|
-
(error) => {
|
|
212
|
-
if (error) {
|
|
213
|
-
console.error(error);
|
|
214
|
-
logger.error('http proxy error', { error });
|
|
215
|
-
res.status(502).send(`Can not proxy to upstream blocklet: ${target}`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
);
|
|
223
|
+
proxy.safeWeb(req, res, {
|
|
224
|
+
target,
|
|
225
|
+
});
|
|
219
226
|
} else {
|
|
220
227
|
next();
|
|
221
228
|
}
|
|
@@ -290,6 +297,11 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
290
297
|
}
|
|
291
298
|
});
|
|
292
299
|
|
|
300
|
+
// Events
|
|
301
|
+
server.on('sendToUser', (data) => {
|
|
302
|
+
notificationService.sendToUser.exec(data);
|
|
303
|
+
});
|
|
304
|
+
|
|
293
305
|
return server;
|
|
294
306
|
};
|
|
295
307
|
|
package/api/libs/auth.js
CHANGED
|
@@ -34,7 +34,7 @@ module.exports = (node, opts) => {
|
|
|
34
34
|
|
|
35
35
|
return {
|
|
36
36
|
name: meta.name,
|
|
37
|
-
description: meta.description || `
|
|
37
|
+
description: meta.description || `Connect to ${meta.name}`,
|
|
38
38
|
icon: logo,
|
|
39
39
|
updateSubEndpoint: true,
|
|
40
40
|
subscriptionEndpoint: joinUrl(groupPathPrefix, WELLKNOWN_SERVICE_PATH_PREFIX, 'websocket'),
|
package/api/libs/jwt.js
CHANGED
|
@@ -21,7 +21,7 @@ const initJwt = (node, options) => {
|
|
|
21
21
|
throw new Error('Auth service require a non-empty session secret to start');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const
|
|
24
|
+
const createSessionToken = async (did, { role, passport }) =>
|
|
25
25
|
createAuthToken({
|
|
26
26
|
did,
|
|
27
27
|
passport,
|
|
@@ -30,7 +30,7 @@ const initJwt = (node, options) => {
|
|
|
30
30
|
expiresIn: ttl,
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const verifySessionToken = (token, teamDid) =>
|
|
34
34
|
// eslint-disable-next-line implicit-arrow-linebreak
|
|
35
35
|
new Promise((resolve, reject) => {
|
|
36
36
|
jwt.verify(token, secret, async (err, decoded) => {
|
|
@@ -64,8 +64,8 @@ const initJwt = (node, options) => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
return {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
createSessionToken,
|
|
68
|
+
verifySessionToken,
|
|
69
69
|
};
|
|
70
70
|
};
|
|
71
71
|
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
const joinUrl = require('url-join');
|
|
2
|
-
|
|
3
1
|
const { BlockletStatus } = require('@blocklet/meta/lib/constants');
|
|
4
|
-
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
5
2
|
const getBlockletNotRunningTemplate = require('@abtnode/router-templates/lib/blocklet-not-running');
|
|
6
3
|
const getBlockletMaintenanceTemplate = require('@abtnode/router-templates/lib/blocklet-maintenance');
|
|
7
|
-
const { getBaseUrl } = require('@abtnode/router-adapter');
|
|
8
4
|
|
|
9
|
-
const { shouldGotoStartPage } = require('../util');
|
|
5
|
+
const { shouldGotoStartPage, getRedirectUrl } = require('../util');
|
|
10
6
|
|
|
11
7
|
const checkRunning = async (req, res, next) => {
|
|
12
8
|
const blocklet = await req.getBlocklet();
|
|
@@ -18,12 +14,12 @@ const checkRunning = async (req, res, next) => {
|
|
|
18
14
|
BlockletStatus.downloading,
|
|
19
15
|
].includes(blocklet.status)
|
|
20
16
|
) {
|
|
21
|
-
if (shouldGotoStartPage(req)) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
if (shouldGotoStartPage(req, blocklet)) {
|
|
18
|
+
if (blocklet.settings.initialized) {
|
|
19
|
+
res.redirect(getRedirectUrl({ req, pagePath: '/start' }));
|
|
20
|
+
} else {
|
|
21
|
+
res.redirect(getRedirectUrl({ req, pagePath: '/setup' }));
|
|
22
|
+
}
|
|
27
23
|
return;
|
|
28
24
|
}
|
|
29
25
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const jwt = require('jsonwebtoken');
|
|
2
|
+
const LRU = require('lru-cache');
|
|
3
|
+
|
|
4
|
+
const proxyToDaemon = ({ proxy, sessionSecret }) => {
|
|
5
|
+
const cache = new LRU({
|
|
6
|
+
max: 50, // cache at most 50 blocklet
|
|
7
|
+
maxAge: 86400 * 1000, // cache for 1 day
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const getToken = (did) => {
|
|
11
|
+
const cacheToken = cache.get(did);
|
|
12
|
+
if (cacheToken) {
|
|
13
|
+
return cacheToken;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const token = jwt.sign(
|
|
17
|
+
{
|
|
18
|
+
type: 'blocklet',
|
|
19
|
+
did,
|
|
20
|
+
},
|
|
21
|
+
sessionSecret,
|
|
22
|
+
{ expiresIn: '1d' }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
cache.set(did, token);
|
|
26
|
+
|
|
27
|
+
return token;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return async (req, res) => {
|
|
31
|
+
req.url = '/api/gql';
|
|
32
|
+
const did = req.getBlockletDid();
|
|
33
|
+
const token = getToken(did);
|
|
34
|
+
|
|
35
|
+
req.headers.authorization = `Bearer ${token}`;
|
|
36
|
+
proxy.safeWeb(req, res, {
|
|
37
|
+
target: `http://127.0.0.1:${process.env.ABT_NODE_PORT}`,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports = proxyToDaemon;
|
package/api/routes/blocklet.js
CHANGED
|
@@ -3,11 +3,22 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const get = require('lodash/get');
|
|
5
5
|
const cloneDeep = require('lodash/cloneDeep');
|
|
6
|
+
const joinUrl = require('url-join');
|
|
7
|
+
const JWT = require('@arcblock/jwt');
|
|
6
8
|
|
|
7
9
|
const { fixBlockletStatus, wipeSensitiveData } = require('@blocklet/meta/lib/util');
|
|
8
10
|
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
9
11
|
const logger = require('@abtnode/logger')(require('../../package.json').name);
|
|
10
12
|
|
|
13
|
+
const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
createPassportVC,
|
|
17
|
+
createPassport,
|
|
18
|
+
upsertToPassports,
|
|
19
|
+
createUserPassport,
|
|
20
|
+
} = require('@abtnode/auth/lib/passport');
|
|
21
|
+
|
|
11
22
|
const checkAdminPermission = require('../middlewares/check-admin-permission');
|
|
12
23
|
|
|
13
24
|
const polishBlocklet = (doc) => {
|
|
@@ -62,15 +73,105 @@ module.exports = {
|
|
|
62
73
|
|
|
63
74
|
server.post(`${prefix}/api/blocklet/start`, checkAdminPermission, async (req, res) => {
|
|
64
75
|
const blocklet = await req.getBlocklet();
|
|
76
|
+
const { fromSetup } = req.body;
|
|
77
|
+
|
|
65
78
|
const doc = await node.startBlocklet({
|
|
66
79
|
did: blocklet.meta.did,
|
|
67
80
|
checkHealthImmediately: true,
|
|
68
81
|
throwOnError: true,
|
|
69
82
|
});
|
|
70
83
|
|
|
84
|
+
if (fromSetup) {
|
|
85
|
+
await node.setBlockletInitialized({ did: blocklet.meta.did });
|
|
86
|
+
|
|
87
|
+
const { did: userDid, role } = req.user;
|
|
88
|
+
const { wallet, name, passportColor } = await req.getBlockletInfo();
|
|
89
|
+
const teamDid = blocklet.meta.did;
|
|
90
|
+
const user = await node.getUser({ teamDid, user: { did: userDid } });
|
|
91
|
+
const { pk, locale = 'en', extra = {} } = user;
|
|
92
|
+
const { baseUrl } = extra;
|
|
93
|
+
|
|
94
|
+
// create vc
|
|
95
|
+
const vc = createPassportVC({
|
|
96
|
+
issuerName: name,
|
|
97
|
+
issuerWallet: wallet,
|
|
98
|
+
ownerDid: userDid,
|
|
99
|
+
passport: await createPassport({
|
|
100
|
+
name: role,
|
|
101
|
+
node,
|
|
102
|
+
teamDid,
|
|
103
|
+
locale,
|
|
104
|
+
endpoint: (extra || {}).baseUrl,
|
|
105
|
+
}),
|
|
106
|
+
endpoint: getPassportStatusEndpoint({
|
|
107
|
+
baseUrl: joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
|
|
108
|
+
userDid,
|
|
109
|
+
teamDid,
|
|
110
|
+
}),
|
|
111
|
+
ownerProfile: user,
|
|
112
|
+
preferredColor: passportColor,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// write passport to db
|
|
116
|
+
const passport = createUserPassport(vc, { role });
|
|
117
|
+
await node.updateUser({
|
|
118
|
+
teamDid,
|
|
119
|
+
user: {
|
|
120
|
+
did: userDid,
|
|
121
|
+
pk,
|
|
122
|
+
passports: upsertToPassports(user.passports || [], passport),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// send vc to wallet
|
|
127
|
+
const receiver = userDid;
|
|
128
|
+
const token = JWT.sign(wallet.address, wallet.secretKey);
|
|
129
|
+
const data = {
|
|
130
|
+
sender: { did: teamDid, token, appDid: wallet.address },
|
|
131
|
+
receiver,
|
|
132
|
+
notification: {
|
|
133
|
+
title: {
|
|
134
|
+
en: 'Up and Running',
|
|
135
|
+
zh: '启动并运行',
|
|
136
|
+
}[locale],
|
|
137
|
+
body: {
|
|
138
|
+
en: 'The application is up and running successfully',
|
|
139
|
+
zh: '应用程序已启动并成功运行',
|
|
140
|
+
}[locale],
|
|
141
|
+
attachments: [
|
|
142
|
+
{
|
|
143
|
+
type: 'vc',
|
|
144
|
+
data: {
|
|
145
|
+
credential: vc,
|
|
146
|
+
tag: role,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
server.emit('sendToUser', data);
|
|
153
|
+
}
|
|
154
|
+
|
|
71
155
|
res.json(polishBlocklet(doc));
|
|
72
156
|
});
|
|
73
157
|
|
|
158
|
+
server.get(`${prefix}/api/blocklet/meta`, async (req, res) => {
|
|
159
|
+
const blocklet = await req.getBlocklet();
|
|
160
|
+
|
|
161
|
+
res.json(blocklet.meta);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
server.post(`${prefix}/api/blocklet/who-can-access`, checkAdminPermission, async (req, res) => {
|
|
165
|
+
const { whoCanAccess } = req.body;
|
|
166
|
+
const blocklet = await req.getBlocklet();
|
|
167
|
+
|
|
168
|
+
await node.configWhoCanAccess({ teamDid: blocklet.meta.did, value: whoCanAccess });
|
|
169
|
+
|
|
170
|
+
const newBlocklet = await req.getBlocklet({ useCache: false });
|
|
171
|
+
|
|
172
|
+
res.json(polishBlocklet(newBlocklet));
|
|
173
|
+
});
|
|
174
|
+
|
|
74
175
|
// backward compatible
|
|
75
176
|
server.get(`${prefix}/blocklet/logo/:did`, async (req, res) => {
|
|
76
177
|
const sendOptions = { maxAge: '1d' };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const dns = require('dns');
|
|
2
|
+
const LRU = require('lru-cache');
|
|
3
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
init(app) {
|
|
7
|
+
const cache = new LRU({
|
|
8
|
+
max: 50, // cache at most 50
|
|
9
|
+
maxAge: 5 * 60 * 1000, // cache for 5 minute
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
app.get(`${WELLKNOWN_SERVICE_PATH_PREFIX}/api/dns-resolve/`, async (req, res) => {
|
|
13
|
+
const { hostname } = req.query;
|
|
14
|
+
|
|
15
|
+
if (!hostname) {
|
|
16
|
+
res.status(400).send('hostname should not be empty');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const cachedAddress = cache.get(hostname);
|
|
21
|
+
|
|
22
|
+
if (cachedAddress) {
|
|
23
|
+
res.json({ address: cachedAddress });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
dns.lookup(hostname, async (err, address) => {
|
|
28
|
+
if (err) {
|
|
29
|
+
cache.del(hostname);
|
|
30
|
+
res.json({ address: null, error: err.message });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
cache.set(hostname, address);
|
|
35
|
+
res.json({ address });
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
};
|
package/api/routes/env.js
CHANGED
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
1
|
+
const get = require('lodash/get');
|
|
2
|
+
const { NODE_SERVICES, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
3
|
+
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/meta/lib/constants');
|
|
3
4
|
|
|
4
5
|
module.exports = {
|
|
5
6
|
init(server, node, opts) {
|
|
6
7
|
server.get(`**${WELLKNOWN_SERVICE_PATH_PREFIX}/api/env`, async (req, res) => {
|
|
7
8
|
res.type('js');
|
|
8
9
|
|
|
9
|
-
const [blocklet, config, info] = await Promise.all([
|
|
10
|
+
const [blockletInfo, blocklet, config, info] = await Promise.all([
|
|
10
11
|
req.getBlockletInfo(),
|
|
12
|
+
req.getBlocklet(),
|
|
11
13
|
req.getServiceConfig(NODE_SERVICES.AUTH),
|
|
12
14
|
req.getNodeInfo(),
|
|
13
15
|
]);
|
|
14
16
|
const pathPrefix = req.headers['x-path-prefix'] || '/';
|
|
15
17
|
const groupPathPrefix = req.headers['x-group-path-prefix'];
|
|
16
18
|
|
|
19
|
+
const passportColor = get(
|
|
20
|
+
(blocklet.environments || []).find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_PASSPORT_COLOR),
|
|
21
|
+
'value',
|
|
22
|
+
'auto'
|
|
23
|
+
);
|
|
24
|
+
|
|
17
25
|
res.send(`window.env = {
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
did: "${blockletInfo.did}",
|
|
27
|
+
appId: "${blocklet.appDid}",
|
|
28
|
+
appName: "${blockletInfo.name}",
|
|
20
29
|
pathPrefix: "${pathPrefix}",
|
|
21
30
|
apiPrefix: "${pathPrefix.replace(/\/+$/, '')}${WELLKNOWN_SERVICE_PATH_PREFIX}",
|
|
22
31
|
${groupPathPrefix ? `groupPathPrefix: "${groupPathPrefix}",` : ''}
|
|
23
32
|
webWalletUrl: "${info.webWalletUrl || config.webWalletUrl || opts.webWalletUrl}",
|
|
33
|
+
passportColor: "${passportColor}",
|
|
24
34
|
}`);
|
|
25
35
|
});
|
|
26
36
|
},
|
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
const { NODE_SERVICES, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
11
11
|
const logger = require('@abtnode/logger')(require('../../../../package.json').name);
|
|
12
12
|
|
|
13
|
-
module.exports = function createRoutes(node, authenticator,
|
|
13
|
+
module.exports = function createRoutes(node, authenticator, createSessionToken) {
|
|
14
14
|
return {
|
|
15
15
|
action: 'invite',
|
|
16
16
|
|
|
@@ -71,9 +71,9 @@ module.exports = function createRoutes(node, authenticator, login) {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
// Generate new session token that client can save to localStorage
|
|
74
|
-
const
|
|
75
|
-
await storage.update(token, { did: userDid,
|
|
76
|
-
logger.info('
|
|
74
|
+
const sessionToken = await createSessionToken(userDid, { passport, role });
|
|
75
|
+
await storage.update(token, { did: userDid, sessionToken });
|
|
76
|
+
logger.info('invite.success', { userDid });
|
|
77
77
|
|
|
78
78
|
return response;
|
|
79
79
|
},
|
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
VC_TYPE_NODE_PASSPORT,
|
|
16
16
|
NODE_SERVICES,
|
|
17
17
|
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
18
|
+
WHO_CAN_ACCESS,
|
|
18
19
|
} = require('@abtnode/constant');
|
|
19
20
|
const {
|
|
20
21
|
createPassportVC,
|
|
@@ -36,7 +37,7 @@ const vcTypes = [VC_TYPE_GENERAL_PASSPORT, VC_TYPE_NODE_PASSPORT];
|
|
|
36
37
|
* @returns {String} config[1] default role
|
|
37
38
|
* @returns {Boolean} config[2] issue passport
|
|
38
39
|
*/
|
|
39
|
-
const isInvitedUserOnly = async (
|
|
40
|
+
const isInvitedUserOnly = async (config, node, teamDid) => {
|
|
40
41
|
const count = await node.getUsersCount({ teamDid });
|
|
41
42
|
|
|
42
43
|
// issue owner passport for first login user
|
|
@@ -44,49 +45,67 @@ const isInvitedUserOnly = async (invitedUserOnly, node, teamDid) => {
|
|
|
44
45
|
return [false, ROLES.OWNER, true];
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
if ([
|
|
48
|
+
if ([WHO_CAN_ACCESS.OWNER, WHO_CAN_ACCESS.INVITED].includes(config.whoCanAccess)) {
|
|
49
|
+
return [true];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if ([WHO_CAN_ACCESS.ALL].includes(config.whoCanAccess)) {
|
|
53
|
+
return [false, ROLES.GUEST];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if ([true, 'yes', 'not-first'].includes(config.invitedUserOnly)) {
|
|
48
57
|
return [true];
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
return [false, ROLES.GUEST];
|
|
52
61
|
};
|
|
53
62
|
|
|
54
|
-
module.exports = function createRoutes(node, authenticator,
|
|
63
|
+
module.exports = function createRoutes(node, authenticator, createSessionToken) {
|
|
55
64
|
return {
|
|
56
65
|
action: 'login',
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
66
|
+
onConnect: async ({ req, userDid }) => {
|
|
67
|
+
const blocklet = await req.getBlocklet();
|
|
68
|
+
|
|
69
|
+
const claims = {
|
|
70
|
+
profile: async ({ extraParams, context }) => {
|
|
71
|
+
const { locale } = extraParams;
|
|
72
|
+
|
|
73
|
+
const config = await context.request.getServiceConfig(NODE_SERVICES.AUTH);
|
|
74
|
+
const profileFields = get(config, 'profileFields');
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
fields: profileFields || ['fullName', 'avatar'],
|
|
78
|
+
description: messages.description[locale],
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
verifiableCredential: async ({ context, extraParams: { locale } }) => {
|
|
83
|
+
const { request, abtwallet } = context;
|
|
84
|
+
const { wallet, did: teamDid } = await request.getBlockletInfo();
|
|
85
|
+
|
|
86
|
+
checkWalletVersion({ abtwallet, locale });
|
|
87
|
+
|
|
88
|
+
const trustedPassports = (blocklet.trustedPassports || []).map((x) => x.issuerDid);
|
|
89
|
+
const trustedIssuers = [wallet.address, ...trustedPassports].filter(Boolean);
|
|
90
|
+
|
|
91
|
+
const config = (await request.getServiceConfig(NODE_SERVICES.AUTH)) || {};
|
|
92
|
+
const [invitedUserOnly] = await isInvitedUserOnly(config, node, teamDid);
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
description: messages.requestPassport[locale],
|
|
96
|
+
item: vcTypes,
|
|
97
|
+
trustedIssuers,
|
|
98
|
+
optional: !invitedUserOnly,
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const user = await node.getUser({ teamDid: blocklet.meta.did, user: { did: userDid } });
|
|
104
|
+
if (user) {
|
|
105
|
+
delete claims.profile;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return claims;
|
|
90
109
|
},
|
|
91
110
|
|
|
92
111
|
// eslint-disable-next-line consistent-return
|
|
@@ -114,11 +133,7 @@ module.exports = function createRoutes(node, authenticator, login) {
|
|
|
114
133
|
});
|
|
115
134
|
|
|
116
135
|
const config = (await req.getServiceConfig(NODE_SERVICES.AUTH)) || {};
|
|
117
|
-
const [invitedUserOnly, defaultRole, issuePassport] = await isInvitedUserOnly(
|
|
118
|
-
config.invitedUserOnly,
|
|
119
|
-
node,
|
|
120
|
-
teamDid
|
|
121
|
-
);
|
|
136
|
+
const [invitedUserOnly, defaultRole, issuePassport] = await isInvitedUserOnly(config, node, teamDid);
|
|
122
137
|
|
|
123
138
|
if (invitedUserOnly && !vc) {
|
|
124
139
|
throw new Error(messages.missingCredentialClaim[locale]);
|
|
@@ -180,18 +195,25 @@ module.exports = function createRoutes(node, authenticator, login) {
|
|
|
180
195
|
}
|
|
181
196
|
}
|
|
182
197
|
|
|
198
|
+
if (config.whoCanAccess === WHO_CAN_ACCESS.OWNER && role !== ROLES.OWNER) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
{
|
|
201
|
+
zh: '你不是该应用的所有者',
|
|
202
|
+
en: 'You are not the owner of this application',
|
|
203
|
+
}[locale]
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
183
207
|
// Recreate passport with correct role
|
|
184
208
|
passport = vc ? createUserPassport(vc, { role }) : null;
|
|
185
209
|
|
|
186
210
|
// Update profile
|
|
187
211
|
try {
|
|
188
|
-
const profile = claims.find((x) => x.type === 'profile');
|
|
189
212
|
if (user) {
|
|
190
213
|
// Update user
|
|
191
214
|
await node.updateUser({
|
|
192
215
|
teamDid,
|
|
193
216
|
user: {
|
|
194
|
-
...profile,
|
|
195
217
|
did: userDid,
|
|
196
218
|
pk: userPk,
|
|
197
219
|
locale,
|
|
@@ -201,6 +223,7 @@ module.exports = function createRoutes(node, authenticator, login) {
|
|
|
201
223
|
});
|
|
202
224
|
} else {
|
|
203
225
|
// Create user
|
|
226
|
+
const profile = claims.find((x) => x.type === 'profile');
|
|
204
227
|
await node.addUser({
|
|
205
228
|
teamDid,
|
|
206
229
|
user: {
|
|
@@ -217,8 +240,8 @@ module.exports = function createRoutes(node, authenticator, login) {
|
|
|
217
240
|
}
|
|
218
241
|
|
|
219
242
|
// Generate new session token that client can save to localStorage
|
|
220
|
-
const
|
|
221
|
-
await storage.update(token, { did: userDid,
|
|
243
|
+
const sessionToken = await createSessionToken(userDid, { passport, role });
|
|
244
|
+
await storage.update(token, { did: userDid, sessionToken });
|
|
222
245
|
logger.info('login.success', { userDid, role });
|
|
223
246
|
|
|
224
247
|
// issue passport for the first login user in a invite-only team
|