Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
12de1fac
authored
Nov 10, 2024
by
seefs001
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: integrate Linux DO OAuth authentication
parent
f66198b9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
451 additions
and
16 deletions
+451
-16
common/constants.go
+4
-0
controller/linuxdo.go
+265
-0
controller/misc.go
+2
-0
controller/option.go
+8
-0
i18n/en.json
+0
-0
model/option.go
+7
-0
model/user.go
+15
-0
router/api-router.go
+1
-0
web/src/components/LoginForm.js
+26
-7
web/src/components/PersonalSetting.js
+31
-3
web/src/components/SystemSetting.js
+78
-6
web/src/components/utils.js
+8
-0
web/src/pages/Home/index.js
+6
-0
No files found.
common/constants.go
View file @
12de1fac
...
@@ -41,6 +41,7 @@ var PasswordLoginEnabled = true
...
@@ -41,6 +41,7 @@ var PasswordLoginEnabled = true
var
PasswordRegisterEnabled
=
true
var
PasswordRegisterEnabled
=
true
var
EmailVerificationEnabled
=
false
var
EmailVerificationEnabled
=
false
var
GitHubOAuthEnabled
=
false
var
GitHubOAuthEnabled
=
false
var
LinuxDOOAuthEnabled
=
false
var
WeChatAuthEnabled
=
false
var
WeChatAuthEnabled
=
false
var
TelegramOAuthEnabled
=
false
var
TelegramOAuthEnabled
=
false
var
TurnstileCheckEnabled
=
false
var
TurnstileCheckEnabled
=
false
...
@@ -75,6 +76,9 @@ var SMTPToken = ""
...
@@ -75,6 +76,9 @@ var SMTPToken = ""
var
GitHubClientId
=
""
var
GitHubClientId
=
""
var
GitHubClientSecret
=
""
var
GitHubClientSecret
=
""
var
LinuxDOClientId
=
""
var
LinuxDOClientSecret
=
""
var
WeChatServerAddress
=
""
var
WeChatServerAddress
=
""
var
WeChatServerToken
=
""
var
WeChatServerToken
=
""
var
WeChatAccountQRCodeImageURL
=
""
var
WeChatAccountQRCodeImageURL
=
""
...
...
controller/linuxdo.go
0 → 100644
View file @
12de1fac
package
controller
import
(
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"one-api/common"
"one-api/model"
"strconv"
"strings"
"time"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
type
LinuxdoUser
struct
{
Id
int
`json:"id"`
Username
string
`json:"username"`
Name
string
`json:"name"`
Active
bool
`json:"active"`
TrustLevel
int
`json:"trust_level"`
Silenced
bool
`json:"silenced"`
}
func
LinuxDoBind
(
c
*
gin
.
Context
)
{
if
!
common
.
LinuxDOOAuthEnabled
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"管理员未开启通过 Linux DO 登录以及注册"
,
})
return
}
code
:=
c
.
Query
(
"code"
)
linuxdoUser
,
err
:=
getLinuxdoUserInfoByCode
(
code
,
c
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
user
:=
model
.
User
{
LinuxDOId
:
strconv
.
Itoa
(
linuxdoUser
.
Id
),
}
if
model
.
IsLinuxDOIdAlreadyTaken
(
user
.
LinuxDOId
)
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"该 Linux DO 账户已被绑定"
,
})
return
}
session
:=
sessions
.
Default
(
c
)
id
:=
session
.
Get
(
"id"
)
user
.
Id
=
id
.
(
int
)
err
=
user
.
FillUserById
()
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
user
.
LinuxDOId
=
strconv
.
Itoa
(
linuxdoUser
.
Id
)
err
=
user
.
Update
(
false
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
"bind"
,
})
}
func
getLinuxdoUserInfoByCode
(
code
string
,
c
*
gin
.
Context
)
(
*
LinuxdoUser
,
error
)
{
if
code
==
""
{
return
nil
,
errors
.
New
(
"invalid code"
)
}
// Get access token using Basic auth
tokenEndpoint
:=
"https://connect.linux.do/oauth2/token"
credentials
:=
common
.
LinuxDOClientId
+
":"
+
common
.
LinuxDOClientSecret
basicAuth
:=
"Basic "
+
base64
.
StdEncoding
.
EncodeToString
([]
byte
(
credentials
))
// Get redirect URI from request
scheme
:=
"http"
if
c
.
Request
.
TLS
!=
nil
{
scheme
=
"https"
}
redirectURI
:=
fmt
.
Sprintf
(
"%s://%s/api/oauth/linuxdo"
,
scheme
,
c
.
Request
.
Host
)
data
:=
url
.
Values
{}
data
.
Set
(
"grant_type"
,
"authorization_code"
)
data
.
Set
(
"code"
,
code
)
data
.
Set
(
"redirect_uri"
,
redirectURI
)
req
,
err
:=
http
.
NewRequest
(
"POST"
,
tokenEndpoint
,
strings
.
NewReader
(
data
.
Encode
()))
if
err
!=
nil
{
return
nil
,
err
}
req
.
Header
.
Set
(
"Authorization"
,
basicAuth
)
req
.
Header
.
Set
(
"Content-Type"
,
"application/x-www-form-urlencoded"
)
req
.
Header
.
Set
(
"Accept"
,
"application/json"
)
client
:=
http
.
Client
{
Timeout
:
5
*
time
.
Second
}
res
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
return
nil
,
errors
.
New
(
"failed to connect to Linux DO server"
)
}
defer
res
.
Body
.
Close
()
var
tokenRes
struct
{
AccessToken
string
`json:"access_token"`
Message
string
`json:"message"`
}
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
tokenRes
);
err
!=
nil
{
return
nil
,
err
}
if
tokenRes
.
AccessToken
==
""
{
return
nil
,
fmt
.
Errorf
(
"failed to get access token: %s"
,
tokenRes
.
Message
)
}
// Get user info
userEndpoint
:=
"https://connect.linux.do/api/user"
req
,
err
=
http
.
NewRequest
(
"GET"
,
userEndpoint
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
req
.
Header
.
Set
(
"Authorization"
,
"Bearer "
+
tokenRes
.
AccessToken
)
req
.
Header
.
Set
(
"Accept"
,
"application/json"
)
res2
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
return
nil
,
errors
.
New
(
"failed to get user info from Linux DO"
)
}
defer
res2
.
Body
.
Close
()
var
linuxdoUser
LinuxdoUser
if
err
:=
json
.
NewDecoder
(
res2
.
Body
)
.
Decode
(
&
linuxdoUser
);
err
!=
nil
{
return
nil
,
err
}
if
linuxdoUser
.
Id
==
0
{
return
nil
,
errors
.
New
(
"invalid user info returned"
)
}
return
&
linuxdoUser
,
nil
}
func
LinuxdoOAuth
(
c
*
gin
.
Context
)
{
session
:=
sessions
.
Default
(
c
)
errorCode
:=
c
.
Query
(
"error"
)
if
errorCode
!=
""
{
errorDescription
:=
c
.
Query
(
"error_description"
)
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
errorDescription
,
})
return
}
state
:=
c
.
Query
(
"state"
)
if
state
==
""
||
session
.
Get
(
"oauth_state"
)
==
nil
||
state
!=
session
.
Get
(
"oauth_state"
)
.
(
string
)
{
c
.
JSON
(
http
.
StatusForbidden
,
gin
.
H
{
"success"
:
false
,
"message"
:
"state is empty or not same"
,
})
return
}
username
:=
session
.
Get
(
"username"
)
if
username
!=
nil
{
LinuxDoBind
(
c
)
return
}
if
!
common
.
LinuxDOOAuthEnabled
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"管理员未开启通过 Linux DO 登录以及注册"
,
})
return
}
code
:=
c
.
Query
(
"code"
)
linuxdoUser
,
err
:=
getLinuxdoUserInfoByCode
(
code
,
c
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
user
:=
model
.
User
{
LinuxDOId
:
strconv
.
Itoa
(
linuxdoUser
.
Id
),
}
// Check if user exists
if
model
.
IsLinuxDOIdAlreadyTaken
(
user
.
LinuxDOId
)
{
err
:=
user
.
FillUserByLinuxDOId
()
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
if
user
.
Id
==
0
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"用户已注销"
,
})
return
}
}
else
{
if
common
.
RegisterEnabled
{
user
.
Username
=
"linuxdo_"
+
strconv
.
Itoa
(
model
.
GetMaxUserId
()
+
1
)
user
.
DisplayName
=
linuxdoUser
.
Name
user
.
Role
=
common
.
RoleCommonUser
user
.
Status
=
common
.
UserStatusEnabled
if
err
:=
user
.
Insert
(
0
);
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
err
.
Error
(),
})
return
}
}
else
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"管理员关闭了新用户注册"
,
})
return
}
}
if
user
.
Status
!=
common
.
UserStatusEnabled
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"message"
:
"用户已被封禁"
,
"success"
:
false
,
})
return
}
setupLogin
(
&
user
,
c
)
}
controller/misc.go
View file @
12de1fac
...
@@ -38,6 +38,8 @@ func GetStatus(c *gin.Context) {
...
@@ -38,6 +38,8 @@ func GetStatus(c *gin.Context) {
"email_verification"
:
common
.
EmailVerificationEnabled
,
"email_verification"
:
common
.
EmailVerificationEnabled
,
"github_oauth"
:
common
.
GitHubOAuthEnabled
,
"github_oauth"
:
common
.
GitHubOAuthEnabled
,
"github_client_id"
:
common
.
GitHubClientId
,
"github_client_id"
:
common
.
GitHubClientId
,
"linuxdo_oauth"
:
common
.
LinuxDOOAuthEnabled
,
"linuxdo_client_id"
:
common
.
LinuxDOClientId
,
"telegram_oauth"
:
common
.
TelegramOAuthEnabled
,
"telegram_oauth"
:
common
.
TelegramOAuthEnabled
,
"telegram_bot_name"
:
common
.
TelegramBotName
,
"telegram_bot_name"
:
common
.
TelegramBotName
,
"system_name"
:
common
.
SystemName
,
"system_name"
:
common
.
SystemName
,
...
...
controller/option.go
View file @
12de1fac
...
@@ -50,6 +50,14 @@ func UpdateOption(c *gin.Context) {
...
@@ -50,6 +50,14 @@ func UpdateOption(c *gin.Context) {
})
})
return
return
}
}
case
"LinuxDOOAuthEnabled"
:
if
option
.
Value
==
"true"
&&
common
.
LinuxDOClientId
==
""
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"无法启用 LinuxDO OAuth,请先填入 LinuxDO Client Id 以及 LinuxDO Client Secret!"
,
})
return
}
case
"EmailDomainRestrictionEnabled"
:
case
"EmailDomainRestrictionEnabled"
:
if
option
.
Value
==
"true"
&&
len
(
common
.
EmailDomainWhitelist
)
==
0
{
if
option
.
Value
==
"true"
&&
len
(
common
.
EmailDomainWhitelist
)
==
0
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
...
...
i18n/en.json
View file @
12de1fac
This diff is collapsed.
Click to expand it.
model/option.go
View file @
12de1fac
...
@@ -31,6 +31,7 @@ func InitOptionMap() {
...
@@ -31,6 +31,7 @@ func InitOptionMap() {
common
.
OptionMap
[
"PasswordRegisterEnabled"
]
=
strconv
.
FormatBool
(
common
.
PasswordRegisterEnabled
)
common
.
OptionMap
[
"PasswordRegisterEnabled"
]
=
strconv
.
FormatBool
(
common
.
PasswordRegisterEnabled
)
common
.
OptionMap
[
"EmailVerificationEnabled"
]
=
strconv
.
FormatBool
(
common
.
EmailVerificationEnabled
)
common
.
OptionMap
[
"EmailVerificationEnabled"
]
=
strconv
.
FormatBool
(
common
.
EmailVerificationEnabled
)
common
.
OptionMap
[
"GitHubOAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
GitHubOAuthEnabled
)
common
.
OptionMap
[
"GitHubOAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
GitHubOAuthEnabled
)
common
.
OptionMap
[
"LinuxDOOAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
LinuxDOOAuthEnabled
)
common
.
OptionMap
[
"TelegramOAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
TelegramOAuthEnabled
)
common
.
OptionMap
[
"TelegramOAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
TelegramOAuthEnabled
)
common
.
OptionMap
[
"WeChatAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
WeChatAuthEnabled
)
common
.
OptionMap
[
"WeChatAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
WeChatAuthEnabled
)
common
.
OptionMap
[
"TurnstileCheckEnabled"
]
=
strconv
.
FormatBool
(
common
.
TurnstileCheckEnabled
)
common
.
OptionMap
[
"TurnstileCheckEnabled"
]
=
strconv
.
FormatBool
(
common
.
TurnstileCheckEnabled
)
...
@@ -175,6 +176,8 @@ func updateOptionMap(key string, value string) (err error) {
...
@@ -175,6 +176,8 @@ func updateOptionMap(key string, value string) (err error) {
common
.
EmailVerificationEnabled
=
boolValue
common
.
EmailVerificationEnabled
=
boolValue
case
"GitHubOAuthEnabled"
:
case
"GitHubOAuthEnabled"
:
common
.
GitHubOAuthEnabled
=
boolValue
common
.
GitHubOAuthEnabled
=
boolValue
case
"LinuxDOOAuthEnabled"
:
common
.
LinuxDOOAuthEnabled
=
boolValue
case
"WeChatAuthEnabled"
:
case
"WeChatAuthEnabled"
:
common
.
WeChatAuthEnabled
=
boolValue
common
.
WeChatAuthEnabled
=
boolValue
case
"TelegramOAuthEnabled"
:
case
"TelegramOAuthEnabled"
:
...
@@ -267,6 +270,10 @@ func updateOptionMap(key string, value string) (err error) {
...
@@ -267,6 +270,10 @@ func updateOptionMap(key string, value string) (err error) {
common
.
GitHubClientId
=
value
common
.
GitHubClientId
=
value
case
"GitHubClientSecret"
:
case
"GitHubClientSecret"
:
common
.
GitHubClientSecret
=
value
common
.
GitHubClientSecret
=
value
case
"LinuxDOClientId"
:
common
.
LinuxDOClientId
=
value
case
"LinuxDOClientSecret"
:
common
.
LinuxDOClientSecret
=
value
case
"Footer"
:
case
"Footer"
:
common
.
Footer
=
value
common
.
Footer
=
value
case
"SystemName"
:
case
"SystemName"
:
...
...
model/user.go
View file @
12de1fac
...
@@ -36,6 +36,7 @@ type User struct {
...
@@ -36,6 +36,7 @@ type User struct {
AffHistoryQuota
int
`json:"aff_history_quota" gorm:"type:int;default:0;column:aff_history"`
// 邀请历史额度
AffHistoryQuota
int
`json:"aff_history_quota" gorm:"type:int;default:0;column:aff_history"`
// 邀请历史额度
InviterId
int
`json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
InviterId
int
`json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
DeletedAt
gorm
.
DeletedAt
`gorm:"index"`
DeletedAt
gorm
.
DeletedAt
`gorm:"index"`
LinuxDOId
string
`json:"linux_do_id" gorm:"column:linux_do_id;index"`
}
}
func
(
user
*
User
)
GetAccessToken
()
string
{
func
(
user
*
User
)
GetAccessToken
()
string
{
...
@@ -537,3 +538,17 @@ func GetUsernameById(id int) (username string, err error) {
...
@@ -537,3 +538,17 @@ func GetUsernameById(id int) (username string, err error) {
err
=
DB
.
Model
(
&
User
{})
.
Where
(
"id = ?"
,
id
)
.
Select
(
"username"
)
.
Find
(
&
username
)
.
Error
err
=
DB
.
Model
(
&
User
{})
.
Where
(
"id = ?"
,
id
)
.
Select
(
"username"
)
.
Find
(
&
username
)
.
Error
return
username
,
err
return
username
,
err
}
}
func
IsLinuxDOIdAlreadyTaken
(
linuxDOId
string
)
bool
{
var
user
User
err
:=
DB
.
Unscoped
()
.
Where
(
"linux_do_id = ?"
,
linuxDOId
)
.
First
(
&
user
)
.
Error
return
!
errors
.
Is
(
err
,
gorm
.
ErrRecordNotFound
)
}
func
(
u
*
User
)
FillUserByLinuxDOId
()
error
{
if
u
.
LinuxDOId
==
""
{
return
errors
.
New
(
"linux do id is empty"
)
}
err
:=
DB
.
Where
(
"linux_do_id = ?"
,
u
.
LinuxDOId
)
.
First
(
u
)
.
Error
return
err
}
router/api-router.go
View file @
12de1fac
...
@@ -25,6 +25,7 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -25,6 +25,7 @@ func SetApiRouter(router *gin.Engine) {
apiRouter
.
GET
(
"/reset_password"
,
middleware
.
CriticalRateLimit
(),
middleware
.
TurnstileCheck
(),
controller
.
SendPasswordResetEmail
)
apiRouter
.
GET
(
"/reset_password"
,
middleware
.
CriticalRateLimit
(),
middleware
.
TurnstileCheck
(),
controller
.
SendPasswordResetEmail
)
apiRouter
.
POST
(
"/user/reset"
,
middleware
.
CriticalRateLimit
(),
controller
.
ResetPassword
)
apiRouter
.
POST
(
"/user/reset"
,
middleware
.
CriticalRateLimit
(),
controller
.
ResetPassword
)
apiRouter
.
GET
(
"/oauth/github"
,
middleware
.
CriticalRateLimit
(),
controller
.
GitHubOAuth
)
apiRouter
.
GET
(
"/oauth/github"
,
middleware
.
CriticalRateLimit
(),
controller
.
GitHubOAuth
)
apiRouter
.
GET
(
"/oauth/linuxdo"
,
middleware
.
CriticalRateLimit
(),
controller
.
LinuxdoOAuth
)
apiRouter
.
GET
(
"/oauth/state"
,
middleware
.
CriticalRateLimit
(),
controller
.
GenerateOAuthCode
)
apiRouter
.
GET
(
"/oauth/state"
,
middleware
.
CriticalRateLimit
(),
controller
.
GenerateOAuthCode
)
apiRouter
.
GET
(
"/oauth/wechat"
,
middleware
.
CriticalRateLimit
(),
controller
.
WeChatAuth
)
apiRouter
.
GET
(
"/oauth/wechat"
,
middleware
.
CriticalRateLimit
(),
controller
.
WeChatAuth
)
apiRouter
.
GET
(
"/oauth/wechat/bind"
,
middleware
.
CriticalRateLimit
(),
middleware
.
UserAuth
(),
controller
.
WeChatBind
)
apiRouter
.
GET
(
"/oauth/wechat/bind"
,
middleware
.
CriticalRateLimit
(),
middleware
.
UserAuth
(),
controller
.
WeChatBind
)
...
...
web/src/components/LoginForm.js
View file @
12de1fac
import
React
,
{
useContext
,
useEffect
,
useState
}
from
'react'
;
import
React
,
{
useContext
,
useEffect
,
useState
}
from
'react'
;
import
{
Link
,
useNavigate
,
useSearchParams
}
from
'react-router-dom'
;
import
{
Link
,
useNavigate
,
useSearchParams
}
from
'react-router-dom'
;
import
{
UserContext
}
from
'../context/User'
;
import
{
UserContext
}
from
'../context/User'
;
import
{
API
,
getLogo
,
showError
,
showInfo
,
showSuccess
,
updateAPI
}
from
'../helpers'
;
import
{
import
{
onGitHubOAuthClicked
}
from
'./utils'
;
API
,
getLogo
,
showError
,
showInfo
,
showSuccess
,
updateAPI
,
}
from
'../helpers'
;
import
{
onGitHubOAuthClicked
,
onLinuxDOOAuthClicked
}
from
'./utils'
;
import
Turnstile
from
'react-turnstile'
;
import
Turnstile
from
'react-turnstile'
;
import
{
import
{
Button
,
Button
,
...
@@ -17,7 +24,7 @@ import Title from '@douyinfe/semi-ui/lib/es/typography/title';
...
@@ -17,7 +24,7 @@ import Title from '@douyinfe/semi-ui/lib/es/typography/title';
import
Text
from
'@douyinfe/semi-ui/lib/es/typography/text'
;
import
Text
from
'@douyinfe/semi-ui/lib/es/typography/text'
;
import
TelegramLoginButton
from
'react-telegram-login'
;
import
TelegramLoginButton
from
'react-telegram-login'
;
import
{
IconGithubLogo
}
from
'@douyinfe/semi-icons'
;
import
{
IconGithubLogo
,
IconAlarm
}
from
'@douyinfe/semi-icons'
;
import
WeChatIcon
from
'./WeChatIcon'
;
import
WeChatIcon
from
'./WeChatIcon'
;
import
{
setUserData
}
from
'../helpers/data.js'
;
import
{
setUserData
}
from
'../helpers/data.js'
;
...
@@ -72,7 +79,7 @@ const LoginForm = () => {
...
@@ -72,7 +79,7 @@ const LoginForm = () => {
userDispatch
({
type
:
'login'
,
payload
:
data
});
userDispatch
({
type
:
'login'
,
payload
:
data
});
localStorage
.
setItem
(
'user'
,
JSON
.
stringify
(
data
));
localStorage
.
setItem
(
'user'
,
JSON
.
stringify
(
data
));
setUserData
(
data
);
setUserData
(
data
);
updateAPI
()
updateAPI
()
;
navigate
(
'/'
);
navigate
(
'/'
);
showSuccess
(
'登录成功!'
);
showSuccess
(
'登录成功!'
);
setShowWeChatLoginModal
(
false
);
setShowWeChatLoginModal
(
false
);
...
@@ -103,7 +110,7 @@ const LoginForm = () => {
...
@@ -103,7 +110,7 @@ const LoginForm = () => {
if
(
success
)
{
if
(
success
)
{
userDispatch
({
type
:
'login'
,
payload
:
data
});
userDispatch
({
type
:
'login'
,
payload
:
data
});
setUserData
(
data
);
setUserData
(
data
);
updateAPI
()
updateAPI
()
;
showSuccess
(
'登录成功!'
);
showSuccess
(
'登录成功!'
);
if
(
username
===
'root'
&&
password
===
'123456'
)
{
if
(
username
===
'root'
&&
password
===
'123456'
)
{
Modal
.
error
({
Modal
.
error
({
...
@@ -146,7 +153,7 @@ const LoginForm = () => {
...
@@ -146,7 +153,7 @@ const LoginForm = () => {
localStorage
.
setItem
(
'user'
,
JSON
.
stringify
(
data
));
localStorage
.
setItem
(
'user'
,
JSON
.
stringify
(
data
));
showSuccess
(
'登录成功!'
);
showSuccess
(
'登录成功!'
);
setUserData
(
data
);
setUserData
(
data
);
updateAPI
()
updateAPI
()
;
navigate
(
'/'
);
navigate
(
'/'
);
}
else
{
}
else
{
showError
(
message
);
showError
(
message
);
...
@@ -214,7 +221,8 @@ const LoginForm = () => {
...
@@ -214,7 +221,8 @@ const LoginForm = () => {
<
/div
>
<
/div
>
{
status
.
github_oauth
||
{
status
.
github_oauth
||
status
.
wechat_login
||
status
.
wechat_login
||
status
.
telegram_oauth
?
(
status
.
telegram_oauth
||
status
.
linuxdo_oauth
?
(
<>
<>
<
Divider
margin
=
'12px'
align
=
'center'
>
<
Divider
margin
=
'12px'
align
=
'center'
>
第三方登录
第三方登录
...
@@ -237,6 +245,17 @@ const LoginForm = () => {
...
@@ -237,6 +245,17 @@ const LoginForm = () => {
)
:
(
)
:
(
<><
/
>
<><
/
>
)}
)}
{
status
.
linuxdo_oauth
?
(
<
Button
type
=
'primary'
icon
=
{
<
IconAlarm
/>
}
onClick
=
{()
=>
onLinuxDOOAuthClicked
(
status
.
linuxdo_client_id
)
}
/
>
)
:
(
<><
/
>
)}
{
status
.
wechat_login
?
(
{
status
.
wechat_login
?
(
<
Button
<
Button
type
=
'primary'
type
=
'primary'
...
...
web/src/components/PersonalSetting.js
View file @
12de1fac
...
@@ -10,7 +10,7 @@ import {
...
@@ -10,7 +10,7 @@ import {
}
from
'../helpers'
;
}
from
'../helpers'
;
import
Turnstile
from
'react-turnstile'
;
import
Turnstile
from
'react-turnstile'
;
import
{
UserContext
}
from
'../context/User'
;
import
{
UserContext
}
from
'../context/User'
;
import
{
onGitHubOAuthClicked
}
from
'./utils'
;
import
{
onGitHubOAuthClicked
,
onLinuxDOOAuthClicked
}
from
'./utils'
;
import
{
import
{
Avatar
,
Avatar
,
Banner
,
Banner
,
...
@@ -519,7 +519,6 @@ const PersonalSetting = () => {
...
@@ -519,7 +519,6 @@ const PersonalSetting = () => {
</div>
</div>
</div>
</div>
</div>
</div>
<div style={{ marginTop: 10 }}>
<div style={{ marginTop: 10 }}>
<Typography.Text strong>Telegram</Typography.Text>
<Typography.Text strong>Telegram</Typography.Text>
<div
<div
...
@@ -551,7 +550,36 @@ const PersonalSetting = () => {
...
@@ -551,7 +550,36 @@ const PersonalSetting = () => {
</div>
</div>
</div>
</div>
</div>
</div>
<div style={{ marginTop: 10 }}>
<Typography.Text strong>LinuxDO</Typography.Text>
<div
style={{ display: 'flex', justifyContent: 'space-between' }}
>
<div>
<Input
value={
userState.user && userState.user.linux_do_id !== ''
? userState.user.linux_do_id
: '未绑定'
}
readonly={true}
></Input>
</div>
<div>
<Button
onClick={() => {
onLinuxDOOAuthClicked(status.linuxdo_client_id);
}}
disabled={
(userState.user && userState.user.linux_do_id !== '') ||
!status.linuxdo_oauth
}
>
{status.linuxdo_oauth ? '绑定' : '未启用'}
</Button>
</div>
</div>
</div>
<div style={{ marginTop: 10 }}>
<div style={{ marginTop: 10 }}>
<Space>
<Space>
<Button onClick={generateAccessToken}>
<Button onClick={generateAccessToken}>
...
...
web/src/components/SystemSetting.js
View file @
12de1fac
...
@@ -53,6 +53,9 @@ const SystemSetting = () => {
...
@@ -53,6 +53,9 @@ const SystemSetting = () => {
TelegramOAuthEnabled
:
''
,
TelegramOAuthEnabled
:
''
,
TelegramBotToken
:
''
,
TelegramBotToken
:
''
,
TelegramBotName
:
''
,
TelegramBotName
:
''
,
LinuxDOOAuthEnabled
:
''
,
LinuxDOClientId
:
''
,
LinuxDOClientSecret
:
''
,
});
});
const
[
originInputs
,
setOriginInputs
]
=
useState
({});
const
[
originInputs
,
setOriginInputs
]
=
useState
({});
let
[
loading
,
setLoading
]
=
useState
(
false
);
let
[
loading
,
setLoading
]
=
useState
(
false
);
...
@@ -103,6 +106,7 @@ const SystemSetting = () => {
...
@@ -103,6 +106,7 @@ const SystemSetting = () => {
case
'PasswordRegisterEnabled'
:
case
'PasswordRegisterEnabled'
:
case
'EmailVerificationEnabled'
:
case
'EmailVerificationEnabled'
:
case
'GitHubOAuthEnabled'
:
case
'GitHubOAuthEnabled'
:
case
'LinuxDOOAuthEnabled'
:
case
'WeChatAuthEnabled'
:
case
'WeChatAuthEnabled'
:
case
'TelegramOAuthEnabled'
:
case
'TelegramOAuthEnabled'
:
case
'TurnstileCheckEnabled'
:
case
'TurnstileCheckEnabled'
:
...
@@ -163,7 +167,9 @@ const SystemSetting = () => {
...
@@ -163,7 +167,9 @@ const SystemSetting = () => {
name
===
'EmailDomainWhitelist'
||
name
===
'EmailDomainWhitelist'
||
name
===
'TopupGroupRatio'
||
name
===
'TopupGroupRatio'
||
name
===
'TelegramBotToken'
||
name
===
'TelegramBotToken'
||
name
===
'TelegramBotName'
name
===
'TelegramBotName'
||
name
===
'LinuxDOClientId'
||
name
===
'LinuxDOClientSecret'
)
{
)
{
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
}
else
{
}
else
{
...
@@ -182,7 +188,7 @@ const SystemSetting = () => {
...
@@ -182,7 +188,7 @@ const SystemSetting = () => {
if
(
inputs
.
WorkerValidKey
!==
''
)
{
if
(
inputs
.
WorkerValidKey
!==
''
)
{
await
updateOption
(
'WorkerValidKey'
,
inputs
.
WorkerValidKey
);
await
updateOption
(
'WorkerValidKey'
,
inputs
.
WorkerValidKey
);
}
}
}
}
;
const
submitPayAddress
=
async
()
=>
{
const
submitPayAddress
=
async
()
=>
{
if
(
inputs
.
ServerAddress
===
''
)
{
if
(
inputs
.
ServerAddress
===
''
)
{
...
@@ -320,6 +326,18 @@ const SystemSetting = () => {
...
@@ -320,6 +326,18 @@ const SystemSetting = () => {
}
}
};
};
const
submitLinuxDOOAuth
=
async
()
=>
{
if
(
originInputs
[
'LinuxDOClientId'
]
!==
inputs
.
LinuxDOClientId
)
{
await
updateOption
(
'LinuxDOClientId'
,
inputs
.
LinuxDOClientId
);
}
if
(
originInputs
[
'LinuxDOClientSecret'
]
!==
inputs
.
LinuxDOClientSecret
&&
inputs
.
LinuxDOClientSecret
!==
''
)
{
await
updateOption
(
'LinuxDOClientSecret'
,
inputs
.
LinuxDOClientSecret
);
}
};
return
(
return
(
<
Grid
columns
=
{
1
}
>
<
Grid
columns
=
{
1
}
>
<
Grid
.
Column
>
<
Grid
.
Column
>
...
@@ -340,7 +358,15 @@ const SystemSetting = () => {
...
@@ -340,7 +358,15 @@ const SystemSetting = () => {
更新服务器地址
更新服务器地址
<
/Form.Button
>
<
/Form.Button
>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
代理设置(支持
<
a
href
=
'https://github.com/Calcium-Ion/new-api-worker'
target
=
'_blank'
rel
=
'noreferrer'
>
new
-
api
-
worker
<
/a>
)
代理设置(支持
{
' '
}
<
a
href
=
'https://github.com/Calcium-Ion/new-api-worker'
target
=
'_blank'
rel
=
'noreferrer'
>
new
-
api
-
worker
<
/a
>
)
<
/Header
>
<
/Header
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
Input
<
Form
.
Input
...
@@ -358,9 +384,7 @@ const SystemSetting = () => {
...
@@ -358,9 +384,7 @@ const SystemSetting = () => {
onChange
=
{
handleInputChange
}
onChange
=
{
handleInputChange
}
/
>
/
>
<
/Form.Group
>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{
submitWorker
}
>
<
Form
.
Button
onClick
=
{
submitWorker
}
>
更新
Worker
设置
<
/Form.Button
>
更新
Worker
设置
<
/Form.Button
>
<
Divider
/>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
支付设置(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)
支付设置(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)
...
@@ -484,6 +508,12 @@ const SystemSetting = () => {
...
@@ -484,6 +508,12 @@ const SystemSetting = () => {
onChange
=
{
handleInputChange
}
onChange
=
{
handleInputChange
}
/
>
/
>
<
Form
.
Checkbox
<
Form
.
Checkbox
checked
=
{
inputs
.
LinuxDOOAuthEnabled
===
'true'
}
label
=
'允许通过 LinuxDO 账户登录 & 注册'
name
=
'LinuxDOOAuthEnabled'
onChange
=
{
handleInputChange
}
/
>
<
Form
.
Checkbox
checked
=
{
inputs
.
WeChatAuthEnabled
===
'true'
}
checked
=
{
inputs
.
WeChatAuthEnabled
===
'true'
}
label
=
'允许通过微信登录 & 注册'
label
=
'允许通过微信登录 & 注册'
name
=
'WeChatAuthEnabled'
name
=
'WeChatAuthEnabled'
...
@@ -781,6 +811,48 @@ const SystemSetting = () => {
...
@@ -781,6 +811,48 @@ const SystemSetting = () => {
<
Form
.
Button
onClick
=
{
submitTurnstile
}
>
<
Form
.
Button
onClick
=
{
submitTurnstile
}
>
保存
Turnstile
设置
保存
Turnstile
设置
<
/Form.Button
>
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
配置
LinuxDO
OAuth
App
<
Header
.
Subheader
>
用以支持通过
LinuxDO
进行登录注册,
<
a
href
=
'https://connect.linux.do/'
target
=
'_blank'
rel
=
'noreferrer'
>
点击此处
<
/a
>
管理你的
LinuxDO
OAuth
App
<
/Header.Subheader
>
<
/Header
>
<
Message
>
Homepage
URL
填
<
code
>
{
inputs
.
ServerAddress
}
<
/code
>
,
Authorization
callback
URL
填
{
' '
}
<
code
>
{
`
${
inputs
.
ServerAddress
}
/oauth/linuxdo`
}
<
/code
>
<
/Message
>
<
Form
.
Group
widths
=
{
3
}
>
<
Form
.
Input
label
=
'LinuxDO Client ID'
name
=
'LinuxDOClientId'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
LinuxDOClientId
}
placeholder
=
'输入你注册的 LinuxDO OAuth APP 的 ID'
/>
<
Form
.
Input
label
=
'LinuxDO Client Secret'
name
=
'LinuxDOClientSecret'
onChange
=
{
handleInputChange
}
type
=
'password'
autoComplete
=
'new-password'
value
=
{
inputs
.
LinuxDOClientSecret
}
placeholder
=
'敏感信息不会发送到前端显示'
/>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{
submitLinuxDOOAuth
}
>
保存
LinuxDO
OAuth
设置
<
/Form.Button
>
<
/Form
>
<
/Form
>
<
/Grid.Column
>
<
/Grid.Column
>
<
/Grid
>
<
/Grid
>
...
...
web/src/components/utils.js
View file @
12de1fac
...
@@ -19,6 +19,14 @@ export async function onGitHubOAuthClicked(github_client_id) {
...
@@ -19,6 +19,14 @@ export async function onGitHubOAuthClicked(github_client_id) {
);
);
}
}
export
async
function
onLinuxDOOAuthClicked
(
linuxdo_client_id
)
{
const
state
=
await
getOAuthState
();
if
(
!
state
)
return
;
window
.
open
(
`https://connect.linux.do/oauth2/authorize?response_type=code&client_id=
${
linuxdo_client_id
}
&state=
${
state
}
`
,
);
}
let
channelModels
=
undefined
;
let
channelModels
=
undefined
;
export
async
function
loadChannelModels
()
{
export
async
function
loadChannelModels
()
{
const
res
=
await
API
.
get
(
'/api/models'
);
const
res
=
await
API
.
get
(
'/api/models'
);
...
...
web/src/pages/Home/index.js
View file @
12de1fac
...
@@ -150,6 +150,12 @@ const Home = () => {
...
@@ -150,6 +150,12 @@ const Home = () => {
?
'已启用'
?
'已启用'
:
'未启用'
}
:
'未启用'
}
<
/p
>
<
/p
>
<
p
>
Linux
DO
身份验证:
{
statusState
?.
status
?.
linuxdo_oauth
===
true
?
'已启用'
:
'未启用'
}
<
/p
>
<
/Card
>
<
/Card
>
<
/Col
>
<
/Col
>
<
/Row
>
<
/Row
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment