Files
zeekayandHanzo Dev 55aa586615 a product states which of its own routes a publishable key reaches
What such a key may reach was a list of addresses kept in the rule that
reads it. The list held Base's own routes and nothing else, so bootnode's
twelve reads — which it registers precisely to be reachable that way, and
guards with a read-only refusal beside each of its eleven writes —
answered 403 wherever the plugin was on.

A list of one package's addresses kept in another goes stale the moment
that package mounts a route, and it goes stale silently. So the answer
moves to the route: apis.Public declares one where it is registered, and
the rule holds no addresses at all. Bootnode declares its twelve reads;
Base declares its records, rest, files and health reads. The rule keeps
one address of its own, the public submit path, because whether it is
public is the collection's statement and not the route's.

A route needs to know its whole address for that, so the mux records it
as it mounts: Route.Pattern is the method, the group prefixes and the
path, the same string a request reports having matched.

Second layer, restored: bootnode read ReadOnly off the identity headers,
which the org plugin writes identically for every key it resolves — so a
publishable key arrived as a person and no read-only refusal could fire.
The headers say who; the credential says what kind.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-24 06:55:07 -07:00

89 lines
2.9 KiB
Go

package router
import "github.com/hanzoai/base/tools/hook"
type Route[T hook.Resolver] struct {
excludedMiddlewares map[string]struct{}
// pattern is the whole address the route was mounted at, assembled by
// [Router.BuildMux] from the method, the group prefixes and the path.
pattern string
Action func(e T) error
Method string
Path string
Middlewares []*hook.Handler[T]
}
// Pattern is the whole address the route answers at: its method, every group
// prefix above it and its own path, which is also what the mux reports in
// [net/http.Request.Pattern]. It is empty until the mux is built, because a
// route knows its path but not the groups it was registered under.
//
// A route's own address is otherwise only knowable by reassembling it from the
// group tree, and two assemblies of one address drift.
func (route *Route[T]) Pattern() string {
return route.pattern
}
// BindFunc registers one or multiple middleware functions to the current route.
//
// The registered middleware functions are "anonymous" and with default priority,
// aka. executes in the order they were registered.
//
// If you need to specify a named middleware (ex. so that it can be removed)
// or middleware with custom exec prirority, use the [Route.Bind] method.
func (route *Route[T]) BindFunc(middlewareFuncs ...func(e T) error) *Route[T] {
for _, m := range middlewareFuncs {
route.Middlewares = append(route.Middlewares, &hook.Handler[T]{Func: m})
}
return route
}
// Bind registers one or multiple middleware handlers to the current route.
func (route *Route[T]) Bind(middlewares ...*hook.Handler[T]) *Route[T] {
route.Middlewares = append(route.Middlewares, middlewares...)
// unmark the newly added middlewares in case they were previously "excluded"
if route.excludedMiddlewares != nil {
for _, m := range middlewares {
if m.Id != "" {
delete(route.excludedMiddlewares, m.Id)
}
}
}
return route
}
// Unbind removes one or more middlewares with the specified id(s) from the current route.
//
// It also adds the removed middleware ids to an exclude list so that they could be skipped from
// the execution chain in case the middleware is registered in a parent group.
//
// Anonymous middlewares are considered non-removable, aka. this method
// does nothing if the middleware id is an empty string.
func (route *Route[T]) Unbind(middlewareIds ...string) *Route[T] {
for _, middlewareId := range middlewareIds {
if middlewareId == "" {
continue
}
// remove from the route's middlewares
for i := len(route.Middlewares) - 1; i >= 0; i-- {
if route.Middlewares[i].Id == middlewareId {
route.Middlewares = append(route.Middlewares[:i], route.Middlewares[i+1:]...)
}
}
// add to the exclude list
if route.excludedMiddlewares == nil {
route.excludedMiddlewares = map[string]struct{}{}
}
route.excludedMiddlewares[middlewareId] = struct{}{}
}
return route
}