diff options
| author | Christine Dodrill <me@christine.website> | 2020-02-29 16:05:00 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-29 16:05:00 -0500 |
| commit | 1da6129332d63ac04767900868b0e1d03219acca (patch) | |
| tree | 3d0e03d9a986b33a176a5eb5a08825c3a7b676e6 /cmd | |
| parent | 6adc88b1ff03482030b919b6b7a4bfb03079413f (diff) | |
| download | xesite-1da6129332d63ac04767900868b0e1d03219acca.tar.xz xesite-1da6129332d63ac04767900868b0e1d03219acca.zip | |
Patron page (#122)
* implement /patrons
* bump go to 1.14
* go mod tidy
* bump go in github actions
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/site/main.go | 23 | ||||
| -rw-r--r-- | cmd/site/patreon.go | 112 |
2 files changed, 134 insertions, 1 deletions
diff --git a/cmd/site/main.go b/cmd/site/main.go index da0207d..3a534f0 100644 --- a/cmd/site/main.go +++ b/cmd/site/main.go @@ -13,6 +13,7 @@ import ( "christine.website/cmd/site/internal/middleware" "christine.website/jsonfeed" "github.com/gorilla/feeds" + _ "github.com/joho/godotenv/autoload" "github.com/povilasv/prommod" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -60,6 +61,7 @@ type Site struct { Gallery blog.Posts Resume template.HTML Series []string + patrons []string rssFeed *feeds.Feed jsonFeed *jsonfeed.Feed @@ -84,10 +86,20 @@ func (s *Site) ServeHTTP(w http.ResponseWriter, r *http.Request) { middleware.RequestID(s.xffmw.Handler(ex.HTTPLog(s.mux))).ServeHTTP(w, r) } -var arbDate = time.Date(2020, time.January, 9, 0, 0, 0, 0, time.UTC) +var arbDate = time.Date(2020, time.February, 29, 0, 0, 0, 0, time.UTC) // Build creates a new Site instance or fails. func Build() (*Site, error) { + pc, err := NewPatreonClient() + if err != nil { + return nil, err + } + + pledges, err := GetPledges(pc) + if err != nil { + return nil, err + } + smi := sitemap.New() smi.Add(&sitemap.URL{ Loc: "https://christine.website/resume", @@ -108,6 +120,12 @@ func Build() (*Site, error) { }) smi.Add(&sitemap.URL{ + Loc: "https://christine.website/patrons", + LastMod: &arbDate, + ChangeFreq: sitemap.Weekly, + }) + + smi.Add(&sitemap.URL{ Loc: "https://christine.website/blog", LastMod: &arbDate, ChangeFreq: sitemap.Weekly, @@ -143,6 +161,8 @@ func Build() (*Site, error) { }, mux: http.NewServeMux(), xffmw: xffmw, + + patrons: pledges, } posts, err := blog.LoadPosts("./blog/", "blog") @@ -215,6 +235,7 @@ func Build() (*Site, error) { s.renderTemplatePage("index.html", nil).ServeHTTP(w, r) }) s.mux.Handle("/metrics", promhttp.Handler()) + s.mux.Handle("/patrons", middleware.Metrics("patrons", s.renderTemplatePage("patrons.html", s.patrons))) s.mux.Handle("/resume", middleware.Metrics("resume", s.renderTemplatePage("resume.html", s.Resume))) s.mux.Handle("/blog", middleware.Metrics("blog", s.renderTemplatePage("blogindex.html", s.Posts))) s.mux.Handle("/talks", middleware.Metrics("talks", s.renderTemplatePage("talkindex.html", s.Talks))) diff --git a/cmd/site/patreon.go b/cmd/site/patreon.go new file mode 100644 index 0000000..c8e7dba --- /dev/null +++ b/cmd/site/patreon.go @@ -0,0 +1,112 @@ +package main + +import ( + "context" + "fmt" + "net/http" + "os" + "sort" + "time" + + "github.com/mxpv/patreon-go" + "golang.org/x/oauth2" + "within.website/ln" +) + +func NewPatreonClient() (*patreon.Client, error) { + for _, name := range []string{"CLIENT_ID", "CLIENT_SECRET", "ACCESS_TOKEN", "REFRESH_TOKEN"} { + if os.Getenv("PATREON_"+name) == "" { + return nil, fmt.Errorf("wanted envvar PATREON_%s", name) + } + } + + config := oauth2.Config{ + ClientID: os.Getenv("PATREON_CLIENT_ID"), + ClientSecret: os.Getenv("PATREON_CLIENT_SECRET"), + Endpoint: oauth2.Endpoint{ + AuthURL: patreon.AuthorizationURL, + TokenURL: patreon.AccessTokenURL, + }, + Scopes: []string{"users", "campaigns", "pledges", "pledges-to-me", "my-campaign"}, + } + + token := oauth2.Token{ + AccessToken: os.Getenv("PATREON_ACCESS_TOKEN"), + RefreshToken: os.Getenv("PATREON_REFRESH_TOKEN"), + // Must be non-nil, otherwise token will not be expired + Expiry: time.Now().Add(90 * 24 * time.Hour), + } + + tc := config.Client(context.Background(), &token) + + trans := tc.Transport + tc.Transport = lnLoggingTransport{next: trans} + client := patreon.NewClient(tc) + + return client, nil +} + +func GetPledges(pc *patreon.Client) ([]string, error) { + campaign, err := pc.FetchCampaign() + if err != nil { + return nil, fmt.Errorf("campaign fetch error: %w", err) + } + + campaignID := campaign.Data[0].ID + + cursor := "" + var result []string + + for { + pledgesResponse, err := pc.FetchPledges(campaignID, patreon.WithPageSize(25), patreon.WithCursor(cursor)) + if err != nil { + return nil, err + } + + users := make(map[string]*patreon.User) + for _, item := range pledgesResponse.Included.Items { + u, ok := item.(*patreon.User) + if !ok { + continue + } + + users[u.ID] = u + } + + for _, pledge := range pledgesResponse.Data { + pid := pledge.Relationships.Patron.Data.ID + patronFullName := users[pid].Attributes.FullName + + result = append(result, patronFullName) + } + + cursor = pledgesResponse.Links.Next + if cursor == "" { + break + } + } + + sort.Strings(result) + return result, nil +} + +type lnLoggingTransport struct{ next http.RoundTripper } + +func (l lnLoggingTransport) RoundTrip(r *http.Request) (*http.Response, error) { + ctx := r.Context() + f := ln.F{ + "url": r.URL.String(), + "has_token": r.Header.Get("Authorization") != "", + } + + resp, err := l.next.RoundTrip(r) + if err != nil { + return nil, err + } + + f["status"] = resp.Status + + ln.Log(ctx, f) + + return resp, nil +} |
