修复多个token请求混乱问题

This commit is contained in:
ben Gutier 2023-09-11 01:31:07 +08:00
parent 6ac0e083e6
commit 8cb096239b
4 changed files with 246 additions and 85 deletions

Binary file not shown.

View File

@ -21,7 +21,9 @@ type Config struct {
var ( var (
//初始化需要返回给客户端的响应体 //初始化需要返回给客户端的响应体
responseData map[string]interface{} tokenMap = make(map[string]map[string]interface{})
//有效的token列表
validTokenList = make(map[string]bool)
requestCountMutex sync.Mutex requestCountMutex sync.Mutex
githubApiCount = 0 githubApiCount = 0
requestCount = 0 requestCount = 0

View File

@ -15,6 +15,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
) )
@ -30,13 +31,18 @@ func main() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
// 初始化有效的token列表
initValidTokenList()
// 定义路由 // 定义路由
domainDefault := engine.Group("/", DomainMiddleware(configFile.Server.Domain)) domainDefault := engine.Group("/", DomainMiddleware(configFile.Server.Domain))
domainDefault.GET("/copilot_internal/v2/token", getToken()) domainDefault.GET("/copilot_internal/v2/token", getGithubToken())
// 初始化服务器
initServer(engine) initServer(engine)
// 显示信息 // 显示信息
showMsg() showMsg()
} }
// 初始化服务器
func initServer(engine *gin.Engine) { func initServer(engine *gin.Engine) {
// 配置支持的应用程序协议 // 配置支持的应用程序协议
server := &http.Server{ server := &http.Server{
@ -62,47 +68,28 @@ func initServer(engine *gin.Engine) {
} }
}() }()
} }
func showMsg() {
var url = ""
if configFile.Server.Port == 80 {
url = "http://" + configFile.Server.Domain
} else if configFile.Server.Port == 443 {
url = "https://" + configFile.Server.Domain
} else {
url = "http://" + configFile.Server.Domain + ":" + strconv.Itoa(configFile.Server.Port)
}
var jetStr = color.WhiteString("[Jetbrains]")
var vsStr = color.WhiteString("[Vscode]")
fmt.Println(jetStr + ": " + color.HiBlueString(url+"/copilot_internal/v2/token")) // 初始化有效的token列表
fmt.Println(vsStr + ": " + color.HiBlueString(url)) func initValidTokenList() {
fmt.Println(color.WhiteString("-----------------------------------------------------------------------")) //为了安全起见,应该等待请求完成并处理其响应。
for { var wg sync.WaitGroup
requestCountMutex.Lock() for _, token := range configFile.CopilotConfig.Token {
sCount := successCount wg.Add(1)
tCount := requestCount go func(token string) {
gCount := githubApiCount defer wg.Done()
requestCountMutex.Unlock() if getGithubApi(token) {
currentTime := time.Now().Format("2006-01-02 15:04:05") validTokenList[token] = true
if "00:00:00" == currentTime { }
resetRequestCount() }(token)
}
var s2 = color.WhiteString("[Succeed]")
var s3 = color.WhiteString("[Failed]")
var s4 = color.WhiteString("[GithubApi]")
// 打印文本
fmt.Printf("\033[G%s - %s: %s %s: %s %s: %s ",
color.HiYellowString(currentTime),
s2, color.GreenString(strconv.Itoa(sCount)),
s3, color.RedString(strconv.Itoa(tCount-sCount)),
s4, color.CyanString(strconv.Itoa(gCount)))
time.Sleep(1 * time.Second) //
} }
wg.Wait()
} }
func getToken() gin.HandlerFunc {
// 获取Github的token
func getGithubToken() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
// 请求计数 // 请求计数
incrementRequestCount() requestCount++
// 如果配置了verification则需要获取请求头中的Authorization令牌 // 如果配置了verification则需要获取请求头中的Authorization令牌
if configFile.Verification != "" { if configFile.Verification != "" {
token := c.GetHeader("Authorization") token := c.GetHeader("Authorization")
@ -110,61 +97,91 @@ func getToken() gin.HandlerFunc {
configCert := strings.ReplaceAll(configFile.Verification, " ", "") configCert := strings.ReplaceAll(configFile.Verification, " ", "")
if tokenStr != "token"+configCert { if tokenStr != "token"+configCert {
// 拒绝不符合Verification的请求 // 拒绝不符合Verification的请求
c.JSON(http.StatusBadRequest, gin.H{ badRequest(c)
"message": "Bad credentials",
"documentation_url": "https://docs.github.com/rest"})
return return
} }
} }
//判断时间戳key是否存在 //从有效的token列表中随机获取一个token
if _, exists := responseData["expires_at"]; exists { token := getRandomToken(validTokenList)
// 获取当前时间的Unix时间戳 //判断tokenMap 里的token是否存在
currentTime := time.Now().Unix() if _, exists := tokenMap[token]; exists {
if expiresAt, ok := responseData["expires_at"].(float64); ok { respDataMap := tokenMap[token]
// 判断expires_at是否已经过期 //判断时间戳key是否存在
expiresAtInt64 := int64(expiresAt) if _, exists := respDataMap["expires_at"]; exists {
//提前一分钟请求 // 获取当前时间的Unix时间戳
if expiresAtInt64 > currentTime+60 { currentTime := time.Now().Unix()
//fmt.Println("\n未过期无需请求") if expiresAt, ok := respDataMap["expires_at"].(float64); ok {
respProxy(c) // 判断expires_at是否已经过期
expiresAtInt64 := int64(expiresAt)
//提前一分钟请求
if expiresAtInt64 > currentTime+60 {
//fmt.Println("\n未过期无需请求")
proxyResp(c, tokenMap[token])
} else {
//fmt.Println("\n已过期重新请求")
if getGithubApi(token) {
proxyResp(c, tokenMap[token])
return
} else {
badRequest(c)
}
}
} else { } else {
//fmt.Println("\n已过期重新请求") badRequest(c)
getGithubApi(c)
respProxy(c)
} }
} else { } else {
fmt.Println("Age is not an int") //tokenMap里的token对应的返回体不存在expires_at
if getGithubApi(token) {
proxyResp(c, tokenMap[token])
return
} else {
badRequest(c)
}
} }
} else { } else {
//向githubApi发送请求 //不存在则githubApi发送请求并存到tokenMap
//fmt.Println("\n第一次请求") if getGithubApi(token) {
getGithubApi(c) proxyResp(c, tokenMap[token])
respProxy(c) return
} else {
badRequest(c)
}
} }
} }
} }
func respProxy(c *gin.Context) {
// 请求错误
func badRequest(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{
"message": "Bad credentials",
"documentation_url": "https://docs.github.com/rest"})
}
// 本服务器响应
func proxyResp(c *gin.Context, respDataMap map[string]interface{}) {
// 将map转换为JSON字符串 // 将map转换为JSON字符串
responseJSON, err := json.Marshal(responseData) responseJSON, err := json.Marshal(respDataMap)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "JSON marshaling error"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "JSON marshaling error"})
} }
// 请求成功统计 // 请求成功统计
incrementSuccessCount() successCount++
// 将JSON字符串作为响应体返回 // 将JSON字符串作为响应体返回
c.Header("Content-Type", "application/json") c.Header("Content-Type", "application/json")
c.String(http.StatusOK, string(responseJSON)) c.String(http.StatusOK, string(responseJSON))
} }
func getGithubApi(c *gin.Context) {
// 请求githubApi
func getGithubApi(token string) bool {
githubApiCount++ githubApiCount++
// 设置请求头 // 设置请求头
headers := map[string]string{ headers := map[string]string{
"Authorization": "token " + getRandomToken(configFile.CopilotConfig.Token), "Authorization": "token " + token,
"editor-version": c.GetHeader("editor-version"), /*"editor-version": c.GetHeader("editor-version"),
"editor-plugin-version": c.GetHeader("editor-plugin-version"), "editor-plugin-version": c.GetHeader("editor-plugin-version"),
"user-agent": c.GetHeader("user-agent"), "user-agent": c.GetHeader("user-agent"),
"accept": c.GetHeader("accept"), "accept": c.GetHeader("accept"),
"accept-encoding": c.GetHeader("accept-encoding"), "accept-encoding": c.GetHeader("accept-encoding"),*/
} }
// 发起GET请求 // 发起GET请求
response, err := resty.New().R(). response, err := resty.New().R().
@ -172,17 +189,28 @@ func getGithubApi(c *gin.Context) {
Get(configFile.CopilotConfig.GithubApiUrl) Get(configFile.CopilotConfig.GithubApiUrl)
if err != nil { if err != nil {
// 处理请求错误 // 处理请求错误
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return false
return
} }
// 判断响应状态码
err = json.Unmarshal(response.Body(), &responseData) if response.StatusCode() == http.StatusOK {
if err != nil { // 响应状态码为200 OK
// 处理JSON解析错误 respDataMap := map[string]interface{}{}
c.JSON(http.StatusInternalServerError, gin.H{"error": "JSON parsing error"}) err = json.Unmarshal(response.Body(), &respDataMap)
return if err != nil {
// 处理JSON解析错误
return false
}
//token map
tokenMap[token] = respDataMap
return true
} else {
// 处理其他状态码
delete(validTokenList, token)
return false
} }
} }
// 初始化配置文件
func initConfig() Config { func initConfig() Config {
// 读取配置文件 // 读取配置文件
exePath, err := os.Executable() exePath, err := os.Executable()
@ -209,26 +237,30 @@ func initConfig() Config {
} }
return config return config
} }
func incrementRequestCount() {
requestCount++ // 重置请求计数
}
func incrementSuccessCount() {
successCount++
}
func resetRequestCount() { func resetRequestCount() {
requestCountMutex.Lock() requestCountMutex.Lock()
defer requestCountMutex.Unlock() defer requestCountMutex.Unlock()
requestCount = 0 requestCount = 0
successCount = 0 successCount = 0
} }
func getRandomToken(tokens []string) string {
if len(tokens) == 0 { // 从map中随机获取一个key
func getRandomToken(m map[string]bool) string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
if len(keys) == 0 {
return "" // 返回空字符串或处理其他错误情况 return "" // 返回空字符串或处理其他错误情况
} }
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
randomIndex := r.Intn(len(tokens)) randomIndex := r.Intn(len(keys))
return tokens[randomIndex] return keys[randomIndex]
} }
// DomainMiddleware 域名中间件
func DomainMiddleware(domain string) gin.HandlerFunc { func DomainMiddleware(domain string) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
// 检查域名是否匹配 // 检查域名是否匹配
@ -241,3 +273,44 @@ func DomainMiddleware(domain string) gin.HandlerFunc {
} }
} }
} }
// 显示信息
func showMsg() {
var url = ""
if configFile.Server.Port == 80 {
url = "http://" + configFile.Server.Domain
} else if configFile.Server.Port == 443 {
url = "https://" + configFile.Server.Domain
} else {
url = "http://" + configFile.Server.Domain + ":" + strconv.Itoa(configFile.Server.Port)
}
var jetStr = color.WhiteString("[Jetbrains]")
var vsStr = color.WhiteString("[Vscode]")
var valid = color.WhiteString("[Valid tokens]")
fmt.Println(jetStr + ": " + color.HiBlueString(url+"/copilot_internal/v2/token"))
fmt.Println(vsStr + ": " + color.HiBlueString(url))
fmt.Println(valid + ": " + color.HiBlueString(strconv.Itoa(len(validTokenList))))
fmt.Println(color.WhiteString("-----------------------------------------------------------------------"))
for {
requestCountMutex.Lock()
sCount := successCount
tCount := requestCount
gCount := githubApiCount
requestCountMutex.Unlock()
currentTime := time.Now().Format("2006-01-02 15:04:05")
if "00:00:00" == currentTime {
resetRequestCount()
}
var s2 = color.WhiteString("[Succeed]")
var s3 = color.WhiteString("[Failed]")
var s4 = color.WhiteString("[GithubApi]")
// 打印文本
fmt.Printf("\033[G%s - %s: %s %s: %s %s: %s ",
color.HiYellowString(currentTime),
s2, color.GreenString(strconv.Itoa(sCount)),
s3, color.RedString(strconv.Itoa(tCount-sCount)),
s4, color.CyanString(strconv.Itoa(gCount)))
time.Sleep(1 * time.Second) //
}
}

