@next-core/brick-container 2.98.23 → 2.99.0
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/dist/{dll.90ef3f2c.js → dll.42523c4f.js} +3 -3
- package/dist/{dll.90ef3f2c.js.map → dll.42523c4f.js.map} +1 -1
- package/dist/icons--app.eb086178.js +2 -0
- package/dist/{icons--app.bc80361d.js.map → icons--app.eb086178.js.map} +1 -1
- package/dist/icons--colored-big-screen.95f612f1.js +2 -0
- package/dist/{icons--colored-big-screen.144fc070.js.map → icons--colored-big-screen.95f612f1.js.map} +1 -1
- package/dist/index.html +1 -1
- package/dist/main.9bb0888ea3d2d54712d5.js +2 -0
- package/dist/{main.5a0df8877941241d6a07.js.map → main.9bb0888ea3d2d54712d5.js.map} +1 -1
- package/dist/{preview.3d1b49a405ab0a4ae5f6.js → preview.793b87ea20dd67a8a1e7.js} +2 -2
- package/dist/{preview.3d1b49a405ab0a4ae5f6.js.map → preview.793b87ea20dd67a8a1e7.js.map} +1 -1
- package/dist/preview.html +1 -1
- package/package.json +2 -2
- package/serve/getEnv.js +53 -0
- package/serve/liveReload.js +0 -1
- package/serve/localProxy.js +76 -0
- package/serve/serve.js +4 -0
- package/serve/tokenManager.js +224 -0
- package/dist/icons--app.bc80361d.js +0 -2
- package/dist/icons--colored-big-screen.144fc070.js +0 -2
- package/dist/main.5a0df8877941241d6a07.js +0 -2
package/dist/preview.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="zh-CN" data-theme="light" data-mode="default"><head><meta charset="utf-8"/><base href="<!--# echo var='base_href' default='/' -->"/><script>window.DEVELOPER_PREVIEW = true;</script><link href="preview.5fb551513edc8051ce6c.css" rel="stylesheet"></head><body><div id="preview-root"><div id="main-mount-point"></div><div id="bg-mount-point" style="display: none"></div><div id="portal-mount-point"></div></div><script src="dll.
|
|
1
|
+
<!doctype html><html lang="zh-CN" data-theme="light" data-mode="default"><head><meta charset="utf-8"/><base href="<!--# echo var='base_href' default='/' -->"/><script>window.DEVELOPER_PREVIEW = true;</script><link href="preview.5fb551513edc8051ce6c.css" rel="stylesheet"></head><body><div id="preview-root"><div id="main-mount-point"></div><div id="bg-mount-point" style="display: none"></div><div id="portal-mount-point"></div></div><script src="dll.42523c4f.js"></script><script src="polyfill.3e88f145a0893497373b.js"></script><script src="preview.793b87ea20dd67a8a1e7.js"></script></body></html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@next-core/brick-container",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.99.0",
|
|
4
4
|
"description": "Brick Container Server",
|
|
5
5
|
"homepage": "https://github.com/easyops-cn/next-core/tree/master/packages/brick-container",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"webpack-dev-server": "^4.11.1",
|
|
74
74
|
"webpack-merge": "^5.8.0"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "dd3c7ae18c87b52d96c60732ff7f957dfd46862e"
|
|
77
77
|
}
|
package/serve/getEnv.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const meow = require("meow");
|
|
4
4
|
const chalk = require("chalk");
|
|
5
|
+
const yaml = require("js-yaml");
|
|
5
6
|
const { getEasyopsConfig } = require("@next-core/repo-config");
|
|
6
7
|
const {
|
|
7
8
|
getNamesOfMicroApps,
|
|
@@ -163,6 +164,9 @@ module.exports = (runtimeFlags) => {
|
|
|
163
164
|
useSkywalkingAnalysis: {
|
|
164
165
|
type: "string",
|
|
165
166
|
},
|
|
167
|
+
proxyConfig: {
|
|
168
|
+
type: "string",
|
|
169
|
+
},
|
|
166
170
|
};
|
|
167
171
|
const cli = meow(
|
|
168
172
|
`
|
|
@@ -201,6 +205,7 @@ module.exports = (runtimeFlags) => {
|
|
|
201
205
|
--help Show help message
|
|
202
206
|
--version Show brick container version
|
|
203
207
|
--use-skywalking-analysis Use skywalking analysis
|
|
208
|
+
--proxy-config Specify custom proxy config file path (defaults to "dev.proxy.yaml" in project root)
|
|
204
209
|
`,
|
|
205
210
|
{
|
|
206
211
|
flags: flagOptions,
|
|
@@ -247,6 +252,37 @@ module.exports = (runtimeFlags) => {
|
|
|
247
252
|
);
|
|
248
253
|
const appConfig = (devConfig && devConfig.appConfig) || {};
|
|
249
254
|
|
|
255
|
+
function getLocalProxies() {
|
|
256
|
+
const customPath = flags.proxyConfig;
|
|
257
|
+
const proxyConfigPath = customPath
|
|
258
|
+
? path.resolve(customPath)
|
|
259
|
+
: path.join(rootDir, "dev.proxy.yaml");
|
|
260
|
+
if (fs.existsSync(proxyConfigPath)) {
|
|
261
|
+
console.log(chalk.cyan("proxy config:"), proxyConfigPath);
|
|
262
|
+
let content;
|
|
263
|
+
try {
|
|
264
|
+
content = yaml.safeLoad(fs.readFileSync(proxyConfigPath, "utf8"));
|
|
265
|
+
} catch (e) {
|
|
266
|
+
console.error(
|
|
267
|
+
chalk.red("Failed to parse proxy config:"),
|
|
268
|
+
proxyConfigPath,
|
|
269
|
+
e.message
|
|
270
|
+
);
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
if (!content) return {};
|
|
274
|
+
if (content.proxies) {
|
|
275
|
+
return content;
|
|
276
|
+
}
|
|
277
|
+
return { proxies: content };
|
|
278
|
+
}
|
|
279
|
+
if (customPath) {
|
|
280
|
+
console.error(chalk.red("proxy config not found:"), proxyConfigPath);
|
|
281
|
+
}
|
|
282
|
+
return {};
|
|
283
|
+
}
|
|
284
|
+
const localProxies = getLocalProxies();
|
|
285
|
+
|
|
250
286
|
const { usePublicScope, standalone: confStandalone } =
|
|
251
287
|
getEasyopsConfig(nextRepoDir);
|
|
252
288
|
|
|
@@ -412,6 +448,7 @@ module.exports = (runtimeFlags) => {
|
|
|
412
448
|
publicCdn: flags.publicCdn,
|
|
413
449
|
asCdn: flags.asCdn,
|
|
414
450
|
useSkywalkingAnalysis,
|
|
451
|
+
localProxies,
|
|
415
452
|
isWebpackServe,
|
|
416
453
|
};
|
|
417
454
|
|
|
@@ -482,6 +519,22 @@ module.exports = (runtimeFlags) => {
|
|
|
482
519
|
console.log("local templates:", env.localTemplates);
|
|
483
520
|
}
|
|
484
521
|
|
|
522
|
+
if (
|
|
523
|
+
env.localProxies.proxies &&
|
|
524
|
+
Object.keys(env.localProxies.proxies).length > 0
|
|
525
|
+
) {
|
|
526
|
+
console.log();
|
|
527
|
+
console.log("local proxies:", env.localProxies.proxies);
|
|
528
|
+
if (env.localProxies.auth) {
|
|
529
|
+
console.log(
|
|
530
|
+
"local proxy auth: appId=%s, org=%s, user=%s",
|
|
531
|
+
env.localProxies.auth.appId || "api_gateway",
|
|
532
|
+
env.localProxies.auth.org,
|
|
533
|
+
env.localProxies.auth.user
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
485
538
|
console.log();
|
|
486
539
|
console.log(
|
|
487
540
|
chalk.bold.cyan("mode:"),
|
package/serve/liveReload.js
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
2
|
+
const { escapeRegExp } = require("lodash");
|
|
3
|
+
const chalk = require("chalk");
|
|
4
|
+
const { getToken } = require("./tokenManager");
|
|
5
|
+
|
|
6
|
+
function setupLocalProxies(app, env) {
|
|
7
|
+
const config = env.localProxies;
|
|
8
|
+
if (!config || !config.proxies || Object.keys(config.proxies).length === 0) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { proxies, auth } = config;
|
|
13
|
+
const org = auth && auth.org;
|
|
14
|
+
const user = auth && auth.user;
|
|
15
|
+
const serverUrl = env.server;
|
|
16
|
+
|
|
17
|
+
// 预热 token,避免首次请求 401
|
|
18
|
+
if (auth && !auth.token) {
|
|
19
|
+
getToken(serverUrl, auth).catch((e) =>
|
|
20
|
+
console.error(
|
|
21
|
+
chalk.red("[local-proxy]"),
|
|
22
|
+
"Token pre-warm failed:",
|
|
23
|
+
e.message
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const [pathPattern, target] of Object.entries(proxies)) {
|
|
29
|
+
const gatewayPath = pathPattern.startsWith("/")
|
|
30
|
+
? pathPattern
|
|
31
|
+
: `/api/gateway/${pathPattern}`;
|
|
32
|
+
const fullPath = `${env.baseHref}${gatewayPath.slice(1)}`;
|
|
33
|
+
|
|
34
|
+
const proxy = createProxyMiddleware({
|
|
35
|
+
target,
|
|
36
|
+
secure: false,
|
|
37
|
+
changeOrigin: true,
|
|
38
|
+
logLevel: "debug",
|
|
39
|
+
pathRewrite: { [`^${escapeRegExp(fullPath)}`]: "" },
|
|
40
|
+
onProxyReq: (proxyReq) => {
|
|
41
|
+
if (org && !proxyReq.getHeader("org")) {
|
|
42
|
+
proxyReq.setHeader("org", String(org));
|
|
43
|
+
}
|
|
44
|
+
if (user && !proxyReq.getHeader("user")) {
|
|
45
|
+
proxyReq.setHeader("user", user);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
app.use(fullPath, async (req, res, next) => {
|
|
51
|
+
console.log(
|
|
52
|
+
chalk.cyan("[local-proxy]"),
|
|
53
|
+
chalk.green(pathPattern) + chalk.white(":"),
|
|
54
|
+
chalk.yellow(req.method),
|
|
55
|
+
chalk.white(req.url)
|
|
56
|
+
);
|
|
57
|
+
if (auth) {
|
|
58
|
+
try {
|
|
59
|
+
const token = await getToken(serverUrl, auth);
|
|
60
|
+
if (token) {
|
|
61
|
+
req.headers["authorization"] = `Bearer ${token}`;
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error(
|
|
65
|
+
chalk.red("[local-proxy]"),
|
|
66
|
+
"Failed to get token:",
|
|
67
|
+
e.message
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
proxy(req, res, next);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = { setupLocalProxies };
|
package/serve/serve.js
CHANGED
|
@@ -11,6 +11,7 @@ const serveLocal = require("./serveLocal");
|
|
|
11
11
|
const getProxies = require("./getProxies");
|
|
12
12
|
const { getIndexHtml, distDir, getRawIndexHtml } = require("./getIndexHtml");
|
|
13
13
|
const liveReload = require("./liveReload");
|
|
14
|
+
const { setupLocalProxies } = require("./localProxy");
|
|
14
15
|
|
|
15
16
|
module.exports = function serve(runtimeFlags) {
|
|
16
17
|
const env = getEnv(runtimeFlags);
|
|
@@ -86,6 +87,9 @@ module.exports = function serve(runtimeFlags) {
|
|
|
86
87
|
app.use(`${env.baseHref}:appId/-/core/`, express.static(distDir));
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
// Register local service proxies before default proxies.
|
|
91
|
+
setupLocalProxies(app, env);
|
|
92
|
+
|
|
89
93
|
// Using proxies.
|
|
90
94
|
const proxies = getProxies(env, getRawIndexHtml);
|
|
91
95
|
if (proxies) {
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
const crypto = require("crypto");
|
|
2
|
+
const https = require("https");
|
|
3
|
+
const http = require("http");
|
|
4
|
+
const { URL } = require("url");
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
|
|
7
|
+
const TOKEN_CACHE_TTL = 1800 * 1000;
|
|
8
|
+
const CREDENTIALS_CACHE_TTL = 3600 * 1000;
|
|
9
|
+
const DEFAULT_APP_ID = "api_gateway";
|
|
10
|
+
const GATEWAY_INNER_PORT = 8107;
|
|
11
|
+
const GATEWAY_SERVICE_PATH = "/api/gateway/user_service.apikey";
|
|
12
|
+
|
|
13
|
+
const _tokenCache = {};
|
|
14
|
+
const _credentialsCache = {};
|
|
15
|
+
|
|
16
|
+
function generateSignature(
|
|
17
|
+
method,
|
|
18
|
+
urlPath,
|
|
19
|
+
query,
|
|
20
|
+
contentType,
|
|
21
|
+
clientId,
|
|
22
|
+
secret,
|
|
23
|
+
timestamp
|
|
24
|
+
) {
|
|
25
|
+
const keyList = Object.keys(query)
|
|
26
|
+
.filter((k) => k !== "signature")
|
|
27
|
+
.sort();
|
|
28
|
+
|
|
29
|
+
const queryParts = [];
|
|
30
|
+
for (const key of keyList) {
|
|
31
|
+
const val = query[key];
|
|
32
|
+
if (Array.isArray(val)) {
|
|
33
|
+
for (const v of val) {
|
|
34
|
+
queryParts.push(`${key}${v}`);
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
queryParts.push(`${key}${val}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = [
|
|
42
|
+
method,
|
|
43
|
+
urlPath,
|
|
44
|
+
queryParts.join(""),
|
|
45
|
+
contentType,
|
|
46
|
+
clientId,
|
|
47
|
+
secret,
|
|
48
|
+
timestamp,
|
|
49
|
+
].join("\n");
|
|
50
|
+
return crypto.createHmac("sha256", secret).update(data).digest("hex");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function httpRequest(fullUrl, options, body) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const parsed = new URL(fullUrl);
|
|
56
|
+
const mod = parsed.protocol === "https:" ? https : http;
|
|
57
|
+
|
|
58
|
+
if (body) {
|
|
59
|
+
options.headers = options.headers || {};
|
|
60
|
+
options.headers["Content-Length"] = Buffer.byteLength(body);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const req = mod.request(
|
|
64
|
+
fullUrl,
|
|
65
|
+
{ ...options, timeout: 30000 },
|
|
66
|
+
(res) => {
|
|
67
|
+
let data = "";
|
|
68
|
+
res.on("data", (chunk) => (data += chunk));
|
|
69
|
+
res.on("end", () => {
|
|
70
|
+
if (res.statusCode !== 200) {
|
|
71
|
+
return reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
resolve(JSON.parse(data));
|
|
75
|
+
} catch (e) {
|
|
76
|
+
reject(new Error(`JSON parse error: ${e.message}`));
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
req.on("error", (e) => reject(new Error(`network error: ${e.message}`)));
|
|
82
|
+
req.on("timeout", () => {
|
|
83
|
+
req.destroy();
|
|
84
|
+
reject(new Error("request timeout"));
|
|
85
|
+
});
|
|
86
|
+
req.end(body || undefined);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getInnerGatewayUrl(serverUrl) {
|
|
91
|
+
const parsed = new URL(serverUrl);
|
|
92
|
+
return `http://${parsed.hostname}:${GATEWAY_INNER_PORT}${GATEWAY_SERVICE_PATH}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function createServerApiKey(serverUrl, clientId) {
|
|
96
|
+
const cacheKey = `${serverUrl}:${clientId}`;
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
if (
|
|
99
|
+
_credentialsCache[cacheKey] &&
|
|
100
|
+
now < _credentialsCache[cacheKey].expireAt
|
|
101
|
+
) {
|
|
102
|
+
return _credentialsCache[cacheKey].value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const timestamp = String(Math.floor(now / 1000));
|
|
106
|
+
const servicePath = "/api/v1/apikey/server";
|
|
107
|
+
const signature = generateSignature(
|
|
108
|
+
"POST",
|
|
109
|
+
servicePath,
|
|
110
|
+
{},
|
|
111
|
+
"application/json",
|
|
112
|
+
clientId,
|
|
113
|
+
clientId,
|
|
114
|
+
timestamp
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const baseUrl = getInnerGatewayUrl(serverUrl);
|
|
118
|
+
const fullUrl = `${baseUrl}${servicePath}`;
|
|
119
|
+
console.log(
|
|
120
|
+
chalk.cyan("[token-manager]"),
|
|
121
|
+
"Creating server apikey for",
|
|
122
|
+
clientId,
|
|
123
|
+
"(via inner port)"
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const data = await httpRequest(
|
|
127
|
+
fullUrl,
|
|
128
|
+
{
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: {
|
|
131
|
+
"Content-Type": "application/json",
|
|
132
|
+
"X-Timestamp": timestamp,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
JSON.stringify({ clientId, signature })
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const secret = data.data?.secret;
|
|
139
|
+
if (!secret) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`CreateServerApiKey: unexpected response: ${JSON.stringify(data)}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const value = { clientId, secret };
|
|
146
|
+
_credentialsCache[cacheKey] = {
|
|
147
|
+
value,
|
|
148
|
+
expireAt: now + CREDENTIALS_CACHE_TTL,
|
|
149
|
+
};
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function requestToken(serverUrl, clientId, secret, user, org) {
|
|
154
|
+
const timestamp = String(Math.floor(Date.now() / 1000));
|
|
155
|
+
const servicePath = `/api/v1/apikey/request_token/client_id/${encodeURIComponent(
|
|
156
|
+
clientId
|
|
157
|
+
)}`;
|
|
158
|
+
|
|
159
|
+
const query = {
|
|
160
|
+
user: user || "defaultUser",
|
|
161
|
+
org: org != null ? String(org) : "0",
|
|
162
|
+
};
|
|
163
|
+
query.signature = generateSignature(
|
|
164
|
+
"GET",
|
|
165
|
+
servicePath,
|
|
166
|
+
query,
|
|
167
|
+
"",
|
|
168
|
+
clientId,
|
|
169
|
+
secret,
|
|
170
|
+
timestamp
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const qs = Object.entries(query)
|
|
174
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
175
|
+
.join("&");
|
|
176
|
+
|
|
177
|
+
const baseUrl = getInnerGatewayUrl(serverUrl);
|
|
178
|
+
const fullUrl = `${baseUrl}${servicePath}?${qs}`;
|
|
179
|
+
const data = await httpRequest(fullUrl, {
|
|
180
|
+
method: "GET",
|
|
181
|
+
headers: { "X-Timestamp": timestamp },
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (data.code !== 0 || !data.data) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`RequestToken: unexpected response: ${JSON.stringify(data)}`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return data.data.token || "";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function getToken(serverUrl, auth) {
|
|
193
|
+
if (auth.token) {
|
|
194
|
+
return auth.token;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const appId = auth.appId || DEFAULT_APP_ID;
|
|
198
|
+
const clientId = `easyops_server_${appId}`;
|
|
199
|
+
const cacheKey = `${serverUrl}:${clientId}:${auth.org || ""}:${
|
|
200
|
+
auth.user || ""
|
|
201
|
+
}`;
|
|
202
|
+
|
|
203
|
+
const now = Date.now();
|
|
204
|
+
if (_tokenCache[cacheKey] && now < _tokenCache[cacheKey].expireAt) {
|
|
205
|
+
return _tokenCache[cacheKey].token;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const creds = await createServerApiKey(serverUrl, clientId);
|
|
209
|
+
const token = await requestToken(
|
|
210
|
+
serverUrl,
|
|
211
|
+
creds.clientId,
|
|
212
|
+
creds.secret,
|
|
213
|
+
auth.user,
|
|
214
|
+
auth.org
|
|
215
|
+
);
|
|
216
|
+
_tokenCache[cacheKey] = { token, expireAt: now + TOKEN_CACHE_TTL };
|
|
217
|
+
console.log(
|
|
218
|
+
chalk.green("[token-manager]"),
|
|
219
|
+
"Token acquired, cached for 30 minutes"
|
|
220
|
+
);
|
|
221
|
+
return token;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = { getToken };
|