package admin import ( "strconv" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/helpers" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/interfaces" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/mapper" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/models" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" ) const errArticleExists = "Статья с данным URL уже существует" func ArticleHandler(sp interfaces.ServiceProvider) fiber.Handler { return func(ctx *fiber.Ctx) error { articles, err := sp.GetArticleRepository().GetAll(ctx.Context()) if err != nil { return err } return ctx.Render("admin/article", fiber.Map{ "articles": mapper.ConvertArticleModelsToDTO(articles), "section": "article", }, "admin/_layout") } } func AddArticleHandler(sp interfaces.ServiceProvider) fiber.Handler { return func(fctx *fiber.Ctx) error { ctx := fctx.Context() var validate = validator.New() validateErrors := make(map[string]string) trans, err := helpers.GetDefaultTranslator(validate) if err != nil { return err } form := models.ArticleForm{ ActiveTags: make(map[int]bool), } tags, err := sp.GetTagRepository().GetAll(ctx) if err != nil { return err } tagsDTO := mapper.ConvertTagModelsToDTO(tags) if fctx.Method() == fiber.MethodPost { if err = fctx.BodyParser(&form); err != nil { return err } if err = validate.Struct(form); err != nil { validateErrors = helpers.FormatValidateErrors(err, trans) } if res, _ := sp.GetArticleRepository().GetByURL(ctx, form.URL); res != nil { validateErrors["ArticleForm.URL"] = errArticleExists } tagIDs := make([]int, 0, len(form.Tags)) for i := range form.Tags { tagID, tagErr := strconv.Atoi(form.Tags[i]) if tagErr != nil { return tagErr } tagIDs = append(tagIDs, tagID) } for i := range tagIDs { form.ActiveTags[tagIDs[i]] = true } if len(validateErrors) == 0 { articleModel, err := mapper.ConvertArticleFormToModel(form) if err != nil { return err } articleID, articleErr := sp.GetArticleRepository().Add(ctx, *articleModel) if articleErr != nil { return articleErr } if len(form.Tags) > 0 { if err = sp.GetArticleTagRepository().Add(ctx, articleID, tagIDs); err != nil { return err } } sp.GetCacheService().FlushAll() return fctx.Redirect("/admin") } } return fctx.Render("admin/article_edit", fiber.Map{ "form": form, "errors": validateErrors, "tags": tagsDTO, "section": "article", "title": "Добавление статьи", "show_apply": false, }, "admin/_layout") } } func EditArticleHandler(sp interfaces.ServiceProvider) fiber.Handler { return func(fctx *fiber.Ctx) error { ctx := fctx.Context() var validate = validator.New() validateErrors := make(map[string]string) trans, err := helpers.GetDefaultTranslator(validate) if err != nil { return err } ID, err := strconv.Atoi(fctx.Params("id")) if err != nil { return err } article, err := sp.GetArticleRepository().GetByID(ctx, ID) if err != nil { return err } if article == nil { return fiber.ErrNotFound } articleTags, err := sp.GetTagRepository().GetByArticleID(ctx, ID) if err != nil { return err } tags, err := sp.GetTagRepository().GetAll(ctx) if err != nil { return err } tagsDTO := mapper.ConvertTagModelsToDTO(tags) var form *models.ArticleForm if fctx.Method() == fiber.MethodGet { form = mapper.ConvertArticleModelToForm(*article, articleTags) } else if fctx.Method() == fiber.MethodPost { form = &models.ArticleForm{ ID: ID, ActiveTags: make(map[int]bool), } if err = fctx.BodyParser(form); err != nil { return err } if err = validate.Struct(*form); err != nil { validateErrors = helpers.FormatValidateErrors(err, trans) } if res, _ := sp.GetArticleRepository().GetByURL(ctx, form.URL); res != nil { if res.ID != ID { validateErrors["ArticleForm.URL"] = errArticleExists } } tagIDs := make([]int, 0, len(form.Tags)) for i := range form.Tags { tagID, tagErr := strconv.Atoi(form.Tags[i]) if tagErr != nil { return tagErr } tagIDs = append(tagIDs, tagID) } for i := range tagIDs { form.ActiveTags[tagIDs[i]] = true } if len(validateErrors) == 0 { articleModel, err := mapper.ConvertArticleFormToModel(*form) if err != nil { return err } err = sp.GetArticleRepository().Update(ctx, *articleModel) if err != nil { return err } var tagsToAdd, tagsToDelete []int oldTagsMap := make(map[int]struct{}, len(articleTags)) for i := range articleTags { oldTagsMap[articleTags[i].ID] = struct{}{} if _, ok := form.ActiveTags[articleTags[i].ID]; !ok { tagsToDelete = append(tagsToDelete, articleTags[i].ID) } } for i := range tagIDs { if _, ok := oldTagsMap[tagIDs[i]]; !ok { tagsToAdd = append(tagsToAdd, tagIDs[i]) } } if len(tagsToAdd) > 0 { if err = sp.GetArticleTagRepository().Add(ctx, ID, tagsToAdd); err != nil { return err } } if len(tagsToDelete) > 0 { if err = sp.GetArticleTagRepository().Delete(ctx, ID, tagsToDelete); err != nil { return err } } sp.GetCacheService().FlushAll() if fctx.FormValue("action", "save") == "save" { return fctx.Redirect("/admin") } } } return fctx.Render("admin/article_edit", fiber.Map{ "form": form, "errors": validateErrors, "tags": tagsDTO, "show_apply": true, "section": "article", "title": "Редактирование статьи", }, "admin/_layout") } } func DeleteArticleHandler(sp interfaces.ServiceProvider) fiber.Handler { return func(fctx *fiber.Ctx) error { ctx := fctx.Context() ID, err := strconv.Atoi(fctx.Params("id")) if err != nil { return err } article, err := sp.GetArticleRepository().GetByID(ctx, ID) if err != nil { return err } if fctx.Method() == fiber.MethodPost { err = sp.GetArticleTagRepository().DeleteByArticleID(ctx, ID) if err != nil { return err } err = sp.GetArticleRepository().Delete(ctx, ID) if err != nil { return err } sp.GetCacheService().FlushAll() return fctx.Redirect("/admin") } return fctx.Render("admin/article_delete", fiber.Map{ "article": article.Title, "section": "article", }, "admin/_layout") } }