all repos — legit @ 95dec0f0564c158d2884faa24cac650043e8dc37

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