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 := chi.URLParam(r, "name") 159 file := chi.URLParam(r, "file") 160 161 // TODO: extend this to add more files compression (e.g.: xz) 162 if !strings.HasSuffix(file, ".tar.gz") { 163 notFound(w) 164 return 165 } 166 167 ref := strings.TrimSuffix(file, ".tar.gz") 168 169 // This allows the browser to use a proper name for the file when 170 // downloading 171 filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) 172 setContentDisposition(w, filename) 173 setGZipMIME(w) 174 175 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 176 gr, err := git.Open(path, ref) 177 if err != nil { 178 notFound(w) 179 return 180 } 181 182 gw := gzip.NewWriter(w) 183 defer gw.Close() 184 185 prefix := fmt.Sprintf("%s-%s", name, ref) 186 err = gr.WriteTar(gw, prefix) 187 if err != nil { 188 // once we start writing to the body we can't report error anymore 189 // so we are only left with printing the error. 190 log.Println(err) 191 return 192 } 193 194 err = gw.Flush() 195 if err != nil { 196 // once we start writing to the body we can't report error anymore 197 // so we are only left with printing the error. 198 log.Println(err) 199 return 200 } 201} 202 203func (h *Handle) Log(w http.ResponseWriter, r *http.Request) { 204 fmt.Println(r.URL.Path) 205 ref := chi.URLParam(r, "ref") 206 207 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 208 gr, err := git.Open(path, ref) 209 if err != nil { 210 notFound(w) 211 return 212 } 213 214 commits, err := gr.Commits() 215 if err != nil { 216 writeError(w, err.Error(), http.StatusInternalServerError) 217 log.Println(err) 218 return 219 } 220 221 data := make(map[string]interface{}) 222 data["commits"] = commits 223 data["meta"] = h.c.Meta 224 data["ref"] = ref 225 data["desc"] = getDescription(path) 226 data["log"] = true 227 228 writeJSON(w, data) 229 return 230} 231 232func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) { 233 ref := chi.URLParam(r, "ref") 234 235 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 236 gr, err := git.Open(path, ref) 237 if err != nil { 238 notFound(w) 239 return 240 } 241 242 diff, err := gr.Diff() 243 if err != nil { 244 writeError(w, err.Error(), http.StatusInternalServerError) 245 log.Println(err) 246 return 247 } 248 249 data := make(map[string]interface{}) 250 251 data["commit"] = diff.Commit 252 data["stat"] = diff.Stat 253 data["diff"] = diff.Diff 254 data["meta"] = h.c.Meta 255 data["ref"] = ref 256 data["desc"] = getDescription(path) 257 258 writeJSON(w, data) 259 return 260} 261 262func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) { 263 path := filepath.Join(h.c.Repo.ScanPath, didPath(r)) 264 gr, err := git.Open(path, "") 265 if err != nil { 266 notFound(w) 267 return 268 } 269 270 tags, err := gr.Tags() 271 if err != nil { 272 // Non-fatal, we *should* have at least one branch to show. 273 log.Println(err) 274 } 275 276 branches, err := gr.Branches() 277 if err != nil { 278 log.Println(err) 279 writeError(w, err.Error(), http.StatusInternalServerError) 280 return 281 } 282 283 data := make(map[string]interface{}) 284 285 data["meta"] = h.c.Meta 286 data["branches"] = branches 287 data["tags"] = tags 288 data["desc"] = getDescription(path) 289 290 writeJSON(w, data) 291 return 292} 293 294func (h *Handle) ServeStatic(w http.ResponseWriter, r *http.Request) { 295 f := chi.URLParam(r, "file") 296 f = filepath.Clean(filepath.Join(h.c.Dirs.Static, f)) 297 298 http.ServeFile(w, r, f) 299} 300 301// func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) { 302// session, _ := h.s.Get(r, "bild-session") 303// did := session.Values["did"].(string) 304 305// switch r.Method { 306// case http.MethodGet: 307// keys, err := h.db.GetPublicKeys(did) 308// if err != nil { 309// h.WriteOOBNotice(w, "keys", "Failed to list keys. Try again later.") 310// log.Println(err) 311// return 312// } 313 314// data := make(map[string]interface{}) 315// data["keys"] = keys 316// if err := h.t.ExecuteTemplate(w, "settings/keys", data); err != nil { 317// log.Println(err) 318// return 319// } 320// case http.MethodPut: 321// key := r.FormValue("key") 322// name := r.FormValue("name") 323// client, _ := h.auth.AuthorizedClient(r) 324 325// _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key)) 326// if err != nil { 327// h.WriteOOBNotice(w, "keys", "Invalid public key. Check your formatting and try again.") 328// log.Printf("parsing public key: %s", err) 329// return 330// } 331 332// if err := h.db.AddPublicKey(did, name, key); err != nil { 333// h.WriteOOBNotice(w, "keys", "Failed to add key.") 334// log.Printf("adding public key: %s", err) 335// return 336// } 337 338// // store in pds too 339// resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 340// Collection: "sh.bild.publicKey", 341// Repo: did, 342// Rkey: uuid.New().String(), 343// Record: &lexutil.LexiconTypeDecoder{Val: &shbild.PublicKey{ 344// Created: time.Now().String(), 345// Key: key, 346// Name: name, 347// }}, 348// }) 349 350// // invalid record 351// if err != nil { 352// h.WriteOOBNotice(w, "keys", "Invalid inputs. Check your formatting and try again.") 353// log.Printf("failed to create record: %s", err) 354// return 355// } 356 357// log.Println("created atproto record: ", resp.Uri) 358 359// h.WriteOOBNotice(w, "keys", "Key added!") 360// return 361// } 362// } 363 364// func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) { 365// session, _ := h.s.Get(r, "bild-session") 366// did := session.Values["did"].(string) 367// handle := session.Values["handle"].(string) 368 369// switch r.Method { 370// case http.MethodGet: 371// if err := h.t.ExecuteTemplate(w, "repo/new", nil); err != nil { 372// log.Println(err) 373// return 374// } 375// case http.MethodPut: 376// name := r.FormValue("name") 377// description := r.FormValue("description") 378 379// repoPath := filepath.Join(h.c.Repo.ScanPath, did, name) 380// err := git.InitBare(repoPath) 381// if err != nil { 382// h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") 383// return 384// } 385 386// err = h.db.AddRepo(did, name, description) 387// if err != nil { 388// h.WriteOOBNotice(w, "repo", "Error creating repo. Try again later.") 389// return 390// } 391 392// w.Header().Set("HX-Redirect", fmt.Sprintf("/@%s/%s", handle, name)) 393// w.WriteHeader(http.StatusOK) 394// } 395// } 396 397// func (h *Handle) Timeline(w http.ResponseWriter, r *http.Request) { 398// session, err := h.s.Get(r, "bild-session") 399// user := make(map[string]string) 400// if err != nil || session.IsNew { 401// // user is not logged in 402// } else { 403// user["handle"] = session.Values["handle"].(string) 404// user["did"] = session.Values["did"].(string) 405// } 406 407// if err := h.t.ExecuteTemplate(w, "timeline", user); err != nil { 408// log.Println(err) 409// return 410// } 411// }