package pypihub import ( "encoding/base64" "fmt" "io" "log" "net/http" "strings" "time" "github.com/gorilla/mux" ) type Router struct { config Config client *Client assets []Asset timer *time.Timer } func NewRouter(config Config) *Router { return &Router{ config: config, client: NewClient(config), assets: make([]Asset, 0), } } func (r *Router) refetchAssets() { go r.startAssetsTimer() log.Printf("refetching assets for %d repos", len(r.config.RepoNames)) var err error r.assets, err = r.client.GetAllAssets() if err != nil { log.Println(err) } log.Printf("found %d assets for %d repos", len(r.assets), len(r.config.RepoNames)) } func (r *Router) startAssetsTimer() { if r.timer != nil { r.timer.Stop() } r.timer = time.AfterFunc(5*time.Minute, r.refetchAssets) } func (r *Router) handleSimple(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Simple index") var projects = make(map[string]bool) for _, a := range r.assets { projects[strings.ToLower(a.Repo)] = true } for project := range projects { fmt.Fprintf(w, "%s ", project, project) } fmt.Fprintf(w, "") } func (r *Router) handleSimpleProject(w http.ResponseWriter, req *http.Request) { var vars map[string]string vars = mux.Vars(req) var repo = strings.ToLower(vars["repo"]) fmt.Fprintf(w, "Links for %s", repo) fmt.Fprintf(w, "

Links for all %s

", repo) for _, a := range r.assets { if strings.ToLower(a.Repo) == repo { fmt.Fprintf(w, "%s ", a.URL(), a.Name) } } fmt.Fprintf(w, "") } func (r *Router) handleIndex(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Links for all projects") fmt.Fprintf(w, "

Links for all projects

") for _, a := range r.assets { fmt.Fprintf(w, "%s ", a.URL(), a.Name) } fmt.Fprintf(w, "") } func (r *Router) handleFavicon(w http.ResponseWriter, req *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 (r *Router) handleOwnerIndex(w http.ResponseWriter, req *http.Request) { var vars map[string]string vars = mux.Vars(req) var owner = strings.ToLower(vars["owner"]) fmt.Fprintf(w, "Packages for %s", owner) fmt.Fprintf(w, "

Links for %s projects

", owner) for _, a := range r.assets { if strings.ToLower(a.Owner) == owner { fmt.Fprintf(w, "%s ", a.URL(), a.Name) } } fmt.Fprintf(w, "") } func (r *Router) handleRepoIndex(w http.ResponseWriter, req *http.Request) { var vars map[string]string vars = mux.Vars(req) var owner = strings.ToLower(vars["owner"]) var repo = strings.ToLower(vars["repo"]) fmt.Fprintf(w, "Packages for %s/%s", owner, repo) fmt.Fprintf(w, "

Links for all %s/%s

", owner, repo) for _, a := range r.assets { if strings.ToLower(a.Owner) == owner && strings.ToLower(a.Repo) == repo { fmt.Fprintf(w, "%s ", a.URL(), a.Name) } } fmt.Fprintf(w, "") } func (r *Router) handleFetchAsset(w http.ResponseWriter, req *http.Request) { var vars map[string]string vars = mux.Vars(req) var owner = strings.ToLower(vars["owner"]) var repo = strings.ToLower(vars["repo"]) var asset = vars["asset"] for _, a := range r.assets { if strings.ToLower(a.Owner) == owner && strings.ToLower(a.Repo) == repo && a.Name == asset { var rc io.ReadCloser var err error rc, err = a.Download(r.client) if err != nil { w.WriteHeader(http.StatusNotFound) return } io.Copy(w, rc) return } } w.WriteHeader(http.StatusNotFound) } func (r *Router) logRequests(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { log.Println(req.Method, req.URL.Path) h.ServeHTTP(w, req) }) } func (r *Router) Handler() http.Handler { var h *mux.Router h = mux.NewRouter().StrictSlash(false) // Static favicon h.HandleFunc("/favicon.ico", r.handleFavicon).Methods("GET") // All links, useful for non-owner/repo specific --find-links h.HandleFunc("/", r.handleIndex).Methods("GET") // Simple index h.HandleFunc("/simple", r.handleSimple).Methods("GET") h.HandleFunc("/simple/", r.handleSimple).Methods("GET") h.HandleFunc("/simple/{repo}", r.handleSimpleProject).Methods("GET") h.HandleFunc("/simple/{repo}/", r.handleSimpleProject).Methods("GET") // Owner/repo specific find-links h.HandleFunc("/{owner}", r.handleOwnerIndex).Methods("GET") h.HandleFunc("/{owner}/", r.handleOwnerIndex).Methods("GET") h.HandleFunc("/{owner}/{repo}", r.handleRepoIndex).Methods("GET") h.HandleFunc("/{owner}/{repo}/", r.handleRepoIndex).Methods("GET") // Download asset h.HandleFunc("/{owner}/{repo}/{asset}", r.handleFetchAsset).Methods("GET") return r.logRequests(h) } func (r *Router) Start() error { r.refetchAssets() http.Handle("/", r.Handler()) return http.ListenAndServe(r.config.Bind, nil) }