mirror of https://github.com/jlelse/GoBlog
Simplify activitypub followers listing, parse and save activitypub usernames in @user@example.org format
This commit is contained in:
parent
4407f0aae1
commit
fc8e8e9a9d
|
@ -247,7 +247,7 @@ func (a *goBlog) apOnCreateUpdate(blog *configBlog, requestActor *ap.Actor, acti
|
||||||
// It's a reply
|
// It's a reply
|
||||||
original := object.GetID().String()
|
original := object.GetID().String()
|
||||||
name := requestActor.Name.First().Value.String()
|
name := requestActor.Name.First().Value.String()
|
||||||
if username := requestActor.PreferredUsername.First().String(); name == "" && username != "" {
|
if username := apUsername(requestActor); name == "" && username != "" {
|
||||||
name = username
|
name = username
|
||||||
}
|
}
|
||||||
website := requestActor.GetLink().String()
|
website := requestActor.GetLink().String()
|
||||||
|
@ -388,27 +388,30 @@ func (db *database) apGetAllInboxes(blog string) (inboxes []string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type apFollower struct {
|
type apFollower struct {
|
||||||
follower, inbox string
|
follower, inbox, username string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) apGetAllFollowers(blog string) (followers []*apFollower, err error) {
|
func (db *database) apGetAllFollowers(blog string) (followers []*apFollower, err error) {
|
||||||
rows, err := db.Query("select follower, inbox from activitypub_followers where blog = @blog", sql.Named("blog", blog))
|
rows, err := db.Query("select follower, inbox, username from activitypub_followers where blog = @blog", sql.Named("blog", blog))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var follower, inbox string
|
var follower, inbox, username string
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err = rows.Scan(&follower, &inbox)
|
err = rows.Scan(&follower, &inbox, &username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
followers = append(followers, &apFollower{follower: follower, inbox: inbox})
|
followers = append(followers, &apFollower{follower: follower, inbox: inbox, username: username})
|
||||||
}
|
}
|
||||||
return followers, nil
|
return followers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) apAddFollower(blog, follower, inbox string) error {
|
func (db *database) apAddFollower(blog, follower, inbox, username string) error {
|
||||||
_, err := db.Exec("insert or replace into activitypub_followers (blog, follower, inbox) values (@blog, @follower, @inbox)", sql.Named("blog", blog), sql.Named("follower", follower), sql.Named("inbox", inbox))
|
_, err := db.Exec(
|
||||||
|
"insert or replace into activitypub_followers (blog, follower, inbox, username) values (@blog, @follower, @inbox, @username)",
|
||||||
|
sql.Named("blog", blog), sql.Named("follower", follower), sql.Named("inbox", inbox), sql.Named("username", username),
|
||||||
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +466,7 @@ func (a *goBlog) apUndelete(p *post) {
|
||||||
|
|
||||||
func (a *goBlog) apAccept(blogName, blogIri string, blog *configBlog, follow *ap.Activity) {
|
func (a *goBlog) apAccept(blogName, blogIri string, blog *configBlog, follow *ap.Activity) {
|
||||||
newFollower := follow.Actor.GetID()
|
newFollower := follow.Actor.GetID()
|
||||||
log.Println("New follow request:", newFollower.String())
|
log.Println("New follow request from follower id:", newFollower.String())
|
||||||
// Get remote actor
|
// Get remote actor
|
||||||
follower, status, err := a.apGetRemoteActor(newFollower.String(), blogIri)
|
follower, status, err := a.apGetRemoteActor(newFollower.String(), blogIri)
|
||||||
if err != nil || status != 0 {
|
if err != nil || status != 0 {
|
||||||
|
@ -479,7 +482,8 @@ func (a *goBlog) apAccept(blogName, blogIri string, blog *configBlog, follow *ap
|
||||||
if inbox == "" {
|
if inbox == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = a.db.apAddFollower(blogName, follower.GetID().String(), inbox.String()); err != nil {
|
username := apUsername(follower)
|
||||||
|
if err = a.db.apAddFollower(blogName, follower.GetID().String(), inbox.String(), username); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Send accept response to the new follower
|
// Send accept response to the new follower
|
||||||
|
@ -488,7 +492,7 @@ func (a *goBlog) apAccept(blogName, blogIri string, blog *configBlog, follow *ap
|
||||||
accept.Actor = a.apAPIri(blog)
|
accept.Actor = a.apAPIri(blog)
|
||||||
_ = a.apQueueSendSigned(a.apIri(blog), inbox.String(), accept)
|
_ = a.apQueueSendSigned(a.apIri(blog), inbox.String(), accept)
|
||||||
// Notification
|
// Notification
|
||||||
a.sendNotification(fmt.Sprintf("%s started following %s", newFollower.String(), a.apIri(blog)))
|
a.sendNotification(fmt.Sprintf("%s (%s) started following %s", username, follower.GetID().String(), a.apIri(blog)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) apSendProfileUpdates() {
|
func (a *goBlog) apSendProfileUpdates() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/araddon/dateparse"
|
"github.com/araddon/dateparse"
|
||||||
ct "github.com/elnormous/contenttype"
|
ct "github.com/elnormous/contenttype"
|
||||||
|
@ -151,3 +152,12 @@ func (a *goBlog) serveAPItem(item any, w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set(contentType, contenttype.ASUTF8)
|
w.Header().Set(contentType, contenttype.ASUTF8)
|
||||||
_ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary))
|
_ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apUsername(person *ap.Person) string {
|
||||||
|
preferredUsername := person.PreferredUsername.First().Value.String()
|
||||||
|
u, err := url.Parse(person.GetLink().String())
|
||||||
|
if err != nil || u == nil || u.Host == "" || preferredUsername == "" {
|
||||||
|
return person.GetLink().String()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("@%s@%s", preferredUsername, u.Host)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
ap "github.com/go-ap/activitypub"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_apUsername(t *testing.T) {
|
||||||
|
item, err := ap.UnmarshalJSON([]byte(`
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1"
|
||||||
|
],
|
||||||
|
"id": "https://example.org/users/user",
|
||||||
|
"type": "Person",
|
||||||
|
"preferredUsername": "user",
|
||||||
|
"name": "Example user",
|
||||||
|
"url": "https://example.org/@user"
|
||||||
|
}
|
||||||
|
`))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
actor, err := ap.ToActor(item)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
username := apUsername(actor)
|
||||||
|
assert.Equal(t, "@user@example.org", username)
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
alter table activitypub_followers add username text not null default "";
|
||||||
|
update activitypub_followers set username = follower;
|
8
go.mod
8
go.mod
|
@ -21,9 +21,9 @@ require (
|
||||||
github.com/elnormous/contenttype v1.0.3
|
github.com/elnormous/contenttype v1.0.3
|
||||||
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead
|
||||||
github.com/emersion/go-smtp v0.15.0
|
github.com/emersion/go-smtp v0.15.0
|
||||||
github.com/go-ap/activitypub v0.0.0-20221206062958-cae46e718d79
|
github.com/go-ap/activitypub v0.0.0-20221207073405-5d6d22cbc42e
|
||||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.8
|
||||||
github.com/go-fed/httpsig v1.1.0
|
github.com/go-fed/httpsig v1.1.0
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
@ -61,8 +61,8 @@ require (
|
||||||
github.com/yuin/goldmark v1.5.3
|
github.com/yuin/goldmark v1.5.3
|
||||||
// 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.3.0
|
golang.org/x/crypto v0.4.0
|
||||||
golang.org/x/net v0.3.0
|
golang.org/x/net v0.4.0
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/text v0.5.0
|
golang.org/x/text v0.5.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -124,14 +124,14 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/go-ap/activitypub v0.0.0-20221206062958-cae46e718d79 h1:iZ2ITdeyrEpQL0nOIWpdje6qtk3cBBuylB5fI7TI3Rc=
|
github.com/go-ap/activitypub v0.0.0-20221207073405-5d6d22cbc42e h1:Qb0THMsDaDvRqvAwUFzJmN6WRbweiHUOoy9nY6GCw2M=
|
||||||
github.com/go-ap/activitypub v0.0.0-20221206062958-cae46e718d79/go.mod h1:1oVD0h0aPT3OEE1ZoSUoym/UGKzxe+e0y8K2AkQ1Hqs=
|
github.com/go-ap/activitypub v0.0.0-20221207073405-5d6d22cbc42e/go.mod h1:1oVD0h0aPT3OEE1ZoSUoym/UGKzxe+e0y8K2AkQ1Hqs=
|
||||||
github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea h1:ywGtLGVjJjMrq4mu35Qmu+NtlhlTk/gTayE6Bb4tQZk=
|
github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea h1:ywGtLGVjJjMrq4mu35Qmu+NtlhlTk/gTayE6Bb4tQZk=
|
||||||
github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea/go.mod h1:SaTNjEEkp0q+w3pUS1ccyEL/lUrHteORlDq/e21mCc8=
|
github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea/go.mod h1:SaTNjEEkp0q+w3pUS1ccyEL/lUrHteORlDq/e21mCc8=
|
||||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw=
|
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw=
|
||||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
|
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
|
||||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
@ -418,8 +418,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
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-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||||
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=
|
||||||
|
@ -494,8 +494,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk=
|
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
|
25
ui.go
25
ui.go
|
@ -1627,30 +1627,15 @@ func (a *goBlog) renderActivityPubFollowers(hb *htmlbuilder.HtmlBuilder, rd *ren
|
||||||
hb.WriteElementClose("h1")
|
hb.WriteElementClose("h1")
|
||||||
|
|
||||||
// List followers
|
// List followers
|
||||||
hb.WriteElementOpen("table")
|
hb.WriteElementOpen("ul")
|
||||||
hb.WriteElementOpen("thead")
|
|
||||||
hb.WriteElementOpen("th", "class", "tal")
|
|
||||||
hb.WriteEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "apfollower"))
|
|
||||||
hb.WriteElementClose("th")
|
|
||||||
hb.WriteElementOpen("th", "class", "tar")
|
|
||||||
hb.WriteEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "apinbox"))
|
|
||||||
hb.WriteElementClose("th")
|
|
||||||
hb.WriteElementClose("thead")
|
|
||||||
hb.WriteElementOpen("tbody")
|
|
||||||
for _, follower := range aprd.followers {
|
for _, follower := range aprd.followers {
|
||||||
hb.WriteElementOpen("tr")
|
hb.WriteElementOpen("li")
|
||||||
hb.WriteElementOpen("td", "class", "tal")
|
|
||||||
hb.WriteElementOpen("a", "href", follower.follower, "target", "_blank")
|
hb.WriteElementOpen("a", "href", follower.follower, "target", "_blank")
|
||||||
hb.WriteEscaped(follower.follower)
|
hb.WriteEscaped(follower.username)
|
||||||
hb.WriteElementClose("a")
|
hb.WriteElementClose("a")
|
||||||
hb.WriteElementClose("td")
|
hb.WriteElementClose("li")
|
||||||
hb.WriteElementOpen("td", "class", "tar")
|
|
||||||
hb.WriteEscaped(follower.inbox)
|
|
||||||
hb.WriteElementClose("td")
|
|
||||||
hb.WriteElementClose("tr")
|
|
||||||
}
|
}
|
||||||
hb.WriteElementClose("tbody")
|
hb.WriteElementClose("ul")
|
||||||
hb.WriteElementClose("table")
|
|
||||||
|
|
||||||
hb.WriteElementClose("main")
|
hb.WriteElementClose("main")
|
||||||
},
|
},
|
||||||
|
|
19
utils.go
19
utils.go
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -404,21 +403,3 @@ func stringToInt(s string) int {
|
||||||
func loStringNotEmpty(s string, _ int) bool {
|
func loStringNotEmpty(s string, _ int) bool {
|
||||||
return s != ""
|
return s != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func mimeTypeFromUrl(urlString string) string {
|
|
||||||
parsedUrl, err := url.Parse(urlString)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
ext := path.Ext(parsedUrl.Path)
|
|
||||||
mimeType := mime.TypeByExtension(ext)
|
|
||||||
if mimeType == "" {
|
|
||||||
switch ext {
|
|
||||||
case ".jpg":
|
|
||||||
mimeType = "image/jpeg"
|
|
||||||
default:
|
|
||||||
mimeType = "image/" + strings.TrimPrefix(ext, ".")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mimeType
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -168,24 +167,3 @@ func Test_groupStrings(t *testing.T) {
|
||||||
assert.Equal(t, "H", groups[3].Identifier)
|
assert.Equal(t, "H", groups[3].Identifier)
|
||||||
assert.Equal(t, "🚴", groups[4].Identifier)
|
assert.Equal(t, "🚴", groups[4].Identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_mimeTypeFromUrl(t *testing.T) {
|
|
||||||
type test struct {
|
|
||||||
url string
|
|
||||||
want string
|
|
||||||
}
|
|
||||||
tests := []*test{
|
|
||||||
{url: "https://example.com/profile.jpg", want: "image/jpeg"},
|
|
||||||
{url: "https://example.com/profile.jpeg", want: "image/jpeg"},
|
|
||||||
{url: "https://example.com/profile.png", want: "image/png"},
|
|
||||||
{url: "https://example.com/profile.png?v=3", want: "image/png"},
|
|
||||||
{url: "/profile.png?v=3", want: "image/png"},
|
|
||||||
}
|
|
||||||
for i, tt := range tests {
|
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
|
||||||
if got := mimeTypeFromUrl(tt.url); got != tt.want {
|
|
||||||
t.Errorf("mimeTypeFromUrl() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue