diff --git a/avatar/gen.go b/avatar/gen.go index 5fbb164..78cd5a1 100644 --- a/avatar/gen.go +++ b/avatar/gen.go @@ -9,7 +9,7 @@ import ( "github.com/teacat/noire" ) -const avatarSVG = ` +const avatarLocalSVG = ` @@ -24,7 +24,23 @@ const avatarSVG = ` ` -func AvatarSVG(input string, size int) string { +const avatarRemoteSVG = ` + + + + + + + + + + + + + +` + +func AvatarSVG(input string, size int, local bool) string { primary := toColor(input) secondary := fmt.Sprintf("#%s", noire.NewHex(primary).Complement().Hex()) @@ -36,7 +52,10 @@ func AvatarSVG(input string, size int) string { "$WIDTH", strconv.Itoa(width), "$HEIGHT", strconv.Itoa(height), ) - return r.Replace(avatarSVG) + if local { + return r.Replace(avatarLocalSVG) + } + return r.Replace(avatarRemoteSVG) } func djb2(data []byte) int32 { diff --git a/fed/actor.go b/fed/actor.go index f747572..a0c962f 100644 --- a/fed/actor.go +++ b/fed/actor.go @@ -6,6 +6,7 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "strings" "go.uber.org/zap" @@ -103,7 +104,18 @@ func GetOrFetchActor(ctx context.Context, store storage.Storage, logger *zap.Log return nil, fmt.Errorf("no id found for actor") } - err = store.CreateActor(ctx, actorRowID, keyRowID, actorID, actorBody, keyID, keyPEM) + name, ok := storage.JSONString(actorPayload, "preferredUsername") + if !ok { + return nil, fmt.Errorf("no preferredUsername found for actor") + } + + u, err := url.Parse(actorID) + if err != nil { + return nil, err + } + domain := u.Hostname() + + err = store.CreateActor(ctx, actorRowID, keyRowID, actorID, actorBody, keyID, keyPEM, name, domain) if err != nil { return nil, err } @@ -162,7 +174,6 @@ func ActorsFromActivity(activity storage.Payload) []string { return actors } - func ActorsFromObject(obj storage.Payload) []string { var actors []string @@ -190,4 +201,4 @@ func ActorsFromObject(obj storage.Payload) []string { } return actors -} \ No newline at end of file +} diff --git a/job/webfinger.go b/job/webfinger.go index b33b482..a703417 100644 --- a/job/webfinger.go +++ b/job/webfinger.go @@ -2,7 +2,6 @@ package job import ( "context" - "strings" "time" "go.uber.org/zap" @@ -67,53 +66,6 @@ func (job *webfinger) work() error { return nil } - var actorID string - - if strings.HasPrefix(work, "https://") { - actorID = work - } - - if len(actorID) == 0 { - wfc := fed.WebFingerClient{ - HTTPClient: job.httpClient, - Logger: job.logger, - } - - wfp, err := wfc.Fetch(work) - if err != nil { - return err - } - - actorID, err = fed.ActorIDFromWebFingerPayload(wfp) - if err != nil { - return err - } - - job.logger.Debug("parsed actor id from webfinger payload", zap.String("actor", actorID)) - } - - count, err := job.storage.RowCount(job.ctx, `SELECT COUNT(*) FROM actors WHERE actor_id = $1`, actorID) - if err != nil { - return err - } - if count > 0 { - return nil - } - - ac := fed.ActorClient{ - HTTPClient: job.httpClient, - Logger: job.logger, - } - - actorBody, actorPayload, err := ac.Get(actorID) - if err != nil { - return err - } - - keyID, keyPEM, err := storage.KeyFromActor(actorPayload) - - actorRowID := storage.NewV4() - keyRowID := storage.NewV4() - - return job.storage.CreateActor(context.Background(), actorRowID, keyRowID, actorID, actorBody, keyID, keyPEM) + _, err = fed.GetOrFetchActor(context.Background(), job.storage, job.logger, job.httpClient, work) + return err } diff --git a/storage/actor.go b/storage/actor.go index 460fa29..e5296ad 100644 --- a/storage/actor.go +++ b/storage/actor.go @@ -18,7 +18,7 @@ import ( type ActorStorage interface { GetActor(ctx context.Context, id string) (Actor, error) GetActorByAlias(ctx context.Context, alias string) (Actor, error) - CreateActor(context.Context, uuid.UUID, uuid.UUID, string, string, string, string) error + CreateActor(context.Context, uuid.UUID, uuid.UUID, string, string, string, string, string, string) error GetKey(ctx context.Context, keyID string) (*Key, error) RecordActorAlias(ctx context.Context, actorID, alias string) error ActorPayloadsByActorID(ctx context.Context, actorIDs []string) ([]Payload, error) @@ -102,10 +102,10 @@ func (s pgStorage) GetKey(ctx context.Context, keyID string) (*Key, error) { return key, nil } -func (s pgStorage) CreateActor(ctx context.Context, actorRowID, keyRowID uuid.UUID, actorID, payload, keyID, pem string) error { +func (s pgStorage) CreateActor(ctx context.Context, actorRowID, keyRowID uuid.UUID, actorID, payload, keyID, pem, name, domain string) error { now := s.now() return runTransactionWithOptions(s.db, func(tx *sql.Tx) error { - _, err := tx.ExecContext(ctx, "INSERT INTO actors (id, actor_id, payload, created_at) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING", actorRowID, actorID, payload, now) + _, err := tx.ExecContext(ctx, "INSERT INTO actors (id, actor_id, payload, created_at, name, domain) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT DO NOTHING", actorRowID, actorID, payload, now, name, domain) if err != nil { return errors.NewInsertQueryFailedError(err) } diff --git a/templates/partials/activity_announce_note.html b/templates/partials/activity_announce_note.html index 230aba4..31e723d 100644 --- a/templates/partials/activity_announce_note.html +++ b/templates/partials/activity_announce_note.html @@ -8,13 +8,14 @@

+ icon Note by - {{- lookupStr $actors $note.author -}} + {{- $actors.Lookup "name" $note.author -}} boosted by - {{- lookupStr $actors $boost.announcer -}} + {{- $actors.Lookup "name" $boost.announcer -}} on {{ datetime $note.published_at }}

diff --git a/templates/partials/activity_create_note.html b/templates/partials/activity_create_note.html index 06b9dcc..254f80f 100644 --- a/templates/partials/activity_create_note.html +++ b/templates/partials/activity_create_note.html @@ -7,21 +7,22 @@

+ icon {{ if $note.in_reply_to }} - {{- lookupStr $actors $note.author -}} + {{- $actors.Lookup "name" $note.author -}} replied to {{ if $note.in_reply_to_author }} - {{- lookupStr $actors $note.in_reply_to_author -}} + {{- $actors.Lookup "name" $note.in_reply_to_author -}} {{ else }} a note {{ end }} {{ else }} - {{- lookupStr $actors $note.author -}} + {{- $actors.Lookup "name" $note.author -}} wrote {{ end }} on {{ datetime $note.published_at }} diff --git a/web/command.go b/web/command.go index 8dab19a..236d05e 100644 --- a/web/command.go +++ b/web/command.go @@ -210,9 +210,9 @@ func serverCommandAction(cliCtx *cli.Context) error { root.GET("/object/:object", h.getObject) root.GET("/tags/:tag", h.getTaggedObjects) - root.GET("/avatar/svg/:name", h.avatarSVG) + root.GET("/avatar/svg/:domain/:name", h.avatarSVG) if svgerConfig.Enabled { - root.GET("/avatar/png/:name", h.avatarPNG) + root.GET("/avatar/png/:domain/:name", h.avatarPNG) } authenticated := r.Group("/") diff --git a/web/handle_avatar.go b/web/handler_avatar.go similarity index 76% rename from web/handle_avatar.go rename to web/handler_avatar.go index 69fb331..d2588f6 100644 --- a/web/handle_avatar.go +++ b/web/handler_avatar.go @@ -47,10 +47,20 @@ func (h handler) avatarPNG(c *gin.Context) { func (h handler) avatar(c *gin.Context) ([]byte, error) { name := c.Param("name") - + domain := c.Param("domain") size := intParam(c, "size", 120) - exists, err := h.storage.RowCount(c.Request.Context(), `SELECT COUNT(*) FROM users WHERE name = $1`, name) + if len(name) == 0 { + name = domain + domain = h.domain + } + + if name == domain && name == "unknown" { + svg := avatar.AvatarSVG("unknown", size, false) + return []byte(svg), nil + } + + exists, err := h.storage.RowCount(c.Request.Context(), `SELECT COUNT(*) FROM actors WHERE name = $1 AND domain = $2`, name, domain) if err != nil { return nil, err } @@ -58,7 +68,7 @@ func (h handler) avatar(c *gin.Context) ([]byte, error) { return nil, errors.NewNotFoundError(nil) } - id := fmt.Sprintf("@%s@%s", name, h.domain) - svg := avatar.AvatarSVG(id, size) + id := fmt.Sprintf("@%s@%s", name, domain) + svg := avatar.AvatarSVG(id, size, domain == h.domain) return []byte(svg), nil } diff --git a/web/handler_feed.go b/web/handler_feed.go index ddafb6e..9842e4b 100644 --- a/web/handler_feed.go +++ b/web/handler_feed.go @@ -123,7 +123,7 @@ func (h handler) viewFeed(c *gin.Context) { h.hardFail(c, err) return } - allActors := gatherActors(actors) + allActors := h.gatherActors(actors) var pages []int for i := page - 3; i <= page+3; i++ { @@ -132,9 +132,8 @@ func (h handler) viewFeed(c *gin.Context) { } } data["pages"] = pages - actorNames := actorNames(allActors) - actorNames[string(userActorID)] = "You" - data["actors"] = actorNames + + data["actors"] = actorLookup{h.domain, allActors} if cont = h.saveSession(c, session); !cont { return @@ -215,7 +214,7 @@ func (h handler) viewMyFeed(c *gin.Context) { h.hardFail(c, err) return } - allActors := gatherActors(actors) + allActors := h.gatherActors(actors) var pages []int for i := page - 3; i <= page+3; i++ { @@ -224,9 +223,7 @@ func (h handler) viewMyFeed(c *gin.Context) { } } data["pages"] = pages - actorNames := actorNames(allActors) - actorNames[string(userActorID)] = "You" - data["actors"] = actorNames + data["actors"] = actorLookup{h.domain, allActors} if cont = h.saveSession(c, session); !cont { return @@ -234,7 +231,7 @@ func (h handler) viewMyFeed(c *gin.Context) { c.HTML(http.StatusOK, "feed", data) } -func gatherActors(actors []storage.Payload) map[string]map[string]string { +func (h handler) gatherActors(actors []storage.Payload) map[string]map[string]string { results := make(map[string]map[string]string) for _, actor := range actors { @@ -261,26 +258,50 @@ func gatherActors(actors []storage.Payload) map[string]map[string]string { summary["domain"] = domain summary["at"] = fmt.Sprintf("%s@%s", preferredUsername, domain) + summary["icon"] = fmt.Sprintf("https://%s/avatar/png/%s/%s", h.domain, domain, preferredUsername) + results[actorID] = summary } return results } -func actorNames(actors map[string]map[string]string) map[string]string { - names := make(map[string]string) - for actorID, actor := range actors { - names[actorID] = actorID - - if preferredUsername, ok := actor["preferred_username"]; ok { - names[actorID] = preferredUsername - } - if at, ok := actor["at"]; ok { - names[actorID] = at - } - if displayName, ok := actor["name"]; ok { - names[actorID] = displayName - } - } - return names +type actorLookup struct { + domain string + actors map[string]map[string]string +} + +func (al actorLookup) Lookup(focus string, actorID string) string { + switch focus { + case "icon": + actor, found := al.actors[actorID] + if found { + icon, found := actor["icon"] + if found { + return icon + } + username, foundA := actor["name"] + actorDomain, foundB := actor["domain"] + if foundA && foundB { + return fmt.Sprintf("https://%s/avatar/png/%s/%s", al.domain, actorDomain, username) + } + } + return fmt.Sprintf("https://%s/avatar/png/unknown/unknown", al.domain) + case "name": + actor, found := al.actors[actorID] + if found { + if displayName, ok := actor["name"]; ok { + return displayName + } + if at, ok := actor["at"]; ok { + return at + } + if preferredUsername, ok := actor["preferred_username"]; ok { + return preferredUsername + } + } + return actorID + default: + return fmt.Sprintf("unknown key: %s", focus) + } }