refac: article reads, story delete, no leaks, stored SEO status, rate limit, cleanup (%site%, article repo split, old lang repos, one default per site rule to domain)
This commit is contained in:
1 parent
9d284e65a9
commit
7c15206170
85 files changed
+1788
-593
No files matched your search
@@ -61,8 +61,11 @@ func (a *ArticleRepository) scoped(filters domain.Filters) *gorm.DB {
|
||||
case domain.StatusDraft:
|
||||
query = query.Where("is_online = ?", false)
|
||||
}
|
||||
if filters.Lang != nil {
|
||||
query = query.Where("lang = ?", *filters.Lang)
|
||||
if filters.Lang != "" {
|
||||
query = query.Where("lang = ?", filters.Lang)
|
||||
}
|
||||
if filters.SEOStatus != "" {
|
||||
query = query.Where("seo_status = ?", filters.SEOStatus)
|
||||
}
|
||||
if filters.Langs != nil {
|
||||
query = restrictToLangs(query, *filters.Langs)
|
||||
@@ -119,7 +122,8 @@ func (a *ArticleRepository) FindPage(filters domain.Filters) (domain.PageResult,
|
||||
var entityArticles []entity.Article
|
||||
ordered := a.scoped(filters).Order(orderClause(filters.Sort))
|
||||
if !filters.Unpaged {
|
||||
ordered = ordered.Limit(filters.Size).Offset((filters.Page - 1) * filters.Size)
|
||||
offset, limit := filters.Window()
|
||||
ordered = ordered.Limit(limit).Offset(offset)
|
||||
}
|
||||
err := ordered.Find(&entityArticles).Error
|
||||
if err != nil {
|
||||
@@ -146,9 +150,9 @@ func (a *ArticleRepository) findMatchingTitle(filters domain.Filters) (domain.Pa
|
||||
}
|
||||
pageIds := matchingIds
|
||||
if !filters.Unpaged {
|
||||
start := min((filters.Page-1)*filters.Size, len(matchingIds))
|
||||
end := min(start+filters.Size, len(matchingIds))
|
||||
pageIds = matchingIds[start:end]
|
||||
offset, limit := filters.Window()
|
||||
start := min(offset, len(matchingIds))
|
||||
pageIds = matchingIds[start : start+min(limit, len(matchingIds)-start)]
|
||||
}
|
||||
page := domain.PageResult{Items: []domain.Article{}, Total: len(matchingIds)}
|
||||
if len(pageIds) == 0 {
|
||||
@@ -220,6 +224,13 @@ func (a *ArticleRepository) CountVersions(filters domain.Filters) (domain.Counts
|
||||
return domain.Counts{All: int(online + draft), Online: int(online), Draft: int(draft)}, nil
|
||||
}
|
||||
|
||||
func (a *ArticleRepository) CountSuggestions(filters domain.Filters) (int, error) {
|
||||
visible := domain.Filters{IsOnline: filters.IsOnline, VisibleTo: filters.VisibleTo, SEOStatus: domain.SEOStatusSuggestions}
|
||||
var total int64
|
||||
err := a.scoped(visible).Count(&total).Error
|
||||
return int(total), err
|
||||
}
|
||||
|
||||
func toDomainArticles(entityArticles []entity.Article) []domain.Article {
|
||||
domainArticles := make([]domain.Article, 0, len(entityArticles))
|
||||
for _, entityArticle := range entityArticles {
|
||||
@@ -463,7 +474,7 @@ func (a *ArticleRepository) GetAll() ([]domain.Article, error) {
|
||||
}
|
||||
|
||||
func (a *ArticleRepository) UpdateBody(id uint32, body string) (domain.Article, error) {
|
||||
return a.updateColumns(id, map[string]any{"body": body})
|
||||
return a.updateColumns(id, withStaleSEO(map[string]any{"body": body}))
|
||||
}
|
||||
|
||||
func (a *ArticleRepository) updateColumns(id uint32, columns map[string]any) (domain.Article, error) {
|
||||
@@ -603,6 +614,8 @@ func writeArticleColumns(tx *gorm.DB, written entity.Article, expectedRevision u
|
||||
"edit_revision": written.EditRevision,
|
||||
"content_revision": written.ContentRevision,
|
||||
"updated_at": written.UpdatedAt,
|
||||
seoStatusColumn: "",
|
||||
seoVersionColumn: gorm.Expr(seoVersionColumn + " + 1"),
|
||||
})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
@@ -668,20 +681,42 @@ func (a *ArticleRepository) Delete(id uint32) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
retired := map[string]any{"slug": domain.Tombstone(id), "retired_id": id}
|
||||
if err := tx.Model(&entity.Article{}).Where("id = ?", id).UpdateColumns(retired).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := deleteOrNotFound(tx.Delete(&entity.Article{}, id), domain.ErrArticleNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Where("article_id = ?", id).Delete(&entity.ArticleAuthor{}).Error; err != nil {
|
||||
if err := retireVersion(tx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return closeEmptyStory(tx, found.StoryID)
|
||||
})
|
||||
}
|
||||
|
||||
func (a *ArticleRepository) DeleteStory(storyID uint32) error {
|
||||
return a.db.Transaction(func(tx *gorm.DB) error {
|
||||
var ids []uint32
|
||||
if err := tx.Model(&entity.Article{}).Where("story_id = ?", storyID).Order("id ASC").Pluck("id", &ids).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return domain.ErrArticleNotFound
|
||||
}
|
||||
for _, id := range ids {
|
||||
if err := retireVersion(tx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return deleteOrNotFound(tx.Delete(&entity.Story{}, storyID), domain.ErrArticleNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func retireVersion(tx *gorm.DB, id uint32) error {
|
||||
retired := map[string]any{"slug": domain.Tombstone(id), "retired_id": id}
|
||||
if err := tx.Model(&entity.Article{}).Where("id = ?", id).UpdateColumns(retired).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := deleteOrNotFound(tx.Delete(&entity.Article{}, id), domain.ErrArticleNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Where("article_id = ?", id).Delete(&entity.ArticleAuthor{}).Error
|
||||
}
|
||||
|
||||
func closeEmptyStory(tx *gorm.DB, storyID uint32) error {
|
||||
if storyID == 0 {
|
||||
return nil
|
||||
@@ -709,7 +744,7 @@ func (a *ArticleRepository) MoveToSite(ids []uint32, siteID uint32) error {
|
||||
if err := tx.First(&moving, id).Error; err != nil {
|
||||
return mapNotFoundErr(err, domain.ErrArticleNotFound)
|
||||
}
|
||||
moved := map[string]any{"site_id": siteID, "edit_revision": gorm.Expr("edit_revision + 1")}
|
||||
moved := withStaleSEO(map[string]any{"site_id": siteID, "edit_revision": gorm.Expr("edit_revision + 1")})
|
||||
if err := tx.Model(&entity.Article{}).Where("id = ?", id).Updates(moved).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -763,7 +798,7 @@ func clearColumn(tx *gorm.DB, column string, imageID uint32, cleared *[]uint32)
|
||||
if len(*cleared) == 0 {
|
||||
return nil
|
||||
}
|
||||
return tx.Model(&entity.Article{}).Where("id IN ?", *cleared).UpdateColumn(column, nil).Error
|
||||
return tx.Model(&entity.Article{}).Where("id IN ?", *cleared).UpdateColumns(withStaleSEO(map[string]any{column: nil})).Error
|
||||
}
|
||||
|
||||
func (a *ArticleRepository) RestoreImage(references domain.ImageReferences, imageID uint32) error {
|
||||
@@ -779,7 +814,7 @@ func assignColumn(tx *gorm.DB, column string, articleIDs []uint32, imageID uint3
|
||||
if len(articleIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return tx.Model(&entity.Article{}).Where("id IN ?", articleIDs).UpdateColumn(column, imageID).Error
|
||||
return tx.Model(&entity.Article{}).Where("id IN ?", articleIDs).UpdateColumns(withStaleSEO(map[string]any{column: imageID})).Error
|
||||
}
|
||||
|
||||
func (a *ArticleRepository) RepointImage(fromImageIDs []uint32, toImageID uint32) ([]uint32, error) {
|
||||
@@ -791,10 +826,10 @@ func (a *ArticleRepository) RepointImage(fromImageIDs []uint32, toImageID uint32
|
||||
if err := tx.Model(&entity.Article{}).Where("cover_image_id IN ? OR social_image_id IN ?", fromImageIDs, fromImageIDs).Order("id ASC").Pluck("id", &changed).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&entity.Article{}).Where("cover_image_id IN ?", fromImageIDs).UpdateColumn("cover_image_id", toImageID).Error; err != nil {
|
||||
if err := tx.Model(&entity.Article{}).Where("cover_image_id IN ?", fromImageIDs).UpdateColumns(withStaleSEO(map[string]any{"cover_image_id": toImageID})).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Model(&entity.Article{}).Where("social_image_id IN ?", fromImageIDs).UpdateColumn("social_image_id", toImageID).Error
|
||||
return tx.Model(&entity.Article{}).Where("social_image_id IN ?", fromImageIDs).UpdateColumns(withStaleSEO(map[string]any{"social_image_id": toImageID})).Error
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -805,7 +840,7 @@ func (a *ArticleRepository) RepointImage(fromImageIDs []uint32, toImageID uint32
|
||||
var _ domain.Repository = &ArticleRepository{}
|
||||
|
||||
func (a *ArticleRepository) AssignUnassignedLanguage(siteID uint32, lang string) (int, error) {
|
||||
result := a.db.Unscoped().Model(&entity.Article{}).Where("site_id = ? AND lang = ?", siteID, language.Unassigned).UpdateColumn("lang", lang)
|
||||
result := a.db.Unscoped().Model(&entity.Article{}).Where("site_id = ? AND lang = ?", siteID, language.Unassigned).UpdateColumns(withStaleSEO(map[string]any{"lang": lang}))
|
||||
return int(result.RowsAffected), result.Error
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user