refactor(api): restructured API for type safety, maintainability and docs generation

- These changes makes the API incombatible with previous versions
- Added new types for error handling, success responses, and health checks.
- Updated health check logic to utilize the new types for better clarity and structure.
- Refactored existing handlers to improve response consistency and error handling.
- Updated Makefile to include a new target for generating API types from Swagger.
- Updated "new agent" API to respond an encrypted cert pair
This commit is contained in:
yusing
2025-08-16 13:04:05 +08:00
parent fce9ce21c9
commit 35a3e3fef6
149 changed files with 13173 additions and 2173 deletions

View File

@@ -0,0 +1,26 @@
package routeApi
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/yusing/go-proxy/internal/route"
"github.com/yusing/go-proxy/internal/route/routes"
)
type RoutesByProvider map[string][]route.Route
// @x-id "byProvider"
// @BasePath /api/v1
// @Summary List routes by provider
// @Description List routes by provider
// @Tags route
// @Accept json
// @Produce json
// @Success 200 {object} RoutesByProvider
// @Failure 403 {object} apitypes.ErrorResponse
// @Failure 500 {object} apitypes.ErrorResponse
// @Router /route/by_provider [get]
func ByProvider(c *gin.Context) {
c.JSON(http.StatusOK, routes.ByProvider())
}

View File

@@ -0,0 +1,33 @@
package routeApi
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
)
// @x-id "providers"
// @BasePath /api/v1
// @Summary List route providers
// @Description List route providers
// @Tags route,websocket
// @Accept json
// @Produce json
// @Success 200 {array} config.RouteProviderListResponse
// @Failure 403 {object} apitypes.ErrorResponse
// @Failure 500 {object} apitypes.ErrorResponse
// @Router /route/providers [get]
func Providers(c *gin.Context) {
cfg := config.GetInstance()
if httpheaders.IsWebsocket(c.Request.Header) {
websocket.PeriodicWrite(c, 5*time.Second, func() (any, error) {
return config.GetInstance().RouteProviderList(), nil
})
} else {
c.JSON(http.StatusOK, cfg.RouteProviderList())
}
}

View File

@@ -0,0 +1,41 @@
package routeApi
import (
"net/http"
"github.com/gin-gonic/gin"
apitypes "github.com/yusing/go-proxy/internal/api/types"
"github.com/yusing/go-proxy/internal/route/routes"
)
type ListRouteRequest struct {
Which string `uri:"which" validate:"required"`
} // @name ListRouteRequest
// @x-id "route"
// @BasePath /api/v1
// @Summary List route
// @Description List route
// @Tags route
// @Accept json
// @Produce json
// @Param which path string true "Route name"
// @Success 200 {object} RouteType
// @Failure 400 {object} apitypes.ErrorResponse
// @Failure 403 {object} apitypes.ErrorResponse
// @Failure 404 {object} apitypes.ErrorResponse
// @Router /route/{which} [get]
func Route(c *gin.Context) {
var request ListRouteRequest
if err := c.ShouldBindUri(&request); err != nil {
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
return
}
route, ok := routes.Get(request.Which)
if ok {
c.JSON(http.StatusOK, route)
} else {
c.JSON(http.StatusNotFound, nil)
}
}

View File

@@ -0,0 +1,68 @@
package routeApi
import (
"net/http"
"slices"
"time"
"github.com/gin-gonic/gin"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/gphttp/websocket"
"github.com/yusing/go-proxy/internal/route"
"github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/types"
)
type RouteType route.Route // @name Route
// @x-id "routes"
// @BasePath /api/v1
// @Summary List routes
// @Description List routes
// @Tags route,websocket
// @Accept json
// @Produce json
// @Param provider query string false "Provider"
// @Success 200 {array} RouteType
// @Failure 403 {object} apitypes.ErrorResponse
// @Router /route/list [get]
func Routes(c *gin.Context) {
if httpheaders.IsWebsocket(c.Request.Header) {
RoutesWS(c)
return
}
provider := c.Query("provider")
if provider == "" {
c.JSON(http.StatusOK, slices.Collect(routes.Iter))
return
}
rts := make([]types.Route, 0, routes.NumRoutes())
for r := range routes.Iter {
if r.ProviderName() == provider {
rts = append(rts, r)
}
}
c.JSON(http.StatusOK, rts)
}
func RoutesWS(c *gin.Context) {
provider := c.Query("provider")
if provider == "" {
websocket.PeriodicWrite(c, 3*time.Second, func() (any, error) {
return slices.Collect(routes.Iter), nil
})
return
}
websocket.PeriodicWrite(c, 3*time.Second, func() (any, error) {
rts := make([]types.Route, 0, routes.NumRoutes())
for r := range routes.Iter {
if r.ProviderName() == provider {
rts = append(rts, r)
}
}
return rts, nil
})
}