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