1package state
2
3import (
4 "context"
5 "crypto/rand"
6 "fmt"
7 "log"
8 "math/big"
9 "net/http"
10
11 "github.com/bluesky-social/indigo/atproto/identity"
12 "github.com/bluesky-social/indigo/atproto/syntax"
13 "github.com/go-chi/chi/v5"
14 "github.com/go-git/go-git/v5/plumbing/object"
15 "tangled.sh/tangled.sh/core/appview/db"
16 "tangled.sh/tangled.sh/core/appview/knotclient"
17 "tangled.sh/tangled.sh/core/appview/oauth"
18 "tangled.sh/tangled.sh/core/appview/pages/repoinfo"
19)
20
21func (s *State) fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {
22 repoName := chi.URLParam(r, "repo")
23 knot, ok := r.Context().Value("knot").(string)
24 if !ok {
25 log.Println("malformed middleware")
26 return nil, fmt.Errorf("malformed middleware")
27 }
28 id, ok := r.Context().Value("resolvedId").(identity.Identity)
29 if !ok {
30 log.Println("malformed middleware")
31 return nil, fmt.Errorf("malformed middleware")
32 }
33
34 repoAt, ok := r.Context().Value("repoAt").(string)
35 if !ok {
36 log.Println("malformed middleware")
37 return nil, fmt.Errorf("malformed middleware")
38 }
39
40 parsedRepoAt, err := syntax.ParseATURI(repoAt)
41 if err != nil {
42 log.Println("malformed repo at-uri")
43 return nil, fmt.Errorf("malformed middleware")
44 }
45
46 ref := chi.URLParam(r, "ref")
47
48 if ref == "" {
49 us, err := knotclient.NewUnsignedClient(knot, s.config.Core.Dev)
50 if err != nil {
51 return nil, err
52 }
53
54 defaultBranch, err := us.DefaultBranch(id.DID.String(), repoName)
55 if err != nil {
56 return nil, err
57 }
58
59 ref = defaultBranch.Branch
60 }
61
62 // pass through values from the middleware
63 description, ok := r.Context().Value("repoDescription").(string)
64 addedAt, ok := r.Context().Value("repoAddedAt").(string)
65
66 return &FullyResolvedRepo{
67 Knot: knot,
68 OwnerId: id,
69 RepoName: repoName,
70 RepoAt: parsedRepoAt,
71 Description: description,
72 CreatedAt: addedAt,
73 Ref: ref,
74 }, nil
75}
76
77func RolesInRepo(s *State, u *oauth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo {
78 if u != nil {
79 r := s.enforcer.GetPermissionsInRepo(u.Did, f.Knot, f.DidSlashRepo())
80 return repoinfo.RolesInRepo{r}
81 } else {
82 return repoinfo.RolesInRepo{}
83 }
84}
85
86func uniqueEmails(commits []*object.Commit) []string {
87 emails := make(map[string]struct{})
88 for _, commit := range commits {
89 if commit.Author.Email != "" {
90 emails[commit.Author.Email] = struct{}{}
91 }
92 if commit.Committer.Email != "" {
93 emails[commit.Committer.Email] = struct{}{}
94 }
95 }
96 var uniqueEmails []string
97 for email := range emails {
98 uniqueEmails = append(uniqueEmails, email)
99 }
100 return uniqueEmails
101}
102
103func balanceIndexItems(commitCount, branchCount, tagCount, fileCount int) (commitsTrunc int, branchesTrunc int, tagsTrunc int) {
104 if commitCount == 0 && tagCount == 0 && branchCount == 0 {
105 return
106 }
107
108 // typically 1 item on right side = 2 files in height
109 availableSpace := fileCount / 2
110
111 // clamp tagcount
112 if tagCount > 0 {
113 tagsTrunc = 1
114 availableSpace -= 1 // an extra subtracted for headers etc.
115 }
116
117 // clamp branchcount
118 if branchCount > 0 {
119 branchesTrunc = min(max(branchCount, 1), 2)
120 availableSpace -= branchesTrunc // an extra subtracted for headers etc.
121 }
122
123 // show
124 if commitCount > 0 {
125 commitsTrunc = max(availableSpace, 3)
126 }
127
128 return
129}
130
131func EmailToDidOrHandle(s *State, emails []string) map[string]string {
132 emailToDid, err := db.GetEmailToDid(s.db, emails, true) // only get verified emails for mapping
133 if err != nil {
134 log.Printf("error fetching dids for emails: %v", err)
135 return nil
136 }
137
138 var dids []string
139 for _, v := range emailToDid {
140 dids = append(dids, v)
141 }
142 resolvedIdents := s.resolver.ResolveIdents(context.Background(), dids)
143
144 didHandleMap := make(map[string]string)
145 for _, identity := range resolvedIdents {
146 if !identity.Handle.IsInvalidHandle() {
147 didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String())
148 } else {
149 didHandleMap[identity.DID.String()] = identity.DID.String()
150 }
151 }
152
153 // Create map of email to didOrHandle for commit display
154 emailToDidOrHandle := make(map[string]string)
155 for email, did := range emailToDid {
156 if didOrHandle, ok := didHandleMap[did]; ok {
157 emailToDidOrHandle[email] = didOrHandle
158 }
159 }
160
161 return emailToDidOrHandle
162}
163
164func randomString(n int) string {
165 const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
166 result := make([]byte, n)
167
168 for i := 0; i < n; i++ {
169 n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
170 result[i] = letters[n.Int64()]
171 }
172
173 return string(result)
174}