106 lines
2.9 KiB
Go
Executable File
106 lines
2.9 KiB
Go
Executable File
package api
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net/http"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/labstack/echo/v4/middleware"
|
|
echoSwagger "github.com/swaggo/echo-swagger"
|
|
|
|
_ "git.zhouxhere.com/zhouxhere/maptile/docs"
|
|
"git.zhouxhere.com/zhouxhere/maptile/store"
|
|
)
|
|
|
|
type API struct {
|
|
*echo.Echo
|
|
store *store.Store
|
|
}
|
|
|
|
func NewAPI(e *echo.Echo, s *store.Store) {
|
|
api := &API{e, s}
|
|
|
|
api.Validator = NewCustomValidator()
|
|
|
|
api.Use(middleware.Recover())
|
|
|
|
api.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{LogStatus: true,
|
|
LogURI: true,
|
|
LogError: true,
|
|
HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code
|
|
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
|
|
if v.Error != nil {
|
|
slog.LogAttrs(context.Background(), slog.LevelError, "REQUEST", slog.String("method", v.Method), slog.String("uri", v.URI), slog.Int("status", v.Status), slog.String("latency", v.Latency.String()), slog.String("error", v.Error.Error()))
|
|
} else {
|
|
slog.LogAttrs(context.Background(), slog.LevelInfo, "REQUEST", slog.String("method", v.Method), slog.String("uri", v.URI), slog.Int("status", v.Status), slog.String("latency", v.Latency.String()))
|
|
}
|
|
return nil
|
|
},
|
|
}))
|
|
|
|
api.HTTPErrorHandler = func(err error, c echo.Context) {
|
|
req := c.Request()
|
|
slog.LogAttrs(context.Background(), slog.LevelError, "REQUEST", slog.String("method", req.Method), slog.String("uri", req.RequestURI), slog.String("error", err.Error()))
|
|
c.JSON(http.StatusOK, Response[interface{}]{
|
|
Code: http.StatusInternalServerError,
|
|
Message: err.Error(),
|
|
Data: nil,
|
|
})
|
|
}
|
|
|
|
// swagger
|
|
api.GET("/swagger/*", echoSwagger.WrapHandler)
|
|
api.GET("/swagger", func(c echo.Context) error {
|
|
return c.Redirect(http.StatusMovedPermanently, "/swagger/index.html")
|
|
})
|
|
api.Register()
|
|
|
|
// for _, route := range api.Routes() {
|
|
// fmt.Println(route.Method, route.Path)
|
|
// }
|
|
}
|
|
|
|
func (a *API) Register() {
|
|
v := reflect.ValueOf(a)
|
|
t := reflect.TypeOf(a)
|
|
|
|
group := a.Group("/api/v1")
|
|
|
|
for i := 0; i < v.NumMethod(); i++ {
|
|
method := v.Method(i)
|
|
methodType := t.Method(i)
|
|
methodName := methodType.Name
|
|
|
|
// 使用正则表达式匹配方法名
|
|
re := regexp.MustCompile(`^(Post|Get|Put|Delete)([A-Za-z]+)(By[A-Za-z]+)?$`)
|
|
matches := re.FindStringSubmatch(methodName)
|
|
if len(matches) == 0 {
|
|
continue
|
|
}
|
|
|
|
action := matches[1]
|
|
route := strings.ToLower(matches[2])
|
|
if matches[3] != "" {
|
|
param := strings.ToLower(matches[3][2:])
|
|
route = "/" + route + "/:" + param
|
|
} else {
|
|
route = "/" + route
|
|
}
|
|
|
|
switch action {
|
|
case "Post":
|
|
group.POST(route, method.Interface().(func(echo.Context) error))
|
|
case "Get":
|
|
group.GET(route, method.Interface().(func(echo.Context) error))
|
|
case "Put":
|
|
group.PUT(route, method.Interface().(func(echo.Context) error))
|
|
case "Delete":
|
|
group.DELETE(route, method.Interface().(func(echo.Context) error))
|
|
}
|
|
}
|
|
}
|