Docker Hub Mirrors

Show other mirrors to pull current docker image.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Docker Hub Mirrors
// @namespace    https://github.com/iamspark1e/my-tampermonkey-scripts/blob/main/docker-mirror-urls.js
// @version      0.1.1
// @description  Show other mirrors to pull current docker image.
// @author       iamspark1e
// @license      AGPL-3.0-or-later
// @match        *://hub.docker.com/_/*
// @match        *://hub.docker.com/r/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

// ref: https://juejin.cn/post/7022654292880424991
// - 阿里云(杭州)   https://registry.cn-hangzhou.aliyuncs.com
// - 阿里云(上海)   https://registry.cn-shanghai.aliyuncs.com
// - 阿里云(青岛)   https://registry.cn-qingdao.aliyuncs.com
// - 阿里云(北京)   https://registry.cn-beijing.aliyuncs.com
// - 阿里云(张家口)   https://registry.cn-zhangjiakou.aliyuncs.com
// - 阿里云(呼和浩特)   https://registry.cn-huhehaote.aliyuncs.com
// - 阿里云(乌兰察布)   https://registry.cn-wulanchabu.aliyuncs.com
// - 阿里云(深圳)   https://registry.cn-shenzhen.aliyuncs.com
// - 阿里云(河源)   https://registry.cn-heyuan.aliyuncs.com
// - 阿里云(广州)   https://registry.cn-guangzhou.aliyuncs.com
// - 阿里云(成都)   https://registry.cn-chengdu.aliyuncs.com
// - 腾讯云   https://mirror.ccs.tencentyun.com
// - 微软云   https://dockerhub.azk8s.com
// - 网易   https://hub-mirror.c.163.com
// - 上海交通大学   https://mirror.sjtu.edu.cn/docs/docker-registry
// - ❤❤❤南京大学   https://docker.nju.edu.cn
// - 道客 DaoCloud   https://f1361db2.m.daocloud.io
// - 阿里云(香港)   https://registry.cn-hongkong.aliyuncs.com
// - 阿里云(日本-东京)   https://registry.ap-northeast-1.aliyuncs.com
// - 阿里云(新加坡)   https://registry.ap-southeast-1.aliyuncs.com
// - 阿里云(澳大利亚-悉尼)   https://registry.ap-southeast-2.aliyuncs.com
// - 阿里云(马来西亚-吉隆坡)   https://registry.ap-southeast-3.aliyuncs.com
// - 阿里云(印度尼西亚-雅加达)   https://registry.ap-southeast-5.aliyuncs.com
// - 阿里云(印度-孟买)   https://registry.ap-south-1.aliyuncs.com
// - 阿里云(德国-法兰克福)   https://registry.eu-central-1.aliyuncs.com
// - 阿里云(英国-伦敦)   https://registry.eu-west-1.aliyuncs.com
// - 阿里云(美国西部-硅谷)   https://registry.us-west-1.aliyuncs.com
// - 阿里云(美国东部-弗吉尼亚)   https://registry.us-east-1.aliyuncs.com
// - 阿里云(阿联酋-迪拜)   https://registry.me-east-1.aliyuncs.com
// - 谷歌云   https://gcr.io
// - 官方   https://registry.hub.docker.com

(function () {
    'use strict';
    const ALTER_HUBS = [
        { "url": "registry.cn-hangzhou.aliyuncs.com", "name": "阿里云(杭州)" },
        { "url": "mirror.ccs.tencentyun.com", "name": "腾讯云" },
        { "url": "dockerhub.azk8s.com", "name": "微软云(Azure)" },
        { "url": "hub-mirror.c.163.com", "name": "网易" },
        { "url": "mirror.sjtu.edu.cn/docs/docker-registry", "name": "上海交通大学" },
        { "url": "docker.nju.edu.cn", "name": "南京大学" },
        { "url": "f1361db2.m.daocloud.io", "name": "DaoCloud" },
        { "url": "gcr.io", "name": "Google" },
        { "url": "registry.hub.docker.com", "name": "官方" },
    ]
    const DOM_IDENTIFIER = "iamspark1e_tampermonkey_docker_mirror_helper"
    const DOM_TPL = `
        <label>
            <span>Choose a mirror: </span>
            <select>
                ${ALTER_HUBS.map(hub => `<option value="${hub.url}">${hub.name}</option>`).join("")}
            </select>
        </label>
        <div class="show_url" style="background-color:rgba(255,255,255,0.3);border-radius:4px;margin-top:4px;padding:4px;"></div>
    `

    const GENERATE_SNIPPETS = (hub, image_name) => `
        <p>URL: <span style="user-select:all;word-break:break-all;">${hub.url + '/' + image_name}</span></p>
        <p>Docker pull: <code style="user-select:all;word-break:break-all;">docker pull ${hub.url + '/' + image_name}</code></p>
    `

    let siblingNode;
    let currentHub = ALTER_HUBS[0];
    let imageFullName;
    let oldHref = window.location.href;

    function renderUrls() {
        let dom = document.querySelector(`#${DOM_IDENTIFIER} div.show_url`);
        dom.innerHTML = GENERATE_SNIPPETS(currentHub, imageFullName)
    }
    function selectHandler(e) {
        currentHub = ALTER_HUBS.find(hub => hub.url === e.target.value);
        renderUrls();
    }

    function initHelper() {
        if (!siblingNode) {
            console.error("no siblingNode, init failed");
            return;
        }
        const mountNode = siblingNode.parentNode.parentNode;
        imageFullName = siblingNode.innerText.replace("docker pull ", "");

        if (!document.querySelector(`#${DOM_IDENTIFIER}`)) {
            const testDiv = document.createElement("div")
            testDiv.setAttribute("style", "border:1px solid #c4c8d1;background-color: #e1e2e6;border-radius: 4px;padding:8px;margin-top: 8px;");
            testDiv.id = DOM_IDENTIFIER;
            testDiv.innerHTML = DOM_TPL;
            mountNode.appendChild(testDiv);

            // addEventListener
            const selector = document.querySelector(`#${DOM_IDENTIFIER} select`);
            selector.addEventListener("change", selectHandler);
            renderUrls();
        }
    }

    let observer = new MutationObserver((mutationList, observer) => {
        if(oldHref !== window.location.href) {
            siblingNode = null;
            oldHref = window.location.href;
        }
        for (const mutation of mutationList) {
            if (mutation.type === "childList" && !siblingNode) {
                const tmp = document.querySelector('code[data-testid="copyPullCommandPullCommand"]')
                if (tmp && !siblingNode) {
                    siblingNode = tmp;
                    initHelper();
                    // observer.disconnect()
                }
            }
        }
    });
    // *://hub.docker.com/_/*
    // *://hub.docker.com/r/*
    observer.observe(document.body, {
        childList: true,
        subtree: true
    })



    // TODO:
    // *://hub.docker.com/_/*/tags
    // *://hub.docker.com/r/*/tags
})();