all repos — legit @ a13ced49e12440e25a4553fd897ca0d009bad11e

web frontend for git

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