package admin import ( "context" "database/sql" "errors" "strconv" "git.dmitriygnatenko.ru/dima/go-common/logger" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/gofiber/fiber/v2" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/dto" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/mapper" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/i18n" ) type articleAddPage struct { tm TransactionManager articleRepository ArticleRepository tagRepository TagRepository articleTagRepository ArticleTagRepository languageRepository LanguageRepository cacheService CacheService } func NewArticleAddPageHandler( tm TransactionManager, articleRepository ArticleRepository, tagRepository TagRepository, articleTagRepository ArticleTagRepository, languageRepository LanguageRepository, cacheService CacheService, ) fiber.Handler { h := articleAddPage{ tm: tm, articleRepository: articleRepository, tagRepository: tagRepository, articleTagRepository: articleTagRepository, languageRepository: languageRepository, cacheService: cacheService, } return h.handler() } func (h articleAddPage) handler() fiber.Handler { return func(fctx *fiber.Ctx) error { ctx := fctx.Context() lang := i18n.LanguageFromContext(fctx) validationErrors := make(map[string]string) form := dto.ArticleForm{ ActiveTags: make(map[uint64]bool), } tags, err := h.getTags(ctx) if err != nil { logger.Error(ctx, err.Error()) return err } languages, err := h.getLanguages(ctx) if err != nil { logger.Error(ctx, err.Error()) return err } if fctx.Method() == fiber.MethodPost { if err = fctx.BodyParser(&form); err != nil { logger.Info(ctx, err.Error()) return err } if err = h.validateAddArticleForm(ctx, form); err != nil { logger.Info(ctx, err.Error()) validationErrors = mapper.ToErrorsMap(err) } tagIDs, err := h.formatTags(form.Tags) if err != nil { logger.Error(ctx, err.Error()) return err } for i := range tagIDs { form.ActiveTags[tagIDs[i]] = true } if len(validationErrors) == 0 { articleModel, err := mapper.ToArticleModel(form) if err != nil { logger.Error(ctx, err.Error()) return err } err = h.tm.ReadCommitted(ctx, func(ctx context.Context) error { articleID, txErr := h.articleRepository.Add(ctx, *articleModel) if txErr != nil { return txErr } if len(tagIDs) > 0 { if txErr = h.articleTagRepository.Add(ctx, articleID, tagIDs); txErr != nil { return txErr } } return nil }) if err != nil { logger.Error(ctx, err.Error()) return err } return fctx.Redirect("/admin") } } return fctx.Render("admin/article_edit", fiber.Map{ "lang": lang, "title": i18n.T(lang, "admin_article_add_title"), "section": "article", "show_apply": false, "form": form, "errors": validationErrors, "tags": tags, "languages": languages, }, "admin/_layout") } } func (h articleAddPage) getTags(ctx context.Context) ([]dto.Tag, error) { tagModels, err := h.tagRepository.GetAll(ctx) if err != nil { return nil, err } return mapper.ToTagsList(tagModels), nil } func (h articleAddPage) getLanguages(ctx context.Context) ([]string, error) { langModels, err := h.languageRepository.GetAll(ctx) if err != nil { return nil, err } res := make([]string, 0, len(langModels)) for i := range langModels { res = append(res, langModels[i].URL) } return res, nil } func (h articleAddPage) formatTags(tags []string) ([]uint64, error) { ids := make([]uint64, 0, len(tags)) for i := range tags { id, err := strconv.ParseUint(tags[i], 10, 64) if err != nil { return nil, err } ids = append(ids, id) } return ids, nil } func (h articleAddPage) validateAddArticleForm(ctx context.Context, req dto.ArticleForm) error { lang := i18n.DefaultLanguage() requiredMsg := i18n.T(lang, "admin_err_required") lengthMsg := i18n.T(lang, "admin_err_length_255") return validation.ValidateStructWithContext( ctx, &req, validation.Field( &req.Title, validation.Required.Error(requiredMsg), validation.Length(1, 255).Error(lengthMsg), ), validation.Field( &req.Image, validation.Required.Error(requiredMsg), validation.Length(1, 255).Error(lengthMsg), ), validation.Field( &req.URL, validation.Required.Error(requiredMsg), validation.Length(1, 255).Error(lengthMsg), validation.By(h.validateDuplicateURL), ), validation.Field( &req.Text, validation.Required.Error(requiredMsg), ), validation.Field( &req.PreviewText, validation.Length(1, 255).Error(lengthMsg), ), validation.Field( &req.MetaKeywords, validation.Length(1, 255).Error(lengthMsg), ), validation.Field( &req.MetaDescription, validation.Length(1, 255).Error(lengthMsg), ), validation.Field( &req.PublishTime, validation.Required.Error(requiredMsg), ), validation.Field( &req.Language, validation.Required.Error(requiredMsg), ), ) } func (h articleAddPage) validateDuplicateURL(value interface{}) error { url, _ := value.(string) ctx := context.Background() lang := i18n.DefaultLanguage() _, err := h.articleRepository.GetByURL(ctx, url) if err != nil { if !errors.Is(err, sql.ErrNoRows) { logger.Error(ctx, err.Error()) } return nil } return errors.New(i18n.T(lang, "admin_err_article_duplicate", url)) }