this repo has no description
1package knotserver 2 3import ( 4 "compress/gzip" 5 "errors" 6 "fmt" 7 "html/template" 8 "log" 9 "net/http" 10 "path/filepath" 11 "strconv" 12 "strings" 13 14 "github.com/go-chi/chi/v5" 15 "github.com/go-git/go-git/v5/plumbing" 16 "github.com/icyphox/bild/git" 17 "github.com/russross/blackfriday/v2" 18) 19 20func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { 21 w.Write([]byte("This is a knot, part of the wider Tangle network: https://knots.sh")) 22} 23 24func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) { 25 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 26 27 gr, err := git.Open(path, "") 28 if err != nil { 29 if errors.Is(err, plumbing.ErrReferenceNotFound) { 30 writeMsg(w, "repo empty") 31 return 32 } else { 33 log.Println(err) 34 notFound(w) 35 return 36 } 37 } 38 commits, err := gr.Commits() 39 if err != nil { 40 writeError(w, err.Error(), http.StatusInternalServerError) 41 log.Println(err) 42 return 43 } 44 45 var readmeContent template.HTML 46 for _, readme := range h.c.Repo.Readme { 47 ext := filepath.Ext(readme) 48 content, _ := gr.FileContent(readme) 49 if len(content) > 0 { 50 switch ext { 51 case ".md", ".mkd", ".markdown": 52 unsafe := blackfriday.Run( 53 []byte(content), 54 blackfriday.WithExtensions(blackfriday.CommonExtensions), 55 ) 56 html := sanitize(unsafe) 57 readmeContent = template.HTML(html) 58 default: 59 safe := sanitize([]byte(content)) 60 readmeContent = template.HTML( 61 fmt.Sprintf(`<pre>%s</pre>`, safe), 62 ) 63 } 64 break 65 } 66 } 67 68 if readmeContent == "" { 69 log.Printf("no readme found for %s", path) 70 } 71 72 mainBranch, err := gr.FindMainBranch(h.c.Repo.MainBranch) 73 if err != nil { 74 writeError(w, err.Error(), http.StatusInternalServerError) 75 log.Println(err) 76 return 77 } 78 79 if len(commits) >= 3 { 80 commits = commits[:3] 81 } 82 data := make(map[string]any) 83 data["ref"] = mainBranch 84 data["readme"] = readmeContent 85 data["commits"] = commits 86 data["desc"] = getDescription(path) 87 data["servername"] = h.c.Server.Name 88 data["meta"] = h.c.Meta 89 90 writeJSON(w, data) 91 return 92} 93 94func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) { 95 treePath := chi.URLParam(r, "*") 96 ref := chi.URLParam(r, "ref") 97 98 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 99 gr, err := git.Open(path, ref) 100 if err != nil { 101 notFound(w) 102 return 103 } 104 105 files, err := gr.FileTree(treePath) 106 if err != nil { 107 writeError(w, err.Error(), http.StatusInternalServerError) 108 log.Println(err) 109 return 110 } 111 112 data := make(map[string]any) 113 data["ref"] = ref 114 data["parent"] = treePath 115 data["desc"] = getDescription(path) 116 data["dotdot"] = filepath.Dir(treePath) 117 118 h.listFiles(files, data, w) 119 return 120} 121 122func (h *Handle) FileContent(w http.ResponseWriter, r *http.Request) { 123 var raw bool 124 if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil { 125 raw = rawParam 126 } 127 128 treePath := chi.URLParam(r, "*") 129 ref := chi.URLParam(r, "ref") 130 131 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 132 gr, err := git.Open(path, ref) 133 if err != nil { 134 notFound(w) 135 return 136 } 137 138 contents, err := gr.FileContent(treePath) 139 if err != nil { 140 writeError(w, err.Error(), http.StatusInternalServerError) 141 return 142 } 143 data := make(map[string]any) 144 data["ref"] = ref 145 data["desc"] = getDescription(path) 146 data["path"] = treePath 147 148 safe := sanitize([]byte(contents)) 149 150 if raw { 151 h.showRaw(string(safe), w) 152 } else { 153 h.showFile(string(safe), data, w) 154 } 155} 156 157func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) { 158 name := displayRepoName(r) 159 160 file := chi.URLParam(r, "file") 161 162 // TODO: extend this to add more files compression (e.g.: xz) 163 if !strings.HasSuffix(file, ".tar.gz") { 164 notFound(w) 165 return 166 } 167 168 ref := strings.TrimSuffix(file, ".tar.gz") 169 170 // This allows the browser to use a proper name for the file when 171 // downloading 172 filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) 173 setContentDisposition(w, filename) 174 setGZipMIME(w) 175 176 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 177 gr, err := git.Open(path, ref) 178 if err != nil { 179 notFound(w) 180 return 181 } 182 183 gw := gzip.NewWriter(w) 184 defer gw.Close() 185 186 prefix := fmt.Sprintf("%s-%s", name, ref) 187 err = gr.WriteTar(gw, prefix) 188 if err != nil { 189 // once we start writing to the body we can't report error anymore 190 // so we are only left with printing the error. 191 log.Println(err) 192 return 193 } 194 195 err = gw.Flush() 196 if err != nil { 197 // once we start writing to the body we can't report error anymore 198 // so we are only left with printing the error. 199 log.Println(err) 200 return 201 } 202} 203 204func (h *Handle) Log(w http.ResponseWriter, r *http.Request) { 205 fmt.Println(r.URL.Path) 206 ref := chi.URLParam(r, "ref") 207 208 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 209 gr, err := git.Open(path, ref) 210 if err != nil { 211 notFound(w) 212 return 213 } 214 215 commits, err := gr.Commits() 216 if err != nil { 217 writeError(w, err.Error(), http.StatusInternalServerError) 218 log.Println(err) 219 return 220 } 221 222 data := make(map[string]interface{}) 223 data["commits"] = commits 224 data["meta"] = h.c.Meta 225 data["ref"] = ref 226 data["desc"] = getDescription(path) 227 data["log"] = true 228 229 writeJSON(w, data) 230 return 231} 232 233func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) { 234 name := displayRepoName(r) 235 if h.isIgnored(name) { 236 notFound(w) 237 return 238 } 239 ref := chi.URLParam(r, "ref") 240 241 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 242 gr, err := git.Open(path, ref) 243 if err != nil { 244 notFound(w) 245 return 246 } 247 248 diff, err := gr.Diff() 249 if err != nil { 250 writeError(w, err.Error(), http.StatusInternalServerError) 251 log.Println(err) 252 return 253 } 254 255 data := make(map[string]interface{}) 256 257 data["commit"] = diff.Commit 258 data["stat"] = diff.Stat 259 data["diff"] = diff.Diff 260 data["meta"] = h.c.Meta 261 data["ref"] = ref 262 data["desc"] = getDescription(path) 263 264 writeJSON(w, data) 265 return 266} 267 268func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) { 269 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 270 gr, err := git.Open(path, "") 271 if err != nil { 272 notFound(w) 273 return 274 } 275 276 tags, err := gr.Tags() 277 if err != nil { 278 // Non-fatal, we *should* have at least one branch to show. 279 log.Println(err) 280 } 281 282 branches, err := gr.Branches() 283 if err != nil { 284 log.Println(err) 285 writeError(w, err.Error(), http.StatusInternalServerError) 286 return 287 } 288 289 data := make(map[string]interface{}) 290 291 data["meta"] = h.c.Meta 292 data["branches"] = branches 293 data["tags"] = tags 294 data["desc"] = getDescription(path) 295 296 writeJSON(w, data) 297 return 298} 299 300func (h *Handle) ServeStatic(w http.ResponseWriter, r *http.Request) { 301 f := chi.URLParam(r, "file") 302 f = filepath.Clean(filepath.Join(h.c.Dirs.Static, f)) 303 304 http.ServeFile(w, r, f) 305} 306 307// func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) { 308// session, _ := h.s.Get(r, "bild-session") 309// did := session.Values["did"].(string) 310 311// switch r.Method { 312// case http.MethodGet: 313// keys, err := h.db.GetPublicKeys(did) 314// if err != nil { 315// h.WriteOOBNotice(w, "keys", "Failed to list keys. Try again later.") 316// log.Println(err) 317// return 318// } 319 320// data := make(map[string]interface{}) 321// data["keys"] = keys 322// if err := h.t.ExecuteTemplate(w, "settings/keys", data); err != nil { 323// log.Println(err) 324// return 325// } 326// case http.MethodPut: 327// key := r.FormValue("key") 328// name := r.FormValue("name") 329// client, _ := h.auth.AuthorizedClient(r) 330 331// _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key)) 332// if err != nil { 333// h.WriteOOBNotice(w, "keys", "Invalid public key. Check your formatting and try again.") 334// log.Printf("parsing public key: %s", err) 335// return 336// } 337 338// if err := h.db.AddPublicKey(did, name, key); err != nil { 339// h.WriteOOBNotice(w, "keys", "Failed to add key.") 340// log.Printf("adding public key: %s", err) 341// return 342// } 343 344// // store in pds too 345// resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 346// Collection: "sh.bild.publicKey", 347// Repo: did, 348// Rkey: uuid.New().String(), 349// Record: &lexutil.LexiconTypeDecoder{Val: &shbild.PublicKey{ 350// Created: time.Now().String(), 351// Key: key, 352// Name: name, 353// }}, 354// }) 355 356// // invalid record 357// if err != nil { 358// h.WriteOOBNotice(w, "keys", "Invalid inputs. Check your formatting and try again.") 359// log.Printf("failed to create record: %s", err) 360// return 361// } 362 363// log.Println("created atproto record: ", resp.Uri) 364 365// h.WriteOOBNotice(w, "keys", "Key added!") 366// return 367// } 368// } 369 370// func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) { 371// session, _ := h.s.Get(r, "bild-session") 372// did := session.Values["did"].(string) 373// handle := session.Values["handle"].(string) 374 375// switch r.Method { 376// case http.MethodGet: 377// if err := h.t.ExecuteTemplate(w, "repo/new", nil); err != nil { 378// log.Println(err) 379// return 380// } 381// case http.MethodPut: 382// name := r.FormValue("name") 383// description := r.FormValue("description") 384 385// repoPath := filepath.Join(h.c.Repo.ScanPath, did, name) 386// err := git.InitBare(repoPath) 387// if err != nil { 388// h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") 389// return 390// } 391 392// err = h.db.AddRepo(did, name, description) 393// if err != nil { 394// h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") 395// return 396// } 397 398// w.Header().Set("HX-Redirect", fmt.Sprintf("/@%s/%s", handle, name)) 399// w.WriteHeader(http.StatusOK) 400// } 401// } 402 403// func (h *Handle) Timeline(w http.ResponseWriter, r *http.Request) { 404// session, err := h.s.Get(r, "bild-session") 405// user := make(map[string]string) 406// if err != nil || session.IsNew { 407// // user is not logged in 408// } else { 409// user["handle"] = session.Values["handle"].(string) 410// user["did"] = session.Values["did"].(string) 411// } 412 413// if err := h.t.ExecuteTemplate(w, "timeline", user); err != nil { 414// log.Println(err) 415// return 416// } 417// }