diff --git a/go.mod b/go.mod index 354049f..5ef3b4a 100644 --- a/go.mod +++ b/go.mod @@ -64,11 +64,13 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pemistahl/lingua-go v1.4.0 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect @@ -79,7 +81,7 @@ require ( github.com/valyala/fasthttp v1.52.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect diff --git a/go.sum b/go.sum index 3fd7404..858926a 100644 --- a/go.sum +++ b/go.sum @@ -218,6 +218,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pemistahl/lingua-go v1.4.0 h1:ifYhthrlW7iO4icdubwlduYnmwU37V1sbNrwhKBR4rM= +github.com/pemistahl/lingua-go v1.4.0/go.mod h1:ECuM1Hp/3hvyh7k8aWSqNCPlTxLemFZsRjocUf3KgME= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -259,6 +261,8 @@ github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -308,6 +312,8 @@ golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pkg/internal/models/articles.go b/pkg/internal/models/articles.go index 67496ee..779b4ab 100644 --- a/pkg/internal/models/articles.go +++ b/pkg/internal/models/articles.go @@ -13,6 +13,7 @@ type Article struct { Title string `json:"title"` Description string `json:"description"` Content string `json:"content"` + Language string `json:"language"` Tags []Tag `json:"tags" gorm:"many2many:article_tags"` Categories []Category `json:"categories" gorm:"many2many:article_categories"` Reactions []Reaction `json:"reactions"` @@ -26,7 +27,5 @@ type Article struct { AuthorID uint `json:"author_id"` Author Account `json:"author"` - // Dynamic Calculated Values - ReactionCount int64 `json:"reaction_count"` - ReactionList map[string]int64 `json:"reaction_list" gorm:"-"` + Metric PostMetric `json:"metric" gorm:"-"` } diff --git a/pkg/internal/models/metrics.go b/pkg/internal/models/metrics.go new file mode 100644 index 0000000..c033268 --- /dev/null +++ b/pkg/internal/models/metrics.go @@ -0,0 +1,7 @@ +package models + +type PostMetric struct { + ReplyCount int64 `json:"reply_count,omitempty"` + ReactionCount int64 `json:"reaction_count,omitempty"` + ReactionList map[string]int64 `json:"reaction_list,omitempty"` +} diff --git a/pkg/internal/models/posts.go b/pkg/internal/models/posts.go index 358fbf4..191f5a5 100644 --- a/pkg/internal/models/posts.go +++ b/pkg/internal/models/posts.go @@ -10,7 +10,8 @@ type Post struct { BaseModel Alias string `json:"alias" gorm:"uniqueIndex"` - Content string `json:"content"` + Content *string `json:"content"` + Language string `json:"language"` Tags []Tag `json:"tags" gorm:"many2many:post_tags"` Categories []Category `json:"categories" gorm:"many2many:post_categories"` Reactions []Reaction `json:"reactions"` @@ -29,8 +30,5 @@ type Post struct { AuthorID uint `json:"author_id"` Author Account `json:"author"` - // Dynamic Calculated Values - ReplyCount int64 `json:"reply_count"` - ReactionCount int64 `json:"reaction_count"` - ReactionList map[string]int64 `json:"reaction_list" gorm:"-"` + Metric PostMetric `json:"metric" gorm:"-"` } diff --git a/pkg/internal/server/api/articles_api.go b/pkg/internal/server/api/articles_api.go index 3d80045..8b5ef1f 100644 --- a/pkg/internal/server/api/articles_api.go +++ b/pkg/internal/server/api/articles_api.go @@ -23,8 +23,8 @@ func getArticle(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusNotFound, err.Error()) } - item.ReactionCount = services.CountArticleReactions(item.ID) - item.ReactionList, err = services.ListResourceReactions(database.C.Where("article_id = ?", item.ID)) + item.Metric.ReactionCount = services.CountArticleReactions(item.ID) + item.Metric.ReactionList, err = services.ListResourceReactions(database.C.Where("article_id = ?", item.ID)) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } diff --git a/pkg/internal/server/api/posts_api.go b/pkg/internal/server/api/posts_api.go index 860dfc2..8662535 100644 --- a/pkg/internal/server/api/posts_api.go +++ b/pkg/internal/server/api/posts_api.go @@ -23,9 +23,11 @@ func getPost(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusNotFound, err.Error()) } - item.ReplyCount = services.CountPostReply(item.ID) - item.ReactionCount = services.CountPostReactions(item.ID) - item.ReactionList, err = services.ListResourceReactions(database.C.Where("post_id = ?", item.ID)) + item.Metric = models.PostMetric{ + ReplyCount: services.CountPostReply(item.ID), + ReactionCount: services.CountPostReactions(item.ID), + } + item.Metric.ReactionList, err = services.ListResourceReactions(database.C.Where("post_id = ?", item.ID)) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } @@ -139,7 +141,7 @@ func createPost(c *fiber.Ctx) error { item := models.Post{ Alias: data.Alias, - Content: data.Content, + Content: &data.Content, Tags: data.Tags, Categories: data.Categories, Attachments: data.Attachments, @@ -218,8 +220,8 @@ func editPost(c *fiber.Ctx) error { } } + item.Content = &data.Content item.Alias = data.Alias - item.Content = data.Content item.IsDraft = data.IsDraft item.PublishedAt = data.PublishedAt item.Tags = data.Tags diff --git a/pkg/internal/services/articles.go b/pkg/internal/services/articles.go index 41354cd..a3d6b26 100644 --- a/pkg/internal/services/articles.go +++ b/pkg/internal/services/articles.go @@ -138,7 +138,7 @@ func ListArticle(tx *gorm.DB, take int, offset int, noReact ...bool) ([]*models. for k, v := range mapping { if post, ok := itemMap[k]; ok { - post.ReactionList = v + post.Metric.ReactionList = v } } } @@ -165,6 +165,8 @@ func EnsureArticleCategoriesAndTags(item models.Article) (models.Article, error) } func NewArticle(user models.Account, item models.Article) (models.Article, error) { + item.Language = DetectLanguage(&item.Content) + item, err := EnsureArticleCategoriesAndTags(item) if err != nil { return item, err @@ -185,6 +187,7 @@ func NewArticle(user models.Account, item models.Article) (models.Article, error } func EditArticle(item models.Article) (models.Article, error) { + item.Language = DetectLanguage(&item.Content) item, err := EnsureArticleCategoriesAndTags(item) if err != nil { return item, err diff --git a/pkg/internal/services/languages.go b/pkg/internal/services/languages.go new file mode 100644 index 0000000..f244290 --- /dev/null +++ b/pkg/internal/services/languages.go @@ -0,0 +1,18 @@ +package services + +import ( + "github.com/pemistahl/lingua-go" + "strings" +) + +func DetectLanguage(content *string) string { + if content != nil { + detector := lingua.NewLanguageDetectorBuilder(). + FromLanguages(lingua.AllLanguages()...). + Build() + if lang, ok := detector.DetectLanguageOf(*content); ok { + return strings.ToLower(lang.String()) + } + } + return "unknown" +} diff --git a/pkg/internal/services/posts.go b/pkg/internal/services/posts.go index 0e511f7..0623aab 100644 --- a/pkg/internal/services/posts.go +++ b/pkg/internal/services/posts.go @@ -167,6 +167,7 @@ func ListPost(tx *gorm.DB, take int, offset int, noReact ...bool) ([]*models.Pos } idx := lo.Map(items, func(item *models.Post, index int) uint { + item.Metric = models.PostMetric{} return item.ID }) @@ -181,7 +182,7 @@ func ListPost(tx *gorm.DB, take int, offset int, noReact ...bool) ([]*models.Pos for k, v := range mapping { if post, ok := itemMap[k]; ok { - post.ReactionList = v + post.Metric.ReactionList = v } } } @@ -213,7 +214,7 @@ func ListPost(tx *gorm.DB, take int, offset int, noReact ...bool) ([]*models.Pos for k, v := range list { if post, ok := itemMap[k]; ok { - post.ReplyCount = v + post.Metric.ReplyCount = v } } } @@ -239,6 +240,8 @@ func EnsurePostCategoriesAndTags(item models.Post) (models.Post, error) { } func NewPost(user models.Account, item models.Post) (models.Post, error) { + item.Language = DetectLanguage(item.Content) + item, err := EnsurePostCategoriesAndTags(item) if err != nil { return item, err @@ -281,6 +284,7 @@ func NewPost(user models.Account, item models.Post) (models.Post, error) { } func EditPost(item models.Post) (models.Post, error) { + item.Language = DetectLanguage(item.Content) item, err := EnsurePostCategoriesAndTags(item) if err != nil { return item, err