Compare commits

..
4 Commits
Author SHA1 Message Date
John O'Keefe 336a1f353f chore(release): bump version to 1.6.8
Release / release (push) Canceled after 20s
2026-09-12 21:27:12 -04:00
John O'Keefe a19080ef39 Revert "docs: add Wails v3 + Android migration plan"
The migration plan belongs to the wailsv3 branch, not main. It was merged forward into wailsv3 (now at origin/wailsv3) before this revert, so no content is lost. Main keeps only the v2 keyring error-handling fix.
2026-09-12 21:26:25 -04:00
John O'Keefe 2bddc6a224 docs: add Wails v3 + Android migration plan
Skimmable phased plan for getting AniTrack onto Android for personal
sideload use (no Play Store, no official F-Droid). Written for both the
maintainer and as working context for AI-assisted implementation.

Covers: desktop v3 parity as a mechanical step (main.go, runtime call
mapping, wailsjs import rewrite), per-file Android storage shims
(keyring on desktop vs EncryptedSharedPreferences via
Android.Secure*), the localhost :6734 to anitrack:// deep-link OAuth
redesign with per-provider dashboard changes, and the sideload run/
package commands plus Obtainium updates. Includes guardrails: small
diffs, per-service file ownership, build-tagged platform files only,
runnable check per phase, and open decisions (first provider, desktop
parallel builds, redirect URI registration).
2026-09-12 21:23:14 -04:00
John O'Keefe 3bf5d8290e fix(auth): handle keyring open/set errors per service
Stop ignoring keyring.Open failures in all three login files. A failed
Open previously left a nil ring behind the blank identifier, so the next
Get/Set panicked with no message. Each service now opens its ring in
init, logs a service-prefixed warning when storage is unavailable, and
guards every use with a Ready check that fails safe to logged-out.

- AniListUserFunctions.go: add aniRingReady/aniRingSet helpers, log
  Open and per-key Set failures, reject invalid ExpiresIn instead of
  silently storing 0, clear in-memory JWT on logout even when storage
  is missing
- MALUserFunctions.go: add malRingReady/malRingSet helpers covering
  login, OAuth callback, and token-refresh saves; same Open/Set/
  ExpiresIn/logout treatment
- SimklUserFunctions.go: add simklRingReady/simklRingSet helpers;
  same Open/Set/logout treatment

