feat: SEO backend data (1/4)
This commit is contained in:
1 parent
cb1410f0b0
commit
0eb46b0d74
90 files changed
+6094
-350
No files matched your search
@@ -1,10 +1,9 @@
|
||||
package author
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/slug"
|
||||
"errors"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
@@ -41,31 +40,6 @@ const (
|
||||
SocialCustom = "custom"
|
||||
)
|
||||
|
||||
var slugPattern = regexp.MustCompile(`^[a-z0-9]+(-[a-z0-9]+)*$`)
|
||||
|
||||
var nonSlugRun = regexp.MustCompile(`[^a-z0-9]+`)
|
||||
|
||||
var diacriticFolder = strings.NewReplacer(
|
||||
"à", "a", "á", "a", "â", "a", "ã", "a", "ä", "a", "å", "a", "ā", "a", "ă", "a", "ą", "a",
|
||||
"æ", "ae",
|
||||
"ç", "c", "ć", "c", "č", "c", "ĉ", "c",
|
||||
"ď", "d", "đ", "d",
|
||||
"è", "e", "é", "e", "ê", "e", "ë", "e", "ē", "e", "ė", "e", "ę", "e", "ě", "e",
|
||||
"ğ", "g",
|
||||
"ì", "i", "í", "i", "î", "i", "ï", "i", "ī", "i", "į", "i", "ı", "i",
|
||||
"ł", "l", "ľ", "l",
|
||||
"ñ", "n", "ń", "n", "ň", "n",
|
||||
"ò", "o", "ó", "o", "ô", "o", "õ", "o", "ö", "o", "ø", "o", "ō", "o", "ő", "o",
|
||||
"œ", "oe",
|
||||
"ř", "r",
|
||||
"ś", "s", "š", "s", "ş", "s", "ș", "s",
|
||||
"ß", "ss",
|
||||
"ť", "t", "ț", "t", "ţ", "t",
|
||||
"ù", "u", "ú", "u", "û", "u", "ü", "u", "ū", "u", "ů", "u", "ű", "u", "ų", "u",
|
||||
"ý", "y", "ÿ", "y",
|
||||
"ź", "z", "ż", "z", "ž", "z",
|
||||
)
|
||||
|
||||
type SocialLink struct {
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
@@ -103,6 +77,14 @@ func (a Author) Byline() Byline {
|
||||
return Byline{ID: a.ID, Name: a.Name, Slug: a.Slug, Bio: a.Bio, AvatarID: a.AvatarID, Website: a.Website, SocialLinks: links}
|
||||
}
|
||||
|
||||
func BylineIDs(bylines []Byline) []uint32 {
|
||||
ids := make([]uint32, 0, len(bylines))
|
||||
for _, byline := range bylines {
|
||||
ids = append(ids, byline.ID)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
type Nullable[T any] struct {
|
||||
Set bool
|
||||
Value *T
|
||||
@@ -129,9 +111,8 @@ type Page struct {
|
||||
Total int
|
||||
}
|
||||
|
||||
func IsValidSlug(slug string) bool {
|
||||
length := utf8.RuneCountInString(slug)
|
||||
return length >= 1 && length <= MaxSlugRunes && slugPattern.MatchString(slug)
|
||||
func IsValidSlug(candidate string) bool {
|
||||
return slug.IsValid(candidate)
|
||||
}
|
||||
|
||||
func IsValidName(name string) bool {
|
||||
@@ -191,26 +172,9 @@ func ValidateLinks(links []SocialLink) error {
|
||||
}
|
||||
|
||||
func GenerateSlug(name string) string {
|
||||
folded := diacriticFolder.Replace(strings.ToLower(name))
|
||||
slug := strings.Trim(nonSlugRun.ReplaceAllString(folded, "-"), "-")
|
||||
slug = truncateSlug(slug, MaxSlugRunes)
|
||||
if slug == "" {
|
||||
return FallbackSlug
|
||||
}
|
||||
return slug
|
||||
}
|
||||
|
||||
func truncateSlug(slug string, limit int) string {
|
||||
if len(slug) <= limit {
|
||||
return slug
|
||||
}
|
||||
return strings.TrimRight(slug[:limit], "-")
|
||||
return slug.Generate(name, FallbackSlug)
|
||||
}
|
||||
|
||||
func SlugWithSuffix(base string, attempt int) string {
|
||||
if attempt <= 1 {
|
||||
return base
|
||||
}
|
||||
suffix := "-" + strconv.Itoa(attempt)
|
||||
return truncateSlug(base, MaxSlugRunes-len(suffix)) + suffix
|
||||
return slug.WithSuffix(base, attempt)
|
||||
}
|
||||
Reference in new issue
Block a user