com.jimuwd.xian.registry-proxy 1.1.0 → 1.1.2
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/server/index.js +30 -10
- package/dist/utils/resolveEnvValue.d.ts +19 -0
- package/dist/utils/resolveEnvValue.js +33 -0
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import http, { createServer } from 'node:http';
|
|
3
3
|
import https, { createServer as createHttpsServer } from 'node:https';
|
|
4
|
-
import { promises as fsPromises, readFileSync } from 'node:fs';
|
|
4
|
+
import { existsSync, promises as fsPromises, readFileSync } from 'node:fs';
|
|
5
5
|
import { load } from 'js-yaml';
|
|
6
6
|
import fetch from 'node-fetch';
|
|
7
7
|
import { homedir } from 'os';
|
|
@@ -11,6 +11,7 @@ import logger from "../utils/logger.js";
|
|
|
11
11
|
import ConcurrencyLimiter from "../utils/ConcurrencyLimiter.js";
|
|
12
12
|
import { gracefulShutdown, registerProcessShutdownHook } from "./gracefullShutdown.js";
|
|
13
13
|
import { writePortFile } from "../port.js";
|
|
14
|
+
import resolveEnvValue from "../utils/resolveEnvValue.js";
|
|
14
15
|
const { readFile } = fsPromises;
|
|
15
16
|
const limiter = new ConcurrencyLimiter(Infinity);
|
|
16
17
|
function removeEndingSlashAndForceStartingSlash(str) {
|
|
@@ -57,6 +58,11 @@ function removeRegistryPrefix(tarballUrl, registries) {
|
|
|
57
58
|
}
|
|
58
59
|
throw new Error(`Can't find tarball url ${tarballUrl} does not match given registries ${normalizedRegistries}`);
|
|
59
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* 读取yml配置文件得到配置值对象{@link ProxyConfig}
|
|
63
|
+
* @note 本读取操作不会解析环境变量值
|
|
64
|
+
* @param proxyConfigPath 配置文件路径
|
|
65
|
+
*/
|
|
60
66
|
async function readProxyConfig(proxyConfigPath = './.registry-proxy.yml') {
|
|
61
67
|
let config = undefined;
|
|
62
68
|
const resolvedPath = resolvePath(proxyConfigPath);
|
|
@@ -74,10 +80,20 @@ async function readProxyConfig(proxyConfigPath = './.registry-proxy.yml') {
|
|
|
74
80
|
}
|
|
75
81
|
return config;
|
|
76
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* 读取yml配置文件为yml对象
|
|
85
|
+
* @param path yml文件路径
|
|
86
|
+
*/
|
|
77
87
|
async function readYarnConfig(path) {
|
|
78
88
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
if (existsSync(path)) {
|
|
90
|
+
const content = await readFile(resolvePath(path), 'utf8');
|
|
91
|
+
return load(content);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
logger.info(`Skip reading ${path}, because it does not exist.`);
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
81
97
|
}
|
|
82
98
|
catch (e) {
|
|
83
99
|
logger.warn(`Failed to load Yarn config from ${path}:`, e);
|
|
@@ -93,7 +109,7 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
|
|
|
93
109
|
const registryMap = new Map();
|
|
94
110
|
for (const [proxiedRegUrl, proxyRegConfig] of Object.entries(proxyConfig.registries)) {
|
|
95
111
|
const normalizedProxiedRegUrl = normalizeUrl(proxiedRegUrl);
|
|
96
|
-
let token = proxyRegConfig?.npmAuthToken;
|
|
112
|
+
let token = resolveEnvValue(proxyRegConfig?.npmAuthToken);
|
|
97
113
|
if (!token) {
|
|
98
114
|
const yarnConfigs = [localYarnConfig, globalYarnConfig];
|
|
99
115
|
for (const yarnConfig of yarnConfigs) {
|
|
@@ -103,6 +119,7 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
|
|
|
103
119
|
if (foundEntry) {
|
|
104
120
|
const [, registryConfig] = foundEntry;
|
|
105
121
|
if (registryConfig?.npmAuthToken) {
|
|
122
|
+
// .yarnrc.yml内的配置值,暂时不处理环境变量值,未来按需扩展
|
|
106
123
|
token = registryConfig.npmAuthToken;
|
|
107
124
|
break;
|
|
108
125
|
}
|
|
@@ -110,7 +127,10 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
|
|
|
110
127
|
}
|
|
111
128
|
}
|
|
112
129
|
}
|
|
113
|
-
registryMap.set(normalizedProxiedRegUrl, {
|
|
130
|
+
registryMap.set(normalizedProxiedRegUrl, {
|
|
131
|
+
normalizedRegistryUrl: normalizedProxiedRegUrl,
|
|
132
|
+
token: token ? token : undefined
|
|
133
|
+
});
|
|
114
134
|
}
|
|
115
135
|
const registries = Array.from(registryMap.values());
|
|
116
136
|
const https = proxyConfig.https;
|
|
@@ -382,16 +402,16 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
|
|
|
382
402
|
const promisedServer = new Promise((resolve, reject) => {
|
|
383
403
|
const errHandler = async (err) => {
|
|
384
404
|
if (err.code === 'EADDRINUSE') {
|
|
385
|
-
|
|
386
|
-
|
|
405
|
+
reject(new Error(`Port ${port} is in use, please specify a different port or free it.`, { cause: err, }));
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
reject(new Error('Server error', { cause: err, }));
|
|
387
409
|
}
|
|
388
|
-
logger.error('Server error:', err);
|
|
389
|
-
reject(err);
|
|
390
410
|
};
|
|
391
411
|
const connectionHandler = (socket) => {
|
|
392
|
-
logger.info("Server on connection");
|
|
393
412
|
socket.setTimeout(60000);
|
|
394
413
|
socket.setKeepAlive(true, 30000);
|
|
414
|
+
logger.info("Server on connection", socket);
|
|
395
415
|
};
|
|
396
416
|
server.on('error', errHandler /*this handler will call 'reject'*/);
|
|
397
417
|
server.on('connection', connectionHandler);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 解析字符串中的环境变量占位符(格式为 `${ENV_VAR}`),并替换为实际环境变量的值。
|
|
3
|
+
* - 若环境变量不存在,则占位符会被替换为空字符串 `''`。
|
|
4
|
+
* - 若输入为 `null` 或 `undefined`,直接返回原值(不处理)。
|
|
5
|
+
*
|
|
6
|
+
* @param str - 待处理的字符串,可能包含环境变量占位符(如 `${API_URL}`)。支持 `null` 或 `undefined`。
|
|
7
|
+
* @returns 处理后的字符串。若输入为 `null` 或 `undefined`,返回原值;否则返回替换后的新字符串。
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // 环境变量未定义时,替换为 ''
|
|
11
|
+
* resolveEnvValue('${UNDEFINED_VAR}'); // => ''
|
|
12
|
+
*
|
|
13
|
+
* // 混合替换
|
|
14
|
+
* resolveEnvValue('Host: ${HOST}, Port: ${PORT}'); // => 'Host: 127.0.0.1, Port: 3000'(假设环境变量已定义)
|
|
15
|
+
*
|
|
16
|
+
* // 保留 null/undefined
|
|
17
|
+
* resolveEnvValue(null); // => null
|
|
18
|
+
*/
|
|
19
|
+
export default function resolveEnvValue(str: string | null | undefined): string | null | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 解析字符串中的环境变量占位符(格式为 `${ENV_VAR}`),并替换为实际环境变量的值。
|
|
3
|
+
* - 若环境变量不存在,则占位符会被替换为空字符串 `''`。
|
|
4
|
+
* - 若输入为 `null` 或 `undefined`,直接返回原值(不处理)。
|
|
5
|
+
*
|
|
6
|
+
* @param str - 待处理的字符串,可能包含环境变量占位符(如 `${API_URL}`)。支持 `null` 或 `undefined`。
|
|
7
|
+
* @returns 处理后的字符串。若输入为 `null` 或 `undefined`,返回原值;否则返回替换后的新字符串。
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // 环境变量未定义时,替换为 ''
|
|
11
|
+
* resolveEnvValue('${UNDEFINED_VAR}'); // => ''
|
|
12
|
+
*
|
|
13
|
+
* // 混合替换
|
|
14
|
+
* resolveEnvValue('Host: ${HOST}, Port: ${PORT}'); // => 'Host: 127.0.0.1, Port: 3000'(假设环境变量已定义)
|
|
15
|
+
*
|
|
16
|
+
* // 保留 null/undefined
|
|
17
|
+
* resolveEnvValue(null); // => null
|
|
18
|
+
*/
|
|
19
|
+
export default function resolveEnvValue(str) {
|
|
20
|
+
// 1. 处理 null 或 undefined 输入:直接返回原值
|
|
21
|
+
if (str == null) {
|
|
22
|
+
return str;
|
|
23
|
+
}
|
|
24
|
+
// 2. 使用正则表达式全局匹配所有 ${...} 占位符
|
|
25
|
+
// - 正则说明: \${(.+?)}
|
|
26
|
+
// - \${ 匹配字面量 `${`
|
|
27
|
+
// - (.+?) 非贪婪匹配任意字符(环境变量名),直到遇到第一个 `}`
|
|
28
|
+
// - /g 标志确保替换全部匹配项(而非仅第一个)
|
|
29
|
+
// - 替换逻辑: 若 process.env[key] 不存在(undefined/null),则返回 ''
|
|
30
|
+
return str.replace(/\${(.+?)}/g, (_, key) => {
|
|
31
|
+
return process.env[key] ?? '';
|
|
32
|
+
});
|
|
33
|
+
}
|