all repos — legit @ 30c0f3eea0b2148902c4f4e94c6fa6a63251b665

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