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