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
118 if d.c.Misc.GoImport.PrettyURL == "" {
119 d.c.Misc.GoImport.PrettyURL = cloneURL
120 }
121
122 goImport := fmt.Sprintf(`<meta name="go-import" content="%s git %s">`,
123 d.c.Misc.GoImport.PrettyURL, cloneURL)
124
125 tpath := filepath.Join(d.c.Dirs.Templates, "*")
126 t := template.Must(template.ParseGlob(tpath))
127
128 if len(commits) >= 3 {
129 commits = commits[:3]
130 }
131
132 data := make(map[string]any)
133 data["name"] = name
134 data["ref"] = mainBranch
135 data["readme"] = readmeContent
136 data["commits"] = commits
137 data["desc"] = getDescription(path)
138 data["clone"] = cloneURL
139 data["goimport"] = template.HTML(goImport)
140
141 if err := t.ExecuteTemplate(w, "repo", data); err != nil {
142 log.Println(err)
143 return
144 }
145
146 return
147}
148
149func (d *deps) RepoTree(w http.ResponseWriter, r *http.Request) {
150 name := flow.Param(r.Context(), "name")
151 treePath := flow.Param(r.Context(), "...")
152 ref := flow.Param(r.Context(), "ref")
153
154 name = filepath.Clean(name)
155 path := filepath.Join(d.c.Repo.ScanPath, name)
156 gr, err := git.Open(path, ref)
157 if err != nil {
158 d.Write404(w)
159 return
160 }
161
162 files, err := gr.FileTree(treePath)
163 if err != nil {
164 d.Write500(w)
165 log.Println(err)
166 return
167 }
168
169 data := make(map[string]any)
170 data["name"] = name
171 data["ref"] = ref
172 data["parent"] = treePath
173 data["desc"] = getDescription(path)
174
175 d.listFiles(files, data, w)
176 return
177}
178
179func (d *deps) FileContent(w http.ResponseWriter, r *http.Request) {
180 name := flow.Param(r.Context(), "name")
181 treePath := flow.Param(r.Context(), "...")
182 ref := flow.Param(r.Context(), "ref")
183
184 name = filepath.Clean(name)
185 path := filepath.Join(d.c.Repo.ScanPath, name)
186 gr, err := git.Open(path, ref)
187 if err != nil {
188 d.Write404(w)
189 return
190 }
191
192 contents, err := gr.FileContent(treePath)
193 data := make(map[string]any)
194 data["name"] = name
195 data["ref"] = ref
196 data["desc"] = getDescription(path)
197 data["path"] = treePath
198
199 d.showFile(contents, data, w)
200 return
201}
202
203func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
204 name := flow.Param(r.Context(), "name")
205 ref := flow.Param(r.Context(), "ref")
206
207 path := filepath.Join(d.c.Repo.ScanPath, name)
208 gr, err := git.Open(path, ref)
209 if err != nil {
210 d.Write404(w)
211 return
212 }
213
214 commits, err := gr.Commits()
215 if err != nil {
216 d.Write500(w)
217 log.Println(err)
218 return
219 }
220
221 tpath := filepath.Join(d.c.Dirs.Templates, "*")
222 t := template.Must(template.ParseGlob(tpath))
223
224 data := make(map[string]interface{})
225 data["commits"] = commits
226 data["meta"] = d.c.Meta
227 data["name"] = name
228 data["ref"] = ref
229 data["desc"] = getDescription(path)
230
231 if err := t.ExecuteTemplate(w, "log", data); err != nil {
232 log.Println(err)
233 return
234 }
235}
236
237func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
238 name := flow.Param(r.Context(), "name")
239 ref := flow.Param(r.Context(), "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 diff, err := gr.Diff()
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
260 data["commit"] = diff.Commit
261 data["stat"] = diff.Stat
262 data["diff"] = diff.Diff
263 data["meta"] = d.c.Meta
264 data["name"] = name
265 data["ref"] = ref
266 data["desc"] = getDescription(path)
267
268 if err := t.ExecuteTemplate(w, "commit", data); err != nil {
269 log.Println(err)
270 return
271 }
272}
273
274func (d *deps) Refs(w http.ResponseWriter, r *http.Request) {
275 name := flow.Param(r.Context(), "name")
276
277 path := filepath.Join(d.c.Repo.ScanPath, name)
278 gr, err := git.Open(path, "")
279 if err != nil {
280 d.Write404(w)
281 return
282 }
283
284 tags, err := gr.Tags()
285 if err != nil {
286 // Non-fatal, we *should* have at least one branch to show.
287 log.Println(err)
288 }
289
290 branches, err := gr.Branches()
291 if err != nil {
292 log.Println(err)
293 d.Write500(w)
294 return
295 }
296
297 tpath := filepath.Join(d.c.Dirs.Templates, "*")
298 t := template.Must(template.ParseGlob(tpath))
299
300 data := make(map[string]interface{})
301
302 data["meta"] = d.c.Meta
303 data["name"] = name
304 data["branches"] = branches
305 data["tags"] = tags
306 data["desc"] = getDescription(path)
307
308 if err := t.ExecuteTemplate(w, "refs", data); err != nil {
309 log.Println(err)
310 return
311 }
312}
313
314func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) {
315 f := flow.Param(r.Context(), "file")
316 f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f))
317
318 http.ServeFile(w, r, f)
319}
320
321func getDescription(path string) (desc string) {
322 db, err := os.ReadFile(filepath.Join(path, "description"))
323 if err == nil {
324 desc = string(db)
325 } else {
326 desc = ""
327 }
328 return
329}