all repos — legit @ hide-dotgit-extension

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		DisplayName, 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		name := dir.Name()
 60
 61		desc := getDescription(path)
 62
 63		infos = append(infos, info{
 64			DisplayName: getDisplayName(name),
 65			Name:        name,
 66			Desc:        desc,
 67			Idle:        humanize.Time(c.Author.When),
 68			d:           c.Author.When,
 69		})
 70	}
 71
 72	sort.Slice(infos, func(i, j int) bool {
 73		return infos[j].d.Before(infos[i].d)
 74	})
 75
 76	tpath := filepath.Join(d.c.Dirs.Templates, "*")
 77	t := template.Must(template.ParseGlob(tpath))
 78
 79	data := make(map[string]interface{})
 80	data["meta"] = d.c.Meta
 81	data["info"] = infos
 82
 83	if err := t.ExecuteTemplate(w, "index", data); err != nil {
 84		log.Println(err)
 85		return
 86	}
 87}
 88
 89func (d *deps) RepoIndex(w http.ResponseWriter, r *http.Request) {
 90	name := r.PathValue("name")
 91	if d.isIgnored(name) {
 92		d.Write404(w)
 93		return
 94	}
 95	name = filepath.Clean(name)
 96	path := filepath.Join(d.c.Repo.ScanPath, name)
 97
 98	gr, err := git.Open(path, "")
 99	if err != nil {
100		d.Write404(w)
101		return
102	}
103
104	commits, err := gr.Commits()
105	if err != nil {
106		d.Write500(w)
107		log.Println(err)
108		return
109	}
110
111	var readmeContent template.HTML
112	for _, readme := range d.c.Repo.Readme {
113		ext := filepath.Ext(readme)
114		content, _ := gr.FileContent(readme)
115		if len(content) > 0 {
116			switch ext {
117			case ".md", ".mkd", ".markdown":
118				unsafe := blackfriday.Run(
119					[]byte(content),
120					blackfriday.WithExtensions(blackfriday.CommonExtensions),
121				)
122				html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
123				readmeContent = template.HTML(html)
124			default:
125				readmeContent = template.HTML(
126					fmt.Sprintf(`<pre>%s</pre>`, content),
127				)
128			}
129			break
130		}
131	}
132
133	if readmeContent == "" {
134		log.Printf("no readme found for %s", name)
135	}
136
137	mainBranch, err := gr.FindMainBranch(d.c.Repo.MainBranch)
138	if err != nil {
139		d.Write500(w)
140		log.Println(err)
141		return
142	}
143
144	tpath := filepath.Join(d.c.Dirs.Templates, "*")
145	t := template.Must(template.ParseGlob(tpath))
146
147	if len(commits) >= 3 {
148		commits = commits[:3]
149	}
150
151	data := make(map[string]any)
152	data["name"] = name
153	data["displayname"] = getDisplayName(name)
154	data["ref"] = mainBranch
155	data["readme"] = readmeContent
156	data["commits"] = commits
157	data["desc"] = getDescription(path)
158	data["servername"] = d.c.Server.Name
159	data["meta"] = d.c.Meta
160	data["gomod"] = isGoModule(gr)
161
162	if err := t.ExecuteTemplate(w, "repo", data); err != nil {
163		log.Println(err)
164		return
165	}
166
167	return
168}
169
170func (d *deps) RepoTree(w http.ResponseWriter, r *http.Request) {
171	name := r.PathValue("name")
172	if d.isIgnored(name) {
173		d.Write404(w)
174		return
175	}
176	treePath := r.PathValue("rest")
177	ref := r.PathValue("ref")
178
179	name = filepath.Clean(name)
180	path := filepath.Join(d.c.Repo.ScanPath, name)
181	gr, err := git.Open(path, ref)
182	if err != nil {
183		d.Write404(w)
184		return
185	}
186
187	files, err := gr.FileTree(treePath)
188	if err != nil {
189		d.Write500(w)
190		log.Println(err)
191		return
192	}
193
194	data := make(map[string]any)
195	data["name"] = name
196	data["displayname"] = getDisplayName(name)
197	data["ref"] = ref
198	data["parent"] = treePath
199	data["desc"] = getDescription(path)
200	data["dotdot"] = filepath.Dir(treePath)
201
202	d.listFiles(files, data, w)
203	return
204}
205
206func (d *deps) FileContent(w http.ResponseWriter, r *http.Request) {
207	var raw bool
208	if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil {
209		raw = rawParam
210	}
211
212	name := r.PathValue("name")
213	if d.isIgnored(name) {
214		d.Write404(w)
215		return
216	}
217	treePath := r.PathValue("rest")
218	ref := r.PathValue("ref")
219
220	name = filepath.Clean(name)
221	path := filepath.Join(d.c.Repo.ScanPath, name)
222	gr, err := git.Open(path, ref)
223	if err != nil {
224		d.Write404(w)
225		return
226	}
227
228	contents, err := gr.FileContent(treePath)
229	data := make(map[string]any)
230	data["name"] = name
231	data["displayname"] = getDisplayName(name)
232	data["ref"] = ref
233	data["desc"] = getDescription(path)
234	data["path"] = treePath
235
236	if raw {
237		d.showRaw(contents, w)
238	} else {
239		d.showFile(contents, data, w)
240	}
241	return
242}
243
244func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
245	name := r.PathValue("name")
246	if d.isIgnored(name) {
247		d.Write404(w)
248		return
249	}
250	ref := r.PathValue("ref")
251
252	path := filepath.Join(d.c.Repo.ScanPath, name)
253	gr, err := git.Open(path, ref)
254	if err != nil {
255		d.Write404(w)
256		return
257	}
258
259	commits, err := gr.Commits()
260	if err != nil {
261		d.Write500(w)
262		log.Println(err)
263		return
264	}
265
266	tpath := filepath.Join(d.c.Dirs.Templates, "*")
267	t := template.Must(template.ParseGlob(tpath))
268
269	data := make(map[string]interface{})
270	data["commits"] = commits
271	data["meta"] = d.c.Meta
272	data["name"] = name
273	data["displayname"] = getDisplayName(name)
274	data["ref"] = ref
275	data["desc"] = getDescription(path)
276	data["log"] = true
277
278	if err := t.ExecuteTemplate(w, "log", data); err != nil {
279		log.Println(err)
280		return
281	}
282}
283
284func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
285	name := r.PathValue("name")
286	if d.isIgnored(name) {
287		d.Write404(w)
288		return
289	}
290	ref := r.PathValue("ref")
291
292	path := filepath.Join(d.c.Repo.ScanPath, name)
293	gr, err := git.Open(path, ref)
294	if err != nil {
295		d.Write404(w)
296		return
297	}
298
299	diff, err := gr.Diff()
300	if err != nil {
301		d.Write500(w)
302		log.Println(err)
303		return
304	}
305
306	tpath := filepath.Join(d.c.Dirs.Templates, "*")
307	t := template.Must(template.ParseGlob(tpath))
308
309	data := make(map[string]interface{})
310
311	data["commit"] = diff.Commit
312	data["stat"] = diff.Stat
313	data["diff"] = diff.Diff
314	data["meta"] = d.c.Meta
315	data["name"] = name
316	data["displayname"] = getDisplayName(name)
317	data["ref"] = ref
318	data["desc"] = getDescription(path)
319
320	if err := t.ExecuteTemplate(w, "commit", data); err != nil {
321		log.Println(err)
322		return
323	}
324}
325
326func (d *deps) Refs(w http.ResponseWriter, r *http.Request) {
327	name := r.PathValue("name")
328	if d.isIgnored(name) {
329		d.Write404(w)
330		return
331	}
332
333	path := filepath.Join(d.c.Repo.ScanPath, name)
334	gr, err := git.Open(path, "")
335	if err != nil {
336		d.Write404(w)
337		return
338	}
339
340	tags, err := gr.Tags()
341	if err != nil {
342		// Non-fatal, we *should* have at least one branch to show.
343		log.Println(err)
344	}
345
346	branches, err := gr.Branches()
347	if err != nil {
348		log.Println(err)
349		d.Write500(w)
350		return
351	}
352
353	tpath := filepath.Join(d.c.Dirs.Templates, "*")
354	t := template.Must(template.ParseGlob(tpath))
355
356	data := make(map[string]interface{})
357
358	data["meta"] = d.c.Meta
359	data["name"] = name
360	data["displayname"] = getDisplayName(name)
361	data["branches"] = branches
362	data["tags"] = tags
363	data["desc"] = getDescription(path)
364
365	if err := t.ExecuteTemplate(w, "refs", data); err != nil {
366		log.Println(err)
367		return
368	}
369}
370
371func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) {
372	f := r.PathValue("file")
373	f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f))
374
375	http.ServeFile(w, r, f)
376}