+12
appview/pages/markup/format.go
+12
appview/pages/markup/format.go
···
13
13
FormatMarkdown: []string{".md", ".markdown", ".mdown", ".mkdn", ".mkd"},
14
14
}
15
15
16
+
// ReadmeFilenames contains the list of common README filenames to search for,
17
+
// in order of preference. Only includes well-supported formats.
18
+
var ReadmeFilenames = []string{
19
+
"README.md", "readme.md",
20
+
"README",
21
+
"readme",
22
+
"README.markdown",
23
+
"readme.markdown",
24
+
"README.txt",
25
+
"readme.txt",
26
+
}
27
+
16
28
func GetFormat(filename string) Format {
17
29
for format, extensions := range FileTypes {
18
30
for _, extension := range extensions {
+6
-1
appview/pages/pages.go
+6
-1
appview/pages/pages.go
···
763
763
ShowRendered bool
764
764
RenderToggle bool
765
765
RenderedContents template.HTML
766
-
types.RepoBlobResponse
766
+
*tangled.RepoBlob_Output
767
+
// Computed fields for template compatibility
768
+
Contents string
769
+
Lines int
770
+
SizeHint uint64
771
+
IsBinary bool
767
772
}
768
773
769
774
func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error {
+6
appview/pages/templates/repo/fragments/diff.html
+6
appview/pages/templates/repo/fragments/diff.html
···
11
11
{{ $last := sub (len $diff) 1 }}
12
12
13
13
<div class="flex flex-col gap-4">
14
+
{{ if eq (len $diff) 0 }}
15
+
<div class="text-center text-gray-500 dark:text-gray-400 py-8">
16
+
<p>No differences found between the selected revisions.</p>
17
+
</div>
18
+
{{ else }}
14
19
{{ range $idx, $hunk := $diff }}
15
20
{{ with $hunk }}
16
21
<details open id="file-{{ .Name.New }}" class="group border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm" tabindex="{{ add $idx 1 }}">
···
49
54
</div>
50
55
</details>
51
56
{{ end }}
57
+
{{ end }}
52
58
{{ end }}
53
59
</div>
54
60
{{ end }}
+26
-8
appview/repo/artifact.go
+26
-8
appview/repo/artifact.go
···
1
1
package repo
2
2
3
3
import (
4
+
"context"
5
+
"encoding/json"
4
6
"fmt"
5
7
"log"
6
8
"net/http"
···
9
11
10
12
comatproto "github.com/bluesky-social/indigo/api/atproto"
11
13
lexutil "github.com/bluesky-social/indigo/lex/util"
14
+
indigoxrpc "github.com/bluesky-social/indigo/xrpc"
12
15
"github.com/dustin/go-humanize"
13
16
"github.com/go-chi/chi/v5"
14
17
"github.com/go-git/go-git/v5/plumbing"
···
17
20
"tangled.sh/tangled.sh/core/appview/db"
18
21
"tangled.sh/tangled.sh/core/appview/pages"
19
22
"tangled.sh/tangled.sh/core/appview/reporesolver"
20
-
"tangled.sh/tangled.sh/core/knotclient"
23
+
"tangled.sh/tangled.sh/core/appview/xrpcclient"
21
24
"tangled.sh/tangled.sh/core/tid"
22
25
"tangled.sh/tangled.sh/core/types"
23
26
)
···
33
36
return
34
37
}
35
38
36
-
tag, err := rp.resolveTag(f, tagParam)
39
+
tag, err := rp.resolveTag(r.Context(), f, tagParam)
37
40
if err != nil {
38
41
log.Println("failed to resolve tag", err)
39
42
rp.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution")
···
140
143
return
141
144
}
142
145
143
-
tag, err := rp.resolveTag(f, tagParam)
146
+
tag, err := rp.resolveTag(r.Context(), f, tagParam)
144
147
if err != nil {
145
148
log.Println("failed to resolve tag", err)
146
149
rp.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution")
···
259
262
w.Write([]byte{})
260
263
}
261
264
262
-
func (rp *Repo) resolveTag(f *reporesolver.ResolvedRepo, tagParam string) (*types.TagReference, error) {
265
+
func (rp *Repo) resolveTag(ctx context.Context, f *reporesolver.ResolvedRepo, tagParam string) (*types.TagReference, error) {
263
266
tagParam, err := url.QueryUnescape(tagParam)
264
267
if err != nil {
265
268
return nil, err
266
269
}
267
270
268
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
269
-
if err != nil {
270
-
return nil, err
271
+
scheme := "http"
272
+
if !rp.config.Core.Dev {
273
+
scheme = "https"
274
+
}
275
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
276
+
xrpcc := &indigoxrpc.Client{
277
+
Host: host,
271
278
}
272
279
273
-
result, err := us.Tags(f.OwnerDid(), f.Name)
280
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
281
+
xrpcBytes, err := tangled.RepoTags(ctx, xrpcc, "", 0, repo)
274
282
if err != nil {
283
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
284
+
log.Println("failed to call XRPC repo.tags", xrpcerr)
285
+
return nil, xrpcerr
286
+
}
275
287
log.Println("failed to reach knotserver", err)
288
+
return nil, err
289
+
}
290
+
291
+
var result types.RepoTagsResponse
292
+
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
293
+
log.Println("failed to decode XRPC tags response", err)
276
294
return nil, err
277
295
}
278
296
+220
-16
appview/repo/index.go
+220
-16
appview/repo/index.go
···
1
1
package repo
2
2
3
3
import (
4
+
"fmt"
4
5
"log"
5
6
"net/http"
6
7
"slices"
7
8
"sort"
8
9
"strings"
10
+
"sync"
11
+
"time"
9
12
13
+
"context"
14
+
"encoding/json"
15
+
16
+
indigoxrpc "github.com/bluesky-social/indigo/xrpc"
17
+
"github.com/go-git/go-git/v5/plumbing"
18
+
"tangled.sh/tangled.sh/core/api/tangled"
10
19
"tangled.sh/tangled.sh/core/appview/commitverify"
11
20
"tangled.sh/tangled.sh/core/appview/db"
12
21
"tangled.sh/tangled.sh/core/appview/pages"
22
+
"tangled.sh/tangled.sh/core/appview/pages/markup"
13
23
"tangled.sh/tangled.sh/core/appview/reporesolver"
14
-
"tangled.sh/tangled.sh/core/knotclient"
24
+
"tangled.sh/tangled.sh/core/appview/xrpcclient"
15
25
"tangled.sh/tangled.sh/core/types"
16
26
17
27
"github.com/go-chi/chi/v5"
···
27
37
return
28
38
}
29
39
30
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
31
-
if err != nil {
32
-
log.Printf("failed to create unsigned client for %s", f.Knot)
33
-
rp.pages.Error503(w)
34
-
return
40
+
scheme := "http"
41
+
if !rp.config.Core.Dev {
42
+
scheme = "https"
43
+
}
44
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
45
+
xrpcc := &indigoxrpc.Client{
46
+
Host: host,
35
47
}
36
48
37
-
result, err := us.Index(f.OwnerDid(), f.Name, ref)
49
+
// Build index response from multiple XRPC calls
50
+
result, err := rp.buildIndexResponse(r.Context(), xrpcc, f, ref)
38
51
if err != nil {
39
52
rp.pages.Error503(w)
40
-
log.Println("failed to reach knotserver", err)
53
+
log.Println("failed to build index response", err)
41
54
return
42
55
}
43
56
···
102
115
repoInfo := f.RepoInfo(user)
103
116
104
117
// TODO: a bit dirty
105
-
languageInfo, err := rp.getLanguageInfo(f, us, result.Ref, ref == "")
118
+
languageInfo, err := rp.getLanguageInfo(r.Context(), f, xrpcc, result.Ref, ref == "")
106
119
if err != nil {
107
120
log.Printf("failed to compute language percentages: %s", err)
108
121
// non-fatal
···
135
148
}
136
149
137
150
func (rp *Repo) getLanguageInfo(
151
+
ctx context.Context,
138
152
f *reporesolver.ResolvedRepo,
139
-
us *knotclient.UnsignedClient,
153
+
xrpcc *indigoxrpc.Client,
140
154
currentRef string,
141
155
isDefaultRef bool,
142
156
) ([]types.RepoLanguageDetails, error) {
···
148
162
)
149
163
150
164
if err != nil || langs == nil {
151
-
// non-fatal, fetch langs from ks
152
-
ls, err := us.RepoLanguages(f.OwnerDid(), f.Name, currentRef)
165
+
// non-fatal, fetch langs from ks via XRPC
166
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
167
+
ls, err := tangled.RepoLanguages(ctx, xrpcc, currentRef, repo)
153
168
if err != nil {
169
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
170
+
log.Println("failed to call XRPC repo.languages", xrpcerr)
171
+
return nil, xrpcerr
172
+
}
154
173
return nil, err
155
174
}
156
-
if ls == nil {
175
+
176
+
if ls == nil || ls.Languages == nil {
157
177
return nil, nil
158
178
}
159
179
160
-
for l, s := range ls.Languages {
180
+
for _, lang := range ls.Languages {
161
181
langs = append(langs, db.RepoLanguage{
162
182
RepoAt: f.RepoAt(),
163
183
Ref: currentRef,
164
184
IsDefaultRef: isDefaultRef,
165
-
Language: l,
166
-
Bytes: s,
185
+
Language: lang.Name,
186
+
Bytes: lang.Size,
167
187
})
168
188
}
169
189
···
206
226
207
227
return languageStats, nil
208
228
}
229
+
230
+
// buildIndexResponse creates a RepoIndexResponse by combining multiple xrpc calls in parallel
231
+
func (rp *Repo) buildIndexResponse(ctx context.Context, xrpcc *indigoxrpc.Client, f *reporesolver.ResolvedRepo, ref string) (*types.RepoIndexResponse, error) {
232
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
233
+
234
+
// first get branches to determine the ref if not specified
235
+
branchesBytes, err := tangled.RepoBranches(ctx, xrpcc, "", 0, repo)
236
+
if err != nil {
237
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
238
+
log.Println("failed to call XRPC repo.branches", xrpcerr)
239
+
return nil, xrpcerr
240
+
}
241
+
return nil, err
242
+
}
243
+
244
+
var branchesResp types.RepoBranchesResponse
245
+
if err := json.Unmarshal(branchesBytes, &branchesResp); err != nil {
246
+
return nil, err
247
+
}
248
+
249
+
// if no ref specified, use default branch or first available
250
+
if ref == "" && len(branchesResp.Branches) > 0 {
251
+
for _, branch := range branchesResp.Branches {
252
+
if branch.IsDefault {
253
+
ref = branch.Name
254
+
break
255
+
}
256
+
}
257
+
if ref == "" {
258
+
ref = branchesResp.Branches[0].Name
259
+
}
260
+
}
261
+
262
+
// check if repo is empty
263
+
if len(branchesResp.Branches) == 0 {
264
+
return &types.RepoIndexResponse{
265
+
IsEmpty: true,
266
+
Branches: branchesResp.Branches,
267
+
}, nil
268
+
}
269
+
270
+
// now run the remaining queries in parallel
271
+
var wg sync.WaitGroup
272
+
var mu sync.Mutex
273
+
var errs []error
274
+
275
+
var (
276
+
tagsResp types.RepoTagsResponse
277
+
treeResp *tangled.RepoTree_Output
278
+
logResp types.RepoLogResponse
279
+
readmeContent string
280
+
readmeFileName string
281
+
)
282
+
283
+
// tags
284
+
wg.Add(1)
285
+
go func() {
286
+
defer wg.Done()
287
+
tagsBytes, err := tangled.RepoTags(ctx, xrpcc, "", 0, repo)
288
+
if err != nil {
289
+
mu.Lock()
290
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
291
+
log.Println("failed to call XRPC repo.tags", xrpcerr)
292
+
errs = append(errs, xrpcerr)
293
+
} else {
294
+
errs = append(errs, err)
295
+
}
296
+
mu.Unlock()
297
+
return
298
+
}
299
+
300
+
if err := json.Unmarshal(tagsBytes, &tagsResp); err != nil {
301
+
mu.Lock()
302
+
errs = append(errs, err)
303
+
mu.Unlock()
304
+
}
305
+
}()
306
+
307
+
// tree/files
308
+
wg.Add(1)
309
+
go func() {
310
+
defer wg.Done()
311
+
resp, err := tangled.RepoTree(ctx, xrpcc, "", ref, repo)
312
+
if err != nil {
313
+
mu.Lock()
314
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
315
+
log.Println("failed to call XRPC repo.tree", xrpcerr)
316
+
errs = append(errs, xrpcerr)
317
+
} else {
318
+
errs = append(errs, err)
319
+
}
320
+
mu.Unlock()
321
+
return
322
+
}
323
+
treeResp = resp
324
+
}()
325
+
326
+
// commits
327
+
wg.Add(1)
328
+
go func() {
329
+
defer wg.Done()
330
+
logBytes, err := tangled.RepoLog(ctx, xrpcc, "", 50, "", ref, repo)
331
+
if err != nil {
332
+
mu.Lock()
333
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
334
+
log.Println("failed to call XRPC repo.log", xrpcerr)
335
+
errs = append(errs, xrpcerr)
336
+
} else {
337
+
errs = append(errs, err)
338
+
}
339
+
mu.Unlock()
340
+
return
341
+
}
342
+
343
+
if err := json.Unmarshal(logBytes, &logResp); err != nil {
344
+
mu.Lock()
345
+
errs = append(errs, err)
346
+
mu.Unlock()
347
+
}
348
+
}()
349
+
350
+
// readme content
351
+
wg.Add(1)
352
+
go func() {
353
+
defer wg.Done()
354
+
for _, filename := range markup.ReadmeFilenames {
355
+
blobResp, err := tangled.RepoBlob(ctx, xrpcc, filename, false, ref, repo)
356
+
if err != nil {
357
+
continue
358
+
}
359
+
360
+
if blobResp == nil {
361
+
continue
362
+
}
363
+
364
+
readmeContent = blobResp.Content
365
+
readmeFileName = filename
366
+
break
367
+
}
368
+
}()
369
+
370
+
wg.Wait()
371
+
372
+
if len(errs) > 0 {
373
+
return nil, errs[0] // return first error
374
+
}
375
+
376
+
var files []types.NiceTree
377
+
if treeResp != nil && treeResp.Files != nil {
378
+
for _, file := range treeResp.Files {
379
+
niceFile := types.NiceTree{
380
+
IsFile: file.Is_file,
381
+
IsSubtree: file.Is_subtree,
382
+
Name: file.Name,
383
+
Mode: file.Mode,
384
+
Size: file.Size,
385
+
}
386
+
if file.Last_commit != nil {
387
+
when, _ := time.Parse(time.RFC3339, file.Last_commit.When)
388
+
niceFile.LastCommit = &types.LastCommitInfo{
389
+
Hash: plumbing.NewHash(file.Last_commit.Hash),
390
+
Message: file.Last_commit.Message,
391
+
When: when,
392
+
}
393
+
}
394
+
files = append(files, niceFile)
395
+
}
396
+
}
397
+
398
+
result := &types.RepoIndexResponse{
399
+
IsEmpty: false,
400
+
Ref: ref,
401
+
Readme: readmeContent,
402
+
ReadmeFileName: readmeFileName,
403
+
Commits: logResp.Commits,
404
+
Description: logResp.Description,
405
+
Files: files,
406
+
Branches: branchesResp.Branches,
407
+
Tags: tagsResp.Tags,
408
+
TotalCommits: logResp.Total,
409
+
}
410
+
411
+
return result, nil
412
+
}
+358
-153
appview/repo/repo.go
+358
-153
appview/repo/repo.go
···
11
11
"log/slog"
12
12
"net/http"
13
13
"net/url"
14
+
"path"
14
15
"path/filepath"
15
16
"slices"
16
17
"strconv"
···
19
20
20
21
comatproto "github.com/bluesky-social/indigo/api/atproto"
21
22
lexutil "github.com/bluesky-social/indigo/lex/util"
23
+
indigoxrpc "github.com/bluesky-social/indigo/xrpc"
22
24
"tangled.sh/tangled.sh/core/api/tangled"
23
25
"tangled.sh/tangled.sh/core/appview/commitverify"
24
26
"tangled.sh/tangled.sh/core/appview/config"
···
31
33
xrpcclient "tangled.sh/tangled.sh/core/appview/xrpcclient"
32
34
"tangled.sh/tangled.sh/core/eventconsumer"
33
35
"tangled.sh/tangled.sh/core/idresolver"
34
-
"tangled.sh/tangled.sh/core/knotclient"
35
36
"tangled.sh/tangled.sh/core/patchutil"
36
37
"tangled.sh/tangled.sh/core/rbac"
37
38
"tangled.sh/tangled.sh/core/tid"
···
92
93
return
93
94
}
94
95
95
-
var uri string
96
-
if rp.config.Core.Dev {
97
-
uri = "http"
98
-
} else {
99
-
uri = "https"
96
+
scheme := "http"
97
+
if !rp.config.Core.Dev {
98
+
scheme = "https"
99
+
}
100
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
101
+
xrpcc := &indigoxrpc.Client{
102
+
Host: host,
103
+
}
104
+
105
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
106
+
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", refParam, repo)
107
+
if err != nil {
108
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
109
+
log.Println("failed to call XRPC repo.archive", xrpcerr)
110
+
rp.pages.Error503(w)
111
+
return
112
+
}
113
+
rp.pages.Error404(w)
114
+
return
100
115
}
101
-
url := fmt.Sprintf("%s://%s/%s/%s/archive/%s.tar.gz", uri, f.Knot, f.OwnerDid(), f.Name, url.PathEscape(refParam))
102
116
103
-
http.Redirect(w, r, url, http.StatusFound)
117
+
// Set headers for file download
118
+
filename := fmt.Sprintf("%s-%s.tar.gz", f.Name, refParam)
119
+
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
120
+
w.Header().Set("Content-Type", "application/gzip")
121
+
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(archiveBytes)))
122
+
123
+
// Write the archive data directly
124
+
w.Write(archiveBytes)
104
125
}
105
126
106
127
func (rp *Repo) RepoLog(w http.ResponseWriter, r *http.Request) {
···
120
141
121
142
ref := chi.URLParam(r, "ref")
122
143
123
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
144
+
scheme := "http"
145
+
if !rp.config.Core.Dev {
146
+
scheme = "https"
147
+
}
148
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
149
+
xrpcc := &indigoxrpc.Client{
150
+
Host: host,
151
+
}
152
+
153
+
limit := int64(60)
154
+
cursor := ""
155
+
if page > 1 {
156
+
// Convert page number to cursor (offset)
157
+
offset := (page - 1) * int(limit)
158
+
cursor = strconv.Itoa(offset)
159
+
}
160
+
161
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
162
+
xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
124
163
if err != nil {
125
-
log.Println("failed to create unsigned client", err)
164
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
165
+
log.Println("failed to call XRPC repo.log", xrpcerr)
166
+
rp.pages.Error503(w)
167
+
return
168
+
}
169
+
rp.pages.Error404(w)
126
170
return
127
171
}
128
172
129
-
repolog, err := us.Log(f.OwnerDid(), f.Name, ref, page)
130
-
if err != nil {
173
+
var xrpcResp types.RepoLogResponse
174
+
if err := json.Unmarshal(xrpcBytes, &xrpcResp); err != nil {
175
+
log.Println("failed to decode XRPC response", err)
131
176
rp.pages.Error503(w)
132
-
log.Println("failed to reach knotserver", err)
133
177
return
134
178
}
135
179
136
-
tagResult, err := us.Tags(f.OwnerDid(), f.Name)
180
+
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
137
181
if err != nil {
138
-
rp.pages.Error503(w)
139
-
log.Println("failed to reach knotserver", err)
140
-
return
182
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
183
+
log.Println("failed to call XRPC repo.tags", xrpcerr)
184
+
rp.pages.Error503(w)
185
+
return
186
+
}
141
187
}
142
188
143
189
tagMap := make(map[string][]string)
144
-
for _, tag := range tagResult.Tags {
145
-
hash := tag.Hash
146
-
if tag.Tag != nil {
147
-
hash = tag.Tag.Target.String()
190
+
if tagBytes != nil {
191
+
var tagResp types.RepoTagsResponse
192
+
if err := json.Unmarshal(tagBytes, &tagResp); err == nil {
193
+
for _, tag := range tagResp.Tags {
194
+
tagMap[tag.Hash] = append(tagMap[tag.Hash], tag.Name)
195
+
}
148
196
}
149
-
tagMap[hash] = append(tagMap[hash], tag.Name)
150
197
}
151
198
152
-
branchResult, err := us.Branches(f.OwnerDid(), f.Name)
199
+
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
153
200
if err != nil {
154
-
rp.pages.Error503(w)
155
-
log.Println("failed to reach knotserver", err)
156
-
return
201
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
202
+
log.Println("failed to call XRPC repo.branches", xrpcerr)
203
+
rp.pages.Error503(w)
204
+
return
205
+
}
157
206
}
158
207
159
-
for _, branch := range branchResult.Branches {
160
-
hash := branch.Hash
161
-
tagMap[hash] = append(tagMap[hash], branch.Name)
208
+
if branchBytes != nil {
209
+
var branchResp types.RepoBranchesResponse
210
+
if err := json.Unmarshal(branchBytes, &branchResp); err == nil {
211
+
for _, branch := range branchResp.Branches {
212
+
tagMap[branch.Hash] = append(tagMap[branch.Hash], branch.Name)
213
+
}
214
+
}
162
215
}
163
216
164
217
user := rp.oauth.GetUser(r)
165
218
166
-
emailToDidMap, err := db.GetEmailToDid(rp.db, uniqueEmails(repolog.Commits), true)
219
+
emailToDidMap, err := db.GetEmailToDid(rp.db, uniqueEmails(xrpcResp.Commits), true)
167
220
if err != nil {
168
221
log.Println("failed to fetch email to did mapping", err)
169
222
}
170
223
171
-
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, repolog.Commits)
224
+
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, xrpcResp.Commits)
172
225
if err != nil {
173
226
log.Println(err)
174
227
}
···
176
229
repoInfo := f.RepoInfo(user)
177
230
178
231
var shas []string
179
-
for _, c := range repolog.Commits {
232
+
for _, c := range xrpcResp.Commits {
180
233
shas = append(shas, c.Hash.String())
181
234
}
182
235
pipelines, err := getPipelineStatuses(rp.db, repoInfo, shas)
···
189
242
LoggedInUser: user,
190
243
TagMap: tagMap,
191
244
RepoInfo: repoInfo,
192
-
RepoLogResponse: *repolog,
245
+
RepoLogResponse: xrpcResp,
193
246
EmailToDidOrHandle: emailToDidOrHandle(rp, emailToDidMap),
194
247
VerifiedCommits: vc,
195
248
Pipelines: pipelines,
···
301
354
return
302
355
}
303
356
ref := chi.URLParam(r, "ref")
304
-
protocol := "http"
305
-
if !rp.config.Core.Dev {
306
-
protocol = "https"
307
-
}
308
357
309
358
var diffOpts types.DiffOpts
310
359
if d := r.URL.Query().Get("diff"); d == "split" {
···
316
365
return
317
366
}
318
367
319
-
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/commit/%s", protocol, f.Knot, f.OwnerDid(), f.Repo.Name, ref))
320
-
if err != nil {
321
-
rp.pages.Error503(w)
322
-
log.Println("failed to reach knotserver", err)
323
-
return
368
+
scheme := "http"
369
+
if !rp.config.Core.Dev {
370
+
scheme = "https"
371
+
}
372
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
373
+
xrpcc := &indigoxrpc.Client{
374
+
Host: host,
324
375
}
325
376
326
-
body, err := io.ReadAll(resp.Body)
377
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
378
+
xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
327
379
if err != nil {
328
-
log.Printf("Error reading response body: %v", err)
380
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
381
+
log.Println("failed to call XRPC repo.diff", xrpcerr)
382
+
rp.pages.Error503(w)
383
+
return
384
+
}
385
+
rp.pages.Error404(w)
329
386
return
330
387
}
331
388
332
389
var result types.RepoCommitResponse
333
-
err = json.Unmarshal(body, &result)
334
-
if err != nil {
335
-
log.Println("failed to parse response:", err)
390
+
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
391
+
log.Println("failed to decode XRPC response", err)
392
+
rp.pages.Error503(w)
336
393
return
337
394
}
338
395
···
378
435
379
436
ref := chi.URLParam(r, "ref")
380
437
treePath := chi.URLParam(r, "*")
381
-
protocol := "http"
382
-
if !rp.config.Core.Dev {
383
-
protocol = "https"
384
-
}
385
438
386
439
// if the tree path has a trailing slash, let's strip it
387
440
// so we don't 404
388
441
treePath = strings.TrimSuffix(treePath, "/")
389
442
390
-
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.Repo.Name, ref, treePath))
391
-
if err != nil {
392
-
rp.pages.Error503(w)
393
-
log.Println("failed to reach knotserver", err)
394
-
return
443
+
scheme := "http"
444
+
if !rp.config.Core.Dev {
445
+
scheme = "https"
446
+
}
447
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
448
+
xrpcc := &indigoxrpc.Client{
449
+
Host: host,
395
450
}
396
451
397
-
// uhhh so knotserver returns a 500 if the entry isn't found in
398
-
// the requested tree path, so let's stick to not-OK here.
399
-
// we can fix this once we build out the xrpc apis for these operations.
400
-
if resp.StatusCode != http.StatusOK {
452
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
453
+
xrpcResp, err := tangled.RepoTree(r.Context(), xrpcc, treePath, ref, repo)
454
+
if err != nil {
455
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
456
+
log.Println("failed to call XRPC repo.tree", xrpcerr)
457
+
rp.pages.Error503(w)
458
+
return
459
+
}
401
460
rp.pages.Error404(w)
402
461
return
403
462
}
404
463
405
-
body, err := io.ReadAll(resp.Body)
406
-
if err != nil {
407
-
log.Printf("Error reading response body: %v", err)
408
-
return
464
+
// Convert XRPC response to internal types.RepoTreeResponse
465
+
files := make([]types.NiceTree, len(xrpcResp.Files))
466
+
for i, xrpcFile := range xrpcResp.Files {
467
+
file := types.NiceTree{
468
+
Name: xrpcFile.Name,
469
+
Mode: xrpcFile.Mode,
470
+
Size: int64(xrpcFile.Size),
471
+
IsFile: xrpcFile.Is_file,
472
+
IsSubtree: xrpcFile.Is_subtree,
473
+
}
474
+
475
+
// Convert last commit info if present
476
+
if xrpcFile.Last_commit != nil {
477
+
commitWhen, _ := time.Parse(time.RFC3339, xrpcFile.Last_commit.When)
478
+
file.LastCommit = &types.LastCommitInfo{
479
+
Hash: plumbing.NewHash(xrpcFile.Last_commit.Hash),
480
+
Message: xrpcFile.Last_commit.Message,
481
+
When: commitWhen,
482
+
}
483
+
}
484
+
485
+
files[i] = file
409
486
}
410
487
411
-
var result types.RepoTreeResponse
412
-
err = json.Unmarshal(body, &result)
413
-
if err != nil {
414
-
log.Println("failed to parse response:", err)
415
-
return
488
+
result := types.RepoTreeResponse{
489
+
Ref: xrpcResp.Ref,
490
+
Files: files,
491
+
}
492
+
493
+
if xrpcResp.Parent != nil {
494
+
result.Parent = *xrpcResp.Parent
495
+
}
496
+
if xrpcResp.Dotdot != nil {
497
+
result.DotDot = *xrpcResp.Dotdot
416
498
}
417
499
418
500
// redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,
···
451
533
return
452
534
}
453
535
454
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
536
+
scheme := "http"
537
+
if !rp.config.Core.Dev {
538
+
scheme = "https"
539
+
}
540
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
541
+
xrpcc := &indigoxrpc.Client{
542
+
Host: host,
543
+
}
544
+
545
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
546
+
xrpcBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
455
547
if err != nil {
456
-
log.Println("failed to create unsigned client", err)
548
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
549
+
log.Println("failed to call XRPC repo.tags", xrpcerr)
550
+
rp.pages.Error503(w)
551
+
return
552
+
}
553
+
rp.pages.Error404(w)
457
554
return
458
555
}
459
556
460
-
result, err := us.Tags(f.OwnerDid(), f.Name)
461
-
if err != nil {
557
+
var result types.RepoTagsResponse
558
+
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
559
+
log.Println("failed to decode XRPC response", err)
462
560
rp.pages.Error503(w)
463
-
log.Println("failed to reach knotserver", err)
464
561
return
465
562
}
466
563
···
496
593
rp.pages.RepoTags(w, pages.RepoTagsParams{
497
594
LoggedInUser: user,
498
595
RepoInfo: f.RepoInfo(user),
499
-
RepoTagsResponse: *result,
596
+
RepoTagsResponse: result,
500
597
ArtifactMap: artifactMap,
501
598
DanglingArtifacts: danglingArtifacts,
502
599
})
···
509
606
return
510
607
}
511
608
512
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
609
+
scheme := "http"
610
+
if !rp.config.Core.Dev {
611
+
scheme = "https"
612
+
}
613
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
614
+
xrpcc := &indigoxrpc.Client{
615
+
Host: host,
616
+
}
617
+
618
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
619
+
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
513
620
if err != nil {
514
-
log.Println("failed to create unsigned client", err)
621
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
622
+
log.Println("failed to call XRPC repo.branches", xrpcerr)
623
+
rp.pages.Error503(w)
624
+
return
625
+
}
626
+
rp.pages.Error404(w)
515
627
return
516
628
}
517
629
518
-
result, err := us.Branches(f.OwnerDid(), f.Name)
519
-
if err != nil {
630
+
var result types.RepoBranchesResponse
631
+
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
632
+
log.Println("failed to decode XRPC response", err)
520
633
rp.pages.Error503(w)
521
-
log.Println("failed to reach knotserver", err)
522
634
return
523
635
}
524
636
···
528
640
rp.pages.RepoBranches(w, pages.RepoBranchesParams{
529
641
LoggedInUser: user,
530
642
RepoInfo: f.RepoInfo(user),
531
-
RepoBranchesResponse: *result,
643
+
RepoBranchesResponse: result,
532
644
})
533
645
}
534
646
···
541
653
542
654
ref := chi.URLParam(r, "ref")
543
655
filePath := chi.URLParam(r, "*")
544
-
protocol := "http"
656
+
657
+
scheme := "http"
545
658
if !rp.config.Core.Dev {
546
-
protocol = "https"
547
-
}
548
-
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.Repo.Name, ref, filePath))
549
-
if err != nil {
550
-
rp.pages.Error503(w)
551
-
log.Println("failed to reach knotserver", err)
552
-
return
659
+
scheme = "https"
553
660
}
554
-
555
-
if resp.StatusCode == http.StatusNotFound {
556
-
rp.pages.Error404(w)
557
-
return
661
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
662
+
xrpcc := &indigoxrpc.Client{
663
+
Host: host,
558
664
}
559
665
560
-
body, err := io.ReadAll(resp.Body)
666
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
667
+
resp, err := tangled.RepoBlob(r.Context(), xrpcc, filePath, false, ref, repo)
561
668
if err != nil {
562
-
log.Printf("Error reading response body: %v", err)
669
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
670
+
log.Println("failed to call XRPC repo.blob", xrpcerr)
671
+
rp.pages.Error503(w)
672
+
return
673
+
}
674
+
rp.pages.Error404(w)
563
675
return
564
676
}
565
677
566
-
var result types.RepoBlobResponse
567
-
err = json.Unmarshal(body, &result)
568
-
if err != nil {
569
-
log.Println("failed to parse response:", err)
570
-
return
571
-
}
678
+
// Use XRPC response directly instead of converting to internal types
572
679
573
680
var breadcrumbs [][]string
574
681
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)})
···
581
688
showRendered := false
582
689
renderToggle := false
583
690
584
-
if markup.GetFormat(result.Path) == markup.FormatMarkdown {
691
+
if markup.GetFormat(resp.Path) == markup.FormatMarkdown {
585
692
renderToggle = true
586
693
showRendered = r.URL.Query().Get("code") != "true"
587
694
}
···
591
698
var isVideo bool
592
699
var contentSrc string
593
700
594
-
if result.IsBinary {
595
-
ext := strings.ToLower(filepath.Ext(result.Path))
701
+
if resp.IsBinary != nil && *resp.IsBinary {
702
+
ext := strings.ToLower(filepath.Ext(resp.Path))
596
703
switch ext {
597
704
case ".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp":
598
705
isImage = true
···
602
709
unsupported = true
603
710
}
604
711
605
-
// fetch the actual binary content like in RepoBlobRaw
712
+
// fetch the raw binary content using sh.tangled.repo.blob xrpc
713
+
repoName := path.Join("%s/%s", f.OwnerDid(), f.Name)
714
+
blobURL := fmt.Sprintf("%s://%s/xrpc/sh.tangled.repo.blob?repo=%s&ref=%s&path=%s&raw=true",
715
+
scheme, f.Knot, url.QueryEscape(repoName), url.QueryEscape(ref), url.QueryEscape(filePath))
606
716
607
-
blobURL := fmt.Sprintf("%s://%s/%s/%s/raw/%s/%s", protocol, f.Knot, f.OwnerDid(), f.Name, ref, filePath)
608
717
contentSrc = blobURL
609
718
if !rp.config.Core.Dev {
610
719
contentSrc = markup.GenerateCamoURL(rp.config.Camo.Host, rp.config.Camo.SharedSecret, blobURL)
611
720
}
612
721
}
613
722
723
+
lines := 0
724
+
if resp.IsBinary == nil || !*resp.IsBinary {
725
+
lines = strings.Count(resp.Content, "\n") + 1
726
+
}
727
+
728
+
var sizeHint uint64
729
+
if resp.Size != nil {
730
+
sizeHint = uint64(*resp.Size)
731
+
} else {
732
+
sizeHint = uint64(len(resp.Content))
733
+
}
734
+
614
735
user := rp.oauth.GetUser(r)
736
+
737
+
// Determine if content is binary (dereference pointer)
738
+
isBinary := false
739
+
if resp.IsBinary != nil {
740
+
isBinary = *resp.IsBinary
741
+
}
742
+
615
743
rp.pages.RepoBlob(w, pages.RepoBlobParams{
616
-
LoggedInUser: user,
617
-
RepoInfo: f.RepoInfo(user),
618
-
RepoBlobResponse: result,
619
-
BreadCrumbs: breadcrumbs,
620
-
ShowRendered: showRendered,
621
-
RenderToggle: renderToggle,
622
-
Unsupported: unsupported,
623
-
IsImage: isImage,
624
-
IsVideo: isVideo,
625
-
ContentSrc: contentSrc,
744
+
LoggedInUser: user,
745
+
RepoInfo: f.RepoInfo(user),
746
+
BreadCrumbs: breadcrumbs,
747
+
ShowRendered: showRendered,
748
+
RenderToggle: renderToggle,
749
+
Unsupported: unsupported,
750
+
IsImage: isImage,
751
+
IsVideo: isVideo,
752
+
ContentSrc: contentSrc,
753
+
RepoBlob_Output: resp,
754
+
Contents: resp.Content,
755
+
Lines: lines,
756
+
SizeHint: sizeHint,
757
+
IsBinary: isBinary,
626
758
})
627
759
}
628
760
···
637
769
ref := chi.URLParam(r, "ref")
638
770
filePath := chi.URLParam(r, "*")
639
771
640
-
protocol := "http"
772
+
scheme := "http"
641
773
if !rp.config.Core.Dev {
642
-
protocol = "https"
774
+
scheme = "https"
643
775
}
644
776
645
-
blobURL := fmt.Sprintf("%s://%s/%s/%s/raw/%s/%s", protocol, f.Knot, f.OwnerDid(), f.Repo.Name, ref, filePath)
777
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
778
+
blobURL := fmt.Sprintf("%s://%s/xrpc/sh.tangled.repo.blob?repo=%s&ref=%s&path=%s&raw=true",
779
+
scheme, f.Knot, url.QueryEscape(repo), url.QueryEscape(ref), url.QueryEscape(filePath))
646
780
647
781
req, err := http.NewRequest("GET", blobURL, nil)
648
782
if err != nil {
···
685
819
return
686
820
}
687
821
688
-
// Safely serve content based on type
689
822
if strings.HasPrefix(contentType, "text/") || isTextualMimeType(contentType) {
690
-
// Serve all textual content as text/plain for security
823
+
// serve all textual content as text/plain
691
824
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
692
825
w.Write(body)
693
826
} else if strings.HasPrefix(contentType, "image/") || strings.HasPrefix(contentType, "video/") {
694
-
// Serve images and videos with their original content type
827
+
// serve images and videos with their original content type
695
828
w.Header().Set("Content-Type", contentType)
696
829
w.Write(body)
697
830
} else {
698
-
// Block potentially dangerous content types
699
831
w.WriteHeader(http.StatusUnsupportedMediaType)
700
832
w.Write([]byte("unsupported content type"))
701
833
return
···
716
848
"message/",
717
849
}
718
850
719
-
for _, t := range textualTypes {
720
-
if mimeType == t {
721
-
return true
722
-
}
723
-
}
724
-
return false
851
+
return slices.Contains(textualTypes, mimeType)
725
852
}
726
853
727
854
// modify the spindle configured for this repo
···
1227
1354
f, err := rp.repoResolver.Resolve(r)
1228
1355
user := rp.oauth.GetUser(r)
1229
1356
1230
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
1357
+
scheme := "http"
1358
+
if !rp.config.Core.Dev {
1359
+
scheme = "https"
1360
+
}
1361
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
1362
+
xrpcc := &indigoxrpc.Client{
1363
+
Host: host,
1364
+
}
1365
+
1366
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1367
+
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
1231
1368
if err != nil {
1232
-
log.Println("failed to create unsigned client", err)
1369
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1370
+
log.Println("failed to call XRPC repo.branches", xrpcerr)
1371
+
rp.pages.Error503(w)
1372
+
return
1373
+
}
1374
+
rp.pages.Error503(w)
1233
1375
return
1234
1376
}
1235
1377
1236
-
result, err := us.Branches(f.OwnerDid(), f.Name)
1237
-
if err != nil {
1378
+
var result types.RepoBranchesResponse
1379
+
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
1380
+
log.Println("failed to decode XRPC response", err)
1238
1381
rp.pages.Error503(w)
1239
-
log.Println("failed to reach knotserver", err)
1240
1382
return
1241
1383
}
1242
1384
···
1607
1749
return
1608
1750
}
1609
1751
1610
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
1752
+
scheme := "http"
1753
+
if !rp.config.Core.Dev {
1754
+
scheme = "https"
1755
+
}
1756
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
1757
+
xrpcc := &indigoxrpc.Client{
1758
+
Host: host,
1759
+
}
1760
+
1761
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1762
+
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
1611
1763
if err != nil {
1612
-
log.Printf("failed to create unsigned client for %s", f.Knot)
1613
-
rp.pages.Error503(w)
1764
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1765
+
log.Println("failed to call XRPC repo.branches", xrpcerr)
1766
+
rp.pages.Error503(w)
1767
+
return
1768
+
}
1769
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1614
1770
return
1615
1771
}
1616
1772
1617
-
result, err := us.Branches(f.OwnerDid(), f.Name)
1618
-
if err != nil {
1773
+
var branchResult types.RepoBranchesResponse
1774
+
if err := json.Unmarshal(branchBytes, &branchResult); err != nil {
1775
+
log.Println("failed to decode XRPC branches response", err)
1619
1776
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1620
-
log.Println("failed to reach knotserver", err)
1621
1777
return
1622
1778
}
1623
-
branches := result.Branches
1779
+
branches := branchResult.Branches
1624
1780
1625
1781
sortBranches(branches)
1626
1782
···
1644
1800
head = queryHead
1645
1801
}
1646
1802
1647
-
tags, err := us.Tags(f.OwnerDid(), f.Name)
1803
+
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
1648
1804
if err != nil {
1805
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1806
+
log.Println("failed to call XRPC repo.tags", xrpcerr)
1807
+
rp.pages.Error503(w)
1808
+
return
1809
+
}
1649
1810
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1650
-
log.Println("failed to reach knotserver", err)
1811
+
return
1812
+
}
1813
+
1814
+
var tags types.RepoTagsResponse
1815
+
if err := json.Unmarshal(tagBytes, &tags); err != nil {
1816
+
log.Println("failed to decode XRPC tags response", err)
1817
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1651
1818
return
1652
1819
}
1653
1820
···
1699
1866
return
1700
1867
}
1701
1868
1702
-
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
1869
+
scheme := "http"
1870
+
if !rp.config.Core.Dev {
1871
+
scheme = "https"
1872
+
}
1873
+
host := fmt.Sprintf("%s://%s", scheme, f.Knot)
1874
+
xrpcc := &indigoxrpc.Client{
1875
+
Host: host,
1876
+
}
1877
+
1878
+
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1879
+
1880
+
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
1703
1881
if err != nil {
1704
-
log.Printf("failed to create unsigned client for %s", f.Knot)
1705
-
rp.pages.Error503(w)
1882
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1883
+
log.Println("failed to call XRPC repo.branches", xrpcerr)
1884
+
rp.pages.Error503(w)
1885
+
return
1886
+
}
1887
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1888
+
return
1889
+
}
1890
+
1891
+
var branches types.RepoBranchesResponse
1892
+
if err := json.Unmarshal(branchBytes, &branches); err != nil {
1893
+
log.Println("failed to decode XRPC branches response", err)
1894
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1706
1895
return
1707
1896
}
1708
1897
1709
-
branches, err := us.Branches(f.OwnerDid(), f.Name)
1898
+
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
1710
1899
if err != nil {
1900
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1901
+
log.Println("failed to call XRPC repo.tags", xrpcerr)
1902
+
rp.pages.Error503(w)
1903
+
return
1904
+
}
1711
1905
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1712
-
log.Println("failed to reach knotserver", err)
1906
+
return
1907
+
}
1908
+
1909
+
var tags types.RepoTagsResponse
1910
+
if err := json.Unmarshal(tagBytes, &tags); err != nil {
1911
+
log.Println("failed to decode XRPC tags response", err)
1912
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1713
1913
return
1714
1914
}
1715
1915
1716
-
tags, err := us.Tags(f.OwnerDid(), f.Name)
1916
+
compareBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, base, head)
1717
1917
if err != nil {
1918
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1919
+
log.Println("failed to call XRPC repo.compare", xrpcerr)
1920
+
rp.pages.Error503(w)
1921
+
return
1922
+
}
1718
1923
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1719
-
log.Println("failed to reach knotserver", err)
1720
1924
return
1721
1925
}
1722
1926
1723
-
formatPatch, err := us.Compare(f.OwnerDid(), f.Name, base, head)
1724
-
if err != nil {
1927
+
var formatPatch types.RepoFormatPatchResponse
1928
+
if err := json.Unmarshal(compareBytes, &formatPatch); err != nil {
1929
+
log.Println("failed to decode XRPC compare response", err)
1725
1930
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1726
-
log.Println("failed to compare", err)
1727
1931
return
1728
1932
}
1933
+
1729
1934
diff := patchutil.AsNiceDiff(formatPatch.Patch, base)
1730
1935
1731
1936
repoinfo := f.RepoInfo(user)
+1
-1
appview/xrpcclient/xrpc.go
+1
-1
appview/xrpcclient/xrpc.go