// Package main is the main package for the WeKnora server // It contains the main function and the entry point for the server // // @title WeKnora API // @version 1.0 // @description WeKnora 知识库管理系统 API 文档 // @termsOfService http://swagger.io/terms/ // // @contact.name WeKnora Github // @contact.url https://github.com/Tencent/WeKnora // // @BasePath /api/v1 // // @securityDefinitions.apikey Bearer // @in header // @name Authorization // @description 用户登录认证:输入 Bearer {token} 格式的 JWT 令牌 // @securityDefinitions.apikey ApiKeyAuth // @in header // @name X-API-Key // @description 租户身份认证:输入 sk- 开头的 API Key package main import ( "context" "fmt" "net/http" "os" "os/signal" "time" "github.com/gin-gonic/gin" "github.com/Tencent/WeKnora/internal/config" "github.com/Tencent/WeKnora/internal/container" "github.com/Tencent/WeKnora/internal/logger" "github.com/Tencent/WeKnora/internal/runtime" "github.com/Tencent/WeKnora/internal/tracing" "github.com/Tencent/WeKnora/internal/types/interfaces" ) func main() { // Set Gin mode if os.Getenv("GIN_MODE") == "release" { gin.SetMode(gin.ReleaseMode) } else { gin.SetMode(gin.DebugMode) } // Build dependency injection container c := container.BuildContainer(runtime.GetContainer()) // Run application err := c.Invoke(func( cfg *config.Config, router *gin.Engine, tracer *tracing.Tracer, resourceCleaner interfaces.ResourceCleaner, ) error { // Create HTTP server server := &http.Server{ Handler: router, } addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) listener, err := listenWithRetry(addr, 10, 300*time.Millisecond) if err != nil { return fmt.Errorf("failed to start server: %v", err) } ctx, done := context.WithCancel(context.Background()) signals := make(chan os.Signal, 1) signal.Notify(signals, shutdownSignals...) go func() { sig := <-signals logger.Infof(context.Background(), "Received signal: %v, starting server shutdown...", sig) // Close listener first to release port immediately, // so the next process can bind during our graceful drain. listener.Close() shutdownTimeout := cfg.Server.ShutdownTimeout if shutdownTimeout == 0 { shutdownTimeout = 30 * time.Second } shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), shutdownTimeout) defer shutdownCancel() // Second signal → force close all connections immediately go func() { sig := <-signals logger.Warnf(context.Background(), "Received second signal: %v, forcing shutdown...", sig) server.Close() }() if err := server.Shutdown(shutdownCtx); err != nil { logger.Errorf(context.Background(), "Server forced to shutdown: %v", err) server.Close() } logger.Info(context.Background(), "Cleaning up resources...") errs := resourceCleaner.Cleanup(shutdownCtx) if len(errs) > 0 { logger.Errorf(context.Background(), "Errors occurred during resource cleanup: %v", errs) } logger.Info(context.Background(), "Server has exited") done() }() logger.Infof(context.Background(), "Server is running at %s", addr) if err := server.Serve(listener); err != nil && err != http.ErrServerClosed { return fmt.Errorf("server error: %v", err) } <-ctx.Done() return nil }) if err != nil { logger.Fatalf(context.Background(), "Failed to run application: %v", err) } }