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
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
}
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)

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)
if err != nil {
return err

View File

@ -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"`

View File

@ -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)
}

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) {
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 {

View File

@ -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)

View File

@ -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
}

View File

@ -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")

View File

@ -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

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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...)...)
}
}

View File

@ -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,
})
}

View File

@ -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)

8
geo.go
View File

@ -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

9
go.mod
View File

@ -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

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 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=

View File

@ -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())

View File

@ -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 {

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...)
a.shutdown.ShutdownAndWait()
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.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))
}

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").
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,

View File

@ -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)

View File

@ -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)
})
}
}

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

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

View File

@ -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)
}

View File

@ -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() {

View File

@ -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

View File

@ -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 {

View File

@ -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]

View File

@ -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{}
}
}

View File

@ -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

View File

@ -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() {

View File

@ -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...)
},
}

10
tts.go
View File

@ -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).

8
ui.go
View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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) {

View File

@ -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 ")

View File

@ -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

View File

@ -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
}

View File

@ -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)