Files
subconverter-go/internal/http/server.go
Rogee 7fcabe0225
Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped
first commit
2025-09-28 10:05:07 +08:00

346 lines
8.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package http
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/limiter"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/subconverter-go/internal/config"
"github.com/subconverter-go/internal/logging"
)
// Server HTTP服务器封装
// 封装Fiber HTTP服务器功能提供统一的HTTP服务接口
type Server struct {
app *fiber.App
config *config.ServerConfig
logger *logging.Logger
manager *config.ConfigManager
startTime time.Time
}
// NewServer 创建新的HTTP服务器
// 返回初始化好的Server实例
func NewServer(manager *config.ConfigManager) (*Server, error) {
if manager == nil {
return nil, fmt.Errorf("config manager cannot be nil")
}
// 获取日志记录器
logger, err := logging.NewDefaultLogger()
if err != nil {
return nil, fmt.Errorf("failed to create logger: %v", err)
}
// 获取服务器配置
serverConfig := manager.GetServerConfig()
if serverConfig == nil {
return nil, fmt.Errorf("server config cannot be nil")
}
// 创建Fiber应用
app := fiber.New(fiber.Config{
AppName: "subconverter-go",
ServerHeader: "subconverter-go",
ReadTimeout: time.Duration(serverConfig.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(serverConfig.WriteTimeout) * time.Second,
IdleTimeout: time.Duration(serverConfig.WriteTimeout) * time.Second,
BodyLimit: int(serverConfig.MaxRequestSize),
CaseSensitive: false,
StrictRouting: false,
EnablePrintRoutes: false,
})
server := &Server{
app: app,
config: serverConfig,
logger: logger,
manager: manager,
startTime: time.Now(),
}
// 设置中间件
server.setupMiddleware()
// 设置路由
server.setupRoutes()
return server, nil
}
// setupMiddleware 设置中间件
func (s *Server) setupMiddleware() {
// 恢复中间件 - 处理panic
s.app.Use(recover.New(recover.Config{
EnableStackTrace: true,
StackTraceHandler: func(c *fiber.Ctx, e any) {
s.logger.WithField("error", e).Error("Panic recovered")
},
}))
// 日志中间件
s.app.Use(logger.New(logger.Config{
Format: "${time} | ${status} | ${latency} | ${ip} | ${method} | ${path} | ${error}\n",
Output: s.logger.Logger.Out,
}))
// CORS中间件
s.setupCORS()
// 速率限制中间件
s.setupRateLimiter()
}
// setupCORS 设置CORS
func (s *Server) setupCORS() {
securityConfig := s.manager.GetSecurityConfig()
if securityConfig == nil {
securityConfig = &config.SecurityConfig{
CorsOrigins: []string{"*"},
}
}
s.app.Use(cors.New(cors.Config{
AllowOrigins: strings.Join(securityConfig.CorsOrigins, ","),
AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
AllowHeaders: "*",
MaxAge: 86400, // 24 hours
}))
}
// setupRateLimiter 设置速率限制
func (s *Server) setupRateLimiter() {
securityConfig := s.manager.GetSecurityConfig()
if securityConfig == nil || securityConfig.RateLimit <= 0 {
// 不启用速率限制
return
}
s.app.Use(limiter.New(limiter.Config{
Max: securityConfig.RateLimit,
Expiration: 60 * time.Second, // 1分钟窗口
KeyGenerator: func(c *fiber.Ctx) string {
return c.IP()
},
LimitReached: func(c *fiber.Ctx) error {
s.logger.WithField("ip", c.IP()).Warn("Rate limit exceeded")
return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
"error": "Rate limit exceeded",
})
},
}))
}
// setupRoutes 设置路由
func (s *Server) setupRoutes() {
// 健康检查路由
s.app.Get("/health", s.healthCheck)
// 版本信息路由
s.app.Get("/version", s.versionInfo)
// 根路由
s.app.Get("/", s.rootHandler)
// API路由组
api := s.app.Group("/api")
// 转换相关路由
api.Get("/sub", s.convertHandler)
api.Post("/sub", s.convertHandler)
// 配置相关路由
api.Get("/config", s.getConfigHandler)
api.Post("/config", s.updateConfigHandler)
// 代理相关路由
api.Get("/proxy/validate", s.validateProxyHandler)
api.Post("/proxy/validate", s.validateProxyHandler)
// 统计信息路由
api.Get("/stats", s.statsHandler)
}
// Start 启动HTTP服务器
func (s *Server) Start() error {
s.logger.Infof("Starting HTTP server on %s:%d", s.config.Host, s.config.Port)
// 创建监听地址
addr := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port)
// 启动服务器
go func() {
if err := s.app.Listen(addr); err != nil && err != http.ErrServerClosed {
s.logger.WithError(err).Error("Failed to start HTTP server")
os.Exit(1)
}
}()
s.logger.Infof("HTTP server started successfully")
return nil
}
// Stop 停止HTTP服务器
func (s *Server) Stop() error {
s.logger.Info("Stopping HTTP server...")
// 创建上下文设置5秒超时
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 优雅关闭服务器
if err := s.app.ShutdownWithContext(ctx); err != nil {
s.logger.WithError(err).Error("Failed to shutdown HTTP server gracefully")
return err
}
s.logger.Info("HTTP server stopped successfully")
return nil
}
// WaitForShutdown 等待服务器关闭信号
func (s *Server) WaitForShutdown() {
// 创建信号通道
quit := make(chan os.Signal, 1)
// 监听中断信号
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// 等待信号
<-quit
s.logger.Info("Received shutdown signal, shutting down server...")
// 停止服务器
if err := s.Stop(); err != nil {
s.logger.WithError(err).Error("Error during server shutdown")
}
}
// GetApp 获取Fiber应用实例
// 用于测试或扩展功能
func (s *Server) GetApp() *fiber.App {
return s.app
}
// GetUptime 获取服务器运行时间
func (s *Server) GetUptime() time.Duration {
return time.Since(s.startTime)
}
// GetStats 获取服务器统计信息
func (s *Server) GetStats() map[string]interface{} {
return map[string]interface{}{
"uptime": s.GetUptime().String(),
"start_time": s.startTime.Format(time.RFC3339),
"host": s.config.Host,
"port": s.config.Port,
"max_request_size": s.config.MaxRequestSize,
"read_timeout": s.config.ReadTimeout,
"write_timeout": s.config.WriteTimeout,
}
}
// 健康检查处理器
func (s *Server) healthCheck(c *fiber.Ctx) error {
health := map[string]interface{}{
"status": "healthy",
"uptime": s.GetUptime().String(),
"version": "1.0.0",
"host": s.config.Host,
"port": s.config.Port,
}
return c.JSON(health)
}
// 版本信息处理器
func (s *Server) versionInfo(c *fiber.Ctx) error {
version := map[string]interface{}{
"version": "1.0.0",
"name": "subconverter-go",
"description": "Proxy subscription converter service",
"author": "subconverter-go team",
"uptime": s.GetUptime().String(),
}
return c.JSON(version)
}
// 根路径处理器
func (s *Server) rootHandler(c *fiber.Ctx) error {
info := map[string]interface{}{
"service": "subconverter-go",
"version": "1.0.0",
"status": "running",
"endpoints": []string{
"GET /health - Health check",
"GET /version - Version information",
"GET /api/sub - Convert subscription",
"POST /api/sub - Convert subscription",
"GET /api/config - Get configuration",
"POST /api/config - Update configuration",
"GET /api/proxy/validate - Validate proxy",
"POST /api/proxy/validate - Validate proxy",
"GET /api/stats - Server statistics",
},
"uptime": s.GetUptime().String(),
}
return c.JSON(info)
}
// 转换处理器(占位符)
func (s *Server) convertHandler(c *fiber.Ctx) error {
s.logger.Info("Convert handler called (placeholder)")
return c.Status(fiber.StatusNotImplemented).JSON(fiber.Map{
"error": "Conversion handler not implemented yet",
})
}
// 获取配置处理器(占位符)
func (s *Server) getConfigHandler(c *fiber.Ctx) error {
s.logger.Info("Get config handler called (placeholder)")
return c.Status(fiber.StatusNotImplemented).JSON(fiber.Map{
"error": "Get config handler not implemented yet",
})
}
// 更新配置处理器(占位符)
func (s *Server) updateConfigHandler(c *fiber.Ctx) error {
s.logger.Info("Update config handler called (placeholder)")
return c.Status(fiber.StatusNotImplemented).JSON(fiber.Map{
"error": "Update config handler not implemented yet",
})
}
// 验证代理处理器(占位符)
func (s *Server) validateProxyHandler(c *fiber.Ctx) error {
s.logger.Info("Validate proxy handler called (placeholder)")
return c.Status(fiber.StatusNotImplemented).JSON(fiber.Map{
"error": "Validate proxy handler not implemented yet",
})
}
// 统计信息处理器(占位符)
func (s *Server) statsHandler(c *fiber.Ctx) error {
stats := s.GetStats()
stats["requests"] = 0 // 占位符
stats["errors"] = 0 // 占位符
return c.JSON(stats)
}