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 "github.com/alexedwards/flow"
14 "github.com/dustin/go-humanize"
15 "icyphox.sh/legit/config"
16 "icyphox.sh/legit/git"
17)
18
19type deps struct {
20 c *config.Config
21}
22
23func (d *deps) Index(w http.ResponseWriter, r *http.Request) {
24 dirs, err := os.ReadDir(d.c.Repo.ScanPath)
25 if err != nil {
26 d.Write500(w)
27 log.Printf("reading scan path: %s", err)
28 return
29 }
30
31 type info struct {
32 Name, Desc, Idle string
33 d time.Time
34 }
35
36 infos := []info{}
37
38 for _, dir := range dirs {
39 path := filepath.Join(d.c.Repo.ScanPath, dir.Name())
40 gr, err := git.Open(path, "")
41 if err != nil {
42 log.Println(err)
43 continue
44 }
45
46 c, err := gr.LastCommit()
47 if err != nil {
48 d.Write500(w)
49 log.Println(err)
50 return
51 }
52
53 desc := getDescription(path)
54
55 infos = append(infos, info{
56 Name: dir.Name(),
57 Desc: desc,
58 Idle: humanize.Time(c.Author.When),
59 d: c.Author.When,
60 })
61 }
62
63 sort.Slice(infos, func(i, j int) bool {
64 return infos[j].d.Before(infos[i].d)
65 })
66
67 tpath := filepath.Join(d.c.Dirs.Templates, "*")
68 t := template.Must(template.ParseGlob(tpath))
69
70 data := make(map[string]interface{})
71 data["meta"] = d.c.Meta
72 data["info"] = infos
73
74 if err := t.ExecuteTemplate(w, "index", data); err != nil {
75 log.Println(err)
76 return
77 }
78}
79
80func (d *deps) RepoIndex(w http.ResponseWriter, r *http.Request) {
81 name := flow.Param(r.Context(), "name")
82 name = filepath.Clean(name)
83 path := filepath.Join(d.c.Repo.ScanPath, name)
84 gr, err := git.Open(path, "")
85 if err != nil {
86 d.Write404(w)
87 return
88 }
89
90 commits, err := gr.Commits()
91 if err != nil {
92 d.Write500(w)
93 log.Println(err)
94 return
95 }
96
97 var readmeContent string
98 for _, readme := range d.c.Repo.Readme {
99 readmeContent, _ = gr.FileContent(readme)
100 if readmeContent != "" {
101 break
102 }
103 }
104
105 if readmeContent == "" {
106 log.Printf("no readme found for %s", name)
107 }
108
109 mainBranch, err := gr.FindMainBranch(d.c.Repo.MainBranch)
110 if err != nil {
111 d.Write500(w)
112 log.Println(err)
113 return
114 }
115
116 cloneURL := fmt.Sprintf("https://%s/%s", d.c.Server.FQDN, name)
117 prettyURL := d.c.Misc.GoImport.PrettyURL
118
119 if prettyURL == "" {
120 prettyURL = cloneURL
121 } else {
122 prettyURL = filepath.Join(prettyURL, name)
123 }
124
125 goImport := fmt.Sprintf(`<meta name="go-import" content="%s git %s">`,
126 prettyURL, cloneURL)
127
128 tpath := filepath.Join(d.c.Dirs.Templates, "*")
129 t := template.Must(template.ParseGlob(tpath))
130
131 if len(commits) >= 3 {
132 commits = commits[:3]
133 }
134
135 data := make(map[string]any)
136 data["name"] = name
137 data["ref"] = mainBranch
138 data["readme"] = readmeContent
139 data["commits"] = commits
140 data["desc"] = getDescription(path)
141 data["clone"] = cloneURL
142 data["goimport"] = template.HTML(goImport)
143
144 if err := t.ExecuteTemplate(w, "repo", data); err != nil {
145 log.Println(err)
146 return
147 }
148
149 return
150}
151
152func (d *deps) RepoTree(w http.ResponseWriter, r *http.Request) {
153 name := flow.Param(r.Context(), "name")
154 treePath := flow.Param(r.Context(), "...")
155 ref := flow.Param(r.Context(), "ref")
156
157 name = filepath.Clean(name)
158 path := filepath.Join(d.c.Repo.ScanPath, name)
159 gr, err := git.Open(path, ref)
160 if err != nil {
161 d.Write404(w)
162 return
163 }
164
165 files, err := gr.FileTree(treePath)
166 if err != nil {
167 d.Write500(w)
168 log.Println(err)
169 return
170 }
171
172 data := make(map[string]any)
173 data["name"] = name
174 data["ref"] = ref
175 data["parent"] = treePath
176 data["desc"] = getDescription(path)
177
178 d.listFiles(files, data, w)
179 return
180}
181
182func (d *deps) FileContent(w http.ResponseWriter, r *http.Request) {
183 name := flow.Param(r.Context(), "name")
184 treePath := flow.Param(r.Context(), "...")
185 ref := flow.Param(r.Context(), "ref")
186
187 name = filepath.Clean(name)
188 path := filepath.Join(d.c.Repo.ScanPath, name)
189 gr, err := git.Open(path, ref)
190 if err != nil {
191 d.Write404(w)
192 return
193 }
194
195 contents, err := gr.FileContent(treePath)
196 data := make(map[string]any)
197 data["name"] = name
198 data["ref"] = ref
199 data["desc"] = getDescription(path)
200 data["path"] = treePath
201
202 d.showFile(contents, data, w)
203 return
204}
205
206func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
207 name := flow.Param(r.Context(), "name")
208 ref := flow.Param(r.Context(), "ref")
209
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 commits, err := gr.Commits()
218 if err != nil {
219 d.Write500(w)
220 log.Println(err)
221 return
222 }
223
224 tpath := filepath.Join(d.c.Dirs.Templates, "*")
225 t := template.Must(template.ParseGlob(tpath))
226
227 data := make(map[string]interface{})
228 data["commits"] = commits
229 data["meta"] = d.c.Meta
230 data["name"] = name
231 data["ref"] = ref
232 data["desc"] = getDescription(path)
233
234 if err := t.ExecuteTemplate(w, "log", data); err != nil {
235 log.Println(err)
236 return
237 }
238}
239
240func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
241 name := flow.Param(r.Context(), "name")
242 ref := flow.Param(r.Context(), "ref")
243
244 path := filepath.Join(d.c.Repo.ScanPath, name)
245 gr, err := git.Open(path, ref)
246 if err != nil {
247 d.Write404(w)
248 return
249 }
250
251 diff, err := gr.Diff()
252 if err != nil {
253 d.Write500(w)
254 log.Println(err)
255 return
256 }
257
258 tpath := filepath.Join(d.c.Dirs.Templates, "*")
259 t := template.Must(template.ParseGlob(tpath))
260
261 data := make(map[string]interface{})
262
263 data["commit"] = diff.Commit
264 data["stat"] = diff.Stat
265 data["diff"] = diff.Diff
266 data["meta"] = d.c.Meta
267 data["name"] = name
268 data["ref"] = ref
269 data["desc"] = getDescription(path)
270
271 if err := t.ExecuteTemplate(w, "commit", data); err != nil {
272 log.Println(err)
273 return
274 }
275}
276
277func (d *deps) Refs(w http.ResponseWriter, r *http.Request) {
278 name := flow.Param(r.Context(), "name")
279
280 path := filepath.Join(d.c.Repo.ScanPath, name)
281 gr, err := git.Open(path, "")
282 if err != nil {
283 d.Write404(w)
284 return
285 }
286
287 tags, err := gr.Tags()
288 if err != nil {
289 // Non-fatal, we *should* have at least one branch to show.
290 log.Println(err)
291 }
292
293 branches, err := gr.Branches()
294 if err != nil {
295 log.Println(err)
296 d.Write500(w)
297 return
298 }
299
300 tpath := filepath.Join(d.c.Dirs.Templates, "*")
301 t := template.Must(template.ParseGlob(tpath))
302
303 data := make(map[string]interface{})
304
305 data["meta"] = d.c.Meta
306 data["name"] = name
307 data["branches"] = branches
308 data["tags"] = tags
309 data["desc"] = getDescription(path)
310
311 if err := t.ExecuteTemplate(w, "refs", data); err != nil {
312 log.Println(err)
313 return
314 }
315}
316
317func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) {
318 f := flow.Param(r.Context(), "file")
319 f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f))
320
321 http.ServeFile(w, r, f)
322}
323
324func getDescription(path string) (desc string) {
325 db, err := os.ReadFile(filepath.Join(path, "description"))
326 if err == nil {
327 desc = string(db)
328 } else {
329 desc = ""
330 }
331 return
332}