200字
基于边缘函数(阿里云esa)搭建docker镜像加速
2025-12-03
2025-12-07

匿名用户版本:

// Docker Hub 的官方 Registry 地址
const DOCKER_HUB_REGISTRY = "registry-1.docker.io";

// 用于解析 Docker Hub 认证服务的地址
const DOCKER_HUB_AUTH_SERVICE = "auth.docker.io";

// 处理请求的主函数
async function handleRequest(request) {
  const url = new URL(request.url);

  // 1. 处理 /v2/ 的请求,这是所有操作的入口
  //    例如: GET /v2/ 或 GET /v2/library/nginx/manifests/latest
  if (url.pathname.startsWith('/v2/')) {
    // 修改请求的 Host 和目标 URL,将其指向 Docker Hub
    const newUrl = new URL(url.pathname + url.search, `https://${DOCKER_HUB_REGISTRY}`);
    
    // 创建一个新的请求,复制原始请求的 Method、Headers 和 Body
    const newRequest = new Request(newUrl, {
      method: request.method,
      headers: request.headers,
      body: request.body,
      redirect: 'manual', // 手动处理重定向,对于认证很重要
    });

    // 发送请求到 Docker Hub
    const response = await fetch(newRequest);

    // 2. 处理 401 Unauthorized 响应
    //    当 Docker Hub 需要认证时,会返回 401 和一个 WWW-Authenticate 头
    if (response.status === 401) {
      const authHeader = response.headers.get('WWW-Authenticate');
      if (authHeader && authHeader.startsWith('Bearer ')) {
        // 从 WWW-Authenticate 头中解析出认证参数
        const params = parseAuthParams(authHeader);

        // 构建获取 Token 的 URL
        const tokenUrl = new URL(`https://${DOCKER_HUB_AUTH_SERVICE}/token`);
        tokenUrl.searchParams.set('service', params.service);
        tokenUrl.searchParams.set('scope', params.scope);

        // 请求 Token
        const tokenResponse = await fetch(tokenUrl);
        if (!tokenResponse.ok) {
          return new Response('Failed to get auth token', { status: 502 });
        }
        const { token } = await tokenResponse.json();

        // 使用获取到的 Token 重新发送原始请求
        const authedRequest = new Request(newRequest);
        authedRequest.headers.set('Authorization', `Bearer ${token}`);
        const authedResponse = await fetch(authedRequest);

        // 返回带有 CORS 头的最终响应,允许 Docker 客户端跨域访问
        return addCorsHeaders(authedResponse);
      }
    }

    // 3. 处理 307/308 临时重定向
    //    Docker Hub 在某些情况下(如获取 manifest)会返回重定向
    if (response.status === 307 || response.status === 308) {
      const location = response.headers.get('Location');
      if (location) {
        // 递归调用自己来处理重定向,确保所有请求都经过我们的代理
        const redirectResponse = await fetch(new URL(location, request.url), {
          method: request.method,
          headers: request.headers,
          body: request.body,
        });
        return addCorsHeaders(redirectResponse);
      }
    }
    
    // 4. 对于其他所有成功或失败的响应,直接返回
    return addCorsHeaders(response);
  }

  // 如果请求不是 /v2/ 开头,返回一个简单的提示页面
  return new Response(`This is a Docker registry proxy. Please use it in your Docker daemon configuration.\n\nYour request path: ${url.pathname}`, {
    status: 400,
    headers: { 'Content-Type': 'text/plain' }
  });
}

// 辅助函数:解析 WWW-Authenticate 头中的参数
function parseAuthParams(authHeader) {
  const paramsStr = authHeader.substring('Bearer '.length);
  const params = {};
  paramsStr.split(',').forEach(pair => {
    const [key, value] = pair.trim().split('=');
    params[key] = value.replace(/"/g, '');
  });
  return params;
}

// 辅助函数:为响应添加 CORS 头
function addCorsHeaders(response) {
  const newResponse = new Response(response.body, response);
  newResponse.headers.set('Access-Control-Allow-Origin', '*');
  newResponse.headers.set('Access-Control-Allow-Methods', 'GET, HEAD, POST, PUT, DELETE, OPTIONS');
  newResponse.headers.set('Access-Control-Allow-Headers', 'Authorization, Content-Type, User-Agent');
  return newResponse;
}

// ESA 边缘函数的入口点
export default {
  async fetch(request) {
    return handleRequest(request);
  }
};

登陆用户版本:

// Docker Hub 的官方 Registry 地址
const DOCKER_HUB_REGISTRY = "registry-1.docker.io";

// !!! 在这里填入你刚刚生成的 Docker Hub Access Token !!!
const DOCKER_HUB_TOKEN = "你的Docker Hub Token"; // 例如: dckr_pat_xxxxxxxxxxxx

// 处理请求的主函数
async function handleRequest(request) {
  const url = new URL(request.url);

  // 只处理 /v2/ 路径下的请求
  if (url.pathname.startsWith('/v2/')) {
    // 构造新的 URL,目标指向 Docker Hub
    const newUrl = `https://${DOCKER_HUB_REGISTRY}${url.pathname}${url.search}`;

    // 复制原始请求的头
    const newHeaders = new Headers(request.headers);

    // 智能认证:
    // 如果原始请求没有 Authorization 头,我们就自动加上我们的 Token
    if (!newHeaders.has('Authorization')) {
      newHeaders.set('Authorization', `Bearer ${DOCKER_HUB_TOKEN}`);
    }

    // 创建一个新的请求,使用修改后的头
    const newRequest = new Request(newUrl, {
      method: request.method,
      headers: newHeaders,
      body: request.body,
      redirect: 'follow',
    });

    try {
      const response = await fetch(newRequest);
      return addCorsHeaders(response);
    } catch (e) {
      return new Response(`Proxy fetch error: ${e.message}`, { status: 502 });
    }
  }

  return new Response(`This is a Docker registry proxy. Please use it in your Docker daemon configuration.\n\nYour request path: ${url.pathname}`, {
    status: 400,
    headers: { 'Content-Type': 'text/plain' }
  });
}

// 辅助函数:为响应添加 CORS 头
function addCorsHeaders(response) {
  const newResponse = new Response(response.body, response);
  newResponse.headers.set('Access-Control-Allow-Origin', '*');
  newResponse.headers.set('Access-Control-Allow-Methods', 'GET, HEAD, POST, PUT, DELETE, OPTIONS');
  newResponse.headers.set('Access-Control-Allow-Headers', 'Authorization, Content-Type, User-Agent');
  return newResponse;
}

// ESA 边缘函数的入口点
export default {
  async fetch(request) {
    return handleRequest(request);
  }
};

评论