@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.
Files changed (106) hide show
  1. package/api/index.js +27 -15
  2. package/api/libs/auth.js +1 -1
  3. package/api/libs/jwt.js +4 -4
  4. package/api/middlewares/check-running.js +7 -11
  5. package/api/middlewares/proxy-to-daemon.js +42 -0
  6. package/api/routes/blocklet.js +101 -0
  7. package/api/routes/dns-resolver.js +39 -0
  8. package/api/routes/env.js +15 -5
  9. package/api/services/auth/connect/invite.js +4 -4
  10. package/api/services/auth/connect/login.js +68 -45
  11. package/api/services/auth/connect/setup.js +102 -0
  12. package/api/services/auth/index.js +23 -17
  13. package/api/services/notification/index.js +15 -19
  14. package/api/services/static.js +6 -26
  15. package/api/state/message.js +1 -1
  16. package/api/util/attach-shared-utils.js +21 -10
  17. package/api/util/get-dynamic-service-config.js +22 -0
  18. package/api/util/index.js +28 -2
  19. package/build/asset-manifest.json +34 -29
  20. package/build/index.html +1 -1
  21. package/build/precache-manifest.fc03dfbbf370fcf983cbef3f121b7584.js +186 -0
  22. package/build/service-worker.js +1 -1
  23. package/build/static/css/4.634341e0.chunk.css +2 -0
  24. package/build/static/css/4.634341e0.chunk.css.map +1 -0
  25. package/build/static/js/0.b6b3b23c.chunk.js +3 -0
  26. package/build/static/js/{3.38949d38.chunk.js.LICENSE.txt → 0.b6b3b23c.chunk.js.LICENSE.txt} +0 -9
  27. package/build/static/js/0.b6b3b23c.chunk.js.map +1 -0
  28. package/build/static/js/1.0e83f63d.chunk.js +2 -0
  29. package/build/static/js/1.0e83f63d.chunk.js.map +1 -0
  30. package/build/static/js/10.4199ec3c.chunk.js +2 -0
  31. package/build/static/js/10.4199ec3c.chunk.js.map +1 -0
  32. package/build/static/js/4.0046a465.chunk.js +3 -0
  33. package/build/static/js/{2.ca22e0d7.chunk.js.LICENSE.txt → 4.0046a465.chunk.js.LICENSE.txt} +9 -0
  34. package/build/static/js/4.0046a465.chunk.js.map +1 -0
  35. package/build/static/js/5.4304d38f.chunk.js +2 -0
  36. package/build/static/js/5.4304d38f.chunk.js.map +1 -0
  37. package/build/static/js/6.08043dea.chunk.js +2 -0
  38. package/build/static/js/6.08043dea.chunk.js.map +1 -0
  39. package/build/static/js/7.165c3091.chunk.js +2 -0
  40. package/build/static/js/7.165c3091.chunk.js.map +1 -0
  41. package/build/static/js/8.49993466.chunk.js +2 -0
  42. package/build/static/js/8.49993466.chunk.js.map +1 -0
  43. package/build/static/js/9.69df4f05.chunk.js +2 -0
  44. package/build/static/js/9.69df4f05.chunk.js.map +1 -0
  45. package/build/static/js/main.af31d0af.chunk.js +2 -0
  46. package/build/static/js/main.af31d0af.chunk.js.map +1 -0
  47. package/build/static/js/runtime-main.aa0aaf4a.js +2 -0
  48. package/build/static/js/runtime-main.aa0aaf4a.js.map +1 -0
  49. package/build/static/media/lato-all-400-normal.a82dcb33.woff +0 -0
  50. package/build/static/media/lato-all-700-normal.8190ee0e.woff +0 -0
  51. package/build/static/media/lato-latin-400-normal.e1b3b590.woff2 +0 -0
  52. package/build/static/media/lato-latin-700-normal.de69cf9e.woff2 +0 -0
  53. package/build/static/media/lato-latin-ext-400-normal.4bde07f9.woff2 +0 -0
  54. package/build/static/media/lato-latin-ext-700-normal.a48b0f04.woff2 +0 -0
  55. package/build/static/media/rubik-cyrillic-400-normal.49f3ce37.woff2 +0 -0
  56. package/build/static/media/rubik-cyrillic-500-normal.0c1bd2ab.woff2 +0 -0
  57. package/build/static/media/rubik-cyrillic-600-normal.b76031ea.woff2 +0 -0
  58. package/build/static/media/rubik-cyrillic-ext-400-normal.d3902986.woff2 +0 -0
  59. package/build/static/media/rubik-cyrillic-ext-500-normal.7e493ee0.woff2 +0 -0
  60. package/build/static/media/rubik-cyrillic-ext-600-normal.f4b6c519.woff2 +0 -0
  61. package/build/static/media/rubik-hebrew-400-normal.0ebef856.woff2 +0 -0
  62. package/build/static/media/rubik-hebrew-500-normal.3664f2b5.woff2 +0 -0
  63. package/build/static/media/rubik-hebrew-600-normal.6207349a.woff2 +0 -0
  64. package/build/static/media/rubik-latin-400-normal.b836d2ed.woff2 +0 -0
  65. package/build/static/media/rubik-latin-500-normal.18984747.woff2 +0 -0
  66. package/build/static/media/rubik-latin-600-normal.b1216018.woff2 +0 -0
  67. package/build/static/media/rubik-latin-ext-400-normal.69a88832.woff2 +0 -0
  68. package/build/static/media/rubik-latin-ext-500-normal.21b63491.woff2 +0 -0
  69. package/build/static/media/rubik-latin-ext-600-normal.d0d90e83.woff2 +0 -0
  70. package/package.json +28 -22
  71. package/build/precache-manifest.f8d516b676d205d1f6e2c4e869fbf297.js +0 -154
  72. package/build/static/css/2.d49e994f.chunk.css +0 -2
  73. package/build/static/css/2.d49e994f.chunk.css.map +0 -1
  74. package/build/static/js/2.ca22e0d7.chunk.js +0 -3
  75. package/build/static/js/2.ca22e0d7.chunk.js.map +0 -1
  76. package/build/static/js/3.38949d38.chunk.js +0 -3
  77. package/build/static/js/3.38949d38.chunk.js.map +0 -1
  78. package/build/static/js/4.97f8d423.chunk.js +0 -2
  79. package/build/static/js/4.97f8d423.chunk.js.map +0 -1
  80. package/build/static/js/5.8149df59.chunk.js +0 -2
  81. package/build/static/js/5.8149df59.chunk.js.map +0 -1
  82. package/build/static/js/6.001b8434.chunk.js +0 -2
  83. package/build/static/js/6.001b8434.chunk.js.map +0 -1
  84. package/build/static/js/7.f30a5254.chunk.js +0 -2
  85. package/build/static/js/7.f30a5254.chunk.js.map +0 -1
  86. package/build/static/js/8.e354cbf1.chunk.js +0 -2
  87. package/build/static/js/8.e354cbf1.chunk.js.map +0 -1
  88. package/build/static/js/main.561b39e2.chunk.js +0 -2
  89. package/build/static/js/main.561b39e2.chunk.js.map +0 -1
  90. package/build/static/js/runtime-main.8ddd5828.js +0 -2
  91. package/build/static/js/runtime-main.8ddd5828.js.map +0 -1
  92. package/build/static/media/rubik-cyrillic-400-normal.aa383bbd.woff2 +0 -0
  93. package/build/static/media/rubik-cyrillic-500-normal.27a1ebd4.woff2 +0 -0
  94. package/build/static/media/rubik-cyrillic-600-normal.74d5cdba.woff2 +0 -0
  95. package/build/static/media/rubik-cyrillic-ext-400-normal.b8647475.woff2 +0 -0
  96. package/build/static/media/rubik-cyrillic-ext-500-normal.860932d9.woff2 +0 -0
  97. package/build/static/media/rubik-cyrillic-ext-600-normal.942f240f.woff2 +0 -0
  98. package/build/static/media/rubik-hebrew-400-normal.2c9e3c2a.woff2 +0 -0
  99. package/build/static/media/rubik-hebrew-500-normal.08d6e502.woff2 +0 -0
  100. package/build/static/media/rubik-hebrew-600-normal.bfa32e44.woff2 +0 -0
  101. package/build/static/media/rubik-latin-400-normal.b8fd53c5.woff2 +0 -0
  102. package/build/static/media/rubik-latin-500-normal.595f1a98.woff2 +0 -0
  103. package/build/static/media/rubik-latin-600-normal.5f06934f.woff2 +0 -0
  104. package/build/static/media/rubik-latin-ext-400-normal.3c5c378e.woff2 +0 -0
  105. package/build/static/media/rubik-latin-ext-500-normal.5663c731.woff2 +0 -0
  106. 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
