package pypihub import ( "encoding/base64" "fmt" "io" "net/http" "strconv" "strings" "time" ) type Server struct { client *Client config Config assets []Asset timer *time.Timer } func NewServer(cfg Config) *Server { return &Server{ client: NewClient(cfg), config: cfg, assets: make([]Asset, 0), } } func (s *Server) findAsset(owner string, repo string, id int) *Asset { for _, a := range s.assets { if a.Owner == owner && a.Repo == repo && a.ID == id { return &a } } return nil } func (s *Server) refetchAssets() { var err error s.assets, err = s.client.GetAllAssets() if err != nil { fmt.Println(err) } } func (s *Server) startTimer() { if s.timer != nil { s.timer.Stop() } s.timer = time.AfterFunc(time.Duration(5*time.Minute), func() { go s.refetchAssets() }) } func (s *Server) listAssets(w http.ResponseWriter, r *http.Request) { var repo = strings.Trim(r.URL.Path, "/") w.Header().Add("Content-Type", "text/html") fmt.Fprintf(w, "Links for %s", repo) fmt.Fprintf(w, "

Links for %s

", repo) for _, a := range s.assets { if a.Repo == repo { fmt.Fprintf(w, "%s ", a.URL(), a.Name) } } fmt.Fprintf(w, "") } func (s *Server) listAllAssets(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/html") fmt.Fprintf(w, "All asset links") fmt.Fprintf(w, "

All asset links

") for _, a := range s.assets { fmt.Fprintf(w, "%s ", a.URL(), a.Name) } fmt.Fprintf(w, "") } func (s *Server) listRepos(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/html") fmt.Fprintf(w, "Simple index") for _, r := range s.config.RepoNames { var parts = strings.SplitN(r, "/", 2) fmt.Fprintf(w, "%s ", parts[1], parts[1]) } fmt.Fprintf(w, "") } func (s *Server) fetchAsset(w http.ResponseWriter, r *http.Request) { var url = strings.Trim(r.URL.Path, "/") var parts = strings.SplitN(url, "/", 4) if len(parts) != 4 { w.WriteHeader(http.StatusNotFound) return } var asset *Asset var id int64 var err error id, err = strconv.ParseInt(parts[2], 10, 32) if err != nil { w.WriteHeader(http.StatusNotFound) return } asset = s.findAsset(parts[0], parts[1], int(id)) if asset == nil { w.WriteHeader(http.StatusNotFound) return } var rc io.ReadCloser rc, err = s.client.DownloadAsset(*asset) if err != nil || rc == nil { w.WriteHeader(http.StatusNotFound) return } defer rc.Close() io.Copy(w, rc) } func (s *Server) serveFavicon(w http.ResponseWriter, r *http.Request) { var decoded []byte var err error decoded, err = base64.StdEncoding.DecodeString("AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAPs7/AJ9vNwBO3f8APMz/AEzb/wCdbjYAStn/AJZqNgBI1/8ARtX/AETT/wBA0f8AqnY3AD7P/wCjcjcApHI3ADzN/wBQ3v8AOsv/AE7c/wA4yf8Amm02ALF6OACYazYARtb/AETU/wCveTcAQtL/AKh1NwCtdzcAQND/AKFxNwCmczcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcDA4RExUAAAAAAAAAAAAZGhwfDhEAFQAAAAAAAAAACRkaHB8OABMAAAAAAAAAAAcJGRocHw4RAAAAAAAgAgYFBwkZGhwfARETFQAhDyACAwUHCQoaHB8BERMVHSEPIBIUBQcJChocHwEREw0dIQ8AEhQFBwkKGhwfAQQeDR0hDyACBhYYCAALHB8BGx4NHSEPIAIGFhgICgscHxcbHg0dIRAgAgYWGAkKCxwAFxseDR0hECACBhYHCQoAAAAAAB4NHSEQIAIGAAAAAAAAAAAbAA0dIRAgAgAAAAAAAAAAFwAeDR0hECAAAAAAAAAAAAAXGx4NHSEAAAAAAPgfAADwLwAA8C8AAPAPAACAAQAAAAAAAAAAAAAIAAAAABAAAAAAAAAAAAAAgAEAAPAPAAD0DwAA9A8AAPgfAAA=") if err != nil { w.WriteHeader(http.StatusNotFound) return } fmt.Fprintf(w, "%s", decoded) } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { var parts = strings.Split(strings.Trim(r.URL.Path, "/"), "/") parts = removeEmpty(parts) fmt.Println(r.URL.Path) switch len(parts) { case 0: s.listAllAssets(w, r) case 1: if parts[0] == "simple" { s.listRepos(w, r) } else if parts[0] == "favicon.ico" { s.serveFavicon(w, r) } else { s.listAssets(w, r) } default: s.fetchAsset(w, r) } } func (s *Server) ListenAndServe() error { s.refetchAssets() s.startTimer() http.Handle("/", s) fmt.Println("Server listening at", s.config.Bind) return http.ListenAndServe(s.config.Bind, nil) }