aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2020-02-29 16:05:00 -0500
committerGitHub <noreply@github.com>2020-02-29 16:05:00 -0500
commit1da6129332d63ac04767900868b0e1d03219acca (patch)
tree3d0e03d9a986b33a176a5eb5a08825c3a7b676e6 /cmd
parent6adc88b1ff03482030b919b6b7a4bfb03079413f (diff)
downloadxesite-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.go23
-rw-r--r--cmd/site/patreon.go112
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
+}