feat(refractor): use misskey userinfo key as scope
...and UI tweaks 🎨
This commit is contained in:
parent
6aa0db6d0e
commit
a99045a034
|
@ -1,8 +1,8 @@
|
|||
package consts
|
||||
|
||||
const (
|
||||
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_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
|
||||
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_USER_ACCESS_TOKEN = "misso:user:token:%s" // Subject, access token as value
|
||||
REDIS_KEY_USER_INFO = "misso:user:info:%s" // Subject, user info as value
|
||||
)
|
||||
|
|
|
@ -5,8 +5,8 @@ import "time"
|
|||
const (
|
||||
TIME_REQUEST_VALID = 1 * time.Hour
|
||||
|
||||
TIME_LOGIN_REMEMBER = 10 * time.Minute
|
||||
TIME_LOGIN_REMEMBER = 10 * time.Minute
|
||||
TIME_CONSENT_REMEMBER = 0 // Forever
|
||||
|
||||
TIME_USERINFO_CACHE = 10 * time.Minute
|
||||
TIME_USERINFO_CACHE = 1 * time.Hour
|
||||
)
|
||||
|
|
|
@ -56,8 +56,8 @@ func ConsentCheck(ctx *gin.Context) {
|
|||
// Generate CSRF token
|
||||
global.Logger.Debugf("Generating CSRF token...")
|
||||
csrf := utils.RandString(32)
|
||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, csrf)
|
||||
err := global.Redis.Set(context.Background(), sessKey, oauth2challenge, consts.TIME_REQUEST_VALID).Err()
|
||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, oauth2challenge)
|
||||
err := global.Redis.Set(context.Background(), sessKey, csrf, consts.TIME_REQUEST_VALID).Err()
|
||||
if err != nil {
|
||||
global.Logger.Errorf("Failed to save csrf into redis with error: %v", err)
|
||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||
|
@ -69,7 +69,7 @@ func ConsentCheck(ctx *gin.Context) {
|
|||
// Retrieve context
|
||||
global.Logger.Debugf("Retrieving context...")
|
||||
|
||||
userinfoCtx, err := utils.GetUserinfo(*consentReq.Subject)
|
||||
userinfo, err := utils.GetUserinfo(*consentReq.Subject)
|
||||
if err != nil {
|
||||
global.Logger.Errorf("Failed to retrieve userinfo with error: %v", err)
|
||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||
|
@ -81,9 +81,10 @@ func ConsentCheck(ctx *gin.Context) {
|
|||
// Show the consent UI
|
||||
global.Logger.Debugf("Rendering consent UI...")
|
||||
templateFields := gin.H{
|
||||
"user": *userinfoCtx,
|
||||
"user": *userinfo,
|
||||
"challenge": oauth2challenge,
|
||||
"csrf": csrf,
|
||||
"scopes": consentReq.RequestedScope,
|
||||
}
|
||||
|
||||
if consentReq.Client.LogoUri != nil && *consentReq.Client.LogoUri != "" {
|
||||
|
@ -94,6 +95,12 @@ func ConsentCheck(ctx *gin.Context) {
|
|||
} else {
|
||||
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)
|
||||
|
||||
global.Logger.Debugf("User should now see Consent UI.")
|
||||
|
|
|
@ -12,9 +12,10 @@ import (
|
|||
)
|
||||
|
||||
type ConsentConfirmRequest struct {
|
||||
CSRF string `form:"_csrf"`
|
||||
Remember bool `form:"remember"`
|
||||
Action string `form:"action"`
|
||||
CSRF string `form:"_csrf"`
|
||||
Challenge string `form:"challenge"`
|
||||
Remember bool `form:"remember"`
|
||||
Action string `form:"action"`
|
||||
}
|
||||
|
||||
func ConsentConfirm(ctx *gin.Context) {
|
||||
|
@ -32,16 +33,23 @@ func ConsentConfirm(ctx *gin.Context) {
|
|||
|
||||
// Validate CSRF
|
||||
global.Logger.Debugf("Validating CSRF...")
|
||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, req.CSRF)
|
||||
oauth2challenge, err := global.Redis.Get(context.Background(), sessKey).Result()
|
||||
sessKey := fmt.Sprintf(consts.REDIS_KEY_CONSENT_CSRF, req.Challenge)
|
||||
csrfSession, err := global.Redis.Get(context.Background(), sessKey).Result()
|
||||
if err != nil {
|
||||
global.Logger.Errorf("Failed to get csrf from redis with error: %v", err)
|
||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||
"error": "Failed to get csrf",
|
||||
})
|
||||
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
|
||||
global.Redis.Del(context.Background(), sessKey)
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"misso/consts"
|
||||
"misso/global"
|
||||
"misso/misskey"
|
||||
"misso/utils"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
@ -62,9 +61,10 @@ func MisskeyAuthCallback(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
userid := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance)
|
||||
// Save user key
|
||||
userIdentifier := fmt.Sprintf("%s@%s", usermeta.User.Username, config.Config.Misskey.Instance)
|
||||
|
||||
sessAccessTokenKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_ACCESS_TOKEN, userid)
|
||||
sessAccessTokenKey := fmt.Sprintf(consts.REDIS_KEY_USER_ACCESS_TOKEN, userIdentifier)
|
||||
err = global.Redis.Set(context.Background(), sessAccessTokenKey, usermeta.AccessToken, 0).Err()
|
||||
if err != nil {
|
||||
global.Logger.Errorf("Failed to save session access token into redis with error: %v", err)
|
||||
|
@ -74,21 +74,11 @@ func MisskeyAuthCallback(ctx *gin.Context) {
|
|||
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...")
|
||||
remember := true
|
||||
rememberFor := int64(consts.TIME_LOGIN_REMEMBER / time.Second)
|
||||
acceptReq, _, err := global.Hydra.Admin.OAuth2Api.AcceptOAuth2LoginRequest(context.Background()).LoginChallenge(oauth2challenge).AcceptOAuth2LoginRequest(client.AcceptOAuth2LoginRequest{
|
||||
Subject: userid,
|
||||
Subject: userIdentifier,
|
||||
Remember: &remember,
|
||||
RememberFor: &rememberFor,
|
||||
}).Execute()
|
||||
|
|
|
@ -4,17 +4,11 @@ import (
|
|||
"context"
|
||||
"github.com/gin-gonic/gin"
|
||||
"misso/global"
|
||||
"misso/types"
|
||||
"misso/utils"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UserinfoResponse struct {
|
||||
types.MisskeyUser
|
||||
EMail string `json:"email"`
|
||||
}
|
||||
|
||||
func UserInfo(ctx *gin.Context) {
|
||||
// Get token from header
|
||||
accessToken := strings.Replace(ctx.GetHeader("Authorization"), "Bearer ", "", 1)
|
||||
|
@ -45,7 +39,7 @@ func UserInfo(ctx *gin.Context) {
|
|||
|
||||
// Return user info
|
||||
global.Logger.Debugf("Retrieving context...")
|
||||
userinfoCtx, err := utils.GetUserinfo(*tokenInfo.Sub)
|
||||
userinfo, err := utils.GetUserinfo(*tokenInfo.Sub)
|
||||
if err != nil {
|
||||
global.Logger.Errorf("Failed to retrieve userinfo with error: %v", err)
|
||||
ctx.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||
|
@ -54,9 +48,19 @@ func UserInfo(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, UserinfoResponse{
|
||||
MisskeyUser: *userinfoCtx,
|
||||
EMail: *tokenInfo.Sub,
|
||||
})
|
||||
userinfoRes := gin.H{} // map[string]interface{}
|
||||
|
||||
// Get scopes
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, userinfoRes)
|
||||
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ type AuthSessionUserkey_Request struct {
|
|||
}
|
||||
|
||||
type AuthSessionUserkey_Response struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
User types.MisskeyUser `json:"user"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
User types.MisskeyUserBase `json:"user"`
|
||||
}
|
||||
|
||||
func GetUserkey(token string) (*AuthSessionUserkey_Response, error) {
|
||||
|
|
|
@ -1,11 +1,58 @@
|
|||
{{ define "consent.tmpl" }}
|
||||
<html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<title>授权确认</title>
|
||||
{{ template "head.tmpl" }}
|
||||
<style>
|
||||
ul.scopes,
|
||||
ul.app-terms {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
ul.scopes {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
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 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
p.remember, p.remember > * {
|
||||
cursor: pointer;
|
||||
|
@ -59,7 +106,7 @@
|
|||
background: #15803d;
|
||||
}
|
||||
|
||||
#app-name, #user-name {
|
||||
.app-name, .user-name {
|
||||
color: #62b6e7;
|
||||
}
|
||||
</style>
|
||||
|
@ -67,25 +114,65 @@
|
|||
<body>
|
||||
<form id="main" action="/consent" method="POST">
|
||||
<input type="hidden" name="_csrf" value="{{ .csrf }}" />
|
||||
<input type="hidden" name="challenge" value="{{ .challenge }}" />
|
||||
|
||||
{{ if .logo }}
|
||||
<img src="{{ .logo }}" alt="{{ .clientName }}" width="120" height="120" />
|
||||
{{ 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>
|
||||
{{ end }}
|
||||
<div class="logo">
|
||||
{{ if .logo }}
|
||||
<img src="{{ .logo }}" alt="{{ .clientName }}" width="120" height="120" />
|
||||
{{ else }}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48">
|
||||
<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>
|
||||
|
||||
<p>
|
||||
应用程序
|
||||
<span id="app-name">{{ .clientName }}</span>
|
||||
正请求读取
|
||||
<span id="user-name">{{ .user.Name }}</span>
|
||||
的信息
|
||||
</p>
|
||||
<div>
|
||||
<p>
|
||||
应用程序
|
||||
<span class="app-name">{{ .clientName }}</span>
|
||||
正请求
|
||||
<br />
|
||||
读取
|
||||
<span class="user-name">{{ .user.name }}</span>
|
||||
的这些信息:
|
||||
</p>
|
||||
|
||||
<p class="remember">
|
||||
<input type="checkbox" id="remember" name="remember" value="true" />
|
||||
<label for="remember">记住我的选择</label>
|
||||
</p>
|
||||
<ul class="scopes">
|
||||
{{ $user := .user }}
|
||||
{{ range $scope := .scopes }}
|
||||
<li>
|
||||
<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">使用政策</a></li>
|
||||
{{ end }}
|
||||
{{ if .clientTos }}
|
||||
<li><a href="{{ .clientTos }}" target="_blank" referrerpolicy="no-referrer">服务条款</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="consent-notice">
|
||||
<p>是否接受该请求?</p>
|
||||
|
||||
<p class="remember">
|
||||
<input type="checkbox" id="remember" name="remember" value="true" />
|
||||
<label for="remember">记住我的选择</label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="buttons">
|
||||
<button type="submit" name="action" value="reject" class="reject">拒绝</button>
|
||||
|
|
|
@ -1,14 +1,30 @@
|
|||
{{ define "error.tmpl" }}
|
||||
<html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<title>出错了</title>
|
||||
{{ template "head.tmpl" }}
|
||||
<style>
|
||||
.error {
|
||||
width: 100%;
|
||||
padding: 20px 0;
|
||||
border-radius: 6px;
|
||||
background-color: #282c34;
|
||||
user-select: all;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<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>
|
||||
<div class="logo">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48">
|
||||
<g>
|
||||
<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>发生了一些错误</h1>
|
||||
<p>{{ .error }}</p>
|
||||
<p class="error">{{ .error }}</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #282c34;
|
||||
background-color: #282c34;
|
||||
}
|
||||
|
||||
#main {
|
||||
|
@ -13,12 +13,23 @@
|
|||
text-align: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
padding: 40px;
|
||||
padding: 80px 40px 40px 40px;
|
||||
background-color: #21252b;
|
||||
border-radius: 12px;
|
||||
margin: 0 40px;
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
word-break: break-word;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: absolute;
|
||||
border: 10px solid #282c34;
|
||||
top: -70px;
|
||||
}
|
||||
|
||||
.logo, .logo > * {
|
||||
border-radius: 120px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{ define "index.tmpl" }}
|
||||
<html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<title>MiSSO</title>
|
||||
{{ template "head.tmpl" }}
|
||||
|
@ -24,7 +24,15 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<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>
|
||||
<div class="logo">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 48 48">
|
||||
<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>欢迎来到 MiSSO</h1>
|
||||
<p>
|
||||
直接访问这里好像不太对哦
|
||||
|
|
|
@ -1,186 +1,8 @@
|
|||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
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 MisskeyUserBase struct {
|
||||
Username string `json:"username"`
|
||||
// Ignore other fields
|
||||
}
|
||||
|
||||
// TODO: Find a better way to split necessary fields and additional fields
|
||||
type MisskeyUser = map[string]interface{} // Just raw json map
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
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)
|
||||
userinfoCacheKey := fmt.Sprintf(consts.REDIS_KEY_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)
|
||||
|
@ -38,7 +38,7 @@ func GetUserinfo(subject string) (*types.MisskeyUser, error) {
|
|||
|
||||
// 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)
|
||||
accessTokenCacheKey := fmt.Sprintf(consts.REDIS_KEY_USER_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)
|
||||
|
@ -52,6 +52,9 @@ func GetUserinfo(subject string) (*types.MisskeyUser, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Append subject as email to userinfo
|
||||
(*userinfo)["email"] = subject
|
||||
|
||||
// Save userinfo into redis
|
||||
_ = SaveUserinfo(subject, userinfo) // Ignore errors
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ func SaveUserinfo(subject string, userinfo *types.MisskeyUser) error {
|
|||
global.Logger.Errorf("Failed to parse accept context with error: %v", err)
|
||||
return err
|
||||
}
|
||||
sessUserInfoKey := fmt.Sprintf(consts.REDIS_KEY_SHARE_USER_INFO, subject)
|
||||
sessUserInfoKey := fmt.Sprintf(consts.REDIS_KEY_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)
|
||||
|
|
Loading…
Reference in a new issue