Преглед изворни кода

feat: able to delete account by self (#294)

* feat: support account deletion

* chore: update style

---------

Co-authored-by: JustSong <songquanpeng@foxmail.com>
ckt пре 2 година
родитељ
комит
2fcd6852e0
3 измењених фајлова са 86 додато и 5 уклоњено
  1. 13 2
      controller/user.go
  2. 1 1
      router/api-router.go
  3. 72 2
      web/src/components/PersonalSetting.js

+ 13 - 2
controller/user.go

@@ -3,12 +3,13 @@ package controller
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/gin-contrib/sessions"
-	"github.com/gin-gonic/gin"
 	"net/http"
 	"one-api/common"
 	"one-api/model"
 	"strconv"
+
+	"github.com/gin-contrib/sessions"
+	"github.com/gin-gonic/gin"
 )
 
 type LoginRequest struct {
@@ -477,6 +478,16 @@ func DeleteUser(c *gin.Context) {
 
 func DeleteSelf(c *gin.Context) {
 	id := c.GetInt("id")
+	user, _ := model.GetUserById(id, false)
+
+	if user.Role == common.RoleRootUser {
+		c.JSON(http.StatusOK, gin.H{
+			"success": false,
+			"message": "不能删除超级管理员账户",
+		})
+		return
+	}
+
 	err := model.DeleteUserById(id)
 	if err != nil {
 		c.JSON(http.StatusOK, gin.H{

+ 1 - 1
router/api-router.go

@@ -36,7 +36,7 @@ func SetApiRouter(router *gin.Engine) {
 			{
 				selfRoute.GET("/self", controller.GetSelf)
 				selfRoute.PUT("/self", controller.UpdateSelf)
-				selfRoute.DELETE("/self", controller.DeleteSelf)
+				selfRoute.DELETE("/self", middleware.TurnstileCheck(), controller.DeleteSelf)
 				selfRoute.GET("/token", controller.GenerateAccessToken)
 				selfRoute.GET("/aff", controller.GetAffCode)
 				selfRoute.POST("/topup", controller.TopUp)

+ 72 - 2
web/src/components/PersonalSetting.js

@@ -1,18 +1,24 @@
-import React, { useEffect, useState } from 'react';
+import React, { useContext, useEffect, useState } from 'react';
 import { Button, Divider, Form, Header, Image, Message, Modal } from 'semantic-ui-react';
-import { Link } from 'react-router-dom';
+import { Link, useNavigate } from 'react-router-dom';
 import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers';
 import Turnstile from 'react-turnstile';
+import { UserContext } from '../context/User';
 
 const PersonalSetting = () => {
+  const [userState, userDispatch] = useContext(UserContext);
+  let navigate = useNavigate();
+
   const [inputs, setInputs] = useState({
     wechat_verification_code: '',
     email_verification_code: '',
     email: '',
+    self_account_deletion_confirmation: ''
   });
   const [status, setStatus] = useState({});
   const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
   const [showEmailBindModal, setShowEmailBindModal] = useState(false);
+  const [showAccountDeleteModal, setShowAccountDeleteModal] = useState(false);
   const [turnstileEnabled, setTurnstileEnabled] = useState(false);
   const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
   const [turnstileToken, setTurnstileToken] = useState('');
@@ -72,6 +78,26 @@ const PersonalSetting = () => {
     }
   };
 
+  const deleteAccount = async () => {
+    if (inputs.self_account_deletion_confirmation !== userState.user.username) {
+      showError('请输入你的账户名以确认删除!');
+      return;
+    }
+
+    const res = await API.delete('/api/user/self');
+    const { success, message } = res.data;
+
+    if (success) {
+      showSuccess('账户已删除!');
+      await API.get('/api/user/logout');
+      userDispatch({ type: 'logout' });
+      localStorage.removeItem('user');
+      navigate('/login');
+    } else {
+      showError(message);
+    }
+  };
+
   const bindWeChat = async () => {
     if (inputs.wechat_verification_code === '') return;
     const res = await API.get(
@@ -139,6 +165,9 @@ const PersonalSetting = () => {
       </Button>
       <Button onClick={generateAccessToken}>生成系统访问令牌</Button>
       <Button onClick={getAffLink}>复制邀请链接</Button>
+      <Button onClick={() => {
+        setShowAccountDeleteModal(true);
+      }}>删除个人账户</Button>
       <Divider />
       <Header as='h3'>账号绑定</Header>
       {
@@ -246,6 +275,47 @@ const PersonalSetting = () => {
           </Modal.Description>
         </Modal.Content>
       </Modal>
+      <Modal
+        onClose={() => setShowAccountDeleteModal(false)}
+        onOpen={() => setShowAccountDeleteModal(true)}
+        open={showAccountDeleteModal}
+        size={'tiny'}
+        style={{ maxWidth: '450px' }}
+      >
+        <Modal.Header>确认删除自己的帐户</Modal.Header>
+        <Modal.Content>
+          <Modal.Description>
+            <Form size='large'>
+              <Form.Input
+                fluid
+                placeholder={`输入你的账户名 ${userState.user.username} 以确认删除`}
+                name='self_account_deletion_confirmation'
+                value={inputs.self_account_deletion_confirmation}
+                onChange={handleInputChange}
+              />
+              {turnstileEnabled ? (
+                <Turnstile
+                  sitekey={turnstileSiteKey}
+                  onVerify={(token) => {
+                    setTurnstileToken(token);
+                  }}
+                />
+              ) : (
+                <></>
+              )}
+              <Button
+                color='red'
+                fluid
+                size='large'
+                onClick={deleteAccount}
+                loading={loading}
+              >
+                删除
+              </Button>
+            </Form>
+          </Modal.Description>
+        </Modal.Content>
+      </Modal>
     </div>
   );
 };