diff --git a/Dockerfile b/Dockerfile index 5d432e4..23c26d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.17-alpine3.15 as buildbase +FROM golang:1.18-alpine3.15 as buildbase WORKDIR /app RUN apk add --no-cache git gcc musl-dev diff --git a/activityPub.go b/activityPub.go index 02be17e..64eb5de 100644 --- a/activityPub.go +++ b/activityPub.go @@ -89,7 +89,7 @@ func (a *goBlog) apHandleWebfinger(w http.ResponseWriter, r *http.Request) { return } apIri := a.apIri(blog) - b, _ := json.Marshal(map[string]interface{}{ + b, _ := json.Marshal(map[string]any{ "subject": a.webfingerAccts[apIri], "aliases": []string{ a.webfingerAccts[apIri], @@ -142,7 +142,7 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) { return } // Parse activity - activity := map[string]interface{}{} + activity := map[string]any{} err = json.NewDecoder(r.Body).Decode(&activity) _ = r.Body.Close() if err != nil { @@ -164,7 +164,7 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) { case "Follow": a.apAccept(blogName, blog, activity) case "Undo": - if object, ok := activity["object"].(map[string]interface{}); ok { + if object, ok := activity["object"].(map[string]any); ok { ot := cast.ToString(object["type"]) actor := cast.ToString(object["actor"]) if ot == "Follow" && actor == activityActor { @@ -172,7 +172,7 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) { } } case "Create": - if object, ok := activity["object"].(map[string]interface{}); ok { + if object, ok := activity["object"].(map[string]any); ok { baseUrl := cast.ToString(object["id"]) if ou := cast.ToString(object["url"]); ou != "" { baseUrl = ou @@ -297,7 +297,7 @@ func (db *database) apRemoveInbox(inbox string) error { func (a *goBlog) apPost(p *post) { n := a.toASNote(p) - a.apSendToAllFollowers(p.Blog, map[string]interface{}{ + a.apSendToAllFollowers(p.Blog, map[string]any{ "@context": []string{asContext}, "actor": a.apIri(a.cfg.Blogs[p.Blog]), "id": a.fullPostURL(p), @@ -308,7 +308,7 @@ func (a *goBlog) apPost(p *post) { } func (a *goBlog) apUpdate(p *post) { - a.apSendToAllFollowers(p.Blog, map[string]interface{}{ + a.apSendToAllFollowers(p.Blog, map[string]any{ "@context": []string{asContext}, "actor": a.apIri(a.cfg.Blogs[p.Blog]), "id": a.fullPostURL(p), @@ -319,7 +319,7 @@ func (a *goBlog) apUpdate(p *post) { } func (a *goBlog) apDelete(p *post) { - a.apSendToAllFollowers(p.Blog, map[string]interface{}{ + a.apSendToAllFollowers(p.Blog, map[string]any{ "@context": []string{asContext}, "actor": a.apIri(a.cfg.Blogs[p.Blog]), "type": "Delete", @@ -328,11 +328,11 @@ func (a *goBlog) apDelete(p *post) { } func (a *goBlog) apUndelete(p *post) { - a.apSendToAllFollowers(p.Blog, map[string]interface{}{ + a.apSendToAllFollowers(p.Blog, map[string]any{ "@context": []string{asContext}, "actor": a.apIri(a.cfg.Blogs[p.Blog]), "type": "Undo", - "object": map[string]interface{}{ + "object": map[string]any{ "@context": []string{asContext}, "actor": a.apIri(a.cfg.Blogs[p.Blog]), "type": "Delete", @@ -341,7 +341,7 @@ func (a *goBlog) apUndelete(p *post) { }) } -func (a *goBlog) apAccept(blogName string, blog *configBlog, follow map[string]interface{}) { +func (a *goBlog) apAccept(blogName string, blog *configBlog, follow map[string]any) { // it's a follow, write it down newFollower := follow["actor"].(string) log.Println("New follow request:", newFollower) @@ -365,7 +365,7 @@ func (a *goBlog) apAccept(blogName string, blog *configBlog, follow map[string]i return } // Send accept response to the new follower - accept := map[string]interface{}{ + accept := map[string]any{ "@context": []string{asContext}, "type": "Accept", "to": follow["actor"], @@ -376,7 +376,7 @@ func (a *goBlog) apAccept(blogName string, blog *configBlog, follow map[string]i _ = a.db.apQueueSendSigned(a.apIri(blog), inbox, accept) } -func (a *goBlog) apSendToAllFollowers(blog string, activity interface{}) { +func (a *goBlog) apSendToAllFollowers(blog string, activity any) { inboxes, err := a.db.apGetAllInboxes(blog) if err != nil { log.Println("Failed to retrieve inboxes:", err.Error()) @@ -385,7 +385,7 @@ func (a *goBlog) apSendToAllFollowers(blog string, activity interface{}) { a.db.apSendTo(a.apIri(a.cfg.Blogs[blog]), activity, inboxes) } -func (db *database) apSendTo(blogIri string, activity interface{}, inboxes []string) { +func (db *database) apSendTo(blogIri string, activity any, inboxes []string) { for _, i := range inboxes { go func(inbox string) { _ = db.apQueueSendSigned(blogIri, inbox, activity) diff --git a/activityPubSending.go b/activityPubSending.go index c1909cc..027e52e 100644 --- a/activityPubSending.go +++ b/activityPubSending.go @@ -47,7 +47,7 @@ func (a *goBlog) initAPSendQueue() { }) } -func (db *database) apQueueSendSigned(blogIri, to string, activity interface{}) error { +func (db *database) apQueueSendSigned(blogIri, to string, activity any) error { body, err := json.Marshal(activity) if err != nil { return err diff --git a/activityStreams.go b/activityStreams.go index 747f227..f6e5472 100644 --- a/activityStreams.go +++ b/activityStreams.go @@ -38,7 +38,7 @@ func (a *goBlog) checkActivityStreamsRequest(next http.Handler) http.Handler { } type asNote struct { - Context interface{} `json:"@context,omitempty"` + Context any `json:"@context,omitempty"` To []string `json:"to,omitempty"` InReplyTo string `json:"inReplyTo,omitempty"` Name string `json:"name,omitempty"` @@ -55,7 +55,7 @@ type asNote struct { } type asPerson struct { - Context interface{} `json:"@context,omitempty"` + Context any `json:"@context,omitempty"` ID string `json:"id,omitempty"` URL string `json:"url,omitempty"` Type string `json:"type,omitempty"` diff --git a/blogroll.go b/blogroll.go index 83d908b..492d510 100644 --- a/blogroll.go +++ b/blogroll.go @@ -11,7 +11,7 @@ import ( "github.com/carlmjohnson/requests" "github.com/kaorimatz/go-opml" - "github.com/thoas/go-funk" + "github.com/samber/lo" "go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/contenttype" ) @@ -20,7 +20,7 @@ const defaultBlogrollPath = "/blogroll" func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) { blog, bc := a.getBlog(r) - outlines, err, _ := a.blogrollCacheGroup.Do(blog, func() (interface{}, error) { + outlines, err, _ := a.blogrollCacheGroup.Do(blog, func() (any, error) { return a.getBlogrollOutlines(blog) }) if err != nil { @@ -43,7 +43,7 @@ func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) { blog, _ := a.getBlog(r) - outlines, err, _ := a.blogrollCacheGroup.Do(blog, func() (interface{}, error) { + outlines, err, _ := a.blogrollCacheGroup.Do(blog, func() (any, error) { return a.getBlogrollOutlines(blog) }) if err != nil { @@ -91,9 +91,9 @@ func (a *goBlog) getBlogrollOutlines(blog string) ([]*opml.Outline, error) { if len(config.Categories) > 0 { filtered := []*opml.Outline{} for _, category := range config.Categories { - if outline, ok := funk.Find(outlines, func(outline *opml.Outline) bool { + if outline, ok := lo.Find(outlines, func(outline *opml.Outline) bool { return outline.Title == category || outline.Text == category - }).(*opml.Outline); ok && outline != nil { + }); ok && outline != nil { outline.Outlines = sortOutlines(outline.Outlines) filtered = append(filtered, outline) } diff --git a/blogstats.go b/blogstats.go index 6d9329f..3fef416 100644 --- a/blogstats.go +++ b/blogstats.go @@ -37,7 +37,7 @@ func (a *goBlog) serveBlogStats(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveBlogStatsTable(w http.ResponseWriter, r *http.Request) { blog, _ := a.getBlog(r) - data, err, _ := a.blogStatsCacheGroup.Do(blog, func() (interface{}, error) { + data, err, _ := a.blogStatsCacheGroup.Do(blog, func() (any, error) { return a.db.getBlogStats(blog) }) if err != nil { diff --git a/cache.go b/cache.go index cd60535..98d1259 100644 --- a/cache.go +++ b/cache.go @@ -76,7 +76,7 @@ func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler { // Search and serve cache key := cacheKey(r) // Get cache or render it - cacheInterface, _, _ := a.cache.g.Do(key, func() (interface{}, error) { + cacheInterface, _, _ := a.cache.g.Do(key, func() (any, error) { return a.cache.getCache(key, next, r), nil }) ci := cacheInterface.(*cacheItem) diff --git a/check.go b/check.go index aa17b77..16e13c8 100644 --- a/check.go +++ b/check.go @@ -12,6 +12,7 @@ import ( "time" "github.com/klauspost/compress/gzhttp" + "github.com/samber/lo" "go.goblog.app/app/pkgs/bufferpool" "golang.org/x/sync/singleflight" ) @@ -79,7 +80,7 @@ func (a *goBlog) checkLinks(w io.Writer, posts ...*post) error { return } // Process link - r, err, _ := sg.Do(link.Second, func() (interface{}, error) { + r, err, _ := sg.Do(link.Second, func() (any, error) { // Check if already cached if mr, ok := sm.Load(link.Second); ok { return mr, nil @@ -131,9 +132,9 @@ func (a *goBlog) allLinks(posts ...*post) (allLinks []*stringPair, err error) { if err != nil { return nil, err } - for _, link := range links { - allLinks = append(allLinks, &stringPair{a.fullPostURL(p), link}) - } + allLinks = lo.Map(links, func(s string, _ int) *stringPair { + return &stringPair{a.fullPostURL(p), s} + }) } return allLinks, nil } diff --git a/comments.go b/comments.go index 344a024..d6e7e39 100644 --- a/comments.go +++ b/comments.go @@ -102,7 +102,7 @@ type commentsRequestConfig struct { offset, limit int } -func buildCommentsQuery(config *commentsRequestConfig) (query string, args []interface{}) { +func buildCommentsQuery(config *commentsRequestConfig) (query string, args []any) { queryBuilder := bufferpool.Get() defer bufferpool.Put(queryBuilder) queryBuilder.WriteString("select id, target, name, website, comment from comments order by id desc") diff --git a/commentsAdmin.go b/commentsAdmin.go index d5b202f..36033e1 100644 --- a/commentsAdmin.go +++ b/commentsAdmin.go @@ -5,26 +5,28 @@ import ( "net/http" "reflect" "strconv" + "sync" "github.com/go-chi/chi/v5" "github.com/vcraescu/go-paginator" ) type commentsPaginationAdapter struct { - config *commentsRequestConfig - nums int64 - db *database + config *commentsRequestConfig + nums int64 + getNums sync.Once + db *database } func (p *commentsPaginationAdapter) Nums() (int64, error) { - if p.nums == 0 { + p.getNums.Do(func() { nums, _ := p.db.countComments(p.config) p.nums = int64(nums) - } + }) return p.nums, nil } -func (p *commentsPaginationAdapter) Slice(offset, length int, data interface{}) error { +func (p *commentsPaginationAdapter) Slice(offset, length int, data any) error { modifiedConfig := *p.config modifiedConfig.offset = offset modifiedConfig.limit = length diff --git a/config.go b/config.go index 849f01a..64d6a37 100644 --- a/config.go +++ b/config.go @@ -7,6 +7,7 @@ import ( "net/url" "strings" + "github.com/samber/lo" "github.com/spf13/viper" ) @@ -370,9 +371,7 @@ func (a *goBlog) initConfig() error { if a.cfg.DefaultBlog == "" { if len(a.cfg.Blogs) == 1 { // Set default blog to the only blog that is configured - for k := range a.cfg.Blogs { - a.cfg.DefaultBlog = k - } + a.cfg.DefaultBlog = lo.Keys(a.cfg.Blogs)[0] } else { return errors.New("no default blog configured") } diff --git a/database.go b/database.go index b047453..9bf5ac6 100644 --- a/database.go +++ b/database.go @@ -66,7 +66,7 @@ func (a *goBlog) openDatabase(file string, logging bool) (*database, error) { sql.Register(dbDriverName, &sqlite.SQLiteDriver{ ConnectHook: func(c *sqlite.SQLiteConn) error { // Register functions - for n, f := range map[string]interface{}{ + for n, f := range map[string]any{ "mdtext": a.renderText, "tolocal": toLocalSafe, "toutc": toUTCSafe, @@ -174,14 +174,14 @@ func (db *database) close() error { return db.db.Close() } -func (db *database) prepare(query string, args ...interface{}) (*sql.Stmt, []interface{}, error) { +func (db *database) prepare(query string, args ...any) (*sql.Stmt, []any, error) { if db == nil || db.db == nil { return nil, nil, errors.New("database not initialized") } if len(args) > 0 && args[0] == dbNoCache { return nil, args[1:], nil } - stmt, err, _ := db.sg.Do(query, func() (interface{}, error) { + stmt, err, _ := db.sg.Do(query, func() (any, error) { // Look if statement already exists st, ok := db.psc.Get(query) if ok { @@ -207,11 +207,11 @@ func (db *database) prepare(query string, args ...interface{}) (*sql.Stmt, []int const dbNoCache = "nocache" -func (db *database) exec(query string, args ...interface{}) (sql.Result, error) { +func (db *database) exec(query string, args ...any) (sql.Result, error) { return db.execContext(context.Background(), query, args...) } -func (db *database) execContext(c context.Context, query string, args ...interface{}) (sql.Result, error) { +func (db *database) execContext(c context.Context, query string, args ...any) (sql.Result, error) { if db == nil || db.db == nil { return nil, errors.New("database not initialized") } @@ -230,11 +230,11 @@ func (db *database) execContext(c context.Context, query string, args ...interfa return db.db.ExecContext(ctx, query, args...) } -func (db *database) query(query string, args ...interface{}) (*sql.Rows, error) { +func (db *database) query(query string, args ...any) (*sql.Rows, error) { return db.queryContext(context.Background(), query, args...) } -func (db *database) queryContext(c context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) { +func (db *database) queryContext(c context.Context, query string, args ...any) (rows *sql.Rows, err error) { if db == nil || db.db == nil { return nil, errors.New("database not initialized") } @@ -253,11 +253,11 @@ func (db *database) queryContext(c context.Context, query string, args ...interf return } -func (db *database) queryRow(query string, args ...interface{}) (*sql.Row, error) { +func (db *database) queryRow(query string, args ...any) (*sql.Row, error) { return db.queryRowContext(context.Background(), query, args...) } -func (db *database) queryRowContext(c context.Context, query string, args ...interface{}) (row *sql.Row, err error) { +func (db *database) queryRowContext(c context.Context, query string, args ...any) (row *sql.Row, err error) { if db == nil || db.db == nil { return nil, errors.New("database not initialized") } diff --git a/databaseHooks.go b/databaseHooks.go index 68ae3d9..e4eed64 100644 --- a/databaseHooks.go +++ b/databaseHooks.go @@ -13,14 +13,14 @@ import ( const dbHooksBegin contextKey = "begin" -func (db *database) dbBefore(ctx context.Context, _ string, _ ...interface{}) context.Context { +func (db *database) dbBefore(ctx context.Context, _ string, _ ...any) context.Context { if !db.debug { return ctx } return context.WithValue(ctx, dbHooksBegin, time.Now()) } -func (db *database) dbAfter(ctx context.Context, query string, args ...interface{}) { +func (db *database) dbAfter(ctx context.Context, query string, args ...any) { if !db.debug { return } @@ -55,7 +55,7 @@ func (db *database) dbAfter(ctx context.Context, query string, args ...interface bufferpool.Put(logBuilder) } -func argToString(arg interface{}) string { +func argToString(arg any) string { val := cast.ToString(arg) if val == "" { val = fmt.Sprintf("%v", arg) diff --git a/databaseMigrations.go b/databaseMigrations.go index e44e21b..514e886 100644 --- a/databaseMigrations.go +++ b/databaseMigrations.go @@ -14,7 +14,7 @@ import ( var dbMigrations embed.FS func migrateDb(db *sql.DB, logging bool) error { - var sqlMigrations []interface{} + var sqlMigrations []any err := fs.WalkDir(dbMigrations, "dbmigrations", func(path string, d fs.DirEntry, err error) error { if err != nil || d.Type().IsDir() { return err @@ -39,7 +39,7 @@ func migrateDb(db *sql.DB, logging bool) error { return err } m, err := migrator.New( - migrator.WithLogger(migrator.LoggerFunc(func(s string, i ...interface{}) { + migrator.WithLogger(migrator.LoggerFunc(func(s string, i ...any) { if logging { log.Printf(s, i) } diff --git a/debug.go b/debug.go index 7f133a6..49c990c 100644 --- a/debug.go +++ b/debug.go @@ -2,8 +2,8 @@ package main import "log" -func (a *goBlog) debug(msg ...interface{}) { +func (a *goBlog) debug(msg ...any) { if a.cfg.Debug { - log.Println(append([]interface{}{"Debug:"}, msg...)...) + log.Println(append([]any{"Debug:"}, msg...)...) } } diff --git a/editor.go b/editor.go index ff9c5be..d81f7a8 100644 --- a/editor.go +++ b/editor.go @@ -94,7 +94,7 @@ func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) { case "updatepost": buf := bufferpool.Get() defer bufferpool.Put(buf) - err := json.NewEncoder(buf).Encode(map[string]interface{}{ + err := json.NewEncoder(buf).Encode(map[string]any{ "action": actionUpdate, "url": r.FormValue("url"), "replace": map[string][]string{ @@ -179,8 +179,8 @@ func (a *goBlog) editorMicropubPost(w http.ResponseWriter, r *http.Request, medi func (*goBlog) editorPostTemplate(blog string, bc *configBlog) string { builder := bufferpool.Get() defer bufferpool.Put(builder) - marsh := func(param string, i interface{}) { - _ = yaml.NewEncoder(builder).Encode(map[string]interface{}{ + marsh := func(param string, i any) { + _ = yaml.NewEncoder(builder).Encode(map[string]any{ param: i, }) } diff --git a/editorFiles.go b/editorFiles.go index 47aedfb..a07bcd6 100644 --- a/editorFiles.go +++ b/editorFiles.go @@ -4,7 +4,7 @@ import ( "net/http" "sort" - "github.com/thoas/go-funk" + "github.com/samber/lo" ) func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) { @@ -26,13 +26,9 @@ func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) { return files[i].Time.After(files[j].Time) }) // Find uses - fileNames, ok := funk.Map(files, func(f *mediaFile) string { + fileNames := lo.Map(files, func(f *mediaFile, _ int) string { return f.Name - }).([]string) - if !ok { - a.serveError(w, r, "Failed to get file names", http.StatusInternalServerError) - return - } + }) uses, err := a.db.usesOfMediaFile(fileNames...) if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) diff --git a/geo.go b/geo.go index 10473d7..c98e805 100644 --- a/geo.go +++ b/geo.go @@ -10,7 +10,7 @@ import ( gogeouri "git.jlel.se/jlelse/go-geouri" "github.com/carlmjohnson/requests" geojson "github.com/paulmach/go.geojson" - "github.com/thoas/go-funk" + "github.com/samber/lo" "go.goblog.app/app/pkgs/bufferpool" ) @@ -23,9 +23,9 @@ func (a *goBlog) geoTitle(g *gogeouri.Geo, lang string) string { return "" } f := fc.Features[0] - return strings.Join(funk.FilterString([]string{ + return strings.Join(lo.Filter([]string{ f.PropertyMustString("name", ""), f.PropertyMustString("city", ""), f.PropertyMustString("state", ""), f.PropertyMustString("country", ""), - }, func(s string) bool { return s != "" }), ", ") + }, func(s string, _ int) bool { return s != "" }), ", ") } func (a *goBlog) photonReverse(lat, lon float64, lang string) (*geojson.FeatureCollection, error) { @@ -50,7 +50,7 @@ func (a *goBlog) photonReverse(lat, lon float64, lang string) (*geojson.FeatureC rb := requests.URL("https://photon.komoot.io/reverse").Client(a.httpClient).UserAgent(appUserAgent).ToBytesBuffer(buf) // Set parameters rb.Param("lat", fmt.Sprintf("%v", lat)).Param("lon", fmt.Sprintf("%v", lon)) - rb.Param("lang", funk.ShortIf(lang == "de" || lang == "fr" || lang == "it", lang, "en").(string)) // Photon only supports en, de, fr, it + rb.Param("lang", lo.If(lang == "de" || lang == "fr" || lang == "it", lang).Else("en")) // Photon only supports en, de, fr, it // Do request if err := rb.Fetch(context.Background()); err != nil { return nil, err diff --git a/go.mod b/go.mod index c17a2e0..ba64122 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.goblog.app/app -go 1.17 +go 1.18 require ( git.jlel.se/jlelse/go-geouri v0.0.0-20210525190615-a9c1d50f42d6 @@ -42,20 +42,20 @@ require ( github.com/paulmach/go.geojson v1.4.0 github.com/posener/wstest v1.2.0 github.com/pquerna/otp v1.3.0 + github.com/samber/lo v1.10.1 github.com/schollz/sqlite3dump v1.3.1 github.com/snabb/sitemap v1.0.0 github.com/spf13/cast v1.4.1 github.com/spf13/viper v1.10.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/tdewolff/minify/v2 v2.10.0 - github.com/thoas/go-funk v0.9.2 github.com/tkrajina/gpxgo v1.2.1 github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2 github.com/yuin/goldmark v1.4.10 // master github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 - golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000 + golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd golang.org/x/net v0.0.0-20220225172249-27dd8689420f golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/text v0.3.7 @@ -128,6 +128,7 @@ require ( go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect diff --git a/go.sum b/go.sum index 90b4af1..15d8487 100644 --- a/go.sum +++ b/go.sum @@ -369,6 +369,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/paulmach/go.geojson v1.4.0 h1:5x5moCkCtDo5x8af62P9IOAYGQcYHtxz2QJ3x1DoCgY= github.com/paulmach/go.geojson v1.4.0/go.mod h1:YaKx1hKpWF+T2oj2lFJPsW/t1Q5e1jQI61eoQSTwpIs= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= @@ -388,6 +389,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/lo v1.10.1 h1:0D3h7i0U3hRAbaCeQ82DLe67n0A7Bbl0/cEoWqFGp+U= +github.com/samber/lo v1.10.1/go.mod h1:2I7tgIv8Q1SG2xEIkRq0F2i2zgxVpnyPOP0d3Gj2r+A= github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA= github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= @@ -413,8 +416,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tailscale/certstore v0.0.0-20210528134328-066c94b793d3 h1:fEubocuQkrlcuYeXelhYq/YcKvVVe1Ah7saQEtj98Mo= @@ -431,8 +435,7 @@ github.com/tdewolff/parse/v2 v2.5.27 h1:PL3LzzXaOpmdrknnOlIeO2muIBHAwiKp6TxN1RbU github.com/tdewolff/parse/v2 v2.5.27/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= -github.com/thoas/go-funk v0.9.2 h1:oKlNYv0AY5nyf9g+/GhMgS/UO2ces0QRdPKwkhY3VCk= -github.com/thoas/go-funk v0.9.2/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/tkrajina/gpxgo v1.2.1 h1:MJJtT4Re5btDGg89brFDrUP3EWz+cBmyo8pQwV0ZOak= github.com/tkrajina/gpxgo v1.2.1/go.mod h1:795sjVRFo5wWyN6oOZp0RYienGGBJjpAlgOz2nCngA0= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= @@ -483,8 +486,8 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000 h1:SL+8VVnkqyshUSz5iNnXtrBQzvFF2SkROm6t5RczFAE= -golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -495,6 +498,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -516,8 +521,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -801,8 +806,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/hooks.go b/hooks.go index 78537eb..d6e4063 100644 --- a/hooks.go +++ b/hooks.go @@ -25,7 +25,7 @@ func (a *goBlog) postPostHooks(p *post) { if hc := a.cfg.Hooks; hc != nil { for _, cmdTmplString := range hc.PostPost { go func(p *post, cmdTmplString string) { - a.cfg.Hooks.executeTemplateCommand("post-post", cmdTmplString, map[string]interface{}{ + a.cfg.Hooks.executeTemplateCommand("post-post", cmdTmplString, map[string]any{ "URL": a.fullPostURL(p), "Post": p, }) @@ -42,7 +42,7 @@ func (a *goBlog) postUpdateHooks(p *post) { if hc := a.cfg.Hooks; hc != nil { for _, cmdTmplString := range hc.PostUpdate { go func(p *post, cmdTmplString string) { - a.cfg.Hooks.executeTemplateCommand("post-update", cmdTmplString, map[string]interface{}{ + a.cfg.Hooks.executeTemplateCommand("post-update", cmdTmplString, map[string]any{ "URL": a.fullPostURL(p), "Post": p, }) @@ -58,7 +58,7 @@ func (a *goBlog) postDeleteHooks(p *post) { if hc := a.cfg.Hooks; hc != nil { for _, cmdTmplString := range hc.PostDelete { go func(p *post, cmdTmplString string) { - a.cfg.Hooks.executeTemplateCommand("post-delete", cmdTmplString, map[string]interface{}{ + a.cfg.Hooks.executeTemplateCommand("post-delete", cmdTmplString, map[string]any{ "URL": a.fullPostURL(p), "Post": p, }) @@ -74,7 +74,7 @@ func (a *goBlog) postUndeleteHooks(p *post) { if hc := a.cfg.Hooks; hc != nil { for _, cmdTmplString := range hc.PostUndelete { go func(p *post, cmdTmplString string) { - a.cfg.Hooks.executeTemplateCommand("post-undelete", cmdTmplString, map[string]interface{}{ + a.cfg.Hooks.executeTemplateCommand("post-undelete", cmdTmplString, map[string]any{ "URL": a.fullPostURL(p), "Post": p, }) @@ -86,7 +86,7 @@ func (a *goBlog) postUndeleteHooks(p *post) { } } -func (cfg *configHooks) executeTemplateCommand(hookType string, tmpl string, data map[string]interface{}) { +func (cfg *configHooks) executeTemplateCommand(hookType string, tmpl string, data map[string]any) { cmdTmpl, err := template.New("cmd").Parse(tmpl) if err != nil { log.Println("Failed to parse cmd template:", err.Error()) diff --git a/indexnow.go b/indexnow.go index 705d08d..bd4d195 100644 --- a/indexnow.go +++ b/indexnow.go @@ -6,7 +6,6 @@ import ( "net/http" "github.com/carlmjohnson/requests" - "github.com/thoas/go-funk" ) // Implement support for the IndexNow protocol @@ -78,7 +77,7 @@ func (a *goBlog) indexNowKey() []byte { } if keyBytes == nil { // Generate 128 character key with hexadecimal characters - keyBytes = []byte(funk.RandomString(128, []rune("0123456789abcdef"))) + keyBytes = []byte(randomString(128, []rune("0123456789abcdef"))) // Store key in database err = a.db.cachePersistently("indexnowkey", keyBytes) if err != nil { diff --git a/main.go b/main.go index 2a946bd..c971df8 100644 --- a/main.go +++ b/main.go @@ -221,7 +221,7 @@ func (app *goBlog) initComponents(logging bool) { } } -func (a *goBlog) logErrAndQuit(v ...interface{}) { +func (a *goBlog) logErrAndQuit(v ...any) { log.Println(v...) a.shutdown.ShutdownAndWait() os.Exit(1) diff --git a/markdown.go b/markdown.go index 0bbb973..5560cb8 100644 --- a/markdown.go +++ b/markdown.go @@ -185,7 +185,7 @@ func (c *customRenderer) renderImage(w util.BufWriter, source []byte, node ast.N } hb := newHtmlBuilder(w) hb.writeElementOpen("a", "href", dest) - imgEls := []interface{}{"src", dest, "alt", string(n.Text(source)), "loading", "lazy"} + imgEls := []any{"src", dest, "alt", string(n.Text(source)), "loading", "lazy"} if len(n.Title) > 0 { imgEls = append(imgEls, "title", string(n.Title)) } diff --git a/mediaCompression.go b/mediaCompression.go index e560252..84a2829 100644 --- a/mediaCompression.go +++ b/mediaCompression.go @@ -67,7 +67,7 @@ func (sp *shortpixel) compress(url string, upload mediaStorageSaveFunc, hc *http URL("https://api.shortpixel.com/v2/reducer-sync.php"). Client(hc). Method(http.MethodPost). - BodyJSON(map[string]interface{}{ + BodyJSON(map[string]any{ "key": sp.key, "plugin_version": "GB001", "lossy": 1, @@ -106,8 +106,8 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli Client(hc). Method(http.MethodPost). BasicAuth("api", tf.key). - BodyJSON(map[string]interface{}{ - "source": map[string]interface{}{ + BodyJSON(map[string]any{ + "source": map[string]any{ "url": url, }, }). @@ -130,8 +130,8 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli Client(hc). Method(http.MethodPost). BasicAuth("api", tf.key). - BodyJSON(map[string]interface{}{ - "resize": map[string]interface{}{ + BodyJSON(map[string]any{ + "resize": map[string]any{ "method": "fit", "width": defaultCompressionWidth, "height": defaultCompressionHeight, diff --git a/mediaCompression_test.go b/mediaCompression_test.go index 7b6fc63..f8f6388 100644 --- a/mediaCompression_test.go +++ b/mediaCompression_test.go @@ -46,7 +46,7 @@ func Test_compress(t *testing.T) { requestBody, _ := io.ReadAll(r.Body) defer r.Body.Close() - var requestJson map[string]interface{} + var requestJson map[string]any err := json.Unmarshal(requestBody, &requestJson) require.Nil(t, err) require.NotNil(t, requestJson) diff --git a/micropub.go b/micropub.go index 2243370..f5deea7 100644 --- a/micropub.go +++ b/micropub.go @@ -11,8 +11,8 @@ import ( "strings" "time" + "github.com/samber/lo" "github.com/spf13/cast" - "github.com/thoas/go-funk" "go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/contenttype" "gopkg.in/yaml.v3" @@ -21,7 +21,7 @@ import ( const micropubPath = "/micropub" func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) { - var result interface{} + var result any switch query := r.URL.Query(); query.Get("q") { case "config": type micropubConfig struct { @@ -66,7 +66,7 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) { } allCategories = append(allCategories, values...) } - result = map[string]interface{}{"categories": allCategories} + result = map[string]any{"categories": allCategories} default: a.serve404(w, r) return @@ -231,30 +231,30 @@ const ( ) type microformatItem struct { - Type []string `json:"type,omitempty"` - URL string `json:"url,omitempty"` - Action micropubAction `json:"action,omitempty"` - Properties *microformatProperties `json:"properties,omitempty"` - Replace map[string][]interface{} `json:"replace,omitempty"` - Add map[string][]interface{} `json:"add,omitempty"` - Delete interface{} `json:"delete,omitempty"` + Type []string `json:"type,omitempty"` + URL string `json:"url,omitempty"` + Action micropubAction `json:"action,omitempty"` + Properties *microformatProperties `json:"properties,omitempty"` + Replace map[string][]any `json:"replace,omitempty"` + Add map[string][]any `json:"add,omitempty"` + Delete any `json:"delete,omitempty"` } type microformatProperties struct { - Name []string `json:"name,omitempty"` - Published []string `json:"published,omitempty"` - Updated []string `json:"updated,omitempty"` - PostStatus []string `json:"post-status,omitempty"` - Visibility []string `json:"visibility,omitempty"` - Category []string `json:"category,omitempty"` - Content []string `json:"content,omitempty"` - URL []string `json:"url,omitempty"` - InReplyTo []string `json:"in-reply-to,omitempty"` - LikeOf []string `json:"like-of,omitempty"` - BookmarkOf []string `json:"bookmark-of,omitempty"` - MpSlug []string `json:"mp-slug,omitempty"` - Photo []interface{} `json:"photo,omitempty"` - Audio []string `json:"audio,omitempty"` + Name []string `json:"name,omitempty"` + Published []string `json:"published,omitempty"` + Updated []string `json:"updated,omitempty"` + PostStatus []string `json:"post-status,omitempty"` + Visibility []string `json:"visibility,omitempty"` + Category []string `json:"category,omitempty"` + Content []string `json:"content,omitempty"` + URL []string `json:"url,omitempty"` + InReplyTo []string `json:"in-reply-to,omitempty"` + LikeOf []string `json:"like-of,omitempty"` + BookmarkOf []string `json:"bookmark-of,omitempty"` + MpSlug []string `json:"mp-slug,omitempty"` + Photo []any `json:"photo,omitempty"` + Audio []string `json:"audio,omitempty"` } func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem) error { @@ -314,7 +314,7 @@ func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem) if theString, justString := photo.(string); justString { entry.Parameters[a.cfg.Micropub.PhotoParam] = append(entry.Parameters[a.cfg.Micropub.PhotoParam], theString) entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam] = append(entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam], "") - } else if thePhoto, isPhoto := photo.(map[string]interface{}); isPhoto { + } else if thePhoto, isPhoto := photo.(map[string]any); isPhoto { entry.Parameters[a.cfg.Micropub.PhotoParam] = append(entry.Parameters[a.cfg.Micropub.PhotoParam], cast.ToString(thePhoto["value"])) entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam] = append(entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam], cast.ToString(thePhoto["alt"])) } @@ -331,7 +331,7 @@ func (a *goBlog) computeExtraPostParameters(p *post) error { if split := strings.Split(p.Content, "---\n"); len(split) >= 3 && strings.TrimSpace(split[0]) == "" { // Contains frontmatter fm := split[1] - meta := map[string]interface{}{} + meta := map[string]any{} err := yaml.Unmarshal([]byte(fm), &meta) if err != nil { return err @@ -340,7 +340,7 @@ func (a *goBlog) computeExtraPostParameters(p *post) error { for key, value := range meta { // Delete existing content - replace p.Parameters[key] = []string{} - if a, ok := value.([]interface{}); ok { + if a, ok := value.([]any); ok { for _, ae := range a { p.Parameters[key] = append(p.Parameters[key], cast.ToString(ae)) } @@ -534,7 +534,7 @@ func (a *goBlog) micropubUpdate(w http.ResponseWriter, r *http.Request, u string http.Redirect(w, r, a.fullPostURL(p), http.StatusNoContent) } -func (a *goBlog) micropubUpdateReplace(p *post, replace map[string][]interface{}) { +func (a *goBlog) micropubUpdateReplace(p *post, replace map[string][]any) { if content, ok := replace["content"]; ok && len(content) > 0 { p.Content = cast.ToStringSlice(content)[0] } @@ -578,7 +578,7 @@ func (a *goBlog) micropubUpdateReplace(p *post, replace map[string][]interface{} // TODO: photos } -func (a *goBlog) micropubUpdateAdd(p *post, add map[string][]interface{}) { +func (a *goBlog) micropubUpdateAdd(p *post, add map[string][]any) { for key, value := range add { switch key { case "content": @@ -602,11 +602,11 @@ func (a *goBlog) micropubUpdateAdd(p *post, add map[string][]interface{}) { } } -func (a *goBlog) micropubUpdateDelete(p *post, del interface{}) { +func (a *goBlog) micropubUpdateDelete(p *post, del any) { if del == nil { return } - deleteProperties, ok := del.([]interface{}) + deleteProperties, ok := del.([]any) if ok { // Completely remove properties for _, prop := range deleteProperties { @@ -637,7 +637,7 @@ func (a *goBlog) micropubUpdateDelete(p *post, del interface{}) { // Return return } - toDelete, ok := del.(map[string]interface{}) + toDelete, ok := del.(map[string]any) if ok { // Only delete parts of properties for key, values := range toDelete { @@ -661,8 +661,8 @@ func (a *goBlog) micropubUpdateDelete(p *post, del interface{}) { // TODO: Support partial deletes of more properties case "category": delValues := cast.ToStringSlice(values) - p.Parameters[a.cfg.Micropub.CategoryParam] = funk.FilterString(p.Parameters[a.cfg.Micropub.CategoryParam], func(s string) bool { - return !funk.ContainsString(delValues, s) + p.Parameters[a.cfg.Micropub.CategoryParam] = lo.Filter(p.Parameters[a.cfg.Micropub.CategoryParam], func(s string, _ int) bool { + return !lo.Contains(delValues, s) }) } } diff --git a/nodeinfo.go b/nodeinfo.go index 04ba3a7..3d1746a 100644 --- a/nodeinfo.go +++ b/nodeinfo.go @@ -11,8 +11,8 @@ import ( func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) { buf := bufferpool.Get() defer bufferpool.Put(buf) - err := json.NewEncoder(buf).Encode(map[string]interface{}{ - "links": []map[string]interface{}{ + err := json.NewEncoder(buf).Encode(map[string]any{ + "links": []map[string]any{ { "href": a.getFullAddress("/nodeinfo"), "rel": "http://nodeinfo.diaspora.software/ns/schema/2.1", @@ -35,14 +35,14 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) { }) buf := bufferpool.Get() defer bufferpool.Put(buf) - err := json.NewEncoder(buf).Encode(map[string]interface{}{ + err := json.NewEncoder(buf).Encode(map[string]any{ "version": "2.1", - "software": map[string]interface{}{ + "software": map[string]any{ "name": "goblog", "repository": "https://go.goblog.app/app", }, - "usage": map[string]interface{}{ - "users": map[string]interface{}{ + "usage": map[string]any{ + "users": map[string]any{ "total": len(a.cfg.Blogs), }, "localPosts": localPosts, @@ -52,7 +52,7 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) { "micropub", "webmention", }, - "metadata": map[string]interface{}{}, + "metadata": map[string]any{}, }) if err != nil { a.serveError(w, r, "", http.StatusInternalServerError) diff --git a/notifications.go b/notifications.go index 58a9673..354c72b 100644 --- a/notifications.go +++ b/notifications.go @@ -56,7 +56,7 @@ type notificationsRequestConfig struct { offset, limit int } -func buildNotificationsQuery(config *notificationsRequestConfig) (query string, args []interface{}) { +func buildNotificationsQuery(config *notificationsRequestConfig) (query string, args []any) { queryBuilder := bufferpool.Get() defer bufferpool.Put(queryBuilder) queryBuilder.WriteString("select id, time, text from notifications order by id desc") @@ -110,7 +110,7 @@ func (p *notificationsPaginationAdapter) Nums() (int64, error) { return p.nums, nil } -func (p *notificationsPaginationAdapter) Slice(offset, length int, data interface{}) error { +func (p *notificationsPaginationAdapter) Slice(offset, length int, data any) error { modifiedConfig := *p.config modifiedConfig.offset = offset modifiedConfig.limit = length diff --git a/persistentCache.go b/persistentCache.go index 44722af..b2d8ff7 100644 --- a/persistentCache.go +++ b/persistentCache.go @@ -26,7 +26,7 @@ func (db *database) retrievePersistentCacheContext(c context.Context, key string if db == nil { return nil, errors.New("database is nil") } - d, err, _ := db.pc.Do(key, func() (interface{}, error) { + d, err, _ := db.pc.Do(key, func() (any, error) { if row, err := db.queryRowContext(c, "select data from persistent_cache where key = @key", sql.Named("key", key)); err != nil { return nil, err } else { diff --git a/pkgs/bufferpool/bufferPool.go b/pkgs/bufferpool/bufferPool.go index cb50bdc..d546483 100644 --- a/pkgs/bufferpool/bufferPool.go +++ b/pkgs/bufferpool/bufferPool.go @@ -6,7 +6,7 @@ import ( ) var bufferPool = sync.Pool{ - New: func() interface{} { + New: func() any { return new(bytes.Buffer) }, } diff --git a/pkgs/highlighting/highlighting.go b/pkgs/highlighting/highlighting.go index 027864b..4ba945a 100644 --- a/pkgs/highlighting/highlighting.go +++ b/pkgs/highlighting/highlighting.go @@ -32,7 +32,7 @@ func newConfig() *config { } // SetOption implements renderer.SetOptioner. -func (c *config) SetOption(name renderer.OptionName, value interface{}) { +func (c *config) SetOption(name renderer.OptionName, value any) { c.Config.SetOption(name, value) } diff --git a/pkgs/httpcompress/httpCompress.go b/pkgs/httpcompress/httpCompress.go index 9d68233..bf50c00 100644 --- a/pkgs/httpcompress/httpCompress.go +++ b/pkgs/httpcompress/httpCompress.go @@ -11,7 +11,7 @@ import ( "github.com/klauspost/compress/flate" "github.com/klauspost/compress/gzip" - "github.com/thoas/go-funk" + "github.com/samber/lo" "go.goblog.app/app/pkgs/contenttype" ) @@ -48,7 +48,7 @@ type Compressor struct { // The mapping of pooled encoders to pools. pooledEncoders map[string]*sync.Pool // The set of content types allowed to be compressed. - allowedTypes map[string]interface{} + allowedTypes map[string]any // The list of encoders in order of decreasing precedence. encodingPrecedence []string // The compression level. @@ -62,8 +62,8 @@ type Compressor struct { func NewCompressor(level int, types ...string) *Compressor { // If types are provided, set those as the allowed types. If none are // provided, use the default list. - allowedTypes := map[string]interface{}{} - for _, t := range funk.ShortIf(len(types) > 0, types, defaultCompressibleContentTypes).([]string) { + allowedTypes := map[string]any{} + for _, t := range lo.If(len(types) > 0, types).Else(defaultCompressibleContentTypes) { allowedTypes[t] = nil } @@ -96,7 +96,7 @@ func (c *Compressor) SetEncoder(encoding string, fn EncoderFunc) { delete(c.pooledEncoders, encoding) c.pooledEncoders[encoding] = &sync.Pool{ - New: func() interface{} { + New: func() any { return fn(io.Discard, c.level) }, } @@ -126,10 +126,6 @@ func (c *Compressor) Handler(next http.Handler) http.Handler { }) } -func matchAcceptEncoding(accepted []string, encoding string) bool { - return funk.ContainsString(accepted, encoding) -} - // An EncoderFunc is a function that wraps the provided io.Writer with a // streaming compression algorithm and returns it. // @@ -178,7 +174,7 @@ func (cw *compressResponseWriter) selectEncoder() (compressWriter, string, func( // Find supported encoder by accepted list by precedence for _, name := range cw.compressor.encodingPrecedence { - if matchAcceptEncoding(accepted, name) { + if lo.Contains(accepted, name) { if pool, ok := cw.compressor.pooledEncoders[name]; ok { encoder := pool.Get().(compressWriter) cleanup := func() { diff --git a/posts.go b/posts.go index 22d70a2..801af21 100644 --- a/posts.go +++ b/posts.go @@ -127,7 +127,7 @@ func (p *postPaginationAdapter) Nums() (int64, error) { return p.nums, nil } -func (p *postPaginationAdapter) Slice(offset, length int, data interface{}) error { +func (p *postPaginationAdapter) Slice(offset, length int, data any) error { modifiedConfig := *p.config modifiedConfig.offset = offset modifiedConfig.limit = length diff --git a/postsDb.go b/postsDb.go index a30094e..abf2c1e 100644 --- a/postsDb.go +++ b/postsDb.go @@ -10,7 +10,7 @@ import ( "time" "github.com/araddon/dateparse" - "github.com/thoas/go-funk" + "github.com/samber/lo" "go.goblog.app/app/pkgs/bufferpool" ) @@ -86,8 +86,7 @@ func (a *goBlog) checkPost(p *post) (err error) { p.Section = a.cfg.Blogs[p.Blog].DefaultSection } if p.Slug == "" { - random := generateRandomString(5) - p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random) + p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), randomString(5)) } published := timeNoErr(dateparse.ParseLocal(p.Published)) pathTmplString := defaultIfEmpty( @@ -100,7 +99,7 @@ func (a *goBlog) checkPost(p *post) (err error) { } pathBuffer := bufferpool.Get() defer bufferpool.Put(pathBuffer) - err = pathTmpl.Execute(pathBuffer, map[string]interface{}{ + err = pathTmpl.Execute(pathBuffer, map[string]any{ "BlogPath": a.getRelativePath(p.Blog, ""), "Year": published.Year(), "Month": int(published.Month()), @@ -173,7 +172,7 @@ func (db *database) savePost(p *post, o *postCreationOptions) error { // Build SQL sqlBuilder := bufferpool.Get() defer bufferpool.Put(sqlBuilder) - var sqlArgs = []interface{}{dbNoCache} + var sqlArgs = []any{dbNoCache} // Start transaction sqlBuilder.WriteString("begin;") // Delete old post @@ -297,7 +296,7 @@ func (db *database) replacePostParam(path, param string, values []string) error // Build SQL sqlBuilder := bufferpool.Get() defer bufferpool.Put(sqlBuilder) - var sqlArgs = []interface{}{dbNoCache} + var sqlArgs = []any{dbNoCache} // Start transaction sqlBuilder.WriteString("begin;") // Delete old post @@ -344,7 +343,7 @@ type postsRequestConfig struct { withoutRenderedTitle bool } -func buildPostsQuery(c *postsRequestConfig, selection string) (query string, args []interface{}) { +func buildPostsQuery(c *postsRequestConfig, selection string) (query string, args []any) { queryBuilder := bufferpool.Get() defer bufferpool.Put(queryBuilder) // Selection @@ -461,7 +460,7 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err return nil } // Build query - sqlArgs := make([]interface{}, 0) + sqlArgs := make([]any, 0) queryBuilder := bufferpool.Get() defer bufferpool.Put(queryBuilder) queryBuilder.WriteString("select path, parameter, value from post_parameters where") @@ -584,10 +583,7 @@ func (d *database) countPosts(config *postsRequestConfig) (count int, err error) } func (a *goBlog) getRandomPostPath(blog string) (path string, err error) { - sections, ok := funk.Keys(a.cfg.Blogs[blog].Sections).([]string) - if !ok { - return "", errors.New("no sections") - } + sections := lo.Keys(a.cfg.Blogs[blog].Sections) query, params := buildPostsQuery(&postsRequestConfig{randomOrder: true, limit: 1, blog: blog, sections: sections}, "path") row, err := a.db.queryRow(query, params...) if err != nil { @@ -634,7 +630,7 @@ group by name; ` func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) { - sqlArgs := []interface{}{dbNoCache} + sqlArgs := []any{dbNoCache} nameValues := bufferpool.Get() for i, n := range names { if i > 0 { diff --git a/postsFuncs.go b/postsFuncs.go index e655765..f1d4cd7 100644 --- a/postsFuncs.go +++ b/postsFuncs.go @@ -211,7 +211,7 @@ func (a *goBlog) photoLinks(p *post) []string { } func (p *post) contentWithParams() string { - params := map[string]interface{}{} + params := map[string]any{} for k, v := range p.Parameters { if l := len(v); l == 1 { params[k] = v[0] diff --git a/render.go b/render.go index a6d2d9b..c247e7c 100644 --- a/render.go +++ b/render.go @@ -13,7 +13,7 @@ type renderData struct { TorAddress string Blog *configBlog User *configUser - Data interface{} + Data any CommentsEnabled bool WebmentionReceivingEnabled bool TorUsed bool @@ -91,6 +91,6 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) { } // Data if data.Data == nil { - data.Data = map[string]interface{}{} + data.Data = map[string]any{} } } diff --git a/shortPath.go b/shortPath.go index a6289ca..0949a9e 100644 --- a/shortPath.go +++ b/shortPath.go @@ -11,7 +11,7 @@ func (db *database) shortenPath(p string) (string, error) { if p == "" { return "", errors.New("empty path") } - spi, err, _ := db.sp.Do(p, func() (interface{}, error) { + spi, err, _ := db.sp.Do(p, func() (any, error) { // Check if already cached if spi, ok := db.spc.Get(p); ok { return spi.(string), nil diff --git a/sitemap.go b/sitemap.go index 6400a36..4857e2a 100644 --- a/sitemap.go +++ b/sitemap.go @@ -172,7 +172,7 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) { a.writeSitemapXML(w, sm) } -func (a *goBlog) writeSitemapXML(w http.ResponseWriter, sm interface{}) { +func (a *goBlog) writeSitemapXML(w http.ResponseWriter, sm any) { w.Header().Set(contentType, contenttype.XMLUTF8) pipeReader, pipeWriter := io.Pipe() go func() { diff --git a/tailscale.go b/tailscale.go index 960dd0d..ffe98cd 100644 --- a/tailscale.go +++ b/tailscale.go @@ -36,7 +36,7 @@ func (a *goBlog) getTailscaleListener(addr string) (net.Listener, error) { a.tss = &tsnet.Server{ Hostname: tsconfig.Hostname, Dir: tailscaleDir, - Logf: func(format string, args ...interface{}) { + Logf: func(format string, args ...any) { log.Printf("tailscale: "+format, args...) }, } diff --git a/tts.go b/tts.go index 0e79379..19bca49 100644 --- a/tts.go +++ b/tts.go @@ -192,20 +192,20 @@ func (a *goBlog) createTTSAudio(lang, ssml string, w io.Writer) error { } // Create request body - body := map[string]interface{}{ - "audioConfig": map[string]interface{}{ + body := map[string]any{ + "audioConfig": map[string]any{ "audioEncoding": "MP3", }, - "input": map[string]interface{}{ + "input": map[string]any{ "ssml": ssml, }, - "voice": map[string]interface{}{ + "voice": map[string]any{ "languageCode": lang, }, } // Do request - var response map[string]interface{} + var response map[string]any err := requests. URL("https://texttospeech.googleapis.com/v1beta1/text:synthesize"). Param("key", gctts.GoogleAPIKey). diff --git a/ui.go b/ui.go index 267f4ce..073e206 100644 --- a/ui.go +++ b/ui.go @@ -7,7 +7,7 @@ import ( "github.com/hacdias/indieauth" "github.com/kaorimatz/go-opml" "github.com/mergestat/timediff" - "github.com/thoas/go-funk" + "github.com/samber/lo" ) func (a *goBlog) renderEditorPreview(hb *htmlBuilder, bc *configBlog, p *post) { @@ -279,7 +279,7 @@ func (a *goBlog) renderSearch(hb *htmlBuilder, rd *renderData) { // Form hb.writeElementOpen("form", "class", "fw p", "method", "post") // Search - args := []interface{}{"type", "text", "name", "q", "required", ""} + args := []any{"type", "text", "name", "q", "required", ""} if sc.Placeholder != "" { args = append(args, "placeholder", a.renderMdTitle(sc.Placeholder)) } @@ -882,7 +882,7 @@ func (a *goBlog) renderPost(hb *htmlBuilder, rd *renderData) { // Post actions hb.writeElementOpen("div", "class", "actions") // Share button - hb.writeElementOpen("a", "class", "button", "href", fmt.Sprintf("https://www.addtoany.com/share#url=%s%s", a.shortPostURL(p), funk.ShortIf(p.RenderedTitle != "", "&title="+p.RenderedTitle, "")), "target", "_blank", "rel", "nofollow noopener noreferrer") + hb.writeElementOpen("a", "class", "button", "href", fmt.Sprintf("https://www.addtoany.com/share#url=%s%s", a.shortPostURL(p), lo.If(p.RenderedTitle != "", "&title="+p.RenderedTitle).Else("")), "target", "_blank", "rel", "nofollow noopener noreferrer") hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "share")) hb.writeElementClose("a") // Translate button @@ -894,7 +894,7 @@ func (a *goBlog) renderPost(hb *htmlBuilder, rd *renderData) { // Speak button hb.writeElementOpen("button", "id", "speakBtn", "class", "hide", "data-speak", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "speak"), "data-stopspeak", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "stopspeak")) hb.writeElementClose("button") - hb.writeElementOpen("script", "defer", "", "src", funk.ShortIf(p.TTS() != "", a.assetFileName("js/tts.js"), a.assetFileName("js/speak.js"))) + hb.writeElementOpen("script", "defer", "", "src", lo.If(p.TTS() != "", a.assetFileName("js/tts.js")).Else(a.assetFileName("js/speak.js"))) hb.writeElementClose("script") hb.writeElementClose("div") // TTS diff --git a/uiHtmlBuilder.go b/uiHtmlBuilder.go index c64c38a..cca258d 100644 --- a/uiHtmlBuilder.go +++ b/uiHtmlBuilder.go @@ -36,7 +36,7 @@ func (h *htmlBuilder) writeEscaped(s string) { textTemplate.HTMLEscape(h, []byte(s)) } -func (h *htmlBuilder) writeAttribute(attr string, val interface{}) { +func (h *htmlBuilder) writeAttribute(attr string, val any) { h.write(` `) h.write(attr) h.write(`=`) @@ -49,7 +49,7 @@ func (h *htmlBuilder) writeAttribute(attr string, val interface{}) { } } -func (h *htmlBuilder) writeElementOpen(tag string, attrs ...interface{}) { +func (h *htmlBuilder) writeElementOpen(tag string, attrs ...any) { h.write(`<`) h.write(tag) for i := 0; i < len(attrs); i += 2 { diff --git a/utils.go b/utils.go index f331150..5440d17 100644 --- a/utils.go +++ b/utils.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "math/rand" "net/http" "net/http/httptest" "net/url" @@ -23,7 +24,7 @@ import ( "github.com/c2h5oh/datasize" tdl "github.com/mergestat/timediff/locale" "github.com/microcosm-cc/bluemonday" - "github.com/thoas/go-funk" + "github.com/samber/lo" "go.goblog.app/app/pkgs/bufferpool" "golang.org/x/text/language" ) @@ -55,10 +56,15 @@ func sortedStrings(s []string) []string { return s } -const randomLetters = "abcdefghijklmnopqrstuvwxyz" +var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyz") -func generateRandomString(chars int) string { - return funk.RandomString(chars, []rune(randomLetters)) +func randomString(n int, allowedChars ...[]rune) string { + letters := append(allowedChars, defaultLetters)[0] + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) } func isAbsoluteURL(s string) bool { @@ -84,7 +90,7 @@ func allLinksFromHTML(r io.Reader, baseURL string) ([]string, error) { } }) links, err = resolveURLReferences(baseURL, links...) - return funk.UniqString(links), err + return lo.Uniq(links), err } func resolveURLReferences(base string, refs ...string) ([]string, error) { @@ -212,8 +218,8 @@ func urlHasExt(rawUrl string, allowed ...string) (ext string, has bool) { return "", false } ext = ext[1:] - allowed = funk.Map(allowed, strings.ToLower).([]string) - return ext, funk.ContainsString(allowed, strings.ToLower(ext)) + allowed = lo.Map(allowed, func(t string, _ int) string { return strings.ToLower(t) }) + return ext, lo.Contains(allowed, strings.ToLower(ext)) } func mBytesString(size int64) string { @@ -274,7 +280,10 @@ func cleanHTMLText(s string) string { } func defaultIfEmpty(s, d string) string { - return funk.ShortIf(s != "", s, d).(string) + if s == "" { + return d + } + return s } func containsStrings(s string, subStrings ...string) bool { diff --git a/utils_test.go b/utils_test.go index c163556..9b38ee7 100644 --- a/utils_test.go +++ b/utils_test.go @@ -12,6 +12,16 @@ func Test_urlize(t *testing.T) { assert.Equal(t, "this-is-a-test", urlize("This Is A Test")) } +func Fuzz_urlize(f *testing.F) { + f.Add("Test") + f.Fuzz(func(t *testing.T, str string) { + out := urlize(str) + if out == "" { + t.Error("Empty output") + } + }) +} + func Benchmark_urlize(b *testing.B) { for i := 0; i < b.N; i++ { urlize("äbc ef") @@ -23,8 +33,8 @@ func Test_sortedStrings(t *testing.T) { } func Test_generateRandomString(t *testing.T) { - assert.Len(t, generateRandomString(30), 30) - assert.Len(t, generateRandomString(50), 50) + assert.Len(t, randomString(30), 30) + assert.Len(t, randomString(50), 50) } func Test_isAbsoluteURL(t *testing.T) { diff --git a/webmention.go b/webmention.go index 3f7574e..64cab3a 100644 --- a/webmention.go +++ b/webmention.go @@ -226,7 +226,7 @@ type webmentionsRequestConfig struct { submentions bool } -func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []interface{}) { +func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []any) { queryBuilder := bufferpool.Get() defer bufferpool.Put(queryBuilder) queryBuilder.WriteString("select id, source, target, url, created, title, content, author, status from webmentions ") diff --git a/webmentionAdmin.go b/webmentionAdmin.go index 36b5115..a3062b1 100644 --- a/webmentionAdmin.go +++ b/webmentionAdmin.go @@ -6,28 +6,30 @@ import ( "net/url" "reflect" "strconv" + "sync" "github.com/go-chi/chi/v5" "github.com/vcraescu/go-paginator" ) type webmentionPaginationAdapter struct { - config *webmentionsRequestConfig - nums int64 - db *database + config *webmentionsRequestConfig + nums int64 + getNums sync.Once + db *database } var _ paginator.Adapter = &webmentionPaginationAdapter{} func (p *webmentionPaginationAdapter) Nums() (int64, error) { - if p.nums == 0 { + p.getNums.Do(func() { nums, _ := p.db.countWebmentions(p.config) p.nums = int64(nums) - } + }) return p.nums, nil } -func (p *webmentionPaginationAdapter) Slice(offset, length int, data interface{}) error { +func (p *webmentionPaginationAdapter) Slice(offset, length int, data any) error { modifiedConfig := *p.config modifiedConfig.offset = offset modifiedConfig.limit = length diff --git a/webmentionSending.go b/webmentionSending.go index 6c23d25..958ddf9 100644 --- a/webmentionSending.go +++ b/webmentionSending.go @@ -12,7 +12,7 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/carlmjohnson/requests" - "github.com/thoas/go-funk" + "github.com/samber/lo" "github.com/tomnomnom/linkheader" "go.goblog.app/app/pkgs/bufferpool" ) @@ -44,7 +44,7 @@ func (a *goBlog) sendWebmentions(p *post) error { if mpc := a.cfg.Micropub; mpc != nil { links = append(links, p.firstParameter(a.cfg.Micropub.LikeParam), p.firstParameter(a.cfg.Micropub.ReplyParam), p.firstParameter(a.cfg.Micropub.BookmarkParam)) } - for _, link := range funk.UniqString(links) { + for _, link := range lo.Uniq(links) { if link == "" { continue } diff --git a/webmentionVerification.go b/webmentionVerification.go index 912a4de..d702632 100644 --- a/webmentionVerification.go +++ b/webmentionVerification.go @@ -14,7 +14,7 @@ import ( "time" "github.com/PuerkitoBio/goquery" - "github.com/thoas/go-funk" + "github.com/samber/lo" "go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/contenttype" "willnorris.com/go/microformats" @@ -161,7 +161,7 @@ func (a *goBlog) verifyReader(m *mention, body io.Reader) error { if err != nil { return err } - if _, hasLink := funk.FindString(links, func(s string) bool { + if _, hasLink := lo.Find(links, func(s string) bool { // Check if link belongs to installation hasShortPrefix := a.cfg.Server.ShortPublicAddress != "" && strings.HasPrefix(s, a.cfg.Server.ShortPublicAddress) hasLongPrefix := strings.HasPrefix(s, a.cfg.Server.PublicAddress)