this repo has no description
1package knotserver
2
3import (
4 "compress/gzip"
5 "errors"
6 "fmt"
7 "html/template"
8 "log"
9 "net/http"
10 "path/filepath"
11 "strconv"
12 "strings"
13
14 "github.com/go-chi/chi/v5"
15 "github.com/go-git/go-git/v5/plumbing"
16 "github.com/icyphox/bild/git"
17 "github.com/russross/blackfriday/v2"
18)
19
20func (h *Handle) Index(w http.ResponseWriter, r *http.Request) {
21 w.Write([]byte("This is a knot, part of the wider Tangle network: https://knots.sh"))
22}
23
24func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
25 path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
26
27 gr, err := git.Open(path, "")
28 if err != nil {
29 if errors.Is(err, plumbing.ErrReferenceNotFound) {
30 writeMsg(w, "repo empty")
31 return
32 } else {
33 log.Println(err)
34 notFound(w)
35 return
36 }
37 }
38 commits, err := gr.Commits()
39 if err != nil {
40 writeError(w, err.Error(), http.StatusInternalServerError)
41 log.Println(err)
42 return
43 }
44
45 var readmeContent template.HTML
46 for _, readme := range h.c.Repo.Readme {
47 ext := filepath.Ext(readme)
48 content, _ := gr.FileContent(readme)
49 if len(content) > 0 {
50 switch ext {
51 case ".md", ".mkd", ".markdown":
52 unsafe := blackfriday.Run(
53 []byte(content),
54 blackfriday.WithExtensions(blackfriday.CommonExtensions),
55 )
56 html := sanitize(unsafe)
57 readmeContent = template.HTML(html)
58 default:
59 safe := sanitize([]byte(content))
60 readmeContent = template.HTML(
61 fmt.Sprintf(`<pre>%s</pre>`, safe),
62 )
63 }
64 break
65 }
66 }
67
68 if readmeContent == "" {
69 log.Printf("no readme found for %s", path)
70 }
71
72 mainBranch, err := gr.FindMainBranch(h.c.Repo.MainBranch)
73 if err != nil {
74 writeError(w, err.Error(), http.StatusInternalServerError)
75 log.Println(err)
76 return
77 }
78
79 if len(commits) >= 3 {
80 commits = commits[:3]
81 }
82 data := make(map[string]any)
83 data["ref"] = mainBranch
84 data["readme"] = readmeContent
85 data["commits"] = commits
86 data["desc"] = getDescription(path)
87 data["servername"] = h.c.Server.Name
88 data["meta"] = h.c.Meta
89
90 writeJSON(w, data)
91 return
92}
93
94func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) {
95 treePath := chi.URLParam(r, "*")
96 ref := chi.URLParam(r, "ref")
97
98 path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
99 gr, err := git.Open(path, ref)
100 if err != nil {
101 notFound(w)
102 return
103 }
104
105 files, err := gr.FileTree(treePath)
106 if err != nil {
107 writeError(w, err.Error(), http.StatusInternalServerError)
108 log.Println(err)
109 return
110 }
111
112 data := make(map[string]any)
113 data["ref"] = ref
114 data["parent"] = treePath
115 data["desc"] = getDescription(path)
116 data["dotdot"] = filepath.Dir(treePath)
117
118 h.listFiles(files, data, w)
119 return
120}
121
122func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) {
123 var raw bool
124 if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil {
125 raw = rawParam
126 }
127
128 treePath := chi.URLParam(r, "*")
129 ref := chi.URLParam(r, "ref")
130
131 path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
132 gr, err := git.Open(path, ref)
133 if err != nil {
134 notFound(w)
135 return
136 }
137
138 contents, err := gr.FileContent(treePath)
139 if err != nil {
140 writeError(w, err.Error(), http.StatusInternalServerError)
141 return
142 }
143 data := make(map[string]any)
144 data["ref"] = ref
145 data["desc"] = getDescription(path)
146 data["path"] = treePath
147
148 safe := sanitize([]byte(contents))
149
150 if raw {
151 h.showRaw(string(safe), w)
152 } else {
153 h.showFile(string(safe), data, w)
154 }
155}
156
157func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
158 name := chi.URLParam(r, "name")
159 file := chi.URLParam(r, "file")
160
161 // TODO: extend this to add more files compression (e.g.: xz)
162 if !strings.HasSuffix(file, ".tar.gz") {
163 notFound(w)
164 return
165 }
166
167 ref := strings.TrimSuffix(file, ".tar.gz")
168
169 // This allows the browser to use a proper name for the file when
170 // downloading
171 filename := fmt.Sprintf("%s-%s.tar.gz", name, ref)
172 setContentDisposition(w, filename)
173 setGZipMIME(w)
174
175 path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
176 gr, err := git.Open(path, ref)
177 if err != nil {
178 notFound(w)
179 return
180 }
181
182 gw := gzip.NewWriter(w)
183 defer gw.Close()
184
185 prefix := fmt.Sprintf("%s-%s", name, ref)
186 err = gr.WriteTar(gw, prefix)
187 if err != nil {
188 // once we start writing to the body we can't report error anymore
189 // so we are only left with printing the error.
190 log.Println(err)
191 return
192 }
193
194 err = gw.Flush()
195 if err != nil {
196 // once we start writing to the body we can't report error anymore
197 // so we are only left with printing the error.
198 log.Println(err)
199 return
200 }
201}
202
203func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
204 fmt.Println(r.URL.Path)
205 ref := chi.URLParam(r, "ref")
206
207 path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
208 gr, err := git.Open(path, ref)
209 if err != nil {
210 notFound(w)
211 return
212 }
213
214 commits, err := gr.Commits()
215 if err != nil {
216 writeError(w, err.Error(), http.StatusInternalServerError)
217 log.Println(err)
218 return
219 }
220
221 data := make(map[string]interface{})
222 data["commits"] = commits
223 data["meta"] = h.c.Meta
224 data["ref"] = ref
225 data["desc"] = getDescription(path)
226 data["log"] = true
227
228 writeJSON(w, data)
229 return
230}
231
232func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
233 ref := chi.URLParam(r, "ref")
234
235 path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
236 gr, err := git.Open(path, ref)
237 if err != nil {
238 notFound(w)
239 return
240 }
241
242 diff, err := gr.Diff()
243 if err != nil {
244 writeError(w, err.Error(), http.StatusInternalServerError)
245 log.Println(err)
246 return
247 }
248
249 data := make(map[string]interface{})
250
251 data["commit"] = diff.Commit
252 data["stat"] = diff.Stat
253 data["diff"] = diff.Diff
254 data["meta"] = h.c.Meta
255 data["ref"] = ref
256 data["desc"] = getDescription(path)
257
258 writeJSON(w, data)
259 return
260}
261
262func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) {
263 path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
264 gr, err := git.Open(path, "")
265 if err != nil {
266 notFound(w)
267 return
268 }
269
270 tags, err := gr.Tags()
271 if err != nil {
272 // Non-fatal, we *should* have at least one branch to show.
273 log.Println(err)
274 }
275
276 branches, err := gr.Branches()
277 if err != nil {
278 log.Println(err)
279 writeError(w, err.Error(), http.StatusInternalServerError)
280 return
281 }
282
283 data := make(map[string]interface{})
284
285 data["meta"] = h.c.Meta
286 data["branches"] = branches
287 data["tags"] = tags
288 data["desc"] = getDescription(path)
289
290 writeJSON(w, data)
291 return
292}
293
294func (h *Handle) ServeStatic(w http.ResponseWriter, r *http.Request) {
295 f := chi.URLParam(r, "file")
296 f = filepath.Clean(filepath.Join(h.c.Dirs.Static, f))
297
298 http.ServeFile(w, r, f)
299}
300
301// func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
302// session, _ := h.s.Get(r, "bild-session")
303// did := session.Values["did"].(string)
304
305// switch r.Method {
306// case http.MethodGet:
307// keys, err := h.db.GetPublicKeys(did)
308// if err != nil {
309// h.WriteOOBNotice(w, "keys", "Failed to list keys. Try again later.")
310// log.Println(err)
311// return
312// }
313
314// data := make(map[string]interface{})
315// data["keys"] = keys
316// if err := h.t.ExecuteTemplate(w, "settings/keys", data); err != nil {
317// log.Println(err)
318// return
319// }
320// case http.MethodPut:
321// key := r.FormValue("key")
322// name := r.FormValue("name")
323// client, _ := h.auth.AuthorizedClient(r)
324
325// _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
326// if err != nil {
327// h.WriteOOBNotice(w, "keys", "Invalid public key. Check your formatting and try again.")
328// log.Printf("parsing public key: %s", err)
329// return
330// }
331
332// if err := h.db.AddPublicKey(did, name, key); err != nil {
333// h.WriteOOBNotice(w, "keys", "Failed to add key.")
334// log.Printf("adding public key: %s", err)
335// return
336// }
337
338// // store in pds too
339// resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
340// Collection: "sh.bild.publicKey",
341// Repo: did,
342// Rkey: uuid.New().String(),
343// Record: &lexutil.LexiconTypeDecoder{Val: &shbild.PublicKey{
344// Created: time.Now().String(),
345// Key: key,
346// Name: name,
347// }},
348// })
349
350// // invalid record
351// if err != nil {
352// h.WriteOOBNotice(w, "keys", "Invalid inputs. Check your formatting and try again.")
353// log.Printf("failed to create record: %s", err)
354// return
355// }
356
357// log.Println("created atproto record: ", resp.Uri)
358
359// h.WriteOOBNotice(w, "keys", "Key added!")
360// return
361// }
362// }
363
364// func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) {
365// session, _ := h.s.Get(r, "bild-session")
366// did := session.Values["did"].(string)
367// handle := session.Values["handle"].(string)
368
369// switch r.Method {
370// case http.MethodGet:
371// if err := h.t.ExecuteTemplate(w, "repo/new", nil); err != nil {
372// log.Println(err)
373// return
374// }
375// case http.MethodPut:
376// name := r.FormValue("name")
377// description := r.FormValue("description")
378
379// repoPath := filepath.Join(h.c.Repo.ScanPath, did, name)
380// err := git.InitBare(repoPath)
381// if err != nil {
382// h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.")
383// return
384// }
385
386// err = h.db.AddRepo(did, name, description)
387// if err != nil {
388// h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.")
389// return
390// }
391
392// w.Header().Set("HX-Redirect", fmt.Sprintf("/@%s/%s", handle, name))
393// w.WriteHeader(http.StatusOK)
394// }
395// }
396
397// func (h *Handle) Timeline(w http.ResponseWriter, r *http.Request) {
398// session, err := h.s.Get(r, "bild-session")
399// user := make(map[string]string)
400// if err != nil || session.IsNew {
401// // user is not logged in
402// } else {
403// user["handle"] = session.Values["handle"].(string)
404// user["did"] = session.Values["did"].(string)
405// }
406
407// if err := h.t.ExecuteTemplate(w, "timeline", user); err != nil {
408// log.Println(err)
409// return
410// }
411// }