@abtnode/webapp 1.8.63-beta-a36b5e1a → 1.8.63
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/blocklet.yml +6 -3
- package/build/asset-manifest.json +93 -0
- package/build/favicon.ico +0 -0
- package/build/icons/css/all.css +5919 -0
- package/build/icons/css/brands.css +14 -0
- package/build/icons/css/brands.min.css +12 -0
- package/build/icons/css/fontawesome.css +7816 -0
- package/build/icons/css/fontawesome.min.css +5864 -0
- package/build/icons/css/light.css +15 -0
- package/build/icons/css/light.min.css +13 -0
- package/build/icons/webfonts/fa-brands-400.eot +0 -0
- package/build/icons/webfonts/fa-brands-400.svg +1256 -0
- package/build/icons/webfonts/fa-brands-400.ttf +0 -0
- package/build/icons/webfonts/fa-brands-400.woff +0 -0
- package/build/icons/webfonts/fa-brands-400.woff2 +0 -0
- package/build/icons/webfonts/fa-light-300.eot +0 -0
- package/build/icons/webfonts/fa-light-300.svg +4445 -0
- package/build/icons/webfonts/fa-light-300.ttf +0 -0
- package/build/icons/webfonts/fa-light-300.woff +0 -0
- package/build/icons/webfonts/fa-light-300.woff2 +0 -0
- package/build/images/blocklet.png +0 -0
- package/build/images/blocklets-00c2c4.png +0 -0
- package/build/images/blocklets-222222.png +0 -0
- package/build/images/celebrate.png +0 -0
- package/build/images/community.svg +6 -0
- package/build/images/console-00c2c4.png +0 -0
- package/build/images/console-222222.png +0 -0
- package/build/images/dashboard-00c2c4.png +0 -0
- package/build/images/dashboard-222222.png +0 -0
- package/build/images/doc-222222.png +0 -0
- package/build/images/hourglass-222222.png +0 -0
- package/build/images/https-certificate-icon.png +0 -0
- package/build/images/labs-00c2c4.png +0 -0
- package/build/images/labs-222222.png +0 -0
- package/build/images/log-00c2c4.png +0 -0
- package/build/images/log-222222.png +0 -0
- package/build/images/member-222222.png +0 -0
- package/build/images/node.png +0 -0
- package/build/images/official.svg +1 -0
- package/build/images/private-222222.png +0 -0
- package/build/images/public-222222.png +0 -0
- package/build/images/public_avatar.png +0 -0
- package/build/images/router-00c2c4.png +0 -0
- package/build/images/router-222222.png +0 -0
- package/build/images/settings-00c2c4.png +0 -0
- package/build/images/settings-222222.png +0 -0
- package/build/images/slack.png +0 -0
- package/build/images/storage-222222.png +0 -0
- package/build/images/store-00c2c4.png +0 -0
- package/build/images/store-222222.png +0 -0
- package/build/images/team-00c2c4.png +0 -0
- package/build/images/team-222222.png +0 -0
- package/build/images/time-222222.png +0 -0
- package/build/images/upgrade.png +0 -0
- package/build/index.html +1 -0
- package/build/manifest.json +15 -0
- package/build/static/css/4381.5ade355d.chunk.css +1 -0
- package/build/static/css/4691.56c2f951.chunk.css +1 -0
- package/build/static/css/5245.5d6e9197.chunk.css +1 -0
- package/build/static/css/8213.f696fdbf.chunk.css +1 -0
- package/build/static/css/9779.f696fdbf.chunk.css +1 -0
- package/build/static/css/main.c9e90622.css +1 -0
- package/build/static/images/logo.png +0 -0
- package/build/static/js/1057.8280393f.chunk.js +1 -0
- package/build/static/js/1361.85e46159.chunk.js +1 -0
- package/build/static/js/1366.2f174c9b.chunk.js +2 -0
- package/build/static/js/1366.2f174c9b.chunk.js.LICENSE.txt +37 -0
- package/build/static/js/154.94956824.chunk.js +1 -0
- package/build/static/js/1826.07c285de.chunk.js +1 -0
- package/build/static/js/2129.120e36ae.chunk.js +1 -0
- package/build/static/js/2308.b65bc299.chunk.js +1 -0
- package/build/static/js/2564.eef717f3.chunk.js +1 -0
- package/build/static/js/2657.d3c62d57.chunk.js +1 -0
- package/build/static/js/2720.26eb1788.chunk.js +1 -0
- package/build/static/js/2828.4f318970.chunk.js +1 -0
- package/build/static/js/2878.5626558d.chunk.js +1 -0
- package/build/static/js/3111.84e02823.chunk.js +1 -0
- package/build/static/js/3476.c0b3411b.chunk.js +1 -0
- package/build/static/js/3633.8c7cf02d.chunk.js +1 -0
- package/build/static/js/3732.f5d8b39b.chunk.js +1 -0
- package/build/static/js/3776.1490b0f7.chunk.js +1 -0
- package/build/static/js/3829.b6d6cf45.chunk.js +1 -0
- package/build/static/js/4074.19db40ec.chunk.js +1 -0
- package/build/static/js/4104.b47ae0e1.chunk.js +1 -0
- package/build/static/js/4241.83e4f741.chunk.js +1 -0
- package/build/static/js/4274.e18ffddd.chunk.js +1 -0
- package/build/static/js/4349.7c2f6507.chunk.js +1 -0
- package/build/static/js/4381.631ef61d.chunk.js +2 -0
- package/build/static/js/4381.631ef61d.chunk.js.LICENSE.txt +22 -0
- package/build/static/js/4557.3907550e.chunk.js +1 -0
- package/build/static/js/4590.5a14012c.chunk.js +1 -0
- package/build/static/js/4633.9f946ed5.chunk.js +1 -0
- package/build/static/js/4691.550fcb40.chunk.js +2 -0
- package/build/static/js/4691.550fcb40.chunk.js.LICENSE.txt +8 -0
- package/build/static/js/5074.9ea094a9.chunk.js +1 -0
- package/build/static/js/5082.18d16cb9.chunk.js +1 -0
- package/build/static/js/519.adae6e43.chunk.js +1 -0
- package/build/static/js/556.381dcbd8.chunk.js +1 -0
- package/build/static/js/5984.25af6288.chunk.js +1 -0
- package/build/static/js/6158.8a43296d.chunk.js +1 -0
- package/build/static/js/6189.d6d74d14.chunk.js +1 -0
- package/build/static/js/6239.5a873a03.chunk.js +2 -0
- package/build/static/js/6239.5a873a03.chunk.js.LICENSE.txt +14 -0
- package/build/static/js/629.1a7f2553.chunk.js +1 -0
- package/build/static/js/6413.ab4a8e01.chunk.js +1 -0
- package/build/static/js/6602.c417a217.chunk.js +1 -0
- package/build/static/js/6780.64314507.chunk.js +1 -0
- package/build/static/js/6830.88b993a0.chunk.js +1 -0
- package/build/static/js/6866.2131ea60.chunk.js +2 -0
- package/build/static/js/6866.2131ea60.chunk.js.LICENSE.txt +8 -0
- package/build/static/js/6898.6fe0c32d.chunk.js +2 -0
- package/build/static/js/6898.6fe0c32d.chunk.js.LICENSE.txt +5 -0
- package/build/static/js/7016.835958ef.chunk.js +1 -0
- package/build/static/js/7298.a7286c09.chunk.js +1 -0
- package/build/static/js/7327.56a5e016.chunk.js +1 -0
- package/build/static/js/744.8b656dd2.chunk.js +1 -0
- package/build/static/js/7652.722b9180.chunk.js +1 -0
- package/build/static/js/8034.c4dcdeff.chunk.js +1 -0
- package/build/static/js/8058.0b2d4727.chunk.js +1 -0
- package/build/static/js/8213.09f25083.chunk.js +1 -0
- package/build/static/js/8235.add24931.chunk.js +1 -0
- package/build/static/js/8249.63c55b3a.chunk.js +1 -0
- package/build/static/js/8262.869cc799.chunk.js +1 -0
- package/build/static/js/8352.b57b0e44.chunk.js +1 -0
- package/build/static/js/8464.b4b82828.chunk.js +1 -0
- package/build/static/js/8606.66db67e2.chunk.js +1 -0
- package/build/static/js/9038.2908c176.chunk.js +1 -0
- package/build/static/js/9076.c77366bc.chunk.js +1 -0
- package/build/static/js/9301.a65db324.chunk.js +2 -0
- package/build/static/js/9301.a65db324.chunk.js.LICENSE.txt +8 -0
- package/build/static/js/9575.36f3de5b.chunk.js +2 -0
- package/build/static/js/9575.36f3de5b.chunk.js.LICENSE.txt +194 -0
- package/build/static/js/9779.bbf0dd81.chunk.js +1 -0
- package/build/static/js/main.cb5d8d67.js +2 -0
- package/build/static/js/main.cb5d8d67.js.LICENSE.txt +159 -0
- package/build/static/media/iconify.32b54549e843e448ee9b.cjs +2 -0
- package/build/static/media/iconify.32b54549e843e448ee9b.cjs.LICENSE.txt +13 -0
- package/build/static/media/lato-all-400-normal.3dc1eff492ab1f598560.woff +0 -0
- package/build/static/media/lato-all-700-normal.1e7707c9ec98d9b97e7f.woff +0 -0
- package/build/static/media/lato-latin-400-normal.be36596da218e1eec01c.woff2 +0 -0
- package/build/static/media/lato-latin-700-normal.8f28e0e1fdb195149f1c.woff2 +0 -0
- package/build/static/media/lato-latin-ext-400-normal.361f3dbb9db6a5980326.woff2 +0 -0
- package/build/static/media/lato-latin-ext-700-normal.9c8812eaec45956201e1.woff2 +0 -0
- package/build/static/media/logo.60f66bbe1ce9674a4df4e374c9d97fc4.svg +16 -0
- package/build/static/media/ubuntu-mono-all-400-normal.c879328bc62e9c68268f.woff +0 -0
- package/build/static/media/ubuntu-mono-cyrillic-400-normal.c367f416832eb8f1b846.woff2 +0 -0
- package/build/static/media/ubuntu-mono-cyrillic-ext-400-normal.eda1c4946b1f7bf58386.woff2 +0 -0
- package/build/static/media/ubuntu-mono-greek-400-normal.22f3bfc91f79c342bdf4.woff2 +0 -0
- package/build/static/media/ubuntu-mono-greek-ext-400-normal.b3459900ea8a25d1f7c2.woff2 +0 -0
- package/build/static/media/ubuntu-mono-latin-400-normal.18e32d9d743af28f913e.woff2 +0 -0
- package/build/static/media/ubuntu-mono-latin-ext-400-normal.b56e2315611d10838ad5.woff2 +0 -0
- package/package.json +1 -1
- package/api/gql/config.js +0 -392
- package/api/gql/index.js +0 -102
- package/api/gql/middlewares/create-audit-log.js +0 -3
- package/api/gql/middlewares/get-blocklet-list.js +0 -14
- package/api/gql/middlewares/install-blocklet.js +0 -13
- package/api/gql/middlewares/verify-blocklet.js +0 -21
- package/api/gql/middlewares/verify-team.js +0 -9
- package/api/index.js +0 -249
- package/api/libs/auth.js +0 -78
- package/api/libs/env.js +0 -3
- package/api/libs/login.js +0 -212
- package/api/libs/security.js +0 -90
- package/api/libs/storage.js +0 -69
- package/api/middlewares/mutate-blocklet-permission.js +0 -18
- package/api/routes/auth/accept-server.js +0 -28
- package/api/routes/auth/connect-owner.js +0 -143
- package/api/routes/auth/delegate-transfer-owner-nft.js +0 -86
- package/api/routes/auth/invite.js +0 -93
- package/api/routes/auth/issue-passport.js +0 -61
- package/api/routes/auth/launch-free-blocklet-by-nft.js +0 -9
- package/api/routes/auth/launch-free-blocklet.js +0 -9
- package/api/routes/auth/launch-paid-blocklet-by-nft.js +0 -9
- package/api/routes/auth/launch-paid-blocklet.js +0 -9
- package/api/routes/auth/login.js +0 -63
- package/api/routes/auth/lost-passport-issue.js +0 -5
- package/api/routes/auth/lost-passport-list.js +0 -5
- package/api/routes/auth/switch-passport.js +0 -47
- package/api/routes/auth/switch-profile.js +0 -69
- package/api/routes/auth/util.js +0 -135
- package/api/routes/auth/verify-owner.js +0 -39
- package/api/routes/auth/verify-purchase.js +0 -185
- package/api/routes/blocklet-info.js +0 -246
- package/api/routes/blocklet-preference.js +0 -161
- package/api/routes/blocklet-proxy.js +0 -220
- package/api/routes/did-resolver.js +0 -38
- package/api/routes/dns-resolver.js +0 -73
- package/api/routes/log.js +0 -31
- package/api/routes/passport.js +0 -19
- package/api/routes/session.js +0 -61
- package/api/routes/user.js +0 -38
- package/api/util/find-routing-rule.js +0 -95
- package/api/util/get-configs.js +0 -31
- package/api/util/index.js +0 -5
- package/api/util/navigation.js +0 -84
- package/api/webpack.blocklet.js +0 -43
- package/api/ws/index.js +0 -146
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const qs = require('querystring');
|
|
3
|
-
const get = require('lodash/get');
|
|
4
|
-
const joinUrl = require('url-join');
|
|
5
|
-
const express = require('express');
|
|
6
|
-
const multer = require('multer');
|
|
7
|
-
const serveStatic = require('serve-static');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const { BLOCKLET_PREFERENCE_FILE, BLOCKLET_PREFERENCE_PREFIX, BLOCKLET_UPLOADS_DIR } = require('@blocklet/constant');
|
|
10
|
-
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
11
|
-
|
|
12
|
-
const mutateBlockletPermission = require('../middlewares/mutate-blocklet-permission');
|
|
13
|
-
const DiskStorage = require('../libs/storage');
|
|
14
|
-
|
|
15
|
-
// For reading and updating blocklet preferences
|
|
16
|
-
module.exports = {
|
|
17
|
-
init(app, node) {
|
|
18
|
-
// find a component from the dependency tree
|
|
19
|
-
const findComponent = (parent, id) => {
|
|
20
|
-
if (parent.env.id === id) {
|
|
21
|
-
return parent;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const possible = parent.children.find(x => id.startsWith(x.env.id));
|
|
25
|
-
return possible ? findComponent(possible, id) : null;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const mergeConfigToSchema = (properties, configs, uploadAction) => {
|
|
29
|
-
Object.keys(properties).forEach(key => {
|
|
30
|
-
const prop = properties[key];
|
|
31
|
-
if (prop.properties) {
|
|
32
|
-
mergeConfigToSchema(prop.properties, configs, uploadAction);
|
|
33
|
-
}
|
|
34
|
-
if (prop['x-decorator'] === 'FormItem') {
|
|
35
|
-
const prefixedKey = `${BLOCKLET_PREFERENCE_PREFIX}${key}`;
|
|
36
|
-
if (typeof configs[prefixedKey] !== 'undefined') {
|
|
37
|
-
prop.default = configs[prefixedKey];
|
|
38
|
-
}
|
|
39
|
-
if (prop['x-component'].startsWith('Upload') && !prop['x-component-props'].action) {
|
|
40
|
-
prop['x-component-props'].action = uploadAction;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const ensurePermission = mutateBlockletPermission(node);
|
|
47
|
-
|
|
48
|
-
const ensureBlocklet = async (req, res, next) => {
|
|
49
|
-
if (!req.query.id) {
|
|
50
|
-
return res.status(400).json({ code: 'bad_request', error: 'no blocklet did' });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const [did] = req.query.id.split('/');
|
|
54
|
-
req.blocklet = await node.getBlocklet({ did, attachRuntimeInfo: false });
|
|
55
|
-
if (!req.blocklet) {
|
|
56
|
-
return res.status(400).json({ code: 'bad_request', error: 'blocklet not found' });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return next();
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const ensureComponent = (req, res, next) => {
|
|
63
|
-
req.component = findComponent(req.blocklet, req.query.id);
|
|
64
|
-
if (!req.component) {
|
|
65
|
-
return res.status(400).json({ code: 'bad_request', error: 'component not found' });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return next();
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const uploadApi = '/api/uploads';
|
|
72
|
-
|
|
73
|
-
// load preferences schema
|
|
74
|
-
app.get('/api/preferences', ensurePermission, ensureBlocklet, ensureComponent, async (req, res) => {
|
|
75
|
-
const schemaFile = path.join(req.component.env.appDir, BLOCKLET_PREFERENCE_FILE);
|
|
76
|
-
if (fs.existsSync(schemaFile)) {
|
|
77
|
-
try {
|
|
78
|
-
const info = await node.getNodeInfo();
|
|
79
|
-
const isFromService = req.get('source') === 'blocklet-service';
|
|
80
|
-
const uploadAction = joinUrl(
|
|
81
|
-
// eslint-disable-next-line no-nested-ternary
|
|
82
|
-
isFromService
|
|
83
|
-
? joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, uploadApi)
|
|
84
|
-
: process.env.NODE_ENV === 'production'
|
|
85
|
-
? joinUrl(info.routing.adminPath, uploadApi)
|
|
86
|
-
: uploadApi,
|
|
87
|
-
`?${qs.stringify(req.query)}`
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const schema = JSON.parse(fs.readFileSync(schemaFile, 'utf8'));
|
|
91
|
-
mergeConfigToSchema(get(schema, 'schema.properties', {}), req.component.configObj, uploadAction);
|
|
92
|
-
return res.json(schema);
|
|
93
|
-
} catch (err) {
|
|
94
|
-
console.error(err);
|
|
95
|
-
return res.json({});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return res.json({});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// save preferences data
|
|
103
|
-
app.post('/api/preferences', ensurePermission, ensureBlocklet, ensureComponent, async (req, res) => {
|
|
104
|
-
const configs = Object.keys(req.body).reduce((acc, key) => {
|
|
105
|
-
const value = req.body[key];
|
|
106
|
-
acc.push({ key: `${BLOCKLET_PREFERENCE_PREFIX}${key}`, value });
|
|
107
|
-
return acc;
|
|
108
|
-
}, []);
|
|
109
|
-
await node.configBlocklet({ did: req.query.id.split('/'), configs }, { user: req.user });
|
|
110
|
-
res.json(req.body);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// download dataDir
|
|
114
|
-
// FIXME: we should limit this feature when dataDir is too large
|
|
115
|
-
app.get('/api/export', ensurePermission, ensureBlocklet, async (req, res) => {
|
|
116
|
-
const timestamp = new Date().toISOString().split('.')[0].split(':').join('-').replaceAll('-', '');
|
|
117
|
-
const fileName = `blocklet-${req.blocklet.env.appId}-${timestamp}.zip`;
|
|
118
|
-
const filePath = await node.createBlockletDataArchive(req.blocklet.env.dataDir, fileName);
|
|
119
|
-
res.download(filePath, err => err && console.error(err));
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const upload = multer({
|
|
123
|
-
storage: new DiskStorage({
|
|
124
|
-
destination: (req, file, cb) => {
|
|
125
|
-
const uploadDir = path.join(req.blocklet.env.dataDir, BLOCKLET_UPLOADS_DIR);
|
|
126
|
-
if (fs.existsSync(uploadDir) === false) {
|
|
127
|
-
fs.mkdirSync(uploadDir, { recursive: true });
|
|
128
|
-
}
|
|
129
|
-
cb(null, uploadDir);
|
|
130
|
-
},
|
|
131
|
-
}),
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// handle upload for blocklet prefs
|
|
135
|
-
// FIXME: should also ensurePermission here
|
|
136
|
-
app.post(uploadApi, ensureBlocklet, ensureComponent, upload.single('file'), async (req, res) => {
|
|
137
|
-
res.json({
|
|
138
|
-
url: joinUrl(
|
|
139
|
-
req.blocklet.environmentObj.BLOCKLET_APP_URL,
|
|
140
|
-
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
141
|
-
BLOCKLET_UPLOADS_DIR,
|
|
142
|
-
req.file.filename
|
|
143
|
-
),
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const staticOptions = {
|
|
148
|
-
maxAge: '30d',
|
|
149
|
-
extensions: ['html', 'htm'],
|
|
150
|
-
index: ['index.html', 'index.htm'],
|
|
151
|
-
setHeaders: (res, file) => {
|
|
152
|
-
if (serveStatic.mime.lookup(file) === 'text/html') {
|
|
153
|
-
res.setHeader('Cache-Control', 'public, max-age=0');
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
// serve hosted version
|
|
159
|
-
app.use('/hosted/form-collector', express.static(node.collectorPath, staticOptions));
|
|
160
|
-
},
|
|
161
|
-
};
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-underscore-dangle, global-require */
|
|
2
|
-
/* eslint-disable no-console */
|
|
3
|
-
const httpProxy = require('http-proxy');
|
|
4
|
-
const LRU = require('lru-cache');
|
|
5
|
-
const { isValid } = require('@arcblock/did');
|
|
6
|
-
const { BlockletEvents } = require('@blocklet/constant');
|
|
7
|
-
const md5 = require('@abtnode/util/lib/md5');
|
|
8
|
-
const { BLOCKLET_PROXY_PATH_PREFIX, WELLKNOWN_SERVICE_PATH_PREFIX, EVENTS } = require('@abtnode/constant');
|
|
9
|
-
const { forEachBlocklet } = require('@blocklet/meta/lib/util');
|
|
10
|
-
|
|
11
|
-
const get = require('lodash/get');
|
|
12
|
-
|
|
13
|
-
const logger = require('@abtnode/logger')(`${require('../../package.json').name}:blockletProxy`);
|
|
14
|
-
|
|
15
|
-
const findRoutingRule = require('../util/find-routing-rule');
|
|
16
|
-
|
|
17
|
-
const createLRU = opts => new LRU(opts);
|
|
18
|
-
|
|
19
|
-
const GENERIC_GROUP = 'GENERIC';
|
|
20
|
-
|
|
21
|
-
const createProxyCache = opts => {
|
|
22
|
-
const groups = {};
|
|
23
|
-
|
|
24
|
-
const resetGroup = did => {
|
|
25
|
-
logger.info('Reset cache group', { did });
|
|
26
|
-
groups[did] = createLRU(opts);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
resetGroup(GENERIC_GROUP);
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
set(did, key, value) {
|
|
33
|
-
if (!groups[did]) {
|
|
34
|
-
resetGroup(did);
|
|
35
|
-
}
|
|
36
|
-
groups[did].set(key, value);
|
|
37
|
-
},
|
|
38
|
-
get(did, key) {
|
|
39
|
-
return groups[did] ? groups[did].get(key) : undefined;
|
|
40
|
-
},
|
|
41
|
-
resetGroup,
|
|
42
|
-
deleteGroup(did) {
|
|
43
|
-
logger.info('Delete cache group', { did });
|
|
44
|
-
delete groups[did];
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const ensureUrl = (req, urlPrefix) => {
|
|
50
|
-
req.url = req.url.slice(urlPrefix.length);
|
|
51
|
-
|
|
52
|
-
// convert all faviconXXX to favicon to ensure file exists in root blocklet
|
|
53
|
-
if (req.url.startsWith('/favicon')) {
|
|
54
|
-
req.url = '/favicon.ico';
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
module.exports = {
|
|
59
|
-
createProxyCache,
|
|
60
|
-
init(app, node, proxy, proxyCache) {
|
|
61
|
-
if (process.env.NODE_ENV !== 'test' && (proxy || proxyCache)) {
|
|
62
|
-
throw new Error('proxy or proxyCache should only be invoked in test environment');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!proxy) {
|
|
66
|
-
// eslint-disable-next-line no-param-reassign
|
|
67
|
-
proxy = httpProxy.createProxyServer({
|
|
68
|
-
timeout: 3600 * 1000,
|
|
69
|
-
proxyTimeout: 3600 * 1000,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (!proxyCache) {
|
|
74
|
-
// eslint-disable-next-line no-param-reassign
|
|
75
|
-
proxyCache = createProxyCache({
|
|
76
|
-
// FIXME: 如果 css 文件加载后, css文件中的 font 文件没有被立即加载, 期间加载了超过 50 个其他的文件, 再加载 css 文件中的 font 文件, 则 font 文件会无法加载. 这种情况可能会在 abt web wallet 中出现
|
|
77
|
-
max: 50, // cache at most 50
|
|
78
|
-
maxAge: 1 * 60 * 1000, // cache for 1 minute
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const refreshSites = async () => {
|
|
83
|
-
try {
|
|
84
|
-
const sites = await node.getSitesFromSnapshot();
|
|
85
|
-
proxyCache.set(GENERIC_GROUP, 'SITES', sites);
|
|
86
|
-
return sites;
|
|
87
|
-
} catch (err) {
|
|
88
|
-
logger.error('refreshSites failed', { error: err });
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
[BlockletEvents.started, BlockletEvents.reloaded].forEach(event => {
|
|
94
|
-
node.on(event, blocklet => {
|
|
95
|
-
forEachBlocklet(
|
|
96
|
-
blocklet,
|
|
97
|
-
b => {
|
|
98
|
-
proxyCache.resetGroup(get(b, 'meta.did'));
|
|
99
|
-
},
|
|
100
|
-
{ sync: true }
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
node.on(BlockletEvents.removed, blocklet => {
|
|
106
|
-
forEachBlocklet(
|
|
107
|
-
blocklet,
|
|
108
|
-
b => {
|
|
109
|
-
proxyCache.deleteGroup(get(b, 'meta.did'));
|
|
110
|
-
},
|
|
111
|
-
{ sync: true }
|
|
112
|
-
);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
node.on(EVENTS.ROUTING_UPDATED, refreshSites);
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* match request
|
|
119
|
-
* e.g.
|
|
120
|
-
* /.blocklet/proxy/<did>/a.css
|
|
121
|
-
* /.blocklet/proxy/path/a.css
|
|
122
|
-
*/
|
|
123
|
-
app.get(`${BLOCKLET_PROXY_PATH_PREFIX}/:did/**`, async (req, res, next) => {
|
|
124
|
-
const { did } = req.params;
|
|
125
|
-
|
|
126
|
-
// proxy to blocklet service
|
|
127
|
-
if (did === 'blocklet-service') {
|
|
128
|
-
req.url = req.url.replace(`${BLOCKLET_PROXY_PATH_PREFIX}/${did}`, `${WELLKNOWN_SERVICE_PATH_PREFIX}/static`);
|
|
129
|
-
const target = `http://127.0.0.1:${process.env.ABT_NODE_SERVICE_PORT}`;
|
|
130
|
-
proxy.web(req, res, { target }, error => {
|
|
131
|
-
if (error) {
|
|
132
|
-
logger.error('http proxy error', { error });
|
|
133
|
-
res.status(502).send(`Can not proxy to blocklet service: ${target}`);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (isValid(did)) {
|
|
140
|
-
req.PROXY_PATH_PREFIX_DID = did;
|
|
141
|
-
req.PROXY_PATH_PREFIX = `${BLOCKLET_PROXY_PATH_PREFIX}/${did}`;
|
|
142
|
-
}
|
|
143
|
-
next();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* match request
|
|
148
|
-
* e.g.
|
|
149
|
-
* /.blocklet/proxy/<did>/a.css
|
|
150
|
-
* /.blocklet/proxy/path/a.css
|
|
151
|
-
* /.blocklet/proxy/a.css
|
|
152
|
-
*/
|
|
153
|
-
app.get(`${BLOCKLET_PROXY_PATH_PREFIX}/**`, async (req, res) => {
|
|
154
|
-
if (!req.headers.referer) {
|
|
155
|
-
res.status(400).send('Referer was not found in headers');
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const urlPrefix = req.PROXY_PATH_PREFIX || BLOCKLET_PROXY_PATH_PREFIX;
|
|
160
|
-
|
|
161
|
-
const group = req.PROXY_PATH_PREFIX_DID || GENERIC_GROUP;
|
|
162
|
-
|
|
163
|
-
// get origin by referer because the origin of referer of css resource and font resource are same
|
|
164
|
-
// e.g. https://a.b.c:8080/.blocklet/proxy/style.css | referer: https://a.b.c:8080/ | origin: https://a.b.c:8080
|
|
165
|
-
// https://a.b.c:8080/.blocklet/proxy/font.woff | referer: https://a.b.c:8080/.blocklet/proxy/style.css | origin: https://a.b.c:8080
|
|
166
|
-
const cacheUrl = md5(`${new URL(req.headers.referer).origin}${req.url}${req.search || ''}`);
|
|
167
|
-
|
|
168
|
-
const cacheKey = md5(`${cacheUrl}${req.headers.referer}`);
|
|
169
|
-
const cacheReferer = md5(req.headers.referer);
|
|
170
|
-
|
|
171
|
-
// use cache target if exist
|
|
172
|
-
const cachedTarget = proxyCache.get(group, cacheKey) || proxyCache.get(group, cacheReferer);
|
|
173
|
-
if (cachedTarget) {
|
|
174
|
-
ensureUrl(req, urlPrefix);
|
|
175
|
-
|
|
176
|
-
proxy.web(req, res, { target: cachedTarget }, error => {
|
|
177
|
-
if (error) {
|
|
178
|
-
logger.error('http proxy error', { error });
|
|
179
|
-
res.status(502).send(`Can not proxy to upstream blocklet: ${cachedTarget}`);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// get sites
|
|
186
|
-
let sites = proxyCache.get(GENERIC_GROUP, 'SITES');
|
|
187
|
-
if (!sites) {
|
|
188
|
-
sites = await refreshSites();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// find routing rule
|
|
192
|
-
const rule = findRoutingRule(sites, req, urlPrefix);
|
|
193
|
-
|
|
194
|
-
// proxy to target is matched rule was founded
|
|
195
|
-
if (rule) {
|
|
196
|
-
const target = `http://127.0.0.1:${rule.to.port}`;
|
|
197
|
-
|
|
198
|
-
// cache the target for subsequent requests with the same url and referer
|
|
199
|
-
proxyCache.set(group, cacheKey, target);
|
|
200
|
-
|
|
201
|
-
// cache the target for subsequent requests request which referer is this url
|
|
202
|
-
// e.g. current request: http://a.com/.blocklet/proxy/a.css
|
|
203
|
-
// follow-up request: http://a.com/.blocklet/proxy/font.woff (referer is http://a.com/.blocklet/proxy/a.css)
|
|
204
|
-
proxyCache.set(group, cacheUrl, target);
|
|
205
|
-
|
|
206
|
-
ensureUrl(req, urlPrefix);
|
|
207
|
-
|
|
208
|
-
proxy.web(req, res, { target }, error => {
|
|
209
|
-
if (error) {
|
|
210
|
-
logger.error('http proxy error', { error });
|
|
211
|
-
res.status(502).send(`Can not proxy to upstream blocklet: ${target}`);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
res.status(400).send(`Cannot find blocklet served at ${req.hostname}`);
|
|
218
|
-
});
|
|
219
|
-
},
|
|
220
|
-
};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const { WELLKNOWN_DID_RESOLVER_PREFIX } = require('@abtnode/constant');
|
|
2
|
-
|
|
3
|
-
const getDidDocumentMiddleware = node => async (req, res) => {
|
|
4
|
-
const blockletDid = req.get('x-blocklet-did');
|
|
5
|
-
if (!blockletDid) {
|
|
6
|
-
return res.status(404).json({ message: 'Service can not be found' });
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const { did: serverDid } = await node.getNodeInfo();
|
|
10
|
-
let appId = serverDid;
|
|
11
|
-
let did = serverDid;
|
|
12
|
-
|
|
13
|
-
if (blockletDid !== serverDid) {
|
|
14
|
-
const blocklet = await node.getBlocklet({ did: blockletDid, attachRuntimeInfo: false });
|
|
15
|
-
if (!blocklet) {
|
|
16
|
-
return res.status(404).json({ message: 'Service can not be found' });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
appId = blocklet.appDid;
|
|
20
|
-
did = blockletDid;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const document = {
|
|
24
|
-
id: `did:abt:${appId}`,
|
|
25
|
-
alsoKnownAs: [`did:abt:${did}`],
|
|
26
|
-
controller: `did:abt:${serverDid}`,
|
|
27
|
-
services: [],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return res.json(document);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
module.exports = {
|
|
34
|
-
init(app, node) {
|
|
35
|
-
app.get(WELLKNOWN_DID_RESOLVER_PREFIX, getDidDocumentMiddleware(node));
|
|
36
|
-
},
|
|
37
|
-
getDidDocumentMiddleware,
|
|
38
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
const dns = require('dns');
|
|
2
|
-
const LRU = require('lru-cache');
|
|
3
|
-
const joinUrl = require('url-join');
|
|
4
|
-
const axios = require('@abtnode/util/lib/axios');
|
|
5
|
-
const { DEFAULT_IP_DOMAIN, DEFAULT_ADMIN_PATH } = require('@abtnode/constant');
|
|
6
|
-
|
|
7
|
-
const getNodeDomain = ip => (ip ? DEFAULT_IP_DOMAIN.replace(/^\*/, ip.replace(/\./g, '-')) : '');
|
|
8
|
-
|
|
9
|
-
const timeout = process.env.NODE_ENV === 'test' ? 500 : 2000;
|
|
10
|
-
|
|
11
|
-
// check if node dashboard https endpoint return 2xx
|
|
12
|
-
const checkConnected = async ({ ip, nodeInfo }) => {
|
|
13
|
-
const { adminPath = DEFAULT_ADMIN_PATH } = nodeInfo.routing || {};
|
|
14
|
-
const origin = `https://${getNodeDomain(ip)}`;
|
|
15
|
-
const endpoint = joinUrl(origin, adminPath);
|
|
16
|
-
await axios.get(endpoint, { timeout });
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
module.exports = {
|
|
20
|
-
init(router, node) {
|
|
21
|
-
const cache = new LRU({
|
|
22
|
-
max: 50, // cache at most 50
|
|
23
|
-
maxAge: 5 * 60 * 1000, // cache for 5 minute
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const accessibleCache = new LRU({
|
|
27
|
-
max: 50, // cache at most 50
|
|
28
|
-
maxAge: 5 * 60 * 1000, // cache for 5 minute
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
router.get('/api/dns-resolve/', async (req, res) => {
|
|
32
|
-
const hostname = req.query.hostname || req.hostname;
|
|
33
|
-
const accessible = !!req.query.accessible;
|
|
34
|
-
|
|
35
|
-
const _cache = accessible ? accessibleCache : cache;
|
|
36
|
-
|
|
37
|
-
const cachedAddress = _cache.get(hostname);
|
|
38
|
-
|
|
39
|
-
if (cachedAddress) {
|
|
40
|
-
res.json({ address: cachedAddress });
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
dns.lookup(hostname, async (err, address, family) => {
|
|
45
|
-
if (err) {
|
|
46
|
-
_cache.del(hostname);
|
|
47
|
-
res.json({ address: null, error: err.message });
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (family !== 4) {
|
|
52
|
-
_cache.del(hostname);
|
|
53
|
-
res.json({ address: null, error: 'address is not IPv4 format' });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (accessible) {
|
|
57
|
-
const nodeInfo = await node.getNodeInfo();
|
|
58
|
-
try {
|
|
59
|
-
await checkConnected({ ip: address, nodeInfo });
|
|
60
|
-
_cache.set(hostname, address);
|
|
61
|
-
res.json({ address });
|
|
62
|
-
} catch {
|
|
63
|
-
_cache.del(hostname);
|
|
64
|
-
res.json({ address: null, error: 'address is not accessible' });
|
|
65
|
-
}
|
|
66
|
-
} else {
|
|
67
|
-
_cache.set(hostname, address);
|
|
68
|
-
res.json({ address });
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
},
|
|
73
|
-
};
|
package/api/routes/log.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const dayjs = require('dayjs');
|
|
2
|
-
const { createDownloadLogStream } = require('@abtnode/util/lib/log');
|
|
3
|
-
|
|
4
|
-
const mutateBlockletPermission = require('../middlewares/mutate-blocklet-permission');
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
init(app, node) {
|
|
8
|
-
const ensurePermission = mutateBlockletPermission(node);
|
|
9
|
-
|
|
10
|
-
app.get('/api/download/log/:did', ensurePermission, async (req, res) => {
|
|
11
|
-
const { did } = req.params;
|
|
12
|
-
const { days } = req.query;
|
|
13
|
-
|
|
14
|
-
if (days > 7) {
|
|
15
|
-
res.status(400).send('Interval should not > 1 week');
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const stream = await createDownloadLogStream({ node, did, days });
|
|
21
|
-
|
|
22
|
-
res.setHeader('Content-Type', 'application/octet-stream');
|
|
23
|
-
res.setHeader('Content-Disposition', `attachment;filename=${did}.${dayjs().format('YYYYMMDDHHmmss')}.log.zip`);
|
|
24
|
-
|
|
25
|
-
stream.pipe(res);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
res.status(500).send(error.message);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
},
|
|
31
|
-
};
|
package/api/routes/passport.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const { getPassportStatus } = require('@abtnode/auth/lib/auth');
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
init(router, node) {
|
|
5
|
-
router.get('/api/passport/status', async (req, res) => {
|
|
6
|
-
const { vcId, userDid, locale } = req.query;
|
|
7
|
-
const nodeInfo = await node.getNodeInfo();
|
|
8
|
-
const teamDid = nodeInfo.did;
|
|
9
|
-
|
|
10
|
-
if (teamDid !== req.query.teamDid) {
|
|
11
|
-
throw new Error('teamDid is invalid');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const status = await getPassportStatus({ node, teamDid, userDid, vcId, locale });
|
|
15
|
-
|
|
16
|
-
res.json(status);
|
|
17
|
-
});
|
|
18
|
-
},
|
|
19
|
-
};
|
package/api/routes/session.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
const url = require('url');
|
|
2
|
-
const SealedBox = require('tweetnacl-sealedbox-js');
|
|
3
|
-
const { getBaseUrl } = require('@abtnode/router-adapter');
|
|
4
|
-
const { decodeEncryptionKey } = require('@abtnode/util/lib/security');
|
|
5
|
-
|
|
6
|
-
const env = require('../libs/env');
|
|
7
|
-
const { createSessionToken } = require('../libs/login');
|
|
8
|
-
|
|
9
|
-
module.exports = {
|
|
10
|
-
init(app, node) {
|
|
11
|
-
// eslint-disable-next-line consistent-return
|
|
12
|
-
app.get('/api/did/session', async (req, res) => {
|
|
13
|
-
// Must be some one try to trick daemon with a blocklet token
|
|
14
|
-
if (req.user && req.user.bid) {
|
|
15
|
-
return res.status(403).send('Forbidden');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!req.user) {
|
|
19
|
-
return res.json({});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Authentication request from dashboard
|
|
23
|
-
const user = { ...req.user };
|
|
24
|
-
const rbac = await node.getRBAC();
|
|
25
|
-
user.permissions = await rbac.getScope(user.role);
|
|
26
|
-
|
|
27
|
-
const encKey = '_ek_';
|
|
28
|
-
let nextToken = '';
|
|
29
|
-
if (req.query[encKey]) {
|
|
30
|
-
try {
|
|
31
|
-
const encryptionKey = decodeEncryptionKey(req.query[encKey]);
|
|
32
|
-
nextToken = JSON.stringify(await createSessionToken(user.did, { passport: user.passport, role: user.role }));
|
|
33
|
-
nextToken = Buffer.from(SealedBox.seal(Buffer.from(nextToken), encryptionKey)).toString('base64');
|
|
34
|
-
} catch {
|
|
35
|
-
// Do nothing
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
res.json({ user, nextToken });
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
app.get('/api/env', async (req, res) => {
|
|
43
|
-
res.type('js');
|
|
44
|
-
|
|
45
|
-
const info = await node.getNodeInfo();
|
|
46
|
-
const baseUrl = url.format({
|
|
47
|
-
protocol: req.protocol,
|
|
48
|
-
host: req.get('host'),
|
|
49
|
-
pathname: '',
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
res.send(`window.env = {
|
|
53
|
-
appId: "${info.did}",
|
|
54
|
-
appName: "${info.name}",
|
|
55
|
-
appDescription: "${info.description}",
|
|
56
|
-
apiPrefix: "${getBaseUrl(req)}",
|
|
57
|
-
baseUrl: "${env.baseUrl || baseUrl}",
|
|
58
|
-
}`);
|
|
59
|
-
});
|
|
60
|
-
},
|
|
61
|
-
};
|
package/api/routes/user.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const { NODE_DATA_DIR_NAME, USER_AVATAR_PATH_PREFIX } = require('@abtnode/constant');
|
|
5
|
-
const { getAvatarFile } = require('@abtnode/util/lib/user-avatar');
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
init(app, node) {
|
|
9
|
-
// eslint-disable-next-line consistent-return
|
|
10
|
-
app.get(`${USER_AVATAR_PATH_PREFIX}/:did/:fileName`, async (req, res) => {
|
|
11
|
-
const sendOptions = { maxAge: '1y' };
|
|
12
|
-
const info = await node.getNodeInfo();
|
|
13
|
-
const { did, fileName } = req.params;
|
|
14
|
-
|
|
15
|
-
let dataDir;
|
|
16
|
-
if (did === info.did) {
|
|
17
|
-
dataDir = path.join(node.dataDirs.data, NODE_DATA_DIR_NAME);
|
|
18
|
-
} else {
|
|
19
|
-
const blocklet = await node.getBlocklet({ did, attachConfig: false });
|
|
20
|
-
if (!blocklet) {
|
|
21
|
-
res.status(404).send('Blocklet Not Found');
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
dataDir = path.join(node.dataDirs.data, blocklet.meta.name);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const avatarFile = getAvatarFile(dataDir, fileName);
|
|
29
|
-
|
|
30
|
-
if (!fs.existsSync(avatarFile)) {
|
|
31
|
-
res.status(404).send('Avatar Not Found');
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
res.sendFile(avatarFile, sendOptions);
|
|
36
|
-
});
|
|
37
|
-
},
|
|
38
|
-
};
|