Browse Source

Initial working prototype

Brett Langdon 9 years ago
parent
commit
a9352f4b09
No known key found for this signature in database GPG Key ID: A2ECAB73CE12147F
6 changed files with 294 additions and 1 deletions
  1. +18
    -0
      asset.go
  2. +100
    -0
      client.go
  3. +4
    -0
      cmd/pypihub/main.go
  4. +1
    -1
      config.go
  5. +161
    -0
      server.go
  6. +10
    -0
      utils.go

+ 18
- 0
asset.go View File

@ -0,0 +1,18 @@
package pypihub
import "fmt"
type Asset struct {
ID int
Name string
Owner string
Repo string
}
func (a Asset) String() string {
return a.Name
}
func (a Asset) URL() string {
return fmt.Sprintf("/%s/%s/%d/%s", a.Owner, a.Repo, a.ID, a.Name)
}

+ 100
- 0
client.go View File

@ -0,0 +1,100 @@
package pypihub
import (
"io"
"net/http"
"strings"
"github.com/google/go-github/github"
)
type Client struct {
config Config
client *github.Client
repos []string
}
func NewClient(cfg Config) *Client {
var t = github.BasicAuthTransport{
Username: cfg.Username,
Password: cfg.AccessToken,
}
return &Client{
config: cfg,
client: github.NewClient(t.Client()),
repos: cfg.RepoNames,
}
}
func (c *Client) splitRepoName(r string) (string, string) {
var p = strings.SplitN(r, "/", 2)
switch len(p) {
case 2:
return p[0], p[1]
case 1:
return c.config.Username, p[0]
default:
return "", ""
}
}
func (c *Client) GetRepoAssets(r string) ([]Asset, error) {
var owner, repo string
owner, repo = c.splitRepoName(r)
var releases []*github.RepositoryRelease
var err error
releases, _, err = c.client.Repositories.ListReleases(owner, repo, nil)
if err != nil {
return nil, err
}
var allAssets = make([]Asset, 0)
for _, rel := range releases {
var assets []*github.ReleaseAsset
assets, _, err = c.client.Repositories.ListReleaseAssets(owner, repo, *rel.ID, nil)
if err != nil {
return nil, err
}
for _, a := range assets {
allAssets = append(allAssets, Asset{
ID: *a.ID,
Name: *a.Name,
Owner: owner,
Repo: repo,
})
}
}
return allAssets, nil
}
func (c *Client) GetAllAssets() ([]Asset, error) {
var allAssets = make([]Asset, 0)
for _, r := range c.config.RepoNames {
var repoAssets = make([]Asset, 0)
var err error
repoAssets, err = c.GetRepoAssets(r)
if err != nil {
return nil, err
}
allAssets = append(allAssets, repoAssets...)
}
return allAssets, nil
}
func (c *Client) DownloadAsset(a Asset) (io.ReadCloser, error) {
var rc io.ReadCloser
var redirect string
var err error
rc, redirect, err = c.client.Repositories.DownloadReleaseAsset(a.Owner, a.Repo, a.ID)
if rc == nil {
var resp *http.Response
resp, err = http.Get(redirect)
if err != nil {
return nil, err
}
rc = resp.Body
}
return rc, err
}

+ 4
- 0
cmd/pypihub/main.go View File

@ -10,4 +10,8 @@ func main() {
var config pypihub.Config var config pypihub.Config
config = pypihub.ParseConfig() config = pypihub.ParseConfig()
fmt.Printf("%#v\r\n", config) fmt.Printf("%#v\r\n", config)
var server *pypihub.Server
server = pypihub.NewServer(config)
panic(server.ListenAndServe())
} }

+ 1
- 1
config.go View File

@ -26,7 +26,7 @@ func ParseConfig() Config {
config.RepoNames = append(config.RepoNames, strings.Split(val, " ")...) config.RepoNames = append(config.RepoNames, strings.Split(val, " ")...)
} }
for i := range config.RepoNames { for i := range config.RepoNames {
config.RepoNames[i] = strings.Trim(config.RepoNames[i], " ")
config.RepoNames[i] = strings.TrimSpace(config.RepoNames[i])
} }
config.RepoNames = uniqueSlice(config.RepoNames) config.RepoNames = uniqueSlice(config.RepoNames)


+ 161
- 0
server.go View File

@ -1 +1,162 @@
package pypihub 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, "<html><title>Links for %s</title><meta name=\"api-version\" content=\"2\" /><body>", repo)
fmt.Fprintf(w, "<h1>Links for %s</h1>", repo)
for _, a := range s.assets {
if a.Repo == repo {
fmt.Fprintf(w, "<a href=\"%s\">%s</a> ", a.URL(), a.Name)
}
}
fmt.Fprintf(w, "</body></html>")
}
func (s *Server) listAllAssets(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html")
fmt.Fprintf(w, "<html><title>All asset links</title><meta name=\"api-version\" content=\"2\" /><body>")
fmt.Fprintf(w, "<h1>All asset links</h1>")
for _, a := range s.assets {
fmt.Fprintf(w, "<a href=\"%s\">%s</a> ", a.URL(), a.Name)
}
fmt.Fprintf(w, "</body></html>")
}
func (s *Server) listRepos(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html")
fmt.Fprintf(w, "<html><title>Simple index</title><meta name=\"api-version\" content=\"2\" /><body>")
for _, r := range s.config.RepoNames {
var parts = strings.SplitN(r, "/", 2)
fmt.Fprintf(w, "<a href=\"/%s\">%s</a> ", parts[1], parts[1])
}
fmt.Fprintf(w, "</body></html>")
}
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)
}

+ 10
- 0
utils.go View File

@ -12,3 +12,13 @@ func uniqueSlice(s []string) []string {
} }
return o return o
} }
func removeEmpty(s []string) []string {
var n = make([]string, 0)
for _, c := range s {
if c != "" {
n = append(n, c)
}
}
return n
}

Loading…
Cancel
Save