maven-proxy 1.0.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.
- package/README.md +420 -0
- package/bin/maven-proxy.js +573 -0
- package/package.json +54 -0
- package/scripts/truststore.js +96 -0
- package/src/cache/cache-path.js +50 -0
- package/src/cache/downloader.js +350 -0
- package/src/cert/cert-manager.js +194 -0
- package/src/cert/truststore-utils.js +289 -0
- package/src/common/console-log-file.js +62 -0
- package/src/common/daily-log-file.js +79 -0
- package/src/common/domain-match.js +39 -0
- package/src/common/download-log-writer.js +27 -0
- package/src/common/ecosystem.js +64 -0
- package/src/common/java-home.js +328 -0
- package/src/config/config.js +213 -0
- package/src/index.js +93 -0
- package/src/proxy/proxy-connect-handler.js +173 -0
- package/src/proxy/proxy-http-handler.js +187 -0
- package/src/proxy/proxy-server.js +35 -0
- package/src/proxy/upstream-proxy.js +236 -0
- package/src/repo/repo-server.js +120 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
function runCommandCapture(command, args) {
|
|
7
|
+
const result = spawnSync(command, args, {
|
|
8
|
+
shell: false,
|
|
9
|
+
encoding: "utf8",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (result.error || result.status !== 0) {
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return String(result.stdout || "").trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isFile(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
return fs.statSync(filePath).isFile();
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isDirectory(dirPath) {
|
|
28
|
+
try {
|
|
29
|
+
return fs.statSync(dirPath).isDirectory();
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isLikelyJavaHome(homePath, platform) {
|
|
36
|
+
const javaName = platform === "win32" ? "java.exe" : "java";
|
|
37
|
+
|
|
38
|
+
if (!isFile(path.join(homePath, "bin", javaName))) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (isFile(path.join(homePath, "release"))) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isFile(path.join(homePath, "lib", "security", "cacerts"))) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (isFile(path.join(homePath, "jre", "lib", "security", "cacerts"))) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function safeRealpath(targetPath) {
|
|
58
|
+
try {
|
|
59
|
+
return fs.realpathSync(targetPath);
|
|
60
|
+
} catch {
|
|
61
|
+
return targetPath;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function deriveJavaHomeFromJavaBin(javaBinPath) {
|
|
66
|
+
const resolved = safeRealpath(javaBinPath);
|
|
67
|
+
const executableName = path.basename(resolved).toLowerCase();
|
|
68
|
+
|
|
69
|
+
if (executableName !== "java" && executableName !== "java.exe") {
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const binDir = path.dirname(resolved);
|
|
74
|
+
if (path.basename(binDir).toLowerCase() !== "bin") {
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const javaHome = path.dirname(binDir);
|
|
79
|
+
if (!isDirectory(javaHome)) {
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const platform = os.platform();
|
|
84
|
+
return isLikelyJavaHome(javaHome, platform) ? javaHome : "";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function normalizeConfiguredJavaHome(configured, platform) {
|
|
88
|
+
if (!configured) {
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const resolved = safeRealpath(configured);
|
|
93
|
+
if (isDirectory(resolved)) {
|
|
94
|
+
if (isLikelyJavaHome(resolved, platform)) {
|
|
95
|
+
return resolved;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (path.basename(resolved).toLowerCase() === "bin") {
|
|
99
|
+
const base = path.dirname(resolved);
|
|
100
|
+
if (isLikelyJavaHome(base, platform)) {
|
|
101
|
+
return base;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isFile(resolved)) {
|
|
109
|
+
return deriveJavaHomeFromJavaBin(resolved);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function detectFromJavaCommand(platform) {
|
|
116
|
+
const command = platform === "win32" ? "where" : "which";
|
|
117
|
+
const output = runCommandCapture(command, ["java"]);
|
|
118
|
+
if (!output) {
|
|
119
|
+
return "";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const firstPath = output
|
|
123
|
+
.split(/\r?\n/g)
|
|
124
|
+
.map((line) => line.trim())
|
|
125
|
+
.find(Boolean);
|
|
126
|
+
|
|
127
|
+
if (!firstPath) {
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return deriveJavaHomeFromJavaBin(firstPath);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function detectFromLinuxCommonPaths() {
|
|
135
|
+
const directCandidates = [
|
|
136
|
+
"/usr/bin/java",
|
|
137
|
+
"/usr/local/bin/java",
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const javaPath of directCandidates) {
|
|
141
|
+
if (!isFile(javaPath)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const home = deriveJavaHomeFromJavaBin(javaPath);
|
|
146
|
+
if (home) {
|
|
147
|
+
return home;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const roots = [
|
|
152
|
+
"/usr/lib/jvm",
|
|
153
|
+
"/usr/java",
|
|
154
|
+
"/opt/java",
|
|
155
|
+
"/opt/jdk",
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
for (const root of roots) {
|
|
159
|
+
if (!isDirectory(root)) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let children = [];
|
|
164
|
+
try {
|
|
165
|
+
children = fs.readdirSync(root, { withFileTypes: true })
|
|
166
|
+
.filter((entry) => entry.isDirectory())
|
|
167
|
+
.map((entry) => entry.name)
|
|
168
|
+
.sort((a, b) => b.localeCompare(a));
|
|
169
|
+
} catch {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const child of children) {
|
|
174
|
+
const candidate = path.join(root, child);
|
|
175
|
+
if (isFile(path.join(candidate, "bin", "java"))) {
|
|
176
|
+
return candidate;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return "";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function detectFromMacJavaHomeCommand() {
|
|
185
|
+
const output = runCommandCapture("/usr/libexec/java_home", []);
|
|
186
|
+
if (!output) {
|
|
187
|
+
return "";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return isDirectory(output) ? output : "";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function detectFromWindowsCommonPaths() {
|
|
194
|
+
const roots = [
|
|
195
|
+
"C:\\Program Files\\Java",
|
|
196
|
+
"C:\\Program Files (x86)\\Java",
|
|
197
|
+
"C:\\Program Files\\Eclipse Adoptium",
|
|
198
|
+
"C:\\Program Files\\Microsoft",
|
|
199
|
+
"C:\\Program Files\\Amazon Corretto",
|
|
200
|
+
"C:\\Program Files\\Zulu",
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
for (const root of roots) {
|
|
204
|
+
if (!isDirectory(root)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let children = [];
|
|
209
|
+
try {
|
|
210
|
+
children = fs.readdirSync(root, { withFileTypes: true })
|
|
211
|
+
.filter((entry) => entry.isDirectory())
|
|
212
|
+
.map((entry) => entry.name)
|
|
213
|
+
.sort((a, b) => b.localeCompare(a));
|
|
214
|
+
} catch {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (const child of children) {
|
|
219
|
+
const base = path.join(root, child);
|
|
220
|
+
const candidates = [base, path.join(base, "jre")];
|
|
221
|
+
for (const candidate of candidates) {
|
|
222
|
+
if (isFile(path.join(candidate, "bin", "java.exe"))) {
|
|
223
|
+
return candidate;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return "";
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function detectAutoJavaHome(platform) {
|
|
233
|
+
if (platform === "darwin") {
|
|
234
|
+
const fromMacCommand = detectFromMacJavaHomeCommand();
|
|
235
|
+
if (fromMacCommand) {
|
|
236
|
+
return {
|
|
237
|
+
javaHome: fromMacCommand,
|
|
238
|
+
source: "auto-macos-java-home",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const fromCommand = detectFromJavaCommand(platform);
|
|
243
|
+
if (fromCommand) {
|
|
244
|
+
return {
|
|
245
|
+
javaHome: fromCommand,
|
|
246
|
+
source: "auto-java-command",
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return { javaHome: "", source: "none" };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (platform === "win32") {
|
|
254
|
+
const fromCommand = detectFromJavaCommand(platform);
|
|
255
|
+
if (fromCommand) {
|
|
256
|
+
return {
|
|
257
|
+
javaHome: fromCommand,
|
|
258
|
+
source: "auto-java-command",
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const fromWindowsPath = detectFromWindowsCommonPaths();
|
|
263
|
+
if (fromWindowsPath) {
|
|
264
|
+
return {
|
|
265
|
+
javaHome: fromWindowsPath,
|
|
266
|
+
source: "auto-windows-common-path",
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return { javaHome: "", source: "none" };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const fromCommand = detectFromJavaCommand(platform);
|
|
274
|
+
if (fromCommand) {
|
|
275
|
+
return {
|
|
276
|
+
javaHome: fromCommand,
|
|
277
|
+
source: "auto-java-command",
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (platform === "linux") {
|
|
282
|
+
const fromLinuxPath = detectFromLinuxCommonPaths();
|
|
283
|
+
if (fromLinuxPath) {
|
|
284
|
+
return {
|
|
285
|
+
javaHome: fromLinuxPath,
|
|
286
|
+
source: "auto-linux-common-path",
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return { javaHome: "", source: "none" };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function detectJavaHome(configuredJavaHome = "", platform = os.platform()) {
|
|
295
|
+
const configured = String(configuredJavaHome || "").trim();
|
|
296
|
+
const normalizedConfigured = normalizeConfiguredJavaHome(configured, platform);
|
|
297
|
+
|
|
298
|
+
if (normalizedConfigured) {
|
|
299
|
+
return {
|
|
300
|
+
javaHome: normalizedConfigured,
|
|
301
|
+
source: "env",
|
|
302
|
+
configuredJavaHome: configured,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const autoDetected = detectAutoJavaHome(platform);
|
|
307
|
+
if (autoDetected.javaHome) {
|
|
308
|
+
return {
|
|
309
|
+
javaHome: autoDetected.javaHome,
|
|
310
|
+
source: configured ? "auto-fallback" : autoDetected.source,
|
|
311
|
+
configuredJavaHome: configured,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (configured) {
|
|
316
|
+
return {
|
|
317
|
+
javaHome: configured,
|
|
318
|
+
source: "env-missing",
|
|
319
|
+
configuredJavaHome: configured,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
javaHome: "",
|
|
325
|
+
source: "none",
|
|
326
|
+
configuredJavaHome: "",
|
|
327
|
+
};
|
|
328
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
import { detectJavaHome } from "../common/java-home.js";
|
|
6
|
+
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const userConfigDir = path.resolve(os.homedir(), "maven-proxy");
|
|
9
|
+
const defaultUserConfigPath = path.join(userConfigDir, "config");
|
|
10
|
+
|
|
11
|
+
function normalizeConfigMode(value) {
|
|
12
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
13
|
+
|
|
14
|
+
if (["dev", "development", "project"].includes(normalized)) {
|
|
15
|
+
return "development";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (["user", "home", "global", "production", "prod"].includes(normalized)) {
|
|
19
|
+
return "user";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isProjectWorkspace(dirPath) {
|
|
26
|
+
const packageJsonPath = path.resolve(dirPath, "package.json");
|
|
27
|
+
const entryPath = path.resolve(dirPath, "src/index.js");
|
|
28
|
+
|
|
29
|
+
if (!fs.existsSync(packageJsonPath) || !fs.existsSync(entryPath)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
35
|
+
return packageJson?.name === "maven-proxy";
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveConfigMode() {
|
|
42
|
+
const forced = normalizeConfigMode(process.env.MAVEN_PROXY_CONFIG_MODE);
|
|
43
|
+
if (forced) {
|
|
44
|
+
return forced;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return isProjectWorkspace(cwd) ? "development" : "user";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function resolveConfigFilePath(configMode) {
|
|
51
|
+
const envPath = String(process.env.MAVEN_PROXY_CONFIG_FILE || "").trim();
|
|
52
|
+
if (envPath) {
|
|
53
|
+
return path.isAbsolute(envPath) ? envPath : path.resolve(cwd, envPath);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (configMode === "development") {
|
|
57
|
+
const devCandidates = [
|
|
58
|
+
path.resolve(cwd, ".env"),
|
|
59
|
+
path.resolve(cwd, ".evn"),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
for (const candidate of devCandidates) {
|
|
63
|
+
if (fs.existsSync(candidate)) {
|
|
64
|
+
return candidate;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return fs.existsSync(defaultUserConfigPath) ? defaultUserConfigPath : "";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function loadEnvFromFile(filePath) {
|
|
75
|
+
if (filePath && fs.existsSync(filePath)) {
|
|
76
|
+
dotenv.config({ path: filePath, override: false });
|
|
77
|
+
return filePath;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const configMode = resolveConfigMode();
|
|
84
|
+
const resolvedConfigFilePath = resolveConfigFilePath(configMode);
|
|
85
|
+
const loadedConfigFile = loadEnvFromFile(resolvedConfigFilePath);
|
|
86
|
+
const configBaseDir = configMode === "development"
|
|
87
|
+
? cwd
|
|
88
|
+
: (resolvedConfigFilePath ? path.dirname(resolvedConfigFilePath) : userConfigDir);
|
|
89
|
+
const javaHomeResolution = detectJavaHome(process.env.JAVA_HOME || "");
|
|
90
|
+
|
|
91
|
+
function toBool(value, defaultValue) {
|
|
92
|
+
if (value == null || value === "") {
|
|
93
|
+
return defaultValue;
|
|
94
|
+
}
|
|
95
|
+
return ["1", "true", "yes", "on"].includes(String(value).toLowerCase());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function toInt(value, defaultValue) {
|
|
99
|
+
const parsed = Number.parseInt(value, 10);
|
|
100
|
+
return Number.isFinite(parsed) ? parsed : defaultValue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function toList(value, defaultValue = []) {
|
|
104
|
+
if (!value) {
|
|
105
|
+
return defaultValue;
|
|
106
|
+
}
|
|
107
|
+
return String(value)
|
|
108
|
+
.split(",")
|
|
109
|
+
.map((item) => item.trim())
|
|
110
|
+
.filter(Boolean);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normalizeRepoList(value, defaultValue = []) {
|
|
114
|
+
const raw = toList(value, defaultValue);
|
|
115
|
+
return raw
|
|
116
|
+
.map((item) => String(item).trim())
|
|
117
|
+
.filter((item) => /^https?:\/\//i.test(item))
|
|
118
|
+
.map((item) => item.replace(/\/+$/, ""));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizeProxyUrl(value) {
|
|
122
|
+
if (!value) {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const trimmed = String(value).trim();
|
|
127
|
+
if (!trimmed) {
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed)) {
|
|
132
|
+
return trimmed;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return `http://${trimmed}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractHostsFromUrls(urls) {
|
|
139
|
+
const hosts = [];
|
|
140
|
+
|
|
141
|
+
for (const rawUrl of urls) {
|
|
142
|
+
try {
|
|
143
|
+
hosts.push(new URL(rawUrl).hostname.toLowerCase());
|
|
144
|
+
} catch {
|
|
145
|
+
// ignore invalid URL
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return [...new Set(hosts)];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const defaultRepoFallbackRepos = [
|
|
153
|
+
"https://repo1.maven.org/maven2",
|
|
154
|
+
"https://jitpack.io",
|
|
155
|
+
"https://plugins.gradle.org/m2",
|
|
156
|
+
"https://maven.google.com",
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const repoFallbackRepos = normalizeRepoList(
|
|
160
|
+
process.env.REPO_FALLBACK_REPOS,
|
|
161
|
+
defaultRepoFallbackRepos,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const defaultMavenRepoDomains = [
|
|
165
|
+
"repo1.maven.org",
|
|
166
|
+
"repo.maven.apache.org",
|
|
167
|
+
"jitpack.io",
|
|
168
|
+
"plugins.gradle.org",
|
|
169
|
+
"maven.google.com",
|
|
170
|
+
...extractHostsFromUrls(repoFallbackRepos),
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
const cacheDir = path.resolve(configBaseDir, process.env.CACHE_DIR || "data/cache");
|
|
174
|
+
|
|
175
|
+
export const config = {
|
|
176
|
+
configMode,
|
|
177
|
+
configBaseDir,
|
|
178
|
+
loadedConfigFile,
|
|
179
|
+
defaultUserConfigPath,
|
|
180
|
+
proxyPort: toInt(process.env.PROXY_PORT, 8080),
|
|
181
|
+
repoPort: toInt(process.env.REPO_PORT, 8081),
|
|
182
|
+
cacheDir,
|
|
183
|
+
mavenCacheDir: path.resolve(cacheDir, "maven"),
|
|
184
|
+
npmCacheDir: path.resolve(cacheDir, "npm"),
|
|
185
|
+
genericCacheDir: path.resolve(cacheDir, "generic"),
|
|
186
|
+
enableHttpsProxy: toBool(process.env.ENABLE_HTTPS_PROXY, true),
|
|
187
|
+
httpsMitmDomains: toList(process.env.HTTPS_MITM_DOMAINS, ["repo1.maven.org", "repo.maven.apache.org", "registry.npmjs.org"]),
|
|
188
|
+
npmRegistryDomains: toList(process.env.NPM_REGISTRY_DOMAINS, ["registry.npmjs.org", "registry.npmmirror.com", "npm.pkg.github.com"]),
|
|
189
|
+
mavenRepoDomains: toList(process.env.MAVEN_REPO_DOMAINS, [...new Set(defaultMavenRepoDomains)]),
|
|
190
|
+
multiThreadDomains: toList(process.env.MULTI_THREAD_DOMAINS, ["repo1.maven.org"]),
|
|
191
|
+
multiThreadCount: Math.max(1, toInt(process.env.MULTI_THREAD_COUNT, 4)),
|
|
192
|
+
multiThreadMinSizeBytes: Math.max(0, toInt(process.env.MULTI_THREAD_MIN_SIZE_BYTES, 1024 * 1024)),
|
|
193
|
+
downloadTimeoutMs: Math.max(1000, toInt(process.env.DOWNLOAD_TIMEOUT_MS, 60000)),
|
|
194
|
+
downloadLogDir: path.resolve(configBaseDir, process.env.DOWNLOAD_LOG_DIR || "data/logs/downloads"),
|
|
195
|
+
logRetentionDays: Math.max(1, toInt(process.env.LOG_RETENTION_DAYS, 7)),
|
|
196
|
+
certDir: path.resolve(configBaseDir, process.env.CERT_DIR || "data/certs"),
|
|
197
|
+
rootCertPath: path.resolve(configBaseDir, process.env.ROOT_CERT_PATH || "data/certs/root-ca.crt"),
|
|
198
|
+
rootKeyPath: path.resolve(configBaseDir, process.env.ROOT_KEY_PATH || "data/certs/root-ca.key.pem"),
|
|
199
|
+
leafCertDir: path.resolve(configBaseDir, process.env.LEAF_CERT_DIR || "data/certs/leaf"),
|
|
200
|
+
trustStorePath: path.resolve(configBaseDir, process.env.TRUST_STORE_PATH || "data/certs/proxy-truststore.jks"),
|
|
201
|
+
trustStoreAlias: process.env.TRUST_STORE_ALIAS || "maven-proxy-root-ca",
|
|
202
|
+
trustStorePassword: process.env.TRUST_STORE_PASSWORD || "changeit",
|
|
203
|
+
javaHome: javaHomeResolution.javaHome,
|
|
204
|
+
javaHomeSource: javaHomeResolution.source,
|
|
205
|
+
javaHomeConfigured: javaHomeResolution.configuredJavaHome || "",
|
|
206
|
+
httpsPassthroughForUnmatched: toBool(process.env.HTTPS_PASSTHROUGH_FOR_UNMATCHED, true),
|
|
207
|
+
upstreamProxyUrl: normalizeProxyUrl(process.env.UPSTREAM_PROXY_URL || process.env.ALL_PROXY || process.env.all_proxy || ""),
|
|
208
|
+
upstreamHttpProxyUrl: normalizeProxyUrl(process.env.UPSTREAM_HTTP_PROXY_URL || process.env.HTTP_PROXY || process.env.http_proxy || ""),
|
|
209
|
+
upstreamHttpsProxyUrl: normalizeProxyUrl(process.env.UPSTREAM_HTTPS_PROXY_URL || process.env.HTTPS_PROXY || process.env.https_proxy || ""),
|
|
210
|
+
upstreamNoProxyDomains: toList(process.env.UPSTREAM_NO_PROXY || process.env.NO_PROXY || process.env.no_proxy || ""),
|
|
211
|
+
upstreamIgnoreDomains: toList(process.env.UPSTREAM_IGNORE_DOMAINS || ""),
|
|
212
|
+
repoFallbackRepos,
|
|
213
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { config } from "./config/config.js";
|
|
3
|
+
import { matchesDomain } from "./common/domain-match.js";
|
|
4
|
+
import { CertManager } from "./cert/cert-manager.js";
|
|
5
|
+
import { Downloader } from "./cache/downloader.js";
|
|
6
|
+
import { startProxyServer } from "./proxy/proxy-server.js";
|
|
7
|
+
import { startRepoServer } from "./repo/repo-server.js";
|
|
8
|
+
import { getTrustStoreCommands } from "./cert/truststore-utils.js";
|
|
9
|
+
import { UpstreamProxyManager } from "./proxy/upstream-proxy.js";
|
|
10
|
+
import { installConsoleLogFileMirror, installGlobalErrorLogging } from "./common/console-log-file.js";
|
|
11
|
+
|
|
12
|
+
installConsoleLogFileMirror({
|
|
13
|
+
logDir: config.downloadLogDir,
|
|
14
|
+
retentionDays: config.logRetentionDays,
|
|
15
|
+
});
|
|
16
|
+
installGlobalErrorLogging();
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
await fs.promises.mkdir(config.cacheDir, { recursive: true });
|
|
20
|
+
await fs.promises.mkdir(config.mavenCacheDir, { recursive: true });
|
|
21
|
+
await fs.promises.mkdir(config.npmCacheDir, { recursive: true });
|
|
22
|
+
await fs.promises.mkdir(config.genericCacheDir, { recursive: true });
|
|
23
|
+
await fs.promises.mkdir(config.downloadLogDir, { recursive: true });
|
|
24
|
+
await fs.promises.mkdir(config.certDir, { recursive: true });
|
|
25
|
+
await fs.promises.mkdir(config.leafCertDir, { recursive: true });
|
|
26
|
+
|
|
27
|
+
const certManager = new CertManager(config);
|
|
28
|
+
await certManager.init();
|
|
29
|
+
|
|
30
|
+
for (const pattern of config.httpsMitmDomains) {
|
|
31
|
+
if (!pattern.includes("*")) {
|
|
32
|
+
await certManager.getOrCreateLeaf(pattern);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const upstreamProxyManager = new UpstreamProxyManager(config, matchesDomain);
|
|
37
|
+
|
|
38
|
+
const downloader = new Downloader(config, matchesDomain, upstreamProxyManager);
|
|
39
|
+
|
|
40
|
+
const { proxyServer, mitmHttpServer } = startProxyServer(
|
|
41
|
+
config,
|
|
42
|
+
certManager,
|
|
43
|
+
downloader,
|
|
44
|
+
matchesDomain,
|
|
45
|
+
upstreamProxyManager,
|
|
46
|
+
);
|
|
47
|
+
const repoServer = startRepoServer(config, downloader);
|
|
48
|
+
|
|
49
|
+
const trustCommands = getTrustStoreCommands(config);
|
|
50
|
+
|
|
51
|
+
console.log("[maven-proxy] started");
|
|
52
|
+
console.log(`[maven-proxy] config mode: ${config.configMode}`);
|
|
53
|
+
console.log(`[maven-proxy] config file: ${config.loadedConfigFile || "(none)"}`);
|
|
54
|
+
console.log(`[maven-proxy] config base: ${config.configBaseDir}`);
|
|
55
|
+
if (config.configMode === "user") {
|
|
56
|
+
console.log(`[maven-proxy] default user config: ${config.defaultUserConfigPath}`);
|
|
57
|
+
}
|
|
58
|
+
console.log(`[maven-proxy] proxy port: ${config.proxyPort}`);
|
|
59
|
+
console.log(`[maven-proxy] repo port: ${config.repoPort}`);
|
|
60
|
+
console.log(`[maven-proxy] cache dir : ${config.cacheDir}`);
|
|
61
|
+
console.log(`[maven-proxy] cache maven: ${config.mavenCacheDir}`);
|
|
62
|
+
console.log(`[maven-proxy] cache npm : ${config.npmCacheDir}`);
|
|
63
|
+
console.log(`[maven-proxy] cache other: ${config.genericCacheDir}`);
|
|
64
|
+
console.log(`[maven-proxy] download log: ${config.downloadLogDir}`);
|
|
65
|
+
console.log(`[maven-proxy] log retention days: ${config.logRetentionDays}`);
|
|
66
|
+
console.log(`[maven-proxy] root cert : ${config.rootCertPath}`);
|
|
67
|
+
console.log(`[maven-proxy] repo fallback repos: ${(config.repoFallbackRepos || []).join(",") || "(none)"}`);
|
|
68
|
+
if (config.upstreamProxyUrl || config.upstreamHttpProxyUrl || config.upstreamHttpsProxyUrl) {
|
|
69
|
+
console.log(`[maven-proxy] upstream proxy (generic): ${config.upstreamProxyUrl || "(none)"}`);
|
|
70
|
+
console.log(`[maven-proxy] upstream proxy (http) : ${config.upstreamHttpProxyUrl || "(none)"}`);
|
|
71
|
+
console.log(`[maven-proxy] upstream proxy (https) : ${config.upstreamHttpsProxyUrl || "(none)"}`);
|
|
72
|
+
console.log(`[maven-proxy] upstream no-proxy : ${(config.upstreamNoProxyDomains || []).join(",") || "(none)"}`);
|
|
73
|
+
console.log(`[maven-proxy] upstream ignore-domains : ${(config.upstreamIgnoreDomains || []).join(",") || "(none)"}`);
|
|
74
|
+
}
|
|
75
|
+
console.log("[maven-proxy] trust store command (copy):");
|
|
76
|
+
console.log(trustCommands.copyCmd);
|
|
77
|
+
console.log("[maven-proxy] trust store command (import):");
|
|
78
|
+
console.log(trustCommands.importCmd);
|
|
79
|
+
|
|
80
|
+
const shutdown = () => {
|
|
81
|
+
proxyServer.close();
|
|
82
|
+
mitmHttpServer.close();
|
|
83
|
+
repoServer.close();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
process.on("SIGINT", shutdown);
|
|
87
|
+
process.on("SIGTERM", shutdown);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
main().catch((error) => {
|
|
91
|
+
console.error("[maven-proxy] fatal error:", error);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|