+208
appview/repo/index.go
+208
appview/repo/index.go
···
1
+
package repo
2
+
3
+
import (
4
+
"encoding/json"
5
+
"fmt"
6
+
"log"
7
+
"net/http"
8
+
"slices"
9
+
"strings"
10
+
11
+
"tangled.sh/tangled.sh/core/appview/commitverify"
12
+
"tangled.sh/tangled.sh/core/appview/db"
13
+
"tangled.sh/tangled.sh/core/appview/oauth"
14
+
"tangled.sh/tangled.sh/core/appview/pages"
15
+
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
16
+
"tangled.sh/tangled.sh/core/appview/reporesolver"
17
+
"tangled.sh/tangled.sh/core/knotclient"
18
+
"tangled.sh/tangled.sh/core/types"
19
+
20
+
"github.com/go-chi/chi/v5"
21
+
)
22
+
23
+
func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) {
24
+
ref := chi.URLParam(r, "ref")
25
+
f, err := rp.repoResolver.Resolve(r)
26
+
if err != nil {
27
+
log.Println("failed to fully resolve repo", err)
28
+
return
29
+
}
30
+
31
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
32
+
if err != nil {
33
+
log.Printf("failed to create unsigned client for %s", f.Knot)
34
+
rp.pages.Error503(w)
35
+
return
36
+
}
37
+
38
+
result, err := us.Index(f.OwnerDid(), f.RepoName, ref)
39
+
if err != nil {
40
+
rp.pages.Error503(w)
41
+
log.Println("failed to reach knotserver", err)
42
+
return
43
+
}
44
+
45
+
tagMap := make(map[string][]string)
46
+
for _, tag := range result.Tags {
47
+
hash := tag.Hash
48
+
if tag.Tag != nil {
49
+
hash = tag.Tag.Target.String()
50
+
}
51
+
tagMap[hash] = append(tagMap[hash], tag.Name)
52
+
}
53
+
54
+
for _, branch := range result.Branches {
55
+
hash := branch.Hash
56
+
tagMap[hash] = append(tagMap[hash], branch.Name)
57
+
}
58
+
59
+
slices.SortFunc(result.Branches, func(a, b types.Branch) int {
60
+
if a.Name == result.Ref {
61
+
return -1
62
+
}
63
+
if a.IsDefault {
64
+
return -1
65
+
}
66
+
if b.IsDefault {
67
+
return 1
68
+
}
69
+
if a.Commit != nil && b.Commit != nil {
70
+
if a.Commit.Committer.When.Before(b.Commit.Committer.When) {
71
+
return 1
72
+
} else {
73
+
return -1
74
+
}
75
+
}
76
+
return strings.Compare(a.Name, b.Name) * -1
77
+
})
78
+
79
+
commitCount := len(result.Commits)
80
+
branchCount := len(result.Branches)
81
+
tagCount := len(result.Tags)
82
+
fileCount := len(result.Files)
83
+
84
+
commitCount, branchCount, tagCount = balanceIndexItems(commitCount, branchCount, tagCount, fileCount)
85
+
commitsTrunc := result.Commits[:min(commitCount, len(result.Commits))]
86
+
tagsTrunc := result.Tags[:min(tagCount, len(result.Tags))]
87
+
branchesTrunc := result.Branches[:min(branchCount, len(result.Branches))]
88
+
89
+
emails := uniqueEmails(commitsTrunc)
90
+
emailToDidMap, err := db.GetEmailToDid(rp.db, emails, true)
91
+
if err != nil {
92
+
log.Println("failed to get email to did map", err)
93
+
}
94
+
95
+
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc)
96
+
if err != nil {
97
+
log.Println(err)
98
+
}
99
+
100
+
user := rp.oauth.GetUser(r)
101
+
repoInfo := f.RepoInfo(user)
102
+
103
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
104
+
if err != nil {
105
+
log.Printf("failed to get registration key for %s: %s", f.Knot, err)
106
+
rp.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
107
+
}
108
+
109
+
signedClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
110
+
if err != nil {
111
+
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
112
+
return
113
+
}
114
+
115
+
var forkInfo *types.ForkInfo
116
+
if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) {
117
+
forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient)
118
+
if err != nil {
119
+
log.Printf("Failed to fetch fork information: %v", err)
120
+
return
121
+
}
122
+
}
123
+
124
+
repoLanguages, err := signedClient.RepoLanguages(f.OwnerDid(), f.RepoName, ref)
125
+
if err != nil {
126
+
log.Printf("failed to compute language percentages: %s", err)
127
+
// non-fatal
128
+
}
129
+
130
+
rp.pages.RepoIndexPage(w, pages.RepoIndexParams{
131
+
LoggedInUser: user,
132
+
RepoInfo: repoInfo,
133
+
TagMap: tagMap,
134
+
RepoIndexResponse: *result,
135
+
CommitsTrunc: commitsTrunc,
136
+
TagsTrunc: tagsTrunc,
137
+
ForkInfo: forkInfo,
138
+
BranchesTrunc: branchesTrunc,
139
+
EmailToDidOrHandle: emailToDidOrHandle(rp, emailToDidMap),
140
+
VerifiedCommits: vc,
141
+
Languages: repoLanguages,
142
+
})
143
+
return
144
+
}
145
+
146
+
func getForkInfo(
147
+
repoInfo repoinfo.RepoInfo,
148
+
rp *Repo,
149
+
f *reporesolver.ResolvedRepo,
150
+
user *oauth.User,
151
+
signedClient *knotclient.SignedClient,
152
+
) (*types.ForkInfo, error) {
153
+
if user == nil {
154
+
return nil, nil
155
+
}
156
+
157
+
forkInfo := types.ForkInfo{
158
+
IsFork: repoInfo.Source != nil,
159
+
Status: types.UpToDate,
160
+
}
161
+
162
+
if !forkInfo.IsFork {
163
+
forkInfo.IsFork = false
164
+
return &forkInfo, nil
165
+
}
166
+
167
+
us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, rp.config.Core.Dev)
168
+
if err != nil {
169
+
log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot)
170
+
return nil, err
171
+
}
172
+
173
+
result, err := us.Branches(repoInfo.Source.Did, repoInfo.Source.Name)
174
+
if err != nil {
175
+
log.Println("failed to reach knotserver", err)
176
+
return nil, err
177
+
}
178
+
179
+
if !slices.ContainsFunc(result.Branches, func(branch types.Branch) bool {
180
+
return branch.Name == f.Ref
181
+
}) {
182
+
forkInfo.Status = types.MissingBranch
183
+
return &forkInfo, nil
184
+
}
185
+
186
+
newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, f.Ref, f.Ref)
187
+
if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent {
188
+
log.Printf("failed to update tracking branch: %s", err)
189
+
return nil, err
190
+
}
191
+
192
+
hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref)
193
+
194
+
var status types.AncestorCheckResponse
195
+
forkSyncableResp, err := signedClient.RepoForkAheadBehind(user.Did, string(f.RepoAt), repoInfo.Name, f.Ref, hiddenRef)
196
+
if err != nil {
197
+
log.Printf("failed to check if fork is ahead/behind: %s", err)
198
+
return nil, err
199
+
}
200
+
201
+
if err := json.NewDecoder(forkSyncableResp.Body).Decode(&status); err != nil {
202
+
log.Printf("failed to decode fork status: %s", err)
203
+
return nil, err
204
+
}
205
+
206
+
forkInfo.Status = status.Status
207
+
return &forkInfo, nil
208
+
}
-188
appview/repo/repo.go
-188
appview/repo/repo.go
···
26
26
"tangled.sh/tangled.sh/core/appview/oauth"
27
27
"tangled.sh/tangled.sh/core/appview/pages"
28
28
"tangled.sh/tangled.sh/core/appview/pages/markup"
29
-
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
30
29
"tangled.sh/tangled.sh/core/appview/reporesolver"
31
30
"tangled.sh/tangled.sh/core/eventconsumer"
32
31
"tangled.sh/tangled.sh/core/knotclient"
···
76
75
posthog: posthog,
77
76
enforcer: enforcer,
78
77
}
79
-
}
80
-
81
-
func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) {
82
-
ref := chi.URLParam(r, "ref")
83
-
f, err := rp.repoResolver.Resolve(r)
84
-
if err != nil {
85
-
log.Println("failed to fully resolve repo", err)
86
-
return
87
-
}
88
-
89
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
90
-
if err != nil {
91
-
log.Printf("failed to create unsigned client for %s", f.Knot)
92
-
rp.pages.Error503(w)
93
-
return
94
-
}
95
-
96
-
result, err := us.Index(f.OwnerDid(), f.RepoName, ref)
97
-
if err != nil {
98
-
rp.pages.Error503(w)
99
-
log.Println("failed to reach knotserver", err)
100
-
return
101
-
}
102
-
103
-
tagMap := make(map[string][]string)
104
-
for _, tag := range result.Tags {
105
-
hash := tag.Hash
106
-
if tag.Tag != nil {
107
-
hash = tag.Tag.Target.String()
108
-
}
109
-
tagMap[hash] = append(tagMap[hash], tag.Name)
110
-
}
111
-
112
-
for _, branch := range result.Branches {
113
-
hash := branch.Hash
114
-
tagMap[hash] = append(tagMap[hash], branch.Name)
115
-
}
116
-
117
-
slices.SortFunc(result.Branches, func(a, b types.Branch) int {
118
-
if a.Name == result.Ref {
119
-
return -1
120
-
}
121
-
if a.IsDefault {
122
-
return -1
123
-
}
124
-
if b.IsDefault {
125
-
return 1
126
-
}
127
-
if a.Commit != nil && b.Commit != nil {
128
-
if a.Commit.Committer.When.Before(b.Commit.Committer.When) {
129
-
return 1
130
-
} else {
131
-
return -1
132
-
}
133
-
}
134
-
return strings.Compare(a.Name, b.Name) * -1
135
-
})
136
-
137
-
commitCount := len(result.Commits)
138
-
branchCount := len(result.Branches)
139
-
tagCount := len(result.Tags)
140
-
fileCount := len(result.Files)
141
-
142
-
commitCount, branchCount, tagCount = balanceIndexItems(commitCount, branchCount, tagCount, fileCount)
143
-
commitsTrunc := result.Commits[:min(commitCount, len(result.Commits))]
144
-
tagsTrunc := result.Tags[:min(tagCount, len(result.Tags))]
145
-
branchesTrunc := result.Branches[:min(branchCount, len(result.Branches))]
146
-
147
-
emails := uniqueEmails(commitsTrunc)
148
-
emailToDidMap, err := db.GetEmailToDid(rp.db, emails, true)
149
-
if err != nil {
150
-
log.Println("failed to get email to did map", err)
151
-
}
152
-
153
-
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc)
154
-
if err != nil {
155
-
log.Println(err)
156
-
}
157
-
158
-
user := rp.oauth.GetUser(r)
159
-
repoInfo := f.RepoInfo(user)
160
-
161
-
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
162
-
if err != nil {
163
-
log.Printf("failed to get registration key for %s: %s", f.Knot, err)
164
-
rp.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
165
-
}
166
-
167
-
signedClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
168
-
if err != nil {
169
-
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
170
-
return
171
-
}
172
-
173
-
var forkInfo *types.ForkInfo
174
-
if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) {
175
-
forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient)
176
-
if err != nil {
177
-
log.Printf("Failed to fetch fork information: %v", err)
178
-
return
179
-
}
180
-
}
181
-
182
-
repoLanguages, err := signedClient.RepoLanguages(f.OwnerDid(), f.RepoName, ref)
183
-
if err != nil {
184
-
log.Printf("failed to compute language percentages: %s", err)
185
-
// non-fatal
186
-
}
187
-
188
-
rp.pages.RepoIndexPage(w, pages.RepoIndexParams{
189
-
LoggedInUser: user,
190
-
RepoInfo: repoInfo,
191
-
TagMap: tagMap,
192
-
RepoIndexResponse: *result,
193
-
CommitsTrunc: commitsTrunc,
194
-
TagsTrunc: tagsTrunc,
195
-
ForkInfo: forkInfo,
196
-
BranchesTrunc: branchesTrunc,
197
-
EmailToDidOrHandle: emailToDidOrHandle(rp, emailToDidMap),
198
-
VerifiedCommits: vc,
199
-
Languages: repoLanguages,
200
-
})
201
-
return
202
-
}
203
-
204
-
func getForkInfo(
205
-
repoInfo repoinfo.RepoInfo,
206
-
rp *Repo,
207
-
f *reporesolver.ResolvedRepo,
208
-
user *oauth.User,
209
-
signedClient *knotclient.SignedClient,
210
-
) (*types.ForkInfo, error) {
211
-
if user == nil {
212
-
return nil, nil
213
-
}
214
-
215
-
forkInfo := types.ForkInfo{
216
-
IsFork: repoInfo.Source != nil,
217
-
Status: types.UpToDate,
218
-
}
219
-
220
-
if !forkInfo.IsFork {
221
-
forkInfo.IsFork = false
222
-
return &forkInfo, nil
223
-
}
224
-
225
-
us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, rp.config.Core.Dev)
226
-
if err != nil {
227
-
log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot)
228
-
return nil, err
229
-
}
230
-
231
-
result, err := us.Branches(repoInfo.Source.Did, repoInfo.Source.Name)
232
-
if err != nil {
233
-
log.Println("failed to reach knotserver", err)
234
-
return nil, err
235
-
}
236
-
237
-
if !slices.ContainsFunc(result.Branches, func(branch types.Branch) bool {
238
-
return branch.Name == f.Ref
239
-
}) {
240
-
forkInfo.Status = types.MissingBranch
241
-
return &forkInfo, nil
242
-
}
243
-
244
-
newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, f.Ref, f.Ref)
245
-
if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent {
246
-
log.Printf("failed to update tracking branch: %s", err)
247
-
return nil, err
248
-
}
249
-
250
-
hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref)
251
-
252
-
var status types.AncestorCheckResponse
253
-
forkSyncableResp, err := signedClient.RepoForkAheadBehind(user.Did, string(f.RepoAt), repoInfo.Name, f.Ref, hiddenRef)
254
-
if err != nil {
255
-
log.Printf("failed to check if fork is ahead/behind: %s", err)
256
-
return nil, err
257
-
}
258
-
259
-
if err := json.NewDecoder(forkSyncableResp.Body).Decode(&status); err != nil {
260
-
log.Printf("failed to decode fork status: %s", err)
261
-
return nil, err
262
-
}
263
-
264
-
forkInfo.Status = status.Status
265
-
return &forkInfo, nil
266
78
}
267
79
268
80
func (rp *Repo) RepoLog(w http.ResponseWriter, r *http.Request) {