package main import ( "crypto/tls" "encoding/json" "fmt" "github.com/fatih/color" "github.com/gin-gonic/gin" "github.com/go-resty/resty/v2" "io" "log" "math/rand" "net/http" "os" "path/filepath" "strconv" "strings" "sync" "time" ) type Config struct { Server struct { Domain string `json:"domain"` Host string `json:"host"` Port int `json:"port"` CertPath string `json:"certPath"` KeyPath string `json:"keyPath"` } `json:"server"` CopilotConfig struct { GithubApiUrl string `json:"github_api_url"` Token []string `json:"token"` } `json:"copilot_config"` Verification string `json:"verification"` } var ( requestCountMutex sync.Mutex requestCount int successCount int configFile = Config{} ) func main() { gin.SetMode(gin.ReleaseMode) gin.DefaultWriter = io.Discard //初始化配置文件 configFile = initConfig() // 创建Gin引擎 engine := gin.New() // 自定义错误处理程序 engine.Use(func(c *gin.Context) { c.Next() err := c.Errors.Last() if err != nil { log.Printf("Error: %v", err.Err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"}) } }) // 设置信任的代理 err := engine.SetTrustedProxies([]string{"127.0.0.1"}) if err != nil { log.Fatal(err) } // 配置支持的应用程序协议 server := &http.Server{ Addr: ":443", TLSConfig: &tls.Config{ NextProtos: []string{"http/1.1", "http/1.2", "http/2"}, // 支持的应用程序协议列表 }, Handler: engine, } // 定义路由 domainDefault := engine.Group("/", DomainMiddleware(configFile.Server.Domain)) domainDefault.GET("/copilot_internal/v2/token", getToken()) // 启动Gin服务器并监听端口 listenAddress := configFile.Server.Host + ":" + strconv.Itoa(configFile.Server.Port) go func() { if configFile.Server.Port != 443 { err := engine.Run(listenAddress) if err != nil { log.Fatal(err) } } else { err := server.ListenAndServeTLS(configFile.Server.CertPath, configFile.Server.KeyPath) if err != nil { log.Fatal(err) } } }() // 显示url displayMsg() ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { showRequestCount() } } func getToken() gin.HandlerFunc { return func(c *gin.Context) { // 请求计数 incrementRequestCount() // 获取请求头中的Authorization令牌 if configFile.Verification != "" { token := c.GetHeader("Authorization") tokenStr := strings.ReplaceAll(token, " ", "") configCert := strings.ReplaceAll(configFile.Verification, " ", "") if tokenStr != "token"+configCert { // 拒绝不符合Verification的请求 c.JSON(http.StatusBadRequest, gin.H{ "message": "Bad credentials", "documentation_url": "https://docs.github.com/rest"}) return } } // 设置请求头 headers := map[string]string{ "Authorization": "token " + getRandomToken(configFile.CopilotConfig.Token), "editor-version": c.GetHeader("editor-version"), "editor-plugin-version": c.GetHeader("editor-plugin-version"), "user-agent": c.GetHeader("user-agent"), "accept": c.GetHeader("accept"), "accept-encoding": c.GetHeader("accept-encoding"), } // 发起GET请求 response, err := resty.New().R(). SetHeaders(headers). Get(configFile.CopilotConfig.GithubApiUrl) if err != nil { // 处理请求错误 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // 检查响应状态码 if response.StatusCode() != http.StatusOK { // 处理非200响应 c.JSON(response.StatusCode(), gin.H{"error": "Request failed"}) return } // 请求成功统计 incrementSuccessCount() // 从响应中获取内容并发送回客户端 responseBody := response.String() // 设置响应头 重要 c.Header("Content-Type", "application/json") c.String(http.StatusOK, responseBody) } } func initConfig() Config { // 打开或创建 error.log 文件 logFile, err := os.OpenFile("error.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer logFile.Close() // 设置日志输出到文件 log.SetOutput(logFile) // 读取配置文件 exePath, err := os.Executable() if err != nil { panic(err) } // 获取可执行文件所在目录 exeDir := filepath.Dir(exePath) configFile, err := os.Open(exeDir + "/config.json") if err != nil { panic("file \"./config.json\" not found") } defer func(configFile *os.File) { err := configFile.Close() if err != nil { panic("close file \"./config.json\" err") } }(configFile) decoder := json.NewDecoder(configFile) config := Config{} err = decoder.Decode(&config) if err != nil { panic("config format err") } return config } func displayMsg() { c := color.New(color.FgGreen) fmt.Printf("\n------------------------------------------------------------\n") if configFile.Server.Port == 80 { _, err := c.Println("[ApiUri] http://" + configFile.Server.Domain + "/copilot_internal/v2/token\r") if err != nil { return } } else if configFile.Server.Port == 443 { _, err := c.Println("[ApiUrl] https://" + configFile.Server.Domain + "/copilot_internal/v2/token\r") if err != nil { return } } else { _, err := c.Println("[ApiUrl] http://" + configFile.Server.Domain + ":" + strconv.Itoa(configFile.Server.Port) + "/copilot_internal/v2/token\r") if err != nil { return } } fmt.Printf("------------------------------------------------------------\n") } func incrementRequestCount() { requestCount++ } func incrementSuccessCount() { successCount++ } func showRequestCount() { requestCountMutex.Lock() count := requestCount sCount := successCount requestCountMutex.Unlock() currentTime := time.Now().Format("2006-01-02 15:04:05") if time.Now().Format("15:04:05") == "00:00:00" { resetRequestCount() } // 黄色文本 timeStr := fmt.Sprintf("\x1b[33m%s\x1b[0m", currentTime) // 蓝色文本 countStr := fmt.Sprintf("\x1b[34m%d\x1b[0m", count) // 绿色文本 successCountStr := fmt.Sprintf("\x1b[32m%d\x1b[0m", sCount) failureCountStr := fmt.Sprintf("\x1b[31m%d\x1b[0m", count-sCount) fmt.Printf("\033[1G%s - Total Count: %s | Success Count %s | Fail Count %s ", timeStr, countStr, successCountStr, failureCountStr) } func resetRequestCount() { requestCountMutex.Lock() defer requestCountMutex.Unlock() requestCount = 0 successCount = 0 } func getRandomToken(tokens []string) string { if len(tokens) == 0 { return "" // 返回空字符串或处理其他错误情况 } r := rand.New(rand.NewSource(time.Now().UnixNano())) randomIndex := r.Intn(len(tokens)) return tokens[randomIndex] } func DomainMiddleware(domain string) gin.HandlerFunc { return func(c *gin.Context) { // 检查域名是否匹配 requestDomain := strings.Split(c.Request.Host, ":")[0] if requestDomain == domain { c.Next() } else { c.String(403, "Forbidden") c.Abort() } } }