This commit is contained in:
Jan-Lukas Else 2022-03-16 08:28:03 +01:00
parent 7e8cb619b5
commit 474155306d
49 changed files with 218 additions and 202 deletions

View File

@ -1,4 +1,4 @@
FROM golang:1.17-alpine3.15 as buildbase FROM golang:1.18-alpine3.15 as buildbase
WORKDIR /app WORKDIR /app
RUN apk add --no-cache git gcc musl-dev RUN apk add --no-cache git gcc musl-dev

View File

@ -89,7 +89,7 @@ func (a *goBlog) apHandleWebfinger(w http.ResponseWriter, r *http.Request) {
return return
} }
apIri := a.apIri(blog) apIri := a.apIri(blog)
b, _ := json.Marshal(map[string]interface{}{ b, _ := json.Marshal(map[string]any{
"subject": a.webfingerAccts[apIri], "subject": a.webfingerAccts[apIri],
"aliases": []string{ "aliases": []string{
a.webfingerAccts[apIri], a.webfingerAccts[apIri],
@ -142,7 +142,7 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) {
return return
} }
// Parse activity // Parse activity
activity := map[string]interface{}{} activity := map[string]any{}
err = json.NewDecoder(r.Body).Decode(&activity) err = json.NewDecoder(r.Body).Decode(&activity)
_ = r.Body.Close() _ = r.Body.Close()
if err != nil { if err != nil {
@ -164,7 +164,7 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) {
case "Follow": case "Follow":
a.apAccept(blogName, blog, activity) a.apAccept(blogName, blog, activity)
case "Undo": 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"]) ot := cast.ToString(object["type"])
actor := cast.ToString(object["actor"]) actor := cast.ToString(object["actor"])
if ot == "Follow" && actor == activityActor { if ot == "Follow" && actor == activityActor {
@ -172,7 +172,7 @@ func (a *goBlog) apHandleInbox(w http.ResponseWriter, r *http.Request) {
} }
} }
case "Create": 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"]) baseUrl := cast.ToString(object["id"])
if ou := cast.ToString(object["url"]); ou != "" { if ou := cast.ToString(object["url"]); ou != "" {
baseUrl = ou baseUrl = ou
@ -297,7 +297,7 @@ func (db *database) apRemoveInbox(inbox string) error {
func (a *goBlog) apPost(p *post) { func (a *goBlog) apPost(p *post) {
n := a.toASNote(p) n := a.toASNote(p)
a.apSendToAllFollowers(p.Blog, map[string]interface{}{ a.apSendToAllFollowers(p.Blog, map[string]any{
"@context": []string{asContext}, "@context": []string{asContext},
"actor": a.apIri(a.cfg.Blogs[p.Blog]), "actor": a.apIri(a.cfg.Blogs[p.Blog]),
"id": a.fullPostURL(p), "id": a.fullPostURL(p),
@ -308,7 +308,7 @@ func (a *goBlog) apPost(p *post) {
} }
func (a *goBlog) apUpdate(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}, "@context": []string{asContext},
"actor": a.apIri(a.cfg.Blogs[p.Blog]), "actor": a.apIri(a.cfg.Blogs[p.Blog]),
"id": a.fullPostURL(p), "id": a.fullPostURL(p),
@ -319,7 +319,7 @@ func (a *goBlog) apUpdate(p *post) {
} }
func (a *goBlog) apDelete(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}, "@context": []string{asContext},
"actor": a.apIri(a.cfg.Blogs[p.Blog]), "actor": a.apIri(a.cfg.Blogs[p.Blog]),
"type": "Delete", "type": "Delete",
@ -328,11 +328,11 @@ func (a *goBlog) apDelete(p *post) {
} }
func (a *goBlog) apUndelete(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}, "@context": []string{asContext},
"actor": a.apIri(a.cfg.Blogs[p.Blog]), "actor": a.apIri(a.cfg.Blogs[p.Blog]),
"type": "Undo", "type": "Undo",
"object": map[string]interface{}{ "object": map[string]any{
"@context": []string{asContext}, "@context": []string{asContext},
"actor": a.apIri(a.cfg.Blogs[p.Blog]), "actor": a.apIri(a.cfg.Blogs[p.Blog]),
"type": "Delete", "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 // it's a follow, write it down
newFollower := follow["actor"].(string) newFollower := follow["actor"].(string)
log.Println("New follow request:", newFollower) log.Println("New follow request:", newFollower)
@ -365,7 +365,7 @@ func (a *goBlog) apAccept(blogName string, blog *configBlog, follow map[string]i
return return
} }
// Send accept response to the new follower // Send accept response to the new follower
accept := map[string]interface{}{ accept := map[string]any{
"@context": []string{asContext}, "@context": []string{asContext},
"type": "Accept", "type": "Accept",
"to": follow["actor"], "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) _ = 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) inboxes, err := a.db.apGetAllInboxes(blog)
if err != nil { if err != nil {
log.Println("Failed to retrieve inboxes:", err.Error()) 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) 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 { for _, i := range inboxes {
go func(inbox string) { go func(inbox string) {
_ = db.apQueueSendSigned(blogIri, inbox, activity) _ = db.apQueueSendSigned(blogIri, inbox, activity)

View File

@ -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) body, err := json.Marshal(activity)
if err != nil { if err != nil {
return err return err

View File

@ -38,7 +38,7 @@ func (a *goBlog) checkActivityStreamsRequest(next http.Handler) http.Handler {
} }
type asNote struct { type asNote struct {
Context interface{} `json:"@context,omitempty"` Context any `json:"@context,omitempty"`
To []string `json:"to,omitempty"` To []string `json:"to,omitempty"`
InReplyTo string `json:"inReplyTo,omitempty"` InReplyTo string `json:"inReplyTo,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@ -55,7 +55,7 @@ type asNote struct {
} }
type asPerson struct { type asPerson struct {
Context interface{} `json:"@context,omitempty"` Context any `json:"@context,omitempty"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`

View File

@ -11,7 +11,7 @@ import (
"github.com/carlmjohnson/requests" "github.com/carlmjohnson/requests"
"github.com/kaorimatz/go-opml" "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/bufferpool"
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
) )
@ -20,7 +20,7 @@ const defaultBlogrollPath = "/blogroll"
func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) {
blog, bc := a.getBlog(r) 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) return a.getBlogrollOutlines(blog)
}) })
if err != nil { 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) { func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) {
blog, _ := a.getBlog(r) 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) return a.getBlogrollOutlines(blog)
}) })
if err != nil { if err != nil {
@ -91,9 +91,9 @@ func (a *goBlog) getBlogrollOutlines(blog string) ([]*opml.Outline, error) {
if len(config.Categories) > 0 { if len(config.Categories) > 0 {
filtered := []*opml.Outline{} filtered := []*opml.Outline{}
for _, category := range config.Categories { 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 return outline.Title == category || outline.Text == category
}).(*opml.Outline); ok && outline != nil { }); ok && outline != nil {
outline.Outlines = sortOutlines(outline.Outlines) outline.Outlines = sortOutlines(outline.Outlines)
filtered = append(filtered, outline) filtered = append(filtered, outline)
} }

View File

@ -37,7 +37,7 @@ func (a *goBlog) serveBlogStats(w http.ResponseWriter, r *http.Request) {
func (a *goBlog) serveBlogStatsTable(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveBlogStatsTable(w http.ResponseWriter, r *http.Request) {
blog, _ := a.getBlog(r) 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) return a.db.getBlogStats(blog)
}) })
if err != nil { if err != nil {

View File

@ -76,7 +76,7 @@ func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler {
// Search and serve cache // Search and serve cache
key := cacheKey(r) key := cacheKey(r)
// Get cache or render it // 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 return a.cache.getCache(key, next, r), nil
}) })
ci := cacheInterface.(*cacheItem) ci := cacheInterface.(*cacheItem)

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/klauspost/compress/gzhttp" "github.com/klauspost/compress/gzhttp"
"github.com/samber/lo"
"go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/bufferpool"
"golang.org/x/sync/singleflight" "golang.org/x/sync/singleflight"
) )
@ -79,7 +80,7 @@ func (a *goBlog) checkLinks(w io.Writer, posts ...*post) error {
return return
} }
// Process link // Process link
r, err, _ := sg.Do(link.Second, func() (interface{}, error) { r, err, _ := sg.Do(link.Second, func() (any, error) {
// Check if already cached // Check if already cached
if mr, ok := sm.Load(link.Second); ok { if mr, ok := sm.Load(link.Second); ok {
return mr, nil return mr, nil
@ -131,9 +132,9 @@ func (a *goBlog) allLinks(posts ...*post) (allLinks []*stringPair, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, link := range links { allLinks = lo.Map(links, func(s string, _ int) *stringPair {
allLinks = append(allLinks, &stringPair{a.fullPostURL(p), link}) return &stringPair{a.fullPostURL(p), s}
} })
} }
return allLinks, nil return allLinks, nil
} }

View File

@ -102,7 +102,7 @@ type commentsRequestConfig struct {
offset, limit int offset, limit int
} }
func buildCommentsQuery(config *commentsRequestConfig) (query string, args []interface{}) { func buildCommentsQuery(config *commentsRequestConfig) (query string, args []any) {
queryBuilder := bufferpool.Get() queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder) defer bufferpool.Put(queryBuilder)
queryBuilder.WriteString("select id, target, name, website, comment from comments order by id desc") queryBuilder.WriteString("select id, target, name, website, comment from comments order by id desc")

View File

@ -5,26 +5,28 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"strconv" "strconv"
"sync"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/vcraescu/go-paginator" "github.com/vcraescu/go-paginator"
) )
type commentsPaginationAdapter struct { type commentsPaginationAdapter struct {
config *commentsRequestConfig config *commentsRequestConfig
nums int64 nums int64
db *database getNums sync.Once
db *database
} }
func (p *commentsPaginationAdapter) Nums() (int64, error) { func (p *commentsPaginationAdapter) Nums() (int64, error) {
if p.nums == 0 { p.getNums.Do(func() {
nums, _ := p.db.countComments(p.config) nums, _ := p.db.countComments(p.config)
p.nums = int64(nums) p.nums = int64(nums)
} })
return p.nums, nil 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 := *p.config
modifiedConfig.offset = offset modifiedConfig.offset = offset
modifiedConfig.limit = length modifiedConfig.limit = length

View File

@ -7,6 +7,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/samber/lo"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -370,9 +371,7 @@ func (a *goBlog) initConfig() error {
if a.cfg.DefaultBlog == "" { if a.cfg.DefaultBlog == "" {
if len(a.cfg.Blogs) == 1 { if len(a.cfg.Blogs) == 1 {
// Set default blog to the only blog that is configured // Set default blog to the only blog that is configured
for k := range a.cfg.Blogs { a.cfg.DefaultBlog = lo.Keys(a.cfg.Blogs)[0]
a.cfg.DefaultBlog = k
}
} else { } else {
return errors.New("no default blog configured") return errors.New("no default blog configured")
} }

View File

@ -66,7 +66,7 @@ func (a *goBlog) openDatabase(file string, logging bool) (*database, error) {
sql.Register(dbDriverName, &sqlite.SQLiteDriver{ sql.Register(dbDriverName, &sqlite.SQLiteDriver{
ConnectHook: func(c *sqlite.SQLiteConn) error { ConnectHook: func(c *sqlite.SQLiteConn) error {
// Register functions // Register functions
for n, f := range map[string]interface{}{ for n, f := range map[string]any{
"mdtext": a.renderText, "mdtext": a.renderText,
"tolocal": toLocalSafe, "tolocal": toLocalSafe,
"toutc": toUTCSafe, "toutc": toUTCSafe,
@ -174,14 +174,14 @@ func (db *database) close() error {
return db.db.Close() 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 { if db == nil || db.db == nil {
return nil, nil, errors.New("database not initialized") return nil, nil, errors.New("database not initialized")
} }
if len(args) > 0 && args[0] == dbNoCache { if len(args) > 0 && args[0] == dbNoCache {
return nil, args[1:], nil 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 // Look if statement already exists
st, ok := db.psc.Get(query) st, ok := db.psc.Get(query)
if ok { if ok {
@ -207,11 +207,11 @@ func (db *database) prepare(query string, args ...interface{}) (*sql.Stmt, []int
const dbNoCache = "nocache" 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...) 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 { if db == nil || db.db == nil {
return nil, errors.New("database not initialized") 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...) 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...) 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 { if db == nil || db.db == nil {
return nil, errors.New("database not initialized") return nil, errors.New("database not initialized")
} }
@ -253,11 +253,11 @@ func (db *database) queryContext(c context.Context, query string, args ...interf
return 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...) 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 { if db == nil || db.db == nil {
return nil, errors.New("database not initialized") return nil, errors.New("database not initialized")
} }

View File

@ -13,14 +13,14 @@ import (
const dbHooksBegin contextKey = "begin" 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 { if !db.debug {
return ctx return ctx
} }
return context.WithValue(ctx, dbHooksBegin, time.Now()) 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 { if !db.debug {
return return
} }
@ -55,7 +55,7 @@ func (db *database) dbAfter(ctx context.Context, query string, args ...interface
bufferpool.Put(logBuilder) bufferpool.Put(logBuilder)
} }
func argToString(arg interface{}) string { func argToString(arg any) string {
val := cast.ToString(arg) val := cast.ToString(arg)
if val == "" { if val == "" {
val = fmt.Sprintf("%v", arg) val = fmt.Sprintf("%v", arg)

View File

@ -14,7 +14,7 @@ import (
var dbMigrations embed.FS var dbMigrations embed.FS
func migrateDb(db *sql.DB, logging bool) error { 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 { err := fs.WalkDir(dbMigrations, "dbmigrations", func(path string, d fs.DirEntry, err error) error {
if err != nil || d.Type().IsDir() { if err != nil || d.Type().IsDir() {
return err return err
@ -39,7 +39,7 @@ func migrateDb(db *sql.DB, logging bool) error {
return err return err
} }
m, err := migrator.New( m, err := migrator.New(
migrator.WithLogger(migrator.LoggerFunc(func(s string, i ...interface{}) { migrator.WithLogger(migrator.LoggerFunc(func(s string, i ...any) {
if logging { if logging {
log.Printf(s, i) log.Printf(s, i)
} }

View File

@ -2,8 +2,8 @@ package main
import "log" import "log"
func (a *goBlog) debug(msg ...interface{}) { func (a *goBlog) debug(msg ...any) {
if a.cfg.Debug { if a.cfg.Debug {
log.Println(append([]interface{}{"Debug:"}, msg...)...) log.Println(append([]any{"Debug:"}, msg...)...)
} }
} }

View File

@ -94,7 +94,7 @@ func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) {
case "updatepost": case "updatepost":
buf := bufferpool.Get() buf := bufferpool.Get()
defer bufferpool.Put(buf) defer bufferpool.Put(buf)
err := json.NewEncoder(buf).Encode(map[string]interface{}{ err := json.NewEncoder(buf).Encode(map[string]any{
"action": actionUpdate, "action": actionUpdate,
"url": r.FormValue("url"), "url": r.FormValue("url"),
"replace": map[string][]string{ "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 { func (*goBlog) editorPostTemplate(blog string, bc *configBlog) string {
builder := bufferpool.Get() builder := bufferpool.Get()
defer bufferpool.Put(builder) defer bufferpool.Put(builder)
marsh := func(param string, i interface{}) { marsh := func(param string, i any) {
_ = yaml.NewEncoder(builder).Encode(map[string]interface{}{ _ = yaml.NewEncoder(builder).Encode(map[string]any{
param: i, param: i,
}) })
} }

View File

@ -4,7 +4,7 @@ import (
"net/http" "net/http"
"sort" "sort"
"github.com/thoas/go-funk" "github.com/samber/lo"
) )
func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) { 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) return files[i].Time.After(files[j].Time)
}) })
// Find uses // Find uses
fileNames, ok := funk.Map(files, func(f *mediaFile) string { fileNames := lo.Map(files, func(f *mediaFile, _ int) string {
return f.Name return f.Name
}).([]string) })
if !ok {
a.serveError(w, r, "Failed to get file names", http.StatusInternalServerError)
return
}
uses, err := a.db.usesOfMediaFile(fileNames...) uses, err := a.db.usesOfMediaFile(fileNames...)
if err != nil { if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError) a.serveError(w, r, err.Error(), http.StatusInternalServerError)

8
geo.go
View File

@ -10,7 +10,7 @@ import (
gogeouri "git.jlel.se/jlelse/go-geouri" gogeouri "git.jlel.se/jlelse/go-geouri"
"github.com/carlmjohnson/requests" "github.com/carlmjohnson/requests"
geojson "github.com/paulmach/go.geojson" geojson "github.com/paulmach/go.geojson"
"github.com/thoas/go-funk" "github.com/samber/lo"
"go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/bufferpool"
) )
@ -23,9 +23,9 @@ func (a *goBlog) geoTitle(g *gogeouri.Geo, lang string) string {
return "" return ""
} }
f := fc.Features[0] 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", ""), 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) { 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) rb := requests.URL("https://photon.komoot.io/reverse").Client(a.httpClient).UserAgent(appUserAgent).ToBytesBuffer(buf)
// Set parameters // Set parameters
rb.Param("lat", fmt.Sprintf("%v", lat)).Param("lon", fmt.Sprintf("%v", lon)) 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 // Do request
if err := rb.Fetch(context.Background()); err != nil { if err := rb.Fetch(context.Background()); err != nil {
return nil, err return nil, err

9
go.mod
View File

@ -1,6 +1,6 @@
module go.goblog.app/app module go.goblog.app/app
go 1.17 go 1.18
require ( require (
git.jlel.se/jlelse/go-geouri v0.0.0-20210525190615-a9c1d50f42d6 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/paulmach/go.geojson v1.4.0
github.com/posener/wstest v1.2.0 github.com/posener/wstest v1.2.0
github.com/pquerna/otp v1.3.0 github.com/pquerna/otp v1.3.0
github.com/samber/lo v1.10.1
github.com/schollz/sqlite3dump v1.3.1 github.com/schollz/sqlite3dump v1.3.1
github.com/snabb/sitemap v1.0.0 github.com/snabb/sitemap v1.0.0
github.com/spf13/cast v1.4.1 github.com/spf13/cast v1.4.1
github.com/spf13/viper v1.10.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/tdewolff/minify/v2 v2.10.0
github.com/thoas/go-funk v0.9.2
github.com/tkrajina/gpxgo v1.2.1 github.com/tkrajina/gpxgo v1.2.1
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2 github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
github.com/yuin/goldmark v1.4.10 github.com/yuin/goldmark v1.4.10
// master // master
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 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/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
@ -128,6 +128,7 @@ require (
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // 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/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect

19
go.sum
View File

@ -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 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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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 h1:5x5moCkCtDo5x8af62P9IOAYGQcYHtxz2QJ3x1DoCgY=
github.com/paulmach/go.geojson v1.4.0/go.mod h1:YaKx1hKpWF+T2oj2lFJPsW/t1Q5e1jQI61eoQSTwpIs= 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= 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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w= 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/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 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI= 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= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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.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.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 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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= 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/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 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= 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.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/thoas/go-funk v0.9.2/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/tkrajina/gpxgo v1.2.1 h1:MJJtT4Re5btDGg89brFDrUP3EWz+cBmyo8pQwV0ZOak= github.com/tkrajina/gpxgo v1.2.1 h1:MJJtT4Re5btDGg89brFDrUP3EWz+cBmyo8pQwV0ZOak=
github.com/tkrajina/gpxgo v1.2.1/go.mod h1:795sjVRFo5wWyN6oOZp0RYienGGBJjpAlgOz2nCngA0= github.com/tkrajina/gpxgo v1.2.1/go.mod h1:795sjVRFo5wWyN6oOZp0RYienGGBJjpAlgOz2nCngA0=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= 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-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-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-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-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 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-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-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-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-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/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= 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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.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.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.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-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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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 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-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/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 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -25,7 +25,7 @@ func (a *goBlog) postPostHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil { if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostPost { for _, cmdTmplString := range hc.PostPost {
go func(p *post, cmdTmplString string) { 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), "URL": a.fullPostURL(p),
"Post": p, "Post": p,
}) })
@ -42,7 +42,7 @@ func (a *goBlog) postUpdateHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil { if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostUpdate { for _, cmdTmplString := range hc.PostUpdate {
go func(p *post, cmdTmplString string) { 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), "URL": a.fullPostURL(p),
"Post": p, "Post": p,
}) })
@ -58,7 +58,7 @@ func (a *goBlog) postDeleteHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil { if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostDelete { for _, cmdTmplString := range hc.PostDelete {
go func(p *post, cmdTmplString string) { 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), "URL": a.fullPostURL(p),
"Post": p, "Post": p,
}) })
@ -74,7 +74,7 @@ func (a *goBlog) postUndeleteHooks(p *post) {
if hc := a.cfg.Hooks; hc != nil { if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostUndelete { for _, cmdTmplString := range hc.PostUndelete {
go func(p *post, cmdTmplString string) { 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), "URL": a.fullPostURL(p),
"Post": 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) cmdTmpl, err := template.New("cmd").Parse(tmpl)
if err != nil { if err != nil {
log.Println("Failed to parse cmd template:", err.Error()) log.Println("Failed to parse cmd template:", err.Error())

View File

@ -6,7 +6,6 @@ import (
"net/http" "net/http"
"github.com/carlmjohnson/requests" "github.com/carlmjohnson/requests"
"github.com/thoas/go-funk"
) )
// Implement support for the IndexNow protocol // Implement support for the IndexNow protocol
@ -78,7 +77,7 @@ func (a *goBlog) indexNowKey() []byte {
} }
if keyBytes == nil { if keyBytes == nil {
// Generate 128 character key with hexadecimal characters // Generate 128 character key with hexadecimal characters
keyBytes = []byte(funk.RandomString(128, []rune("0123456789abcdef"))) keyBytes = []byte(randomString(128, []rune("0123456789abcdef")))
// Store key in database // Store key in database
err = a.db.cachePersistently("indexnowkey", keyBytes) err = a.db.cachePersistently("indexnowkey", keyBytes)
if err != nil { if err != nil {

View File

@ -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...) log.Println(v...)
a.shutdown.ShutdownAndWait() a.shutdown.ShutdownAndWait()
os.Exit(1) os.Exit(1)

View File

@ -185,7 +185,7 @@ func (c *customRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
} }
hb := newHtmlBuilder(w) hb := newHtmlBuilder(w)
hb.writeElementOpen("a", "href", dest) 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 { if len(n.Title) > 0 {
imgEls = append(imgEls, "title", string(n.Title)) imgEls = append(imgEls, "title", string(n.Title))
} }

View File

@ -67,7 +67,7 @@ func (sp *shortpixel) compress(url string, upload mediaStorageSaveFunc, hc *http
URL("https://api.shortpixel.com/v2/reducer-sync.php"). URL("https://api.shortpixel.com/v2/reducer-sync.php").
Client(hc). Client(hc).
Method(http.MethodPost). Method(http.MethodPost).
BodyJSON(map[string]interface{}{ BodyJSON(map[string]any{
"key": sp.key, "key": sp.key,
"plugin_version": "GB001", "plugin_version": "GB001",
"lossy": 1, "lossy": 1,
@ -106,8 +106,8 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli
Client(hc). Client(hc).
Method(http.MethodPost). Method(http.MethodPost).
BasicAuth("api", tf.key). BasicAuth("api", tf.key).
BodyJSON(map[string]interface{}{ BodyJSON(map[string]any{
"source": map[string]interface{}{ "source": map[string]any{
"url": url, "url": url,
}, },
}). }).
@ -130,8 +130,8 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli
Client(hc). Client(hc).
Method(http.MethodPost). Method(http.MethodPost).
BasicAuth("api", tf.key). BasicAuth("api", tf.key).
BodyJSON(map[string]interface{}{ BodyJSON(map[string]any{
"resize": map[string]interface{}{ "resize": map[string]any{
"method": "fit", "method": "fit",
"width": defaultCompressionWidth, "width": defaultCompressionWidth,
"height": defaultCompressionHeight, "height": defaultCompressionHeight,

View File

@ -46,7 +46,7 @@ func Test_compress(t *testing.T) {
requestBody, _ := io.ReadAll(r.Body) requestBody, _ := io.ReadAll(r.Body)
defer r.Body.Close() defer r.Body.Close()
var requestJson map[string]interface{} var requestJson map[string]any
err := json.Unmarshal(requestBody, &requestJson) err := json.Unmarshal(requestBody, &requestJson)
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, requestJson) require.NotNil(t, requestJson)

View File

@ -11,8 +11,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/samber/lo"
"github.com/spf13/cast" "github.com/spf13/cast"
"github.com/thoas/go-funk"
"go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -21,7 +21,7 @@ import (
const micropubPath = "/micropub" const micropubPath = "/micropub"
func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
var result interface{} var result any
switch query := r.URL.Query(); query.Get("q") { switch query := r.URL.Query(); query.Get("q") {
case "config": case "config":
type micropubConfig struct { type micropubConfig struct {
@ -66,7 +66,7 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
} }
allCategories = append(allCategories, values...) allCategories = append(allCategories, values...)
} }
result = map[string]interface{}{"categories": allCategories} result = map[string]any{"categories": allCategories}
default: default:
a.serve404(w, r) a.serve404(w, r)
return return
@ -231,30 +231,30 @@ const (
) )
type microformatItem struct { type microformatItem struct {
Type []string `json:"type,omitempty"` Type []string `json:"type,omitempty"`
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
Action micropubAction `json:"action,omitempty"` Action micropubAction `json:"action,omitempty"`
Properties *microformatProperties `json:"properties,omitempty"` Properties *microformatProperties `json:"properties,omitempty"`
Replace map[string][]interface{} `json:"replace,omitempty"` Replace map[string][]any `json:"replace,omitempty"`
Add map[string][]interface{} `json:"add,omitempty"` Add map[string][]any `json:"add,omitempty"`
Delete interface{} `json:"delete,omitempty"` Delete any `json:"delete,omitempty"`
} }
type microformatProperties struct { type microformatProperties struct {
Name []string `json:"name,omitempty"` Name []string `json:"name,omitempty"`
Published []string `json:"published,omitempty"` Published []string `json:"published,omitempty"`
Updated []string `json:"updated,omitempty"` Updated []string `json:"updated,omitempty"`
PostStatus []string `json:"post-status,omitempty"` PostStatus []string `json:"post-status,omitempty"`
Visibility []string `json:"visibility,omitempty"` Visibility []string `json:"visibility,omitempty"`
Category []string `json:"category,omitempty"` Category []string `json:"category,omitempty"`
Content []string `json:"content,omitempty"` Content []string `json:"content,omitempty"`
URL []string `json:"url,omitempty"` URL []string `json:"url,omitempty"`
InReplyTo []string `json:"in-reply-to,omitempty"` InReplyTo []string `json:"in-reply-to,omitempty"`
LikeOf []string `json:"like-of,omitempty"` LikeOf []string `json:"like-of,omitempty"`
BookmarkOf []string `json:"bookmark-of,omitempty"` BookmarkOf []string `json:"bookmark-of,omitempty"`
MpSlug []string `json:"mp-slug,omitempty"` MpSlug []string `json:"mp-slug,omitempty"`
Photo []interface{} `json:"photo,omitempty"` Photo []any `json:"photo,omitempty"`
Audio []string `json:"audio,omitempty"` Audio []string `json:"audio,omitempty"`
} }
func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem) error { 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 { 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.PhotoParam] = append(entry.Parameters[a.cfg.Micropub.PhotoParam], theString)
entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam] = append(entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam], "") 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.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"])) 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]) == "" { if split := strings.Split(p.Content, "---\n"); len(split) >= 3 && strings.TrimSpace(split[0]) == "" {
// Contains frontmatter // Contains frontmatter
fm := split[1] fm := split[1]
meta := map[string]interface{}{} meta := map[string]any{}
err := yaml.Unmarshal([]byte(fm), &meta) err := yaml.Unmarshal([]byte(fm), &meta)
if err != nil { if err != nil {
return err return err
@ -340,7 +340,7 @@ func (a *goBlog) computeExtraPostParameters(p *post) error {
for key, value := range meta { for key, value := range meta {
// Delete existing content - replace // Delete existing content - replace
p.Parameters[key] = []string{} p.Parameters[key] = []string{}
if a, ok := value.([]interface{}); ok { if a, ok := value.([]any); ok {
for _, ae := range a { for _, ae := range a {
p.Parameters[key] = append(p.Parameters[key], cast.ToString(ae)) 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) 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 { if content, ok := replace["content"]; ok && len(content) > 0 {
p.Content = cast.ToStringSlice(content)[0] p.Content = cast.ToStringSlice(content)[0]
} }
@ -578,7 +578,7 @@ func (a *goBlog) micropubUpdateReplace(p *post, replace map[string][]interface{}
// TODO: photos // 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 { for key, value := range add {
switch key { switch key {
case "content": 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 { if del == nil {
return return
} }
deleteProperties, ok := del.([]interface{}) deleteProperties, ok := del.([]any)
if ok { if ok {
// Completely remove properties // Completely remove properties
for _, prop := range deleteProperties { for _, prop := range deleteProperties {
@ -637,7 +637,7 @@ func (a *goBlog) micropubUpdateDelete(p *post, del interface{}) {
// Return // Return
return return
} }
toDelete, ok := del.(map[string]interface{}) toDelete, ok := del.(map[string]any)
if ok { if ok {
// Only delete parts of properties // Only delete parts of properties
for key, values := range toDelete { for key, values := range toDelete {
@ -661,8 +661,8 @@ func (a *goBlog) micropubUpdateDelete(p *post, del interface{}) {
// TODO: Support partial deletes of more properties // TODO: Support partial deletes of more properties
case "category": case "category":
delValues := cast.ToStringSlice(values) delValues := cast.ToStringSlice(values)
p.Parameters[a.cfg.Micropub.CategoryParam] = funk.FilterString(p.Parameters[a.cfg.Micropub.CategoryParam], func(s string) bool { p.Parameters[a.cfg.Micropub.CategoryParam] = lo.Filter(p.Parameters[a.cfg.Micropub.CategoryParam], func(s string, _ int) bool {
return !funk.ContainsString(delValues, s) return !lo.Contains(delValues, s)
}) })
} }
} }

View File

@ -11,8 +11,8 @@ import (
func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
buf := bufferpool.Get() buf := bufferpool.Get()
defer bufferpool.Put(buf) defer bufferpool.Put(buf)
err := json.NewEncoder(buf).Encode(map[string]interface{}{ err := json.NewEncoder(buf).Encode(map[string]any{
"links": []map[string]interface{}{ "links": []map[string]any{
{ {
"href": a.getFullAddress("/nodeinfo"), "href": a.getFullAddress("/nodeinfo"),
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1", "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() buf := bufferpool.Get()
defer bufferpool.Put(buf) defer bufferpool.Put(buf)
err := json.NewEncoder(buf).Encode(map[string]interface{}{ err := json.NewEncoder(buf).Encode(map[string]any{
"version": "2.1", "version": "2.1",
"software": map[string]interface{}{ "software": map[string]any{
"name": "goblog", "name": "goblog",
"repository": "https://go.goblog.app/app", "repository": "https://go.goblog.app/app",
}, },
"usage": map[string]interface{}{ "usage": map[string]any{
"users": map[string]interface{}{ "users": map[string]any{
"total": len(a.cfg.Blogs), "total": len(a.cfg.Blogs),
}, },
"localPosts": localPosts, "localPosts": localPosts,
@ -52,7 +52,7 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
"micropub", "micropub",
"webmention", "webmention",
}, },
"metadata": map[string]interface{}{}, "metadata": map[string]any{},
}) })
if err != nil { if err != nil {
a.serveError(w, r, "", http.StatusInternalServerError) a.serveError(w, r, "", http.StatusInternalServerError)

View File

@ -56,7 +56,7 @@ type notificationsRequestConfig struct {
offset, limit int offset, limit int
} }
func buildNotificationsQuery(config *notificationsRequestConfig) (query string, args []interface{}) { func buildNotificationsQuery(config *notificationsRequestConfig) (query string, args []any) {
queryBuilder := bufferpool.Get() queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder) defer bufferpool.Put(queryBuilder)
queryBuilder.WriteString("select id, time, text from notifications order by id desc") 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 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 := *p.config
modifiedConfig.offset = offset modifiedConfig.offset = offset
modifiedConfig.limit = length modifiedConfig.limit = length

View File

@ -26,7 +26,7 @@ func (db *database) retrievePersistentCacheContext(c context.Context, key string
if db == nil { if db == nil {
return nil, errors.New("database is 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 { if row, err := db.queryRowContext(c, "select data from persistent_cache where key = @key", sql.Named("key", key)); err != nil {
return nil, err return nil, err
} else { } else {

View File

@ -6,7 +6,7 @@ import (
) )
var bufferPool = sync.Pool{ var bufferPool = sync.Pool{
New: func() interface{} { New: func() any {
return new(bytes.Buffer) return new(bytes.Buffer)
}, },
} }

View File

@ -32,7 +32,7 @@ func newConfig() *config {
} }
// SetOption implements renderer.SetOptioner. // 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) c.Config.SetOption(name, value)
} }

View File

@ -11,7 +11,7 @@ import (
"github.com/klauspost/compress/flate" "github.com/klauspost/compress/flate"
"github.com/klauspost/compress/gzip" "github.com/klauspost/compress/gzip"
"github.com/thoas/go-funk" "github.com/samber/lo"
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
) )
@ -48,7 +48,7 @@ type Compressor struct {
// The mapping of pooled encoders to pools. // The mapping of pooled encoders to pools.
pooledEncoders map[string]*sync.Pool pooledEncoders map[string]*sync.Pool
// The set of content types allowed to be compressed. // 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. // The list of encoders in order of decreasing precedence.
encodingPrecedence []string encodingPrecedence []string
// The compression level. // The compression level.
@ -62,8 +62,8 @@ type Compressor struct {
func NewCompressor(level int, types ...string) *Compressor { func NewCompressor(level int, types ...string) *Compressor {
// If types are provided, set those as the allowed types. If none are // If types are provided, set those as the allowed types. If none are
// provided, use the default list. // provided, use the default list.
allowedTypes := map[string]interface{}{} allowedTypes := map[string]any{}
for _, t := range funk.ShortIf(len(types) > 0, types, defaultCompressibleContentTypes).([]string) { for _, t := range lo.If(len(types) > 0, types).Else(defaultCompressibleContentTypes) {
allowedTypes[t] = nil allowedTypes[t] = nil
} }
@ -96,7 +96,7 @@ func (c *Compressor) SetEncoder(encoding string, fn EncoderFunc) {
delete(c.pooledEncoders, encoding) delete(c.pooledEncoders, encoding)
c.pooledEncoders[encoding] = &sync.Pool{ c.pooledEncoders[encoding] = &sync.Pool{
New: func() interface{} { New: func() any {
return fn(io.Discard, c.level) 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 // An EncoderFunc is a function that wraps the provided io.Writer with a
// streaming compression algorithm and returns it. // 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 // Find supported encoder by accepted list by precedence
for _, name := range cw.compressor.encodingPrecedence { for _, name := range cw.compressor.encodingPrecedence {
if matchAcceptEncoding(accepted, name) { if lo.Contains(accepted, name) {
if pool, ok := cw.compressor.pooledEncoders[name]; ok { if pool, ok := cw.compressor.pooledEncoders[name]; ok {
encoder := pool.Get().(compressWriter) encoder := pool.Get().(compressWriter)
cleanup := func() { cleanup := func() {

View File

@ -127,7 +127,7 @@ func (p *postPaginationAdapter) Nums() (int64, error) {
return p.nums, nil 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 := *p.config
modifiedConfig.offset = offset modifiedConfig.offset = offset
modifiedConfig.limit = length modifiedConfig.limit = length

View File

@ -10,7 +10,7 @@ import (
"time" "time"
"github.com/araddon/dateparse" "github.com/araddon/dateparse"
"github.com/thoas/go-funk" "github.com/samber/lo"
"go.goblog.app/app/pkgs/bufferpool" "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 p.Section = a.cfg.Blogs[p.Blog].DefaultSection
} }
if p.Slug == "" { if p.Slug == "" {
random := generateRandomString(5) p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), randomString(5))
p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random)
} }
published := timeNoErr(dateparse.ParseLocal(p.Published)) published := timeNoErr(dateparse.ParseLocal(p.Published))
pathTmplString := defaultIfEmpty( pathTmplString := defaultIfEmpty(
@ -100,7 +99,7 @@ func (a *goBlog) checkPost(p *post) (err error) {
} }
pathBuffer := bufferpool.Get() pathBuffer := bufferpool.Get()
defer bufferpool.Put(pathBuffer) defer bufferpool.Put(pathBuffer)
err = pathTmpl.Execute(pathBuffer, map[string]interface{}{ err = pathTmpl.Execute(pathBuffer, map[string]any{
"BlogPath": a.getRelativePath(p.Blog, ""), "BlogPath": a.getRelativePath(p.Blog, ""),
"Year": published.Year(), "Year": published.Year(),
"Month": int(published.Month()), "Month": int(published.Month()),
@ -173,7 +172,7 @@ func (db *database) savePost(p *post, o *postCreationOptions) error {
// Build SQL // Build SQL
sqlBuilder := bufferpool.Get() sqlBuilder := bufferpool.Get()
defer bufferpool.Put(sqlBuilder) defer bufferpool.Put(sqlBuilder)
var sqlArgs = []interface{}{dbNoCache} var sqlArgs = []any{dbNoCache}
// Start transaction // Start transaction
sqlBuilder.WriteString("begin;") sqlBuilder.WriteString("begin;")
// Delete old post // Delete old post
@ -297,7 +296,7 @@ func (db *database) replacePostParam(path, param string, values []string) error
// Build SQL // Build SQL
sqlBuilder := bufferpool.Get() sqlBuilder := bufferpool.Get()
defer bufferpool.Put(sqlBuilder) defer bufferpool.Put(sqlBuilder)
var sqlArgs = []interface{}{dbNoCache} var sqlArgs = []any{dbNoCache}
// Start transaction // Start transaction
sqlBuilder.WriteString("begin;") sqlBuilder.WriteString("begin;")
// Delete old post // Delete old post
@ -344,7 +343,7 @@ type postsRequestConfig struct {
withoutRenderedTitle bool 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() queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder) defer bufferpool.Put(queryBuilder)
// Selection // Selection
@ -461,7 +460,7 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err
return nil return nil
} }
// Build query // Build query
sqlArgs := make([]interface{}, 0) sqlArgs := make([]any, 0)
queryBuilder := bufferpool.Get() queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder) defer bufferpool.Put(queryBuilder)
queryBuilder.WriteString("select path, parameter, value from post_parameters where") 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) { func (a *goBlog) getRandomPostPath(blog string) (path string, err error) {
sections, ok := funk.Keys(a.cfg.Blogs[blog].Sections).([]string) sections := lo.Keys(a.cfg.Blogs[blog].Sections)
if !ok {
return "", errors.New("no sections")
}
query, params := buildPostsQuery(&postsRequestConfig{randomOrder: true, limit: 1, blog: blog, sections: sections}, "path") query, params := buildPostsQuery(&postsRequestConfig{randomOrder: true, limit: 1, blog: blog, sections: sections}, "path")
row, err := a.db.queryRow(query, params...) row, err := a.db.queryRow(query, params...)
if err != nil { if err != nil {
@ -634,7 +630,7 @@ group by name;
` `
func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) { func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) {
sqlArgs := []interface{}{dbNoCache} sqlArgs := []any{dbNoCache}
nameValues := bufferpool.Get() nameValues := bufferpool.Get()
for i, n := range names { for i, n := range names {
if i > 0 { if i > 0 {

View File

@ -211,7 +211,7 @@ func (a *goBlog) photoLinks(p *post) []string {
} }
func (p *post) contentWithParams() string { func (p *post) contentWithParams() string {
params := map[string]interface{}{} params := map[string]any{}
for k, v := range p.Parameters { for k, v := range p.Parameters {
if l := len(v); l == 1 { if l := len(v); l == 1 {
params[k] = v[0] params[k] = v[0]

View File

@ -13,7 +13,7 @@ type renderData struct {
TorAddress string TorAddress string
Blog *configBlog Blog *configBlog
User *configUser User *configUser
Data interface{} Data any
CommentsEnabled bool CommentsEnabled bool
WebmentionReceivingEnabled bool WebmentionReceivingEnabled bool
TorUsed bool TorUsed bool
@ -91,6 +91,6 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
} }
// Data // Data
if data.Data == nil { if data.Data == nil {
data.Data = map[string]interface{}{} data.Data = map[string]any{}
} }
} }

View File

@ -11,7 +11,7 @@ func (db *database) shortenPath(p string) (string, error) {
if p == "" { if p == "" {
return "", errors.New("empty path") 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 // Check if already cached
if spi, ok := db.spc.Get(p); ok { if spi, ok := db.spc.Get(p); ok {
return spi.(string), nil return spi.(string), nil

View File

@ -172,7 +172,7 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) {
a.writeSitemapXML(w, sm) 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) w.Header().Set(contentType, contenttype.XMLUTF8)
pipeReader, pipeWriter := io.Pipe() pipeReader, pipeWriter := io.Pipe()
go func() { go func() {

View File

@ -36,7 +36,7 @@ func (a *goBlog) getTailscaleListener(addr string) (net.Listener, error) {
a.tss = &tsnet.Server{ a.tss = &tsnet.Server{
Hostname: tsconfig.Hostname, Hostname: tsconfig.Hostname,
Dir: tailscaleDir, Dir: tailscaleDir,
Logf: func(format string, args ...interface{}) { Logf: func(format string, args ...any) {
log.Printf("tailscale: "+format, args...) log.Printf("tailscale: "+format, args...)
}, },
} }

10
tts.go
View File

@ -192,20 +192,20 @@ func (a *goBlog) createTTSAudio(lang, ssml string, w io.Writer) error {
} }
// Create request body // Create request body
body := map[string]interface{}{ body := map[string]any{
"audioConfig": map[string]interface{}{ "audioConfig": map[string]any{
"audioEncoding": "MP3", "audioEncoding": "MP3",
}, },
"input": map[string]interface{}{ "input": map[string]any{
"ssml": ssml, "ssml": ssml,
}, },
"voice": map[string]interface{}{ "voice": map[string]any{
"languageCode": lang, "languageCode": lang,
}, },
} }
// Do request // Do request
var response map[string]interface{} var response map[string]any
err := requests. err := requests.
URL("https://texttospeech.googleapis.com/v1beta1/text:synthesize"). URL("https://texttospeech.googleapis.com/v1beta1/text:synthesize").
Param("key", gctts.GoogleAPIKey). Param("key", gctts.GoogleAPIKey).

8
ui.go
View File

@ -7,7 +7,7 @@ import (
"github.com/hacdias/indieauth" "github.com/hacdias/indieauth"
"github.com/kaorimatz/go-opml" "github.com/kaorimatz/go-opml"
"github.com/mergestat/timediff" "github.com/mergestat/timediff"
"github.com/thoas/go-funk" "github.com/samber/lo"
) )
func (a *goBlog) renderEditorPreview(hb *htmlBuilder, bc *configBlog, p *post) { func (a *goBlog) renderEditorPreview(hb *htmlBuilder, bc *configBlog, p *post) {
@ -279,7 +279,7 @@ func (a *goBlog) renderSearch(hb *htmlBuilder, rd *renderData) {
// Form // Form
hb.writeElementOpen("form", "class", "fw p", "method", "post") hb.writeElementOpen("form", "class", "fw p", "method", "post")
// Search // Search
args := []interface{}{"type", "text", "name", "q", "required", ""} args := []any{"type", "text", "name", "q", "required", ""}
if sc.Placeholder != "" { if sc.Placeholder != "" {
args = append(args, "placeholder", a.renderMdTitle(sc.Placeholder)) args = append(args, "placeholder", a.renderMdTitle(sc.Placeholder))
} }
@ -882,7 +882,7 @@ func (a *goBlog) renderPost(hb *htmlBuilder, rd *renderData) {
// Post actions // Post actions
hb.writeElementOpen("div", "class", "actions") hb.writeElementOpen("div", "class", "actions")
// Share button // 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.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "share"))
hb.writeElementClose("a") hb.writeElementClose("a")
// Translate button // Translate button
@ -894,7 +894,7 @@ func (a *goBlog) renderPost(hb *htmlBuilder, rd *renderData) {
// Speak button // 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.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.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("script")
hb.writeElementClose("div") hb.writeElementClose("div")
// TTS // TTS

View File

@ -36,7 +36,7 @@ func (h *htmlBuilder) writeEscaped(s string) {
textTemplate.HTMLEscape(h, []byte(s)) 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(` `)
h.write(attr) h.write(attr)
h.write(`=`) 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(`<`)
h.write(tag) h.write(tag)
for i := 0; i < len(attrs); i += 2 { for i := 0; i < len(attrs); i += 2 {

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/rand"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
@ -23,7 +24,7 @@ import (
"github.com/c2h5oh/datasize" "github.com/c2h5oh/datasize"
tdl "github.com/mergestat/timediff/locale" tdl "github.com/mergestat/timediff/locale"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
"github.com/thoas/go-funk" "github.com/samber/lo"
"go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/bufferpool"
"golang.org/x/text/language" "golang.org/x/text/language"
) )
@ -55,10 +56,15 @@ func sortedStrings(s []string) []string {
return s return s
} }
const randomLetters = "abcdefghijklmnopqrstuvwxyz" var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyz")
func generateRandomString(chars int) string { func randomString(n int, allowedChars ...[]rune) string {
return funk.RandomString(chars, []rune(randomLetters)) 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 { func isAbsoluteURL(s string) bool {
@ -84,7 +90,7 @@ func allLinksFromHTML(r io.Reader, baseURL string) ([]string, error) {
} }
}) })
links, err = resolveURLReferences(baseURL, links...) links, err = resolveURLReferences(baseURL, links...)
return funk.UniqString(links), err return lo.Uniq(links), err
} }
func resolveURLReferences(base string, refs ...string) ([]string, error) { func resolveURLReferences(base string, refs ...string) ([]string, error) {
@ -212,8 +218,8 @@ func urlHasExt(rawUrl string, allowed ...string) (ext string, has bool) {
return "", false return "", false
} }
ext = ext[1:] ext = ext[1:]
allowed = funk.Map(allowed, strings.ToLower).([]string) allowed = lo.Map(allowed, func(t string, _ int) string { return strings.ToLower(t) })
return ext, funk.ContainsString(allowed, strings.ToLower(ext)) return ext, lo.Contains(allowed, strings.ToLower(ext))
} }
func mBytesString(size int64) string { func mBytesString(size int64) string {
@ -274,7 +280,10 @@ func cleanHTMLText(s string) string {
} }
func defaultIfEmpty(s, d 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 { func containsStrings(s string, subStrings ...string) bool {

View File

@ -12,6 +12,16 @@ func Test_urlize(t *testing.T) {
assert.Equal(t, "this-is-a-test", urlize("This Is A Test")) 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) { func Benchmark_urlize(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
urlize("äbc ef") urlize("äbc ef")
@ -23,8 +33,8 @@ func Test_sortedStrings(t *testing.T) {
} }
func Test_generateRandomString(t *testing.T) { func Test_generateRandomString(t *testing.T) {
assert.Len(t, generateRandomString(30), 30) assert.Len(t, randomString(30), 30)
assert.Len(t, generateRandomString(50), 50) assert.Len(t, randomString(50), 50)
} }
func Test_isAbsoluteURL(t *testing.T) { func Test_isAbsoluteURL(t *testing.T) {

View File

@ -226,7 +226,7 @@ type webmentionsRequestConfig struct {
submentions bool submentions bool
} }
func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []interface{}) { func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []any) {
queryBuilder := bufferpool.Get() queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder) defer bufferpool.Put(queryBuilder)
queryBuilder.WriteString("select id, source, target, url, created, title, content, author, status from webmentions ") queryBuilder.WriteString("select id, source, target, url, created, title, content, author, status from webmentions ")

View File

@ -6,28 +6,30 @@ import (
"net/url" "net/url"
"reflect" "reflect"
"strconv" "strconv"
"sync"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/vcraescu/go-paginator" "github.com/vcraescu/go-paginator"
) )
type webmentionPaginationAdapter struct { type webmentionPaginationAdapter struct {
config *webmentionsRequestConfig config *webmentionsRequestConfig
nums int64 nums int64
db *database getNums sync.Once
db *database
} }
var _ paginator.Adapter = &webmentionPaginationAdapter{} var _ paginator.Adapter = &webmentionPaginationAdapter{}
func (p *webmentionPaginationAdapter) Nums() (int64, error) { func (p *webmentionPaginationAdapter) Nums() (int64, error) {
if p.nums == 0 { p.getNums.Do(func() {
nums, _ := p.db.countWebmentions(p.config) nums, _ := p.db.countWebmentions(p.config)
p.nums = int64(nums) p.nums = int64(nums)
} })
return p.nums, nil 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 := *p.config
modifiedConfig.offset = offset modifiedConfig.offset = offset
modifiedConfig.limit = length modifiedConfig.limit = length

View File

@ -12,7 +12,7 @@ import (
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/carlmjohnson/requests" "github.com/carlmjohnson/requests"
"github.com/thoas/go-funk" "github.com/samber/lo"
"github.com/tomnomnom/linkheader" "github.com/tomnomnom/linkheader"
"go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/bufferpool"
) )
@ -44,7 +44,7 @@ func (a *goBlog) sendWebmentions(p *post) error {
if mpc := a.cfg.Micropub; mpc != nil { 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)) 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 == "" { if link == "" {
continue continue
} }

View File

@ -14,7 +14,7 @@ import (
"time" "time"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/thoas/go-funk" "github.com/samber/lo"
"go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
"willnorris.com/go/microformats" "willnorris.com/go/microformats"
@ -161,7 +161,7 @@ func (a *goBlog) verifyReader(m *mention, body io.Reader) error {
if err != nil { if err != nil {
return err 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 // Check if link belongs to installation
hasShortPrefix := a.cfg.Server.ShortPublicAddress != "" && strings.HasPrefix(s, a.cfg.Server.ShortPublicAddress) hasShortPrefix := a.cfg.Server.ShortPublicAddress != "" && strings.HasPrefix(s, a.cfg.Server.ShortPublicAddress)
hasLongPrefix := strings.HasPrefix(s, a.cfg.Server.PublicAddress) hasLongPrefix := strings.HasPrefix(s, a.cfg.Server.PublicAddress)