Compare commits
5 Commits
89282dd276
...
d811ab18ac
Author | SHA1 | Date |
---|---|---|
kim | d811ab18ac | |
dependabot[bot] | 16c1832793 | |
kim | 707bee5d41 | |
kim | 16cd45586f | |
kim | 83768a9ed9 |
2
go.mod
2
go.mod
|
@ -29,7 +29,7 @@ require (
|
|||
github.com/buckket/go-blurhash v1.1.0
|
||||
github.com/coreos/go-oidc/v3 v3.10.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/gin-contrib/cors v1.7.1
|
||||
github.com/gin-contrib/cors v1.7.2
|
||||
github.com/gin-contrib/gzip v1.0.1
|
||||
github.com/gin-contrib/sessions v1.0.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -193,8 +193,8 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq
|
|||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||
github.com/gin-contrib/cors v1.7.1 h1:s9SIppU/rk8enVvkzwiC2VK3UZ/0NNGsWfUKvV55rqs=
|
||||
github.com/gin-contrib/cors v1.7.1/go.mod h1:n/Zj7B4xyrgk/cX1WCX2dkzFfaNm/xJb6oIUk7WTtps=
|
||||
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||
github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=
|
||||
github.com/gin-contrib/gzip v1.0.1/go.mod h1:njt428fdUNRvjuJf16tZMYZ2Yl+WQB53X5wmhDwXvC4=
|
||||
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
|
||||
|
|
|
@ -531,6 +531,11 @@ func (c *Caches) initFilterKeyword() {
|
|||
// See internal/db/bundb/filter.go.
|
||||
filterKeyword2.Filter = nil
|
||||
|
||||
// We specifically DO NOT unset
|
||||
// the regexp field here, as any
|
||||
// regexp.Regexp instance is safe
|
||||
// for concurrent access.
|
||||
|
||||
return filterKeyword2
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
@ -34,12 +35,22 @@ func (f *filterDB) GetFilterKeywordByID(ctx context.Context, id string) (*gtsmod
|
|||
"ID",
|
||||
func() (*gtsmodel.FilterKeyword, error) {
|
||||
var filterKeyword gtsmodel.FilterKeyword
|
||||
err := f.db.
|
||||
|
||||
// Scan from DB.
|
||||
if err := f.db.
|
||||
NewSelect().
|
||||
Model(&filterKeyword).
|
||||
Where("? = ?", bun.Ident("id"), id).
|
||||
Scan(ctx)
|
||||
return &filterKeyword, err
|
||||
Scan(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Pre-compile filter keyword regular expression.
|
||||
if err := filterKeyword.Compile(); err != nil {
|
||||
return nil, gtserror.Newf("error compiling filter keyword regex: %w", err)
|
||||
}
|
||||
|
||||
return &filterKeyword, nil
|
||||
},
|
||||
id,
|
||||
)
|
||||
|
@ -57,20 +68,20 @@ func (f *filterDB) GetFilterKeywordByID(ctx context.Context, id string) (*gtsmod
|
|||
return filterKeyword, nil
|
||||
}
|
||||
|
||||
func (f *filterDB) populateFilterKeyword(ctx context.Context, filterKeyword *gtsmodel.FilterKeyword) error {
|
||||
func (f *filterDB) populateFilterKeyword(ctx context.Context, filterKeyword *gtsmodel.FilterKeyword) (err error) {
|
||||
if filterKeyword.Filter == nil {
|
||||
// Filter is not set, fetch from the cache or database.
|
||||
filter, err := f.state.DB.GetFilterByID(
|
||||
// Don't populate the filter with all of its keywords and statuses or we'll just end up back here.
|
||||
filterKeyword.Filter, err = f.state.DB.GetFilterByID(
|
||||
|
||||
// Don't populate the filter with all of its keywords
|
||||
// and statuses or we'll just end up back here.
|
||||
gtscontext.SetBarebones(ctx),
|
||||
filterKeyword.FilterID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filterKeyword.Filter = filter
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -84,6 +95,7 @@ func (f *filterDB) GetFilterKeywordsForAccountID(ctx context.Context, accountID
|
|||
|
||||
func (f *filterDB) getFilterKeywords(ctx context.Context, idColumn string, id string) ([]*gtsmodel.FilterKeyword, error) {
|
||||
var filterKeywordIDs []string
|
||||
|
||||
if err := f.db.
|
||||
NewSelect().
|
||||
Model((*gtsmodel.FilterKeyword)(nil)).
|
||||
|
@ -92,6 +104,7 @@ func (f *filterDB) getFilterKeywords(ctx context.Context, idColumn string, id st
|
|||
Scan(ctx, &filterKeywordIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(filterKeywordIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -101,6 +114,8 @@ func (f *filterDB) getFilterKeywords(ctx context.Context, idColumn string, id st
|
|||
filterKeywordIDs,
|
||||
func(uncachedFilterKeywordIDs []string) ([]*gtsmodel.FilterKeyword, error) {
|
||||
uncachedFilterKeywords := make([]*gtsmodel.FilterKeyword, 0, len(uncachedFilterKeywordIDs))
|
||||
|
||||
// Scan from DB.
|
||||
if err := f.db.
|
||||
NewSelect().
|
||||
Model(&uncachedFilterKeywords).
|
||||
|
@ -108,6 +123,16 @@ func (f *filterDB) getFilterKeywords(ctx context.Context, idColumn string, id st
|
|||
Scan(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compile all the keyword regular expressions.
|
||||
uncachedFilterKeywords = slices.DeleteFunc(uncachedFilterKeywords, func(filterKeyword *gtsmodel.FilterKeyword) bool {
|
||||
if err := filterKeyword.Compile(); err != nil {
|
||||
log.Errorf(ctx, "error compiling filter keyword regex: %v", err)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return uncachedFilterKeywords, nil
|
||||
},
|
||||
)
|
||||
|
@ -125,23 +150,26 @@ func (f *filterDB) getFilterKeywords(ctx context.Context, idColumn string, id st
|
|||
}
|
||||
|
||||
// Populate the filter keywords. Remove any that we can't populate from the return slice.
|
||||
errs := gtserror.NewMultiError(len(filterKeywords))
|
||||
filterKeywords = slices.DeleteFunc(filterKeywords, func(filterKeyword *gtsmodel.FilterKeyword) bool {
|
||||
if err := f.populateFilterKeyword(ctx, filterKeyword); err != nil {
|
||||
errs.Appendf(
|
||||
"error populating filter keyword %s: %w",
|
||||
filterKeyword.ID,
|
||||
err,
|
||||
)
|
||||
log.Errorf(ctx, "error populating filter keyword: %v", err)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return filterKeywords, errs.Combine()
|
||||
return filterKeywords, nil
|
||||
}
|
||||
|
||||
func (f *filterDB) PutFilterKeyword(ctx context.Context, filterKeyword *gtsmodel.FilterKeyword) error {
|
||||
if filterKeyword.Regexp == nil {
|
||||
// Ensure regexp is compiled
|
||||
// before attempted caching.
|
||||
err := filterKeyword.Compile()
|
||||
if err != nil {
|
||||
return gtserror.Newf("error compiling filter keyword regex: %w", err)
|
||||
}
|
||||
}
|
||||
return f.state.Caches.GTS.FilterKeyword.Store(filterKeyword, func() error {
|
||||
_, err := f.db.
|
||||
NewInsert().
|
||||
|
@ -156,7 +184,14 @@ func (f *filterDB) UpdateFilterKeyword(ctx context.Context, filterKeyword *gtsmo
|
|||
if len(columns) > 0 {
|
||||
columns = append(columns, "updated_at")
|
||||
}
|
||||
|
||||
if filterKeyword.Regexp == nil {
|
||||
// Ensure regexp is compiled
|
||||
// before attempted caching.
|
||||
err := filterKeyword.Compile()
|
||||
if err != nil {
|
||||
return gtserror.Newf("error compiling filter keyword regex: %w", err)
|
||||
}
|
||||
}
|
||||
return f.state.Caches.GTS.FilterKeyword.Store(filterKeyword, func() error {
|
||||
_, err := f.db.
|
||||
NewUpdate().
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
|
||||
package gtsmodel
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Filter stores a filter created by a local account.
|
||||
type Filter struct {
|
||||
|
@ -39,14 +42,28 @@ type Filter struct {
|
|||
|
||||
// FilterKeyword stores a single keyword to filter statuses against.
|
||||
type FilterKeyword struct {
|
||||
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
|
||||
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
|
||||
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
|
||||
AccountID string `bun:"type:CHAR(26),notnull,nullzero"` // ID of the local account that created the filter keyword.
|
||||
FilterID string `bun:"type:CHAR(26),notnull,nullzero,unique:filter_keywords_filter_id_keyword_uniq"` // ID of the filter that this keyword belongs to.
|
||||
Filter *Filter `bun:"-"` // Filter corresponding to FilterID
|
||||
Keyword string `bun:",nullzero,notnull,unique:filter_keywords_filter_id_keyword_uniq"` // The keyword or phrase to filter against.
|
||||
WholeWord *bool `bun:",nullzero,notnull,default:false"` // Should the filter consider word boundaries?
|
||||
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
|
||||
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
|
||||
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
|
||||
AccountID string `bun:"type:CHAR(26),notnull,nullzero"` // ID of the local account that created the filter keyword.
|
||||
FilterID string `bun:"type:CHAR(26),notnull,nullzero,unique:filter_keywords_filter_id_keyword_uniq"` // ID of the filter that this keyword belongs to.
|
||||
Filter *Filter `bun:"-"` // Filter corresponding to FilterID
|
||||
Keyword string `bun:",nullzero,notnull,unique:filter_keywords_filter_id_keyword_uniq"` // The keyword or phrase to filter against.
|
||||
WholeWord *bool `bun:",nullzero,notnull,default:false"` // Should the filter consider word boundaries?
|
||||
Regexp *regexp.Regexp `bun:"-"` // pre-prepared regular expression
|
||||
}
|
||||
|
||||
// Compile will compile this FilterKeyword as a prepared regular expression.
|
||||
func (k *FilterKeyword) Compile() (err error) {
|
||||
var wordBreak string
|
||||
if k.WholeWord != nil && *k.WholeWord {
|
||||
wordBreak = `\b`
|
||||
}
|
||||
|
||||
// Compile keyword filter regexp.
|
||||
quoted := regexp.QuoteMeta(k.Keyword)
|
||||
k.Regexp, err = regexp.Compile(`(?i)` + wordBreak + quoted + wordBreak)
|
||||
return // caller is expected to wrap this error
|
||||
}
|
||||
|
||||
// FilterStatus stores a single status to filter.
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -746,18 +745,9 @@ func (c *Converter) statusToAPIFilterResults(
|
|||
keywordMatches := make([]string, 0, len(filter.Keywords))
|
||||
fields := filterableTextFields(s)
|
||||
for _, filterKeyword := range filter.Keywords {
|
||||
wholeWord := util.PtrValueOr(filterKeyword.WholeWord, false)
|
||||
wordBreak := ``
|
||||
if wholeWord {
|
||||
wordBreak = `\b`
|
||||
}
|
||||
re, err := regexp.Compile(`(?i)` + wordBreak + regexp.QuoteMeta(filterKeyword.Keyword) + wordBreak)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var isMatch bool
|
||||
for _, field := range fields {
|
||||
if re.MatchString(field) {
|
||||
if filterKeyword.Regexp.MatchString(field) {
|
||||
isMatch = true
|
||||
break
|
||||
}
|
||||
|
|
|
@ -546,6 +546,7 @@ func (suite *InternalToFrontendTestSuite) TestWarnFilteredStatusToFrontend() {
|
|||
requestingAccount := suite.testAccounts["local_account_1"]
|
||||
expectedMatchingFilter := suite.testFilters["local_account_1_filter_1"]
|
||||
expectedMatchingFilterKeyword := suite.testFilterKeywords["local_account_1_filter_1_keyword_1"]
|
||||
suite.NoError(expectedMatchingFilterKeyword.Compile())
|
||||
expectedMatchingFilterKeyword.Filter = expectedMatchingFilter
|
||||
expectedMatchingFilter.Keywords = []*gtsmodel.FilterKeyword{expectedMatchingFilterKeyword}
|
||||
requestingAccountFilters := []*gtsmodel.Filter{expectedMatchingFilter}
|
||||
|
@ -700,6 +701,7 @@ func (suite *InternalToFrontendTestSuite) TestHideFilteredStatusToFrontend() {
|
|||
expectedMatchingFilter := suite.testFilters["local_account_1_filter_1"]
|
||||
expectedMatchingFilter.Action = gtsmodel.FilterActionHide
|
||||
expectedMatchingFilterKeyword := suite.testFilterKeywords["local_account_1_filter_1_keyword_1"]
|
||||
suite.NoError(expectedMatchingFilterKeyword.Compile())
|
||||
expectedMatchingFilterKeyword.Filter = expectedMatchingFilter
|
||||
expectedMatchingFilter.Keywords = []*gtsmodel.FilterKeyword{expectedMatchingFilterKeyword}
|
||||
requestingAccountFilters := []*gtsmodel.Filter{expectedMatchingFilter}
|
||||
|
|
|
@ -7,32 +7,7 @@ builds:
|
|||
skip: true
|
||||
|
||||
changelog:
|
||||
# Set it to true if you wish to skip the changelog generation.
|
||||
# This may result in an empty release notes on GitHub/GitLab/Gitea.
|
||||
skip: false
|
||||
|
||||
# Changelog generation implementation to use.
|
||||
#
|
||||
# Valid options are:
|
||||
# - `git`: uses `git log`;
|
||||
# - `github`: uses the compare GitHub API, appending the author login to the changelog.
|
||||
# - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog.
|
||||
# - `github-native`: uses the GitHub release notes generation API, disables the groups feature.
|
||||
#
|
||||
# Defaults to `git`.
|
||||
use: git
|
||||
|
||||
# Sorts the changelog by the commit's messages.
|
||||
# Could either be asc, desc or empty
|
||||
# Default is empty
|
||||
sort: asc
|
||||
|
||||
# Group commits messages by given regex and title.
|
||||
# Order value defines the order of the groups.
|
||||
# Proving no regex means all commits will be grouped under the default group.
|
||||
# Groups are disabled when using github-native, as it already groups things by itself.
|
||||
#
|
||||
# Default is no groups.
|
||||
use: github
|
||||
groups:
|
||||
- title: Features
|
||||
regexp: "^.*feat[(\\w)]*:+.*$"
|
||||
|
@ -43,14 +18,13 @@ changelog:
|
|||
- title: "Enhancements"
|
||||
regexp: "^.*chore[(\\w)]*:+.*$"
|
||||
order: 2
|
||||
- title: "Refactor"
|
||||
regexp: "^.*refactor[(\\w)]*:+.*$"
|
||||
order: 3
|
||||
- title: "Build process updates"
|
||||
regexp: ^.*?(build|ci)(\(.+\))??!?:.+$
|
||||
order: 4
|
||||
- title: "Documentation updates"
|
||||
regexp: ^.*?docs?(\(.+\))??!?:.+$
|
||||
order: 4
|
||||
- title: Others
|
||||
order: 999
|
||||
|
||||
filters:
|
||||
# Commit messages matching the regexp listed here will be removed from
|
||||
# the changelog
|
||||
# Default is empty
|
||||
exclude:
|
||||
- "^docs"
|
||||
- "CICD"
|
||||
- typo
|
||||
|
|
|
@ -220,7 +220,7 @@ github.com/gabriel-vasile/mimetype
|
|||
github.com/gabriel-vasile/mimetype/internal/charset
|
||||
github.com/gabriel-vasile/mimetype/internal/json
|
||||
github.com/gabriel-vasile/mimetype/internal/magic
|
||||
# github.com/gin-contrib/cors v1.7.1
|
||||
# github.com/gin-contrib/cors v1.7.2
|
||||
## explicit; go 1.18
|
||||
github.com/gin-contrib/cors
|
||||
# github.com/gin-contrib/gzip v1.0.1
|
||||
|
|
Loading…
Reference in New Issue