this repo has no description
1package routes
2
3import (
4 "compress/gzip"
5 "fmt"
6 "html/template"
7 "log"
8 "net/http"
9 "os"
10 "path/filepath"
11 "sort"
12 "strconv"
13 "strings"
14 "time"
15
16 "github.com/dustin/go-humanize"
17 "github.com/go-chi/chi/v5"
18 "github.com/icyphox/bild/legit/config"
19 "github.com/icyphox/bild/legit/db"
20 "github.com/icyphox/bild/legit/git"
21 "github.com/russross/blackfriday/v2"
22)
23
24type Handle struct {
25 c *config.Config
26 t *template.Template
27 db *db.DB
28}
29
30func (h *Handle) Index(w http.ResponseWriter, r *http.Request) {
31 user := chi.URLParam(r, "user")
32 path := filepath.Join(h.c.Repo.ScanPath, user)
33 dirs, err := os.ReadDir(path)
34 if err != nil {
35 h.Write500(w)
36 log.Printf("reading scan path: %s", err)
37 return
38 }
39
40 type info struct {
41 DisplayName, Name, Desc, Idle string
42 d time.Time
43 }
44
45 infos := []info{}
46
47 for _, dir := range dirs {
48 name := dir.Name()
49 if !dir.IsDir() || h.isIgnored(name) || h.isUnlisted(name) {
50 continue
51 }
52
53 gr, err := git.Open(path, "")
54 if err != nil {
55 log.Println(err)
56 continue
57 }
58
59 c, err := gr.LastCommit()
60 if err != nil {
61 h.Write500(w)
62 log.Println(err)
63 return
64 }
65
66 infos = append(infos, info{
67 DisplayName: getDisplayName(name),
68 Name: name,
69 Desc: getDescription(path),
70 Idle: humanize.Time(c.Author.When),
71 d: c.Author.When,
72 })
73 }
74
75 sort.Slice(infos, func(i, j int) bool {
76 return infos[j].d.Before(infos[i].d)
77 })
78
79 data := make(map[string]interface{})
80 data["meta"] = h.c.Meta
81 data["info"] = infos
82
83 if err := h.t.ExecuteTemplate(w, "index", data); err != nil {
84 log.Println(err)
85 return
86 }
87}
88
89func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
90 name := uniqueName(r)
91 if h.isIgnored(name) {
92 h.Write404(w)
93 return
94 }
95
96 name = filepath.Clean(name)
97 path := filepath.Join(h.c.Repo.ScanPath, name)
98
99 fmt.Println(path)
100 gr, err := git.Open(path, "")
101 if err != nil {
102 h.Write404(w)
103 return
104 }
105 commits, err := gr.Commits()
106 if err != nil {
107 h.Write500(w)
108 log.Println(err)
109 return
110 }
111
112 var readmeContent template.HTML
113 for _, readme := range h.c.Repo.Readme {
114 ext := filepath.Ext(readme)
115 content, _ := gr.FileContent(readme)
116 if len(content) > 0 {
117 switch ext {
118 case ".md", ".mkd", ".markdown":
119 unsafe := blackfriday.Run(
120 []byte(content),
121 blackfriday.WithExtensions(blackfriday.CommonExtensions),
122 )
123 html := sanitize(unsafe)
124 readmeContent = template.HTML(html)
125 default:
126 safe := sanitize([]byte(content))
127 readmeContent = template.HTML(
128 fmt.Sprintf(`<pre>%s</pre>`, safe),
129 )
130 }
131 break
132 }
133 }
134
135 if readmeContent == "" {
136 log.Printf("no readme found for %s", name)
137 }
138
139 mainBranch, err := gr.FindMainBranch(h.c.Repo.MainBranch)
140 if err != nil {
141 h.Write500(w)
142 log.Println(err)
143 return
144 }
145
146 if len(commits) >= 3 {
147 commits = commits[:3]
148 }
149
150 data := make(map[string]any)
151 data["name"] = name
152 data["displayname"] = getDisplayName(name)
153 data["ref"] = mainBranch
154 data["readme"] = readmeContent
155 data["commits"] = commits
156 data["desc"] = getDescription(path)
157 data["servername"] = h.c.Server.Name
158 data["meta"] = h.c.Meta
159 data["gomod"] = isGoModule(gr)
160
161 if err := h.t.ExecuteTemplate(w, "repo", data); err != nil {
162 log.Println(err)
163 return
164 }
165
166 return
167}
168
169func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) {
170 name := uniqueName(r)
171 if h.isIgnored(name) {
172 h.Write404(w)
173 return
174 }
175 treePath := chi.URLParam(r, "*")
176 ref := chi.URLParam(r, "ref")
177
178 name = filepath.Clean(name)
179 path := filepath.Join(h.c.Repo.ScanPath, name)
180 fmt.Println(path)
181 gr, err := git.Open(path, ref)
182 if err != nil {
183 h.Write404(w)
184 return
185 }
186
187 files, err := gr.FileTree(treePath)
188 if err != nil {
189 h.Write500(w)
190 log.Println(err)
191 return
192 }
193
194 data := make(map[string]any)
195 data["name"] = name
196 data["displayname"] = getDisplayName(name)
197 data["ref"] = ref
198 data["parent"] = treePath
199 data["desc"] = getDescription(path)
200 data["dotdot"] = filepath.Dir(treePath)
201
202 h.listFiles(files, data, w)
203 return
204}
205
206func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) {
207 var raw bool
208 if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil {
209 raw = rawParam
210 }
211
212 name := uniqueName(r)
213
214 if h.isIgnored(name) {
215 h.Write404(w)
216 return
217 }
218 treePath := chi.URLParam(r, "*")
219 ref := chi.URLParam(r, "ref")
220
221 name = filepath.Clean(name)
222 path := filepath.Join(h.c.Repo.ScanPath, name)
223 gr, err := git.Open(path, ref)
224 if err != nil {
225 h.Write404(w)
226 return
227 }
228
229 contents, err := gr.FileContent(treePath)
230 if err != nil {
231 h.Write500(w)
232 return
233 }
234 data := make(map[string]any)
235 data["name"] = name
236 data["displayname"] = getDisplayName(name)
237 data["ref"] = ref
238 data["desc"] = getDescription(path)
239 data["path"] = treePath
240
241 safe := sanitize([]byte(contents))
242
243 if raw {
244 h.showRaw(string(safe), w)
245 } else {
246 if h.c.Meta.SyntaxHighlight == "" {
247 h.showFile(string(safe), data, w)
248 } else {
249 h.showFileWithHighlight(treePath, string(safe), data, w)
250 }
251 }
252}
253
254func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
255 name := uniqueName(r)
256 if h.isIgnored(name) {
257 h.Write404(w)
258 return
259 }
260
261 file := chi.URLParam(r, "file")
262
263 // TODO: extend this to add more files compression (e.g.: xz)
264 if !strings.HasSuffix(file, ".tar.gz") {
265 h.Write404(w)
266 return
267 }
268
269 ref := strings.TrimSuffix(file, ".tar.gz")
270
271 // This allows the browser to use a proper name for the file when
272 // downloading
273 filename := fmt.Sprintf("%s-%s.tar.gz", name, ref)
274 setContentDisposition(w, filename)
275 setGZipMIME(w)
276
277 path := filepath.Join(h.c.Repo.ScanPath, name)
278 gr, err := git.Open(path, ref)
279 if err != nil {
280 h.Write404(w)
281 return
282 }
283
284 gw := gzip.NewWriter(w)
285 defer gw.Close()
286
287 prefix := fmt.Sprintf("%s-%s", name, ref)
288 err = gr.WriteTar(gw, prefix)
289 if err != nil {
290 // once we start writing to the body we can't report error anymore
291 // so we are only left with printing the error.
292 log.Println(err)
293 return
294 }
295
296 err = gw.Flush()
297 if err != nil {
298 // once we start writing to the body we can't report error anymore
299 // so we are only left with printing the error.
300 log.Println(err)
301 return
302 }
303}
304
305func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
306 name := uniqueName(r)
307 if h.isIgnored(name) {
308 h.Write404(w)
309 return
310 }
311 ref := chi.URLParam(r, "ref")
312
313 path := filepath.Join(h.c.Repo.ScanPath, name)
314 gr, err := git.Open(path, ref)
315 if err != nil {
316 h.Write404(w)
317 return
318 }
319
320 commits, err := gr.Commits()
321 if err != nil {
322 h.Write500(w)
323 log.Println(err)
324 return
325 }
326
327 data := make(map[string]interface{})
328 data["commits"] = commits
329 data["meta"] = h.c.Meta
330 data["name"] = name
331 data["displayname"] = getDisplayName(name)
332 data["ref"] = ref
333 data["desc"] = getDescription(path)
334 data["log"] = true
335
336 if err := h.t.ExecuteTemplate(w, "log", data); err != nil {
337 log.Println(err)
338 return
339 }
340}
341
342func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
343 name := uniqueName(r)
344 if h.isIgnored(name) {
345 h.Write404(w)
346 return
347 }
348 ref := chi.URLParam(r, "ref")
349
350 path := filepath.Join(h.c.Repo.ScanPath, name)
351 gr, err := git.Open(path, ref)
352 if err != nil {
353 h.Write404(w)
354 return
355 }
356
357 diff, err := gr.Diff()
358 if err != nil {
359 h.Write500(w)
360 log.Println(err)
361 return
362 }
363
364 data := make(map[string]interface{})
365
366 data["commit"] = diff.Commit
367 data["stat"] = diff.Stat
368 data["diff"] = diff.Diff
369 data["meta"] = h.c.Meta
370 data["name"] = name
371 data["displayname"] = getDisplayName(name)
372 data["ref"] = ref
373 data["desc"] = getDescription(path)
374
375 if err := h.t.ExecuteTemplate(w, "commit", data); err != nil {
376 log.Println(err)
377 return
378 }
379}
380
381func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) {
382 name := chi.URLParam(r, "name")
383 if h.isIgnored(name) {
384 h.Write404(w)
385 return
386 }
387
388 path := filepath.Join(h.c.Repo.ScanPath, name)
389 gr, err := git.Open(path, "")
390 if err != nil {
391 h.Write404(w)
392 return
393 }
394
395 tags, err := gr.Tags()
396 if err != nil {
397 // Non-fatal, we *should* have at least one branch to show.
398 log.Println(err)
399 }
400
401 branches, err := gr.Branches()
402 if err != nil {
403 log.Println(err)
404 h.Write500(w)
405 return
406 }
407
408 data := make(map[string]interface{})
409
410 data["meta"] = h.c.Meta
411 data["name"] = name
412 data["displayname"] = getDisplayName(name)
413 data["branches"] = branches
414 data["tags"] = tags
415 data["desc"] = getDescription(path)
416
417 if err := h.t.ExecuteTemplate(w, "refs", data); err != nil {
418 log.Println(err)
419 return
420 }
421}
422
423func (h *Handle) ServeStatic(w http.ResponseWriter, r *http.Request) {
424 f := chi.URLParam(r, "file")
425 f = filepath.Clean(filepath.Join(h.c.Dirs.Static, f))
426
427 http.ServeFile(w, r, f)
428}
429
430func (h *Handle) Login(w http.ResponseWriter, r *http.Request) {
431 if err := h.t.ExecuteTemplate(w, "login", nil); err != nil {
432 log.Println(err)
433 return
434 }
435}
436
437func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
438 switch r.Method {
439 case http.MethodGet:
440 // TODO: fetch keys from db
441 if err := h.t.ExecuteTemplate(w, "keys", nil); err != nil {
442 log.Println(err)
443 return
444 }
445 case http.MethodPut:
446 key := r.FormValue("key")
447 name := r.FormValue("name")
448 // TODO: add did here
449 if err := h.db.AddPublicKey("did:ashtntnashtx", name, key); err != nil {
450 h.WriteOOBNotice(w, "keys", "Failed to add key")
451 log.Printf("adding public key: %s", err)
452 return
453 }
454
455 h.WriteOOBNotice(w, "keys", "Key added!")
456 return
457 }
458}