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