first commit
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
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
This commit is contained in:
346
internal/http/server.go
Normal file
346
internal/http/server.go
Normal file
@@ -0,0 +1,346 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user