all repos — legit @ c14c33b9166614e77aaca735ca3d0eacf8c81a61

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