该功能疑似被eo拦截,无法正常使用,但是部署在CF上可以正常使用
// Docker Hub 的官方 Registry 地址
const DOCKER_HUB_REGISTRY = "registry-1.docker.io";
const DOCKER_HUB_AUTH_SERVICE = "auth.docker.io";
// 带有重试机制的 fetch 函数
async function fetchWithRetry(request, retries = 3) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 8000);
const response = await fetch(request, {
signal: controller.signal,
});
clearTimeout(timeoutId);
return response;
} catch (error) {
if (retries > 0 && (error.name === 'AbortError' || error.message.includes('timeout'))) {
console.log(`Fetch failed, retrying... (${retries} attempts left)`);
return fetchWithRetry(request, retries - 1);
}
throw error;
}
}
// 处理请求的主函数
async function handleRequest(request) {
const url = new URL(request.url);
if (url.pathname.startsWith('/v2/')) {
const newUrl = new URL(url.pathname + url.search, `https://${DOCKER_HUB_REGISTRY}`);
const newRequest = new Request(newUrl, {
method: request.method,
headers: request.headers,
body: request.body,
redirect: 'manual',
});
newRequest.headers.delete('host');
try {
let response = await fetchWithRetry(newRequest);
// 处理401未授权响应
if (response.status === 401) {
const authHeader = response.headers.get('Www-Authenticate');
if (authHeader && authHeader.startsWith('Bearer')) {
const authParams = parseAuthHeader(authHeader);
const authUrl = new URL(`https://${DOCKER_HUB_AUTH_SERVICE}/token`);
authUrl.searchParams.set('service', authParams.service || 'registry.docker.io');
if (authParams.scope) {
authUrl.searchParams.set('scope', authParams.scope);
}
console.log(`Requesting token from: ${authUrl.toString()}`);
const authResponse = await fetchWithRetry(new Request(authUrl.toString()));
if (!authResponse.ok) {
const bodyText = await authResponse.text();
console.error(`Auth failed with status ${authResponse.status}: ${bodyText}`);
throw new Error(`Authentication failed: ${authResponse.status} ${authResponse.statusText}`);
}
// 安全地解析 JSON
let tokenData;
try {
tokenData = await authResponse.json();
} catch (e) {
console.error(`Failed to parse auth response JSON: ${e.message}`);
throw new Error('Invalid authentication response from server.');
}
// 兼容不同的 token 字段名
const token = tokenData.token || tokenData.access_token;
if (!token) {
console.error('Token not found in auth response:', tokenData);
throw new Error('Could not find token in authentication response.');
}
console.log('Successfully obtained token.');
const authedRequest = new Request(newRequest);
authedRequest.headers.set('Authorization', `Bearer ${token}`);
response = await fetchWithRetry(authedRequest);
}
}
// 清理响应头
const newResponse = new Response(response.body, response);
newResponse.headers.delete('set-cookie');
newResponse.headers.delete('x-amz-cf-id');
newResponse.headers.delete('x-cache-status');
return newResponse;
} catch (error) {
// 捕获所有未处理的错误,并返回一个明确的 502 错误
console.error(`Proxy error for ${url.pathname}: ${error.message}`);
console.error(error.stack); // 打印详细的错误堆栈
return new Response(`Proxy upstream error: ${error.message}`, { status: 502 });
}
}
return new Response('Not Found. This is a Docker Registry proxy.', { status: 404 });
}
function parseAuthHeader(header) {
const result = {};
const parts = header.split(' ')[1] || '';
parts.split(',').forEach(part => {
const [key, value] = part.trim().split('=');
if (key && value) {
result[key] = value.replace(/"/g, '');
}
});
return result;
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});