package controller import ( "errors" "fmt" "log" "net/http" "strings" jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-contrib/pprof" "github.com/gin-gonic/gin" swaggerfiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" docs "github.com/naiba/nezha/cmd/dashboard/docs" "github.com/naiba/nezha/model" "github.com/naiba/nezha/service/singleton" ) func ServeWeb() http.Handler { gin.SetMode(gin.ReleaseMode) r := gin.Default() docs.SwaggerInfo.BasePath = "/api/v1" if singleton.Conf.Debug { gin.SetMode(gin.DebugMode) pprof.Register(r) } if singleton.Conf.Debug { r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) } r.Use(recordPath) routers(r) return r } func routers(r *gin.Engine) { authMiddleware, err := jwt.New(initParams()) if err != nil { log.Fatal("JWT Error:" + err.Error()) } if err := authMiddleware.MiddlewareInit(); err != nil { log.Fatal("authMiddleware.MiddlewareInit Error:" + err.Error()) } api := r.Group("api/v1") api.POST("/login", authMiddleware.LoginHandler) optionalAuth := api.Group("", optionalAuthMiddleware(authMiddleware)) optionalAuth.GET("/ws/server", commonHandler(serverStream)) optionalAuth.GET("/server-group", commonHandler(listServerGroup)) auth := api.Group("", authMiddleware.MiddlewareFunc()) auth.GET("/refresh_token", authMiddleware.RefreshHandler) auth.POST("/terminal", commonHandler(createTerminal)) auth.GET("/ws/terminal/:id", commonHandler(terminalStream)) auth.GET("/user", commonHandler(listUser)) auth.POST("/user", commonHandler(createUser)) auth.POST("/batch-delete/user", commonHandler(batchDeleteUser)) auth.POST("/server-group", commonHandler(createServerGroup)) auth.PATCH("/server-group/:id", commonHandler(updateServerGroup)) auth.POST("/batch-delete/server-group", commonHandler(batchDeleteServerGroup)) auth.GET("/server", commonHandler(listServer)) auth.PATCH("/server/:id", commonHandler(updateServer)) auth.POST("/batch-delete/server", commonHandler(batchDeleteServer)) auth.GET("/ddns", commonHandler(listDDNS)) auth.GET("/ddns/providers", commonHandler(listProviders)) auth.POST("/ddns", commonHandler(createDDNS)) auth.PATCH("/ddns/:id", commonHandler(updateDDNS)) auth.POST("/batch-delete/ddns", commonHandler(batchDeleteDDNS)) } func recordPath(c *gin.Context) { url := c.Request.URL.String() for _, p := range c.Params { url = strings.Replace(url, p.Value, ":"+p.Key, 1) } c.Set("MatchedPath", url) } func newErrorResponse(err error) model.CommonResponse[any] { return model.CommonResponse[any]{ Success: false, Error: err.Error(), } } type handlerFunc[T any] func(c *gin.Context) (T, error) // There are many error types in gorm, so create a custom type to represent all // gorm errors here instead type gormError struct { msg string a []interface{} } func newGormError(format string, args ...interface{}) error { return &gormError{ msg: format, a: args, } } func (ge *gormError) Error() string { return fmt.Sprintf(ge.msg, ge.a...) } func commonHandler[T any](handler handlerFunc[T]) func(*gin.Context) { return func(c *gin.Context) { data, err := handler(c) if err == nil { c.JSON(http.StatusOK, model.CommonResponse[T]{Success: true, Data: data}) return } if _, ok := err.(*gormError); ok { log.Printf("NEZHA>> gorm error: %v", err) c.JSON(http.StatusOK, newErrorResponse(errors.New("database error"))) return } else { c.JSON(http.StatusOK, newErrorResponse(err)) return } } }