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