all repos — legit @ 4f5d35ffd22e8727c25ba0b028c0baade25cb32f

web frontend for git, written in go

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