tangled
alpha
login
or
join now
lewis.moe
/
tangled-core
forked from
tangled.org/core
1
fork
atom
Monorepo for Tangled
1
fork
atom
overview
issues
1
pulls
pipelines
appview: repo file content
anirudh.fi
1 year ago
96908354
cd91ad92
+108
-85
10 changed files
expand all
collapse all
unified
split
appview
pages
pages.go
templates
repo
blob.html
file.html
state
repo.go
state.go
knotserver
file.go
handler.go
routes.go
types
repo.go
tree.go
+24
-1
appview/pages/pages.go
···
22
22
t map[string]*template.Template
23
23
}
24
24
25
25
+
func funcMap() template.FuncMap {
26
26
+
return template.FuncMap{
27
27
+
"split": func(s string) []string {
28
28
+
return strings.Split(s, "\n")
29
29
+
},
30
30
+
"add": func(a, b int) int {
31
31
+
return a + b
32
32
+
},
33
33
+
}
34
34
+
}
35
35
+
25
36
func NewPages() *Pages {
26
37
templates := make(map[string]*template.Template)
27
38
···
37
48
38
49
if !strings.HasPrefix(path, "templates/layouts/") {
39
50
// Add the page template on top of the base
40
40
-
tmpl, err := template.New(name).ParseFS(files, "templates/layouts/*.html", path)
51
51
+
tmpl, err := template.New(name).
52
52
+
Funcs(funcMap()).
53
53
+
ParseFS(files, "templates/layouts/*.html", path)
41
54
if err != nil {
42
55
return fmt.Errorf("setting up template: %w", err)
43
56
}
···
215
228
func (p *Pages) RepoTags(w io.Writer, params RepoTagsParams) error {
216
229
return p.executeRepo("repo/tags", w, params)
217
230
}
231
231
+
232
232
+
type RepoBlobParams struct {
233
233
+
LoggedInUser *auth.User
234
234
+
RepoInfo RepoInfo
235
235
+
types.RepoBlobResponse
236
236
+
}
237
237
+
238
238
+
func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error {
239
239
+
return p.executeRepo("repo/blob", w, params)
240
240
+
}
+12
appview/pages/templates/repo/blob.html
···
1
1
+
{{ define "repoContent" }}
2
2
+
<table>
3
3
+
<tbody><tr>
4
4
+
<td class="line-numbers">
5
5
+
{{ range $i, $x := split .Contents }}
6
6
+
{{ add $i 1 }} {{ $x }} <br>
7
7
+
{{ end }}
8
8
+
</td>
9
9
+
<td class="file-content"></td>
10
10
+
</tbody></tr>
11
11
+
</table>
12
12
+
{{end}}
-34
appview/pages/templates/repo/file.html
···
1
1
-
<html>
2
2
-
{{ template "layouts/head" . }}
3
3
-
{{ template "layouts/repo-header" . }}
4
4
-
<body>
5
5
-
{{ template "layouts/nav" . }}
6
6
-
<main>
7
7
-
<p>{{ .path }} (<a style="color: gray" href="?raw=true">view raw</a>)</p>
8
8
-
{{if .chroma }}
9
9
-
<div class="chroma-file-wrapper">
10
10
-
{{ .content }}
11
11
-
</div>
12
12
-
{{else}}
13
13
-
<div class="file-wrapper">
14
14
-
<table>
15
15
-
<tbody><tr>
16
16
-
<td class="line-numbers">
17
17
-
<pre>
18
18
-
{{- range .linecount }}
19
19
-
<a id="L{{ . }}" href="#L{{ . }}">{{ . }}</a>
20
20
-
{{- end -}}
21
21
-
</pre>
22
22
-
</td>
23
23
-
<td class="file-content">
24
24
-
<pre>
25
25
-
{{- .content -}}
26
26
-
</pre>
27
27
-
</td>
28
28
-
</tbody></tr>
29
29
-
</table>
30
30
-
</div>
31
31
-
{{end}}
32
32
-
</main>
33
33
-
</body>
34
34
-
</html>
+40
-3
appview/state/repo.go
···
48
48
OwnerDid: id.DID.String(),
49
49
OwnerHandle: id.Handle.String(),
50
50
Name: repoName,
51
51
-
Description: result.Description,
52
51
},
53
52
RepoIndexResponse: result,
54
53
})
···
89
88
OwnerDid: id.DID.String(),
90
89
OwnerHandle: id.Handle.String(),
91
90
Name: repoName,
92
92
-
Description: result.Description,
93
91
},
94
92
RepoLogResponse: result,
95
93
})
···
171
169
OwnerDid: id.DID.String(),
172
170
OwnerHandle: id.Handle.String(),
173
171
Name: repoName,
174
174
-
Description: result.Description,
175
172
},
176
173
RepoTreeResponse: result,
177
174
})
···
250
247
Name: repoName,
251
248
},
252
249
RepoBranchesResponse: result,
250
250
+
})
251
251
+
return
252
252
+
}
253
253
+
254
254
+
func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) {
255
255
+
repoName, knot, id, err := repoKnotAndId(r)
256
256
+
if err != nil {
257
257
+
log.Println("failed to get repo and knot", err)
258
258
+
return
259
259
+
}
260
260
+
261
261
+
ref := chi.URLParam(r, "ref")
262
262
+
filePath := chi.URLParam(r, "*")
263
263
+
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/blob/%s/%s", knot, id.DID.String(), repoName, ref, filePath))
264
264
+
if err != nil {
265
265
+
log.Println("failed to reach knotserver", err)
266
266
+
return
267
267
+
}
268
268
+
269
269
+
body, err := io.ReadAll(resp.Body)
270
270
+
if err != nil {
271
271
+
log.Fatalf("Error reading response body: %v", err)
272
272
+
return
273
273
+
}
274
274
+
275
275
+
var result types.RepoBlobResponse
276
276
+
err = json.Unmarshal(body, &result)
277
277
+
if err != nil {
278
278
+
log.Println("failed to parse response:", err)
279
279
+
return
280
280
+
}
281
281
+
282
282
+
s.pages.RepoBlob(w, pages.RepoBlobParams{
283
283
+
LoggedInUser: s.auth.GetUser(r),
284
284
+
RepoInfo: pages.RepoInfo{
285
285
+
OwnerDid: id.DID.String(),
286
286
+
OwnerHandle: id.Handle.String(),
287
287
+
Name: repoName,
288
288
+
},
289
289
+
RepoBlobResponse: result,
253
290
})
254
291
return
255
292
}
+1
appview/state/state.go
···
617
617
r.Get("/commit/{ref}", s.RepoCommit)
618
618
r.Get("/branches", s.RepoBranches)
619
619
r.Get("/tags", s.RepoTags)
620
620
+
r.Get("/blob/{ref}/*", s.RepoBlob)
620
621
621
622
// These routes get proxied to the knot
622
623
r.Get("/info/refs", s.InfoRefs)
+4
-20
knotserver/file.go
···
43
43
}
44
44
}
45
45
46
46
-
func (h *Handle) showFile(content string, data map[string]any, w http.ResponseWriter, l *slog.Logger) {
47
47
-
lc, err := countLines(strings.NewReader(content))
46
46
+
func (h *Handle) showFile(resp types.RepoBlobResponse, w http.ResponseWriter, l *slog.Logger) {
47
47
+
lc, err := countLines(strings.NewReader(resp.Contents))
48
48
if err != nil {
49
49
// Non-fatal, we'll just skip showing line numbers in the template.
50
50
l.Warn("counting lines", "error", err)
51
51
}
52
52
53
53
-
lines := make([]int, lc)
54
54
-
if lc > 0 {
55
55
-
for i := range lines {
56
56
-
lines[i] = i + 1
57
57
-
}
58
58
-
}
59
59
-
60
60
-
data["linecount"] = lines
61
61
-
data["content"] = content
62
62
-
63
63
-
writeJSON(w, data)
64
64
-
return
65
65
-
}
66
66
-
67
67
-
func (h *Handle) showRaw(content string, w http.ResponseWriter) {
68
68
-
w.WriteHeader(http.StatusOK)
69
69
-
w.Header().Set("Content-Type", "text/plain")
70
70
-
w.Write([]byte(content))
53
53
+
resp.Lines = lc
54
54
+
writeJSON(w, resp)
71
55
return
72
56
}
+1
-1
knotserver/handler.go
···
78
78
})
79
79
80
80
r.Route("/blob/{ref}", func(r chi.Router) {
81
81
-
r.Get("/*", h.FileContent)
81
81
+
r.Get("/*", h.Blob)
82
82
})
83
83
84
84
r.Get("/log/{ref}", h.Log)
+13
-21
knotserver/routes.go
···
129
129
DotDot: filepath.Dir(treePath),
130
130
Files: files,
131
131
}
132
132
-
// data := make(map[string]any)
133
133
-
// data["ref"] = ref
134
134
-
// data["parent"] = treePath
135
135
-
// data["desc"] = getDescription(path)
136
136
-
// data["dotdot"] = filepath.Dir(treePath)
137
132
138
133
writeJSON(w, resp)
139
139
-
// h.listFiles(files, data, w)
140
134
return
141
135
}
142
136
143
143
-
func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) {
144
144
-
var raw bool
145
145
-
if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil {
146
146
-
raw = rawParam
147
147
-
}
148
148
-
137
137
+
func (h *Handle) Blob(w http.ResponseWriter, r *http.Request) {
149
138
treePath := chi.URLParam(r, "*")
150
139
ref := chi.URLParam(r, "ref")
151
140
···
163
152
writeError(w, err.Error(), http.StatusInternalServerError)
164
153
return
165
154
}
166
166
-
data := make(map[string]any)
167
167
-
data["ref"] = ref
168
168
-
data["desc"] = getDescription(path)
169
169
-
data["path"] = treePath
170
155
171
171
-
safe := sanitize([]byte(contents))
156
156
+
safe := string(sanitize([]byte(contents)))
172
157
173
173
-
if raw {
174
174
-
h.showRaw(string(safe), w)
175
175
-
} else {
176
176
-
h.showFile(string(safe), data, w, l)
158
158
+
resp := types.RepoBlobResponse{
159
159
+
Ref: ref,
160
160
+
Contents: string(safe),
161
161
+
Path: treePath,
177
162
}
163
163
+
164
164
+
h.showFile(resp, w, l)
178
165
}
179
166
180
167
func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
···
341
328
},
342
329
Tag: tag.TagObject(),
343
330
}
331
331
+
332
332
+
if tag.Message() != "" {
333
333
+
tr.Message = tag.Message()
334
334
+
}
335
335
+
344
336
rtags = append(rtags, &tr)
345
337
}
346
338
+8
types/repo.go
···
59
59
type RepoBranchesResponse struct {
60
60
Branches []Branch `json:"branches,omitempty"`
61
61
}
62
62
+
63
63
+
type RepoBlobResponse struct {
64
64
+
Contents string `json:"contents,omitempty"`
65
65
+
Ref string `json:"ref,omitempty"`
66
66
+
Path string `json:"path,omitempty"`
67
67
+
68
68
+
Lines int `json:"lines,omitempty"`
69
69
+
}
+5
-5
types/tree.go
···
2
2
3
3
// A nicer git tree representation.
4
4
type NiceTree struct {
5
5
-
Name string
6
6
-
Mode string
7
7
-
Size int64
8
8
-
IsFile bool
9
9
-
IsSubtree bool
5
5
+
Name string `json:"name"`
6
6
+
Mode string `json:"mode"`
7
7
+
Size int64 `json:"size"`
8
8
+
IsFile bool `json:"is_file"`
9
9
+
IsSubtree bool `json:"is_subtree"`
10
10
}