代码拉取完成,页面将自动刷新
同步操作将从 Ripper/Copilot后端代理服务 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
package main
import (
"context"
"crypto/tls"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"os/signal"
"path/filepath"
"ripper/pkg/certificate"
"ripper/pkg/message"
"strconv"
"syscall"
"time"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"golang.org/x/sync/errgroup"
"ripper/internal/router"
)
// 检查端口是否被占用,如果被占用则退出程序
func checkPortAndExit(host string, port int) {
addr := fmt.Sprintf("%s:%d", host, port)
conn, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("端口: %d 已被占用, 运行结束!", port)
}
conn.Close()
}
func main() {
// 设置日志输出
setupLogging()
// 在非生产环境中加载 .env 文件
if os.Getenv("ENV") != "production" {
if err := godotenv.Load(); err != nil {
log.Printf("Warning: Error loading .env file: %v", err)
}
}
log.Println("Current Environment: ", os.Getenv("ENV"))
// 设置默认环境变量
initDefaultEnv()
r := gin.Default()
// 添加 HSTS 中间件
r.Use(func(c *gin.Context) {
c.Header("Strict-Transport-Security", "max-age=0")
c.Next()
})
//初始化router
router.NewHTTPRouter(r)
//获取配置
httpPort, _ := strconv.Atoi(os.Getenv("PORT"))
httpsPort, _ := strconv.Atoi(os.Getenv("HTTPS_PORT"))
host := os.Getenv("HOST")
// 初始化证书
certFile, keyFile, reloadChan, err := certificate.InitCertificates()
if err != nil {
log.Fatalf("Failed to initialize certificates: %v", err)
}
// 检查端口是否被占用
checkPortAndExit(host, httpPort)
checkPortAndExit(host, httpsPort)
// 创建一个带取消功能的上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 创建一个错误组
g, groupCtx := errgroup.WithContext(ctx)
// 创建一个通道来表示服务器已经启动
serverStarted := make(chan struct{}, 2)
// 启动HTTP服务器
httpServer := &http.Server{
Addr: fmt.Sprintf("%s:%d", host, httpPort),
Handler: r,
}
g.Go(func() error {
log.Printf("Starting HTTP server on %s\n", httpServer.Addr)
serverStarted <- struct{}{}
return httpServer.ListenAndServe()
})
// 创建一个函数来启动HTTPS服务器
var httpsServer *http.Server
startHTTPSServer := func() *http.Server {
server := &http.Server{
Addr: fmt.Sprintf("%s:%d", host, httpsPort),
Handler: r,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS10,
MaxVersion: tls.VersionTLS13,
},
ReadTimeout: 30 * time.Second,
WriteTimeout: 60 * time.Second,
}
g.Go(func() error {
log.Printf("Starting HTTPS server on %s\n", server.Addr)
if httpsServer == nil { // 仅在首次启动时发送信号
serverStarted <- struct{}{}
}
return server.ListenAndServeTLS(certFile, keyFile)
})
return server
}
// 启动初始HTTPS服务器
httpsServer = startHTTPSServer()
// 等待两个服务器都启动
<-serverStarted
<-serverStarted
// 显示消息或消息框
message.ShowAppLaunchMessage()
// 监听证书更新和关闭信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// 处理证书更新和服务器关闭
go func() {
for {
select {
case <-reloadChan:
log.Println("Certificate update detected, reloading HTTPS server...")
// 创建关闭超时上下文
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
// 关闭当前的HTTPS服务器
if err := httpsServer.Shutdown(shutdownCtx); err != nil {
log.Printf("Error shutting down HTTPS server: %v", err)
}
shutdownCancel()
// 启动新的HTTPS服务器
httpsServer = startHTTPSServer()
case <-quit:
log.Println("Shutdown signal received, exiting...")
cancel()
return
case <-groupCtx.Done():
log.Println("Unexpected exit, trying to shutdown gracefully...")
cancel()
return
}
}
}()
// 等待取消信号
<-ctx.Done()
// 给服务器一些时间来完成正在处理的请求
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer shutdownCancel()
// 优雅地关闭服务器
if err := httpServer.Shutdown(shutdownCtx); err != nil {
log.Printf("HTTP server Shutdown: %v", err)
}
if err := httpsServer.Shutdown(shutdownCtx); err != nil {
log.Printf("HTTPS server Shutdown: %v", err)
}
// 等待所有 goroutine 完成
if err := g.Wait(); err != nil && err != http.ErrServerClosed {
log.Printf("Error during server operations: %v", err)
}
}
func setupLogging() {
// 创建日志目录
logDir := "logs"
err := os.MkdirAll(logDir, 0755)
if err != nil {
log.Fatal("无法创建日志目录:", err)
}
// 创建日志文件,使用当前日期作为文件名
currentTime := time.Now()
logFileName := filepath.Join(logDir, fmt.Sprintf("%s.log", currentTime.Format("2006-01-02")))
logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatal("无法创建日志文件:", err)
}
// 设置 gin 的日志输出到文件和控制台
gin.DefaultWriter = io.MultiWriter(logFile, os.Stdout)
// 设置标准日志输出到文件和控制台
log.SetOutput(io.MultiWriter(logFile, os.Stdout))
log.SetPrefix("[Copilot Proxies] ")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
// initDefaultEnv 初始化默认环境变量
func initDefaultEnv() {
if os.Getenv("COPILOT_PROXY_ALL") == "" {
os.Setenv("COPILOT_PROXY_ALL", "false")
}
if os.Getenv("COPILOT_CLIENT_TYPE") == "" {
os.Setenv("COPILOT_CLIENT_TYPE", "default")
}
if os.Getenv("DISGUISE_COPILOT_TOKEN_EXPIRES_AT") == "" {
os.Setenv("DISGUISE_COPILOT_TOKEN_EXPIRES_AT", "1800")
}
if os.Getenv("HTTP_CLIENT_TIMEOUT") == "" {
os.Setenv("DISGUISE_COPILOT_TOKEN_EXPIRES_AT", "60")
}
if os.Getenv("COPILOT_ACCOUNT_TYPE") == "" {
os.Setenv("COPILOT_ACCOUNT_TYPE", "individual")
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。