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
333 lines
10 KiB
Go
333 lines
10 KiB
Go
package service
|
||
|
||
import (
|
||
"fmt"
|
||
"net/http"
|
||
"os"
|
||
"os/signal"
|
||
"syscall"
|
||
"time"
|
||
|
||
"github.com/gofiber/fiber/v2"
|
||
"github.com/subconverter-go/internal/cache"
|
||
"github.com/subconverter-go/internal/config"
|
||
"github.com/subconverter-go/internal/conversion"
|
||
"github.com/subconverter-go/internal/generator"
|
||
"github.com/subconverter-go/internal/handler"
|
||
"github.com/subconverter-go/internal/logging"
|
||
"github.com/subconverter-go/internal/middleware"
|
||
"github.com/subconverter-go/internal/parser"
|
||
)
|
||
|
||
// Application 应用程序主服务
|
||
// 整合所有组件并提供统一的启动和管理接口
|
||
type Application struct {
|
||
config *config.ConfigManager
|
||
logger *logging.Logger
|
||
httpServer *fiber.App
|
||
cacheMgr *cache.CacheManager
|
||
convEngine *conversion.ConversionEngine
|
||
parserMgr *parser.ParserManager
|
||
generatorMgr *generator.GeneratorManager
|
||
conversionHandler *handler.ConversionHandler
|
||
health *handler.HealthHandler
|
||
version *handler.VersionHandler
|
||
templateHandler *handler.TemplateHandler
|
||
}
|
||
|
||
// NewApplication 创建新的应用程序实例
|
||
func NewApplication(configPath string) (*Application, error) {
|
||
// 创建日志记录器
|
||
logger, err := logging.NewDefaultLogger()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create logger: %v", err)
|
||
}
|
||
|
||
// 加载配置
|
||
configMgr, err := config.NewConfigManager(configPath)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to load config: %v", err)
|
||
}
|
||
|
||
// 创建解析器管理器
|
||
parserMgr := parser.NewParserManager(logger, configMgr)
|
||
|
||
// 创建生成器管理器
|
||
generatorMgr := generator.NewGeneratorManager(logger)
|
||
|
||
// 创建缓存管理器
|
||
appConfig := configMgr.GetConfig()
|
||
cacheConfig := &cache.CacheConfig{
|
||
Enabled: appConfig.Conversion.CacheTimeout > 0, // 如果超时时间大于0则启用缓存
|
||
TTL: time.Duration(appConfig.Conversion.CacheTimeout) * time.Minute,
|
||
MaxSize: appConfig.Conversion.MaxNodes,
|
||
Cleanup: time.Minute, // 固定清理间隔
|
||
}
|
||
cacheMgr := cache.NewCacheManager(cacheConfig, logger)
|
||
|
||
// 创建转换引擎
|
||
convEngine := conversion.NewConversionEngine(logger, parserMgr, generatorMgr)
|
||
|
||
// 创建处理器
|
||
health := handler.NewHealthHandler(logger, configMgr)
|
||
version := handler.NewVersionHandler(logger, configMgr)
|
||
conversionHandler := handler.NewConversionHandler(convEngine, cacheMgr, configMgr, logger)
|
||
|
||
// 创建模板处理器(如果配置管理器支持)
|
||
var templateHandler *handler.TemplateHandler
|
||
if configMgr.HasTemplateManager() {
|
||
templateHandler = handler.NewTemplateHandler(configMgr.GetTemplateManager(), logger, configMgr)
|
||
logger.Info("Template handler initialized")
|
||
} else {
|
||
logger.Warn("Template manager not available, template features disabled")
|
||
}
|
||
|
||
// 创建HTTP服务器
|
||
httpServer := createHTTPServer(configMgr, logger, conversionHandler, health, version, templateHandler)
|
||
|
||
app := &Application{
|
||
config: configMgr,
|
||
logger: logger,
|
||
httpServer: httpServer,
|
||
cacheMgr: cacheMgr,
|
||
convEngine: convEngine,
|
||
parserMgr: parserMgr,
|
||
generatorMgr: generatorMgr,
|
||
conversionHandler: conversionHandler,
|
||
health: health,
|
||
version: version,
|
||
templateHandler: templateHandler,
|
||
}
|
||
|
||
return app, nil
|
||
}
|
||
|
||
// createHTTPServer 创建HTTP服务器
|
||
func createHTTPServer(
|
||
configMgr *config.ConfigManager,
|
||
logger *logging.Logger,
|
||
conversionHandler *handler.ConversionHandler,
|
||
health *handler.HealthHandler,
|
||
version *handler.VersionHandler,
|
||
templateHandler *handler.TemplateHandler,
|
||
) *fiber.App {
|
||
// 获取服务器配置
|
||
serverConfig := configMgr.GetServerConfig()
|
||
securityConfig := configMgr.GetSecurityConfig()
|
||
|
||
// 创建Fiber应用
|
||
app := fiber.New(fiber.Config{
|
||
AppName: "SubConverter-Go",
|
||
ServerHeader: "",
|
||
ReadTimeout: time.Duration(serverConfig.ReadTimeout) * time.Second,
|
||
WriteTimeout: time.Duration(serverConfig.WriteTimeout) * time.Second,
|
||
BodyLimit: int(serverConfig.MaxRequestSize),
|
||
})
|
||
|
||
// 设置中间件
|
||
app.Use(middleware.SetupRecovery(logger))
|
||
app.Use(middleware.SetupRequestID())
|
||
app.Use(middleware.SetupSecurity())
|
||
app.Use(middleware.SetupLogging(logger))
|
||
app.Use(middleware.SetupCORS())
|
||
app.Use(middleware.SetupCompression())
|
||
|
||
// 设置限流中间件(如果启用)
|
||
if securityConfig.RateLimit > 0 {
|
||
app.Use(middleware.SetupRateLimit(securityConfig.RateLimit, 60))
|
||
}
|
||
|
||
// 预备认证中间件(仅对需要保护的路由使用)
|
||
var authMiddleware fiber.Handler
|
||
if len(securityConfig.AccessTokens) > 0 {
|
||
authMiddleware = middleware.SetupAuthentication(logger, securityConfig.AccessTokens)
|
||
}
|
||
|
||
// 注册路由
|
||
setupRoutes(app, conversionHandler, health, version, templateHandler, configMgr, authMiddleware)
|
||
|
||
return app
|
||
}
|
||
|
||
// setupRoutes 设置路由
|
||
func setupRoutes(
|
||
app *fiber.App,
|
||
conversionHandler *handler.ConversionHandler,
|
||
health *handler.HealthHandler,
|
||
version *handler.VersionHandler,
|
||
templateHandler *handler.TemplateHandler,
|
||
configMgr *config.ConfigManager,
|
||
auth fiber.Handler,
|
||
) {
|
||
register := func(method, path string, handler fiber.Handler, protected bool) {
|
||
handlers := make([]fiber.Handler, 0, 2)
|
||
if protected && auth != nil {
|
||
handlers = append(handlers, auth)
|
||
}
|
||
handlers = append(handlers, handler)
|
||
app.Add(method, path, handlers...)
|
||
}
|
||
|
||
// 健康检查路由
|
||
register(fiber.MethodGet, "/health", health.HandleHealth, false)
|
||
register(fiber.MethodGet, "/healthz", health.HandleHealth, false)
|
||
register(fiber.MethodGet, "/ready", health.HandleReady, false)
|
||
register(fiber.MethodGet, "/live", health.HandleLive, false)
|
||
register(fiber.MethodGet, "/metrics", health.HandleMetrics, false)
|
||
register(fiber.MethodGet, "/ping", health.HandlePing, false)
|
||
|
||
// 版本信息路由
|
||
register(fiber.MethodGet, "/version", version.HandleVersion, false)
|
||
|
||
// 维护类路由(兼容C++实现)
|
||
register(fiber.MethodGet, "/refreshrules", conversionHandler.HandleRefreshRules, true)
|
||
register(fiber.MethodGet, "/readconf", conversionHandler.HandleReadConf, true)
|
||
register(fiber.MethodPost, "/updateconf", conversionHandler.HandleUpdateConfig, true)
|
||
register(fiber.MethodGet, "/flushcache", conversionHandler.HandleFlushCache, true)
|
||
|
||
// API路由组
|
||
api := app.Group("/api")
|
||
|
||
// 转换路由
|
||
api.Get("/convert", conversionHandler.HandleConversion)
|
||
api.Post("/convert", conversionHandler.HandleConversion)
|
||
api.Get("/sub", conversionHandler.HandleSubscription)
|
||
|
||
// 兼容性路由(与原始SubConverter兼容)
|
||
register(fiber.MethodGet, "/sub", conversionHandler.HandleSubscription, false)
|
||
register(fiber.MethodHead, "/sub", conversionHandler.HandleSubscription, false)
|
||
register(fiber.MethodGet, "/sub2clashr", conversionHandler.HandleSubToClashR, false)
|
||
register(fiber.MethodGet, "/surge2clash", conversionHandler.HandleSurgeToClash, false)
|
||
register(fiber.MethodGet, "/getruleset", conversionHandler.HandleGetRulesetLegacy, false)
|
||
register(fiber.MethodGet, "/getprofile", conversionHandler.HandleGetProfile, true)
|
||
register(fiber.MethodGet, "/get", conversionHandler.HandleGetRemote, false)
|
||
register(fiber.MethodGet, "/getlocal", conversionHandler.HandleGetLocal, false)
|
||
|
||
// 缓存管理路由
|
||
cache := app.Group("/cache")
|
||
if auth != nil {
|
||
cache.Use(auth)
|
||
}
|
||
cache.Get("/stats", conversionHandler.HandleCacheStats)
|
||
cache.Delete("/clear", conversionHandler.HandleCacheClear)
|
||
|
||
// 配置管理路由
|
||
config := app.Group("/config")
|
||
if auth != nil {
|
||
config.Use(auth)
|
||
}
|
||
config.Get("/", conversionHandler.HandleGetConfig)
|
||
config.Post("/", conversionHandler.HandleUpdateConfig)
|
||
|
||
// 模板管理路由(如果模板处理器可用)
|
||
if templateHandler != nil {
|
||
templateHandler.RegisterRoutes(app)
|
||
}
|
||
|
||
// 404处理
|
||
app.Use(func(c *fiber.Ctx) error {
|
||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||
"success": false,
|
||
"error": "Not Found",
|
||
"message": "The requested resource was not found",
|
||
})
|
||
})
|
||
}
|
||
|
||
// Start 启动应用程序
|
||
func (app *Application) Start() error {
|
||
// 获取服务器配置
|
||
serverConfig := app.config.GetServerConfig()
|
||
addr := fmt.Sprintf("%s:%d", serverConfig.Host, serverConfig.Port)
|
||
|
||
// 启动HTTP服务器
|
||
go func() {
|
||
app.logger.Infof("Starting HTTP server on %s", addr)
|
||
if err := app.httpServer.Listen(addr); err != nil && err != http.ErrServerClosed {
|
||
app.logger.WithError(err).Error("Failed to start HTTP server")
|
||
}
|
||
}()
|
||
|
||
// 等待服务器启动
|
||
time.Sleep(100 * time.Millisecond)
|
||
|
||
app.logger.Info("Application started successfully")
|
||
app.logger.Infof("Server is listening on %s", addr)
|
||
app.logger.Infof("Health check: http://%s/health", addr)
|
||
app.logger.Infof("API documentation: http://%s/api", addr)
|
||
|
||
return nil
|
||
}
|
||
|
||
// Stop 停止应用程序
|
||
func (app *Application) Stop() error {
|
||
app.logger.Info("Shutting down application...")
|
||
|
||
// 关闭HTTP服务器
|
||
if err := app.httpServer.Shutdown(); err != nil {
|
||
app.logger.WithError(err).Error("Failed to shutdown HTTP server")
|
||
}
|
||
|
||
// 关闭缓存管理器
|
||
if app.cacheMgr != nil {
|
||
app.cacheMgr.Close()
|
||
}
|
||
|
||
// 关闭配置管理器
|
||
if app.config != nil {
|
||
app.config.Close()
|
||
}
|
||
|
||
app.logger.Info("Application stopped successfully")
|
||
return nil
|
||
}
|
||
|
||
// WaitForShutdown 等待关闭信号
|
||
func (app *Application) WaitForShutdown() {
|
||
// 设置信号处理
|
||
quit := make(chan os.Signal, 1)
|
||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||
<-quit
|
||
|
||
app.logger.Info("Received shutdown signal")
|
||
|
||
// 优雅关闭
|
||
if err := app.Stop(); err != nil {
|
||
app.logger.WithError(err).Error("Failed to stop application")
|
||
os.Exit(1)
|
||
}
|
||
}
|
||
|
||
// GetLogger 获取日志记录器
|
||
func (app *Application) GetLogger() *logging.Logger {
|
||
return app.logger
|
||
}
|
||
|
||
// GetConfig 获取配置管理器
|
||
func (app *Application) GetConfig() *config.ConfigManager {
|
||
return app.config
|
||
}
|
||
|
||
// GetCacheStats 获取缓存统计信息
|
||
func (app *Application) GetCacheStats() *cache.CacheStats {
|
||
if app.cacheMgr == nil {
|
||
return &cache.CacheStats{}
|
||
}
|
||
return app.cacheMgr.GetStats()
|
||
}
|
||
|
||
// GetHTTPServer 获取HTTP服务器实例
|
||
func (app *Application) GetHTTPServer() *fiber.App {
|
||
return app.httpServer
|
||
}
|
||
|
||
// GetConversionEngine exposes the conversion engine instance (primarily for testing).
|
||
func (app *Application) GetConversionEngine() *conversion.ConversionEngine {
|
||
return app.convEngine
|
||
}
|
||
|
||
// IsRunning 检查应用是否正在运行
|
||
func (app *Application) IsRunning() bool {
|
||
return app.httpServer != nil
|
||
}
|