routes/routes.go (view raw)
1package routes
2
3import (
4 "fmt"
5 "html/template"
6 "log"
7 "net/http"
8 "os"
9 "path/filepath"
10 "sort"
11 "strconv"
12 "time"
13
14 "git.icyphox.sh/legit/config"
15 "git.icyphox.sh/legit/git"
16 "github.com/dustin/go-humanize"
17 "github.com/microcosm-cc/bluemonday"
18 "github.com/russross/blackfriday/v2"
19)
20
21type deps struct {
22 c *config.Config
23}
24
25func (d *deps) Index(w http.ResponseWriter, r *http.Request) {
26 dirs, err := os.ReadDir(d.c.Repo.ScanPath)
27 if err != nil {
28 d.Write500(w)
29 log.Printf("reading scan path: %s", err)
30 return
31 }
32
33 type info struct {
34 Name, Desc, Idle string
35 d time.Time
36 }
37
38 infos := []info{}
39
40 for _, dir := range dirs {
41 if d.isIgnored(dir.Name()) {
42 continue
43 }
44
45 path := filepath.Join(d.c.Repo.ScanPath, dir.Name())
46 gr, err := git.Open(path, "")
47 if err != nil {
48 log.Println(err)
49 continue
50 }
51
52 c, err := gr.LastCommit()
53 if err != nil {
54 d.Write500(w)
55 log.Println(err)
56 return
57 }
58
59 desc := getDescription(path)
60
61 infos = append(infos, info{
62 Name: dir.Name(),
63 Desc: desc,
64 Idle: humanize.Time(c.Author.When),
65 d: c.Author.When,
66 })
67 }
68
69 sort.Slice(infos, func(i, j int) bool {
70 return infos[j].d.Before(infos[i].d)
71 })
72
73 tpath := filepath.Join(d.c.Dirs.Templates, "*")
74 t := template.Must(template.ParseGlob(tpath))
75
76 data := make(map[string]interface{})
77 data["meta"] = d.c.Meta
78 data["info"] = infos
79
80 if err := t.ExecuteTemplate(w, "index", data); err != nil {
81 log.Println(err)
82 return
83 }
84}
85
86func (d *deps) RepoIndex(w http.ResponseWriter, r *http.Request) {
87 name := r.PathValue("name")
88 if d.isIgnored(name) {
89 d.Write404(w)
90 return
91 }
92 name = filepath.Clean(name)
93 path := filepath.Join(d.c.Repo.ScanPath, name)
94
95 gr, err := git.Open(path, "")
96 if err != nil {
97 d.Write404(w)
98 return
99 }
100
101 commits, err := gr.Commits()
102 if err != nil {
103 d.Write500(w)
104 log.Println(err)
105 return
106 }
107
108 var readmeContent template.HTML
109 for _, readme := range d.c.Repo.Readme {
110 ext := filepath.Ext(readme)
111 content, _ := gr.FileContent(readme)
112 if len(content) > 0 {
113 switch ext {
114 case ".md", ".mkd", ".markdown":
115 unsafe := blackfriday.Run(
116 []byte(content),
117 blackfriday.WithExtensions(blackfriday.CommonExtensions),
118 )
119 html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
120 readmeContent = template.HTML(html)
121 default:
122 readmeContent = template.HTML(
123 fmt.Sprintf(`<pre>%s</pre>`, content),
124 )
125 }
126 break
127 }
128 }
129
130 if readmeContent == "" {
131 log.Printf("no readme found for %s", name)
132 }
133
134 mainBranch, err := gr.FindMainBranch(d.c.Repo.MainBranch)
135 if err != nil {
136 d.Write500(w)
137 log.Println(err)
138 return
139 }
140
141 tpath := filepath.Join(d.c.Dirs.Templates, "*")
142 t := template.Must(template.ParseGlob(tpath))
143
144 if len(commits) >= 3 {
145 commits = commits[:3]
146 }
147
148 data := make(map[string]any)
149 data["name"] = name
150 data["ref"] = mainBranch
151 data["readme"] = readmeContent
152 data["commits"] = commits
153 data["desc"] = getDescription(path)
154 data["servername"] = d.c.Server.Name
155 data["meta"] = d.c.Meta
156 data["gomod"] = isGoModule(gr)
157
158 if err := t.ExecuteTemplate(w, "repo", data); err != nil {
159 log.Println(err)
160 return
161 }
162
163 return
164}
165
166func (d *deps) RepoTree(w http.ResponseWriter, r *http.Request) {
167 name := r.PathValue("name")
168 if d.isIgnored(name) {
169 d.Write404(w)
170 return
171 }
172 treePath := r.PathValue("rest")
173 ref := r.PathValue("ref")
174
175 name = filepath.Clean(name)
176 path := filepath.Join(d.c.Repo.ScanPath, name)
177 gr, err := git.Open(path, ref)
178 if err != nil {
179 d.Write404(w)
180 return
181 }
182
183 files, err := gr.FileTree(treePath)
184 if err != nil {
185 d.Write500(w)
186 log.Println(err)
187 return
188 }
189
190 data := make(map[string]any)
191 data["name"] = name
192 data["ref"] = ref
193 data["parent"] = treePath
194 data["desc"] = getDescription(path)
195 data["dotdot"] = filepath.Dir(treePath)
196
197 d.listFiles(files, data, w)
198 return
199}
200
201func (d *deps) FileContent(w http.ResponseWriter, r *http.Request) {
202 var raw bool
203 if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil {
204 raw = rawParam
205 }
206
207 name := r.PathValue("name")
208 if d.isIgnored(name) {
209 d.Write404(w)
210 return
211 }
212 treePath := r.PathValue("rest")
213 ref := r.PathValue("ref")
214
215 name = filepath.Clean(name)
216 path := filepath.Join(d.c.Repo.ScanPath, name)
217 gr, err := git.Open(path, ref)
218 if err != nil {
219 d.Write404(w)
220 return
221 }
222
223 contents, err := gr.FileContent(treePath)
224 data := make(map[string]any)
225 data["name"] = name
226 data["ref"] = ref
227 data["desc"] = getDescription(path)
228 data["path"] = treePath
229
230 if raw {
231 d.showRaw(contents, w)
232 } else {
233 d.showFile(contents, data, w)
234 }
235 return
236}
237
238func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
239 name := r.PathValue("name")
240 if d.isIgnored(name) {
241 d.Write404(w)
242 return
243 }
244 ref := r.PathValue("ref")
245
246 path := filepath.Join(d.c.Repo.ScanPath, name)
247 gr, err := git.Open(path, ref)
248 if err != nil {
249 d.Write404(w)
250 return
251 }
252
253 commits, err := gr.Commits()
254 if err != nil {
255 d.Write500(w)
256 log.Println(err)
257 return
258 }
259
260 tpath := filepath.Join(d.c.Dirs.Templates, "*")
261 t := template.Must(template.ParseGlob(tpath))
262
263 data := make(map[string]interface{})
264 data["commits"] = commits
265 data["meta"] = d.c.Meta
266 data["name"] = name
267 data["ref"] = ref
268 data["desc"] = getDescription(path)
269 data["log"] = true
270
271 if err := t.ExecuteTemplate(w, "log", data); err != nil {
272 log.Println(err)
273 return
274 }
275}
276
277func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
278 name := r.PathValue("name")
279 if d.isIgnored(name) {
280 d.Write404(w)
281 return
282 }
283 ref := r.PathValue("ref")
284
285 path := filepath.Join(d.c.Repo.ScanPath, name)
286 gr, err := git.Open(path, ref)
287 if err != nil {
288 d.Write404(w)
289 return
290 }
291
292 diff, err := gr.Diff()
293 if err != nil {
294 d.Write500(w)
295 log.Println(err)
296 return
297 }
298
299 tpath := filepath.Join(d.c.Dirs.Templates, "*")
300 t := template.Must(template.ParseGlob(tpath))
301
302 data := make(map[string]interface{})
303
304 data["commit"] = diff.Commit
305 data["stat"] = diff.Stat
306 data["diff"] = diff.Diff
307 data["meta"] = d.c.Meta
308 data["name"] = name
309 data["ref"] = ref
310 data["desc"] = getDescription(path)
311
312 if err := t.ExecuteTemplate(w, "commit", data); err != nil {
313 log.Println(err)
314 return
315 }
316}
317
318func (d *deps) Refs(w http.ResponseWriter, r *http.Request) {
319 name := r.PathValue("name")
320 if d.isIgnored(name) {
321 d.Write404(w)
322 return
323 }
324
325 path := filepath.Join(d.c.Repo.ScanPath, name)
326 gr, err := git.Open(path, "")
327 if err != nil {
328 d.Write404(w)
329 return
330 }
331
332 tags, err := gr.Tags()
333 if err != nil {
334 // Non-fatal, we *should* have at least one branch to show.
335 log.Println(err)
336 }
337
338 branches, err := gr.Branches()
339 if err != nil {
340 log.Println(err)
341 d.Write500(w)
342 return
343 }
344
345 tpath := filepath.Join(d.c.Dirs.Templates, "*")
346 t := template.Must(template.ParseGlob(tpath))
347
348 data := make(map[string]interface{})
349
350 data["meta"] = d.c.Meta
351 data["name"] = name
352 data["branches"] = branches
353 data["tags"] = tags
354 data["desc"] = getDescription(path)
355
356 if err := t.ExecuteTemplate(w, "refs", data); err != nil {
357 log.Println(err)
358 return
359 }
360}
361
362func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) {
363 f := r.PathValue("file")
364 f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f))
365
366 http.ServeFile(w, r, f)
367}