Files
cursor-byok/internal/backend/server/route.go
T
leookun a5437e60fe feat(cursor): add contributor profile link and improve UI for Cursor account card
- Integrated a tooltip with a button to open the contributor's GitHub profile in the CursorAccountCard component.
- Updated the layout of the account card for better visual organization.
- Removed routing mode options from the configuration view and adjusted related translations in multiple languages.
- Cleaned up unused routing mode logic in the app state management.
2026-08-02 22:53:57 +08:00

171 lines
3.8 KiB
Go

package server
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
type HandlerFunc func(*Context) error
type Middleware func(HandlerFunc) HandlerFunc
type Route struct {
Method string
Pattern string
Name string
Protocol ProtocolClass
Middleware []Middleware
Local HandlerFunc
}
type App struct {
router chi.Router
globalMiddlewares []Middleware
routes []Route
mounts []mountSpec
}
type mountSpec struct {
pattern string
handler http.Handler
}
type Option func(*App)
type RouteOption func(*Route)
func New(options ...Option) http.Handler {
app := &App{router: chi.NewRouter()}
for _, option := range options {
if option != nil {
option(app)
}
}
for _, route := range app.routes {
app.registerRoute(route)
}
for _, mount := range app.mounts {
app.router.Mount(mount.pattern, mount.handler)
}
return app.router
}
func Use(middlewares ...Middleware) Option {
return func(app *App) {
app.globalMiddlewares = append(app.globalMiddlewares, middlewares...)
}
}
func Mount(pattern string, handler http.Handler) Option {
return func(app *App) {
if handler == nil {
return
}
app.mounts = append(app.mounts, mountSpec{pattern: pattern, handler: handler})
}
}
func GET(pattern string, options ...RouteOption) Option {
return routeOption(http.MethodGet, pattern, options...)
}
func POST(pattern string, options ...RouteOption) Option {
return routeOption(http.MethodPost, pattern, options...)
}
func Any(pattern string, options ...RouteOption) Option { return routeOption("", pattern, options...) }
func routeOption(method string, pattern string, options ...RouteOption) Option {
return func(app *App) {
route := Route{
Method: method,
Pattern: pattern,
Protocol: ProtocolHTTP,
Local: func(ctx *Context) error {
return fmt.Errorf("route %s has no local action", pattern)
},
}
for _, option := range options {
if option != nil {
option(&route)
}
}
app.routes = append(app.routes, route)
}
}
func Name(name string) RouteOption {
return func(route *Route) {
route.Name = name
}
}
func HTTP() RouteOption {
return func(route *Route) {
route.Protocol = ProtocolHTTP
}
}
func ConnectUnary() RouteOption {
return func(route *Route) {
route.Protocol = ProtocolConnectUnary
}
}
func ConnectStream() RouteOption {
return func(route *Route) {
route.Protocol = ProtocolConnectStream
}
}
func With(middlewares ...Middleware) RouteOption {
return func(route *Route) {
route.Middleware = append(route.Middleware, middlewares...)
}
}
func Local(action HandlerFunc) RouteOption {
return func(route *Route) {
route.Local = action
}
}
func (app *App) registerRoute(route Route) {
handler := app.buildRouteHandler(route)
if route.Method == "" {
app.router.HandleFunc(route.Pattern, handler)
return
}
app.router.MethodFunc(route.Method, route.Pattern, handler)
}
func (app *App) buildRouteHandler(route Route) http.HandlerFunc {
chain := append([]Middleware{}, app.globalMiddlewares...)
chain = append(chain, route.Middleware...)
final := Chain(chain...)(func(ctx *Context) error {
if route.Local != nil {
return route.Local(ctx)
}
return fmt.Errorf("route %s has no executable action", route.Name)
})
return func(writer http.ResponseWriter, request *http.Request) {
trackedWriter := newTrackedResponseWriter(writer)
ctx := newContext(trackedWriter, request, route)
if err := final(ctx); err != nil {
writeServerError(trackedWriter, err)
}
}
}
func Chain(middlewares ...Middleware) Middleware {
return func(final HandlerFunc) HandlerFunc {
wrapped := final
for index := len(middlewares) - 1; index >= 0; index-- {
current := middlewares[index]
if current == nil {
continue
}
wrapped = current(wrapped)
}
return wrapped
}
}