package handler import ( "context" "database/sql" "errors" "git.dmitriygnatenko.ru/dima/go-common/logger" "github.com/gofiber/fiber/v2" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/dto" customErrors "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/helpers/errors" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/mapper" "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/i18n" ) const ( ArticleCacheKey = "article" RecentArticlesCacheKey = "recent-articles" UsedTagsCacheKey = "used-tags" previewArticlesCount = 3 articleParam = "article" ) type articlePage struct { cacheService CacheService articleRepository ArticleRepository tagRepository TagRepository } func NewArticlePageHandler( cacheService CacheService, articleRepository ArticleRepository, tagRepository TagRepository, ) fiber.Handler { h := articlePage{ cacheService: cacheService, articleRepository: articleRepository, tagRepository: tagRepository, } return h.handler() } func (h articlePage) handler() fiber.Handler { return func(fctx *fiber.Ctx) error { ctx := fctx.Context() url := fctx.Params(articleParam) lang := i18n.LanguageFromContext(fctx) // article renderData, err := h.article(ctx, lang, url) if err != nil { if !errors.Is(err, fiber.ErrNotFound) { logger.Error(ctx, customErrors.Wrap(err, "article page").Error()) } return err } // recent articles articles, err := h.recentArticles(ctx, lang) if err != nil { logger.Error(ctx, customErrors.Wrap(err, "article page").Error()) return err } renderData["sidebarArticles"] = articles // tags tags, err := h.tags(ctx, lang) if err != nil { logger.Error(ctx, customErrors.Wrap(err, "article page").Error()) return err } renderData["sidebarTags"] = tags return fctx.Render("article", renderData, "_layout") } } func (h articlePage) article( ctx context.Context, lang i18n.Language, url string, ) (fiber.Map, error) { articleCacheKey := ArticleCacheKey + url cacheData, found := h.cacheService.Get(articleCacheKey) if found { if articleData, ok := cacheData.(fiber.Map); ok { return articleData, nil } } articleModel, err := h.articleRepository.GetByURL(ctx, url) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, fiber.ErrNotFound } return nil, customErrors.Wrap(err, "can't get article by url") } if !articleModel.IsActive { return nil, fiber.ErrNotFound } article := mapper.ToArticle(lang, *articleModel) renderData := fiber.Map{ "lang": lang, "headTitle": i18n.T(lang, "head_title"), "headDescription": article.MetaDescription, "headKeywords": article.MetaKeywords, "pageTitle": i18n.T(lang, "article_page_title", article.Title), "article": article, } h.cacheService.Set(articleCacheKey, renderData, nil) return renderData, nil } func (h articlePage) recentArticles( ctx context.Context, lang i18n.Language, ) ([]dto.ArticlePreview, error) { recentArticlesCacheKey := RecentArticlesCacheKey + string(lang) cacheData, found := h.cacheService.Get(recentArticlesCacheKey) if found { if articles, ok := cacheData.([]dto.ArticlePreview); ok { return articles, nil } } articleModels, err := h.articleRepository.GetAllPreview(ctx, i18n.LanguageID(lang)) if err != nil { return nil, customErrors.Wrap(err, "can't get articles") } if len(articleModels) > previewArticlesCount { articleModels = articleModels[:previewArticlesCount] } articles := mapper.ToArticlesPreview(lang, articleModels) h.cacheService.Set(recentArticlesCacheKey, articles, nil) return articles, nil } func (h articlePage) tags( ctx context.Context, lang i18n.Language, ) ([]dto.Tag, error) { usedTagsCacheKey := UsedTagsCacheKey + string(lang) cacheData, found := h.cacheService.Get(usedTagsCacheKey) if found { if tagsData, ok := cacheData.([]dto.Tag); ok { return tagsData, nil } } tagModels, err := h.tagRepository.GetAllUsed(ctx, i18n.LanguageID(lang)) if err != nil { return nil, customErrors.Wrap(err, "can't get tags") } tags := mapper.ToTagsList(tagModels) h.cacheService.Set(usedTagsCacheKey, tags, nil) return tags, nil }