86
source/test.go Normal file
View File

@ -0,0 +1,86 @@
package main
import (
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
)
func getGithubTest(c *gin.Context, token string) {
githubApiCount++
data1 := `
{
"chat_enabled": false,
"code_quote_enabled": false,
"code_quote_v2_enabled": false,
"copilotignore_enabled": false,
"expires_at": 3194360727,
"prompt_8k": true,
"public_suggestions": "disabled",
"refresh_in": 1500,
"sku": "free_educational",
"telemetry": "disabled",
"token": "tid=;exp=1694360727;sku=free_educational;st=dotcom;8kp=1:",
"tracking_id": ""
}
`
data2 := `
{
"chat_enabled": false,
"code_quote_enabled": false,
"code_quote_v2_enabled": false,
"copilotignore_enabled": false,
"expires_at": 2294360727,
"prompt_8k": true,
"public_suggestions": "disabled",
"refresh_in": 1500,
"sku": "free_educational",
"telemetry": "disabled",
"token": "tid=;exp=1694360727;sku=free_educational;st=dotcom;8kp=1:",
"tracking_id": ""
}
`
data3 := `
{
"chat_enabled": false,
"code_quote_enabled": false,
"code_quote_v2_enabled": false,
"copilotignore_enabled": false,
"expires_at": 3394360727,
"prompt_8k": true,
"public_suggestions": "disabled",
"refresh_in": 1500,
"sku": "free_educational",
"telemetry": "disabled",
"token": "tid=;exp=1694360727;sku=free_educational;st=dotcom;8kp=1:",
"tracking_id": ""
}
`
//响应体map
var respDataMap = make(map[string]interface{})
if token == "1" {
err := json.Unmarshal([]byte(data1), &respDataMap)
if err != nil {
// 处理JSON解析错误
c.JSON(http.StatusInternalServerError, gin.H{"error": "JSON parsing error"})
return
}
} else if token == "2" {
err := json.Unmarshal([]byte(data2), &respDataMap)
if err != nil {
// 处理JSON解析错误
c.JSON(http.StatusInternalServerError, gin.H{"error": "JSON parsing error"})
return
}
} else {
err := json.Unmarshal([]byte(data3), &respDataMap)
if err != nil {
// 处理JSON解析错误
c.JSON(http.StatusInternalServerError, gin.H{"error": "JSON parsing error"})
return
}
}
//token map
tokenMap[token] = respDataMap
}