@abtnode/blocklet-services 1.6.26 → 1.6.29
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/README.md +1 -5
- package/{lib → api}/cache.js +0 -0
- package/api/index.js +296 -0
- package/{services/auth → api}/libs/auth.js +24 -8
- package/api/libs/env.js +7 -0
- package/{services/auth → api}/libs/jwt.js +1 -0
- package/api/middlewares/check-admin-permission.js +15 -0
- package/api/middlewares/check-running.js +46 -0
- package/api/routes/blocklet.js +98 -0
- package/api/routes/env.js +27 -0
- package/{services/auth/routes → api/services/auth/connect}/invite.js +5 -4
- package/{services/auth/routes → api/services/auth/connect}/issue-passport.js +6 -2
- package/{services/auth/routes → api/services/auth/connect}/login.js +18 -10
- package/api/services/auth/connect/lost-passport-issue.js +10 -0
- package/{services/auth/routes → api/services/auth/connect}/lost-passport-list.js +1 -1
- package/api/services/auth/index.js +189 -0
- package/api/services/auth/passport.js +22 -0
- package/api/services/auth/session.js +31 -0
- package/{services/auth/routes/notification.js → api/services/notification/index.js} +25 -9
- package/api/services/static.js +76 -0
- package/{services/auth → api}/state/index.js +0 -0
- package/{services/auth → api}/state/message.js +0 -0
- package/api/util/attach-shared-utils.js +149 -0
- package/api/util/constants.js +10 -0
- package/api/util/format-context.js +24 -0
- package/{services/auth → api}/util/index.js +31 -1
- package/build/asset-manifest.json +32 -30
- package/build/index.html +1 -1
- package/build/precache-manifest.f8d516b676d205d1f6e2c4e869fbf297.js +154 -0
- package/build/service-worker.js +2 -2
- package/build/static/css/2.d49e994f.chunk.css +2 -0
- package/build/static/css/{2.cc9dc85c.chunk.css.map → 2.d49e994f.chunk.css.map} +1 -1
- package/build/static/js/2.ca22e0d7.chunk.js +3 -0
- package/build/static/js/{2.90523f87.chunk.js.LICENSE.txt → 2.ca22e0d7.chunk.js.LICENSE.txt} +0 -0
- package/build/static/js/2.ca22e0d7.chunk.js.map +1 -0
- package/build/static/js/3.38949d38.chunk.js +3 -0
- package/build/static/js/{3.e6540184.chunk.js.LICENSE.txt → 3.38949d38.chunk.js.LICENSE.txt} +0 -0
- package/build/static/js/3.38949d38.chunk.js.map +1 -0
- package/build/static/js/4.97f8d423.chunk.js +2 -0
- package/build/static/js/4.97f8d423.chunk.js.map +1 -0
- package/build/static/js/5.8149df59.chunk.js +2 -0
- package/build/static/js/5.8149df59.chunk.js.map +1 -0
- package/build/static/js/6.001b8434.chunk.js +2 -0
- package/build/static/js/6.001b8434.chunk.js.map +1 -0
- package/build/static/js/7.f30a5254.chunk.js +2 -0
- package/build/static/js/7.f30a5254.chunk.js.map +1 -0
- package/build/static/js/8.e354cbf1.chunk.js +2 -0
- package/build/static/js/8.e354cbf1.chunk.js.map +1 -0
- package/build/static/js/main.561b39e2.chunk.js +2 -0
- package/build/static/js/main.561b39e2.chunk.js.map +1 -0
- package/build/static/js/runtime-main.8ddd5828.js +2 -0
- package/build/static/js/runtime-main.8ddd5828.js.map +1 -0
- package/{services/auth/meta.json → configs/auth.json} +2 -2
- package/package.json +27 -24
- package/build/precache-manifest.f3419eb785f255da7655fa425fc775a1.js +0 -150
- package/build/static/css/2.cc9dc85c.chunk.css +0 -2
- package/build/static/js/2.90523f87.chunk.js +0 -3
- package/build/static/js/2.90523f87.chunk.js.map +0 -1
- package/build/static/js/3.e6540184.chunk.js +0 -3
- package/build/static/js/3.e6540184.chunk.js.map +0 -1
- package/build/static/js/4.4410aa8a.chunk.js +0 -2
- package/build/static/js/4.4410aa8a.chunk.js.map +0 -1
- package/build/static/js/5.967448ae.chunk.js +0 -2
- package/build/static/js/5.967448ae.chunk.js.map +0 -1
- package/build/static/js/6.1de827ed.chunk.js +0 -2
- package/build/static/js/6.1de827ed.chunk.js.map +0 -1
- package/build/static/js/7.cb386b54.chunk.js +0 -2
- package/build/static/js/7.cb386b54.chunk.js.map +0 -1
- package/build/static/js/main.c51a8d5b.chunk.js +0 -2
- package/build/static/js/main.c51a8d5b.chunk.js.map +0 -1
- package/build/static/js/runtime-main.24dfe4a3.js +0 -2
- package/build/static/js/runtime-main.24dfe4a3.js.map +0 -1
- package/lib/index.js +0 -443
- package/lib/mount.js +0 -52
- package/lib/util.js +0 -17
- package/services/auth/index.js +0 -290
- package/services/auth/routes/blocklet-info.js +0 -33
- package/services/auth/routes/env.js +0 -33
- package/services/auth/routes/lost-passport-issue.js +0 -5
- package/services/auth/routes/passport.js +0 -18
- package/services/auth/routes/session.js +0 -27
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Aggregator to mount all blocklet services and make them work together.
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```shell
|
|
8
|
-
yarn add @abtnode/service
|
|
8
|
+
yarn add @abtnode/blocklet-service
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Then:
|
|
@@ -17,10 +17,6 @@ const ABTNode = require('@abtnode/core');
|
|
|
17
17
|
const node = ABTNode({ ...nodeOptions });
|
|
18
18
|
const server = createServer(node, { ...globalOptions });
|
|
19
19
|
|
|
20
|
-
server.mountService('@abtnode/auth-service', { ...loginServiceOptions });
|
|
21
|
-
server.mountService('@abtnode/acl-service', { ...aclServiceOptions });
|
|
22
|
-
server.mountService('@abtnode/payment-service', { ...paymentServiceOptions });
|
|
23
|
-
|
|
24
20
|
node.onReady(() => {
|
|
25
21
|
server.listen(5000, () => {
|
|
26
22
|
console.log('ABT Node Service server ready on port 5000');
|
package/{lib → api}/cache.js
RENAMED
|
File without changes
|
package/api/index.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
const http = require('http');
|
|
2
|
+
const get = require('lodash/get');
|
|
3
|
+
const morgan = require('morgan');
|
|
4
|
+
const express = require('express');
|
|
5
|
+
require('express-async-errors');
|
|
6
|
+
const cors = require('cors');
|
|
7
|
+
const compression = require('compression');
|
|
8
|
+
const cookieParser = require('cookie-parser');
|
|
9
|
+
const bodyParser = require('body-parser');
|
|
10
|
+
const RotatingFileStream = require('rotating-file-stream');
|
|
11
|
+
const httpProxy = require('http-proxy');
|
|
12
|
+
const moment = require('dayjs');
|
|
13
|
+
const minimatch = require('minimatch');
|
|
14
|
+
|
|
15
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES_PREFIX, EVENTS } = require('@abtnode/constant');
|
|
16
|
+
const { BlockletEvents } = require('@blocklet/meta/lib/constants');
|
|
17
|
+
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
18
|
+
const eventHub =
|
|
19
|
+
process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
|
|
20
|
+
const logger = require('@abtnode/logger')(require('../package.json').name);
|
|
21
|
+
|
|
22
|
+
const cache = require('./cache');
|
|
23
|
+
const { ensureProxyUrl } = require('./util');
|
|
24
|
+
const { isProduction, isE2E } = require('./libs/env');
|
|
25
|
+
const { init: initStates } = require('./state/index');
|
|
26
|
+
|
|
27
|
+
const { init: initNotification } = require('./services/notification');
|
|
28
|
+
const { init: initAuth } = require('./services/auth');
|
|
29
|
+
const StaticService = require('./services/static');
|
|
30
|
+
const createEnvRoutes = require('./routes/env');
|
|
31
|
+
const createBlockletRoutes = require('./routes/blocklet');
|
|
32
|
+
const checkRunning = require('./middlewares/check-running');
|
|
33
|
+
const attachSharedUtils = require('./util/attach-shared-utils');
|
|
34
|
+
|
|
35
|
+
const logFileGenerator = (time, index) => {
|
|
36
|
+
if (!time) {
|
|
37
|
+
return 'service.log';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let filename = `service-${moment(time).subtract(1, 'day').format('YYYY-MM-DD')}`; // prev date
|
|
41
|
+
|
|
42
|
+
if (index > 1) {
|
|
43
|
+
filename = `${filename}-${index}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return `${filename}.log.gz`;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
module.exports = function createServer(node, serverOptions = {}) {
|
|
50
|
+
const options = {
|
|
51
|
+
dataDir: node.dataDirs.services,
|
|
52
|
+
sessionSecret: process.env.ABT_NODE_SESSION_SECRET,
|
|
53
|
+
sessionTtl: process.env.ABT_NODE_SESSION_TTL,
|
|
54
|
+
webWalletUrl: 'https://web.abtwallet.io',
|
|
55
|
+
loginTokenKey: 'login_token',
|
|
56
|
+
...serverOptions,
|
|
57
|
+
isProduction,
|
|
58
|
+
isE2E,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (!options.dataDir) {
|
|
62
|
+
throw new Error('Blocklet services requires dataDir to start');
|
|
63
|
+
}
|
|
64
|
+
if (!options.sessionSecret) {
|
|
65
|
+
throw new Error('Blocklet services requires sessionSecret to start');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
logger.info('init blocklet service', { isProduction });
|
|
69
|
+
|
|
70
|
+
initStates(options.dataDir);
|
|
71
|
+
|
|
72
|
+
const { middlewares: authMiddlewares, routes: authRoutes } = initAuth({ node, options });
|
|
73
|
+
const notificationService = initNotification({ node, options });
|
|
74
|
+
|
|
75
|
+
// Proxy engine
|
|
76
|
+
const proxy = httpProxy.createProxyServer({});
|
|
77
|
+
// eslint-disable-next-line no-unused-vars
|
|
78
|
+
proxy.on('proxyReq', (proxyReq, req, res) => {
|
|
79
|
+
if (req.rawBody) {
|
|
80
|
+
// Since we already consumed request stream, we need to write req.rawBody to proxy request here
|
|
81
|
+
proxyReq.write(req.rawBody);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Cross process events
|
|
86
|
+
[BlockletEvents.updated, BlockletEvents.started, BlockletEvents.removed, BlockletEvents.statusChange].forEach(
|
|
87
|
+
(name) => {
|
|
88
|
+
eventHub.on(name, (data) => {
|
|
89
|
+
const did = get(data, 'meta.did');
|
|
90
|
+
if (did) {
|
|
91
|
+
logger.info('delete blocklet cache on update', { did, pid: process.pid });
|
|
92
|
+
cache.del(cache.keyFns.blocklet(did));
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
eventHub.on(EVENTS.NODE_UPDATED, () => {
|
|
98
|
+
logger.info('node update', { pid: process.pid });
|
|
99
|
+
cache.del(cache.keyFns.node());
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Http server
|
|
103
|
+
const server = express();
|
|
104
|
+
|
|
105
|
+
server.set('trust proxy', 'loopback');
|
|
106
|
+
server.disable('x-powered-by');
|
|
107
|
+
|
|
108
|
+
server.use(compression());
|
|
109
|
+
server.use(cookieParser());
|
|
110
|
+
|
|
111
|
+
server.use(
|
|
112
|
+
bodyParser.json({
|
|
113
|
+
// We have to set a larger hard limit since
|
|
114
|
+
limit: '2mb',
|
|
115
|
+
|
|
116
|
+
// https://flaviocopes.com/express-get-raw-body/
|
|
117
|
+
// Side effects: this will double the memory consumption
|
|
118
|
+
verify: (req, res, buf) => {
|
|
119
|
+
req.rawBody = buf;
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// NOTE: will be overwrite by Blocklet Server in production
|
|
125
|
+
server.use(cors());
|
|
126
|
+
|
|
127
|
+
// Shared util functions on current request
|
|
128
|
+
server.use((req, res, next) => {
|
|
129
|
+
attachSharedUtils({ node, req, options });
|
|
130
|
+
StaticService.attachUtils({ req, res, proxy });
|
|
131
|
+
next();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/* istanbul ignore if */
|
|
135
|
+
if (isProduction) {
|
|
136
|
+
const accessLogStream = RotatingFileStream.createStream(logFileGenerator, {
|
|
137
|
+
interval: '1d',
|
|
138
|
+
path: process.env.ABT_NODE_LOG_DIR,
|
|
139
|
+
compress: 'gzip',
|
|
140
|
+
});
|
|
141
|
+
server.use(morgan('combined', { stream: accessLogStream }));
|
|
142
|
+
/* istanbul ignore else */
|
|
143
|
+
} else {
|
|
144
|
+
server.use(
|
|
145
|
+
morgan((tokens, req, res) => {
|
|
146
|
+
const log = [
|
|
147
|
+
tokens.method(req, res),
|
|
148
|
+
tokens.url(req, res),
|
|
149
|
+
tokens.status(req, res),
|
|
150
|
+
tokens.res(req, res, 'content-length'),
|
|
151
|
+
'-',
|
|
152
|
+
tokens['response-time'](req, res),
|
|
153
|
+
'ms',
|
|
154
|
+
].join(' ');
|
|
155
|
+
|
|
156
|
+
return log;
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Static assets
|
|
162
|
+
StaticService.attachStaticResources({ app: server, proxy });
|
|
163
|
+
|
|
164
|
+
// API: notification
|
|
165
|
+
notificationService.sendToUser.attach(server);
|
|
166
|
+
|
|
167
|
+
// Middleware: auth info
|
|
168
|
+
server.use(authMiddlewares.bearerToken);
|
|
169
|
+
server.use(authMiddlewares.userInfo);
|
|
170
|
+
|
|
171
|
+
// API: auth
|
|
172
|
+
createEnvRoutes.init(server, node, options);
|
|
173
|
+
createBlockletRoutes.init(server, node, options);
|
|
174
|
+
authRoutes.attachDidAuthHandlers(server);
|
|
175
|
+
authRoutes.createPassportRoutes.init(server, node, options);
|
|
176
|
+
authRoutes.createSessionRoutes.init(server, node, options);
|
|
177
|
+
|
|
178
|
+
// Web Page
|
|
179
|
+
server.get(`${WELLKNOWN_SERVICE_PATH_PREFIX}/**`, (req, res) => {
|
|
180
|
+
res.sendWebPage();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Middleware: check running
|
|
184
|
+
// Request would not arrive here before blocklet is installed, because there is no config in router provider(nginx)
|
|
185
|
+
server.use(checkRunning);
|
|
186
|
+
|
|
187
|
+
// Middleware: check auth
|
|
188
|
+
server.use(authMiddlewares.checkAuth);
|
|
189
|
+
|
|
190
|
+
// Block invalid path in reserved prefix
|
|
191
|
+
server.use((req, res, next) => {
|
|
192
|
+
if ([NODE_SERVICES_PREFIX.AUTH_SERVICE, WELLKNOWN_SERVICE_PATH_PREFIX].some((x) => req.path.startsWith(x))) {
|
|
193
|
+
res.status(400).send('Bad request');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
next();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// After all service middleware, we can now safely pass all traffic to blocklets
|
|
201
|
+
server.use((req, res, next) => {
|
|
202
|
+
const { target } = ensureProxyUrl(req);
|
|
203
|
+
|
|
204
|
+
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
|
+
);
|
|
219
|
+
} else {
|
|
220
|
+
next();
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Following handlers exist just in case
|
|
225
|
+
|
|
226
|
+
// 404 handler
|
|
227
|
+
server.use((req, res) => {
|
|
228
|
+
res.status(404).send('Blocklet Service: You should not be here!');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// error handler
|
|
232
|
+
// eslint-disable-next-line no-unused-vars
|
|
233
|
+
server.use((err, req, res, next) => {
|
|
234
|
+
console.error('service error', { error: err });
|
|
235
|
+
res.status(500).send('Blocklet Service: Something broke!');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Web socket server
|
|
239
|
+
|
|
240
|
+
// Simple websocket router like http router
|
|
241
|
+
const httpServer = http.createServer(server);
|
|
242
|
+
server.listen = httpServer.listen.bind(httpServer);
|
|
243
|
+
const wsRoutingRules = [];
|
|
244
|
+
httpServer.on('upgrade', async (req, socket, head) => {
|
|
245
|
+
attachSharedUtils({ node, req, options });
|
|
246
|
+
|
|
247
|
+
// find matched handler registered by each service
|
|
248
|
+
const { pathname } = new URL(req.url, `http://${req.headers.host || 'unknown'}`);
|
|
249
|
+
const routes = wsRoutingRules.filter((x) => minimatch(normalizePathPrefix(pathname), normalizePathPrefix(x.path)));
|
|
250
|
+
|
|
251
|
+
let routeIndex = 0;
|
|
252
|
+
const next = () => {
|
|
253
|
+
const route = routes[routeIndex];
|
|
254
|
+
if (route) {
|
|
255
|
+
routeIndex++;
|
|
256
|
+
route.handle(req, socket, head, next);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
next();
|
|
261
|
+
});
|
|
262
|
+
const wsRouter = {
|
|
263
|
+
use(mountPoint, handler) {
|
|
264
|
+
wsRoutingRules.push({ path: mountPoint, handle: handler });
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// Notification
|
|
269
|
+
notificationService.attach(wsRouter);
|
|
270
|
+
|
|
271
|
+
// Only for Development
|
|
272
|
+
StaticService.attachDevProxy({ wsRouter, proxy });
|
|
273
|
+
|
|
274
|
+
// Auth
|
|
275
|
+
wsRouter.use('**', authMiddlewares.ensureWsAuth);
|
|
276
|
+
|
|
277
|
+
// Final: directly proxy all websocket request to target blocklet
|
|
278
|
+
wsRouter.use('**', async (req, socket, head) => {
|
|
279
|
+
const { target } = ensureProxyUrl(req);
|
|
280
|
+
if (target) {
|
|
281
|
+
proxy.ws(req, socket, head, { target }, (error) => {
|
|
282
|
+
if (error) {
|
|
283
|
+
logger.error('socket proxy error', { from: req.url, to: target, error });
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
} else {
|
|
287
|
+
logger.error('socket proxy error: cannot find target service');
|
|
288
|
+
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
289
|
+
socket.destroy();
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return server;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
module.exports.logFileGenerator = logFileGenerator;
|
|
@@ -5,6 +5,7 @@ const DiskStorage = require('@arcblock/did-auth-storage-nedb');
|
|
|
5
5
|
const { WalletAuthenticator } = require('@arcblock/did-auth');
|
|
6
6
|
const WalletHandlers = require('@blocklet/sdk/lib/wallet-handler');
|
|
7
7
|
const sendNotification = require('@blocklet/sdk/lib/util/send-notification');
|
|
8
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES_PREFIX } = require('@abtnode/constant');
|
|
8
9
|
|
|
9
10
|
const { getBlockletLogo } = require('../util');
|
|
10
11
|
|
|
@@ -25,14 +26,18 @@ module.exports = (node, opts) => {
|
|
|
25
26
|
]);
|
|
26
27
|
|
|
27
28
|
// logo
|
|
28
|
-
const logo = getBlockletLogo({
|
|
29
|
+
const logo = getBlockletLogo({
|
|
30
|
+
baseUrl: joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
|
|
31
|
+
blocklet,
|
|
32
|
+
nodeInfo: info,
|
|
33
|
+
});
|
|
29
34
|
|
|
30
35
|
return {
|
|
31
36
|
name: meta.name,
|
|
32
37
|
description: meta.description || `Login to ${meta.name}`,
|
|
33
38
|
icon: logo,
|
|
34
39
|
updateSubEndpoint: true,
|
|
35
|
-
subscriptionEndpoint: joinUrl(groupPathPrefix,
|
|
40
|
+
subscriptionEndpoint: joinUrl(groupPathPrefix, WELLKNOWN_SERVICE_PATH_PREFIX, 'websocket'),
|
|
36
41
|
nodeDid: info.did,
|
|
37
42
|
};
|
|
38
43
|
},
|
|
@@ -48,12 +53,8 @@ module.exports = (node, opts) => {
|
|
|
48
53
|
},
|
|
49
54
|
});
|
|
50
55
|
|
|
51
|
-
const
|
|
56
|
+
const handlerOpts = {
|
|
52
57
|
authenticator,
|
|
53
|
-
options: {
|
|
54
|
-
prefix: `${opts.prefix}/api/did`,
|
|
55
|
-
},
|
|
56
|
-
tokenGenerator: () => Date.now().toString(),
|
|
57
58
|
tokenStorage: new DiskStorage({
|
|
58
59
|
dbPath: path.join(opts.dataDir, 'auth.db'),
|
|
59
60
|
dbPort: process.env.NODE_ENV === 'test' ? null : Number(process.env.NEDB_MULTI_PORT),
|
|
@@ -67,10 +68,25 @@ module.exports = (node, opts) => {
|
|
|
67
68
|
};
|
|
68
69
|
return sendNotification(connectedDid, message, sender, process.env.ABT_NODE_SERVICE_PORT);
|
|
69
70
|
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// backward compatible
|
|
74
|
+
const oldHandler = new WalletHandlers({
|
|
75
|
+
...handlerOpts,
|
|
76
|
+
options: {
|
|
77
|
+
prefix: `${NODE_SERVICES_PREFIX.AUTH_SERVICE}/api/did`,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const handler = new WalletHandlers({
|
|
82
|
+
...handlerOpts,
|
|
83
|
+
options: {
|
|
84
|
+
prefix: `${WELLKNOWN_SERVICE_PATH_PREFIX}/api/did`,
|
|
85
|
+
},
|
|
70
86
|
});
|
|
71
87
|
|
|
72
88
|
return {
|
|
73
89
|
authenticator,
|
|
74
|
-
handlers,
|
|
90
|
+
handlers: [handler, oldHandler],
|
|
75
91
|
};
|
|
76
92
|
};
|
package/api/libs/env.js
ADDED
|
@@ -3,6 +3,7 @@ const jwt = require('jsonwebtoken');
|
|
|
3
3
|
const { createAuthToken } = require('@abtnode/auth/lib/auth');
|
|
4
4
|
const { isUserPassportRevoked } = require('@abtnode/auth/lib/passport');
|
|
5
5
|
|
|
6
|
+
// FIXME: we need to test performance for this code
|
|
6
7
|
const getUser = async (node, teamDid, userDid) => {
|
|
7
8
|
const user = await node.getUser({ teamDid, user: { did: userDid } });
|
|
8
9
|
if (user && user.approved) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { ROLES } = require('@abtnode/constant');
|
|
2
|
+
|
|
3
|
+
module.exports = async (req, res, next) => {
|
|
4
|
+
if (!req.user) {
|
|
5
|
+
res.status(401).json({ code: 'forbidden', error: 'not authorized' });
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (![ROLES.OWNER, ROLES.ADMIN].includes(req.user.role)) {
|
|
10
|
+
res.status(403).json({ code: 'forbidden', error: 'no permission' });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
next();
|
|
15
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const joinUrl = require('url-join');
|
|
2
|
+
|
|
3
|
+
const { BlockletStatus } = require('@blocklet/meta/lib/constants');
|
|
4
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
5
|
+
const getBlockletNotRunningTemplate = require('@abtnode/router-templates/lib/blocklet-not-running');
|
|
6
|
+
const getBlockletMaintenanceTemplate = require('@abtnode/router-templates/lib/blocklet-maintenance');
|
|
7
|
+
const { getBaseUrl } = require('@abtnode/router-adapter');
|
|
8
|
+
|
|
9
|
+
const { shouldGotoStartPage } = require('../util');
|
|
10
|
+
|
|
11
|
+
const checkRunning = async (req, res, next) => {
|
|
12
|
+
const blocklet = await req.getBlocklet();
|
|
13
|
+
if (
|
|
14
|
+
![
|
|
15
|
+
BlockletStatus.running,
|
|
16
|
+
// Waiting, Downloading should be allowed because blocklet is currently being upgrading
|
|
17
|
+
BlockletStatus.waiting,
|
|
18
|
+
BlockletStatus.downloading,
|
|
19
|
+
].includes(blocklet.status)
|
|
20
|
+
) {
|
|
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}`));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const nodeInfo = await req.getNodeInfo();
|
|
31
|
+
res.status(500);
|
|
32
|
+
// return json if json has high priority, e.g. "*/*,application/json"
|
|
33
|
+
if (req.accepts(['html', 'json']) === 'json') {
|
|
34
|
+
res.json({ code: 'error', error: 'blocklet is not running' });
|
|
35
|
+
} else if (blocklet.status === BlockletStatus.starting) {
|
|
36
|
+
res.send(getBlockletMaintenanceTemplate(blocklet, nodeInfo));
|
|
37
|
+
} else {
|
|
38
|
+
res.send(getBlockletNotRunningTemplate(blocklet, nodeInfo));
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
next();
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
module.exports = checkRunning;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const get = require('lodash/get');
|
|
5
|
+
const cloneDeep = require('lodash/cloneDeep');
|
|
6
|
+
|
|
7
|
+
const { fixBlockletStatus, wipeSensitiveData } = require('@blocklet/meta/lib/util');
|
|
8
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
9
|
+
const logger = require('@abtnode/logger')(require('../../package.json').name);
|
|
10
|
+
|
|
11
|
+
const checkAdminPermission = require('../middlewares/check-admin-permission');
|
|
12
|
+
|
|
13
|
+
const polishBlocklet = (doc) => {
|
|
14
|
+
const res = cloneDeep(doc);
|
|
15
|
+
fixBlockletStatus(res);
|
|
16
|
+
return wipeSensitiveData(res);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const prefix = WELLKNOWN_SERVICE_PATH_PREFIX;
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
init(server, node) {
|
|
23
|
+
server.get(`${prefix}/blocklet/logo`, async (req, res) => {
|
|
24
|
+
const sendOptions = { maxAge: '1d' };
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const blocklet = await req.getBlocklet();
|
|
28
|
+
|
|
29
|
+
if (blocklet && get(blocklet, 'env.appDir') && blocklet.meta.logo) {
|
|
30
|
+
const logoFile = path.join(get(blocklet, 'env.appDir'), blocklet.meta.logo);
|
|
31
|
+
|
|
32
|
+
if (fs.existsSync(logoFile)) {
|
|
33
|
+
res.sendFile(logoFile, sendOptions);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
res.sendStaticFile('/images/blocklet.png', sendOptions);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
logger.error('failed to send blocklet logo', { did: req.params.did, error: err });
|
|
41
|
+
res.sendStaticFile('/images/blocklet.png', sendOptions);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
server.get(`${prefix}/api/blocklet/detail`, checkAdminPermission, async (req, res) => {
|
|
46
|
+
const blocklet = await req.getBlocklet();
|
|
47
|
+
|
|
48
|
+
res.json(polishBlocklet(blocklet));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
server.post(`${prefix}/api/blocklet/config`, checkAdminPermission, async (req, res) => {
|
|
52
|
+
const blocklet = await req.getBlocklet();
|
|
53
|
+
const { configs, childDid } = req.body;
|
|
54
|
+
const doc = await node.configBlocklet({
|
|
55
|
+
did: blocklet.meta.did,
|
|
56
|
+
childDid,
|
|
57
|
+
configs,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
res.json(polishBlocklet(doc));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
server.post(`${prefix}/api/blocklet/start`, checkAdminPermission, async (req, res) => {
|
|
64
|
+
const blocklet = await req.getBlocklet();
|
|
65
|
+
const doc = await node.startBlocklet({
|
|
66
|
+
did: blocklet.meta.did,
|
|
67
|
+
checkHealthImmediately: true,
|
|
68
|
+
throwOnError: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
res.json(polishBlocklet(doc));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// backward compatible
|
|
75
|
+
server.get(`${prefix}/blocklet/logo/:did`, async (req, res) => {
|
|
76
|
+
const sendOptions = { maxAge: '1d' };
|
|
77
|
+
|
|
78
|
+
let blocklet = null;
|
|
79
|
+
try {
|
|
80
|
+
blocklet = await node.ensureBlockletIntegrity(req.params.did);
|
|
81
|
+
|
|
82
|
+
if (blocklet && get(blocklet, 'env.appDir') && blocklet.meta.logo) {
|
|
83
|
+
const logoFile = path.join(get(blocklet, 'env.appDir'), blocklet.meta.logo);
|
|
84
|
+
|
|
85
|
+
if (fs.existsSync(logoFile)) {
|
|
86
|
+
res.sendFile(logoFile, sendOptions);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
res.sendStaticFile('/images/blocklet.png', sendOptions);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
logger.error('failed to send blocklet logo', { did: req.params.did, error: err });
|
|
94
|
+
res.sendStaticFile('/images/blocklet.png', sendOptions);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { NODE_SERVICES } = require('@abtnode/constant');
|
|
2
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
init(server, node, opts) {
|
|
6
|
+
server.get(`**${WELLKNOWN_SERVICE_PATH_PREFIX}/api/env`, async (req, res) => {
|
|
7
|
+
res.type('js');
|
|
8
|
+
|
|
9
|
+
const [blocklet, config, info] = await Promise.all([
|
|
10
|
+
req.getBlockletInfo(),
|
|
11
|
+
req.getServiceConfig(NODE_SERVICES.AUTH),
|
|
12
|
+
req.getNodeInfo(),
|
|
13
|
+
]);
|
|
14
|
+
const pathPrefix = req.headers['x-path-prefix'] || '/';
|
|
15
|
+
const groupPathPrefix = req.headers['x-group-path-prefix'];
|
|
16
|
+
|
|
17
|
+
res.send(`window.env = {
|
|
18
|
+
appId: "${blocklet.did}",
|
|
19
|
+
appName: "${blocklet.name}",
|
|
20
|
+
pathPrefix: "${pathPrefix}",
|
|
21
|
+
apiPrefix: "${pathPrefix.replace(/\/+$/, '')}${WELLKNOWN_SERVICE_PATH_PREFIX}",
|
|
22
|
+
${groupPathPrefix ? `groupPathPrefix: "${groupPathPrefix}",` : ''}
|
|
23
|
+
webWalletUrl: "${info.webWalletUrl || config.webWalletUrl || opts.webWalletUrl}",
|
|
24
|
+
}`);
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -7,9 +7,10 @@ const {
|
|
|
7
7
|
checkWalletVersion,
|
|
8
8
|
beforeInvitationRequest,
|
|
9
9
|
} = require('@abtnode/auth/lib/auth');
|
|
10
|
-
const
|
|
10
|
+
const { NODE_SERVICES, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
11
|
+
const logger = require('@abtnode/logger')(require('../../../../package.json').name);
|
|
11
12
|
|
|
12
|
-
module.exports = function createRoutes(node, authenticator, login
|
|
13
|
+
module.exports = function createRoutes(node, authenticator, login) {
|
|
13
14
|
return {
|
|
14
15
|
action: 'invite',
|
|
15
16
|
|
|
@@ -24,7 +25,7 @@ module.exports = function createRoutes(node, authenticator, login, opts) {
|
|
|
24
25
|
profile: async ({ extraParams, context }) => {
|
|
25
26
|
const { locale } = extraParams;
|
|
26
27
|
|
|
27
|
-
const config = await context.request.getServiceConfig();
|
|
28
|
+
const config = await context.request.getServiceConfig(NODE_SERVICES.AUTH);
|
|
28
29
|
const profileFields = get(config, 'profileFields');
|
|
29
30
|
|
|
30
31
|
return {
|
|
@@ -53,7 +54,7 @@ module.exports = function createRoutes(node, authenticator, login, opts) {
|
|
|
53
54
|
const { locale, inviteId } = extraParams;
|
|
54
55
|
const nodeInfo = await req.getNodeInfo();
|
|
55
56
|
const teamDid = req.headers['x-blocklet-did'];
|
|
56
|
-
const statusEndpointBaseUrl = joinUrl(baseUrl,
|
|
57
|
+
const statusEndpointBaseUrl = joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX);
|
|
57
58
|
const endpoint = baseUrl;
|
|
58
59
|
|
|
59
60
|
const { passport, response, role } = await handleInvitationResponse({
|
|
@@ -6,7 +6,11 @@ const {
|
|
|
6
6
|
beforeIssuePassportRequest,
|
|
7
7
|
} = require('@abtnode/auth/lib/auth');
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
10
|
+
|
|
11
|
+
// todo: move to connect dir
|
|
12
|
+
|
|
13
|
+
module.exports = function createRoutes(node) {
|
|
10
14
|
return {
|
|
11
15
|
action: 'issue-passport',
|
|
12
16
|
|
|
@@ -38,7 +42,7 @@ module.exports = function createRoutes(node, authenticator, login, opts) {
|
|
|
38
42
|
const { locale, id } = extraParams;
|
|
39
43
|
const nodeInfo = await node.getNodeInfo();
|
|
40
44
|
const teamDid = req.headers['x-blocklet-did'];
|
|
41
|
-
const statusEndpointBaseUrl = joinUrl(baseUrl,
|
|
45
|
+
const statusEndpointBaseUrl = joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX);
|
|
42
46
|
const endpoint = baseUrl;
|
|
43
47
|
|
|
44
48
|
return handleIssuePassportResponse({
|