feat: latest info, logout, and better ui
This commit is contained in:
parent
83daefe9f0
commit
24efbe8127
|
@ -1,7 +1,8 @@
|
||||||
package consts
|
package consts
|
||||||
|
|
||||||
const (
|
const (
|
||||||
REDIS_KEY_LOGIN_SESSION = "misso:login:%s" // Username, token as value
|
REDIS_KEY_LOGIN_SESSION = "misso:login:%s" // Username, token as value
|
||||||
REDIS_KEY_CONSENT_CSRF = "misso:consent:%s" // Random string, consent challenge as value
|
REDIS_KEY_CONSENT_CSRF = "misso:consent:%s" // Random string, consent challenge as value
|
||||||
REDIS_KEY_SHARE_CONTEXT = "misso:share:%s" // Subject, context as value
|
REDIS_KEY_SHARE_ACCESS_TOKEN = "misso:share:at:%s" // Subject, access token as value
|
||||||
|
REDIS_KEY_SHARE_USER_INFO = "misso:share:ui:%s" // Subject, user info as value
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,9 +3,9 @@ package consts
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TIME_LOGIN_REQUEST_VALID = 10 * time.Minute
|
TIME_REQUEST_VALID = 1 * time.Hour
|
||||||
TIME_LOGIN_SESSION_VALID = 7 * 24 * time.Hour
|
|
||||||
|
|
||||||
TIME_CONSENT_REQUEST_VALID = 1 * time.Hour
|
TIME_LOGIN_REMEMBER = 10 * time.Minute
|
||||||
TIME_CONSENT_SESSION_VALID = 30 * 24 * time.Hour
|
|
||||||
|
TIME_USERINFO_CACHE = 10 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,13 +2,11 @@ package consent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
client "github.com/ory/hydra-client-go/v2"
|
client "github.com/ory/hydra-client-go/v2"
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/types"
|
|
||||||
"misso/utils"
|
"misso/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -59,7 +57,7 @@ func ConsentCheck(ctx *gin.Context) {
|
||||||
global.Logger.Debugf("Generating CSRF token...")
|
global.Logger.Debugf("Generating CSRF token...")
|
||||||
csrf := utils.RandString(32)
|
csrf := utils.RandString(32)
|
||||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, csrf)
|
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, csrf)
|
||||||
err := global.Redis.Set(context.Background(), sessKey, oauth2challenge, consts.TIME_CONSENT_REQUEST_VALID).Err()
|
err := global.Redis.Set(context.Background(), sessKey, oauth2challenge, consts.TIME_REQUEST_VALID).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to save csrf into redis with error: %v", err)
|
global.Logger.Errorf("Failed to save csrf into redis with error: %v", err)
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
|
@ -70,23 +68,12 @@ func ConsentCheck(ctx *gin.Context) {
|
||||||
|
|
||||||
// Retrieve context
|
// Retrieve context
|
||||||
global.Logger.Debugf("Retrieving context...")
|
global.Logger.Debugf("Retrieving context...")
|
||||||
var userinfoCtx types.SessionContext
|
|
||||||
sessKey = fmt.Sprintf(consts.REDIS_KEY_SHARE_CONTEXT, *consentReq.Subject)
|
|
||||||
userinfoCtxBytes, err := global.Redis.Get(context.Background(), sessKey).Bytes()
|
|
||||||
if err != nil {
|
|
||||||
global.Logger.Errorf("Failed to retrieve context with error: %v", err)
|
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
|
||||||
"error": "Failed to retrieve context",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Logger.Debugf("Decoding context...")
|
userinfoCtx, err := utils.GetUserinfo(*consentReq.Subject)
|
||||||
err = json.Unmarshal(userinfoCtxBytes, &userinfoCtx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to parse context with error: %v", err)
|
global.Logger.Errorf("Failed to retrieve userinfo with error: %v", err)
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
"error": "Failed to parse context",
|
"error": "Failed to get userinfo",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -94,7 +81,7 @@ func ConsentCheck(ctx *gin.Context) {
|
||||||
// Show the consent UI
|
// Show the consent UI
|
||||||
global.Logger.Debugf("Rendering consent UI...")
|
global.Logger.Debugf("Rendering consent UI...")
|
||||||
templateFields := gin.H{
|
templateFields := gin.H{
|
||||||
"user": userinfoCtx.User,
|
"user": *userinfoCtx,
|
||||||
"challenge": oauth2challenge,
|
"challenge": oauth2challenge,
|
||||||
"csrf": csrf,
|
"csrf": csrf,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,12 @@ package consent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
client "github.com/ory/hydra-client-go/v2"
|
client "github.com/ory/hydra-client-go/v2"
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/types"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConsentConfirmRequest struct {
|
type ConsentConfirmRequest struct {
|
||||||
|
@ -80,31 +77,9 @@ func ConsentConfirm(ctx *gin.Context) {
|
||||||
global.Logger.Debugf("User should now be redirecting to target URI.")
|
global.Logger.Debugf("User should now be redirecting to target URI.")
|
||||||
} else if req.Action == "accept" {
|
} else if req.Action == "accept" {
|
||||||
global.Logger.Debugf("User accepted the request, reporting back to hydra...")
|
global.Logger.Debugf("User accepted the request, reporting back to hydra...")
|
||||||
// Retrieve context
|
|
||||||
global.Logger.Debugf("Retrieving context...")
|
|
||||||
var userinfoCtx types.SessionContext
|
|
||||||
sessKey = fmt.Sprintf(consts.REDIS_KEY_SHARE_CONTEXT, *consentReq.Subject)
|
|
||||||
userinfoCtxBytes, err := global.Redis.Get(context.Background(), sessKey).Bytes()
|
|
||||||
if err != nil {
|
|
||||||
global.Logger.Errorf("Failed to retrieve context with error: %v", err)
|
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
|
||||||
"error": "Failed to retrieve context",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Logger.Debugf("Decoding context...")
|
|
||||||
err = json.Unmarshal(userinfoCtxBytes, &userinfoCtx)
|
|
||||||
if err != nil {
|
|
||||||
global.Logger.Errorf("Failed to parse context with error: %v", err)
|
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
|
||||||
"error": "Failed to parse context",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Logger.Debugf("Initializing ID Token...")
|
global.Logger.Debugf("Initializing ID Token...")
|
||||||
rememberFor := int64(consts.TIME_CONSENT_SESSION_VALID / time.Second)
|
rememberFor := int64(0) // Remember forever
|
||||||
acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2ConsentRequest(context.Background()).ConsentChallenge(oauth2challenge).AcceptOAuth2ConsentRequest(client.AcceptOAuth2ConsentRequest{
|
acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2ConsentRequest(context.Background()).ConsentChallenge(oauth2challenge).AcceptOAuth2ConsentRequest(client.AcceptOAuth2ConsentRequest{
|
||||||
GrantScope: consentReq.RequestedScope, // TODO: Specify scopes
|
GrantScope: consentReq.RequestedScope, // TODO: Specify scopes
|
||||||
GrantAccessTokenAudience: consentReq.RequestedAccessTokenAudience,
|
GrantAccessTokenAudience: consentReq.RequestedAccessTokenAudience,
|
||||||
|
|
|
@ -65,9 +65,11 @@ func Login(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global.Logger.Debugf("Grabbed auth session: %s", authSess.Token)
|
||||||
|
|
||||||
// Save login challenge state into redis (misskey cannot keep state info)
|
// Save login challenge state into redis (misskey cannot keep state info)
|
||||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_LOGIN_SESSION, authSess.Token)
|
sessKey := fmt.Sprintf(consts.REDIS_KEY_LOGIN_SESSION, authSess.Token)
|
||||||
err = global.Redis.Set(context.Background(), sessKey, oauth2challenge, consts.TIME_LOGIN_REQUEST_VALID).Err()
|
err = global.Redis.Set(context.Background(), sessKey, oauth2challenge, consts.TIME_REQUEST_VALID).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to save session into redis with error: %v", err)
|
global.Logger.Errorf("Failed to save session into redis with error: %v", err)
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
|
|
|
@ -2,7 +2,6 @@ package login
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
client "github.com/ory/hydra-client-go/v2"
|
client "github.com/ory/hydra-client-go/v2"
|
||||||
|
@ -10,7 +9,7 @@ import (
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/misskey"
|
"misso/misskey"
|
||||||
"misso/types"
|
"misso/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -65,31 +64,29 @@ func MisskeyAuthCallback(ctx *gin.Context) {
|
||||||
|
|
||||||
userid := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance)
|
userid := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance)
|
||||||
|
|
||||||
// Save context into redis
|
sessAccessTokenKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_ACCESS_TOKEN, userid)
|
||||||
userinfoCtxBytes, err := json.Marshal(&types.SessionContext{
|
err = global.Redis.Set(context.Background(), sessAccessTokenKey, usermeta.AccessToken, 0).Err()
|
||||||
MisskeyToken: usermeta.AccessToken,
|
|
||||||
User: usermeta.User,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to parse accept context with error: %v", err)
|
global.Logger.Errorf("Failed to save session access token into redis with error: %v", err)
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
|
||||||
"error": "Failed to parse accept context",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sessKey = fmt.Sprintf(consts.REDIS_KEY_SHARE_CONTEXT, userid)
|
|
||||||
err = global.Redis.Set(context.Background(), sessKey, userinfoCtxBytes, consts.TIME_LOGIN_SESSION_VALID).Err()
|
|
||||||
if err != nil {
|
|
||||||
global.Logger.Errorf("Failed to save session into redis with error: %v", err)
|
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
"error": "Failed to save context",
|
"error": "Failed to save context",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save context into redis
|
||||||
|
err = utils.SaveUserinfo(userid, &usermeta.User)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to save session user info into redis with error: %v", err)
|
||||||
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
|
"error": "Failed to save userinfo",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
global.Logger.Debugf("User accepted the request, reporting back to hydra...")
|
global.Logger.Debugf("User accepted the request, reporting back to hydra...")
|
||||||
remember := true
|
remember := true
|
||||||
rememberFor := int64(consts.TIME_LOGIN_SESSION_VALID / time.Second)
|
rememberFor := int64(consts.TIME_LOGIN_REMEMBER / time.Second)
|
||||||
acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LoginRequest(context.Background()).LoginChallenge(oauth2challenge).AcceptOAuth2LoginRequest(client.AcceptOAuth2LoginRequest{
|
acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LoginRequest(context.Background()).LoginChallenge(oauth2challenge).AcceptOAuth2LoginRequest(client.AcceptOAuth2LoginRequest{
|
||||||
Subject: userid,
|
Subject: userid,
|
||||||
Remember: &remember,
|
Remember: &remember,
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package logout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"misso/global"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Logout(ctx *gin.Context) {
|
||||||
|
oauth2challenge := ctx.Query("logout_challenge") // OAuth2 login
|
||||||
|
if oauth2challenge == "" {
|
||||||
|
ctx.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||||
|
"error": "Necessary challenge not provided",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LogoutRequest(context.Background()).LogoutChallenge(oauth2challenge).Execute()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to accept logout request with error: %v", err)
|
||||||
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
|
"error": "Failed to accept logout request",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(http.StatusTemporaryRedirect, acceptReq.RedirectTo)
|
||||||
|
|
||||||
|
}
|
|
@ -2,12 +2,10 @@ package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"misso/consts"
|
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/types"
|
"misso/types"
|
||||||
|
"misso/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -46,32 +44,18 @@ func UserInfo(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return user info
|
// Return user info
|
||||||
|
|
||||||
// Retrieve context
|
|
||||||
global.Logger.Debugf("Retrieving context...")
|
global.Logger.Debugf("Retrieving context...")
|
||||||
var userinfoCtx types.SessionContext
|
userinfoCtx, err := utils.GetUserinfo(*tokenInfo.Sub)
|
||||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_CONTEXT, *tokenInfo.Sub)
|
|
||||||
userinfoCtxBytes, err := global.Redis.Get(context.Background(), sessKey).Bytes()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to retrieve context with error: %v", err)
|
global.Logger.Errorf("Failed to retrieve userinfo with error: %v", err)
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
"error": "Failed to retrieve context",
|
"error": "Failed to get userinfo",
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Logger.Debugf("Decoding context...")
|
|
||||||
err = json.Unmarshal(userinfoCtxBytes, &userinfoCtx)
|
|
||||||
if err != nil {
|
|
||||||
global.Logger.Errorf("Failed to parse context with error: %v", err)
|
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
|
||||||
"error": "Failed to parse context",
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, UserinfoResponse{
|
ctx.JSON(http.StatusOK, UserinfoResponse{
|
||||||
MisskeyUser: userinfoCtx.User,
|
MisskeyUser: *userinfoCtx,
|
||||||
EMail: *tokenInfo.Sub,
|
EMail: *tokenInfo.Sub,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ type Error_Response struct {
|
||||||
} `json:"error"`
|
} `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostAPIRequest[T AuthSessionGenerate_Response | AuthSessionUserkey_Response](
|
func PostAPIRequest[T I_Response | AuthSessionGenerate_Response | AuthSessionUserkey_Response](
|
||||||
apiEndpointPath string, reqBody any,
|
apiEndpointPath string, reqBody any,
|
||||||
) (*T, error) {
|
) (*T, error) {
|
||||||
// Prepare request
|
// Prepare request
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package misskey
|
||||||
|
|
||||||
|
import "misso/types"
|
||||||
|
|
||||||
|
type I_Request struct {
|
||||||
|
I string `json:"i"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type I_Response = types.MisskeyUser
|
||||||
|
|
||||||
|
func GetUserinfo(accessToken string) (*I_Response, error) {
|
||||||
|
return PostAPIRequest[I_Response]("/api/i", &I_Request{
|
||||||
|
I: accessToken,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"misso/handlers/logout"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Logout(rg *gin.RouterGroup) {
|
||||||
|
rg.GET("/logout", logout.Logout)
|
||||||
|
}
|
|
@ -18,6 +18,9 @@ func R(e *gin.Engine) {
|
||||||
// Consent
|
// Consent
|
||||||
Consent(rootGroup)
|
Consent(rootGroup)
|
||||||
|
|
||||||
|
// Logout
|
||||||
|
Logout(rootGroup)
|
||||||
|
|
||||||
// Userinfo
|
// Userinfo
|
||||||
User(rootGroup)
|
User(rootGroup)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,14 @@
|
||||||
#main {
|
#main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: fit-content;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 32px 40px;
|
padding: 40px;
|
||||||
background-color: #21252b;
|
background-color: #21252b;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
margin: 0 40px;
|
||||||
|
width: 100%;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,3 +182,5 @@ type MisskeyUser struct {
|
||||||
EmailNotificationTypes []string `json:"emailNotificationTypes"`
|
EmailNotificationTypes []string `json:"emailNotificationTypes"`
|
||||||
ShowTimelineReplies bool `json:"showTimelineReplies"`
|
ShowTimelineReplies bool `json:"showTimelineReplies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Find a better way to split necessary fields and additional fields
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
type SessionContext struct {
|
|
||||||
MisskeyToken string `json:"token"`
|
|
||||||
User MisskeyUser `json:"user"`
|
|
||||||
}
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"misso/consts"
|
||||||
|
"misso/global"
|
||||||
|
"misso/misskey"
|
||||||
|
"misso/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetUserinfo(subject string) (*types.MisskeyUser, error) {
|
||||||
|
// Check cache key
|
||||||
|
global.Logger.Debugf("Checking userinfo cache...")
|
||||||
|
userinfoCacheKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_USER_INFO, subject)
|
||||||
|
exist, err := global.Redis.Exists(context.Background(), userinfoCacheKey).Result()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to check userinfo exist status with error: %v", err)
|
||||||
|
} else if exist > 0 {
|
||||||
|
// Exist, get from cache
|
||||||
|
userinfoCacheBytes, err := global.Redis.Get(context.Background(), userinfoCacheKey).Bytes()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Userinfo cache exists, but failed to retrieve with error: %v", err)
|
||||||
|
} else {
|
||||||
|
// Parse into user
|
||||||
|
var userinfo types.MisskeyUser
|
||||||
|
err = json.Unmarshal(userinfoCacheBytes, &userinfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to parse userinfo cache into json with error: %v", err)
|
||||||
|
} else {
|
||||||
|
// Works!
|
||||||
|
global.Logger.Debugf("Get cached userinfo successfully.")
|
||||||
|
return &userinfo, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to get info directly, we need user's access token.
|
||||||
|
global.Logger.Debugf("No cached userinfo found (or valid), trying to get latest response.")
|
||||||
|
accessTokenCacheKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_ACCESS_TOKEN, subject)
|
||||||
|
accessToken, err := global.Redis.Get(context.Background(), accessTokenCacheKey).Result()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to get user access token with error: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get userinfo with access token
|
||||||
|
userinfo, err := misskey.GetUserinfo(accessToken)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to get user info with saved access token with error: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save userinfo into redis
|
||||||
|
_ = SaveUserinfo(subject, userinfo) // Ignore errors
|
||||||
|
|
||||||
|
return userinfo, nil
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"misso/consts"
|
||||||
|
"misso/global"
|
||||||
|
"misso/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SaveUserinfo(subject string, userinfo *types.MisskeyUser) error {
|
||||||
|
userinfoBytes, err := json.Marshal(userinfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to parse accept context with error: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sessUserInfoKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_USER_INFO, subject)
|
||||||
|
err = global.Redis.Set(context.Background(), sessUserInfoKey, userinfoBytes, consts.TIME_USERINFO_CACHE).Err()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("Failed to save session user info into redis with error: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue