139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
package article
|
|
|
|
import (
|
|
"RenewCMS/api/pkg/apperr"
|
|
validator "RenewCMS/api/pkg/validator"
|
|
domain "RenewCMS/internal/domain/article"
|
|
"RenewCMS/internal/domain/language"
|
|
"RenewCMS/internal/domain/site"
|
|
"RenewCMS/internal/infrastructure/useCases"
|
|
"encoding/json"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
type PublicHandler struct {
|
|
resolveUseCase *useCases.ResolvePublicArticleUseCase
|
|
}
|
|
|
|
func NewPublicHandler(resolve *useCases.ResolvePublicArticleUseCase) *PublicHandler {
|
|
return &PublicHandler{resolveUseCase: resolve}
|
|
}
|
|
|
|
type publicLookupResponse struct {
|
|
articleResponse
|
|
Alternates []domain.Alternate `json:"alternates"`
|
|
}
|
|
|
|
type publicStoryResponse struct {
|
|
articleResponse
|
|
Alternates []domain.Alternate `json:"alternates"`
|
|
RequestedLang string `json:"requested_lang"`
|
|
Fallback bool `json:"fallback"`
|
|
}
|
|
|
|
type publicStoryQuery struct {
|
|
Fallback *string `validate:"omitempty,eq=default"`
|
|
}
|
|
|
|
var publicErrors = []struct {
|
|
cause error
|
|
message string
|
|
status int
|
|
}{
|
|
{domain.ErrArticleNotFound, apperr.ArticleNotFoundCode, http.StatusNotFound},
|
|
{domain.ErrLanguageNotAvailable, apperr.LanguageNotAvailableCode, http.StatusNotFound},
|
|
{site.ErrSiteNotFound, apperr.SiteNotFoundCode, http.StatusNotFound},
|
|
{language.ErrInvalidLanguage, apperr.InvalidLanguageCode, http.StatusBadRequest},
|
|
}
|
|
|
|
func writePublicError(w http.ResponseWriter, err error) {
|
|
for _, known := range publicErrors {
|
|
if errors.Is(err, known.cause) {
|
|
http.Error(w, known.message, known.status)
|
|
return
|
|
}
|
|
}
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
}
|
|
|
|
func (h *PublicHandler) Lookup(w http.ResponseWriter, r *http.Request) {
|
|
format, ok := parseBodyFormat(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
found, err := h.resolveUseCase.ByLangSlug(useCases.PublicLookupQuery{
|
|
SiteKey: chi.URLParam(r, "key"),
|
|
Lang: chi.URLParam(r, "lang"),
|
|
Slug: chi.URLParam(r, "slug"),
|
|
Format: format,
|
|
})
|
|
if err != nil {
|
|
writePublicError(w, err)
|
|
return
|
|
}
|
|
h.write(w, found, format, func(response articleResponse) any {
|
|
return publicLookupResponse{articleResponse: response, Alternates: found.Alternates}
|
|
})
|
|
}
|
|
|
|
func (h *PublicHandler) Story(w http.ResponseWriter, r *http.Request) {
|
|
storyID, err := strconv.ParseUint(chi.URLParam(r, "story_id"), 10, 32)
|
|
if err != nil {
|
|
http.Error(w, apperr.IdUint32ErrorMessage, http.StatusBadRequest)
|
|
return
|
|
}
|
|
format, ok := parseBodyFormat(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
values := r.URL.Query()
|
|
query := publicStoryQuery{}
|
|
if values.Has("fallback") {
|
|
fallback := values.Get("fallback")
|
|
query.Fallback = &fallback
|
|
}
|
|
if validator.Validate.Struct(query) != nil {
|
|
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
|
return
|
|
}
|
|
command := useCases.PublicStoryQuery{
|
|
SiteKey: chi.URLParam(r, "key"),
|
|
StoryID: uint32(storyID),
|
|
FallbackDefault: query.Fallback != nil,
|
|
Format: format,
|
|
}
|
|
if values.Has("lang") {
|
|
requested := values.Get("lang")
|
|
command.Lang = &requested
|
|
}
|
|
found, err := h.resolveUseCase.ByStory(command)
|
|
if err != nil {
|
|
writePublicError(w, err)
|
|
return
|
|
}
|
|
h.write(w, found, format, func(response articleResponse) any {
|
|
return publicStoryResponse{articleResponse: response, Alternates: found.Alternates, RequestedLang: found.RequestedLang, Fallback: found.Fallback}
|
|
})
|
|
}
|
|
|
|
func (h *PublicHandler) write(w http.ResponseWriter, found useCases.PublicArticle, format domain.BodyFormat, shape func(articleResponse) any) {
|
|
response, err := toArticleResponse(found.Article, format, found.Viewer, found.Context)
|
|
if err != nil {
|
|
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
body, err := json.Marshal(shape(response))
|
|
if err != nil {
|
|
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if _, err := w.Write(body); err != nil {
|
|
log.Printf("Failed to write response: %v", err)
|
|
}
|
|
}
|