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