Files
hubproxy/src/public/index.html
2026-07-11 20:56:50 +08:00

833 lines
27 KiB
HTML
Vendored
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Github文件加速、docker镜像加速">
<meta name="keywords" content="Github、文件加速、ghproxy、docker镜像加速">
<meta name="color-scheme" content="dark light">
<title>Github、Docker加速</title>
<link rel="icon" href="/favicon.ico">
<style>
:root {
--background: #ffffff;
--foreground: #0f172a;
--card: #ffffff;
--card-foreground: #0f172a;
--primary: #2563eb;
--primary-foreground: #f8fafc;
--secondary: #f1f5f9;
--secondary-foreground: #0f172a;
--muted: #f1f5f9;
--muted-foreground: #64748b;
--accent: #f1f5f9;
--accent-foreground: #0f172a;
--border: #e2e8f0;
--input: #ffffff;
--ring: #2563eb;
--radius: 0.5rem;
}
.dark {
--background: #0f172a;
--foreground: #f8fafc;
--card: #1e293b;
--card-foreground: #f8fafc;
--primary: #3b82f6;
--primary-foreground: #f8fafc;
--secondary: #1e293b;
--secondary-foreground: #f8fafc;
--muted: #1e293b;
--muted-foreground: #94a3b8;
--accent: #1e293b;
--accent-foreground: #f8fafc;
--border: #334155;
--input: #1e293b;
--ring: #3b82f6;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0f172a;
--foreground: #f8fafc;
--card: #1e293b;
--card-foreground: #f8fafc;
--primary: #3b82f6;
--primary-foreground: #f8fafc;
--secondary: #1e293b;
--secondary-foreground: #f8fafc;
--muted: #1e293b;
--muted-foreground: #94a3b8;
--accent: #1e293b;
--accent-foreground: #f8fafc;
--border: #334155;
--input: #1e293b;
--ring: #3b82f6;
}
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
background-color: var(--background);
color: var(--foreground);
line-height: 1.5;
min-height: 100vh;
display: flex;
flex-direction: column;
transition: background-color 0.3s, color 0.3s;
}
.navbar {
position: sticky;
top: 0;
z-index: 50;
width: 100%;
border-bottom: 1px solid var(--border);
background-color: var(--background);
backdrop-filter: blur(8px);
background-color: rgba(255, 255, 255, 0.95);
}
.dark .navbar {
background-color: rgba(15, 23, 42, 0.95);
}
.navbar-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
display: flex;
align-items: center;
justify-content: space-between;
height: 4rem;
}
.logo {
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
color: var(--foreground);
font-weight: 600;
font-size: 1.125rem;
}
.logo-icon {
width: 2rem;
height: 2rem;
border-radius: 0.5rem;
background: linear-gradient(135deg, var(--primary), #3b82f6);
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.nav-links {
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-link {
padding: 0.5rem 1rem;
border-radius: var(--radius);
text-decoration: none;
color: var(--muted-foreground);
transition: all 0.2s;
font-weight: 500;
}
.nav-link:hover,
.nav-link.active {
color: var(--foreground);
background-color: var(--muted);
}
.theme-toggle {
padding: 0.5rem;
border: none;
border-radius: var(--radius);
background-color: transparent;
color: var(--muted-foreground);
cursor: pointer;
transition: all 0.2s;
}
.theme-toggle:hover {
background-color: var(--muted);
color: var(--foreground);
}
.main {
flex: 1;
padding: 2rem 1rem;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.hero {
text-align: center;
margin-bottom: 3rem;
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.6s ease-out forwards;
}
.hero-title {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
background: linear-gradient(135deg, var(--primary), #3b82f6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 1.125rem;
color: var(--muted-foreground);
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: var(--card);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.6s ease-out 0.2s forwards;
}
.card-header {
margin-bottom: 1.5rem;
}
.card-title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.card-description {
color: var(--muted-foreground);
font-size: 0.875rem;
}
.form-group {
margin-bottom: 1rem;
}
.input-container {
position: relative;
display: flex;
gap: 0.75rem;
}
.input {
flex: 1;
padding: 0.75rem 1rem;
border: 1px solid var(--border);
border-radius: var(--radius);
background-color: var(--input);
color: var(--foreground);
font-size: 1rem;
transition: all 0.2s;
}
.input:focus {
outline: none;
border-color: var(--ring);
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
}
.input::placeholder {
color: var(--muted-foreground);
}
.button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: var(--radius);
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.button-primary {
background-color: var(--primary);
color: var(--primary-foreground);
}
.button-primary:hover {
background-color: #1d4ed8;
transform: translateY(-1px);
}
.button-secondary {
background-color: var(--secondary);
color: var(--secondary-foreground);
}
.button-secondary:hover {
background-color: var(--muted);
}
.output-container {
margin-top: 1.5rem;
display: none;
opacity: 0;
transform: translateY(10px);
transition: all 0.3s;
}
.output-container.show {
display: block;
opacity: 1;
transform: translateY(0);
}
.success-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
color: #059669;
}
.output-box {
background-color: var(--muted);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 1rem;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.875rem;
word-break: break-all;
position: relative;
margin-bottom: 1rem;
}
.output-actions {
display: flex;
gap: 0.5rem;
}
.docker-info {
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.6s ease-out 0.4s forwards;
}
.docker-button {
width: 100%;
padding: 1rem;
background: linear-gradient(135deg, #f1f5f9, #e2e8f0);
border: 1px solid var(--border);
border-radius: var(--radius);
cursor: pointer;
transition: all 0.3s;
font-size: 1rem;
font-weight: 500;
color: var(--foreground);
}
.docker-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.dark .docker-button {
background: linear-gradient(135deg, #374151, #4b5563);
}
.dark .docker-button:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(4px);
}
.modal-content {
background-color: var(--card);
border-radius: 0.75rem;
padding: 2rem;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}
.modal-header {
text-align: center;
margin-bottom: 2rem;
}
.modal-title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.close-button {
position: absolute;
top: 1rem;
right: 1rem;
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--muted-foreground);
padding: 0.25rem;
border-radius: var(--radius);
}
.close-button:hover {
background-color: var(--muted);
}
.domain-examples {
background-color: var(--muted);
border-radius: var(--radius);
padding: 1rem;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.875rem;
line-height: 1.6;
}
.domain-examples strong {
color: var(--foreground);
display: block;
margin: 1rem 0 0.5rem 0;
}
.domain-examples strong:first-child {
margin-top: 0;
}
.toast {
position: fixed;
top: 1rem;
right: 1rem;
background-color: var(--primary);
color: var(--primary-foreground);
padding: 1rem 1.5rem;
border-radius: var(--radius);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1001;
display: none;
opacity: 0;
transform: translateX(100%);
transition: all 0.3s ease;
}
.toast.show {
display: block;
opacity: 1;
transform: translateX(0);
}
.footer {
padding: 2rem 1rem;
text-align: center;
border-top: 1px solid var(--border);
}
.github-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: var(--muted-foreground);
text-decoration: none;
transition: color 0.2s;
}
.github-link:hover {
color: var(--foreground);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.hero-title {
font-size: 2rem;
}
.nav-links {
position: fixed;
top: 70px;
left: 0;
right: 0;
background: var(--background);
border: 1px solid var(--border);
border-top: none;
border-radius: 0 0 12px 12px;
padding: 1rem;
flex-direction: column;
gap: 0.5rem;
z-index: 1000;
transform: translateY(-100vh);
transition: transform 0.3s ease;
}
.nav-links.active {
transform: translateY(0);
}
.mobile-menu-toggle {
display: block !important;
background: none;
border: none;
color: var(--foreground);
font-size: 1.5rem;
cursor: pointer;
padding: 0.5rem;
border-radius: var(--radius);
transition: background-color 0.2s;
}
.mobile-menu-toggle:hover {
background-color: var(--muted);
}
.navbar-container {
justify-content: space-between !important;
}
.container {
padding: 0 1rem;
}
.modal-content {
padding: 1.5rem;
}
.input-container {
flex-direction: column;
gap: 1rem;
}
.input {
font-size: 16px;
}
.button {
width: 100%;
justify-content: center;
padding: 0.875rem 1.5rem;
}
.output-actions {
flex-direction: column;
gap: 0.75rem;
}
}
.mobile-menu-toggle {
display: none;
}
</style>
</head>
<body>
<nav class="navbar">
<div class="navbar-container">
<a href="/" class="logo">
<div class="logo-icon">
</div>
加速服务
</a>
<button class="mobile-menu-toggle" id="mobileMenuToggle">
</button>
<div class="nav-links" id="navLinks">
<a href="/" class="nav-link active">🚀 GitHub加速</a>
<a href="/images.html" class="nav-link">🐳 离线镜像下载</a>
<a href="/search.html" class="nav-link">🔍 镜像搜索</a>
<button class="theme-toggle" id="themeToggle">
🌙
</button>
</div>
</div>
</nav>
<main class="main">
<div class="container">
<div class="hero">
<h1 class="hero-title">GitHub 文件加速</h1>
<p class="hero-subtitle">
快速下载GitHub上的文件和仓库解决国内访问GitHub速度慢的问题支持Docker镜像加速和Hugging Face仓库。
</p>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">
⚡ 快速转换加速链接
</h2>
<p class="card-description">
输入GitHub文件链接自动转换加速链接可以直接在Github文件链接前加上本站域名使用。
</p>
</div>
<div class="form-group">
<div class="input-container">
<input
type="text"
class="input"
id="githubLinkInput"
placeholder="请输入GitHub文件链接例如https://github.com/user/repo/releases/download/..."
>
<button class="button button-primary" id="formatButton">
获取加速链接
</button>
</div>
</div>
<div class="output-container" id="outputBlock">
<div class="success-header">
<span></span>
<strong>加速链接已生成</strong>
</div>
<div class="output-box" id="formattedLinkOutput"></div>
<div class="output-actions">
<button class="button button-secondary" id="copyButton">
📋 复制链接
</button>
<button class="button button-secondary" id="redirButton">
🔗 打开链接
</button>
</div>
</div>
</div>
<div class="card docker-info">
<div class="card-header">
<h3 class="card-title">
🐳 Docker 镜像加速
</h3>
<p class="card-description">
支持多种镜像仓库,在镜像名称前添加本站域名即可加速下载。
</p>
</div>
<button class="docker-button" id="dockerButton">
查看 Docker 镜像加速使用说明
</button>
</div>
</div>
</main>
<div id="dockerModal" class="modal">
<div class="modal-content">
<button class="close-button" id="closeModal">&times;</button>
<div class="modal-header">
<h2 class="modal-title">Docker 镜像加速</h2>
<p>支持多种镜像仓库,在镜像名称前添加本站域名即可加速下载。</p>
</div>
<div class="domain-examples">
<strong>Docker 官方镜像:</strong>
docker pull <span class="domain-base"></span>/nginx
<strong>Docker 镜像:</strong>
docker pull <span class="domain-base"></span>/user/image
<strong>ghcr.io 镜像:</strong>
docker pull <span class="domain-base"></span>/ghcr.io/user/image
<strong>Quay.io 镜像:</strong>
docker pull <span class="domain-base"></span>/quay.io/org/image
<strong>Kubernetes 镜像:</strong>
docker pull <span class="domain-base"></span>/registry.k8s.io/pause:3.8
</div>
</div>
</div>
<div id="toast" class="toast">
链接已复制到剪贴板
</div>
<footer class="footer">
<a href="https://github.com/sky22333/hubproxy" target="_blank" class="github-link">
<svg width="20" height="20" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
</svg>
GitHub
</a>
</footer>
<script>
const themeToggle = document.getElementById('themeToggle');
const html = document.documentElement;
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
html.classList.add('dark');
themeToggle.textContent = '☀️';
}
themeToggle.addEventListener('click', () => {
html.classList.toggle('dark');
const isDark = html.classList.contains('dark');
themeToggle.textContent = isDark ? '☀️' : '🌙';
localStorage.setItem('theme', isDark ? 'dark' : 'light');
});
document.addEventListener('DOMContentLoaded', function() {
const fullDomain = window.location.host;
document.querySelectorAll('.domain-base').forEach(span => {
span.textContent = fullDomain;
});
const modal = document.getElementById('dockerModal');
const dockerButton = document.getElementById('dockerButton');
const closeButton = document.getElementById('closeModal');
dockerButton.onclick = () => modal.style.display = "flex";
closeButton.onclick = () => modal.style.display = "none";
window.onclick = (event) => {
if (event.target == modal) modal.style.display = "none";
};
});
function formatGithubLink() {
const githubLinkInput = document.getElementById('githubLinkInput');
const currentHost = window.location.host;
let formattedLink = "";
const link = githubLinkInput.value.trim();
if (link.startsWith("https://") || link.startsWith("http://")) {
formattedLink = "https://" + currentHost + "/" + link;
} else if (
link.startsWith("github.com/") ||
link.startsWith("raw.githubusercontent.com/") ||
link.startsWith("gist.githubusercontent.com/") ||
link.startsWith("huggingface.co/") ||
link.startsWith("cdn-lfs.hf.co/")
) {
formattedLink = "https://" + currentHost + "/https://" + link;
} else {
showToast('请输入有效的链接');
return;
}
const formattedLinkOutput = document.getElementById('formattedLinkOutput');
formattedLinkOutput.textContent = formattedLink;
const outputBlock = document.getElementById('outputBlock');
outputBlock.classList.add('show');
}
function copyToClipboard() {
const output = document.getElementById('formattedLinkOutput');
const text = output.textContent;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(() => {
showToast('链接已复制到剪贴板');
}).catch(() => {
showToast('复制失败');
});
} else {
const range = document.createRange();
range.selectNode(output);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand('copy');
window.getSelection().removeAllRanges();
showToast('链接已复制到剪贴板');
}
}
function openLink() {
const formattedLinkOutput = document.getElementById('formattedLinkOutput');
window.open(formattedLinkOutput.textContent);
}
function showToast(message) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
document.getElementById('formatButton').addEventListener('click', formatGithubLink);
document.getElementById('copyButton').addEventListener('click', copyToClipboard);
document.getElementById('redirButton').addEventListener('click', openLink);
document.getElementById('githubLinkInput').addEventListener('keyup', function(event) {
if (event.key === 'Enter') {
formatGithubLink();
}
});
const mobileMenuToggle = document.getElementById('mobileMenuToggle');
const navLinks = document.getElementById('navLinks');
mobileMenuToggle.addEventListener('click', () => {
navLinks.classList.toggle('active');
mobileMenuToggle.textContent = navLinks.classList.contains('active') ? '✕' : '☰';
});
document.addEventListener('click', (e) => {
if (!e.target.closest('.navbar') && navLinks.classList.contains('active')) {
navLinks.classList.remove('active');
mobileMenuToggle.textContent = '☰';
}
});
</script>
</body>
</html>