2019-12-08 03:59:58 -05:00
|
|
|
package controller
|
|
|
|
|
|
|
|
import (
|
2024-10-21 02:30:50 -04:00
|
|
|
"errors"
|
2019-12-08 03:59:58 -05:00
|
|
|
"fmt"
|
2023-11-28 20:42:51 -05:00
|
|
|
"log"
|
2021-07-14 11:53:37 -04:00
|
|
|
"net/http"
|
2024-10-19 23:47:45 -04:00
|
|
|
"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
|
|
|
)
|
|
|
|
|
2024-10-19 23:47:45 -04: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)
|
2024-10-19 23:47:45 -04:00
|
|
|
if singleton.Conf.Debug {
|
|
|
|
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
|
|
|
}
|
2024-10-20 02:05:43 -04:00
|
|
|
|
2024-10-19 23:47:45 -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,
|
2021-07-14 11:53:37 -04:00
|
|
|
}
|
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
|
|
|
|
2024-10-21 02:30:50 -04:00
|
|
|
optionalAuth := api.Group("", optionalAuthMiddleware(authMiddleware))
|
|
|
|
optionalAuth.GET("/ws/server", commonHandler[any](serverStream))
|
|
|
|
optionalAuth.GET("/server-group", commonHandler[[]model.ServerGroup](listServerGroup))
|
|
|
|
optionalAuth.GET("/ddns", listDDNS) // TODO
|
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 02:30:50 -04:00
|
|
|
auth.PATCH("/server/:id", commonHandler[any](editServer))
|
2024-10-20 11:23:04 -04:00
|
|
|
|
2024-10-21 02:30:50 -04:00
|
|
|
auth.POST("/ddns", commonHandler[any](newDDNS))
|
|
|
|
auth.PATCH("/ddns/:id", commonHandler[any](editDDNS))
|
|
|
|
|
|
|
|
api.POST("/batch-delete/server", commonHandler[any](batchDeleteServer))
|
|
|
|
api.POST("/batch-delete/ddns", commonHandler[any](batchDeleteDDNS))
|
2024-10-19 12:09:16 -04:00
|
|
|
|
2019-12-08 03:59:58 -05:00
|
|
|
// 通用页面
|
2024-10-20 02:05:43 -04:00
|
|
|
// cp := commonPage{r: r}
|
|
|
|
// cp.serve()
|
|
|
|
// // 会员页面
|
|
|
|
// mp := &memberPage{r}
|
|
|
|
// mp.serve()
|
|
|
|
// // API
|
|
|
|
// external := api.Group("api")
|
|
|
|
// {
|
|
|
|
// ma := &memberAPI{external}
|
|
|
|
// ma.serve()
|
|
|
|
// }
|
2019-12-08 03:59:58 -05:00
|
|
|
}
|
2022-04-29 21:32:57 -04: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)
|
|
|
|
|
2024-08-23 23:11:06 -04:00
|
|
|
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()
|
|
|
|
}
|
2024-10-19 23:47:45 -04:00
|
|
|
|
|
|
|
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 02:30:50 -04:00
|
|
|
|
|
|
|
func newErrorResponse[T any](err error) model.CommonResponse[T] {
|
|
|
|
return model.CommonResponse[T]{
|
|
|
|
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...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func commonHandler[T any](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)
|
|
|
|
c.JSON(http.StatusOK, newErrorResponse[T](errors.New("database error")))
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
c.JSON(http.StatusOK, newErrorResponse[T](err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|