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