- loginTokenKey: 'login_token',
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.web(
206
- req,
207
- res,
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 || `Login to ${meta.name}`,
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 login = async (did, { role, passport }) =>
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 verify = (token, teamDid) =>
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
- login,
68
- verify,
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
- const baseUrl = getBaseUrl(req);
23
- const redirect = encodeURIComponent(
24
- `${baseUrl.replace(/\/+$/, '')}/${req.url.replace(/^\/+/, '').replace('__start__=1', '')}`
25
- );
26
- res.redirect(joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/start', `/?redirect=${redirect}`));
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;
@@ -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 { NODE_SERVICES } = require('@abtnode/constant');
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
- appId: "${blocklet.did}",
19
- appName: "${blocklet.name}",
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, login) {
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 loginToken = await login(userDid, { passport, role });
75
- await storage.update(token, { did: userDid, loginToken });
76
- logger.info('login.success', { userDid });
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 (invitedUserOnly, node, teamDid) => {
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 ([true, 'yes', 'not-first'].includes(invitedUserOnly)) {
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, login) {
63
+ module.exports = function createRoutes(node, authenticator, createSessionToken) {
55
64
  return {
56
65
  action: 'login',
57
- claims: {
58
- profile: async ({ extraParams, context }) => {
59
- const { locale } = extraParams;
60
-
61
- const config = await context.request.getServiceConfig(NODE_SERVICES.AUTH);
62
- const profileFields = get(config, 'profileFields');
63
-
64
- return {
65
- fields: profileFields || ['fullName', 'avatar'],
66
- description: messages.description[locale],
67
- };
68
- },
69
-
70
- verifiableCredential: async ({ context, extraParams: { locale } }) => {
71
- const { request, abtwallet } = context;
72
- const { wallet, did: teamDid } = await request.getBlockletInfo();
73
-
74
- checkWalletVersion({ abtwallet, locale });
75
-
76
- const blocklet = await request.getBlocklet();
77
- const trustedPassports = (blocklet.trustedPassports || []).map((x) => x.issuerDid);
78
- const trustedIssuers = [wallet.address, ...trustedPassports].filter(Boolean);
79
-
80
- const config = (await request.getServiceConfig(NODE_SERVICES.AUTH)) || {};
81
- const [invitedUserOnly] = await isInvitedUserOnly(config.invitedUserOnly, node, teamDid);
82
-
83
- return {
84
- description: messages.requestPassport[locale],
85
- item: vcTypes,
86
- trustedIssuers,
87
- optional: !invitedUserOnly,
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 loginToken = await login(userDid, { passport, role });
221
- await storage.update(token, { did: userDid, loginToken });
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