From cba7511fb9c93875d26d07342d93473fff914c00 Mon Sep 17 00:00:00 2001 From: "shanming.sun" Date: Sun, 9 Jun 2024 00:24:38 +0800 Subject: [PATCH] add worker --- README.md | 4 + worker.js | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 344 insertions(+) create mode 100644 worker.js diff --git a/README.md b/README.md index c77a5b46..1f85d3e2 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,8 @@ docker pull registry.cn-beijing.aliyuncs.com/ijx-public/nginx:alpine docker tag registry.cn-beijing.aliyuncs.com/ijx-public/nginx:alpine nginx:alpine ``` +## 使用 cf worker +[worker 怎么用可以参考这里](https://github.com/yaming116/npm-registry-worker) + +[docker registry worker 脚本在这里](./worker.js) diff --git a/worker.js b/worker.js new file mode 100644 index 00000000..2edca706 --- /dev/null +++ b/worker.js @@ -0,0 +1,340 @@ +'use strict' + +const hub_host = 'registry-1.docker.io' +const auth_url = 'https://auth.docker.io' + +const workers_host = '你的自定义域名' +const workers_url = `https://${workers_host}` + +const HTML = ` + + + + + + + 镜像加速说明 + + + +
+

镜像加速说明

+

为了加速镜像拉取,使用以下命令设置registry mirror

+

+sudo tee /etc/docker/daemon.json <<EOF
+{
+    "registry-mirrors": ["https://{workers_host}"]
+}
+EOF
+

用法:

+

原拉取镜像命令:

+

+docker pull library/alpine:latest
+

加速拉取镜像命令:

+

+docker pull {workers_host}/library/alpine:latest
+ +
+ + + + + + +` +/** @type {RequestInit} */ +const PREFLIGHT_INIT = { + status: 204, + headers: new Headers({ + 'access-control-allow-origin': '*', + 'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS', + 'access-control-max-age': '1728000', + }), +} + +/** + * @param {any} body + * @param {number} status + * @param {Object} headers + */ +function makeRes(body, status = 200, headers = {}) { + headers['access-control-allow-origin'] = '*' + return new Response(body, {status, headers}) +} + + +/** + * @param {string} urlStr + */ +function newUrl(urlStr) { + try { + return new URL(urlStr) + } catch (err) { + return null + } +} + + +addEventListener('fetch', e => { + const ret = fetchHandler(e) + .catch(err => makeRes('cfworker error:\n' + err.stack, 502)) + e.respondWith(ret) +}) + + +/** + * @param {FetchEvent} e + */ +async function fetchHandler(e) { + const getReqHeader = (key) => e.request.headers.get(key); + + let url = new URL(e.request.url); + + if (url.pathname === '/') { + // Fetch and return the home page HTML content with replacement + + let text = HTML.replace(/{workers_host}/g, workers_host); + return new Response(text, { + status: 200, + headers: { + 'Content-Type': 'text/html' + } + }); + } + + if (url.pathname === '/token') { + let token_parameter = { + headers: { + 'Host': 'auth.docker.io', + 'User-Agent': getReqHeader("User-Agent"), + 'Accept': getReqHeader("Accept"), + 'Accept-Language': getReqHeader("Accept-Language"), + 'Accept-Encoding': getReqHeader("Accept-Encoding"), + 'Connection': 'keep-alive', + 'Cache-Control': 'max-age=0' + } + }; + let token_url = auth_url + url.pathname + url.search + return fetch(new Request(token_url, e.request), token_parameter) + } + + url.hostname = hub_host; + + let parameter = { + headers: { + 'Host': hub_host, + 'User-Agent': getReqHeader("User-Agent"), + 'Accept': getReqHeader("Accept"), + 'Accept-Language': getReqHeader("Accept-Language"), + 'Accept-Encoding': getReqHeader("Accept-Encoding"), + 'Connection': 'keep-alive', + 'Cache-Control': 'max-age=0' + }, + cacheTtl: 3600 + }; + + if (e.request.headers.has("Authorization")) { + parameter.headers.Authorization = getReqHeader("Authorization"); + } + + let original_response = await fetch(new Request(url, e.request), parameter) + let original_response_clone = original_response.clone(); + let original_text = original_response_clone.body; + let response_headers = original_response.headers; + let new_response_headers = new Headers(response_headers); + let status = original_response.status; + + if (new_response_headers.get("Www-Authenticate")) { + let auth = new_response_headers.get("Www-Authenticate"); + let re = new RegExp(auth_url, 'g'); + new_response_headers.set("Www-Authenticate", response_headers.get("Www-Authenticate").replace(re, workers_url)); + } + + if (new_response_headers.get("Location")) { + return httpHandler(e.request, new_response_headers.get("Location")) + } + + let response = new Response(original_text, { + status, + headers: new_response_headers + }) + return response; + +} + + +/** + * @param {Request} req + * @param {string} pathname + */ +function httpHandler(req, pathname) { + const reqHdrRaw = req.headers + + // preflight + if (req.method === 'OPTIONS' && + reqHdrRaw.has('access-control-request-headers') + ) { + return new Response(null, PREFLIGHT_INIT) + } + + let rawLen = '' + + const reqHdrNew = new Headers(reqHdrRaw) + + const refer = reqHdrNew.get('referer') + + let urlStr = pathname + + const urlObj = newUrl(urlStr) + + /** @type {RequestInit} */ + const reqInit = { + method: req.method, + headers: reqHdrNew, + redirect: 'follow', + body: req.body + } + return proxy(urlObj, reqInit, rawLen, 0) +} + + +/** + * + * @param {URL} urlObj + * @param {RequestInit} reqInit + */ +async function proxy(urlObj, reqInit, rawLen) { + const res = await fetch(urlObj.href, reqInit) + const resHdrOld = res.headers + const resHdrNew = new Headers(resHdrOld) + + // verify + if (rawLen) { + const newLen = resHdrOld.get('content-length') || '' + const badLen = (rawLen !== newLen) + + if (badLen) { + return makeRes(res.body, 400, { + '--error': `bad len: ${newLen}, except: ${rawLen}`, + 'access-control-expose-headers': '--error', + }) + } + } + const status = res.status + resHdrNew.set('access-control-expose-headers', '*') + resHdrNew.set('access-control-allow-origin', '*') + resHdrNew.set('Cache-Control', 'max-age=1500') + + resHdrNew.delete('content-security-policy') + resHdrNew.delete('content-security-policy-report-only') + resHdrNew.delete('clear-site-data') + + return new Response(res.body, { + status, + headers: resHdrNew + }) +} \ No newline at end of file