225 lines
7.3 KiB
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()))
|
|
}
|
|
}
|