From 7ddb7c586d6d426438ca66d2bc31a966e2466bee Mon Sep 17 00:00:00 2001 From: wozulong <> Date: Thu, 14 Mar 2024 18:53:06 +0800 Subject: [PATCH] 1. add LINUX DO oauth 2. fix oauth reg aff issue Signed-off-by: wozulong <> --- makefile => Makefile | 2 +- common/constants.go | 4 + controller/github.go | 4 +- controller/linuxdo.go | 223 ++++++++++++++++++++++++++ controller/misc.go | 2 + controller/option.go | 8 + model/option.go | 9 ++ model/user.go | 13 ++ router/api-router.go | 1 + web/src/App.js | 9 ++ web/src/components/GitHubOAuth.js | 11 +- web/src/components/LinuxDoIcon.js | 21 +++ web/src/components/LinuxDoOAuth.js | 67 ++++++++ web/src/components/LoginForm.js | 15 +- web/src/components/PersonalSetting.js | 23 ++- web/src/components/SystemSetting.js | 62 +++++++ web/src/components/utils.js | 10 +- web/src/pages/Home/index.js | 4 + web/src/pages/User/EditUser.js | 16 +- 19 files changed, 494 insertions(+), 10 deletions(-) rename makefile => Makefile (83%) create mode 100644 controller/linuxdo.go create mode 100644 web/src/components/LinuxDoIcon.js create mode 100644 web/src/components/LinuxDoOAuth.js diff --git a/makefile b/Makefile similarity index 83% rename from makefile rename to Makefile index f846d30..9292c0e 100644 --- a/makefile +++ b/Makefile @@ -7,7 +7,7 @@ all: build-frontend start-backend build-frontend: @echo "Building frontend..." - @cd $(FRONTEND_DIR) && npm install && DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build npm run build + @cd $(FRONTEND_DIR) && npm install && DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build start-backend: @echo "Starting backend dev server..." diff --git a/common/constants.go b/common/constants.go index 98fa67a..3e67c27 100644 --- a/common/constants.go +++ b/common/constants.go @@ -50,6 +50,7 @@ var PasswordLoginEnabled = true var PasswordRegisterEnabled = true var EmailVerificationEnabled = false var GitHubOAuthEnabled = false +var LinuxDoOAuthEnabled = false var WeChatAuthEnabled = false var TelegramOAuthEnabled = false var TurnstileCheckEnabled = false @@ -82,6 +83,9 @@ var SMTPToken = "" var GitHubClientId = "" var GitHubClientSecret = "" +var LinuxDoClientId = "" +var LinuxDoClientSecret = "" + var WeChatServerAddress = "" var WeChatServerToken = "" var WeChatAccountQRCodeImageURL = "" diff --git a/controller/github.go b/controller/github.go index ee99537..186ca0f 100644 --- a/controller/github.go +++ b/controller/github.go @@ -123,6 +123,8 @@ func GitHubOAuth(c *gin.Context) { } } else { if common.RegisterEnabled { + user.InviterId, _ = model.GetUserIdByAffCode(c.Query("aff")) + user.Username = "github_" + strconv.Itoa(model.GetMaxUserId()+1) if githubUser.Name != "" { user.DisplayName = githubUser.Name @@ -133,7 +135,7 @@ func GitHubOAuth(c *gin.Context) { user.Role = common.RoleCommonUser user.Status = common.UserStatusEnabled - if err := user.Insert(0); err != nil { + if err := user.Insert(user.InviterId); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, "message": err.Error(), diff --git a/controller/linuxdo.go b/controller/linuxdo.go new file mode 100644 index 0000000..de4c982 --- /dev/null +++ b/controller/linuxdo.go @@ -0,0 +1,223 @@ +package controller + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "github.com/gin-contrib/sessions" + "github.com/gin-gonic/gin" + "net/http" + "net/url" + "one-api/common" + "one-api/model" + "strconv" + "time" +) + +type LinuxDoOAuthResponse struct { + AccessToken string `json:"access_token"` + Scope string `json:"scope"` + TokenType string `json:"token_type"` +} + +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 getLinuxDoUserInfoByCode(code string) (*LinuxDoUser, error) { + if code == "" { + return nil, errors.New("无效的参数") + } + auth := base64.StdEncoding.EncodeToString([]byte(common.LinuxDoClientId + ":" + common.LinuxDoClientSecret)) + form := url.Values{ + "grant_type": {"authorization_code"}, + "code": {code}, + } + req, err := http.NewRequest("POST", "https://connect.linux.do/oauth2/token", bytes.NewBufferString(form.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Authorization", "Basic "+auth) + req.Header.Set("Accept", "application/json") + client := http.Client{ + Timeout: 5 * time.Second, + } + res, err := client.Do(req) + if err != nil { + common.SysLog(err.Error()) + return nil, errors.New("无法连接至 LINUX DO 服务器,请稍后重试!") + } + defer res.Body.Close() + var oAuthResponse LinuxDoOAuthResponse + err = json.NewDecoder(res.Body).Decode(&oAuthResponse) + if err != nil { + return nil, err + } + req, err = http.NewRequest("GET", "https://connect.linux.do/api/user", nil) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oAuthResponse.AccessToken)) + res2, err := client.Do(req) + if err != nil { + common.SysLog(err.Error()) + return nil, errors.New("无法连接至 LINUX DO 服务器,请稍后重试!") + } + defer res2.Body.Close() + var linuxdoUser LinuxDoUser + err = json.NewDecoder(res2.Body).Decode(&linuxdoUser) + if err != nil { + return nil, err + } + if linuxdoUser.ID == 0 { + return nil, errors.New("返回值非法,用户字段为空,请稍后重试!") + } + return &linuxdoUser, nil +} + +func LinuxDoOAuth(c *gin.Context) { + session := sessions.Default(c) + 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) + 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) { + err := user.FillUserByLinuxDoId() + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + } else { + if common.RegisterEnabled { + affCode := c.Query("aff") + user.InviterId, _ = model.GetUserIdByAffCode(affCode) + + user.Username = "linuxdo_" + strconv.Itoa(model.GetMaxUserId()+1) + if linuxdoUser.Name != "" { + user.DisplayName = linuxdoUser.Name + } else { + user.DisplayName = linuxdoUser.Username + } + user.Role = common.RoleCommonUser + user.Status = common.UserStatusEnabled + + if err := user.Insert(user.InviterId); 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) +} + +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) + 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") + // id := c.GetInt("id") // critical bug! + 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", + }) + return +} diff --git a/controller/misc.go b/controller/misc.go index 0259426..bf0ec11 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -36,6 +36,8 @@ func GetStatus(c *gin.Context) { "email_verification": common.EmailVerificationEnabled, "github_oauth": common.GitHubOAuthEnabled, "github_client_id": common.GitHubClientId, + "linuxdo_oauth": common.LinuxDoOAuthEnabled, + "linuxdo_client_id": common.LinuxDoClientId, "telegram_oauth": common.TelegramOAuthEnabled, "telegram_bot_name": common.TelegramBotName, "system_name": common.SystemName, diff --git a/controller/option.go b/controller/option.go index bbf8357..d2272b7 100644 --- a/controller/option.go +++ b/controller/option.go @@ -50,6 +50,14 @@ func UpdateOption(c *gin.Context) { }) return } + case "LinuxDoOAuthEnabled": + if option.Value == "true" && common.LinuxDoClientId == "" { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "无法启用 LINUX DO OAuth,请先填入 LINUX DO Client Id 以及 LINUX DO Client Secret!", + }) + return + } case "EmailDomainRestrictionEnabled": if option.Value == "true" && len(common.EmailDomainWhitelist) == 0 { c.JSON(http.StatusOK, gin.H{ diff --git a/model/option.go b/model/option.go index 9a7ad60..8e08e04 100644 --- a/model/option.go +++ b/model/option.go @@ -30,6 +30,7 @@ func InitOptionMap() { common.OptionMap["PasswordRegisterEnabled"] = strconv.FormatBool(common.PasswordRegisterEnabled) common.OptionMap["EmailVerificationEnabled"] = strconv.FormatBool(common.EmailVerificationEnabled) common.OptionMap["GitHubOAuthEnabled"] = strconv.FormatBool(common.GitHubOAuthEnabled) + common.OptionMap["LinuxDoOAuthEnabled"] = strconv.FormatBool(common.LinuxDoOAuthEnabled) common.OptionMap["TelegramOAuthEnabled"] = strconv.FormatBool(common.TelegramOAuthEnabled) common.OptionMap["WeChatAuthEnabled"] = strconv.FormatBool(common.WeChatAuthEnabled) common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled) @@ -65,6 +66,8 @@ func InitOptionMap() { common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString() common.OptionMap["GitHubClientId"] = "" common.OptionMap["GitHubClientSecret"] = "" + common.OptionMap["LinuxDoClientId"] = "" + common.OptionMap["LinuxDoClientSecret"] = "" common.OptionMap["TelegramBotToken"] = "" common.OptionMap["TelegramBotName"] = "" common.OptionMap["WeChatServerAddress"] = "" @@ -155,6 +158,8 @@ func updateOptionMap(key string, value string) (err error) { common.EmailVerificationEnabled = boolValue case "GitHubOAuthEnabled": common.GitHubOAuthEnabled = boolValue + case "LinuxDoOAuthEnabled": + common.LinuxDoOAuthEnabled = boolValue case "WeChatAuthEnabled": common.WeChatAuthEnabled = boolValue case "TelegramOAuthEnabled": @@ -217,6 +222,10 @@ func updateOptionMap(key string, value string) (err error) { common.GitHubClientId = value case "GitHubClientSecret": common.GitHubClientSecret = value + case "LinuxDoClientId": + common.LinuxDoClientId = value + case "LinuxDoClientSecret": + common.LinuxDoClientSecret = value case "Footer": common.Footer = value case "SystemName": diff --git a/model/user.go b/model/user.go index 00294b2..9c9d42e 100644 --- a/model/user.go +++ b/model/user.go @@ -21,6 +21,7 @@ type User struct { Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled Email string `json:"email" gorm:"index" validate:"max=50"` GitHubId string `json:"github_id" gorm:"column:github_id;index"` + LinuxDoId string `json:"linuxdo_id" gorm:"column:linuxdo_id;index"` WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"` TelegramId string `json:"telegram_id" gorm:"column:telegram_id;index"` VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database! @@ -272,6 +273,14 @@ func (user *User) FillUserByGitHubId() error { return nil } +func (user *User) FillUserByLinuxDoId() error { + if user.LinuxDoId == "" { + return errors.New("LINUX DO id 为空!") + } + DB.Where(User{LinuxDoId: user.LinuxDoId}).First(user) + return nil +} + func (user *User) FillUserByWeChatId() error { if user.WeChatId == "" { return errors.New("WeChat id 为空!") @@ -311,6 +320,10 @@ func IsGitHubIdAlreadyTaken(githubId string) bool { return DB.Where("github_id = ?", githubId).Find(&User{}).RowsAffected == 1 } +func IsLinuxDoIdAlreadyTaken(linuxdoId string) bool { + return DB.Where("linuxdo_id = ?", linuxdoId).Find(&User{}).RowsAffected == 1 +} + func IsUsernameAlreadyTaken(username string) bool { return DB.Where("username = ?", username).Find(&User{}).RowsAffected == 1 } diff --git a/router/api-router.go b/router/api-router.go index 592e8ed..ad506b1 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -23,6 +23,7 @@ func SetApiRouter(router *gin.Engine) { apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail) apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword) 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/wechat", middleware.CriticalRateLimit(), controller.WeChatAuth) apiRouter.GET("/oauth/wechat/bind", middleware.CriticalRateLimit(), middleware.UserAuth(), controller.WeChatBind) diff --git a/web/src/App.js b/web/src/App.js index 86458b0..6d4fbc0 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -11,6 +11,7 @@ import EditUser from './pages/User/EditUser'; import { API, getLogo, getSystemName, showError, showNotice } from './helpers'; import PasswordResetForm from './components/PasswordResetForm'; import GitHubOAuth from './components/GitHubOAuth'; +import LinuxDoOAuth from "./components/LinuxDoOAuth"; import PasswordResetConfirm from './components/PasswordResetConfirm'; import { UserContext } from './context/User'; import { StatusContext } from './context/Status'; @@ -170,6 +171,14 @@ function App() { } /> + }> + + + } + /> { let navigate = useNavigate(); const sendCode = async (code, state, count) => { - const res = await API.get(`/api/oauth/github?code=${code}&state=${state}`); + let aff = localStorage.getItem('aff'); + const res = await API.get(`/api/oauth/github?code=${code}&state=${state}&aff=${aff}`); const { success, message, data } = res.data; if (success) { if (message === 'bind') { @@ -41,6 +42,14 @@ const GitHubOAuth = () => { }; useEffect(() => { + let error = searchParams.get('error'); + if (error) { + let errorDescription = searchParams.get('error_description'); + showError(`授权错误:${error}: ${errorDescription}`); + navigate('/setting'); + return; + } + let code = searchParams.get('code'); let state = searchParams.get('state'); sendCode(code, state, 0).then(); diff --git a/web/src/components/LinuxDoIcon.js b/web/src/components/LinuxDoIcon.js new file mode 100644 index 0000000..d4be5b2 --- /dev/null +++ b/web/src/components/LinuxDoIcon.js @@ -0,0 +1,21 @@ +import React from 'react'; +import {Icon} from '@douyinfe/semi-ui'; + +const LinuxDoIcon = (props) => { + function CustomIcon() { + return + + ; + } + + return ( +
+ }/> +
+ ); +}; + +export default LinuxDoIcon; diff --git a/web/src/components/LinuxDoOAuth.js b/web/src/components/LinuxDoOAuth.js new file mode 100644 index 0000000..97af0a8 --- /dev/null +++ b/web/src/components/LinuxDoOAuth.js @@ -0,0 +1,67 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { Dimmer, Loader, Segment } from 'semantic-ui-react'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import { API, showError, showSuccess } from '../helpers'; +import { UserContext } from '../context/User'; + +const LinuxDoOAuth = () => { + const [searchParams, setSearchParams] = useSearchParams(); + + const [userState, userDispatch] = useContext(UserContext); + const [prompt, setPrompt] = useState('处理中...'); + const [processing, setProcessing] = useState(true); + + let navigate = useNavigate(); + + const sendCode = async (code, state, count) => { + let aff = localStorage.getItem('aff'); + const res = await API.get(`/api/oauth/linuxdo?code=${code}&state=${state}&aff=${aff}`); + const { success, message, data } = res.data; + if (success) { + if (message === 'bind') { + showSuccess('绑定成功!'); + navigate('/setting'); + } else { + userDispatch({ type: 'login', payload: data }); + localStorage.setItem('user', JSON.stringify(data)); + showSuccess('登录成功!'); + navigate('/'); + } + } else { + showError(message); + if (count === 0) { + setPrompt(`操作失败,重定向至登录界面中...`); + navigate('/setting'); // in case this is failed to bind GitHub + return; + } + count++; + setPrompt(`出现错误,第 ${count} 次重试中...`); + await new Promise((resolve) => setTimeout(resolve, count * 2000)); + await sendCode(code, state, count); + } + }; + + useEffect(() => { + let error = searchParams.get('error'); + if (error) { + let errorDescription = searchParams.get('error_description'); + showError(`授权错误:${error}: ${errorDescription}`); + navigate('/setting'); + return; + } + + let code = searchParams.get('code'); + let state = searchParams.get('state'); + sendCode(code, state, 0).then(); + }, []); + + return ( + + + {prompt} + + + ); +}; + +export default LinuxDoOAuth; diff --git a/web/src/components/LoginForm.js b/web/src/components/LoginForm.js index c8f0536..baef358 100644 --- a/web/src/components/LoginForm.js +++ b/web/src/components/LoginForm.js @@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { UserContext } from '../context/User'; import { API, getLogo, isMobile, showError, showInfo, showSuccess, showWarning } from '../helpers'; -import { onGitHubOAuthClicked } from './utils'; +import { onGitHubOAuthClicked, onLinuxDoOAuthClicked } from './utils'; import Turnstile from "react-turnstile"; import { Layout, Card, Image, Form, Button, Divider, Modal, Icon } from '@douyinfe/semi-ui'; import Title from "@douyinfe/semi-ui/lib/es/typography/title"; @@ -10,6 +10,7 @@ import Text from "@douyinfe/semi-ui/lib/es/typography/text"; import TelegramLoginButton from 'react-telegram-login'; import { IconGithubLogo } from '@douyinfe/semi-icons'; +import LinuxDoIcon from './LinuxDoIcon'; import WeChatIcon from './WeChatIcon'; const LoginForm = () => { @@ -165,7 +166,7 @@ const LoginForm = () => { 忘记密码 点击重置 - {status.github_oauth || status.wechat_login || status.telegram_oauth ? ( + {status.github_oauth || status.linuxdo_oauth || status.wechat_login || status.telegram_oauth ? ( <> 第三方登录 @@ -180,6 +181,16 @@ const LoginForm = () => { ) : ( <> )} + {status.linuxdo_oauth ? ( + + + +
Telegram diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index e955afe..9ed76e0 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -10,6 +10,9 @@ const SystemSetting = () => { GitHubOAuthEnabled: '', GitHubClientId: '', GitHubClientSecret: '', + LinuxDoOAuthEnabled: '', + LinuxDoClientId: '', + LinuxDoClientSecret: '', Notice: '', SMTPServer: '', SMTPPort: '', @@ -82,6 +85,7 @@ const SystemSetting = () => { case 'PasswordRegisterEnabled': case 'EmailVerificationEnabled': case 'GitHubOAuthEnabled': + case 'LinuxDoOAuthEnabled': case 'WeChatAuthEnabled': case 'TelegramOAuthEnabled': case 'TurnstileCheckEnabled': @@ -129,6 +133,8 @@ const SystemSetting = () => { name === 'PayAddress' || name === 'GitHubClientId' || name === 'GitHubClientSecret' || + name === 'LinuxDoClientId' || + name === 'LinuxDoClientSecret' || name === 'WeChatServerAddress' || name === 'WeChatServerToken' || name === 'WeChatAccountQRCodeImageURL' || @@ -243,6 +249,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); + } + }; + const submitTelegramSettings = async () => { // await updateOption('TelegramOAuthEnabled', inputs.TelegramOAuthEnabled); await updateOption('TelegramBotToken', inputs.TelegramBotToken); @@ -412,6 +430,12 @@ const SystemSetting = () => { name='GitHubOAuthEnabled' onChange={handleInputChange} /> + { 保存 GitHub OAuth 设置 +
+ 配置 LINUX DO Oauth + + 用以支持通过 LINUX DO 进行登录注册, + + 点击此处 + + 管理你的 LINUX DO OAuth + +
+ + Homepage URL 填 {inputs.ServerAddress} + ,Authorization callback URL 填{' '} + {`${inputs.ServerAddress}/oauth/linuxdo`} + + + + + + + 保存 LINUX DO OAuth 设置 + +
配置 WeChat Server diff --git a/web/src/components/utils.js b/web/src/components/utils.js index 5363ba5..9902fc2 100644 --- a/web/src/components/utils.js +++ b/web/src/components/utils.js @@ -14,7 +14,11 @@ export async function getOAuthState() { export async function onGitHubOAuthClicked(github_client_id) { const state = await getOAuthState(); if (!state) return; - window.open( - `https://github.com/login/oauth/authorize?client_id=${github_client_id}&state=${state}&scope=user:email` - ); + location.href = `https://github.com/login/oauth/authorize?client_id=${github_client_id}&state=${state}&scope=user:email` +} + +export async function onLinuxDoOAuthClicked(linuxdo_client_id) { + const state = await getOAuthState(); + if (!state) return; + location.href = `https://connect.linux.do/oauth2/authorize?client_id=${linuxdo_client_id}&response_type=code&state=${state}&scope=user:profile`; } \ No newline at end of file diff --git a/web/src/pages/Home/index.js b/web/src/pages/Home/index.js index f3e2980..9efde4f 100644 --- a/web/src/pages/Home/index.js +++ b/web/src/pages/Home/index.js @@ -95,6 +95,10 @@ const Home = () => { GitHub 身份验证: {statusState?.status?.github_oauth === true ? '已启用' : '未启用'}

+

+ LINUX DO 身份验证: + {statusState?.status?.linuxdo_oauth === true ? '已启用' : '未启用'} +

微信身份验证: {statusState?.status?.wechat_login === true ? '已启用' : '未启用'} diff --git a/web/src/pages/User/EditUser.js b/web/src/pages/User/EditUser.js index 6d79127..08b1ede 100644 --- a/web/src/pages/User/EditUser.js +++ b/web/src/pages/User/EditUser.js @@ -13,13 +13,14 @@ const EditUser = (props) => { display_name: '', password: '', github_id: '', + linuxdo_id: '', wechat_id: '', email: '', quota: 0, group: 'default' }); const [groupOptions, setGroupOptions] = useState([]); - const { username, display_name, password, github_id, wechat_id, telegram_id, email, quota, group } = + const { username, display_name, password, github_id, linuxdo_id, wechat_id, telegram_id, email, quota, group } = inputs; const handleInputChange = (name, value) => { setInputs((inputs) => ({ ...inputs, [name]: value })); @@ -184,6 +185,16 @@ const EditUser = (props) => { placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改' readonly /> +

+ 已绑定的 LINUX DO 账户 +
+
已绑定的微信账户
@@ -194,6 +205,9 @@ const EditUser = (props) => { placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改' readonly /> +
+ 已绑定的 Telegram 账户 +