feat(pages): add page theme configuration and static files

This commit is contained in:
WJQSERVER 2025-02-18 10:19:11 +08:00
parent 785a74dfeb
commit 8336896979
13 changed files with 477 additions and 25 deletions

View file

@ -99,6 +99,7 @@ maxConnsPerHost = 0 # only for advanced mode 仅用于高级模式
[pages]
enabled = false # 是否开启内置静态页面(Docker版本请关闭此项)
theme = "bootstrap" # "bootstrap" or "nebula" 内置主题
staticPath = "/data/www" # 静态页面文件路径
[log]

View file

@ -39,8 +39,15 @@ type HttpcConfig struct {
MaxConnsPerHost int `toml:"maxConnsPerHost"`
}
/*
[pages]
enabled = false
theme = "bootstrap" # "bootstrap" or "nebula"
staticDir = "/data/www"
*/
type PagesConfig struct {
Enabled bool `toml:"enabled"`
Theme string `toml:"theme"`
StaticDir string `toml:"staticDir"`
}

View file

@ -13,6 +13,7 @@ maxConnsPerHost = 0 # only for advanced mode
[pages]
enabled = false
theme = "bootstrap" # "bootstrap" or "nebula"
staticDir = "/data/www"
[log]

View file

@ -13,6 +13,7 @@ maxConnsPerHost = 0 # only for advanced mode
[pages]
enabled = false
theme = "bootstrap" # "bootstrap" or "nebula"
staticDir = "/usr/local/ghproxy/pages"
[log]

View file

@ -90,8 +90,8 @@ tar -zxvf ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz -C ${ghproxy_dir}
chmod +x ${ghproxy_dir}/ghproxy
# 下载pages
wget -q -O ${ghproxy_dir}/pages/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/pages/index.html
wget -q -O ${ghproxy_dir}/pages/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/pages/favicon.ico
wget -q -O ${ghproxy_dir}/pages/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/pages/boostrap/index.html
wget -q -O ${ghproxy_dir}/pages/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/pages/bootstrap/favicon.ico
# 下载配置文件

View file

@ -90,8 +90,8 @@ tar -zxvf ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz -C ${ghproxy_dir}
chmod +x ${ghproxy_dir}/ghproxy
# 下载pages
wget -q -O ${ghproxy_dir}/pages/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/index.html
wget -q -O ${ghproxy_dir}/pages/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/favicon.ico
wget -q -O ${ghproxy_dir}/pages/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/bootstrap/index.html
wget -q -O ${ghproxy_dir}/pages/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/bootstrap/favicon.ico
# 下载配置文件

2
go.mod
View file

@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v1.4.0
github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0
github.com/gin-gonic/gin v1.10.0
github.com/satomitouka/touka-httpc v0.1.0
github.com/satomitouka/touka-httpc v0.2.0
golang.org/x/net v0.35.0
golang.org/x/time v0.10.0
)

18
go.sum
View file

@ -25,8 +25,6 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
@ -53,20 +51,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satomitouka/touka-httpc v0.0.3 h1:SLb14DWBIDeIaNQ0wMwRwJMjUDakHVR1Jbdct3Qi8fA=
github.com/satomitouka/touka-httpc v0.0.3/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.4 h1:sZs/2kqTSyLQ/pDHs/71l7MSG46j4rZNKfqn3CFAboU=
github.com/satomitouka/touka-httpc v0.0.4/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.5 h1:ov1v29vrjvwRNbGqFJHmrCp+3/qXLoyWubO4kTDvb28=
github.com/satomitouka/touka-httpc v0.0.5/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.6 h1:1iSaTB9KpviXy2NHvMXuRzy5mkcvle+fktWPhpS907c=
github.com/satomitouka/touka-httpc v0.0.6/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.7 h1:igoLqXs6R1yNIdKMcfpwRB1l6KLLus6DvWT3xL1T5FY=
github.com/satomitouka/touka-httpc v0.0.7/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.8 h1:KW521Z2z9BarnTgCajug/W/tIbnoIH+CzA7CON19iAg=
github.com/satomitouka/touka-httpc v0.0.8/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.1.0 h1:CXCsr6NhdskK/W/ezvhwK2CP8QGCxewkBhsEjrM7K8s=
github.com/satomitouka/touka-httpc v0.1.0/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.2.0 h1:JohnKH0T5KuVcouycqSI70oJIhMxY1nlNDhgZRxI73s=
github.com/satomitouka/touka-httpc v0.2.0/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

