this repo has no description
1package routes 2 3import ( 4 "compress/gzip" 5 "fmt" 6 "html/template" 7 "log" 8 "net/http" 9 "os" 10 "path/filepath" 11 "sort" 12 "strconv" 13 "strings" 14 "time" 15 16 "github.com/dustin/go-humanize" 17 "github.com/go-chi/chi/v5" 18 "github.com/icyphox/bild/legit/config" 19 "github.com/icyphox/bild/legit/git" 20 "github.com/russross/blackfriday/v2" 21) 22 23type deps struct { 24 c *config.Config 25} 26 27func (d *deps) Index(w http.ResponseWriter, r *http.Request) { 28 dirs, err := os.ReadDir(d.c.Repo.ScanPath) 29 if err != nil { 30 d.Write500(w) 31 log.Printf("reading scan path: %s", err) 32 return 33 } 34 35 type info struct { 36 DisplayName, Name, Desc, Idle string 37 d time.Time 38 } 39 40 infos := []info{} 41 42 for _, dir := range dirs { 43 name := dir.Name() 44 if !dir.IsDir() || d.isIgnored(name) || d.isUnlisted(name) { 45 continue 46 } 47 48 path := filepath.Join(d.c.Repo.ScanPath, name) 49 gr, err := git.Open(path, "") 50 if err != nil { 51 log.Println(err) 52 continue 53 } 54 55 c, err := gr.LastCommit() 56 if err != nil { 57 d.Write500(w) 58 log.Println(err) 59 return 60 } 61 62 infos = append(infos, info{ 63 DisplayName: getDisplayName(name), 64 Name: name, 65 Desc: getDescription(path), 66 Idle: humanize.Time(c.Author.When), 67 d: c.Author.When, 68 }) 69 } 70 71 sort.Slice(infos, func(i, j int) bool { 72 return infos[j].d.Before(infos[i].d) 73 }) 74 75 tpath := filepath.Join(d.c.Dirs.Templates, "*") 76 t := template.Must(template.ParseGlob(tpath)) 77 78 data := make(map[string]interface{}) 79 data["meta"] = d.c.Meta 80 data["info"] = infos 81 82 if err := t.ExecuteTemplate(w, "index", data); err != nil { 83 log.Println(err) 84 return 85 } 86} 87 88func (d *deps) RepoIndex(w http.ResponseWriter, r *http.Request) { 89 name := uniqueName(r) 90 if d.isIgnored(name) { 91 d.Write404(w) 92 return 93 } 94 95 name = filepath.Clean(name) 96 path := filepath.Join(d.c.Repo.ScanPath, name) 97 98 fmt.Println(path) 99 gr, err := git.Open(path, "") 100 if err != nil { 101 d.Write404(w) 102 return 103 } 104 commits, err := gr.Commits() 105 if err != nil { 106 d.Write500(w) 107 log.Println(err) 108 return 109 } 110 111 var readmeContent template.HTML 112 for _, readme := range d.c.Repo.Readme { 113 ext := filepath.Ext(readme) 114 content, _ := gr.FileContent(readme) 115 if len(content) > 0 { 116 switch ext { 117 case ".md", ".mkd", ".markdown": 118 unsafe := blackfriday.Run( 119 []byte(content), 120 blackfriday.WithExtensions(blackfriday.CommonExtensions), 121 ) 122 html := sanitize(unsafe) 123 readmeContent = template.HTML(html) 124 default: 125 safe := sanitize([]byte(content)) 126 readmeContent = template.HTML( 127 fmt.Sprintf(`<pre>%s</pre>`, safe), 128 ) 129 } 130 break 131 } 132 } 133 134 if readmeContent == "" { 135 log.Printf("no readme found for %s", name) 136 } 137 138 mainBranch, err := gr.FindMainBranch(d.c.Repo.MainBranch) 139 if err != nil { 140 d.Write500(w) 141 log.Println(err) 142 return 143 } 144 145 tpath := filepath.Join(d.c.Dirs.Templates, "*") 146 t := template.Must(template.ParseGlob(tpath)) 147 148 if len(commits) >= 3 { 149 commits = commits[:3] 150 } 151 152 data := make(map[string]any) 153 data["name"] = name 154 data["displayname"] = getDisplayName(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 := uniqueName(r) 173 if d.isIgnored(name) { 174 d.Write404(w) 175 return 176 } 177 treePath := chi.URLParam(r, "*") 178 ref := chi.URLParam(r, "ref") 179 180 name = filepath.Clean(name) 181 path := filepath.Join(d.c.Repo.ScanPath, name) 182 fmt.Println(path) 183 gr, err := git.Open(path, ref) 184 if err != nil { 185 d.Write404(w) 186 return 187 } 188 189 files, err := gr.FileTree(treePath) 190 if err != nil { 191 d.Write500(w) 192 log.Println(err) 193 return 194 } 195 196 data := make(map[string]any) 197 data["name"] = name 198 data["displayname"] = getDisplayName(name) 199 data["ref"] = ref 200 data["parent"] = treePath 201 data["desc"] = getDescription(path) 202 data["dotdot"] = filepath.Dir(treePath) 203 204 d.listFiles(files, data, w) 205 return 206} 207 208func (d *deps) FileContent(w http.ResponseWriter, r *http.Request) { 209 var raw bool 210 if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil { 211 raw = rawParam 212 } 213 214 name := uniqueName(r) 215 216 if d.isIgnored(name) { 217 d.Write404(w) 218 return 219 } 220 treePath := chi.URLParam(r, "*") 221 ref := chi.URLParam(r, "ref") 222 223 name = filepath.Clean(name) 224 path := filepath.Join(d.c.Repo.ScanPath, name) 225 gr, err := git.Open(path, ref) 226 if err != nil { 227 d.Write404(w) 228 return 229 } 230 231 contents, err := gr.FileContent(treePath) 232 if err != nil { 233 d.Write500(w) 234 return 235 } 236 data := make(map[string]any) 237 data["name"] = name 238 data["displayname"] = getDisplayName(name) 239 data["ref"] = ref 240 data["desc"] = getDescription(path) 241 data["path"] = treePath 242 243 safe := sanitize([]byte(contents)) 244 245 if raw { 246 d.showRaw(string(safe), w) 247 } else { 248 if d.c.Meta.SyntaxHighlight == "" { 249 d.showFile(string(safe), data, w) 250 } else { 251 d.showFileWithHighlight(treePath, string(safe), data, w) 252 } 253 } 254} 255 256func (d *deps) Archive(w http.ResponseWriter, r *http.Request) { 257 name := uniqueName(r) 258 if d.isIgnored(name) { 259 d.Write404(w) 260 return 261 } 262 263 file := chi.URLParam(r, "file") 264 265 // TODO: extend this to add more files compression (e.g.: xz) 266 if !strings.HasSuffix(file, ".tar.gz") { 267 d.Write404(w) 268 return 269 } 270 271 ref := strings.TrimSuffix(file, ".tar.gz") 272 273 // This allows the browser to use a proper name for the file when 274 // downloading 275 filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) 276 setContentDisposition(w, filename) 277 setGZipMIME(w) 278 279 path := filepath.Join(d.c.Repo.ScanPath, name) 280 gr, err := git.Open(path, ref) 281 if err != nil { 282 d.Write404(w) 283 return 284 } 285 286 gw := gzip.NewWriter(w) 287 defer gw.Close() 288 289 prefix := fmt.Sprintf("%s-%s", name, ref) 290 err = gr.WriteTar(gw, prefix) 291 if err != nil { 292 // once we start writing to the body we can't report error anymore 293 // so we are only left with printing the error. 294 log.Println(err) 295 return 296 } 297 298 err = gw.Flush() 299 if err != nil { 300 // once we start writing to the body we can't report error anymore 301 // so we are only left with printing the error. 302 log.Println(err) 303 return 304 } 305} 306 307func (d *deps) Log(w http.ResponseWriter, r *http.Request) { 308 name := uniqueName(r) 309 if d.isIgnored(name) { 310 d.Write404(w) 311 return 312 } 313 ref := chi.URLParam(r, "ref") 314 315 path := filepath.Join(d.c.Repo.ScanPath, name) 316 gr, err := git.Open(path, ref) 317 if err != nil { 318 d.Write404(w) 319 return 320 } 321 322 commits, err := gr.Commits() 323 if err != nil { 324 d.Write500(w) 325 log.Println(err) 326 return 327 } 328 329 tpath := filepath.Join(d.c.Dirs.Templates, "*") 330 t := template.Must(template.ParseGlob(tpath)) 331 332 data := make(map[string]interface{}) 333 data["commits"] = commits 334 data["meta"] = d.c.Meta 335 data["name"] = name 336 data["displayname"] = getDisplayName(name) 337 data["ref"] = ref 338 data["desc"] = getDescription(path) 339 data["log"] = true 340 341 if err := t.ExecuteTemplate(w, "log", data); err != nil { 342 log.Println(err) 343 return 344 } 345} 346 347func (d *deps) Diff(w http.ResponseWriter, r *http.Request) { 348 name := uniqueName(r) 349 if d.isIgnored(name) { 350 d.Write404(w) 351 return 352 } 353 ref := chi.URLParam(r, "ref") 354 355 path := filepath.Join(d.c.Repo.ScanPath, name) 356 gr, err := git.Open(path, ref) 357 if err != nil { 358 d.Write404(w) 359 return 360 } 361 362 diff, err := gr.Diff() 363 if err != nil { 364 d.Write500(w) 365 log.Println(err) 366 return 367 } 368 369 tpath := filepath.Join(d.c.Dirs.Templates, "*") 370 t := template.Must(template.ParseGlob(tpath)) 371 372 data := make(map[string]interface{}) 373 374 data["commit"] = diff.Commit 375 data["stat"] = diff.Stat 376 data["diff"] = diff.Diff 377 data["meta"] = d.c.Meta 378 data["name"] = name 379 data["displayname"] = getDisplayName(name) 380 data["ref"] = ref 381 data["desc"] = getDescription(path) 382 383 if err := t.ExecuteTemplate(w, "commit", data); err != nil { 384 log.Println(err) 385 return 386 } 387} 388 389func (d *deps) Refs(w http.ResponseWriter, r *http.Request) { 390 name := chi.URLParam(r, "name") 391 if d.isIgnored(name) { 392 d.Write404(w) 393 return 394 } 395 396 path := filepath.Join(d.c.Repo.ScanPath, name) 397 gr, err := git.Open(path, "") 398 if err != nil { 399 d.Write404(w) 400 return 401 } 402 403 tags, err := gr.Tags() 404 if err != nil { 405 // Non-fatal, we *should* have at least one branch to show. 406 log.Println(err) 407 } 408 409 branches, err := gr.Branches() 410 if err != nil { 411 log.Println(err) 412 d.Write500(w) 413 return 414 } 415 416 tpath := filepath.Join(d.c.Dirs.Templates, "*") 417 t := template.Must(template.ParseGlob(tpath)) 418 419 data := make(map[string]interface{}) 420 421 data["meta"] = d.c.Meta 422 data["name"] = name 423 data["displayname"] = getDisplayName(name) 424 data["branches"] = branches 425 data["tags"] = tags 426 data["desc"] = getDescription(path) 427 428 if err := t.ExecuteTemplate(w, "refs", data); err != nil { 429 log.Println(err) 430 return 431 } 432} 433 434func (d *deps) ServeStatic(w http.ResponseWriter, r *http.Request) { 435 f := chi.URLParam(r, "file") 436 f = filepath.Clean(filepath.Join(d.c.Dirs.Static, f)) 437 438 http.ServeFile(w, r, f) 439}