all repos — legit @ 0e6eaa211357cd5b9de1762b1cb49e47d35580dc

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	"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
118	if d.c.Misc.GoImport.PrettyURL == "" {
119		d.c.Misc.GoImport.PrettyURL = cloneURL
120	}
121
122	goImport := fmt.Sprintf(`<meta name="go-import" content="%s git %s">`,
123		d.c.Misc.GoImport.PrettyURL, cloneURL)
124
125	tpath := filepath.Join(d.c.Dirs.Templates, "*")
126	t := template.Must(template.ParseGlob(tpath))
127
128	if len(commits) >= 3 {
129		commits = commits[:3]
130	}
131
132	data := make(map[string]any)
133	data["name"] = name
134	data["ref"] = mainBranch
135	data["readme"] = readmeContent
136	data["commits"] = commits
137	data["desc"] = getDescription(path)
138	data["clone"] = cloneURL
139	data["goimport"] = template.HTML(goImport)
140
141	if err := t.ExecuteTemplate(w, "repo", data); err != nil {
142		log.Println(err)
143		return
144	}
145
146	return
147}
148
149func (d *deps) RepoTree(w http.ResponseWriter, r *http.Request) {
150	name := flow.Param(r.Context(), "name")
151	treePath := flow.Param(r.Context(), "...")
152	ref := flow.Param(r.Context(), "ref")
153
154	name = filepath.Clean(name)
155	path := filepath.Join(d.c.Repo.ScanPath, name)
156	gr, err := git.Open(path, ref)
157	if err != nil {
158		d.Write404(w)
159		return
160	}
161
162	files, err := gr.FileTree(treePath)
163	if err != nil {
164		d.Write500(w)
165		log.Println(err)
166		return
167	}
168
169	data := make(map[string]any)
170	data["name"] = name
171	data["ref"] = ref
172	data["parent"] = treePath
173	data["desc"] = getDescription(path)
174
175	d.listFiles(files, data, w)
176	return
177}
178
179func (d *deps) FileContent(w http.ResponseWriter, r *http.Request) {
180	name := flow.Param(r.Context(), "name")
181	treePath := flow.Param(r.Context(), "...")
182	ref := flow.Param(r.Context(), "ref")
183
184	name = filepath.Clean(name)
185	path := filepath.Join(d.c.Repo.ScanPath, name)
186	gr, err := git.Open(path, ref)
187	if err != nil {
188		d.Write404(w)
189		return
190	}
191
192	contents, err := gr.FileContent(treePath)
193	data := make(map[string]any)
194	data["name"] = name
195	data["ref"] = ref
196	data["desc"] = getDescription(path)
197	data["path"] = treePath
198
199	d.showFile(contents, data, w)
200	return
201}
202
203func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
204	name := flow.Param(r.Context(), "name")
205	ref := flow.Param(r.Context(), "ref")
206
207	path := filepath.Join(d.c.Repo.ScanPath, name)
208	gr, err := git.Open(path, ref)
209	if err != nil {
210		d.Write404(w)
211		return
212	}
213
214	commits, err := gr.Commits()
215	if err != nil {
216		d.Write500(w)
217		log.Println(err)
218		return
219	}
220
221	tpath := filepath.Join(d.c.Dirs.Templates, "*")
222	t := template.Must(template.ParseGlob(tpath))
223
224	data := make(map[string]interface{})
225	data["commits"] = commits
226	data["meta"] = d.c.Meta
227	data["name"] = name
228	data["ref"] = ref
229	data["desc"] = getDescription(path)
230
231	if err := t.ExecuteTemplate(w, "log", data); err != nil {
232		log.Println(err)
233		return
234	}
235}
236
237func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
238	name := flow.Param(r.Context(), "name")
239	ref := flow.Param(r.Context(), "ref")
240
241	path := filepath.Join(d.c.Repo.ScanPath, name)
242	gr, err := git.Open(path, ref)
243	if err != nil {
244		d.Write404(w)
245		return
246	}
247
248	diff, err := gr.Diff()
249	if err != nil {
250		d.Write500(w)
251		log.Println(err)
252		return
253	}
254
255	tpath := filepath.Join(d.c.Dirs.Templates, "*")
256	t := template.Must(template.ParseGlob(tpath))
257
258	data := make(map[string]interface{})
259
260	data["commit"] = diff.Commit
261	data["stat"] = diff.Stat
262	data["diff"] = diff.Diff
263	data["meta"] = d.c.Meta
264	data["name"] = name
265	data["ref"] = ref
266	data["desc"] = getDescription(path)
267
268	if err := t.ExecuteTemplate(w, "commit", data); err != nil {
269		log.Println(err)
270		return
271	}
272}
273
274func (d *deps) Refs(w http.ResponseWriter, r *http.Request) {
275	name := flow.Param(r.Context(), "name")
276
277	path := filepath.Join(d.c.Repo.ScanPath, name)
278	gr, err := git.Open(path, "")
279	if err != nil {
280		d.Write404(w)
281		return
282	}
283
284	tags, err := gr.Tags()
285	if err != nil {
286		// Non-fatal, we *should* have at least one branch to show.
287		log.Println(err)
288	}
289
290	branches, err := gr.Branches()
291	if err != nil {
292		log.Println(err)
293		d.Write500(w)
294		return
295	}
296
297	tpath := filepath.Join(d.c.Dirs.Templates, "*")
298	t := template.Must(template.ParseGlob(tpath))
299
300	data := make(map[string]interface{})
301
302	data["meta"] = d.c.Meta
303	data["name"] = name
304	data["branches"] = branches
305	data["tags"] = tags
306	data["desc"] = getDescription(path)
307
308	if err := t.ExecuteTemplate(w, "refs", data); err != nil {
309		log.Println(err)
310		return
311	}
312}
313
314func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) {
315	f := flow.Param(r.Context(), "file")
316	f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f))
317
318	http.ServeFile(w, r, f)
319}
320
321func getDescription(path string) (desc string) {
322	db, err := os.ReadFile(filepath.Join(path, "description"))
323	if err == nil {
324		desc = string(db)
325	} else {
326		desc = ""
327	}
328	return
329}