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