pink_fox/application/packages/fw/router_manager.go

225 lines
7.3 KiB
Go

package fw
import (
"fmt"
"github.com/go-chi/chi/v5"
"net/http"
"runtime/debug"
)
type MiddlewareFunc[U any] func(U, ActionFunc) (Response, Error)
type HandlerFunc[U any] func(U) (Response, Error)
type ActionFunc func() (Response, Error)
type RouterManager[S, D any] struct {
mux *chi.Mux
storage S
fnMakeContainer func(S, http.ResponseWriter, *http.Request) D
fnCloseContainer func(D)
debug bool
logger Logger
middlewares []MiddlewareFunc[D]
}
func NewRouterManager[S, D any](mux *chi.Mux, storage S, fnMakeContainer func(S, http.ResponseWriter, *http.Request) D,
fnCloseContainer func(D), debug bool, logger Logger) *RouterManager[S, D] {
return &RouterManager[S, D]{
mux: mux,
storage: storage,
fnMakeContainer: fnMakeContainer,
fnCloseContainer: fnCloseContainer,
debug: debug,
logger: logger,
middlewares: make([]MiddlewareFunc[D], 0),
}
}
func (it *RouterManager[S, D]) Use(middlewares ...MiddlewareFunc[D]) {
it.middlewares = append(it.middlewares, middlewares...)
}
func (it *RouterManager[S, D]) Group(relativePath string) *Group[S, D] {
return NewGroup(it, relativePath, it.middlewares...)
}
func (it *RouterManager[S, D]) NotFound(handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.NotFound(it.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *RouterManager[S, D]) NoMethod(handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.MethodNotAllowed(it.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *RouterManager[S, D]) Get(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.Get(relativePath, it.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *RouterManager[S, D]) Post(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.Post(relativePath, it.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *RouterManager[S, D]) List(methods []string, relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
for _, m := range methods {
it.mux.Method(m, relativePath, it.makeHandler(handler, append(it.middlewares, middlewares...)))
}
}
func (it *RouterManager[S, D]) Common(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.List([]string{http.MethodGet, http.MethodPost, http.MethodOptions}, relativePath, handler, middlewares...)
}
func (it *RouterManager[S, D]) All(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.List([]string{
http.MethodGet,
http.MethodHead,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodConnect,
http.MethodOptions,
http.MethodTrace,
},
relativePath,
handler,
middlewares...,
)
}
type Group[S, D any] struct {
routerManager *RouterManager[S, D]
mux *chi.Mux
middlewares []MiddlewareFunc[D]
}
func NewGroup[S, D any](routerManager *RouterManager[S, D], relativePath string, middlewares ...MiddlewareFunc[D]) *Group[S, D] {
mux := chi.NewRouter()
routerManager.mux.Mount(relativePath, mux)
return &Group[S, D]{
routerManager: routerManager,
mux: mux,
middlewares: middlewares,
}
}
func (it *Group[S, D]) Use(middlewares ...MiddlewareFunc[D]) {
it.middlewares = append(it.middlewares, middlewares...)
}
func (it *Group[S, D]) Group(relativePath string, middlewares ...MiddlewareFunc[D]) *Group[S, D] {
mux := chi.NewRouter()
it.mux.Mount(relativePath, mux)
return &Group[S, D]{
routerManager: it.routerManager,
mux: mux,
middlewares: append(it.middlewares, middlewares...),
}
}
func (it *Group[S, D]) NotFound(handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.NotFound(it.routerManager.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *Group[S, D]) NoMethod(handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.MethodNotAllowed(it.routerManager.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *Group[S, D]) Get(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.Get(relativePath, it.routerManager.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *Group[S, D]) Post(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.mux.Post(relativePath, it.routerManager.makeHandler(handler, append(it.middlewares, middlewares...)))
}
func (it *Group[S, D]) List(methods []string, relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
for _, m := range methods {
it.mux.Method(m, relativePath, it.routerManager.makeHandler(handler, append(it.middlewares, middlewares...)))
}
}
func (it *Group[S, D]) Common(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.List([]string{http.MethodGet, http.MethodPost, http.MethodOptions}, relativePath, handler, middlewares...)
}
func (it *Group[S, D]) All(relativePath string, handler HandlerFunc[D], middlewares ...MiddlewareFunc[D]) {
it.List([]string{
http.MethodGet,
http.MethodHead,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodConnect,
http.MethodOptions,
http.MethodTrace,
},
relativePath,
handler,
middlewares...,
)
}
func (it *RouterManager[S, D]) makeHandler(handler HandlerFunc[D], middlewares []MiddlewareFunc[D]) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
var err Error
defer func() {
if err_ := recover(); err_ != nil {
err = ErrPanic(fmt.Errorf("panic: %v", err_), debug.Stack())
it.errorsHandler(writer, err, it.debug, it.logger)
}
}()
container := it.fnMakeContainer(it.storage, writer, request)
defer it.fnCloseContainer(container)
err = it.pipeline(handler, middlewares, container)
if err != nil {
it.errorsHandler(writer, err, it.debug, it.logger)
}
}
}
func (it *RouterManager[S, D]) pipeline(endpoint HandlerFunc[D], middlewares []MiddlewareFunc[D], container D) (err Error) {
handler := func() (Response, Error) {
return endpoint(container)
}
for i := len(middlewares) - 1; i >= 0; i-- {
handler = it.createMiddleware(handler, middlewares[i], container)
}
resp, err := handler()
if err != nil {
_ = err.Tap()
}
if resp != nil {
err = ErrStr("response должен быть отображен в middleware, по всей видимости этот шаг был пропущен")
}
return
}
func (it *RouterManager[T, U]) createMiddleware(next ActionFunc, middleware MiddlewareFunc[U], container U) ActionFunc {
return func() (Response, Error) {
return middleware(container, next)
}
}
func (it *RouterManager[T, U]) errorsHandler(writer http.ResponseWriter, err Error, debugOn bool, logger Logger) {
if logger != nil {
logger.Error(err.Message(), err.Trace())
}
message := ""
if debugOn {
message = fmt.Sprintf("<pre>%s</pre>", err.Error())
}
message = fmt.Sprintf("<h1>%d %s</h1>\n%s\n", http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), message)
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.WriteHeader(http.StatusInternalServerError)
_, err_ := writer.Write([]byte(message))
if err_ != nil {
panic(ErrPanic(err_, debug.Stack()))
}
}