summaryrefslogtreecommitdiff
path: root/internal/store/oauth_tokens.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store/oauth_tokens.go')
-rw-r--r--internal/store/oauth_tokens.go48
1 files changed, 47 insertions, 1 deletions
diff --git a/internal/store/oauth_tokens.go b/internal/store/oauth_tokens.go
index 12df515..d216269 100644
--- a/internal/store/oauth_tokens.go
+++ b/internal/store/oauth_tokens.go
@@ -50,11 +50,57 @@ func (s *Store) SaveOAuthToken(source string, tok *oauth2.Token) error {
refresh_token = excluded.refresh_token,
token_type = excluded.token_type,
expiry = excluded.expiry,
- updated_at = CURRENT_TIMESTAMP
+ updated_at = CURRENT_TIMESTAMP,
+ -- A successful save means the grant works again; drop any recorded
+ -- failure so settings stops telling the user to reconnect.
+ last_error = NULL,
+ last_error_at = NULL
`, source, tok.AccessToken, tok.RefreshToken, tok.TokenType, expiry)
return err
}
+// RecordOAuthTokenError stores why a token stopped working.
+//
+// A stored row used to mean "connected" on its own, so a dead refresh token
+// showed as connected while every background fetch failed. Persisting the
+// reason is what lets settings say "reconnect" instead.
+func (s *Store) RecordOAuthTokenError(source string, message string) error {
+ _, err := s.db.Exec(`
+ UPDATE oauth_tokens
+ SET last_error = ?, last_error_at = CURRENT_TIMESTAMP
+ WHERE source = ?
+ `, message, source)
+ return err
+}
+
+// OAuthTokenStatus is a stored grant plus whether it is currently working.
+type OAuthTokenStatus struct {
+ Connected bool
+ LastError string
+ LastErrorAt *time.Time
+}
+
+// GetOAuthTokenStatus reports whether a source is usable, not merely present.
+func (s *Store) GetOAuthTokenStatus(source string) (OAuthTokenStatus, error) {
+ var lastError sql.NullString
+ var lastErrorAt sql.NullTime
+ err := s.db.QueryRow(
+ `SELECT COALESCE(last_error, ''), last_error_at FROM oauth_tokens WHERE source = ?`,
+ source).Scan(&lastError, &lastErrorAt)
+ if err == sql.ErrNoRows {
+ return OAuthTokenStatus{}, nil
+ }
+ if err != nil {
+ return OAuthTokenStatus{}, err
+ }
+ st := OAuthTokenStatus{Connected: true, LastError: lastError.String}
+ if lastErrorAt.Valid {
+ t := lastErrorAt.Time
+ st.LastErrorAt = &t
+ }
+ return st, nil
+}
+
// DeleteOAuthToken removes a source's stored token (disconnect).
func (s *Store) DeleteOAuthToken(source string) error {
_, err := s.db.Exec(`DELETE FROM oauth_tokens WHERE source = ?`, source)