maptile/api/api.go

124 lines
3.5 KiB
Go
Executable File

package api
import (
"context"
"fmt"
"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,
})
}
api.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
// AllowHeaders: []string{"Content-Type", "Authorization"},
}))
// 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 := range v.NumMethod() {
method := v.Method(i)
methodType := t.Method(i)
methodName := methodType.Name
if method.Type().String() != "func(echo.Context) error" {
continue
}
// 使用正则表达式匹配方法名
re := regexp.MustCompile(`^(Post|Get|Put|Delete)(\w+)$`)
matches := re.FindStringSubmatch(methodName)
if len(matches) == 0 {
continue
}
action := matches[1]
otherParams := strings.Split(matches[2], "By")
routeStr := strings.ToLower(otherParams[0])
route := "/" + routeStr
otherStr := strings.Join(otherParams[1:], "")
otherConditions := strings.Split(otherStr, "And")
if len(otherConditions) > 0 {
for i := range otherConditions {
if otherConditions[i] != "" {
route = route + "/:" + strings.ToLower(otherConditions[i])
}
}
}
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))
}
}
}