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