This commit is contained in:
WJQSERVER 2024-11-04 05:53:53 +08:00
parent e32adadaff
commit 267dfafcb9
10 changed files with 154 additions and 35 deletions

View file

@ -1,5 +1,15 @@
# 更新日志 # 更新日志
24w21d
---
- PRE-RELEASE: 此版本是v1.7.0的预发布版本,请勿在生产环境中使用
- ADD: 新增`ratePerMinute` API可供查询
- ADD: 前端新增 version 标识
- ADD: 前端新增 `重定向` 按钮,用于重定向到代理后的链接
- CHANGE: 优化输出代码块,使样式更加美观
- CHANGE: 更新相关依赖库
- CHANGE: 对黑名单模块进行实验性功能优化,提升性能
24w21c 24w21c
--- ---
- PRE-RELEASE: 此版本是v1.7.0的预发布版本,请勿在生产环境中使用 - PRE-RELEASE: 此版本是v1.7.0的预发布版本,请勿在生产环境中使用

View file

@ -1 +1 @@
24w21c 24w21d

View file

@ -44,6 +44,9 @@ func InitHandleRouter(cfg *config.Config, router *gin.Engine, version string) {
apiRouter.GET("/rate_limit/status", func(c *gin.Context) { apiRouter.GET("/rate_limit/status", func(c *gin.Context) {
RateLimitStatusHandler(c, cfg) RateLimitStatusHandler(c, cfg)
}) })
apiRouter.GET("/rate_limit/limit", func(c *gin.Context) {
RateLimitLimitHandler(c, cfg)
})
} }
logInfo("API router Init success") logInfo("API router Init success")
} }
@ -104,3 +107,11 @@ func RateLimitStatusHandler(c *gin.Context, cfg *config.Config) {
"RateLimit": cfg.RateLimit.Enabled, "RateLimit": cfg.RateLimit.Enabled,
}) })
} }
func RateLimitLimitHandler(c *gin.Context, cfg *config.Config) {
logInfo("%s %s %s %s %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.UserAgent(), c.Request.Proto)
c.Writer.Header().Set("Content-Type", "application/json")
json.NewEncoder(c.Writer).Encode(map[string]interface{}{
"RatePerMinute": cfg.RateLimit.RatePerMinute,
})
}

View file

@ -8,7 +8,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// 日志模块
var ( var (
logw = logger.Logw logw = logger.Logw
logInfo = logger.LogInfo logInfo = logger.LogInfo
@ -16,7 +15,6 @@ var (
logError = logger.LogError logError = logger.LogError
) )
// Auth Init
func Init(cfg *config.Config) { func Init(cfg *config.Config) {
if cfg.Blacklist.Enabled { if cfg.Blacklist.Enabled {
LoadBlacklist(cfg) LoadBlacklist(cfg)
@ -28,17 +26,13 @@ func Init(cfg *config.Config) {
} }
func AuthHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string) { func AuthHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string) {
// 如果身份验证未启用,直接返回 true
if !cfg.Auth.Enabled { if !cfg.Auth.Enabled {
return true, "" return true, ""
} }
// 获取 auth_token 参数
authToken := c.Query("auth_token") authToken := c.Query("auth_token")
// IP METHOD URL USERAGENT PROTO TOKEN
logInfo("%s %s %s %s %s AUTH_TOKEN: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.UserAgent(), c.Request.Proto, authToken) logInfo("%s %s %s %s %s AUTH_TOKEN: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.UserAgent(), c.Request.Proto, authToken)
// 验证 token
if authToken == "" { if authToken == "" {
err := "Auth token == nil" err := "Auth token == nil"
return false, err return false, err

View file

@ -33,8 +33,8 @@ func LoadBlacklist(cfg *config.Config) {
} }
// fullrepo: "owner/repo" or "owner/*" // fullrepo: "owner/repo" or "owner/*"
func CheckBlacklist(fullrepo string) bool { func CheckBlacklist(repouser string, user string, repo string) bool {
return forRangeCheckBlacklist(blacklist.Blacklist, fullrepo) return forRangeCheckBlacklist(blacklist.Blacklist, repouser, user)
} }
func sliceRepoName_Blacklist(fullrepo string) (string, string) { func sliceRepoName_Blacklist(fullrepo string) (string, string) {
@ -45,12 +45,29 @@ func sliceRepoName_Blacklist(fullrepo string) (string, string) {
return s[0], s[1] return s[0], s[1]
} }
func forRangeCheckBlacklist(blist []string, fullrepo string) bool { func forRangeCheckBlacklist(blist []string, fullrepo string, user string) bool {
repoUser, _ := sliceRepoName_Blacklist(fullrepo) // 先匹配user,再匹配user/*,最后匹配完整repo
for _, blocked := range blist { for _, blocked := range blist {
// 切片
users, _ := sliceRepoName_Blacklist(blocked)
logw("users:%s, blocked:%s", users, blocked)
// 匹配 user
if user == users {
// 匹配 user/*
if strings.HasSuffix(blocked, "/*") {
return true
}
// 匹配完整repo
if fullrepo == blocked {
return true
}
}
}
/* for _, blocked := range blist {
if blocked == fullrepo || (strings.HasSuffix(blocked, "/*") && strings.HasPrefix(repoUser, blocked[:len(blocked)-2])) { if blocked == fullrepo || (strings.HasSuffix(blocked, "/*") && strings.HasPrefix(repoUser, blocked[:len(blocked)-2])) {
return true return true
} }
} } */
return false return false
} }

4
go.mod
View file

@ -6,6 +6,7 @@ require (
github.com/BurntSushi/toml v1.4.0 github.com/BurntSushi/toml v1.4.0
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/imroc/req/v3 v3.48.0 github.com/imroc/req/v3 v3.48.0
golang.org/x/time v0.7.0
) )
require ( require (
@ -22,7 +23,7 @@ require (
github.com/go-playground/validator/v10 v10.22.1 // indirect github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect github.com/goccy/go-json v0.10.3 // indirect
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
@ -48,7 +49,6 @@ require (
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.26.0 // indirect golang.org/x/tools v0.26.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

2
go.sum
View file

@ -52,6 +52,8 @@ github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsP
github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=

View file

@ -56,7 +56,6 @@ func Log(customMessage string) {
logChannel <- customMessage logChannel <- customMessage
} }
// 格式化日志记录
func Logw(format string, args ...interface{}) { func Logw(format string, args ...interface{}) {
message := fmt.Sprintf(format, args...) message := fmt.Sprintf(format, args...)
Log(message) Log(message)
@ -82,7 +81,6 @@ func LogError(format string, args ...interface{}) {
Log(message) Log(message)
} }
// 关闭日志文件
func Close() { func Close() {
logFileMutex.Lock() logFileMutex.Lock()
defer logFileMutex.Unlock() defer logFileMutex.Unlock()

View file

@ -6,12 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Github文件加速"> <meta name="description" content="Github文件加速">
<meta name="keywords" content="Github,文件加速,ghproxy"> <meta name="keywords" content="Github,文件加速,ghproxy">
<meta name="color-scheme" content="dark light"> <meta name="color-scheme" content="dark light">
<title>Github文件加速</title> <title>Github文件加速</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://font.sec.miui.com/font/css?family=MiSans:400,700:MiSans"> <link rel="stylesheet" href="https://font.sec.miui.com/font/css?family=MiSans:400,700:MiSans">
<style> <style>
:root { :root {
--color: #dadada; --color: #dadada;
--fontcolor: #333; --fontcolor: #333;
@ -41,6 +40,35 @@
position: relative; position: relative;
} }
.version {
width: 12.5%;
height: 2%;
background-color: #39c5bb;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.8rem;
border-radius: 0.5rem;
position: fixed;
bottom: 0%;
right: 0%;
}
*::-webkit-scrollbar {
height: 10px;
margin-top: 0px;
}
*::-webkit-scrollbar-track {
background-color: black;
}
*::-webkit-scrollbar-thumb {
background: #39c5bb;
border-radius: 10px;
}
.container { .container {
max-width: 80%; max-width: 80%;
text-align: center; text-align: center;
@ -89,9 +117,9 @@
pre { pre {
background: #012333; background: #012333;
color: #39c5bc; color: #39c5bc;
padding: 20px 20px; padding: 15px 20px 15px 20px;
margin: 10px 0; margin: 0px 0;
border-radius: 8px; border-radius: 0.5rem;
overflow-x: auto; overflow-x: auto;
position: relative; position: relative;
} }
@ -100,10 +128,10 @@
content: " "; content: " ";
display: block; display: block;
position: absolute; position: absolute;
top: 10px; top: 6px;
left: 10px; left: 6px;
width: 12px; width: 10px;
height: 12px; height: 10px;
background: #bd3c35; background: #bd3c35;
border-radius: 50%; border-radius: 50%;
box-shadow: 20px 0 0 #d69f27, 40px 0 0 #39c5bb; box-shadow: 20px 0 0 #d69f27, 40px 0 0 #39c5bb;
@ -111,7 +139,8 @@
code { code {
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 1em; font-size: 0.9em;
margin-bottom: 0px;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@ -158,9 +187,14 @@
.status-container { .status-container {
font-size: 1.05rem; font-size: 1.05rem;
} }
h1 { h1 {
margin-bottom: 10%; margin-bottom: 10%;
} }
.version {
width: 7.5%;
}
} }
.form-group { .form-group {
@ -201,7 +235,7 @@
.copy-button { .copy-button {
position: absolute; position: absolute;
top: 5px; top: 10px;
right: 10px; right: 10px;
background: rgba(0, 217, 224, 0.822); background: rgba(0, 217, 224, 0.822);
color: white; color: white;
@ -211,6 +245,24 @@
cursor: pointer; cursor: pointer;
transition: opacity 0.3s; transition: opacity 0.3s;
z-index: 1; z-index: 1;
font-size: 0.85rem;
display: none;
}
.redir-button {
position: absolute;
top: 10px;
right: 65px;
background: rgba(0, 217, 224, 0.822);
color: white;
border: none;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
transition: opacity 0.3s;
z-index: 1;
font-size: 0.85rem;
display: none;
} }
pre:hover .copy-button { pre:hover .copy-button {
@ -239,6 +291,9 @@
</head> </head>
<body> <body>
<div class="version">
<p id="version"></p>
</div>
<div class="container"> <div class="container">
<h1>Github文件加速</h1> <h1>Github文件加速</h1>
<div class="form-group"> <div class="form-group">
@ -248,6 +303,7 @@
<div class="code" id="outputBlock"> <div class="code" id="outputBlock">
<button class="copy-button" id="copyButton">复制</button> <button class="copy-button" id="copyButton">复制</button>
<button class="redir-button" id="redirButton">打开</button>
<pre id="formattedLinkOutput"></pre> <pre id="formattedLinkOutput"></pre>
</div> </div>
<div class="tips"> <div class="tips">
@ -274,16 +330,22 @@
if (githubLinkInput.value.startsWith("https://github.com/") || githubLinkInput.value.startsWith("http://github.com/")) { if (githubLinkInput.value.startsWith("https://github.com/") || githubLinkInput.value.startsWith("http://github.com/")) {
formattedLink = "https://" + currentHost + "/github.com" + githubLinkInput.value.substring(githubLinkInput.value.indexOf("/", 8)); formattedLink = "https://" + currentHost + "/github.com" + githubLinkInput.value.substring(githubLinkInput.value.indexOf("/", 8));
displayButton();
} else if (githubLinkInput.value.startsWith("github.com/")) { } else if (githubLinkInput.value.startsWith("github.com/")) {
formattedLink = "https://" + currentHost + "/" + githubLinkInput.value; formattedLink = "https://" + currentHost + "/" + githubLinkInput.value;
displayButton();
} else if (githubLinkInput.value.startsWith("https://raw.githubusercontent.com/") || githubLinkInput.value.startsWith("http://raw.githubusercontent.com/")) { } else if (githubLinkInput.value.startsWith("https://raw.githubusercontent.com/") || githubLinkInput.value.startsWith("http://raw.githubusercontent.com/")) {
formattedLink = "https://" + currentHost + githubLinkInput.value.substring(githubLinkInput.value.indexOf("/", 7)); formattedLink = "https://" + currentHost + githubLinkInput.value.substring(githubLinkInput.value.indexOf("/", 7));
displayButton();
} else if (githubLinkInput.value.startsWith("raw.githubusercontent.com/")) { } else if (githubLinkInput.value.startsWith("raw.githubusercontent.com/")) {
formattedLink = "https://" + currentHost + "/" + githubLinkInput.value; formattedLink = "https://" + currentHost + "/" + githubLinkInput.value;
displayButton();
} else if (githubLinkInput.value.startsWith("https://gist.githubusercontent.com/") || githubLinkInput.value.startsWith("http://gist.githubusercontent.com/")) { } else if (githubLinkInput.value.startsWith("https://gist.githubusercontent.com/") || githubLinkInput.value.startsWith("http://gist.githubusercontent.com/")) {
formattedLink = "https://" + currentHost + "/gist.github.com" + githubLinkInput.value.substring(githubLinkInput.value.indexOf("/", 18)); formattedLink = "https://" + currentHost + "/gist.github.com" + githubLinkInput.value.substring(githubLinkInput.value.indexOf("/", 18));
displayButton();
} else if (githubLinkInput.value.startsWith("gist.githubusercontent.com/")) { } else if (githubLinkInput.value.startsWith("gist.githubusercontent.com/")) {
formattedLink = "https://" + currentHost + "/" + githubLinkInput.value; formattedLink = "https://" + currentHost + "/" + githubLinkInput.value;
displayButton();
} else { } else {
showToast('请输入有效的GitHub链接'); showToast('请输入有效的GitHub链接');
} }
@ -291,6 +353,19 @@
formattedLinkOutput.textContent = formattedLink; formattedLinkOutput.textContent = formattedLink;
} }
function displayButton() {
var copyButton = document.getElementById('copyButton');
var redirButton = document.getElementById('redirButton');
copyButton.style.display = 'block';
redirButton.style.display = 'block';
}
function redirToFormattedLink() {
var formattedLinkOutput = document.getElementById('formattedLinkOutput');
console.log(formattedLinkOutput.textContent);
window.open(formattedLinkOutput.textContent);
}
document.getElementById('formatButton').addEventListener('click', formatGithubLink); document.getElementById('formatButton').addEventListener('click', formatGithubLink);
document.getElementById('copyButton').addEventListener('click', function () { document.getElementById('copyButton').addEventListener('click', function () {
const output = document.getElementById('formattedLinkOutput'); const output = document.getElementById('formattedLinkOutput');
@ -300,9 +375,9 @@
window.getSelection().addRange(range); window.getSelection().addRange(range);
document.execCommand('copy'); document.execCommand('copy');
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
//alert('链接已复制到剪贴板');
showToast('链接已复制到剪贴板'); showToast('链接已复制到剪贴板');
}); });
document.getElementById('redirButton').addEventListener('click', redirToFormattedLink);
function showToast(message) { function showToast(message) {
const toast = document.getElementById('toast'); const toast = document.getElementById('toast');
@ -360,10 +435,22 @@
console.error('Error fetching API:', error); console.error('Error fetching API:', error);
}); });
} }
function fetchVersion() {
fetch(window.location.origin + '/api/version')
.then(response => response.json())
.then(data => {
const version = document.getElementById('version');
version.textContent = `${data.Version}`;
})
.catch(error => {
console.error('Error fetching API:', error);
});
}
function fetchAPI() { function fetchAPI() {
fetchSizeLimit(); fetchSizeLimit();
fetchWhiteList(); fetchWhiteList();
fetchBlackList(); fetchBlackList();
fetchVersion();
} }
document.addEventListener('DOMContentLoaded', fetchAPI); document.addEventListener('DOMContentLoaded', fetchAPI);
</script> </script>

View file

@ -60,14 +60,14 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter) gin.HandlerFu
username, repo := MatchUserRepo(rawPath, cfg, c, matches) username, repo := MatchUserRepo(rawPath, cfg, c, matches)
logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, username, repo) logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, username, repo)
fullrepo := fmt.Sprintf("%s/%s", username, repo) repouser := fmt.Sprintf("%s/%s", username, repo)
// 白名单检查 // 白名单检查
if cfg.Whitelist.Enabled { if cfg.Whitelist.Enabled {
whitelist := auth.CheckWhitelist(fullrepo) whitelist := auth.CheckWhitelist(repouser)
if !whitelist { if !whitelist {
logErrMsg := fmt.Sprintf("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, fullrepo) logErrMsg := fmt.Sprintf("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, repouser)
errMsg := fmt.Sprintf("Whitelist Blocked repo: %s", fullrepo) errMsg := fmt.Sprintf("Whitelist Blocked repo: %s", repouser)
c.JSON(http.StatusForbidden, gin.H{"error": errMsg}) c.JSON(http.StatusForbidden, gin.H{"error": errMsg})
logWarning(logErrMsg) logWarning(logErrMsg)
return return
@ -76,10 +76,10 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter) gin.HandlerFu
// 黑名单检查 // 黑名单检查
if cfg.Blacklist.Enabled { if cfg.Blacklist.Enabled {
blacklist := auth.CheckBlacklist(fullrepo) blacklist := auth.CheckBlacklist(repouser, username, repo)
if blacklist { if blacklist {
logErrMsg := fmt.Sprintf("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, fullrepo) logErrMsg := fmt.Sprintf("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, repouser)
errMsg := fmt.Sprintf("Blacklist Blocked repo: %s", fullrepo) errMsg := fmt.Sprintf("Blacklist Blocked repo: %s", repouser)
c.JSON(http.StatusForbidden, gin.H{"error": errMsg}) c.JSON(http.StatusForbidden, gin.H{"error": errMsg})
logWarning(logErrMsg) logWarning(logErrMsg)
return return