feat: Sites feature backend

This commit is contained in:
Florian Sylvain committed 2026-10-14 03:56:50 +02:00
1 parent 45fcbd890d
commit a11338094e
98 files changed
+5365 -214

No files matched your search

+66 -11
View File
@@ -5,10 +5,12 @@ import (
"RenewCMS/api/pkg/claims"
validator "RenewCMS/api/pkg/validator"
domain "RenewCMS/internal/domain/article"
"RenewCMS/internal/domain/site"
"RenewCMS/internal/infrastructure/useCases"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
@@ -50,6 +52,7 @@ type articleResponse struct {
Body json.RawMessage `json:"body"`
BodyFormat domain.BodyFormat `json:"body_format"`
IsOnline bool `json:"is_online"`
SiteID uint32 `json:"site_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
@@ -69,6 +72,7 @@ func toArticleResponse(article domain.Article, format domain.BodyFormat) (articl
Body: body,
BodyFormat: format,
IsOnline: article.IsOnline,
SiteID: article.SiteID,
CreatedAt: article.CreatedAt,
UpdatedAt: article.UpdatedAt,
}, nil
@@ -173,6 +177,10 @@ func writeArticleErrorResponse(w http.ResponseWriter, err error) {
http.Error(w, apperr.ArticleNotFoundErrorMessage, http.StatusNotFound)
return
}
if errors.Is(err, site.ErrSiteNotFound) {
http.Error(w, apperr.SiteNotFoundErrorMessage, http.StatusNotFound)
return
}
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
@@ -213,7 +221,40 @@ func (h *Handler) setIsOnline(w http.ResponseWriter, r *http.Request, isOnline b
}
type UpdateArticleBody struct {
Body string `json:"body"`
Body *string `json:"body"`
SiteID *uint32 `json:"site_id" validate:"omitempty,gte=1"`
}
var updatableArticleFields = []string{"body", "site_id"}
func readUpdateArticleBody(r *http.Request) (UpdateArticleBody, bool) {
raw, err := io.ReadAll(r.Body)
if err != nil {
return UpdateArticleBody{}, false
}
var fields map[string]json.RawMessage
if err := json.Unmarshal(raw, &fields); err != nil {
return UpdateArticleBody{}, false
}
known := 0
for _, name := range updatableArticleFields {
value, present := fields[name]
if !present {
continue
}
if string(value) == "null" {
return UpdateArticleBody{}, false
}
known++
}
if known == 0 {
return UpdateArticleBody{}, false
}
var request UpdateArticleBody
if err := json.Unmarshal(raw, &request); err != nil {
return UpdateArticleBody{}, false
}
return request, true
}
func (h *Handler) UpdateArticleBody(w http.ResponseWriter, r *http.Request) {
@@ -228,18 +269,22 @@ func (h *Handler) UpdateArticleBody(w http.ResponseWriter, r *http.Request) {
return
}
var localArticle UpdateArticleBody
if err := json.NewDecoder(r.Body).Decode(&localArticle); err != nil {
request, ok := readUpdateArticleBody(r)
if !ok {
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
return
}
if err := validator.Validate.Struct(localArticle); err != nil {
if err := validator.Validate.Struct(request); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
updatedArticle, err := h.UpdateUseCase.UpdateBody(r.Context(), id, localArticle.Body, format)
updatedArticle, err := h.UpdateUseCase.Update(r.Context(), id, useCases.UpdateArticleCommand{
Body: request.Body,
SiteID: request.SiteID,
Format: format,
})
if err != nil {
writeArticleErrorResponse(w, err)
return
@@ -276,11 +321,12 @@ const (
)
type listQuery struct {
Page int `validate:"gte=1"`
Size int `validate:"oneof=5 25 50 100"`
Q string `validate:"max=200"`
Status string `validate:"omitempty,oneof=online draft"`
Sort string `validate:"oneof=title:asc title:desc status:asc status:desc created_at:asc created_at:desc updated_at:asc updated_at:desc"`
Page int `validate:"gte=1"`
Size int `validate:"oneof=5 25 50 100"`
Q string `validate:"max=200"`
Status string `validate:"omitempty,oneof=online draft"`
Site *string `validate:"omitempty,min=1"`
Sort string `validate:"oneof=title:asc title:desc status:asc status:desc created_at:asc created_at:desc updated_at:asc updated_at:desc"`
}
const defaultListSort = "updated_at:desc"
@@ -322,7 +368,15 @@ func readListQuery(values url.Values) (listQuery, error) {
if values.Has("sort") {
sortValue = values.Get("sort")
}
return listQuery{Page: page, Size: size, Q: values.Get("q"), Status: status, Sort: sortValue}, nil
var siteKey *string
if values.Has("site") {
key := values.Get("site")
if key == "" {
return listQuery{}, errors.New("empty site")
}
siteKey = &key
}
return listQuery{Page: page, Size: size, Q: values.Get("q"), Status: status, Sort: sortValue, Site: siteKey}, nil
}
func (h *Handler) ListArticles(w http.ResponseWriter, r *http.Request) {
@@ -345,6 +399,7 @@ func (h *Handler) ListArticles(w http.ResponseWriter, r *http.Request) {
Q: query.Q,
Status: query.Status,
Sort: parseSort(query.Sort),
SiteKey: query.Site,
})
if err != nil {
http.Error(w, "Failed to retrieve articles", http.StatusInternalServerError)