all repos — legit @ 892ba7e5f3dbaf79b12a457f9ee6191df56625a3

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	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}