24
main.go
View file

@ -35,8 +35,10 @@ var (
)
var (
//go:embed pages/*
//go:embed pages/bootstrap/*
pagesFS embed.FS
//go:embed pages/nebula/*
NebulaPagesFS embed.FS
)
var (
@ -159,9 +161,23 @@ func init() {
})
router.StaticFile("/favicon.ico", faviconPath)
} else if !cfg.Pages.Enabled {
pages, err := fs.Sub(pagesFS, "pages")
if err != nil {
logError("Failed when processing pages: %s", err)
var pages fs.FS
var err error
if cfg.Pages.Theme == "bootstrap" {
pages, err = fs.Sub(pagesFS, "pages/bootstrap")
if err != nil {
logError("Failed when processing pages: %s", err)
}
} else if cfg.Pages.Theme == "nebula" {
pages, err = fs.Sub(NebulaPagesFS, "pages/nebula")
if err != nil {
logError("Failed when processing pages: %s", err)
}
} else {
pages, err = fs.Sub(pagesFS, "pages/bootstrap")
if err != nil {
logError("Failed when processing pages: %s", err)
}
}
router.GET("/", gin.WrapH(http.FileServer(http.FS(pages))))
router.GET("/favicon.ico", gin.WrapH(http.FileServer(http.FS(pages))))

View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before After
Before After

440
pages/bootstrap/index.html Normal file
View file

@ -0,0 +1,440 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Github文件加速</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<style>
/* 通用样式 */
:root {
--primary-color: #007aff;
/* 主要按钮颜色 */
--secondary-color: #34c759;
/* 次要按钮颜色 */
--background-color: #f9f9f9;
/* 亮色模式背景 */
--card-background: #ffffff;
/* 卡片背景 */
--text-color: #333333;
/* 亮色模式文本颜色 */
--border-color: #e0e0e0;
/* 边框颜色 */
--input-background: #ffffff;
/* 输入框背景 */
--input-border: #d1d1d6;
/* 输入框边框 */
--pre-background: #f1f3f4;
/* 代码块背景 */
--pre-text-color: #333333;
/* 代码块文本颜色 */
--version-badge-hover: #39c5bb;
/* 说明文字颜色 */
--muted-text-color: #6c757d;
}
body {
background-color: var(--background-color);
color: var(--text-color);
font-family: sans-serif;
line-height: 1.8;
font-size: 15px;
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6 {
color: var(--text-color);
font-weight: 800;
letter-spacing: 0.5px;
margin: 1rem 0;
}
p, span, a, li {
color: var(--text-color);
margin-bottom: 1rem;
}
a {
text-decoration: none;
color: var(--primary-color);
}
a:hover {
color: #0056b3;
}
.card {
background-color: var(--card-background);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 16px;
margin: 16px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
transform: translateY(-4px);
}
.btn-outline-secondary {
border-radius: 50%;
padding: 6px;
transition: #e9e9e9 0.3s ease-in-out, color 0.3s ease-in-out;
}
.btn-outline-secondary:hover {
background-color: var(--primary-color);
color: white;
}
.form-control {
background-color: var(--input-background);
border: 1px solid var(--input-border);
color: var(--text-color);
padding: 10px;
border-radius: 4px;
font-size: 14px;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.3);
}
.text-muted {
color: var(--muted-text-color) !important;
}
.bg-light {
background-color: var(--card-background) !important;
}
pre {
background-color: var(--pre-background);
color: var(--pre-text-color);
padding: 16px;
border-radius: 8px;
overflow-x: auto;
font-size: 14px;
line-height: 1.6;
}
.version-badge {
position: fixed;
bottom: 20px;
right: 20px;
background-color: var(--secondary-color);
color: white;
padding: 6px 12px;
border-radius: 20px;
font-size: 0.8rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s ease-in-out;
}
.version-badge:hover {
background-color: var(--version-badge-hover);
}
footer {
padding: 16px;
text-align: center;
color: var(--text-color);
font-size: 0.9rem;
background-color: var(--card-background);
}
footer a {
color: var(--primary-color);
}
footer a:hover {
color: #0056b3;
}
/* 暗色模式 */
@media (prefers-color-scheme: dark) {
:root {
--background-color: #121212;
/* 深灰色背景 */
--card-background: #1e1e1e;
/* 卡片背景稍浅 */
--text-color: #ffffff;
/* 纯白文本 */
--primary-color: #0a84ff;
/* 按钮蓝色 */
--secondary-color: #30d158;
/* 次要按钮绿色 */
--border-color: #3a3a3a;
/* 边框颜色 */
--input-background: #2c2c2c;
/* 输入框背景 */
--input-border: #4a4a4a;
/* 输入框边框 */
--pre-background: #3b3636;
/* 代码块背景 */
--pre-text-color: #ffffff;
/* 代码块文本颜色 */
--version-badge-hover: #39c5bc9a;
/* 说明文字颜色 */
--muted-text-color: #a0a0a0;
}
body {
background-color: var(--background-color);
color: var(--text-color);
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
span,
a,
li {
color: var(--text-color);
}
.card {
background-color: var(--card-background);
color: var(--text-color);
border: 1px solid var(--border-color);
}
.btn-outline-secondary {
border-radius: 50%;
padding: 6px;
transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out;
}
.btn-outline-secondary:hover {
background-color: var(--primary-color);
color: white;
}
.toast {
background-color: var(--card-background);
color: var(--text-color);
border: 1px solid var(--border-color);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.toast-body {
padding: 12px;
}
.form-control {
background-color: var(--input-background);
border: 1px solid var(--input-border);
color: var(--text-color);
}
.bg-light {
background-color: var(--card-background) !important;
}
pre {
background-color: var(--pre-background);
color: var(--pre-text-color);
}
footer {
background-color: var(--card-background);
color: var(--text-color);
}
footer a {
color: var(--primary-color);
}
}
</style>
</head>
<body>
<div class="container py-4 py-md-5">
<main>
<div class="card mb-4">
<div class="card-body">
<h1 class="text-center mb-4">Github文件加速</h1>
<p class="lead text-center mb-4">为访问Github文件进行加速</p>
<form id="github-form">
<div class="mb-3">
<input type="text" class="form-control form-control-lg" id="githubLinkInput"
placeholder="请键入需要代理的 Github 链接">
</div>
<button type="submit" class="btn btn-primary btn-lg w-100">获取文件链接</button>
</form>
<div id="output" class="mt-3 bg-light p-3 rounded position-relative" style="display: none;">
<pre id="formattedLinkOutput" class="mb-0"></pre>
<button id="copyButton"
class="btn btn-outline-secondary btn-sm position-absolute top-0 end-0 m-2" title="复制链接">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-clipboard" viewBox="0 0 16 16">
<path
d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z" />
<path
d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z" />
</svg>
</button>
<button id="openButton"
class="btn btn-outline-secondary btn-sm position-absolute top-0 end-0 m-2 me-5"
title="在新标签页中打开">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-box-arrow-up-right" viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z" />
<path fill-rule="evenodd"
d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z" />
</svg>
</button>
</div>
<p class="text-muted small mt-3 mb-0">GitHub 链接带不带协议头均可,支持 release、archive 以及文件,转换后链接均可使用。</p>
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">文件大小限制</h5>
<p class="card-text" id="sizeLimitDisplay">...</p>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">白名单状态</h5>
<p class="card-text" id="whiteListStatus">...</p>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">黑名单状态</h5>
<p class="card-text" id="blackListStatus">...</p>
</div>
</div>
</div>
</div>
</main>
<footer class="text-center mt-4">
<p class="text-muted">
Copyright &copy; 2024-2025 WJQSERVER-STUDIO<br>
<a href="https://github.com/WJQSERVER-STUDIO/ghproxy" class="text-decoration-none">GitHub 仓库</a> |
<a href="https://t.me/ghproxy_go" class="text-decoration-none">Telegram 交流群</a>
</p>
</footer>
</div>
<div class="toast-container position-fixed top-0 end-0 p-3">
<div id="toast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-body"></div>
</div>
</div>
<div id="versionBadge" class="version-badge"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
const githubForm = document.getElementById('github-form');
const githubLinkInput = document.getElementById('githubLinkInput');
const formattedLinkOutput = document.getElementById('formattedLinkOutput');
const output = document.getElementById('output');
const copyButton = document.getElementById('copyButton');
const openButton = document.getElementById('openButton');
const toast = new bootstrap.Toast(document.getElementById('toast'));
function showToast(message) {
const toastBody = document.querySelector('.toast-body');
toastBody.textContent = message;
toast.show();
}
function formatGithubLink(githubLink) {
const currentHost = window.location.host;
let formattedLink = "";
if (githubLink.startsWith("https://github.com/") || githubLink.startsWith("http://github.com/")) {
formattedLink = window.location.protocol + "//" + currentHost + "/github.com" + githubLink.substring(githubLink.indexOf("/", 8));
} else if (githubLink.startsWith("github.com/")) {
formattedLink = window.location.protocol + "//" + currentHost + "/" + githubLink;
} else if (githubLink.startsWith("https://raw.githubusercontent.com/") || githubLink.startsWith("http://raw.githubusercontent.com/")) {
formattedLink = window.location.protocol + "//" + currentHost + githubLink.substring(githubLink.indexOf("/", 7));
} else if (githubLink.startsWith("raw.githubusercontent.com/")) {
formattedLink = window.location.protocol + "//" + currentHost + "/" + githubLink;
} else if (githubLink.startsWith("https://gist.githubusercontent.com/") || githubLink.startsWith("http://gist.githubusercontent.com/")) {
formattedLink = window.location.protocol + "//" + currentHost + "/gist.github.com" + githubLink.substring(githubLink.indexOf("/", 18));
} else if (githubLink.startsWith("gist.githubusercontent.com/")) {
formattedLink = window.location.protocol + "//" + currentHost + "/" + githubLink;
} else {
showToast('请输入有效的GitHub链接');
return null;
}
return formattedLink;
}
githubForm.addEventListener('submit', function (e) {
e.preventDefault();
const formattedLink = formatGithubLink(githubLinkInput.value);
if (formattedLink) {
formattedLinkOutput.textContent = formattedLink;
output.style.display = 'block';
}
});
copyButton.addEventListener('click', function () {
navigator.clipboard.writeText(formattedLinkOutput.textContent).then(() => {
showToast('链接已复制到剪贴板');
});
});
openButton.addEventListener('click', function () {
window.open(formattedLinkOutput.textContent, '_blank');
});
function fetchAPI() {
fetch('/api/size_limit')
.then(response => response.json())
.then(data => {
document.getElementById('sizeLimitDisplay').textContent = `${data.MaxResponseBodySize} MB`;
});
fetch('/api/whitelist/status')
.then(response => response.json())
.then(data => {
document.getElementById('whiteListStatus').textContent = data.Whitelist ? '已开启' : '已关闭';
});
fetch('/api/blacklist/status')
.then(response => response.json())
.then(data => {
document.getElementById('blackListStatus').textContent = data.Blacklist ? '已开启' : '已关闭';
});
fetch('/api/version')
.then(response => response.json())
.then(data => {
document.getElementById('versionBadge').textContent = data.Version;
});
}
document.addEventListener('DOMContentLoaded', fetchAPI);
</script>
</body>
</html>

BIN
pages/nebula/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB