all repos — legit @ 99a092b8b01dd8edbc5a01401a1ae4178f88a018

web frontend for git, written in go

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