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