mirror of https://github.com/jlelse/GoBlog
Fix more linting issues and add tests
This commit is contained in:
parent
94dba7a9b8
commit
ffa4ba4ee0
|
@ -36,4 +36,6 @@ linters-settings:
|
|||
checks: ["all"]
|
||||
gostatichcheck:
|
||||
go: "1.17"
|
||||
checks: ["all"]
|
||||
checks: ["all"]
|
||||
dupl:
|
||||
threshold: 125
|
|
@ -164,54 +164,44 @@ 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 {
|
||||
ot := cast.ToString(object["type"])
|
||||
actor := cast.ToString(object["actor"])
|
||||
if ot == "Follow" && actor == activityActor {
|
||||
_ = a.db.apRemoveFollower(blogName, activityActor)
|
||||
}
|
||||
if object, ok := activity["object"].(map[string]interface{}); ok {
|
||||
ot := cast.ToString(object["type"])
|
||||
actor := cast.ToString(object["actor"])
|
||||
if ot == "Follow" && actor == activityActor {
|
||||
_ = a.db.apRemoveFollower(blogName, activityActor)
|
||||
}
|
||||
}
|
||||
case "Create":
|
||||
{
|
||||
if object, ok := activity["object"].(map[string]interface{}); ok {
|
||||
baseUrl := cast.ToString(object["id"])
|
||||
if ou := cast.ToString(object["url"]); ou != "" {
|
||||
baseUrl = ou
|
||||
}
|
||||
if r := cast.ToString(object["inReplyTo"]); r != "" && baseUrl != "" && strings.HasPrefix(r, blogIri) {
|
||||
// It's an ActivityPub reply; save reply as webmention
|
||||
_ = a.createWebmention(baseUrl, r)
|
||||
} else if content := cast.ToString(object["content"]); content != "" && baseUrl != "" {
|
||||
// May be a mention; find links to blog and save them as webmentions
|
||||
if links, err := allLinksFromHTMLString(content, baseUrl); err == nil {
|
||||
for _, link := range links {
|
||||
if strings.HasPrefix(link, a.cfg.Server.PublicAddress) {
|
||||
_ = a.createWebmention(baseUrl, link)
|
||||
}
|
||||
if object, ok := activity["object"].(map[string]interface{}); ok {
|
||||
baseUrl := cast.ToString(object["id"])
|
||||
if ou := cast.ToString(object["url"]); ou != "" {
|
||||
baseUrl = ou
|
||||
}
|
||||
if r := cast.ToString(object["inReplyTo"]); r != "" && baseUrl != "" && strings.HasPrefix(r, blogIri) {
|
||||
// It's an ActivityPub reply; save reply as webmention
|
||||
_ = a.createWebmention(baseUrl, r)
|
||||
} else if content := cast.ToString(object["content"]); content != "" && baseUrl != "" {
|
||||
// May be a mention; find links to blog and save them as webmentions
|
||||
if links, err := allLinksFromHTMLString(content, baseUrl); err == nil {
|
||||
for _, link := range links {
|
||||
if strings.HasPrefix(link, a.cfg.Server.PublicAddress) {
|
||||
_ = a.createWebmention(baseUrl, link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "Delete", "Block":
|
||||
{
|
||||
if o := cast.ToString(activity["object"]); o == activityActor {
|
||||
_ = a.db.apRemoveFollower(blogName, activityActor)
|
||||
}
|
||||
if o := cast.ToString(activity["object"]); o == activityActor {
|
||||
_ = a.db.apRemoveFollower(blogName, activityActor)
|
||||
}
|
||||
case "Like":
|
||||
{
|
||||
if o := cast.ToString(activity["object"]); o != "" && strings.HasPrefix(o, blogIri) {
|
||||
a.sendNotification(fmt.Sprintf("%s liked %s", activityActor, o))
|
||||
}
|
||||
if o := cast.ToString(activity["object"]); o != "" && strings.HasPrefix(o, blogIri) {
|
||||
a.sendNotification(fmt.Sprintf("%s liked %s", activityActor, o))
|
||||
}
|
||||
case "Announce":
|
||||
{
|
||||
if o := cast.ToString(activity["object"]); o != "" && strings.HasPrefix(o, blogIri) {
|
||||
a.sendNotification(fmt.Sprintf("%s announced %s", activityActor, o))
|
||||
}
|
||||
if o := cast.ToString(activity["object"]); o != "" && strings.HasPrefix(o, blogIri) {
|
||||
a.sendNotification(fmt.Sprintf("%s announced %s", activityActor, o))
|
||||
}
|
||||
}
|
||||
// Return 200
|
||||
|
|
|
@ -112,14 +112,12 @@ func Test_blogStats(t *testing.T) {
|
|||
// Test HTML
|
||||
|
||||
t.Run("Test stats table", func(t *testing.T) {
|
||||
h := http.HandlerFunc(app.serveBlogStatsTable)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/abc", nil)
|
||||
req = req.WithContext(context.WithValue(req.Context(), blogKey, "en"))
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
h(rec, req)
|
||||
app.serveBlogStatsTable(rec, req)
|
||||
|
||||
res := rec.Result()
|
||||
resBody, _ := io.ReadAll(res.Body)
|
||||
|
@ -132,14 +130,12 @@ func Test_blogStats(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("Test stats page", func(t *testing.T) {
|
||||
h := http.HandlerFunc(app.serveBlogStats)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/abc", nil)
|
||||
req = req.WithContext(context.WithValue(req.Context(), blogKey, "en"))
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
h(rec, req)
|
||||
app.serveBlogStats(rec, req)
|
||||
|
||||
res := rec.Result()
|
||||
resBody, _ := io.ReadAll(res.Body)
|
||||
|
|
10
cache.go
10
cache.go
|
@ -68,12 +68,10 @@ func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler {
|
|||
if cli, ok := r.Context().Value(cacheLoggedInKey).(bool); ok && cli {
|
||||
// Continue caching, but remove login
|
||||
setLoggedIn(r, false)
|
||||
} else {
|
||||
if a.isLoggedIn(r) {
|
||||
// Don't cache logged in requests
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
} else if a.isLoggedIn(r) {
|
||||
// Don't cache logged in requests
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
// Search and serve cache
|
||||
key := cacheKey(r)
|
||||
|
|
|
@ -37,10 +37,8 @@ func (p *commentsPaginationAdapter) Slice(offset, length int, data interface{})
|
|||
func (a *goBlog) commentsAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
commentsPath := r.Context().Value(pathKey).(string)
|
||||
// Adapter
|
||||
pageNoString := chi.URLParam(r, "page")
|
||||
pageNo, _ := strconv.Atoi(pageNoString)
|
||||
p := paginator.New(&commentsPaginationAdapter{config: &commentsRequestConfig{}, db: a.db}, 5)
|
||||
p.SetPage(pageNo)
|
||||
p.SetPage(stringToInt(chi.URLParam(r, "page")))
|
||||
var comments []*comment
|
||||
err := p.Results(&comments)
|
||||
if err != nil {
|
||||
|
|
|
@ -17,14 +17,14 @@ func initGC() {
|
|||
}
|
||||
|
||||
func doGC() {
|
||||
var old, new runtime.MemStats
|
||||
runtime.ReadMemStats(&old)
|
||||
var before, after runtime.MemStats
|
||||
runtime.ReadMemStats(&before)
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&new)
|
||||
runtime.ReadMemStats(&after)
|
||||
log.Println(fmt.Sprintf(
|
||||
"\nAlloc: %d MiB -> %d MiB\nSys: %d MiB -> %d MiB\nNumGC: %d",
|
||||
old.Alloc/1024/1024, new.Alloc/1024/1024,
|
||||
old.Sys/1024/1024, new.Sys/1024/1024,
|
||||
new.NumGC,
|
||||
before.Alloc/1024/1024, after.Alloc/1024/1024,
|
||||
before.Sys/1024/1024, after.Sys/1024/1024,
|
||||
after.NumGC,
|
||||
))
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -57,7 +57,7 @@ require (
|
|||
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
|
||||
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
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
|
|
3
go.sum
3
go.sum
|
@ -562,8 +562,9 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
|
@ -41,7 +41,7 @@ func (a *goBlog) indexNowEnabled() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (a *goBlog) serveIndexNow(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *goBlog) serveIndexNow(w http.ResponseWriter, _ *http.Request) {
|
||||
_, _ = w.Write(a.indexNowKey())
|
||||
}
|
||||
|
||||
|
|
78
micropub.go
78
micropub.go
|
@ -8,35 +8,29 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
const micropubPath = "/micropub"
|
||||
|
||||
type micropubConfig struct {
|
||||
MediaEndpoint string `json:"media-endpoint,omitempty"`
|
||||
}
|
||||
|
||||
func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Query().Get("q") {
|
||||
var result interface{}
|
||||
switch query := r.URL.Query(); query.Get("q") {
|
||||
case "config":
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
mw := a.min.Writer(contenttype.JSON, w)
|
||||
defer mw.Close()
|
||||
_ = json.NewEncoder(mw).Encode(µpubConfig{
|
||||
MediaEndpoint: a.getFullAddress(micropubPath + micropubMediaSubPath),
|
||||
})
|
||||
type micropubConfig struct {
|
||||
MediaEndpoint string `json:"media-endpoint"`
|
||||
}
|
||||
result = micropubConfig{MediaEndpoint: a.getFullAddress(micropubPath + micropubMediaSubPath)}
|
||||
case "source":
|
||||
var mf interface{}
|
||||
if urlString := r.URL.Query().Get("url"); urlString != "" {
|
||||
u, err := url.Parse(r.URL.Query().Get("url"))
|
||||
if urlString := query.Get("url"); urlString != "" {
|
||||
u, err := url.Parse(query.Get("url"))
|
||||
if err != nil {
|
||||
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
|
@ -46,13 +40,11 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
|||
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
mf = a.postToMfItem(p)
|
||||
result = a.postToMfItem(p)
|
||||
} else {
|
||||
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
|
||||
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
|
||||
posts, err := a.getPosts(&postsRequestConfig{
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
limit: stringToInt(query.Get("limit")),
|
||||
offset: stringToInt(query.Get("offset")),
|
||||
})
|
||||
if err != nil {
|
||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -62,12 +54,8 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
|||
for _, p := range posts {
|
||||
list["items"] = append(list["items"], a.postToMfItem(p))
|
||||
}
|
||||
mf = list
|
||||
result = list
|
||||
}
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
mw := a.min.Writer(contenttype.JSON, w)
|
||||
defer mw.Close()
|
||||
_ = json.NewEncoder(mw).Encode(mf)
|
||||
case "category":
|
||||
allCategories := []string{}
|
||||
for blog := range a.cfg.Blogs {
|
||||
|
@ -78,22 +66,28 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
allCategories = append(allCategories, values...)
|
||||
}
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
mw := a.min.Writer(contenttype.JSON, w)
|
||||
defer mw.Close()
|
||||
_ = json.NewEncoder(mw).Encode(map[string]interface{}{
|
||||
"categories": allCategories,
|
||||
})
|
||||
result = map[string]interface{}{"categories": allCategories}
|
||||
default:
|
||||
a.serve404(w, r)
|
||||
return
|
||||
}
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
mw := a.min.Writer(contenttype.JSON, buf)
|
||||
err := json.NewEncoder(mw).Encode(result)
|
||||
_ = mw.Close()
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to encode json", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
_, _ = buf.WriteTo(w)
|
||||
}
|
||||
|
||||
func (a *goBlog) serveMicropubPost(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
switch mt, _, _ := mime.ParseMediaType(r.Header.Get(contentType)); mt {
|
||||
case contenttype.WWWForm, contenttype.MultipartForm:
|
||||
_ = r.ParseForm()
|
||||
_ = r.ParseMultipartForm(0)
|
||||
if r.Form == nil {
|
||||
a.serveError(w, r, "Failed to parse form", http.StatusBadRequest)
|
||||
|
@ -442,9 +436,16 @@ func (a *goBlog) micropubCreatePostFromJson(w http.ResponseWriter, r *http.Reque
|
|||
a.micropubCreate(w, r, p)
|
||||
}
|
||||
|
||||
func (a *goBlog) micropubCheckScope(w http.ResponseWriter, r *http.Request, required string) bool {
|
||||
if !strings.Contains(r.Context().Value(indieAuthScope).(string), required) {
|
||||
a.serveError(w, r, required+" scope missing", http.StatusForbidden)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *goBlog) micropubCreate(w http.ResponseWriter, r *http.Request, p *post) {
|
||||
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "create") {
|
||||
a.serveError(w, r, "create scope missing", http.StatusForbidden)
|
||||
if !a.micropubCheckScope(w, r, "create") {
|
||||
return
|
||||
}
|
||||
if err := a.computeExtraPostParameters(p); err != nil {
|
||||
|
@ -459,8 +460,7 @@ func (a *goBlog) micropubCreate(w http.ResponseWriter, r *http.Request, p *post)
|
|||
}
|
||||
|
||||
func (a *goBlog) micropubDelete(w http.ResponseWriter, r *http.Request, u string) {
|
||||
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "delete") {
|
||||
a.serveError(w, r, "delete scope missing", http.StatusForbidden)
|
||||
if !a.micropubCheckScope(w, r, "delete") {
|
||||
return
|
||||
}
|
||||
uu, err := url.Parse(u)
|
||||
|
@ -476,8 +476,7 @@ func (a *goBlog) micropubDelete(w http.ResponseWriter, r *http.Request, u string
|
|||
}
|
||||
|
||||
func (a *goBlog) micropubUndelete(w http.ResponseWriter, r *http.Request, u string) {
|
||||
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "undelete") {
|
||||
a.serveError(w, r, "undelete scope missing", http.StatusForbidden)
|
||||
if !a.micropubCheckScope(w, r, "undelete") {
|
||||
return
|
||||
}
|
||||
uu, err := url.Parse(u)
|
||||
|
@ -493,8 +492,7 @@ func (a *goBlog) micropubUndelete(w http.ResponseWriter, r *http.Request, u stri
|
|||
}
|
||||
|
||||
func (a *goBlog) micropubUpdate(w http.ResponseWriter, r *http.Request, u string, mf *microformatItem) {
|
||||
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "update") {
|
||||
a.serveError(w, r, "update scope missing", http.StatusForbidden)
|
||||
if !a.micropubCheckScope(w, r, "update") {
|
||||
return
|
||||
}
|
||||
uu, err := url.Parse(u)
|
||||
|
|
|
@ -17,8 +17,7 @@ const micropubMediaSubPath = "/media"
|
|||
|
||||
func (a *goBlog) serveMicropubMedia(w http.ResponseWriter, r *http.Request) {
|
||||
// Check scope
|
||||
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "media") {
|
||||
a.serveError(w, r, "media scope missing", http.StatusForbidden)
|
||||
if !a.micropubCheckScope(w, r, "media") {
|
||||
return
|
||||
}
|
||||
// Check if request is multipart
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_micropubQuery(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
app.initComponents(false)
|
||||
|
||||
// Create a test post with tags
|
||||
err := app.createPost(&post{
|
||||
Path: "/test/post",
|
||||
Content: "Test post",
|
||||
Parameters: map[string][]string{
|
||||
"tags": {"test", "test2"},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
type testCase struct {
|
||||
query string
|
||||
want string
|
||||
wantStatus int
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
query: "config",
|
||||
want: "{\"media-endpoint\":\"http://localhost:8080/micropub/media\"}\n",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
query: "source&url=http://localhost:8080/test/post",
|
||||
want: "{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"]}}\n",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
query: "source",
|
||||
want: "{\"items\":[{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"]}}]}\n",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
query: "category",
|
||||
want: "{\"categories\":[\"test\",\"test2\"]}\n",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
query: "somethingelse",
|
||||
wantStatus: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://localhost:8080/micropub?q="+tc.query, nil)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
app.serveMicropubQuery(rec, req)
|
||||
rec.Flush()
|
||||
|
||||
assert.Equal(t, tc.wantStatus, rec.Code)
|
||||
if tc.want != "" {
|
||||
assert.Equal(t, tc.want, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -122,10 +122,8 @@ func (p *notificationsPaginationAdapter) Slice(offset, length int, data interfac
|
|||
|
||||
func (a *goBlog) notificationsAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
// Adapter
|
||||
pageNoString := chi.URLParam(r, "page")
|
||||
pageNo, _ := strconv.Atoi(pageNoString)
|
||||
p := paginator.New(¬ificationsPaginationAdapter{config: ¬ificationsRequestConfig{}, db: a.db}, 10)
|
||||
p.SetPage(pageNo)
|
||||
p.SetPage(stringToInt(chi.URLParam(r, "page")))
|
||||
var notifications []*notification
|
||||
err := p.Results(¬ifications)
|
||||
if err != nil {
|
||||
|
|
11
posts.go
11
posts.go
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -202,13 +201,13 @@ func (a *goBlog) serveDeleted(w http.ResponseWriter, r *http.Request) {
|
|||
func (a *goBlog) serveDate(w http.ResponseWriter, r *http.Request) {
|
||||
var year, month, day int
|
||||
if ys := chi.URLParam(r, "year"); ys != "" && ys != "x" {
|
||||
year, _ = strconv.Atoi(ys)
|
||||
year = stringToInt(ys)
|
||||
}
|
||||
if ms := chi.URLParam(r, "month"); ms != "" && ms != "x" {
|
||||
month, _ = strconv.Atoi(ms)
|
||||
month = stringToInt(ms)
|
||||
}
|
||||
if ds := chi.URLParam(r, "day"); ds != "" {
|
||||
day, _ = strconv.Atoi(ds)
|
||||
day = stringToInt(ds)
|
||||
}
|
||||
if year == 0 && month == 0 && day == 0 {
|
||||
a.serve404(w, r)
|
||||
|
@ -270,8 +269,6 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) {
|
|||
// Decode and sanitize search
|
||||
search = cleanHTMLText(searchDecode(search))
|
||||
}
|
||||
pageNoString := chi.URLParam(r, "page")
|
||||
pageNo, _ := strconv.Atoi(pageNoString)
|
||||
var sections []string
|
||||
if ic.section != nil {
|
||||
sections = []string{ic.section.Name}
|
||||
|
@ -300,7 +297,7 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) {
|
|||
statusse: statusse,
|
||||
priorityOrder: true,
|
||||
}, a: a}, bc.Pagination)
|
||||
p.SetPage(pageNo)
|
||||
p.SetPage(stringToInt(chi.URLParam(r, "page")))
|
||||
var posts []*post
|
||||
err := p.Results(&posts)
|
||||
if err != nil {
|
||||
|
|
|
@ -122,9 +122,5 @@ func (a *goBlog) initChromaCSS() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = a.compileAsset(chromaPath, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return a.compileAsset(chromaPath, buf)
|
||||
}
|
||||
|
|
22
ui.go
22
ui.go
|
@ -471,21 +471,13 @@ func (a *goBlog) renderBlogStatsTable(hb *htmlBuilder, rd *renderData) {
|
|||
hb.writeElementOpen("th", "class", "tar")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "posts"))
|
||||
hb.writeElementClose("th")
|
||||
// Chars
|
||||
hb.writeElementOpen("th", "class", "tar")
|
||||
hb.write("~")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "chars"))
|
||||
hb.writeElementClose("th")
|
||||
// Words
|
||||
hb.writeElementOpen("th", "class", "tar")
|
||||
hb.write("~")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "words"))
|
||||
hb.writeElementClose("th")
|
||||
// Words/post
|
||||
hb.writeElementOpen("th", "class", "tar")
|
||||
hb.write("~")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "wordsperpost"))
|
||||
hb.writeElementClose("th")
|
||||
// Chars, Words, Words/Post
|
||||
for _, s := range []string{"chars", "words", "wordsperpost"} {
|
||||
hb.writeElementOpen("th", "class", "tar")
|
||||
hb.write("~")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, s))
|
||||
hb.writeElementClose("th")
|
||||
}
|
||||
hb.writeElementClose("thead")
|
||||
// Table body
|
||||
hb.writeElementOpen("tbody")
|
||||
|
|
10
utils.go
10
utils.go
|
@ -12,6 +12,7 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -211,9 +212,7 @@ func urlHasExt(rawUrl string, allowed ...string) (ext string, has bool) {
|
|||
return "", false
|
||||
}
|
||||
ext = ext[1:]
|
||||
allowed = funk.Map(allowed, func(str string) string {
|
||||
return strings.ToLower(str)
|
||||
}).([]string)
|
||||
allowed = funk.Map(allowed, strings.ToLower).([]string)
|
||||
return ext, funk.ContainsString(allowed, strings.ToLower(ext))
|
||||
}
|
||||
|
||||
|
@ -375,3 +374,8 @@ func matchTimeDiffLocale(lang string) tdl.Locale {
|
|||
timeDiffLocaleMap[lang] = locale
|
||||
return locale
|
||||
}
|
||||
|
||||
func stringToInt(s string) int {
|
||||
i, _ := strconv.Atoi(s)
|
||||
return i
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ func (p *webmentionPaginationAdapter) Slice(offset, length int, data interface{}
|
|||
}
|
||||
|
||||
func (a *goBlog) webmentionAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
pageNo, _ := strconv.Atoi(chi.URLParam(r, "page"))
|
||||
var status webmentionStatus = ""
|
||||
switch webmentionStatus(r.URL.Query().Get("status")) {
|
||||
case webmentionStatusVerified:
|
||||
|
@ -51,7 +50,7 @@ func (a *goBlog) webmentionAdmin(w http.ResponseWriter, r *http.Request) {
|
|||
status: status,
|
||||
sourcelike: sourcelike,
|
||||
}, db: a.db}, 5)
|
||||
p.SetPage(pageNo)
|
||||
p.SetPage(stringToInt(chi.URLParam(r, "page")))
|
||||
var mentions []*mention
|
||||
err := p.Results(&mentions)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue