提交
This commit is contained in:
242
main.go
Normal file
242
main.go
Normal file
@@ -0,0 +1,242 @@
|
||||
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")
|
||||
}
|
||||
|
||||
if os.Getenv("LIGHTWEIGHT_MODEL") == "" {
|
||||
os.Setenv("LIGHTWEIGHT_MODEL", "gpt-4o-mini")
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user