diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pink_fox_app/go.mod b/pink_fox_app/go.mod index bd2507e..fb51b26 100644 --- a/pink_fox_app/go.mod +++ b/pink_fox_app/go.mod @@ -3,6 +3,8 @@ module pink_fox go 1.24.1 require ( + github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect + github.com/CloudyKit/jet/v6 v6.3.1 // indirect github.com/bytedance/sonic v1.13.1 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudwego/base64x v0.1.5 // indirect diff --git a/pink_fox_app/go.sum b/pink_fox_app/go.sum index ef4135d..f7fa444 100644 --- a/pink_fox_app/go.sum +++ b/pink_fox_app/go.sum @@ -1,3 +1,7 @@ +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw= +github.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw= github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= diff --git a/pink_fox_app/src/app/cmd/server.go b/pink_fox_app/src/app/cmd/server.go index bb00c6f..c6647a9 100644 --- a/pink_fox_app/src/app/cmd/server.go +++ b/pink_fox_app/src/app/cmd/server.go @@ -1,16 +1,23 @@ package cmd import ( + "bytes" "database/sql" "fmt" + "github.com/CloudyKit/jet/v6" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/render" + "io" "net/http" "pink_fox/src/app" c "pink_fox/src/app/config" "pink_fox/src/app/db" l "pink_fox/src/app/logger" "pink_fox/src/app/routes_manager" + "pink_fox/src/app/views_manager" "pink_fox/src/routes" + "pink_fox/src/views" + "runtime/debug" ) type Server struct { @@ -52,13 +59,93 @@ func (it *Server) StartServer(application *app.Application) error { c.String(http.StatusOK, "ok") }) - routes.RegistrationRoutes(routes_manager.NewManager(r, application)) + r.Use(it.errorHandler(application.Logger)) + + err := it.loadTemplates(r) + if err != nil { + return err + } + + tm := views_manager.NewManager() + views.RegistrationViews(tm) + + routes.RegistrationRoutes(routes_manager.NewManager(r, application, tm)) fmt.Printf("Starting http_server at port %d...", application.Conf.Port) - err := r.Run(fmt.Sprintf(":%d", application.Conf.Port)) + err = r.Run(fmt.Sprintf(":%d", application.Conf.Port)) if err != nil { return err } return nil } + +func (it *Server) loadTemplates(e *gin.Engine) error { + set := jet.NewSet(jet.NewOSFileSystemLoader("views")) + renderer := &JetRenderer{set: set} + e.HTMLRender = renderer + + return nil +} + +func (it *Server) errorHandler(logger *l.Logger) gin.HandlerFunc { + return func(ctx *gin.Context) { + it.errorHandlerSetRecovery(ctx) + if len(ctx.Errors) > 0 { + logger.Error(ctx.Errors[0].Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "errors": "Internal Server Error", + }) + } + } +} + +func (it *Server) errorHandlerSetRecovery(ctx *gin.Context) { + defer func() { + if err := recover(); err != nil { + _ = ctx.Error(fmt.Errorf("panic: %v\n%s", err, debug.Stack())) + } + }() + ctx.Next() +} + +type JetRenderer struct { + set *jet.Set + template string + data any +} + +func (it *JetRenderer) Instance(tpl string, data any) render.Render { + it.template = tpl + it.data = data + return it +} + +func (it *JetRenderer) Render(w http.ResponseWriter) error { + tpl, err := it.set.GetTemplate(it.template) + if err != nil { + return err + } + + vars := make(jet.VarMap) + if it.data != nil { + if v, ok := it.data.(jet.VarMap); ok { + vars = v + } else { + vars.Set("data", it.data) + } + } + + var buf bytes.Buffer + err = tpl.Execute(&buf, vars, nil) + if err != nil { + return err + } + _, err = io.Copy(w, &buf) + return err +} + +func (it *JetRenderer) WriteContentType(http.ResponseWriter) { + // не знаю в каких случаях будет вызван этот метод + panic("implement me") // TODO +} diff --git a/pink_fox_app/src/app/http_server/response-factory.go b/pink_fox_app/src/app/http_server/response-factory.go index cdd9094..65ecc34 100644 --- a/pink_fox_app/src/app/http_server/response-factory.go +++ b/pink_fox_app/src/app/http_server/response-factory.go @@ -2,14 +2,19 @@ package http_server import ( "github.com/gin-gonic/gin" + "pink_fox/src/app/types" ) type ResponseFactory struct { - ctx *gin.Context + ctx *gin.Context + makeViewObject types.MakeViewObject } -func NewResponseFactory(ctx *gin.Context) *ResponseFactory { - return &ResponseFactory{ctx: ctx} +func NewResponseFactory(ctx *gin.Context, makeViewObject types.MakeViewObject) *ResponseFactory { + return &ResponseFactory{ + ctx: ctx, + makeViewObject: makeViewObject, + } } func (it *ResponseFactory) String(s string) *StringResponse { @@ -23,3 +28,7 @@ func (it *ResponseFactory) HtmlError(code int) *HtmlErrorResponse { func (it *ResponseFactory) Redirect(url string) *RedirectResponse { return NewRedirectResponse(url, it.ctx) } + +func (it *ResponseFactory) View(id int16, file string, data any) *ViewResponse { + return NewViewResponse(it.ctx, it.makeViewObject, id, file, data) +} diff --git a/pink_fox_app/src/app/http_server/response.go b/pink_fox_app/src/app/http_server/response.go index b9b4078..93d5a3e 100644 --- a/pink_fox_app/src/app/http_server/response.go +++ b/pink_fox_app/src/app/http_server/response.go @@ -2,8 +2,10 @@ package http_server import ( "fmt" + "github.com/CloudyKit/jet/v6" "github.com/gin-gonic/gin" "net/http" + "pink_fox/src/app/types" ) type Response interface { @@ -65,3 +67,29 @@ func (it *RedirectResponse) SetCode(code int) *RedirectResponse { func (it *RedirectResponse) Render() { it.ctx.Redirect(it.code, it.to) } + +type ViewResponse struct { + ctx *gin.Context + makeViewObject types.MakeViewObject + id int16 + file string + data any +} + +func NewViewResponse(ctx *gin.Context, makeViewObject types.MakeViewObject, id int16, file string, data any) *ViewResponse { + return &ViewResponse{ + ctx: ctx, + makeViewObject: makeViewObject, + id: id, + file: file, + data: data, + } +} + +func (it *ViewResponse) Render() { + data := make(jet.VarMap) + data.Set("vi", it.makeViewObject(it.id)) + data.Set("it", it.data) + + it.ctx.HTML(http.StatusOK, it.file, data) +} diff --git a/pink_fox_app/src/app/routes_manager/routes_manager.go b/pink_fox_app/src/app/routes_manager/routes_manager.go index f450404..7533b00 100644 --- a/pink_fox_app/src/app/routes_manager/routes_manager.go +++ b/pink_fox_app/src/app/routes_manager/routes_manager.go @@ -5,8 +5,10 @@ import ( "github.com/gin-gonic/gin" "net/http" "pink_fox/src/app" - "pink_fox/src/app/http_server" + hs "pink_fox/src/app/http_server" le "pink_fox/src/app/lerror" + "pink_fox/src/app/types" + vm2 "pink_fox/src/app/views_manager" "pink_fox/src/dependence" "runtime/debug" "strings" @@ -16,21 +18,23 @@ type RoutesManager struct { engine *gin.Engine application *app.Application middlewares []MiddlewareFunc + vm *vm2.ViewsManager } -func NewManager(e *gin.Engine, application *app.Application) *RoutesManager { +func NewManager(e *gin.Engine, application *app.Application, vm *vm2.ViewsManager) *RoutesManager { return &RoutesManager{ engine: e, application: application, middlewares: make([]MiddlewareFunc, 0), + vm: vm, } } -type MiddlewareFunc func(*dependence.Container, ActionFunc) (http_server.Response, *le.Error) +type MiddlewareFunc func(*dependence.Container, ActionFunc) (hs.Response, *le.Error) -type HandlerFunc func(container *dependence.Container) (http_server.Response, *le.Error) +type HandlerFunc func(container *dependence.Container) (hs.Response, *le.Error) -type ActionFunc func() (http_server.Response, *le.Error) +type ActionFunc func() (hs.Response, *le.Error) func (it *RoutesManager) Group(relativePath string, middlewares ...MiddlewareFunc) *Group { return NewGroup(it, relativePath, middlewares...) @@ -41,18 +45,18 @@ func (it *RoutesManager) Use(middlewares ...MiddlewareFunc) { } func (it *RoutesManager) ANY(relativePath string, handler HandlerFunc, middlewares ...MiddlewareFunc) { - handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.application) + handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.application, it.vm) it.engine.GET(relativePath, handlerForGin) it.engine.POST(relativePath, handlerForGin) } func (it *RoutesManager) GET(relativePath string, handler HandlerFunc, middlewares ...MiddlewareFunc) { - handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.application) + handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.application, it.vm) it.engine.GET(relativePath, handlerForGin) } func (it *RoutesManager) POST(relativePath string, handler HandlerFunc, middlewares ...MiddlewareFunc) { - handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.application) + handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.application, it.vm) it.engine.POST(relativePath, handlerForGin) } @@ -83,36 +87,62 @@ func (it *Group) Use(middlewares ...MiddlewareFunc) { } func (it *Group) ANY(relativePath string, handler HandlerFunc, middlewares ...MiddlewareFunc) { - handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.routesManager.application) + handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.routesManager.application, it.routesManager.vm) it.routesManager.engine.GET(it.relativePath+relativePath, handlerForGin) it.routesManager.engine.POST(it.relativePath+relativePath, handlerForGin) } func (it *Group) GET(relativePath string, handler HandlerFunc, middlewares ...MiddlewareFunc) { - handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.routesManager.application) + handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.routesManager.application, it.routesManager.vm) it.routesManager.engine.GET(it.relativePath+relativePath, handlerForGin) } func (it *Group) POST(relativePath string, handler HandlerFunc, middlewares ...MiddlewareFunc) { - handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.routesManager.application) + handlerForGin := makeHandlerGin(handler, append(it.middlewares, middlewares...), it.routesManager.application, it.routesManager.vm) it.routesManager.engine.POST(it.relativePath+relativePath, handlerForGin) } -func makeHandlerGin(handler HandlerFunc, middlewares []MiddlewareFunc, application *app.Application) gin.HandlerFunc { +func makeHandlerGin(handler HandlerFunc, middlewares []MiddlewareFunc, application *app.Application, vm *vm2.ViewsManager) gin.HandlerFunc { return func(ctx *gin.Context) { dic := dependence.NewContainer(application, ctx) + dic.SetViewManager(castMakeViewObject(dic, vm)) defer dic.Close() - res, err := pipeline(handler, middlewares, dic) + err := execHandlerGin(handler, middlewares, dic, ctx) if err != nil { writeToLog(err, application) showHtmlError(err, application, ctx) return } - res.Render() } } -func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence.Container) (resp http_server.Response, lerr *le.Error) { +func castMakeViewObject(depend *dependence.Container, vm *vm2.ViewsManager) types.MakeViewObject { + return func(id int16) any { + obj, err := vm.GetViewObject(id, depend) + if err != nil { + panic(fmt.Sprintf("шаблон с id=%d не найден", id)) + } + return obj + } +} + +func execHandlerGin(handler HandlerFunc, middlewares []MiddlewareFunc, dic *dependence.Container, ctx *gin.Context) *le.Error { + res, err := pipeline(handler, middlewares, dic) + if err != nil { + return err + } + + res.Render() + if len(ctx.Errors) > 0 { + errorGin := ctx.Errors[0] + ctx.Errors = make([]*gin.Error, 0) + return le.New(errorGin) + } + + return nil +} + +func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence.Container) (resp hs.Response, lerr *le.Error) { defer func() { if err := recover(); err != nil { resp = nil @@ -120,7 +150,7 @@ func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence } }() - handler := func() (http_server.Response, *le.Error) { + handler := func() (hs.Response, *le.Error) { return endpoint(di) } @@ -133,7 +163,7 @@ func pipeline(endpoint HandlerFunc, middlewares []MiddlewareFunc, di *dependence } func createMiddleware(next ActionFunc, middleware MiddlewareFunc, di *dependence.Container) ActionFunc { - return func() (http_server.Response, *le.Error) { + return func() (hs.Response, *le.Error) { return middleware(di, next) } } @@ -158,5 +188,5 @@ func showHtmlError(err *le.Error, application *app.Application, ctx *gin.Context } } ctx.Header("Content-Type", "text/html; charset=utf-8") - ctx.String(500, fmt.Sprintf("
{{ it }}
\ No newline at end of file