article_add.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package admin
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "strconv"
  7. "git.dmitriygnatenko.ru/dima/go-common/logger"
  8. validation "github.com/go-ozzo/ozzo-validation/v4"
  9. "github.com/gofiber/fiber/v2"
  10. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/dto"
  11. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/mapper"
  12. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/i18n"
  13. )
  14. type articleAddPage struct {
  15. tm TransactionManager
  16. articleRepository ArticleRepository
  17. tagRepository TagRepository
  18. articleTagRepository ArticleTagRepository
  19. languageRepository LanguageRepository
  20. cacheService CacheService
  21. }
  22. func NewArticleAddPageHandler(
  23. tm TransactionManager,
  24. articleRepository ArticleRepository,
  25. tagRepository TagRepository,
  26. articleTagRepository ArticleTagRepository,
  27. languageRepository LanguageRepository,
  28. cacheService CacheService,
  29. ) fiber.Handler {
  30. h := articleAddPage{
  31. tm: tm,
  32. articleRepository: articleRepository,
  33. tagRepository: tagRepository,
  34. articleTagRepository: articleTagRepository,
  35. languageRepository: languageRepository,
  36. cacheService: cacheService,
  37. }
  38. return h.handler()
  39. }
  40. func (h articleAddPage) handler() fiber.Handler {
  41. return func(fctx *fiber.Ctx) error {
  42. ctx := fctx.Context()
  43. lang := i18n.LanguageFromContext(fctx)
  44. validationErrors := make(map[string]string)
  45. form := dto.ArticleForm{
  46. ActiveTags: make(map[uint64]bool),
  47. }
  48. tags, err := h.getTags(ctx)
  49. if err != nil {
  50. logger.Error(ctx, err.Error())
  51. return err
  52. }
  53. languages, err := h.getLanguages(ctx)
  54. if err != nil {
  55. logger.Error(ctx, err.Error())
  56. return err
  57. }
  58. if fctx.Method() == fiber.MethodPost {
  59. if err = fctx.BodyParser(&form); err != nil {
  60. logger.Info(ctx, err.Error())
  61. return err
  62. }
  63. if err = h.validateAddArticleForm(ctx, form); err != nil {
  64. logger.Info(ctx, err.Error())
  65. validationErrors = mapper.ToErrorsMap(err)
  66. }
  67. tagIDs, err := h.formatTags(form.Tags)
  68. if err != nil {
  69. logger.Error(ctx, err.Error())
  70. return err
  71. }
  72. for i := range tagIDs {
  73. form.ActiveTags[tagIDs[i]] = true
  74. }
  75. if len(validationErrors) == 0 {
  76. articleModel, err := mapper.ToArticleModel(form)
  77. if err != nil {
  78. logger.Error(ctx, err.Error())
  79. return err
  80. }
  81. err = h.tm.ReadCommitted(ctx, func(ctx context.Context) error {
  82. articleID, txErr := h.articleRepository.Add(ctx, *articleModel)
  83. if txErr != nil {
  84. return txErr
  85. }
  86. if len(tagIDs) > 0 {
  87. if txErr = h.articleTagRepository.Add(ctx, articleID, tagIDs); txErr != nil {
  88. return txErr
  89. }
  90. }
  91. return nil
  92. })
  93. if err != nil {
  94. logger.Error(ctx, err.Error())
  95. return err
  96. }
  97. return fctx.Redirect("/admin")
  98. }
  99. }
  100. return fctx.Render("admin/article_edit", fiber.Map{
  101. "lang": lang,
  102. "title": i18n.T(lang, "admin_article_add_title"),
  103. "section": "article",
  104. "show_apply": false,
  105. "form": form,
  106. "errors": validationErrors,
  107. "tags": tags,
  108. "languages": languages,
  109. }, "admin/_layout")
  110. }
  111. }
  112. func (h articleAddPage) getTags(ctx context.Context) ([]dto.Tag, error) {
  113. tagModels, err := h.tagRepository.GetAll(ctx)
  114. if err != nil {
  115. return nil, err
  116. }
  117. return mapper.ToTagsList(tagModels), nil
  118. }
  119. func (h articleAddPage) getLanguages(ctx context.Context) ([]string, error) {
  120. langModels, err := h.languageRepository.GetAll(ctx)
  121. if err != nil {
  122. return nil, err
  123. }
  124. res := make([]string, 0, len(langModels))
  125. for i := range langModels {
  126. res = append(res, langModels[i].URL)
  127. }
  128. return res, nil
  129. }
  130. func (h articleAddPage) formatTags(tags []string) ([]uint64, error) {
  131. ids := make([]uint64, 0, len(tags))
  132. for i := range tags {
  133. id, err := strconv.ParseUint(tags[i], 10, 64)
  134. if err != nil {
  135. return nil, err
  136. }
  137. ids = append(ids, id)
  138. }
  139. return ids, nil
  140. }
  141. func (h articleAddPage) validateAddArticleForm(ctx context.Context, req dto.ArticleForm) error {
  142. lang := i18n.DefaultLanguage()
  143. requiredMsg := i18n.T(lang, "admin_err_required")
  144. lengthMsg := i18n.T(lang, "admin_err_length_255")
  145. return validation.ValidateStructWithContext(
  146. ctx,
  147. &req,
  148. validation.Field(
  149. &req.Title,
  150. validation.Required.Error(requiredMsg),
  151. validation.Length(1, 255).Error(lengthMsg),
  152. ),
  153. validation.Field(
  154. &req.Image,
  155. validation.Required.Error(requiredMsg),
  156. validation.Length(1, 255).Error(lengthMsg),
  157. ),
  158. validation.Field(
  159. &req.URL,
  160. validation.Required.Error(requiredMsg),
  161. validation.Length(1, 255).Error(lengthMsg),
  162. validation.By(h.validateDuplicateURL),
  163. ),
  164. validation.Field(
  165. &req.Text,
  166. validation.Required.Error(requiredMsg),
  167. ),
  168. validation.Field(
  169. &req.PreviewText,
  170. validation.Length(1, 255).Error(lengthMsg),
  171. ),
  172. validation.Field(
  173. &req.MetaKeywords,
  174. validation.Length(1, 255).Error(lengthMsg),
  175. ),
  176. validation.Field(
  177. &req.MetaDescription,
  178. validation.Length(1, 255).Error(lengthMsg),
  179. ),
  180. validation.Field(
  181. &req.PublishTime,
  182. validation.Required.Error(requiredMsg),
  183. ),
  184. validation.Field(
  185. &req.Language,
  186. validation.Required.Error(requiredMsg),
  187. ),
  188. )
  189. }
  190. func (h articleAddPage) validateDuplicateURL(value interface{}) error {
  191. url, _ := value.(string)
  192. ctx := context.Background()
  193. lang := i18n.DefaultLanguage()
  194. _, err := h.articleRepository.GetByURL(ctx, url)
  195. if err != nil {
  196. if !errors.Is(err, sql.ErrNoRows) {
  197. logger.Error(ctx, err.Error())
  198. }
  199. return nil
  200. }
  201. return errors.New(i18n.T(lang, "admin_err_article_duplicate", url))
  202. }