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:
332
internal/service/application.go
Normal file
332
internal/service/application.go
Normal file
@@ -0,0 +1,332 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user