nezha/cmd/dashboard/controller/controller.go

190 lines
4.7 KiB
Go
Raw Normal View History

2019-12-08 03:59:58 -05:00
package controller
import (
"errors"
2019-12-08 03:59:58 -05:00
"fmt"
2023-11-28 20:42:51 -05:00
"log"
"net/http"
"strings"
2019-12-08 03:59:58 -05:00
"time"
2024-10-19 12:09:16 -04:00
jwt "github.com/appleboy/gin-jwt/v2"
2021-05-10 06:04:38 -04:00
"github.com/gin-contrib/pprof"
2019-12-08 03:59:58 -05:00
"github.com/gin-gonic/gin"
2024-07-14 07:41:50 -04:00
"github.com/hashicorp/go-uuid"
2024-10-19 11:14:53 -04:00
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
2019-12-08 03:59:58 -05:00
2024-10-19 11:14:53 -04:00
docs "github.com/naiba/nezha/cmd/dashboard/docs"
2024-07-14 07:41:50 -04:00
"github.com/naiba/nezha/model"
"github.com/naiba/nezha/pkg/utils"
"github.com/naiba/nezha/proto"
"github.com/naiba/nezha/service/rpc"
2022-01-08 22:54:14 -05:00
"github.com/naiba/nezha/service/singleton"
2019-12-08 03:59:58 -05:00
)
func ServeWeb() *http.Server {
2019-12-08 10:18:29 -05:00
gin.SetMode(gin.ReleaseMode)
2021-05-10 06:04:38 -04:00
r := gin.Default()
2024-10-19 11:14:53 -04:00
docs.SwaggerInfo.BasePath = "/api/v1"
2024-07-14 07:41:50 -04:00
if singleton.Conf.Debug {
gin.SetMode(gin.DebugMode)
pprof.Register(r)
}
r.Use(natGateway)
if singleton.Conf.Debug {
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
}
2024-10-20 02:05:43 -04:00
r.Use(recordPath)
2019-12-08 03:59:58 -05:00
routers(r)
2021-08-10 08:13:17 -04:00
2024-10-20 02:05:43 -04:00
return &http.Server{
2022-06-22 00:53:21 -04:00
ReadHeaderTimeout: time.Second * 5,
Handler: r,
}
2019-12-08 03:59:58 -05:00
}
func routers(r *gin.Engine) {
2024-10-19 12:09:16 -04:00
authMiddleware, err := jwt.New(initParams())
if err != nil {
log.Fatal("JWT Error:" + err.Error())
}
2024-10-20 11:23:04 -04:00
if err := authMiddleware.MiddlewareInit(); err != nil {
log.Fatal("authMiddleware.MiddlewareInit Error:" + err.Error())
}
2024-10-20 02:05:43 -04:00
api := r.Group("api/v1")
api.POST("/login", authMiddleware.LoginHandler)
2024-10-19 12:09:16 -04:00
optionalAuth := api.Group("", optionalAuthMiddleware(authMiddleware))
2024-10-21 11:00:51 -04:00
optionalAuth.GET("/ws/server", commonHandler(serverStream))
optionalAuth.GET("/server-group", commonHandler(listServerGroup))
2024-10-20 11:23:04 -04:00
2024-10-20 02:05:43 -04:00
auth := api.Group("", authMiddleware.MiddlewareFunc())
2024-10-19 12:09:16 -04:00
auth.GET("/refresh_token", authMiddleware.RefreshHandler)
2024-10-21 11:00:51 -04:00
auth.POST("/server-group", commonHandler(newServerGroup))
auth.PATCH("/server-group/:id", commonHandler(editServerGroup))
auth.POST("/batch-delete/server-group", commonHandler(batchDeleteServerGroup))
auth.PATCH("/server/:id", commonHandler(editServer))
auth.POST("/batch-delete/server", commonHandler(batchDeleteServer))
auth.GET("/ddns", commonHandler(listDDNS))
auth.GET("/ddns/providers", commonHandler(listProviders))
2024-10-21 11:00:51 -04:00
auth.POST("/ddns", commonHandler(newDDNS))
auth.PATCH("/ddns/:id", commonHandler(editDDNS))
auth.POST("/batch-delete/ddns", commonHandler(batchDeleteDDNS))
2019-12-08 03:59:58 -05:00
}
2024-07-14 07:41:50 -04:00
func natGateway(c *gin.Context) {
natConfig := singleton.GetNATConfigByDomain(c.Request.Host)
if natConfig == nil {
return
}
singleton.ServerLock.RLock()
server := singleton.ServerList[natConfig.ServerID]
singleton.ServerLock.RUnlock()
if server == nil || server.TaskStream == nil {
c.Writer.WriteString("server not found or not connected")
c.Abort()
return
}
streamId, err := uuid.GenerateUUID()
if err != nil {
c.Writer.WriteString(fmt.Sprintf("stream id error: %v", err))
c.Abort()
return
}
rpc.NezhaHandlerSingleton.CreateStream(streamId)
defer rpc.NezhaHandlerSingleton.CloseStream(streamId)
taskData, err := utils.Json.Marshal(model.TaskNAT{
2024-07-14 07:41:50 -04:00
StreamID: streamId,
Host: natConfig.Host,
})
if err != nil {
c.Writer.WriteString(fmt.Sprintf("task data error: %v", err))
c.Abort()
return
}
if err := server.TaskStream.Send(&proto.Task{
Type: model.TaskTypeNAT,
Data: string(taskData),
}); err != nil {
c.Writer.WriteString(fmt.Sprintf("send task error: %v", err))
c.Abort()
return
}
w, err := utils.NewRequestWrapper(c.Request, c.Writer)
if err != nil {
c.Writer.WriteString(fmt.Sprintf("request wrapper error: %v", err))
c.Abort()
return
}
if err := rpc.NezhaHandlerSingleton.UserConnected(streamId, w); err != nil {
c.Writer.WriteString(fmt.Sprintf("user connected error: %v", err))
c.Abort()
return
}
rpc.NezhaHandlerSingleton.StartStream(streamId, time.Second*10)
c.Abort()
}
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)
}
2024-10-21 11:00:51 -04:00
func newErrorResponse(err error) model.CommonResponse[any] {
return model.CommonResponse[any]{
Success: false,
Error: err.Error(),
}
}
type handlerFunc func(c *gin.Context) 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...)
}
2024-10-21 11:00:51 -04:00
func commonHandler(handler handlerFunc) func(*gin.Context) {
return func(c *gin.Context) {
if err := handler(c); err != nil {
if _, ok := err.(*gormError); ok {
log.Printf("NEZHA>> gorm error: %v", err)
2024-10-21 11:00:51 -04:00
c.JSON(http.StatusOK, newErrorResponse(errors.New("database error")))
return
} else {
2024-10-21 11:00:51 -04:00
c.JSON(http.StatusOK, newErrorResponse(err))
return
}
}
}
}