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