No wallet, key names, or login flow changed. Same ServiceName
AniTrack, same keys, same OAuth callback behavior.
2026-09-12 21:22:58 -04:00
4 changed files with 168 additions and 89 deletions
+61 -26
View File
@@ -19,18 +19,48 @@ import (
var aniListJwt AniListJWT var aniListJwt AniListJWT
var aniRing, _ = keyring.Open(keyring.Config{ var aniRing keyring.Keyring
ServiceName: "AniTrack",
KeychainName: "AniTrack", func init() {
KeychainSynchronizable: false, var err error
KeychainTrustApplication: true, aniRing, err = keyring.Open(keyring.Config{
KeychainAccessibleWhenUnlocked: true, ServiceName: "AniTrack",
}) KeychainName: "AniTrack",
KeychainSynchronizable: false,
KeychainTrustApplication: true,
KeychainAccessibleWhenUnlocked: true,
})
if err != nil {
log.Printf("anilist: secure storage unavailable: %s", err)
}
}
func aniRingReady() bool {
if aniRing == nil {
log.Println("anilist: secure storage unavailable")
return false
}
return true
}
func aniRingSet(key string, data []byte) error {
if !aniRingReady() {
return errors.New("anilist: secure storage unavailable")
}
if err := aniRing.Set(keyring.Item{Key: key, Data: data}); err != nil {
log.Printf("anilist: save %s failed: %s", key, err)
return err
}
return nil
}
var aniCtxShutdown, aniCancel = context.WithCancel(context.Background()) var aniCtxShutdown, aniCancel = context.WithCancel(context.Background())
func (a *App) CheckIfAniListLoggedIn() bool { func (a *App) CheckIfAniListLoggedIn() bool {
if (AniListJWT{} == aniListJwt) { if (AniListJWT{} == aniListJwt) {
if !aniRingReady() {
return false
}
tokenType, tokenErr := aniRing.Get("anilistTokenType") tokenType, tokenErr := aniRing.Get("anilistTokenType")
expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn") expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn")
refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken") refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken")
@@ -38,10 +68,15 @@ func (a *App) CheckIfAniListLoggedIn() bool {
if (tokenErr != nil || expiresInErr != nil || refreshTokenErr != nil || accessTokenErr != nil) || len(accessToken.Data) == 0 { if (tokenErr != nil || expiresInErr != nil || refreshTokenErr != nil || accessTokenErr != nil) || len(accessToken.Data) == 0 {
return false return false
} else { } else {
var expiresInConvertErr error
aniListJwt.TokenType = string(tokenType.Data) aniListJwt.TokenType = string(tokenType.Data)
aniListJwt.AccessToken = string(accessToken.Data) aniListJwt.AccessToken = string(accessToken.Data)
aniListJwt.RefreshToken = string(refreshToken.Data) aniListJwt.RefreshToken = string(refreshToken.Data)
aniListJwt.ExpiresIn, _ = strconv.Atoi(string(expiresIn.Data)) aniListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data))
if expiresInConvertErr != nil {
log.Printf("anilist: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr)
return false
}
return true return true
} }
} else { } else {
@@ -51,6 +86,10 @@ func (a *App) CheckIfAniListLoggedIn() bool {
func (a *App) AniListLogin() { func (a *App) AniListLogin() {
if (AniListJWT{} == aniListJwt) { if (AniListJWT{} == aniListJwt) {
if !aniRingReady() {
log.Println("anilist: cannot check login, secure storage unavailable")
return
}
tokenType, tokenErr := aniRing.Get("anilistTokenType") tokenType, tokenErr := aniRing.Get("anilistTokenType")
expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn") expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn")
refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken") refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken")
@@ -64,10 +103,14 @@ func (a *App) AniListLogin() {
a.handleAniListCallback(serverDone) a.handleAniListCallback(serverDone)
serverDone.Wait() serverDone.Wait()
} else { } else {
var expiresInConvertErr error
aniListJwt.TokenType = string(tokenType.Data) aniListJwt.TokenType = string(tokenType.Data)
aniListJwt.AccessToken = string(accessToken.Data) aniListJwt.AccessToken = string(accessToken.Data)
aniListJwt.RefreshToken = string(refreshToken.Data) aniListJwt.RefreshToken = string(refreshToken.Data)
aniListJwt.ExpiresIn, _ = strconv.Atoi(string(expiresIn.Data)) aniListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data))
if expiresInConvertErr != nil {
log.Printf("anilist: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr)
}
} }
} }
} }
@@ -85,22 +128,10 @@ func (a *App) handleAniListCallback(wg *sync.WaitGroup) {
content := r.FormValue("code") content := r.FormValue("code")
if content != "" { if content != "" {
aniListJwt = getAniListAuthorizationToken(content) aniListJwt = getAniListAuthorizationToken(content)
_ = aniRing.Set(keyring.Item{ _ = aniRingSet("anilistTokenType", []byte(aniListJwt.TokenType))
Key: "anilistTokenType", _ = aniRingSet("anilistTokenExpiresIn", []byte(strconv.Itoa(aniListJwt.ExpiresIn)))
Data: []byte(aniListJwt.TokenType), _ = aniRingSet("anilistAccessToken", []byte(aniListJwt.AccessToken))
}) _ = aniRingSet("anilistRefreshToken", []byte(aniListJwt.RefreshToken))
_ = aniRing.Set(keyring.Item{
Key: "anilistTokenExpiresIn",
Data: []byte(strconv.Itoa(aniListJwt.ExpiresIn)),
})
_ = aniRing.Set(keyring.Item{
Key: "anilistAccessToken",
Data: []byte(aniListJwt.AccessToken),
})
_ = aniRing.Set(keyring.Item{
Key: "anilistRefreshToken",
Data: []byte(aniListJwt.RefreshToken),
})
_, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ _, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{
Title: "AniList Authorization", Title: "AniList Authorization",
Message: "It is now safe to close your browser tab", Message: "It is now safe to close your browser tab",
@@ -208,12 +239,16 @@ func (a *App) GetAniListLoggedInUser() AniListUser {
func (a *App) LogoutAniList() string { func (a *App) LogoutAniList() string {
if (AniListJWT{} != aniListJwt) { if (AniListJWT{} != aniListJwt) {
if !aniRingReady() {
aniListJwt = AniListJWT{}
return "AniList Logged Out Successfully"
}
typeErr := aniRing.Remove("anilistTokenType") typeErr := aniRing.Remove("anilistTokenType")
expiresInErr := aniRing.Remove("anilistTokenExpiresIn") expiresInErr := aniRing.Remove("anilistTokenExpiresIn")
accessTokenErr := aniRing.Remove("anilistAccessToken") accessTokenErr := aniRing.Remove("anilistAccessToken")
refreshTokenErr := aniRing.Remove("anilistRefreshToken") refreshTokenErr := aniRing.Remove("anilistRefreshToken")
if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil { if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil {
fmt.Println("AniList Logout Failed") log.Printf("anilist: logout cleanup failed (type=%v expires=%v access=%v refresh=%v)", typeErr, expiresInErr, accessTokenErr, refreshTokenErr)
} }
aniListJwt = AniListJWT{} aniListJwt = AniListJWT{}
} }
+57 -42
View File
@@ -23,13 +23,40 @@ import (
var myAnimeListJwt MyAnimeListJWT var myAnimeListJwt MyAnimeListJWT
var myAnimeListRing, _ = keyring.Open(keyring.Config{ var myAnimeListRing keyring.Keyring
ServiceName: "AniTrack",
KeychainName: "AniTrack", func init() {
KeychainSynchronizable: false, var err error
KeychainTrustApplication: true, myAnimeListRing, err = keyring.Open(keyring.Config{
KeychainAccessibleWhenUnlocked: true, ServiceName: "AniTrack",
}) KeychainName: "AniTrack",
KeychainSynchronizable: false,
KeychainTrustApplication: true,
KeychainAccessibleWhenUnlocked: true,
})
if err != nil {
log.Printf("mal: secure storage unavailable: %s", err)
}
}
func malRingReady() bool {
if myAnimeListRing == nil {
log.Println("mal: secure storage unavailable")
return false
}
return true
}
func malRingSet(key string, data []byte) error {
if !malRingReady() {
return errors.New("mal: secure storage unavailable")
}
if err := myAnimeListRing.Set(keyring.Item{Key: key, Data: data}); err != nil {
log.Printf("mal: save %s failed: %s", key, err)
return err
}
return nil
}
var myAnimeListCtxShutdown, myAnimeListCancel = context.WithCancel(context.Background()) var myAnimeListCtxShutdown, myAnimeListCancel = context.WithCancel(context.Background())
@@ -72,6 +99,9 @@ func (v *CodeVerifier) CodeChallengeS256() string {
func (a *App) CheckIfMyAnimeListLoggedIn() bool { func (a *App) CheckIfMyAnimeListLoggedIn() bool {
if (MyAnimeListJWT{} == myAnimeListJwt) { if (MyAnimeListJWT{} == myAnimeListJwt) {
if !malRingReady() {
return false
}
tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType") tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType")
expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn") expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn")
accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken") accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken")
@@ -83,7 +113,8 @@ func (a *App) CheckIfMyAnimeListLoggedIn() bool {
myAnimeListJwt.TokenType = string(tokenType.Data) myAnimeListJwt.TokenType = string(tokenType.Data)
myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data)) myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data))
if expiresInConvertErr != nil { if expiresInConvertErr != nil {
fmt.Println("unable to convert string to int") log.Printf("mal: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr)
return false
} }
myAnimeListJwt.AccessToken = string(accessToken.Data) myAnimeListJwt.AccessToken = string(accessToken.Data)
myAnimeListJwt.RefreshToken = string(refreshToken.Data) myAnimeListJwt.RefreshToken = string(refreshToken.Data)
@@ -97,6 +128,10 @@ func (a *App) CheckIfMyAnimeListLoggedIn() bool {
func (a *App) MyAnimeListLogin() { func (a *App) MyAnimeListLogin() {
if !a.CheckIfMyAnimeListLoggedIn() { if !a.CheckIfMyAnimeListLoggedIn() {
fmt.Println("check logged in function failed") fmt.Println("check logged in function failed")
if !malRingReady() {
log.Println("mal: cannot check login, secure storage unavailable")
return
}
tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType") tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType")
expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn") expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn")
accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken") accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken")
@@ -114,7 +149,7 @@ func (a *App) MyAnimeListLogin() {
myAnimeListJwt.TokenType = string(tokenType.Data) myAnimeListJwt.TokenType = string(tokenType.Data)
myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data)) myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data))
if expiresInConvertErr != nil { if expiresInConvertErr != nil {
fmt.Println("unable to convert string to int in Login function") log.Printf("mal: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr)
} }
myAnimeListJwt.AccessToken = string(accessToken.Data) myAnimeListJwt.AccessToken = string(accessToken.Data)
myAnimeListJwt.RefreshToken = string(refreshToken.Data) myAnimeListJwt.RefreshToken = string(refreshToken.Data)
@@ -136,22 +171,10 @@ func (a *App) handleMyAnimeListCallback(wg *sync.WaitGroup, verifier *CodeVerifi
if content != "" { if content != "" {
myAnimeListJwt = getMyAnimeListAuthorizationToken(content, verifier) myAnimeListJwt = getMyAnimeListAuthorizationToken(content, verifier)
_ = myAnimeListRing.Set(keyring.Item{ _ = malRingSet("MyAnimeListTokenType", []byte(myAnimeListJwt.TokenType))
Key: "MyAnimeListTokenType", _ = malRingSet("MyAnimeListExpiresIn", []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn)))
Data: []byte(myAnimeListJwt.TokenType), _ = malRingSet("MyAnimeListAccessToken", []byte(myAnimeListJwt.AccessToken))
}) _ = malRingSet("MyAnimeListRefreshToken", []byte(myAnimeListJwt.RefreshToken))
_ = myAnimeListRing.Set(keyring.Item{
Key: "MyAnimeListExpiresIn",
Data: []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn)),
})
_ = myAnimeListRing.Set(keyring.Item{
Key: "MyAnimeListAccessToken",
Data: []byte(myAnimeListJwt.AccessToken),
})
_ = myAnimeListRing.Set(keyring.Item{
Key: "MyAnimeListRefreshToken",
Data: []byte(myAnimeListJwt.RefreshToken),
})
_, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ _, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{
Title: "MyAnimeList Authorization", Title: "MyAnimeList Authorization",
Message: "It is now safe to close your browser tab", Message: "It is now safe to close your browser tab",
@@ -292,22 +315,10 @@ func refreshMyAnimeListAuthorizationToken() bool {
myAnimeListJwt = refreshed myAnimeListJwt = refreshed
_ = myAnimeListRing.Set(keyring.Item{ _ = malRingSet("MyAnimeListTokenType", []byte(myAnimeListJwt.TokenType))
Key: "MyAnimeListTokenType", _ = malRingSet("MyAnimeListExpiresIn", []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn)))
Data: []byte(myAnimeListJwt.TokenType), _ = malRingSet("MyAnimeListAccessToken", []byte(myAnimeListJwt.AccessToken))
}) _ = malRingSet("MyAnimeListRefreshToken", []byte(myAnimeListJwt.RefreshToken))
_ = myAnimeListRing.Set(keyring.Item{
Key: "MyAnimeListExpiresIn",
Data: []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn)),
})
_ = myAnimeListRing.Set(keyring.Item{
Key: "MyAnimeListAccessToken",
Data: []byte(myAnimeListJwt.AccessToken),
})
_ = myAnimeListRing.Set(keyring.Item{
Key: "MyAnimeListRefreshToken",
Data: []byte(myAnimeListJwt.RefreshToken),
})
return true return true
} }
@@ -367,12 +378,16 @@ func createUser() MyAnimeListUser {
func (a *App) LogoutMyAnimeList() string { func (a *App) LogoutMyAnimeList() string {
if (MyAnimeListJWT{} != myAnimeListJwt) { if (MyAnimeListJWT{} != myAnimeListJwt) {
if !malRingReady() {
myAnimeListJwt = MyAnimeListJWT{}
return "MAL Logged Out Successfully"
}
typeErr := myAnimeListRing.Remove("MyAnimeListTokenType") typeErr := myAnimeListRing.Remove("MyAnimeListTokenType")
expiresInErr := myAnimeListRing.Remove("MyAnimeListExpiresIn") expiresInErr := myAnimeListRing.Remove("MyAnimeListExpiresIn")
accessTokenErr := myAnimeListRing.Remove("MyAnimeListAccessToken") accessTokenErr := myAnimeListRing.Remove("MyAnimeListAccessToken")
refreshTokenErr := myAnimeListRing.Remove("MyAnimeListRefreshToken") refreshTokenErr := myAnimeListRing.Remove("MyAnimeListRefreshToken")
if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil { if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil {
fmt.Println("MAL Logout Failed") log.Printf("mal: logout cleanup failed (type=%v expires=%v access=%v refresh=%v)", typeErr, expiresInErr, accessTokenErr, refreshTokenErr)
} }
myAnimeListJwt = MyAnimeListJWT{} myAnimeListJwt = MyAnimeListJWT{}
} }
+49 -20
View File
@@ -17,18 +17,48 @@ import (
var simklJwt SimklJWT var simklJwt SimklJWT
var simklRing, _ = keyring.Open(keyring.Config{ var simklRing keyring.Keyring
ServiceName: "AniTrack",
KeychainName: "AniTrack", func init() {
KeychainSynchronizable: false, var err error
KeychainTrustApplication: true, simklRing, err = keyring.Open(keyring.Config{
KeychainAccessibleWhenUnlocked: true, ServiceName: "AniTrack",
}) KeychainName: "AniTrack",
KeychainSynchronizable: false,
KeychainTrustApplication: true,
KeychainAccessibleWhenUnlocked: true,
})
if err != nil {
log.Printf("simkl: secure storage unavailable: %s", err)
}
}
func simklRingReady() bool {
if simklRing == nil {
log.Println("simkl: secure storage unavailable")
return false
}
return true
}
func simklRingSet(key string, data []byte) error {
if !simklRingReady() {
return errors.New("simkl: secure storage unavailable")
}
if err := simklRing.Set(keyring.Item{Key: key, Data: data}); err != nil {
log.Printf("simkl: save %s failed: %s", key, err)
return err
}
return nil
}
var simklCtxShutdown, simklCancel = context.WithCancel(context.Background()) var simklCtxShutdown, simklCancel = context.WithCancel(context.Background())
func (a *App) CheckIfSimklLoggedIn() bool { func (a *App) CheckIfSimklLoggedIn() bool {
if (SimklJWT{} == simklJwt) { if (SimklJWT{} == simklJwt) {
if !simklRingReady() {
return false
}
tokenType, tokenTypeErr := simklRing.Get("SimklTokenType") tokenType, tokenTypeErr := simklRing.Get("SimklTokenType")
accessToken, accessTokenErr := simklRing.Get("SimklAccessToken") accessToken, accessTokenErr := simklRing.Get("SimklAccessToken")
scope, scopeErr := simklRing.Get("SimklScope") scope, scopeErr := simklRing.Get("SimklScope")
@@ -47,6 +77,10 @@ func (a *App) CheckIfSimklLoggedIn() bool {
func (a *App) SimklLogin() { func (a *App) SimklLogin() {
if !a.CheckIfSimklLoggedIn() { if !a.CheckIfSimklLoggedIn() {
if !simklRingReady() {
log.Println("simkl: cannot check login, secure storage unavailable")
return
}
tokenType, tokenTypeErr := simklRing.Get("SimklTokenType") tokenType, tokenTypeErr := simklRing.Get("SimklTokenType")
accessToken, accessTokenErr := simklRing.Get("SimklAccessToken") accessToken, accessTokenErr := simklRing.Get("SimklAccessToken")
scope, scopeErr := simklRing.Get("SimklScope") scope, scopeErr := simklRing.Get("SimklScope")
@@ -80,18 +114,9 @@ func (a *App) handleSimklCallback(wg *sync.WaitGroup) {
if content != "" { if content != "" {
simklJwt = getSimklAuthorizationToken(content) simklJwt = getSimklAuthorizationToken(content)
_ = simklRing.Set(keyring.Item{ _ = simklRingSet("SimklTokenType", []byte(simklJwt.TokenType))
Key: "SimklTokenType", _ = simklRingSet("SimklAccessToken", []byte(simklJwt.AccessToken))
Data: []byte(simklJwt.TokenType), _ = simklRingSet("SimklScope", []byte(simklJwt.Scope))
})
_ = simklRing.Set(keyring.Item{
Key: "SimklAccessToken",
Data: []byte(simklJwt.AccessToken),
})
_ = simklRing.Set(keyring.Item{
Key: "SimklScope",
Data: []byte(simklJwt.Scope),
})
_, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ _, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{
Title: "Simkl Authorization", Title: "Simkl Authorization",
Message: "It is now safe to close your browser tab", Message: "It is now safe to close your browser tab",
@@ -218,12 +243,16 @@ func (a *App) GetSimklLoggedInUser() SimklUser {
func (a *App) LogoutSimkl() string { func (a *App) LogoutSimkl() string {
if (SimklJWT{} != simklJwt) { if (SimklJWT{} != simklJwt) {
if !simklRingReady() {
simklJwt = SimklJWT{}
return "Simkl Logged Out Successfully"
}
tokenTypeErr := simklRing.Remove("SimklTokenType") tokenTypeErr := simklRing.Remove("SimklTokenType")
accessTokenErr := simklRing.Remove("SimklAccessToken") accessTokenErr := simklRing.Remove("SimklAccessToken")
scopeErr := simklRing.Remove("SimklScope") scopeErr := simklRing.Remove("SimklScope")
if tokenTypeErr != nil || accessTokenErr != nil || scopeErr != nil { if tokenTypeErr != nil || accessTokenErr != nil || scopeErr != nil {
fmt.Println("Simkl Logout Failed") log.Printf("simkl: logout cleanup failed (type=%v access=%v scope=%v)", tokenTypeErr, accessTokenErr, scopeErr)
} }
simklJwt = SimklJWT{} simklJwt = SimklJWT{}
} }
+1 -1
View File
@@ -12,6 +12,6 @@
}, },
"info": { "info": {
"productName": "AniTrack", "productName": "AniTrack",
"productVersion": "1.6.7" "productVersion": "1.6.8"
} }
} }