all repos — legit @ 5912a0bcb539dce1a220656b3b5c382e65014447

web frontend for git, written in go

routes/routes.go (view raw)

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