+2
-1
appview/pages/funcmap.go
+2
-1
appview/pages/funcmap.go
···
143
143
return v.Slice(start, end).Interface()
144
144
},
145
145
"markdown": func(text string) template.HTML {
146
-
return template.HTML(markup.RenderMarkdown(text))
146
+
rctx := &markup.RenderContext{}
147
+
return template.HTML(rctx.RenderMarkdown(text))
147
148
},
148
149
"isNil": func(t any) bool {
149
150
// returns false for other "zero" values
+4
-11
appview/pages/markup/markdown.go
+4
-11
appview/pages/markup/markdown.go
···
19
19
const (
20
20
// RendererTypeRepoMarkdown is for repository documentation markdown files
21
21
RendererTypeRepoMarkdown RendererType = iota
22
-
// RendererTypeIssueComment is for issue comments
23
-
RendererTypeIssueComment
24
-
// RendererTypePullComment is for pull request comments
25
-
RendererTypePullComment
26
-
// RendererTypeDefault is the default renderer with minimal transformations
27
-
RendererTypeDefault
28
22
)
29
23
30
24
// RenderContext holds the contextual data for rendering markdown.
31
-
// It can be initialized empty, and that'll skip any transformations
32
-
// and use the default renderer (RendererTypeDefault).
25
+
// It can be initialized empty, and that'll skip any transformations.
33
26
type RenderContext struct {
34
27
Ref string
35
28
FullRepoName string
···
73
66
74
67
switch a.rctx.RendererType {
75
68
case RendererTypeRepoMarkdown:
76
-
a.rctx.relativeLinkTransformer(n.(*ast.Link))
77
-
case RendererTypeDefault:
78
-
a.rctx.relativeLinkTransformer(n.(*ast.Link))
69
+
if v, ok := n.(*ast.Link); ok {
70
+
a.rctx.relativeLinkTransformer(v)
71
+
}
79
72
// more types here like RendererTypeIssue/Pull etc.
80
73
}
81
74
+13
-2
appview/pages/pages.go
+13
-2
appview/pages/pages.go
···
366
366
Roles RolesInRepo
367
367
Source *db.Repo
368
368
SourceHandle string
369
+
Ref string
369
370
DisableFork bool
370
371
}
371
372
···
478
479
return p.executeRepo("repo/empty", w, params)
479
480
}
480
481
482
+
rctx := markup.RenderContext{
483
+
Ref: params.RepoInfo.Ref,
484
+
FullRepoName: params.RepoInfo.FullName(),
485
+
}
486
+
481
487
if params.ReadmeFileName != "" {
482
488
var htmlString string
483
489
ext := filepath.Ext(params.ReadmeFileName)
484
490
switch ext {
485
491
case ".md", ".markdown", ".mdown", ".mkdn", ".mkd":
486
-
htmlString = markup.RenderMarkdown(params.Readme)
492
+
htmlString = rctx.RenderMarkdown(params.Readme)
487
493
params.Raw = false
488
494
params.HTMLReadme = template.HTML(bluemonday.UGCPolicy().Sanitize(htmlString))
489
495
default:
···
601
607
if params.ShowRendered {
602
608
switch markup.GetFormat(params.Path) {
603
609
case markup.FormatMarkdown:
604
-
params.RenderedContents = template.HTML(markup.RenderMarkdown(params.Contents))
610
+
rctx := markup.RenderContext{
611
+
Ref: params.RepoInfo.Ref,
612
+
FullRepoName: params.RepoInfo.FullName(),
613
+
RendererType: markup.RendererTypeRepoMarkdown,
614
+
}
615
+
params.RenderedContents = template.HTML(rctx.RenderMarkdown(params.Contents))
605
616
}
606
617
}
607
618
+2
-2
appview/state/middleware.go
+2
-2
appview/state/middleware.go
···
61
61
http.Error(w, "Forbiden", http.StatusUnauthorized)
62
62
return
63
63
}
64
-
f, err := fullyResolvedRepo(r)
64
+
f, err := s.fullyResolvedRepo(r)
65
65
if err != nil {
66
66
http.Error(w, "malformed url", http.StatusBadRequest)
67
67
return
···
148
148
func ResolvePull(s *State) middleware.Middleware {
149
149
return func(next http.Handler) http.Handler {
150
150
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
151
-
f, err := fullyResolvedRepo(r)
151
+
f, err := s.fullyResolvedRepo(r)
152
152
if err != nil {
153
153
log.Println("failed to fully resolve repo", err)
154
154
http.Error(w, "invalid repo url", http.StatusNotFound)
+19
-19
appview/state/pull.go
+19
-19
appview/state/pull.go
···
30
30
switch r.Method {
31
31
case http.MethodGet:
32
32
user := s.auth.GetUser(r)
33
-
f, err := fullyResolvedRepo(r)
33
+
f, err := s.fullyResolvedRepo(r)
34
34
if err != nil {
35
35
log.Println("failed to get repo and knot", err)
36
36
return
···
74
74
75
75
func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
76
76
user := s.auth.GetUser(r)
77
-
f, err := fullyResolvedRepo(r)
77
+
f, err := s.fullyResolvedRepo(r)
78
78
if err != nil {
79
79
log.Println("failed to get repo and knot", err)
80
80
return
···
251
251
252
252
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
253
253
user := s.auth.GetUser(r)
254
-
f, err := fullyResolvedRepo(r)
254
+
f, err := s.fullyResolvedRepo(r)
255
255
if err != nil {
256
256
log.Println("failed to get repo and knot", err)
257
257
return
···
300
300
func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) {
301
301
user := s.auth.GetUser(r)
302
302
303
-
f, err := fullyResolvedRepo(r)
303
+
f, err := s.fullyResolvedRepo(r)
304
304
if err != nil {
305
305
log.Println("failed to get repo and knot", err)
306
306
return
···
408
408
state = db.PullMerged
409
409
}
410
410
411
-
f, err := fullyResolvedRepo(r)
411
+
f, err := s.fullyResolvedRepo(r)
412
412
if err != nil {
413
413
log.Println("failed to get repo and knot", err)
414
414
return
···
462
462
463
463
func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
464
464
user := s.auth.GetUser(r)
465
-
f, err := fullyResolvedRepo(r)
465
+
f, err := s.fullyResolvedRepo(r)
466
466
if err != nil {
467
467
log.Println("failed to get repo and knot", err)
468
468
return
···
569
569
570
570
func (s *State) NewPull(w http.ResponseWriter, r *http.Request) {
571
571
user := s.auth.GetUser(r)
572
-
f, err := fullyResolvedRepo(r)
572
+
f, err := s.fullyResolvedRepo(r)
573
573
if err != nil {
574
574
log.Println("failed to get repo and knot", err)
575
575
return
···
904
904
}
905
905
906
906
func (s *State) ValidatePatch(w http.ResponseWriter, r *http.Request) {
907
-
_, err := fullyResolvedRepo(r)
907
+
_, err := s.fullyResolvedRepo(r)
908
908
if err != nil {
909
909
log.Println("failed to get repo and knot", err)
910
910
return
···
930
930
931
931
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
932
932
user := s.auth.GetUser(r)
933
-
f, err := fullyResolvedRepo(r)
933
+
f, err := s.fullyResolvedRepo(r)
934
934
if err != nil {
935
935
log.Println("failed to get repo and knot", err)
936
936
return
···
943
943
944
944
func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) {
945
945
user := s.auth.GetUser(r)
946
-
f, err := fullyResolvedRepo(r)
946
+
f, err := s.fullyResolvedRepo(r)
947
947
if err != nil {
948
948
log.Println("failed to get repo and knot", err)
949
949
return
···
983
983
984
984
func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) {
985
985
user := s.auth.GetUser(r)
986
-
f, err := fullyResolvedRepo(r)
986
+
f, err := s.fullyResolvedRepo(r)
987
987
if err != nil {
988
988
log.Println("failed to get repo and knot", err)
989
989
return
···
1004
1004
func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) {
1005
1005
user := s.auth.GetUser(r)
1006
1006
1007
-
f, err := fullyResolvedRepo(r)
1007
+
f, err := s.fullyResolvedRepo(r)
1008
1008
if err != nil {
1009
1009
log.Println("failed to get repo and knot", err)
1010
1010
return
···
1082
1082
1083
1083
func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) {
1084
1084
user := s.auth.GetUser(r)
1085
-
f, err := fullyResolvedRepo(r)
1085
+
f, err := s.fullyResolvedRepo(r)
1086
1086
if err != nil {
1087
1087
log.Println("failed to get repo and knot", err)
1088
1088
return
···
1126
1126
return
1127
1127
}
1128
1128
1129
-
f, err := fullyResolvedRepo(r)
1129
+
f, err := s.fullyResolvedRepo(r)
1130
1130
if err != nil {
1131
1131
log.Println("failed to get repo and knot", err)
1132
1132
return
···
1209
1209
return
1210
1210
}
1211
1211
1212
-
f, err := fullyResolvedRepo(r)
1212
+
f, err := s.fullyResolvedRepo(r)
1213
1213
if err != nil {
1214
1214
log.Println("failed to get repo and knot", err)
1215
1215
return
···
1322
1322
return
1323
1323
}
1324
1324
1325
-
f, err := fullyResolvedRepo(r)
1325
+
f, err := s.fullyResolvedRepo(r)
1326
1326
if err != nil {
1327
1327
log.Println("failed to get repo and knot", err)
1328
1328
return
···
1470
1470
}
1471
1471
1472
1472
func (s *State) MergePull(w http.ResponseWriter, r *http.Request) {
1473
-
f, err := fullyResolvedRepo(r)
1473
+
f, err := s.fullyResolvedRepo(r)
1474
1474
if err != nil {
1475
1475
log.Println("failed to resolve repo:", err)
1476
1476
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
···
1535
1535
func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) {
1536
1536
user := s.auth.GetUser(r)
1537
1537
1538
-
f, err := fullyResolvedRepo(r)
1538
+
f, err := s.fullyResolvedRepo(r)
1539
1539
if err != nil {
1540
1540
log.Println("malformed middleware")
1541
1541
return
···
1589
1589
func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) {
1590
1590
user := s.auth.GetUser(r)
1591
1591
1592
-
f, err := fullyResolvedRepo(r)
1592
+
f, err := s.fullyResolvedRepo(r)
1593
1593
if err != nil {
1594
1594
log.Println("failed to resolve repo", err)
1595
1595
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
+35
-40
appview/state/repo.go
+35
-40
appview/state/repo.go
···
37
37
38
38
func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) {
39
39
ref := chi.URLParam(r, "ref")
40
-
f, err := fullyResolvedRepo(r)
40
+
f, err := s.fullyResolvedRepo(r)
41
41
if err != nil {
42
42
log.Println("failed to fully resolve repo", err)
43
43
return
···
129
129
}
130
130
131
131
func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) {
132
-
f, err := fullyResolvedRepo(r)
132
+
f, err := s.fullyResolvedRepo(r)
133
133
if err != nil {
134
134
log.Println("failed to fully resolve repo", err)
135
135
return
···
210
210
}
211
211
212
212
func (s *State) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
213
-
f, err := fullyResolvedRepo(r)
213
+
f, err := s.fullyResolvedRepo(r)
214
214
if err != nil {
215
215
log.Println("failed to get repo and knot", err)
216
216
w.WriteHeader(http.StatusBadRequest)
···
225
225
}
226
226
227
227
func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) {
228
-
f, err := fullyResolvedRepo(r)
228
+
f, err := s.fullyResolvedRepo(r)
229
229
if err != nil {
230
230
log.Println("failed to get repo and knot", err)
231
231
w.WriteHeader(http.StatusBadRequest)
···
304
304
}
305
305
306
306
func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) {
307
-
f, err := fullyResolvedRepo(r)
307
+
f, err := s.fullyResolvedRepo(r)
308
308
if err != nil {
309
309
log.Println("failed to fully resolve repo", err)
310
310
return
···
350
350
}
351
351
352
352
func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) {
353
-
f, err := fullyResolvedRepo(r)
353
+
f, err := s.fullyResolvedRepo(r)
354
354
if err != nil {
355
355
log.Println("failed to fully resolve repo", err)
356
356
return
···
378
378
err = json.Unmarshal(body, &result)
379
379
if err != nil {
380
380
log.Println("failed to parse response:", err)
381
+
return
382
+
}
383
+
384
+
// redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,
385
+
// so we can safely redirect to the "parent" (which is the same file).
386
+
if len(result.Files) == 0 && result.Parent == treePath {
387
+
http.Redirect(w, r, fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), ref, result.Parent), http.StatusFound)
381
388
return
382
389
}
383
390
···
406
413
}
407
414
408
415
func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) {
409
-
f, err := fullyResolvedRepo(r)
416
+
f, err := s.fullyResolvedRepo(r)
410
417
if err != nil {
411
418
log.Println("failed to get repo and knot", err)
412
419
return
···
447
454
}
448
455
449
456
func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) {
450
-
f, err := fullyResolvedRepo(r)
457
+
f, err := s.fullyResolvedRepo(r)
451
458
if err != nil {
452
459
log.Println("failed to get repo and knot", err)
453
460
return
···
502
509
}
503
510
504
511
func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) {
505
-
f, err := fullyResolvedRepo(r)
512
+
f, err := s.fullyResolvedRepo(r)
506
513
if err != nil {
507
514
log.Println("failed to get repo and knot", err)
508
515
return
···
562
569
}
563
570
564
571
func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
565
-
f, err := fullyResolvedRepo(r)
572
+
f, err := s.fullyResolvedRepo(r)
566
573
if err != nil {
567
574
log.Println("failed to get repo and knot", err)
568
575
return
···
606
613
}
607
614
608
615
func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) {
609
-
f, err := fullyResolvedRepo(r)
616
+
f, err := s.fullyResolvedRepo(r)
610
617
if err != nil {
611
618
log.Println("failed to get repo and knot", err)
612
619
return
···
697
704
func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) {
698
705
user := s.auth.GetUser(r)
699
706
700
-
f, err := fullyResolvedRepo(r)
707
+
f, err := s.fullyResolvedRepo(r)
701
708
if err != nil {
702
709
log.Println("failed to get repo and knot", err)
703
710
return
···
801
808
}
802
809
803
810
func (s *State) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
804
-
f, err := fullyResolvedRepo(r)
811
+
f, err := s.fullyResolvedRepo(r)
805
812
if err != nil {
806
813
log.Println("failed to get repo and knot", err)
807
814
return
···
840
847
}
841
848
842
849
func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) {
843
-
f, err := fullyResolvedRepo(r)
850
+
f, err := s.fullyResolvedRepo(r)
844
851
if err != nil {
845
852
log.Println("failed to get repo and knot", err)
846
853
return
···
891
898
}
892
899
}
893
900
894
-
resp, err = us.DefaultBranch(f.OwnerDid(), f.RepoName)
901
+
defaultBranchResp, err := us.DefaultBranch(f.OwnerDid(), f.RepoName)
895
902
if err != nil {
896
903
log.Println("failed to reach knotserver", err)
897
904
} else {
898
-
defer resp.Body.Close()
899
-
900
-
body, err := io.ReadAll(resp.Body)
901
-
if err != nil {
902
-
log.Printf("Error reading response body: %v", err)
903
-
} else {
904
-
var result types.RepoDefaultBranchResponse
905
-
err = json.Unmarshal(body, &result)
906
-
if err != nil {
907
-
log.Println("failed to parse response:", err)
908
-
} else {
909
-
defaultBranch = result.Branch
910
-
}
911
-
}
905
+
defaultBranch = defaultBranchResp.Branch
912
906
}
913
907
}
914
-
915
908
s.pages.RepoSettings(w, pages.RepoSettingsParams{
916
909
LoggedInUser: user,
917
910
RepoInfo: f.RepoInfo(s, user),
···
930
923
RepoAt syntax.ATURI
931
924
Description string
932
925
CreatedAt string
926
+
Ref string
933
927
}
934
928
935
929
func (f *FullyResolvedRepo) OwnerDid() string {
···
1082
1076
Name: f.RepoName,
1083
1077
RepoAt: f.RepoAt,
1084
1078
Description: f.Description,
1079
+
Ref: f.Ref,
1085
1080
IsStarred: isStarred,
1086
1081
Knot: knot,
1087
1082
Roles: RolesInRepo(s, u, f),
···
1103
1098
1104
1099
func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
1105
1100
user := s.auth.GetUser(r)
1106
-
f, err := fullyResolvedRepo(r)
1101
+
f, err := s.fullyResolvedRepo(r)
1107
1102
if err != nil {
1108
1103
log.Println("failed to get repo and knot", err)
1109
1104
return
···
1157
1152
1158
1153
func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) {
1159
1154
user := s.auth.GetUser(r)
1160
-
f, err := fullyResolvedRepo(r)
1155
+
f, err := s.fullyResolvedRepo(r)
1161
1156
if err != nil {
1162
1157
log.Println("failed to get repo and knot", err)
1163
1158
return
···
1229
1224
1230
1225
func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) {
1231
1226
user := s.auth.GetUser(r)
1232
-
f, err := fullyResolvedRepo(r)
1227
+
f, err := s.fullyResolvedRepo(r)
1233
1228
if err != nil {
1234
1229
log.Println("failed to get repo and knot", err)
1235
1230
return
···
1277
1272
1278
1273
func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) {
1279
1274
user := s.auth.GetUser(r)
1280
-
f, err := fullyResolvedRepo(r)
1275
+
f, err := s.fullyResolvedRepo(r)
1281
1276
if err != nil {
1282
1277
log.Println("failed to get repo and knot", err)
1283
1278
return
···
1356
1351
1357
1352
func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) {
1358
1353
user := s.auth.GetUser(r)
1359
-
f, err := fullyResolvedRepo(r)
1354
+
f, err := s.fullyResolvedRepo(r)
1360
1355
if err != nil {
1361
1356
log.Println("failed to get repo and knot", err)
1362
1357
return
···
1415
1410
1416
1411
func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) {
1417
1412
user := s.auth.GetUser(r)
1418
-
f, err := fullyResolvedRepo(r)
1413
+
f, err := s.fullyResolvedRepo(r)
1419
1414
if err != nil {
1420
1415
log.Println("failed to get repo and knot", err)
1421
1416
return
···
1540
1535
1541
1536
func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
1542
1537
user := s.auth.GetUser(r)
1543
-
f, err := fullyResolvedRepo(r)
1538
+
f, err := s.fullyResolvedRepo(r)
1544
1539
if err != nil {
1545
1540
log.Println("failed to get repo and knot", err)
1546
1541
return
···
1645
1640
}
1646
1641
1647
1642
user := s.auth.GetUser(r)
1648
-
f, err := fullyResolvedRepo(r)
1643
+
f, err := s.fullyResolvedRepo(r)
1649
1644
if err != nil {
1650
1645
log.Println("failed to get repo and knot", err)
1651
1646
return
···
1686
1681
func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) {
1687
1682
user := s.auth.GetUser(r)
1688
1683
1689
-
f, err := fullyResolvedRepo(r)
1684
+
f, err := s.fullyResolvedRepo(r)
1690
1685
if err != nil {
1691
1686
log.Println("failed to get repo and knot", err)
1692
1687
return
···
1768
1763
1769
1764
func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) {
1770
1765
user := s.auth.GetUser(r)
1771
-
f, err := fullyResolvedRepo(r)
1766
+
f, err := s.fullyResolvedRepo(r)
1772
1767
if err != nil {
1773
1768
log.Printf("failed to resolve source repo: %v", err)
1774
1769
return
+18
-1
appview/state/repo_util.go
+18
-1
appview/state/repo_util.go
···
17
17
"tangled.sh/tangled.sh/core/appview/pages"
18
18
)
19
19
20
-
func fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {
20
+
func (s *State) fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {
21
21
repoName := chi.URLParam(r, "repo")
22
22
knot, ok := r.Context().Value("knot").(string)
23
23
if !ok {
···
42
42
return nil, fmt.Errorf("malformed middleware")
43
43
}
44
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
+
45
61
// pass through values from the middleware
46
62
description, ok := r.Context().Value("repoDescription").(string)
47
63
addedAt, ok := r.Context().Value("repoAddedAt").(string)
···
53
69
RepoAt: parsedRepoAt,
54
70
Description: description,
55
71
CreatedAt: addedAt,
72
+
Ref: ref,
56
73
}, nil
57
74
}
58
75
+13
-2
appview/state/signer.go
+13
-2
appview/state/signer.go
···
380
380
return us.client.Do(req)
381
381
}
382
382
383
-
func (us *UnsignedClient) DefaultBranch(ownerDid, repoName string) (*http.Response, error) {
383
+
func (us *UnsignedClient) DefaultBranch(ownerDid, repoName string) (*types.RepoDefaultBranchResponse, error) {
384
384
const (
385
385
Method = "GET"
386
386
)
···
392
392
return nil, err
393
393
}
394
394
395
-
return us.client.Do(req)
395
+
resp, err := us.client.Do(req)
396
+
if err != nil {
397
+
return nil, err
398
+
}
399
+
defer resp.Body.Close()
400
+
401
+
var defaultBranch types.RepoDefaultBranchResponse
402
+
if err := json.NewDecoder(resp.Body).Decode(&defaultBranch); err != nil {
403
+
return nil, err
404
+
}
405
+
406
+
return &defaultBranch, nil
396
407
}
397
408
398
409
func (us *UnsignedClient) Capabilities() (*types.Capabilities, error) {