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 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 := flow.Param(r.Context(), "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["gomod"] = isGoModule(gr)
156
157 if err := t.ExecuteTemplate(w, "repo", data); err != nil {
158 log.Println(err)
159 return
160 }
161
162 return
163}
164
165func (d *deps) RepoTree(w http.ResponseWriter, r *http.Request) {
166 name := flow.Param(r.Context(), "name")
167 if d.isIgnored(name) {
168 d.Write404(w)
169 return
170 }
171 treePath := flow.Param(r.Context(), "...")
172 ref := flow.Param(r.Context(), "ref")
173
174 name = filepath.Clean(name)
175 path := filepath.Join(d.c.Repo.ScanPath, name)
176 gr, err := git.Open(path, ref)
177 if err != nil {
178 d.Write404(w)
179 return
180 }
181
182 files, err := gr.FileTree(treePath)
183 if err != nil {
184 d.Write500(w)
185 log.Println(err)
186 return
187 }
188
189 data := make(map[string]any)
190 data["name"] = name
191 data["ref"] = ref
192 data["parent"] = treePath
193 data["desc"] = getDescription(path)
194 data["dotdot"] = filepath.Dir(treePath)
195
196 d.listFiles(files, data, w)
197 return
198}
199
200func (d *deps) FileContent(w http.ResponseWriter, r *http.Request) {
201 name := flow.Param(r.Context(), "name")
202 if d.isIgnored(name) {
203 d.Write404(w)
204 return
205 }
206 treePath := flow.Param(r.Context(), "...")
207 ref := flow.Param(r.Context(), "ref")
208
209 name = filepath.Clean(name)
210 path := filepath.Join(d.c.Repo.ScanPath, name)
211 gr, err := git.Open(path, ref)
212 if err != nil {
213 d.Write404(w)
214 return
215 }
216
217 contents, err := gr.FileContent(treePath)
218 data := make(map[string]any)
219 data["name"] = name
220 data["ref"] = ref
221 data["desc"] = getDescription(path)
222 data["path"] = treePath
223
224 d.showFile(contents, data, w)
225 return
226}
227
228func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
229 name := flow.Param(r.Context(), "name")
230 if d.isIgnored(name) {
231 d.Write404(w)
232 return
233 }
234 ref := flow.Param(r.Context(), "ref")
235
236 path := filepath.Join(d.c.Repo.ScanPath, name)
237 gr, err := git.Open(path, ref)
238 if err != nil {
239 d.Write404(w)
240 return
241 }
242
243 commits, err := gr.Commits()
244 if err != nil {
245 d.Write500(w)
246 log.Println(err)
247 return
248 }
249
250 tpath := filepath.Join(d.c.Dirs.Templates, "*")
251 t := template.Must(template.ParseGlob(tpath))
252
253 data := make(map[string]interface{})
254 data["commits"] = commits
255 data["meta"] = d.c.Meta
256 data["name"] = name
257 data["ref"] = ref
258 data["desc"] = getDescription(path)
259 data["log"] = true
260
261 if err := t.ExecuteTemplate(w, "log", data); err != nil {
262 log.Println(err)
263 return
264 }
265}
266
267func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
268 name := flow.Param(r.Context(), "name")
269 if d.isIgnored(name) {
270 d.Write404(w)
271 return
272 }
273 ref := flow.Param(r.Context(), "ref")
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 diff, err := gr.Diff()
283 if err != nil {
284 d.Write500(w)
285 log.Println(err)
286 return
287 }
288
289 tpath := filepath.Join(d.c.Dirs.Templates, "*")
290 t := template.Must(template.ParseGlob(tpath))
291
292 data := make(map[string]interface{})
293
294 data["commit"] = diff.Commit
295 data["stat"] = diff.Stat
296 data["diff"] = diff.Diff
297 data["meta"] = d.c.Meta
298 data["name"] = name
299 data["ref"] = ref
300 data["desc"] = getDescription(path)
301
302 if err := t.ExecuteTemplate(w, "commit", data); err != nil {
303 log.Println(err)
304 return
305 }
306}
307
308func (d *deps) Refs(w http.ResponseWriter, r *http.Request) {
309 name := flow.Param(r.Context(), "name")
310 if d.isIgnored(name) {
311 d.Write404(w)
312 return
313 }
314
315 path := filepath.Join(d.c.Repo.ScanPath, name)
316 gr, err := git.Open(path, "")
317 if err != nil {
318 d.Write404(w)
319 return
320 }
321
322 tags, err := gr.Tags()
323 if err != nil {
324 // Non-fatal, we *should* have at least one branch to show.
325 log.Println(err)
326 }
327
328 branches, err := gr.Branches()
329 if err != nil {
330 log.Println(err)
331 d.Write500(w)
332 return
333 }
334
335 tpath := filepath.Join(d.c.Dirs.Templates, "*")
336 t := template.Must(template.ParseGlob(tpath))
337
338 data := make(map[string]interface{})
339
340 data["meta"] = d.c.Meta
341 data["name"] = name
342 data["branches"] = branches
343 data["tags"] = tags
344 data["desc"] = getDescription(path)
345
346 if err := t.ExecuteTemplate(w, "refs", data); err != nil {
347 log.Println(err)
348 return
349 }
350}
351
352func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) {
353 f := flow.Param(r.Context(), "file")
354 f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f))
355
356 http.ServeFile(w, r, f)
357}