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 DisplayName, 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 name := dir.Name()
60
61 desc := getDescription(path)
62
63 infos = append(infos, info{
64 DisplayName: getDisplayName(name),
65 Name: name,
66 Desc: desc,
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 := r.PathValue("name")
91 if d.isIgnored(name) {
92 d.Write404(w)
93 return
94 }
95 name = filepath.Clean(name)
96 path := filepath.Join(d.c.Repo.ScanPath, name)
97
98 gr, err := git.Open(path, "")
99 if err != nil {
100 d.Write404(w)
101 return
102 }
103
104 commits, err := gr.Commits()
105 if err != nil {
106 d.Write500(w)
107 log.Println(err)
108 return
109 }
110
111 var readmeContent template.HTML
112 for _, readme := range d.c.Repo.Readme {
113 ext := filepath.Ext(readme)
114 content, _ := gr.FileContent(readme)
115 if len(content) > 0 {
116 switch ext {
117 case ".md", ".mkd", ".markdown":
118 unsafe := blackfriday.Run(
119 []byte(content),
120 blackfriday.WithExtensions(blackfriday.CommonExtensions),
121 )
122 html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
123 readmeContent = template.HTML(html)
124 default:
125 readmeContent = template.HTML(
126 fmt.Sprintf(`<pre>%s</pre>`, content),
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 tpath := filepath.Join(d.c.Dirs.Templates, "*")
145 t := template.Must(template.ParseGlob(tpath))
146
147 if len(commits) >= 3 {
148 commits = commits[:3]
149 }
150
151 data := make(map[string]any)
152 data["name"] = name
153 data["displayname"] = getDisplayName(name)
154 data["ref"] = mainBranch
155 data["readme"] = readmeContent
156 data["commits"] = commits
157 data["desc"] = getDescription(path)
158 data["servername"] = d.c.Server.Name
159 data["meta"] = d.c.Meta
160 data["gomod"] = isGoModule(gr)
161
162 if err := t.ExecuteTemplate(w, "repo", data); err != nil {
163 log.Println(err)
164 return
165 }
166
167 return
168}
169
170func (d *deps) RepoTree(w http.ResponseWriter, r *http.Request) {
171 name := r.PathValue("name")
172 if d.isIgnored(name) {
173 d.Write404(w)
174 return
175 }
176 treePath := r.PathValue("rest")
177 ref := r.PathValue("ref")
178
179 name = filepath.Clean(name)
180 path := filepath.Join(d.c.Repo.ScanPath, name)
181 gr, err := git.Open(path, ref)
182 if err != nil {
183 d.Write404(w)
184 return
185 }
186
187 files, err := gr.FileTree(treePath)
188 if err != nil {
189 d.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 d.listFiles(files, data, w)
203 return
204}
205
206func (d *deps) 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 := r.PathValue("name")
213 if d.isIgnored(name) {
214 d.Write404(w)
215 return
216 }
217 treePath := r.PathValue("rest")
218 ref := r.PathValue("ref")
219
220 name = filepath.Clean(name)
221 path := filepath.Join(d.c.Repo.ScanPath, name)
222 gr, err := git.Open(path, ref)
223 if err != nil {
224 d.Write404(w)
225 return
226 }
227
228 contents, err := gr.FileContent(treePath)
229 data := make(map[string]any)
230 data["name"] = name
231 data["displayname"] = getDisplayName(name)
232 data["ref"] = ref
233 data["desc"] = getDescription(path)
234 data["path"] = treePath
235
236 if raw {
237 d.showRaw(contents, w)
238 } else {
239 d.showFile(contents, data, w)
240 }
241 return
242}
243
244func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
245 name := r.PathValue("name")
246 if d.isIgnored(name) {
247 d.Write404(w)
248 return
249 }
250 ref := r.PathValue("ref")
251
252 path := filepath.Join(d.c.Repo.ScanPath, name)
253 gr, err := git.Open(path, ref)
254 if err != nil {
255 d.Write404(w)
256 return
257 }
258
259 commits, err := gr.Commits()
260 if err != nil {
261 d.Write500(w)
262 log.Println(err)
263 return
264 }
265
266 tpath := filepath.Join(d.c.Dirs.Templates, "*")
267 t := template.Must(template.ParseGlob(tpath))
268
269 data := make(map[string]interface{})
270 data["commits"] = commits
271 data["meta"] = d.c.Meta
272 data["name"] = name
273 data["displayname"] = getDisplayName(name)
274 data["ref"] = ref
275 data["desc"] = getDescription(path)
276 data["log"] = true
277
278 if err := t.ExecuteTemplate(w, "log", data); err != nil {
279 log.Println(err)
280 return
281 }
282}
283
284func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
285 name := r.PathValue("name")
286 if d.isIgnored(name) {
287 d.Write404(w)
288 return
289 }
290 ref := r.PathValue("ref")
291
292 path := filepath.Join(d.c.Repo.ScanPath, name)
293 gr, err := git.Open(path, ref)
294 if err != nil {
295 d.Write404(w)
296 return
297 }
298
299 diff, err := gr.Diff()
300 if err != nil {
301 d.Write500(w)
302 log.Println(err)
303 return
304 }
305
306 tpath := filepath.Join(d.c.Dirs.Templates, "*")
307 t := template.Must(template.ParseGlob(tpath))
308
309 data := make(map[string]interface{})
310
311 data["commit"] = diff.Commit
312 data["stat"] = diff.Stat
313 data["diff"] = diff.Diff
314 data["meta"] = d.c.Meta
315 data["name"] = name
316 data["displayname"] = getDisplayName(name)
317 data["ref"] = ref
318 data["desc"] = getDescription(path)
319
320 if err := t.ExecuteTemplate(w, "commit", data); err != nil {
321 log.Println(err)
322 return
323 }
324}
325
326func (d *deps) Refs(w http.ResponseWriter, r *http.Request) {
327 name := r.PathValue("name")
328 if d.isIgnored(name) {
329 d.Write404(w)
330 return
331 }
332
333 path := filepath.Join(d.c.Repo.ScanPath, name)
334 gr, err := git.Open(path, "")
335 if err != nil {
336 d.Write404(w)
337 return
338 }
339
340 tags, err := gr.Tags()
341 if err != nil {
342 // Non-fatal, we *should* have at least one branch to show.
343 log.Println(err)
344 }
345
346 branches, err := gr.Branches()
347 if err != nil {
348 log.Println(err)
349 d.Write500(w)
350 return
351 }
352
353 tpath := filepath.Join(d.c.Dirs.Templates, "*")
354 t := template.Must(template.ParseGlob(tpath))
355
356 data := make(map[string]interface{})
357
358 data["meta"] = d.c.Meta
359 data["name"] = name
360 data["displayname"] = getDisplayName(name)
361 data["branches"] = branches
362 data["tags"] = tags
363 data["desc"] = getDescription(path)
364
365 if err := t.ExecuteTemplate(w, "refs", data); err != nil {
366 log.Println(err)
367 return
368 }
369}
370
371func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) {
372 f := r.PathValue("file")
373 f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f))
374
375 http.ServeFile(w, r, f)
376}