all repos — legit @ a87f88a57c68627a1f63f1551fcef2c1f1ec7036

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