Monorepo for Tangled tangled.org

appview: repo file content

anirudh.fi f17f72d0 a0941c5b

verified
Changed files
+108 -85
appview
pages
templates
state
knotserver
types
+24 -1
appview/pages/pages.go
··· 22 22 t map[string]*template.Template 23 23 } 24 24 25 + func funcMap() template.FuncMap { 26 + return template.FuncMap{ 27 + "split": func(s string) []string { 28 + return strings.Split(s, "\n") 29 + }, 30 + "add": func(a, b int) int { 31 + return a + b 32 + }, 33 + } 34 + } 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 - tmpl, err := template.New(name).ParseFS(files, "templates/layouts/*.html", path) 51 + tmpl, err := template.New(name). 52 + Funcs(funcMap()). 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 + 232 + type RepoBlobParams struct { 233 + LoggedInUser *auth.User 234 + RepoInfo RepoInfo 235 + types.RepoBlobResponse 236 + } 237 + 238 + func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error { 239 + return p.executeRepo("repo/blob", w, params) 240 + }
+12
appview/pages/templates/repo/blob.html
··· 1 + {{ define "repoContent" }} 2 + <table> 3 + <tbody><tr> 4 + <td class="line-numbers"> 5 + {{ range $i, $x := split .Contents }} 6 + {{ add $i 1 }} {{ $x }} <br> 7 + {{ end }} 8 + </td> 9 + <td class="file-content"></td> 10 + </tbody></tr> 11 + </table> 12 + {{end}}
-34
appview/pages/templates/repo/file.html
··· 1 - <html> 2 - {{ template "layouts/head" . }} 3 - {{ template "layouts/repo-header" . }} 4 - <body> 5 - {{ template "layouts/nav" . }} 6 - <main> 7 - <p>{{ .path }} (<a style="color: gray" href="?raw=true">view raw</a>)</p> 8 - {{if .chroma }} 9 - <div class="chroma-file-wrapper"> 10 - {{ .content }} 11 - </div> 12 - {{else}} 13 - <div class="file-wrapper"> 14 - <table> 15 - <tbody><tr> 16 - <td class="line-numbers"> 17 - <pre> 18 - {{- range .linecount }} 19 - <a id="L{{ . }}" href="#L{{ . }}">{{ . }}</a> 20 - {{- end -}} 21 - </pre> 22 - </td> 23 - <td class="file-content"> 24 - <pre> 25 - {{- .content -}} 26 - </pre> 27 - </td> 28 - </tbody></tr> 29 - </table> 30 - </div> 31 - {{end}} 32 - </main> 33 - </body> 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 - 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 - 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 - Description: result.Description, 175 172 }, 176 173 RepoTreeResponse: result, 177 174 }) ··· 250 247 Name: repoName, 251 248 }, 252 249 RepoBranchesResponse: result, 250 + }) 251 + return 252 + } 253 + 254 + func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) { 255 + repoName, knot, id, err := repoKnotAndId(r) 256 + if err != nil { 257 + log.Println("failed to get repo and knot", err) 258 + return 259 + } 260 + 261 + ref := chi.URLParam(r, "ref") 262 + filePath := chi.URLParam(r, "*") 263 + resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/blob/%s/%s", knot, id.DID.String(), repoName, ref, filePath)) 264 + if err != nil { 265 + log.Println("failed to reach knotserver", err) 266 + return 267 + } 268 + 269 + body, err := io.ReadAll(resp.Body) 270 + if err != nil { 271 + log.Fatalf("Error reading response body: %v", err) 272 + return 273 + } 274 + 275 + var result types.RepoBlobResponse 276 + err = json.Unmarshal(body, &result) 277 + if err != nil { 278 + log.Println("failed to parse response:", err) 279 + return 280 + } 281 + 282 + s.pages.RepoBlob(w, pages.RepoBlobParams{ 283 + LoggedInUser: s.auth.GetUser(r), 284 + RepoInfo: pages.RepoInfo{ 285 + OwnerDid: id.DID.String(), 286 + OwnerHandle: id.Handle.String(), 287 + Name: repoName, 288 + }, 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 + 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 - func (h *Handle) showFile(content string, data map[string]any, w http.ResponseWriter, l *slog.Logger) { 47 - lc, err := countLines(strings.NewReader(content)) 46 + func (h *Handle) showFile(resp types.RepoBlobResponse, w http.ResponseWriter, l *slog.Logger) { 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 - lines := make([]int, lc) 54 - if lc > 0 { 55 - for i := range lines { 56 - lines[i] = i + 1 57 - } 58 - } 59 - 60 - data["linecount"] = lines 61 - data["content"] = content 62 - 63 - writeJSON(w, data) 64 - return 65 - } 66 - 67 - func (h *Handle) showRaw(content string, w http.ResponseWriter) { 68 - w.WriteHeader(http.StatusOK) 69 - w.Header().Set("Content-Type", "text/plain") 70 - w.Write([]byte(content)) 53 + resp.Lines = lc 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 - r.Get("/*", h.FileContent) 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 - // data := make(map[string]any) 133 - // data["ref"] = ref 134 - // data["parent"] = treePath 135 - // data["desc"] = getDescription(path) 136 - // data["dotdot"] = filepath.Dir(treePath) 137 132 138 133 writeJSON(w, resp) 139 - // h.listFiles(files, data, w) 140 134 return 141 135 } 142 136 143 - func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) { 144 - var raw bool 145 - if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil { 146 - raw = rawParam 147 - } 148 - 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 - data := make(map[string]any) 167 - data["ref"] = ref 168 - data["desc"] = getDescription(path) 169 - data["path"] = treePath 170 155 171 - safe := sanitize([]byte(contents)) 156 + safe := string(sanitize([]byte(contents))) 172 157 173 - if raw { 174 - h.showRaw(string(safe), w) 175 - } else { 176 - h.showFile(string(safe), data, w, l) 158 + resp := types.RepoBlobResponse{ 159 + Ref: ref, 160 + Contents: string(safe), 161 + Path: treePath, 177 162 } 163 + 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 + 332 + if tag.Message() != "" { 333 + tr.Message = tag.Message() 334 + } 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 + 63 + type RepoBlobResponse struct { 64 + Contents string `json:"contents,omitempty"` 65 + Ref string `json:"ref,omitempty"` 66 + Path string `json:"path,omitempty"` 67 + 68 + Lines int `json:"lines,omitempty"` 69 + }
+5 -5
types/tree.go
··· 2 2 3 3 // A nicer git tree representation. 4 4 type NiceTree struct { 5 - Name string 6 - Mode string 7 - Size int64 8 - IsFile bool 9 - IsSubtree bool 5 + Name string `json:"name"` 6 + Mode string `json:"mode"` 7 + Size int64 `json:"size"` 8 + IsFile bool `json:"is_file"` 9 + IsSubtree bool `json:"is_subtree"` 10 10 }