Compare commits
No commits in common. "f4be6e135aaf1ded368934311bf6c7609b6a126d" and "6aa0db6d0e27b53c796bc34ee2125114021c8335" have entirely different histories.
f4be6e135a
...
6aa0db6d0e
|
@ -7,8 +7,3 @@ misskey:
|
||||||
secret: ""
|
secret: ""
|
||||||
hydra:
|
hydra:
|
||||||
admin_url: "http://localhost:4445"
|
admin_url: "http://localhost:4445"
|
||||||
time:
|
|
||||||
request_valid: 3600
|
|
||||||
login_remember: 600
|
|
||||||
consent_remember: 0
|
|
||||||
userinfo_cache: 3600
|
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -4,6 +4,3 @@
|
||||||
|
|
||||||
db/
|
db/
|
||||||
redis/
|
redis/
|
||||||
|
|
||||||
config.yml
|
|
||||||
misso
|
|
||||||
|
|
|
@ -1,8 +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_USER_ACCESS_TOKEN = "misso:user:token:%s" // Subject, access token as value
|
REDIS_KEY_SHARE_ACCESS_TOKEN = "misso:share:at:%s" // Subject, access token as value
|
||||||
REDIS_KEY_USER_INFO = "misso:user:info:%s" // Subject, user info as value
|
REDIS_KEY_SHARE_USER_INFO = "misso:share:ui:%s" // Subject, user info as value
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package consts
|
package consts
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TIME_DEFAULT_REQUEST_VALID = 3600 // 1 Hour
|
TIME_REQUEST_VALID = 1 * time.Hour
|
||||||
|
|
||||||
TIME_DEFAULT_LOGIN_REMEMBER = 600 // 10 Minute
|
TIME_LOGIN_REMEMBER = 10 * time.Minute
|
||||||
TIME_DEFAULT_CONSENT_REMEMBER = 0 // Forever
|
TIME_CONSENT_REMEMBER = 0 // Forever
|
||||||
|
|
||||||
TIME_DEFAULT_USERINFO_CACHE = 3600 // 1 Hour
|
TIME_USERINFO_CACHE = 10 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,12 +5,10 @@ import (
|
||||||
"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/config"
|
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/utils"
|
"misso/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ConsentCheck(ctx *gin.Context) {
|
func ConsentCheck(ctx *gin.Context) {
|
||||||
|
@ -58,8 +56,8 @@ func ConsentCheck(ctx *gin.Context) {
|
||||||
// Generate CSRF token
|
// Generate CSRF token
|
||||||
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, oauth2challenge)
|
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, csrf)
|
||||||
err := global.Redis.Set(context.Background(), sessKey, csrf, time.Duration(config.Config.Time.RequestValid)*time.Second).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{
|
||||||
|
@ -71,7 +69,7 @@ func ConsentCheck(ctx *gin.Context) {
|
||||||
// Retrieve context
|
// Retrieve context
|
||||||
global.Logger.Debugf("Retrieving context...")
|
global.Logger.Debugf("Retrieving context...")
|
||||||
|
|
||||||
userinfo, err := utils.GetUserinfo(*consentReq.Subject)
|
userinfoCtx, err := utils.GetUserinfo(*consentReq.Subject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to retrieve userinfo 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{
|
||||||
|
@ -83,10 +81,9 @@ 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": *userinfo,
|
"user": *userinfoCtx,
|
||||||
"challenge": oauth2challenge,
|
"challenge": oauth2challenge,
|
||||||
"csrf": csrf,
|
"csrf": csrf,
|
||||||
"scopes": consentReq.RequestedScope,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if consentReq.Client.LogoUri != nil && *consentReq.Client.LogoUri != "" {
|
if consentReq.Client.LogoUri != nil && *consentReq.Client.LogoUri != "" {
|
||||||
|
@ -97,12 +94,6 @@ func ConsentCheck(ctx *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
templateFields["clientName"] = *consentReq.Client.ClientId
|
templateFields["clientName"] = *consentReq.Client.ClientId
|
||||||
}
|
}
|
||||||
if consentReq.Client.PolicyUri != nil && *consentReq.Client.PolicyUri != "" {
|
|
||||||
templateFields["clientPolicy"] = *consentReq.Client.PolicyUri
|
|
||||||
}
|
|
||||||
if consentReq.Client.TosUri != nil && *consentReq.Client.TosUri != "" {
|
|
||||||
templateFields["clientTos"] = *consentReq.Client.TosUri
|
|
||||||
}
|
|
||||||
ctx.HTML(http.StatusOK, "consent.tmpl", templateFields)
|
ctx.HTML(http.StatusOK, "consent.tmpl", templateFields)
|
||||||
|
|
||||||
global.Logger.Debugf("User should now see Consent UI.")
|
global.Logger.Debugf("User should now see Consent UI.")
|
||||||
|
|
|
@ -5,17 +5,16 @@ import (
|
||||||
"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/config"
|
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConsentConfirmRequest struct {
|
type ConsentConfirmRequest struct {
|
||||||
CSRF string `form:"_csrf"`
|
CSRF string `form:"_csrf"`
|
||||||
Challenge string `form:"challenge"`
|
Remember bool `form:"remember"`
|
||||||
Remember bool `form:"remember"`
|
Action string `form:"action"`
|
||||||
Action string `form:"action"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConsentConfirm(ctx *gin.Context) {
|
func ConsentConfirm(ctx *gin.Context) {
|
||||||
|
@ -33,23 +32,16 @@ func ConsentConfirm(ctx *gin.Context) {
|
||||||
|
|
||||||
// Validate CSRF
|
// Validate CSRF
|
||||||
global.Logger.Debugf("Validating CSRF...")
|
global.Logger.Debugf("Validating CSRF...")
|
||||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, req.Challenge)
|
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, req.CSRF)
|
||||||
csrfSession, err := global.Redis.Get(context.Background(), sessKey).Result()
|
oauth2challenge, err := global.Redis.Get(context.Background(), sessKey).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to get csrf from redis with error: %v", err)
|
global.Logger.Errorf("Failed to get csrf from redis with error: %v", err)
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
"error": "Failed to get csrf",
|
"error": "Failed to get csrf",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
} else if csrfSession != req.CSRF {
|
|
||||||
ctx.HTML(http.StatusForbidden, "error.tmpl", gin.H{
|
|
||||||
"error": "CSRF not match",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oauth2challenge := req.Challenge
|
|
||||||
|
|
||||||
// Delete used challenge
|
// Delete used challenge
|
||||||
global.Redis.Del(context.Background(), sessKey)
|
global.Redis.Del(context.Background(), sessKey)
|
||||||
|
|
||||||
|
@ -88,9 +80,9 @@ func ConsentConfirm(ctx *gin.Context) {
|
||||||
global.Logger.Debugf("User accepted the request, reporting back to hydra...")
|
global.Logger.Debugf("User accepted the request, reporting back to hydra...")
|
||||||
|
|
||||||
global.Logger.Debugf("Initializing ID Token...")
|
global.Logger.Debugf("Initializing ID Token...")
|
||||||
rememberFor := config.Config.Time.ConsentRemember // Remember forever
|
rememberFor := int64(consts.TIME_CONSENT_REMEMBER / time.Second) // 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,
|
GrantScope: consentReq.RequestedScope, // TODO: Specify scopes
|
||||||
GrantAccessTokenAudience: consentReq.RequestedAccessTokenAudience,
|
GrantAccessTokenAudience: consentReq.RequestedAccessTokenAudience,
|
||||||
Remember: &req.Remember,
|
Remember: &req.Remember,
|
||||||
RememberFor: &rememberFor,
|
RememberFor: &rememberFor,
|
||||||
|
|
|
@ -5,12 +5,10 @@ import (
|
||||||
"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/config"
|
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/misskey"
|
"misso/misskey"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Login(ctx *gin.Context) {
|
func Login(ctx *gin.Context) {
|
||||||
|
@ -71,7 +69,7 @@ func Login(ctx *gin.Context) {
|
||||||
|
|
||||||
// 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, time.Duration(config.Config.Time.RequestValid)*time.Second).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{
|
||||||
|
|
|
@ -9,7 +9,9 @@ import (
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/misskey"
|
"misso/misskey"
|
||||||
|
"misso/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MisskeyAuthCallback(ctx *gin.Context) {
|
func MisskeyAuthCallback(ctx *gin.Context) {
|
||||||
|
@ -60,10 +62,9 @@ func MisskeyAuthCallback(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save user key
|
userid := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance)
|
||||||
userIdentifier := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance)
|
|
||||||
|
|
||||||
sessAccessTokenKey := fmt.Sprintf(consts.REDIS_KEY_USER_ACCESS_TOKEN, userIdentifier)
|
sessAccessTokenKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_ACCESS_TOKEN, userid)
|
||||||
err = global.Redis.Set(context.Background(), sessAccessTokenKey, usermeta.AccessToken, 0).Err()
|
err = global.Redis.Set(context.Background(), sessAccessTokenKey, usermeta.AccessToken, 0).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to save session access token into redis with error: %v", err)
|
global.Logger.Errorf("Failed to save session access token into redis with error: %v", err)
|
||||||
|
@ -73,16 +74,24 @@ func MisskeyAuthCallback(ctx *gin.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...")
|
||||||
acceptReq := client.AcceptOAuth2LoginRequest{
|
remember := true
|
||||||
Subject: userIdentifier,
|
rememberFor := int64(consts.TIME_LOGIN_REMEMBER / time.Second)
|
||||||
}
|
acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LoginRequest(context.Background()).LoginChallenge(oauth2challenge).AcceptOAuth2LoginRequest(client.AcceptOAuth2LoginRequest{
|
||||||
if config.Config.Time.LoginRemember > 0 {
|
Subject: userid,
|
||||||
remember := true
|
Remember: &remember,
|
||||||
acceptReq.Remember = &remember
|
RememberFor: &rememberFor,
|
||||||
acceptReq.RememberFor = &config.Config.Time.LoginRemember
|
}).Execute()
|
||||||
}
|
|
||||||
acceptRes, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LoginRequest(context.Background()).LoginChallenge(oauth2challenge).AcceptOAuth2LoginRequest(acceptReq).Execute()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to accept login request with error: %v", err)
|
global.Logger.Errorf("Failed to accept login request with error: %v", err)
|
||||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||||
|
@ -92,7 +101,7 @@ func MisskeyAuthCallback(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to target uri
|
// Redirect to target uri
|
||||||
ctx.Redirect(http.StatusTemporaryRedirect, acceptRes.RedirectTo)
|
ctx.Redirect(http.StatusTemporaryRedirect, acceptReq.RedirectTo)
|
||||||
|
|
||||||
global.Logger.Debugf("User should now be redirecting to target URI.")
|
global.Logger.Debugf("User should now be redirecting to target URI.")
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,17 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
|
"misso/types"
|
||||||
"misso/utils"
|
"misso/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UserinfoResponse struct {
|
||||||
|
types.MisskeyUser
|
||||||
|
EMail string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
func UserInfo(ctx *gin.Context) {
|
func UserInfo(ctx *gin.Context) {
|
||||||
// Get token from header
|
// Get token from header
|
||||||
accessToken := strings.Replace(ctx.GetHeader("Authorization"), "Bearer ", "", 1)
|
accessToken := strings.Replace(ctx.GetHeader("Authorization"), "Bearer ", "", 1)
|
||||||
|
@ -39,7 +45,7 @@ func UserInfo(ctx *gin.Context) {
|
||||||
|
|
||||||
// Return user info
|
// Return user info
|
||||||
global.Logger.Debugf("Retrieving context...")
|
global.Logger.Debugf("Retrieving context...")
|
||||||
userinfo, err := utils.GetUserinfo(*tokenInfo.Sub)
|
userinfoCtx, err := utils.GetUserinfo(*tokenInfo.Sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to retrieve userinfo 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{
|
||||||
|
@ -48,26 +54,9 @@ func UserInfo(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userinfoRes := gin.H{} // map[string]interface{}
|
ctx.JSON(http.StatusOK, UserinfoResponse{
|
||||||
|
MisskeyUser: *userinfoCtx,
|
||||||
// Get scopes
|
EMail: *tokenInfo.Sub,
|
||||||
if tokenInfo.Scope != nil && *tokenInfo.Scope != "" {
|
})
|
||||||
// Has scopes
|
|
||||||
scopes := strings.Split(*tokenInfo.Scope, " ")
|
|
||||||
for _, s := range scopes {
|
|
||||||
if value, ok := (*userinfo)[s]; ok {
|
|
||||||
userinfoRes[s] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
if s == "openid" {
|
|
||||||
userinfoRes["sub"] = *tokenInfo.Sub
|
|
||||||
} else if s == "username" {
|
|
||||||
// Add "nickname" field for OIDC compatibility
|
|
||||||
userinfoRes["nickname"] = userinfoRes[s]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, userinfoRes)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package inits
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"misso/config"
|
"misso/config"
|
||||||
"misso/consts"
|
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,21 +23,5 @@ func Config() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate time
|
|
||||||
if config.Config.Time.RequestValid <= 0 {
|
|
||||||
config.Config.Time.RequestValid = consts.TIME_DEFAULT_REQUEST_VALID
|
|
||||||
}
|
|
||||||
if config.Config.Time.LoginRemember < 0 {
|
|
||||||
// 0 means don't remember (in extreme account switch situations)
|
|
||||||
config.Config.Time.LoginRemember = consts.TIME_DEFAULT_LOGIN_REMEMBER
|
|
||||||
}
|
|
||||||
if config.Config.Time.ConsentRemember < 0 {
|
|
||||||
// 0 means remember forever (default behavior)
|
|
||||||
config.Config.Time.ConsentRemember = consts.TIME_DEFAULT_CONSENT_REMEMBER
|
|
||||||
}
|
|
||||||
if config.Config.Time.UserinfoCache <= 0 {
|
|
||||||
config.Config.Time.UserinfoCache = consts.TIME_DEFAULT_USERINFO_CACHE
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func PostAPIRequest[T I_Response | AuthSessionGenerate_Response | AuthSessionUse
|
||||||
apiEndpointPath string, reqBody any,
|
apiEndpointPath string, reqBody any,
|
||||||
) (*T, error) {
|
) (*T, error) {
|
||||||
// Prepare request
|
// Prepare request
|
||||||
apiEndpoint := fmt.Sprintf("https://%s/api/%s", config.Config.Misskey.Instance, apiEndpointPath)
|
apiEndpoint := fmt.Sprintf("https://%s%s", config.Config.Misskey.Instance, apiEndpointPath)
|
||||||
|
|
||||||
reqBodyBytes, err := json.Marshal(reqBody)
|
reqBodyBytes, err := json.Marshal(reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -13,7 +13,7 @@ type AuthSessionGenerate_Response struct {
|
||||||
|
|
||||||
func GenerateAuthSession() (*AuthSessionGenerate_Response, error) {
|
func GenerateAuthSession() (*AuthSessionGenerate_Response, error) {
|
||||||
|
|
||||||
return PostAPIRequest[AuthSessionGenerate_Response]("auth/session/generate", &AuthSessionGenerate_Request{
|
return PostAPIRequest[AuthSessionGenerate_Response]("/api/auth/session/generate", &AuthSessionGenerate_Request{
|
||||||
AppSecret: config.Config.Misskey.Application.Secret,
|
AppSecret: config.Config.Misskey.Application.Secret,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ type I_Request struct {
|
||||||
type I_Response = types.MisskeyUser
|
type I_Response = types.MisskeyUser
|
||||||
|
|
||||||
func GetUserinfo(accessToken string) (*I_Response, error) {
|
func GetUserinfo(accessToken string) (*I_Response, error) {
|
||||||
return PostAPIRequest[I_Response]("i", &I_Request{
|
return PostAPIRequest[I_Response]("/api/i", &I_Request{
|
||||||
I: accessToken,
|
I: accessToken,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,13 @@ type AuthSessionUserkey_Request struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthSessionUserkey_Response struct {
|
type AuthSessionUserkey_Response struct {
|
||||||
AccessToken string `json:"accessToken"`
|
AccessToken string `json:"accessToken"`
|
||||||
User types.MisskeyUserBase `json:"user"`
|
User types.MisskeyUser `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserkey(token string) (*AuthSessionUserkey_Response, error) {
|
func GetUserkey(token string) (*AuthSessionUserkey_Response, error) {
|
||||||
|
|
||||||
return PostAPIRequest[AuthSessionUserkey_Response]("auth/session/userkey", &AuthSessionUserkey_Request{
|
return PostAPIRequest[AuthSessionUserkey_Response]("/api/auth/session/userkey", &AuthSessionUserkey_Request{
|
||||||
AppSecret: config.Config.Misskey.Application.Secret,
|
AppSecret: config.Config.Misskey.Application.Secret,
|
||||||
Token: token,
|
Token: token,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,60 +1,11 @@
|
||||||
{{ define "consent.tmpl" }}
|
{{ define "consent.tmpl" }}
|
||||||
<html lang="zh">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Confirm authorization</title>
|
<title>授权确认</title>
|
||||||
{{ template "head.tmpl" }}
|
{{ template "head.tmpl" }}
|
||||||
<style>
|
<style>
|
||||||
ul.scopes,
|
|
||||||
ul.app-terms {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.scopes {
|
|
||||||
text-align: left;
|
|
||||||
width: 100%;
|
|
||||||
max-height: 30vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.scopes > li {
|
|
||||||
padding: 6px 12px;
|
|
||||||
margin: 6px 0;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #282c34;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.scopes > li > * {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.app-terms {
|
|
||||||
display: inline-flex;
|
|
||||||
gap: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.app-terms > li {
|
|
||||||
padding: 0 6px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.app-terms > li:not(:last-child) {
|
|
||||||
border-right: 1px solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.app-terms > li > a {
|
|
||||||
color: #62b6e7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.consent-notice {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #282c34;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.remember {
|
p.remember {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
p.remember, p.remember > * {
|
p.remember, p.remember > * {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -108,7 +59,7 @@
|
||||||
background: #15803d;
|
background: #15803d;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-name, .user-name {
|
#app-name, #user-name {
|
||||||
color: #62b6e7;
|
color: #62b6e7;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -116,69 +67,29 @@
|
||||||
<body>
|
<body>
|
||||||
<form id="main" action="/consent" method="POST">
|
<form id="main" action="/consent" method="POST">
|
||||||
<input type="hidden" name="_csrf" value="{{ .csrf }}" />
|
<input type="hidden" name="_csrf" value="{{ .csrf }}" />
|
||||||
<input type="hidden" name="challenge" value="{{ .challenge }}" />
|
|
||||||
|
|
||||||
<div class="logo">
|
{{ if .logo }}
|
||||||
{{ if .logo }}
|
<img src="{{ .logo }}" alt="{{ .clientName }}" width="120" height="120" />
|
||||||
<img src="{{ .logo }}" alt="{{ .clientName }}" width="120" height="120" />
|
{{ else }}
|
||||||
{{ else }}
|
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48"><title>c-warning</title><g><path fill="#EFD358" d="M24,1C11.31787,1,1,11.31787,1,24s10.31787,23,23,23s23-10.31787,23-23S36.68213,1,24,1z"></path> <path fill="#FFFFFF" d="M24,28c0.55225,0,1-0.44775,1-1V14c0-0.55225-0.44775-1-1-1s-1,0.44775-1,1v13 C23,27.55225,23.44775,28,24,28z"></path> <circle fill="#FFFFFF" cx="24" cy="33" r="2"></circle></g></svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48">
|
{{ end }}
|
||||||
<g>
|
|
||||||
<circle fill="#EFD358" cx="24" cy="24" r="24"></circle>
|
|
||||||
<path fill="#FFFFFF" d="M24,28c0.55225,0,1-0.44775,1-1V14c0-0.55225-0.44775-1-1-1s-1,0.44775-1,1v13 C23,27.55225,23.44775,28,24,28z"></path>
|
|
||||||
<circle fill="#FFFFFF" cx="24" cy="33" r="2"></circle>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<p>
|
||||||
<p>
|
应用程序
|
||||||
Application
|
<span id="app-name">{{ .clientName }}</span>
|
||||||
<span class="app-name">{{ .clientName }}</span>
|
正请求读取
|
||||||
is requesting
|
<span id="user-name">{{ .user.Name }}</span>
|
||||||
<br />
|
的信息
|
||||||
access to account
|
</p>
|
||||||
<span class="user-name">{{ .user.name }}</span>
|
|
||||||
with the following scopes:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul class="scopes">
|
<p class="remember">
|
||||||
{{ $user := .user }}
|
<input type="checkbox" id="remember" name="remember" value="true" />
|
||||||
{{ range $scope := .scopes }}
|
<label for="remember">记住我的选择</label>
|
||||||
<li>
|
</p>
|
||||||
<details>
|
|
||||||
<summary>{{ $scope }}</summary>
|
|
||||||
<pre>{{ index $user $scope }}</pre>
|
|
||||||
</details>
|
|
||||||
</li>
|
|
||||||
{{ end }}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{{ if or .clientPolicy .clientTos }}
|
|
||||||
<ul class="app-terms">
|
|
||||||
{{ if .clientPolicy }}
|
|
||||||
<li><a href="{{ .clientPolicy }}" target="_blank" referrerpolicy="no-referrer">Client Usage Policy</a></li>
|
|
||||||
{{ end }}
|
|
||||||
{{ if .clientTos }}
|
|
||||||
<li><a href="{{ .clientTos }}" target="_blank" referrerpolicy="no-referrer">Terms of Service</a></li>
|
|
||||||
{{ end }}
|
|
||||||
</ul>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="consent-notice">
|
|
||||||
<p>Accept the request?</p>
|
|
||||||
|
|
||||||
<p class="remember">
|
|
||||||
<input type="checkbox" id="remember" name="remember" value="true" />
|
|
||||||
<label for="remember">Remember my choice</label>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="buttons">
|
<p class="buttons">
|
||||||
<button type="submit" name="action" value="reject" class="reject">Reject</button>
|
<button type="submit" name="action" value="reject" class="reject">拒绝</button>
|
||||||
<button type="submit" name="action" value="accept" class="accept">Accept</button>
|
<button type="submit" name="action" value="accept" class="accept">接受</button>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,30 +1,14 @@
|
||||||
{{ define "error.tmpl" }}
|
{{ define "error.tmpl" }}
|
||||||
<html lang="zh">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Error</title>
|
<title>出错了</title>
|
||||||
{{ template "head.tmpl" }}
|
{{ template "head.tmpl" }}
|
||||||
<style>
|
|
||||||
.error {
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px 0;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #282c34;
|
|
||||||
user-select: all;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div class="logo">
|
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48"><title>c-remove</title><g><path fill="#E86C60" d="M24,47C11.31787,47,1,36.68262,1,24S11.31787,1,24,1s23,10.31738,23,23S36.68213,47,24,47z"></path> <path fill="#FFFFFF" d="M25.41406,24l7.29297-7.29297c0.39062-0.39062,0.39062-1.02344,0-1.41406s-1.02344-0.39062-1.41406,0 L24,22.58594l-7.29297-7.29297c-0.39062-0.39062-1.02344-0.39062-1.41406,0s-0.39062,1.02344,0,1.41406L22.58594,24 l-7.29297,7.29297c-0.39062,0.39062-0.39062,1.02344,0,1.41406C15.48828,32.90234,15.74414,33,16,33 s0.51172-0.09766,0.70703-0.29297L24,25.41406l7.29297,7.29297C31.48828,32.90234,31.74414,33,32,33 s0.51172-0.09766,0.70703-0.29297c0.39062-0.39062,0.39062-1.02344,0-1.41406L25.41406,24z"></path></g></svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48">
|
<h1>发生了一些错误</h1>
|
||||||
<g>
|
<p>{{ .error }}</p>
|
||||||
<circle fill="#E86C60" cx="24" cy="24" r="24"></circle>
|
|
||||||
<path fill="#FFFFFF" d="M25.41406,24l7.29297-7.29297c0.39062-0.39062,0.39062-1.02344,0-1.41406s-1.02344-0.39062-1.41406,0 L24,22.58594l-7.29297-7.29297c-0.39062-0.39062-1.02344-0.39062-1.41406,0s-0.39062,1.02344,0,1.41406L22.58594,24 l-7.29297,7.29297c-0.39062,0.39062-0.39062,1.02344,0,1.41406C15.48828,32.90234,15.74414,33,16,33 s0.51172-0.09766,0.70703-0.29297L24,25.41406l7.29297,7.29297C31.48828,32.90234,31.74414,33,32,33 s0.51172-0.09766,0.70703-0.29297c0.39062-0.39062,0.39062-1.02344,0-1.41406L25.41406,24z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h1>An error happened</h1>
|
|
||||||
<p class="error">{{ .error }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: #282c34;
|
background: #282c34;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main {
|
#main {
|
||||||
|
@ -19,23 +13,12 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 80px 40px 40px 40px;
|
padding: 40px;
|
||||||
background-color: #21252b;
|
background-color: #21252b;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
margin: 0 40px;
|
margin: 0 40px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
position: absolute;
|
|
||||||
border: 10px solid #282c34;
|
|
||||||
top: -70px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo, .logo > * {
|
|
||||||
border-radius: 120px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{ define "index.tmpl" }}
|
{{ define "index.tmpl" }}
|
||||||
<html lang="zh">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>MiSSO</title>
|
<title>MiSSO</title>
|
||||||
{{ template "head.tmpl" }}
|
{{ template "head.tmpl" }}
|
||||||
|
@ -24,21 +24,13 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div class="logo">
|
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48"><title>access-key</title><rect data-element="frame" x="0" y="0" width="48" height="48" rx="48" ry="48" stroke="none" fill="#62b6e7"></rect><g transform="translate(9.600000000000001 9.600000000000001) scale(0.6)"><path d="M38,23a1,1,0,0,1-.707-.293l-6-6a1,1,0,0,1,0-1.414l8-8a1,1,0,0,1,1.414,0l6,6a1,1,0,0,1,0,1.414l-2,2a1,1,0,0,1-1.414,0L41,14.414,38.414,17l2.293,2.293a1,1,0,0,1,0,1.414l-2,2A1,1,0,0,1,38,23Z" fill="#eba40a"></path><path d="M44.061,3.939a1.5,1.5,0,0,0-2.122,0L17.923,27.956a10.027,10.027,0,1,0,2.121,2.121L44.061,6.061A1.5,1.5,0,0,0,44.061,3.939ZM12,43a7,7,0,1,1,4.914-11.978c.011.012.014.027.025.039s.027.014.039.025A6.995,6.995,0,0,1,12,43Z" fill="#ffd764"></path></g></svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48">
|
<h1>欢迎来到 MiSSO</h1>
|
||||||
<circle fill="#62b6e7" cx="24" cy="24" r="24"></circle>
|
|
||||||
<g transform="translate(9.600000000000001 9.600000000000001) scale(0.6)">
|
|
||||||
<path d="M38,23a1,1,0,0,1-.707-.293l-6-6a1,1,0,0,1,0-1.414l8-8a1,1,0,0,1,1.414,0l6,6a1,1,0,0,1,0,1.414l-2,2a1,1,0,0,1-1.414,0L41,14.414,38.414,17l2.293,2.293a1,1,0,0,1,0,1.414l-2,2A1,1,0,0,1,38,23Z" fill="#eba40a"></path>
|
|
||||||
<path d="M44.061,3.939a1.5,1.5,0,0,0-2.122,0L17.923,27.956a10.027,10.027,0,1,0,2.121,2.121L44.061,6.061A1.5,1.5,0,0,0,44.061,3.939ZM12,43a7,7,0,1,1,4.914-11.978c.011.012.014.027.025.039s.027.014.039.025A6.995,6.995,0,0,1,12,43Z" fill="#ffd764"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h1>Welcome to MiSSO</h1>
|
|
||||||
<p>
|
<p>
|
||||||
You should not access this page directly.
|
直接访问这里好像不太对哦
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a class="primary" href="https://docs.nya.one/peripheral/misso/">Documentation</a>
|
<a class="primary" href="https://docs.nya.one/peripheral/misso/">查看文档</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -14,10 +14,4 @@ type Config struct {
|
||||||
Hydra struct {
|
Hydra struct {
|
||||||
AdminUrl string `yaml:"admin_url"`
|
AdminUrl string `yaml:"admin_url"`
|
||||||
} `yaml:"hydra"`
|
} `yaml:"hydra"`
|
||||||
Time struct {
|
|
||||||
RequestValid int64 `yaml:"request_valid"`
|
|
||||||
LoginRemember int64 `yaml:"login_remember"`
|
|
||||||
ConsentRemember int64 `yaml:"consent_remember"`
|
|
||||||
UserinfoCache int64 `yaml:"userinfo_cache"`
|
|
||||||
} `yaml:"time"`
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,186 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
type MisskeyUserBase struct {
|
import "time"
|
||||||
Username string `json:"username"`
|
|
||||||
// Ignore other fields
|
type MisskeyUser struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Host interface{} `json:"host"`
|
||||||
|
AvatarUrl string `json:"avatarUrl"`
|
||||||
|
AvatarBlurhash string `json:"avatarBlurhash"`
|
||||||
|
IsBot bool `json:"isBot"`
|
||||||
|
IsCat bool `json:"isCat"`
|
||||||
|
OnlineStatus string `json:"onlineStatus"`
|
||||||
|
Url interface{} `json:"url"`
|
||||||
|
Uri interface{} `json:"uri"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
LastFetchedAt interface{} `json:"lastFetchedAt"`
|
||||||
|
BannerUrl string `json:"bannerUrl"`
|
||||||
|
BannerBlurhash string `json:"bannerBlurhash"`
|
||||||
|
IsLocked bool `json:"isLocked"`
|
||||||
|
IsSilenced bool `json:"isSilenced"`
|
||||||
|
IsSuspended bool `json:"isSuspended"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Birthday string `json:"birthday"`
|
||||||
|
Lang string `json:"lang"`
|
||||||
|
Fields []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
} `json:"fields"`
|
||||||
|
FollowersCount int `json:"followersCount"`
|
||||||
|
FollowingCount int `json:"followingCount"`
|
||||||
|
NotesCount int `json:"notesCount"`
|
||||||
|
PinnedNoteIds []string `json:"pinnedNoteIds"`
|
||||||
|
PinnedNotes []struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
User struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Host interface{} `json:"host"`
|
||||||
|
AvatarUrl string `json:"avatarUrl"`
|
||||||
|
AvatarBlurhash string `json:"avatarBlurhash"`
|
||||||
|
IsBot bool `json:"isBot"`
|
||||||
|
IsCat bool `json:"isCat"`
|
||||||
|
OnlineStatus string `json:"onlineStatus"`
|
||||||
|
} `json:"user"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Cw *string `json:"cw"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
LocalOnly bool `json:"localOnly"`
|
||||||
|
RenoteCount int `json:"renoteCount"`
|
||||||
|
RepliesCount int `json:"repliesCount"`
|
||||||
|
Reactions map[string]int `json:"reactions"`
|
||||||
|
FileIds []string `json:"fileIds"`
|
||||||
|
Files []struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Md5 string `json:"md5"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
IsSensitive bool `json:"isSensitive"`
|
||||||
|
Blurhash string `json:"blurhash"`
|
||||||
|
Properties struct {
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
} `json:"properties"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
ThumbnailUrl string `json:"thumbnailUrl"`
|
||||||
|
Comment interface{} `json:"comment"`
|
||||||
|
FolderId interface{} `json:"folderId"`
|
||||||
|
Folder interface{} `json:"folder"`
|
||||||
|
UserId interface{} `json:"userId"`
|
||||||
|
User interface{} `json:"user"`
|
||||||
|
} `json:"files"`
|
||||||
|
ReplyId interface{} `json:"replyId"`
|
||||||
|
RenoteId interface{} `json:"renoteId"`
|
||||||
|
MyReaction string `json:"myReaction,omitempty"`
|
||||||
|
} `json:"pinnedNotes"`
|
||||||
|
PinnedPageId string `json:"pinnedPageId"`
|
||||||
|
PinnedPage struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
User struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Host interface{} `json:"host"`
|
||||||
|
AvatarUrl string `json:"avatarUrl"`
|
||||||
|
AvatarBlurhash string `json:"avatarBlurhash"`
|
||||||
|
IsBot bool `json:"isBot"`
|
||||||
|
IsCat bool `json:"isCat"`
|
||||||
|
OnlineStatus string `json:"onlineStatus"`
|
||||||
|
} `json:"user"`
|
||||||
|
Content []struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
} `json:"content"`
|
||||||
|
Variables []interface{} `json:"variables"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
HideTitleWhenPinned bool `json:"hideTitleWhenPinned"`
|
||||||
|
AlignCenter bool `json:"alignCenter"`
|
||||||
|
Font string `json:"font"`
|
||||||
|
Script string `json:"script"`
|
||||||
|
EyeCatchingImageId string `json:"eyeCatchingImageId"`
|
||||||
|
EyeCatchingImage struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Md5 string `json:"md5"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
IsSensitive bool `json:"isSensitive"`
|
||||||
|
Blurhash string `json:"blurhash"`
|
||||||
|
Properties struct {
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
} `json:"properties"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
ThumbnailUrl string `json:"thumbnailUrl"`
|
||||||
|
Comment interface{} `json:"comment"`
|
||||||
|
FolderId interface{} `json:"folderId"`
|
||||||
|
Folder interface{} `json:"folder"`
|
||||||
|
UserId interface{} `json:"userId"`
|
||||||
|
User interface{} `json:"user"`
|
||||||
|
} `json:"eyeCatchingImage"`
|
||||||
|
AttachedFiles []interface{} `json:"attachedFiles"`
|
||||||
|
LikedCount int `json:"likedCount"`
|
||||||
|
IsLiked bool `json:"isLiked"`
|
||||||
|
} `json:"pinnedPage"`
|
||||||
|
PublicReactions bool `json:"publicReactions"`
|
||||||
|
FfVisibility string `json:"ffVisibility"`
|
||||||
|
TwoFactorEnabled bool `json:"twoFactorEnabled"`
|
||||||
|
UsePasswordLessLogin bool `json:"usePasswordLessLogin"`
|
||||||
|
SecurityKeys bool `json:"securityKeys"`
|
||||||
|
Roles []struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsModerator bool `json:"isModerator"`
|
||||||
|
IsAdministrator bool `json:"isAdministrator"`
|
||||||
|
} `json:"roles"`
|
||||||
|
AvatarId string `json:"avatarId"`
|
||||||
|
BannerId string `json:"bannerId"`
|
||||||
|
IsModerator bool `json:"isModerator"`
|
||||||
|
IsAdmin bool `json:"isAdmin"`
|
||||||
|
InjectFeaturedNote bool `json:"injectFeaturedNote"`
|
||||||
|
ReceiveAnnouncementEmail bool `json:"receiveAnnouncementEmail"`
|
||||||
|
AlwaysMarkNsfw bool `json:"alwaysMarkNsfw"`
|
||||||
|
AutoSensitive bool `json:"autoSensitive"`
|
||||||
|
CarefulBot bool `json:"carefulBot"`
|
||||||
|
AutoAcceptFollowed bool `json:"autoAcceptFollowed"`
|
||||||
|
NoCrawle bool `json:"noCrawle"`
|
||||||
|
IsExplorable bool `json:"isExplorable"`
|
||||||
|
IsDeleted bool `json:"isDeleted"`
|
||||||
|
HideOnlineStatus bool `json:"hideOnlineStatus"`
|
||||||
|
HasUnreadSpecifiedNotes bool `json:"hasUnreadSpecifiedNotes"`
|
||||||
|
HasUnreadMentions bool `json:"hasUnreadMentions"`
|
||||||
|
HasUnreadAnnouncement bool `json:"hasUnreadAnnouncement"`
|
||||||
|
HasUnreadAntenna bool `json:"hasUnreadAntenna"`
|
||||||
|
HasUnreadChannel bool `json:"hasUnreadChannel"`
|
||||||
|
HasUnreadMessagingMessage bool `json:"hasUnreadMessagingMessage"`
|
||||||
|
HasUnreadNotification bool `json:"hasUnreadNotification"`
|
||||||
|
HasPendingReceivedFollowRequest bool `json:"hasPendingReceivedFollowRequest"`
|
||||||
|
Integrations struct {
|
||||||
|
} `json:"integrations"`
|
||||||
|
MutedWords [][]string `json:"mutedWords"`
|
||||||
|
MutedInstances []string `json:"mutedInstances"`
|
||||||
|
MutingNotificationTypes []string `json:"mutingNotificationTypes"`
|
||||||
|
EmailNotificationTypes []string `json:"emailNotificationTypes"`
|
||||||
|
ShowTimelineReplies bool `json:"showTimelineReplies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MisskeyUser = map[string]any // Just raw json map
|
// TODO: Find a better way to split necessary fields and additional fields
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func GetUserinfo(subject string) (*types.MisskeyUser, error) {
|
func GetUserinfo(subject string) (*types.MisskeyUser, error) {
|
||||||
// Check cache key
|
// Check cache key
|
||||||
global.Logger.Debugf("Checking userinfo cache...")
|
global.Logger.Debugf("Checking userinfo cache...")
|
||||||
userinfoCacheKey := fmt.Sprintf(consts.REDIS_KEY_USER_INFO, subject)
|
userinfoCacheKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_USER_INFO, subject)
|
||||||
exist, err := global.Redis.Exists(context.Background(), userinfoCacheKey).Result()
|
exist, err := global.Redis.Exists(context.Background(), userinfoCacheKey).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to check userinfo exist status with error: %v", err)
|
global.Logger.Errorf("Failed to check userinfo exist status with error: %v", err)
|
||||||
|
@ -38,7 +38,7 @@ func GetUserinfo(subject string) (*types.MisskeyUser, error) {
|
||||||
|
|
||||||
// Fallback to get info directly, we need user's access token.
|
// 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.")
|
global.Logger.Debugf("No cached userinfo found (or valid), trying to get latest response.")
|
||||||
accessTokenCacheKey := fmt.Sprintf(consts.REDIS_KEY_USER_ACCESS_TOKEN, subject)
|
accessTokenCacheKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_ACCESS_TOKEN, subject)
|
||||||
accessToken, err := global.Redis.Get(context.Background(), accessTokenCacheKey).Result()
|
accessToken, err := global.Redis.Get(context.Background(), accessTokenCacheKey).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to get user access token with error: %v", err)
|
global.Logger.Errorf("Failed to get user access token with error: %v", err)
|
||||||
|
@ -52,9 +52,6 @@ func GetUserinfo(subject string) (*types.MisskeyUser, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append subject as email to userinfo
|
|
||||||
(*userinfo)["email"] = subject
|
|
||||||
|
|
||||||
// Save userinfo into redis
|
// Save userinfo into redis
|
||||||
_ = SaveUserinfo(subject, userinfo) // Ignore errors
|
_ = SaveUserinfo(subject, userinfo) // Ignore errors
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"misso/config"
|
|
||||||
"misso/consts"
|
"misso/consts"
|
||||||
"misso/global"
|
"misso/global"
|
||||||
"misso/types"
|
"misso/types"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func SaveUserinfo(subject string, userinfo *types.MisskeyUser) error {
|
func SaveUserinfo(subject string, userinfo *types.MisskeyUser) error {
|
||||||
|
@ -17,8 +15,8 @@ func SaveUserinfo(subject string, userinfo *types.MisskeyUser) error {
|
||||||
global.Logger.Errorf("Failed to parse accept context with error: %v", err)
|
global.Logger.Errorf("Failed to parse accept context with error: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sessUserInfoKey := fmt.Sprintf(consts.REDIS_KEY_USER_INFO, subject)
|
sessUserInfoKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_USER_INFO, subject)
|
||||||
err = global.Redis.Set(context.Background(), sessUserInfoKey, userinfoBytes, time.Duration(config.Config.Time.UserinfoCache)*time.Second).Err()
|
err = global.Redis.Set(context.Background(), sessUserInfoKey, userinfoBytes, consts.TIME_USERINFO_CACHE).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Errorf("Failed to save session user info into redis with error: %v", err)
|
global.Logger.Errorf("Failed to save session user info into redis with error: %v", err)
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue