package routes import ( "context" "dashboard/controller" "dashboard/logger" "dashboard/pkg/jwt" "dashboard/settings" "errors" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-contrib/cors" "github.com/gin-contrib/pprof" "github.com/gin-gonic/gin" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) type Router struct { r *gin.Engine addr string log *logger.Logger } type AddRouter interface { AddRoute(*gin.Engine) } func NewRouter(addr string, log *logger.Logger, rate settings.RateLimitConfig, jwtC settings.JwtConfig, routes ...AddRouter) *Router { cjwt := jwt.New(jwt.WithSalt(jwtC.Salt), jwt.WithExpire(time.Duration(jwtC.Expire)*time.Second)) r := gin.New() r.Use(cors.New(cors.Config{ AllowOrigins: []string{"*"}, AllowMethods: []string{"GET", "POST"}, AllowHeaders: []string{"Origin", "Content-Type"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, MaxAge: 12 * time.Hour, })) r.Use(GinLogger(log), GinRecovery(log, true), GinLog(log), ErrWapper(GinRateLimit(rate))) // 静态文件服务 r.Static("/static", "./static") r.StaticFile("/", "./static/index.html") r.GET("login", controller.LoginPage) r.POST("/api/login", ErrWapper(controller.UserSignInHandler(cjwt))) r.POST("/api/logout", controller.UserLogOutHandler) g1 := r.Group("/api") g1.Use(ErrWapper(GinJwtAuthor(cjwt))) { g1.GET("/system-info", ErrWapper(controller.SystemInfoHandle)) g1.POST("/upload", ErrWapper(controller.FileUploadHandle)) g1.GET("/files", ErrWapper(controller.FileListHandle)) g1.GET("/download", ErrWapper(controller.FileDownloadHandle)) } r.GET("/ws/terminal", ErrWapper(GinJwtAuthor(cjwt)), ErrWapper(controller.TerminalHandle())) for _, route := range routes { route.AddRoute(r) } pprof.Register(r) return &Router{ r: r, addr: addr, log: log, } } func (r *Router) Run(ctx context.Context) error { srv := &http.Server{ Addr: r.addr, Handler: r.r, } r.log.Sugar().Infof("Server listen on %s", r.addr) wg, ctx := errgroup.WithContext(ctx) wg.Go(func() error { // 开启一个goroutine启动服务 if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { r.log.Error("listen: %s\n", zap.Error(err)) return err } return nil }) wg.Go(func() error { // 等待中断信号来优雅地关闭服务器,为关闭服务器操作设置一个5秒的超时 quit := make(chan os.Signal, 1) // 创建一个接收信号的通道 // kill 默认会发送 syscall.SIGTERM 信号 // kill -2 发送 syscall.SIGINT 信号,我们常用的Ctrl+C就是触发系统SIGINT信号 // kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它 // signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞 select { case <-ctx.Done(): r.log.Error("Http Server Cancel by ctx") case <-quit: } // 阻塞在此,当接收到上述两种信号时才会往下执行 r.log.Info("Shutdown Server ...") // 创建一个5秒超时的context ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 5秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超过5秒就超时退出 if err := srv.Shutdown(ctx); err != nil { r.log.Error("Server Shutdown: ", zap.Error(err)) return err } r.log.Info("Server exiting") return errors.New("Server Shutdown") }) if err := wg.Wait(); err != nil { return err } return nil } /* func Setup(log *logger.Logger, rate settings.RateLimitConfig, jwtC settings.JwtConfig) *gin.Engine { cjwt := jwt.New(jwt.WithSalt(jwtC.Salt), jwt.WithExpire(time.Duration(jwtC.Expire)*time.Second)) r := gin.New() r.Use(cors.New(cors.Config{ AllowOrigins: []string{"*"}, AllowMethods: []string{"GET", "POST"}, AllowHeaders: []string{"Origin", "Content-Type"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, MaxAge: 12 * time.Hour, })) r.Use(GinLogger(log), GinRecovery(log, true), GinLog(log), errWapper(GinRateLimit(rate))) // 静态文件服务 r.Static("/static", "./static") r.StaticFile("/", "./static/index.html") r.GET("login", controller.LoginPage) r.POST("/api/login", errWapper(controller.UserSignInHandler(cjwt))) r.POST("/api/logout", controller.UserLogOutHandler) g1 := r.Group("/api") g1.Use(errWapper(GinJwtAuthor(cjwt))) { g1.GET("/system-info", errWapper(controller.SystemInfoHandle)) g1.POST("/upload", errWapper(controller.FileUploadHandle)) g1.GET("/files", errWapper(controller.FileListHandle)) g1.GET("/download", errWapper(controller.FileDownloadHandle)) } r.GET("/ws/terminal", errWapper(GinJwtAuthor(cjwt)), errWapper(controller.TerminalHandle())) // unisr := r.Group("/api/unis") // unisr.Use(errWapper(GinCheckServerId), errWapper(GinStoreRequest)) // { // unisr.POST("/config/v1/add", errWapper(unis.StationConfig)) // } return r } */