Files
subconverter-go/internal/service/application.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

333 lines
10 KiB
Go
Raw Permalink 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 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
}