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