+10
appview/pages/pages.go
+10
appview/pages/pages.go
···
175
175
func (p *Pages) RepoCommit(w io.Writer, params RepoCommitParams) error {
176
176
return p.execute("repo/commit", w, params)
177
177
}
178
+
179
+
type RepoTreeParams struct {
180
+
LoggedInUser *auth.User
181
+
RepoInfo RepoInfo
182
+
types.RepoTreeResponse
183
+
}
184
+
185
+
func (p *Pages) RepoTree(w io.Writer, params RepoTreeParams) error {
186
+
return p.execute("repo/tree", w, params)
187
+
}
+2
-2
appview/pages/templates/repo/index.html
+2
-2
appview/pages/templates/repo/index.html
···
1
-
{{define "title"}} {{ .RepoInfo.OwnerWithAt }} / {{ .RepoInfo.Name }} {{end}}
1
+
{{define "title"}} {{ .RepoInfo.FullName }} {{end}}
2
2
3
3
{{define "content"}}
4
4
5
5
<h1>
6
-
{{ .RepoInfo.OwnerWithAt }} / {{ .RepoInfo.Name }}
6
+
{{ .RepoInfo.FullName }}
7
7
</h1>
8
8
<main>
9
9
{{- if .IsEmpty }}
+21
-26
appview/pages/templates/repo/tree.html
+21
-26
appview/pages/templates/repo/tree.html
···
1
-
<html>
1
+
{{define "title"}} {{ .RepoInfo.FullName }} {{end}}
2
2
3
-
{{ template "layouts/head" . }}
3
+
{{define "content"}}
4
4
5
-
{{ template "layouts/repo-header" . }}
6
-
<body>
7
-
{{ template "layouts/nav" . }}
5
+
<h1>
6
+
{{ .RepoInfo.FullName }}
7
+
</h1>
8
8
<main>
9
-
{{ $repo := .name }}
10
-
{{ $ref := .ref }}
11
-
{{ $parent := .parent }}
12
-
13
9
<div class="tree">
14
-
{{ if $parent }}
10
+
11
+
{{ if .Parent }}
15
12
<div></div>
16
13
<div></div>
17
-
<div><a href="/{{ $repo }}/tree/{{ $ref }}/{{ .dotdot }}">..</a></div>
14
+
<div><a href="/{{ .RepoInfo.FullName }}/tree/{{ .Ref }}/{{ .DotDot }}">..</a></div>
18
15
{{ end }}
19
-
{{ range .files }}
16
+
17
+
{{ range .Files }}
20
18
{{ if not .IsFile }}
21
19
<div class="mode">{{ .Mode }}</div>
22
20
<div class="size">{{ .Size }}</div>
23
21
<div>
24
-
{{ if $parent }}
25
-
<a href="/{{ $repo }}/tree/{{ $ref }}/{{ $parent }}/{{ .Name }}">{{ .Name }}/</a>
22
+
{{ if $.Parent }}
23
+
<a href="/{{ .RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ $.Parent }}/{{ .Name }}">{{ .Name }}/</a>
26
24
{{ else }}
27
-
<a href="/{{ $repo }}/tree/{{ $ref }}/{{ .Name }}">{{ .Name }}/</a>
25
+
<a href="/{{ .RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ .Name }}">{{ .Name }}/</a>
28
26
{{ end }}
29
27
</div>
28
+
<hr />
30
29
{{ end }}
31
30
{{ end }}
32
-
{{ range .files }}
31
+
32
+
{{ range .Files }}
33
33
{{ if .IsFile }}
34
34
<div class="mode">{{ .Mode }}</div>
35
35
<div class="size">{{ .Size }}</div>
36
36
<div>
37
-
{{ if $parent }}
38
-
<a href="/{{ $repo }}/blob/{{ $ref }}/{{ $parent }}/{{ .Name }}">{{ .Name }}</a>
37
+
{{ if $.Parent }}
38
+
<a href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ $.Parent }}/{{ .Name }}">{{ .Name }}</a>
39
39
{{ else }}
40
-
<a href="/{{ $repo }}/blob/{{ $ref }}/{{ .Name }}">{{ .Name }}</a>
40
+
<a href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ .Name }}">{{ .Name }}</a>
41
41
{{ end }}
42
42
</div>
43
+
<hr />
43
44
{{ end }}
44
45
{{ end }}
45
46
</div>
46
-
<article>
47
-
<pre>
48
-
{{- if .readme }}{{ .readme }}{{- end -}}
49
-
</pre>
50
-
</article>
51
47
</main>
52
-
</body>
53
-
</html>
48
+
{{end}}
+42
appview/state/repo.go
+42
appview/state/repo.go
···
133
133
return
134
134
}
135
135
136
+
func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) {
137
+
repoName, knot, id, err := repoKnotAndId(r)
138
+
if err != nil {
139
+
log.Println("failed to get repo and knot", err)
140
+
return
141
+
}
142
+
143
+
ref := chi.URLParam(r, "ref")
144
+
treePath := chi.URLParam(r, "*")
145
+
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/tree/%s/%s", knot, id.DID.String(), repoName, ref, treePath))
146
+
if err != nil {
147
+
log.Println("failed to reach knotserver", err)
148
+
return
149
+
}
150
+
151
+
body, err := io.ReadAll(resp.Body)
152
+
if err != nil {
153
+
log.Fatalf("Error reading response body: %v", err)
154
+
return
155
+
}
156
+
157
+
var result types.RepoTreeResponse
158
+
err = json.Unmarshal(body, &result)
159
+
if err != nil {
160
+
log.Println("failed to parse response:", err)
161
+
return
162
+
}
163
+
164
+
log.Println(result)
165
+
166
+
s.pages.RepoTree(w, pages.RepoTreeParams{
167
+
LoggedInUser: s.auth.GetUser(r),
168
+
RepoInfo: pages.RepoInfo{
169
+
OwnerDid: id.DID.String(),
170
+
OwnerHandle: id.Handle.String(),
171
+
Name: repoName,
172
+
},
173
+
RepoTreeResponse: result,
174
+
})
175
+
return
176
+
}
177
+
136
178
func repoKnotAndId(r *http.Request) (string, string, identity.Identity, error) {
137
179
repoName := chi.URLParam(r, "repo")
138
180
knot, ok := r.Context().Value("knot").(string)
+3
appview/state/state.go
+3
appview/state/state.go
···
602
602
r.With(ResolveRepoKnot(s)).Route("/{repo}", func(r chi.Router) {
603
603
r.Get("/", s.RepoIndex)
604
604
r.Get("/log/{ref}", s.RepoLog)
605
+
r.Route("/tree/{ref}", func(r chi.Router) {
606
+
r.Get("/*", s.RepoTree)
607
+
})
605
608
r.Get("/commit/{ref}", s.RepoCommit)
606
609
607
610
// These routes get proxied to the knot
+6
-14
knotserver/git/tree.go
+6
-14
knotserver/git/tree.go
···
4
4
"fmt"
5
5
6
6
"github.com/go-git/go-git/v5/plumbing/object"
7
+
"github.com/sotangled/tangled/types"
7
8
)
8
9
9
-
func (g *GitRepo) FileTree(path string) ([]NiceTree, error) {
10
+
func (g *GitRepo) FileTree(path string) ([]types.NiceTree, error) {
10
11
c, err := g.r.CommitObject(g.h)
11
12
if err != nil {
12
13
return nil, fmt.Errorf("commit object: %w", err)
13
14
}
14
15
15
-
files := []NiceTree{}
16
+
files := []types.NiceTree{}
16
17
tree, err := c.Tree()
17
18
if err != nil {
18
19
return nil, fmt.Errorf("file tree: %w", err)
···
39
40
return files, nil
40
41
}
41
42
42
-
// A nicer git tree representation.
43
-
type NiceTree struct {
44
-
Name string
45
-
Mode string
46
-
Size int64
47
-
IsFile bool
48
-
IsSubtree bool
49
-
}
50
-
51
-
func makeNiceTree(t *object.Tree) []NiceTree {
52
-
nts := []NiceTree{}
43
+
func makeNiceTree(t *object.Tree) []types.NiceTree {
44
+
nts := []types.NiceTree{}
53
45
54
46
for _, e := range t.Entries {
55
47
mode, _ := e.Mode.ToOSFileMode()
56
48
sz, _ := t.Size(e.Name)
57
-
nts = append(nts, NiceTree{
49
+
nts = append(nts, types.NiceTree{
58
50
Name: e.Name,
59
51
Mode: mode.String(),
60
52
IsFile: e.Mode.IsFile(),
+14
-6
knotserver/routes.go
+14
-6
knotserver/routes.go
···
122
122
return
123
123
}
124
124
125
-
data := make(map[string]any)
126
-
data["ref"] = ref
127
-
data["parent"] = treePath
128
-
data["desc"] = getDescription(path)
129
-
data["dotdot"] = filepath.Dir(treePath)
125
+
resp := types.RepoTreeResponse{
126
+
Ref: ref,
127
+
Parent: treePath,
128
+
Description: getDescription(path),
129
+
DotDot: filepath.Dir(treePath),
130
+
Files: files,
131
+
}
132
+
// data := make(map[string]any)
133
+
// data["ref"] = ref
134
+
// data["parent"] = treePath
135
+
// data["desc"] = getDescription(path)
136
+
// data["dotdot"] = filepath.Dir(treePath)
130
137
131
-
h.listFiles(files, data, w)
138
+
writeJSON(w, resp)
139
+
// h.listFiles(files, data, w)
132
140
return
133
141
}
134
142
+8
-6
types/repo.go
+8
-6
types/repo.go
···
24
24
PerPage int `json:"per_page,omitempty"`
25
25
}
26
26
27
-
// data["commit"] = diff.Commit
28
-
//
29
-
// data["stat"] = diff.Stat
30
-
// data["diff"] = diff.Diff
31
-
// data["ref"] = ref
32
-
// data["desc"] = getDescription(path)
33
27
type RepoCommitResponse struct {
34
28
Ref string `json:"ref,omitempty"`
35
29
Diff *NiceDiff `json:"diff,omitempty"`
36
30
}
31
+
32
+
type RepoTreeResponse struct {
33
+
Ref string `json:"ref,omitempty"`
34
+
Parent string `json:"parent,omitempty"`
35
+
Description string `json:"description,omitempty"`
36
+
DotDot string `json:"dotdot,omitempty"`
37
+
Files []NiceTree `json:"files,omitempty"`
38
+
}