forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
1package state
2
3import (
4 "crypto/hmac"
5 "crypto/sha256"
6 "encoding/hex"
7 "fmt"
8 "log"
9 "net/http"
10
11 "github.com/bluesky-social/indigo/atproto/identity"
12 "github.com/go-chi/chi/v5"
13 "tangled.sh/tangled.sh/core/appview/db"
14 "tangled.sh/tangled.sh/core/appview/pages"
15)
16
17func (s *State) ProfilePage(w http.ResponseWriter, r *http.Request) {
18 didOrHandle := chi.URLParam(r, "user")
19 if didOrHandle == "" {
20 http.Error(w, "Bad request", http.StatusBadRequest)
21 return
22 }
23
24 ident, ok := r.Context().Value("resolvedId").(identity.Identity)
25 if !ok {
26 s.pages.Error404(w)
27 return
28 }
29
30 repos, err := db.GetAllReposByDid(s.db, ident.DID.String())
31 if err != nil {
32 log.Printf("getting repos for %s: %s", ident.DID.String(), err)
33 }
34
35 collaboratingRepos, err := db.CollaboratingIn(s.db, ident.DID.String())
36 if err != nil {
37 log.Printf("getting collaborating repos for %s: %s", ident.DID.String(), err)
38 }
39
40 timeline, err := db.MakeProfileTimeline(s.db, ident.DID.String())
41 if err != nil {
42 log.Printf("failed to create profile timeline for %s: %s", ident.DID.String(), err)
43 }
44
45 var didsToResolve []string
46 for _, r := range collaboratingRepos {
47 didsToResolve = append(didsToResolve, r.Did)
48 }
49 for _, byMonth := range timeline.ByMonth {
50 for _, pe := range byMonth.PullEvents.Items {
51 didsToResolve = append(didsToResolve, pe.Repo.Did)
52 }
53 for _, ie := range byMonth.IssueEvents.Items {
54 didsToResolve = append(didsToResolve, ie.Metadata.Repo.Did)
55 }
56 for _, re := range byMonth.RepoEvents {
57 didsToResolve = append(didsToResolve, re.Repo.Did)
58 if re.Source != nil {
59 didsToResolve = append(didsToResolve, re.Source.Did)
60 }
61 }
62 }
63
64 resolvedIds := s.resolver.ResolveIdents(r.Context(), didsToResolve)
65 didHandleMap := make(map[string]string)
66 for _, identity := range resolvedIds {
67 if !identity.Handle.IsInvalidHandle() {
68 didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String())
69 } else {
70 didHandleMap[identity.DID.String()] = identity.DID.String()
71 }
72 }
73
74 followers, following, err := db.GetFollowerFollowing(s.db, ident.DID.String())
75 if err != nil {
76 log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err)
77 }
78
79 loggedInUser := s.auth.GetUser(r)
80 followStatus := db.IsNotFollowing
81 if loggedInUser != nil {
82 followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String())
83 }
84
85 profileAvatarUri := s.GetAvatarUri(ident.Handle.String())
86 s.pages.ProfilePage(w, pages.ProfilePageParams{
87 LoggedInUser: loggedInUser,
88 UserDid: ident.DID.String(),
89 UserHandle: ident.Handle.String(),
90 Repos: repos,
91 CollaboratingRepos: collaboratingRepos,
92 ProfileStats: pages.ProfileStats{
93 Followers: followers,
94 Following: following,
95 },
96 FollowStatus: db.FollowStatus(followStatus),
97 DidHandleMap: didHandleMap,
98 AvatarUri: profileAvatarUri,
99 ProfileTimeline: timeline,
100 })
101}
102
103func (s *State) GetAvatarUri(handle string) string {
104 secret := s.config.AvatarSharedSecret
105 h := hmac.New(sha256.New, []byte(secret))
106 h.Write([]byte(handle))
107 signature := hex.EncodeToString(h.Sum(nil))
108 return fmt.Sprintf("%s/%s/%s", s.config.AvatarHost, signature, handle)
109}