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