aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/cached_token_source.go86
-rw-r--r--internal/lume/lume.go9
-rw-r--r--internal/saasproxytoken/tokensource.go68
3 files changed, 162 insertions, 1 deletions
diff --git a/internal/cached_token_source.go b/internal/cached_token_source.go
new file mode 100644
index 0000000..22bad75
--- /dev/null
+++ b/internal/cached_token_source.go
@@ -0,0 +1,86 @@
+package internal
+
+import (
+ "context"
+ "encoding/json"
+ "expvar"
+ "fmt"
+ "os"
+ "time"
+
+ "golang.org/x/oauth2"
+)
+
+var (
+ tokenRefreshCount = expvar.NewInt("gauge_xesite_token_refresh_count")
+)
+
+type cachingTokenSource struct {
+ base oauth2.TokenSource
+ filename string
+}
+
+func (c *cachingTokenSource) saveToken(tok *oauth2.Token) error {
+ fout, err := os.Create(c.filename)
+ if err != nil {
+ return fmt.Errorf("error creating %s: %w", c.filename, err)
+ }
+ defer fout.Close()
+
+ return json.NewEncoder(fout).Encode(tok)
+}
+
+func ReadToken(fname string) (*oauth2.Token, error) {
+ fin, err := os.Open(fname)
+ if err != nil {
+ return nil, fmt.Errorf("error opening %s: %w", fname, err)
+ }
+ defer fin.Close()
+
+ var tok oauth2.Token
+ if err := json.NewDecoder(fin).Decode(&tok); err != nil {
+ return nil, fmt.Errorf("error decoding %s: %w", fname, err)
+ }
+
+ return &tok, nil
+}
+
+func FileExists(filename string) bool {
+ _, err := os.Stat(filename)
+ return !os.IsNotExist(err)
+}
+
+func (c *cachingTokenSource) loadToken() (*oauth2.Token, error) {
+ if !FileExists(c.filename) {
+ return nil, nil
+ }
+
+ return ReadToken(c.filename)
+}
+
+func (c *cachingTokenSource) Token() (tok *oauth2.Token, err error) {
+ tok, _ = c.loadToken()
+ if tok != nil && tok.Expiry.Before(time.Now()) {
+ return tok, nil
+ }
+
+ if tok, err = c.base.Token(); err != nil {
+ return nil, err
+ }
+
+ tokenRefreshCount.Add(1)
+
+ if err := c.saveToken(tok); err != nil {
+ return nil, err
+ }
+
+ return tok, err
+}
+
+func CachingTokenSource(filename string, config *oauth2.Config, tok *oauth2.Token) oauth2.TokenSource {
+ orig := config.TokenSource(context.Background(), tok)
+ return oauth2.ReuseTokenSource(nil, &cachingTokenSource{
+ filename: filename,
+ base: orig,
+ })
+}
diff --git a/internal/lume/lume.go b/internal/lume/lume.go
index 676ed2b..b22e7d6 100644
--- a/internal/lume/lume.go
+++ b/internal/lume/lume.go
@@ -118,14 +118,19 @@ type Options struct {
StaticSiteDir string
URL string
PatreonClient *patreon.Client
+ DataDir string
}
func New(ctx context.Context, o *Options) (*FS, error) {
- repoDir, err := os.MkdirTemp("", "lume-repo")
+ repoDir := filepath.Join(o.DataDir, "repo")
+
+ os.RemoveAll(repoDir)
+ err := os.MkdirAll(filepath.Join(o.DataDir, "repo"), 0o755)
if err != nil {
return nil, err
}
+ t0 := time.Now()
repo, err := git.PlainCloneContext(ctx, repoDir, false, &git.CloneOptions{
URL: o.Repo,
ReferenceName: plumbing.NewBranchReferenceName(o.Branch),
@@ -133,6 +138,8 @@ func New(ctx context.Context, o *Options) (*FS, error) {
if err != nil {
return nil, err
}
+ dur := time.Since(t0)
+ slog.Debug("repo cloned", "in", dur.String())
fs := &FS{
repo: repo,
diff --git a/internal/saasproxytoken/tokensource.go b/internal/saasproxytoken/tokensource.go
new file mode 100644
index 0000000..4c3935e
--- /dev/null
+++ b/internal/saasproxytoken/tokensource.go
@@ -0,0 +1,68 @@
+package saasproxytoken
+
+import (
+ "encoding/json"
+ "net/http"
+ "sync"
+ "time"
+
+ "golang.org/x/oauth2"
+ "within.website/x/web"
+)
+
+type remoteTokenSource struct {
+ curr *oauth2.Token
+ lock sync.Mutex
+ remoteURL string
+ httpClient *http.Client
+}
+
+func (r *remoteTokenSource) fetchToken() (*oauth2.Token, error) {
+ resp, err := r.httpClient.Get(r.remoteURL)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, web.NewError(http.StatusOK, resp)
+ }
+
+ var tok oauth2.Token
+ if err := json.NewDecoder(resp.Body).Decode(&tok); err != nil {
+ return nil, err
+ }
+
+ return &tok, nil
+}
+
+func (r *remoteTokenSource) Token() (*oauth2.Token, error) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ if r.curr == nil {
+ tok, err := r.fetchToken()
+ if err != nil {
+ return nil, err
+ }
+ r.curr = tok
+ return tok, nil
+ }
+
+ if r.curr.Expiry.Before(time.Now()) {
+ tok, err := r.fetchToken()
+ if err != nil {
+ return nil, err
+ }
+ r.curr = tok
+ }
+
+ return r.curr, nil
+}
+
+func RemoteTokenSource(remoteURL string, httpClient *http.Client) oauth2.TokenSource {
+ return &remoteTokenSource{
+ remoteURL: remoteURL,
+ httpClient: httpClient,
+ }
+}