⚡️ improve: Prohibit users from deleting accounts (#132)
This commit is contained in:
parent
a3503ba3d9
commit
89a84e2386
@ -140,3 +140,13 @@ func assembleSumSelectStr(selectStr string) string {
|
|||||||
|
|
||||||
return sumSelectStr
|
return sumSelectStr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RecordExists(table interface{}, fieldName string, fieldValue interface{}, excludeID interface{}) bool {
|
||||||
|
var count int64
|
||||||
|
query := DB.Model(table).Where(fmt.Sprintf("%s = ?", fieldName), fieldValue)
|
||||||
|
if excludeID != nil {
|
||||||
|
query = query.Not("id", excludeID)
|
||||||
|
}
|
||||||
|
query.Count(&count)
|
||||||
|
return count > 0
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"one-api/common"
|
"one-api/common"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@ -12,25 +13,26 @@ import (
|
|||||||
// User if you add sensitive fields, don't forget to clean them in setupLogin function.
|
// User if you add sensitive fields, don't forget to clean them in setupLogin function.
|
||||||
// Otherwise, the sensitive information will be saved on local storage in plain text!
|
// Otherwise, the sensitive information will be saved on local storage in plain text!
|
||||||
type User struct {
|
type User struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
Username string `json:"username" gorm:"unique;index" validate:"max=12"`
|
Username string `json:"username" gorm:"unique;index" validate:"max=12"`
|
||||||
Password string `json:"password" gorm:"not null;" validate:"min=8,max=20"`
|
Password string `json:"password" gorm:"not null;" validate:"min=8,max=20"`
|
||||||
DisplayName string `json:"display_name" gorm:"index" validate:"max=20"`
|
DisplayName string `json:"display_name" gorm:"index" validate:"max=20"`
|
||||||
Role int `json:"role" gorm:"type:int;default:1"` // admin, common
|
Role int `json:"role" gorm:"type:int;default:1"` // admin, common
|
||||||
Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled
|
Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled
|
||||||
Email string `json:"email" gorm:"index" validate:"max=50"`
|
Email string `json:"email" gorm:"index" validate:"max=50"`
|
||||||
GitHubId string `json:"github_id" gorm:"column:github_id;index"`
|
GitHubId string `json:"github_id" gorm:"column:github_id;index"`
|
||||||
WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"`
|
WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"`
|
||||||
TelegramId int64 `json:"telegram_id" gorm:"bigint,column:telegram_id;default:0;"`
|
TelegramId int64 `json:"telegram_id" gorm:"bigint,column:telegram_id;default:0;"`
|
||||||
VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database!
|
VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database!
|
||||||
AccessToken string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
|
AccessToken string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
|
||||||
Quota int `json:"quota" gorm:"type:int;default:0"`
|
Quota int `json:"quota" gorm:"type:int;default:0"`
|
||||||
UsedQuota int `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
|
UsedQuota int `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
|
||||||
RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number
|
RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number
|
||||||
Group string `json:"group" gorm:"type:varchar(32);default:'default'"`
|
Group string `json:"group" gorm:"type:varchar(32);default:'default'"`
|
||||||
AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
|
AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
|
||||||
InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
|
InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
|
||||||
CreatedTime int64 `json:"created_time" gorm:"bigint"`
|
CreatedTime int64 `json:"created_time" gorm:"bigint"`
|
||||||
|
DeletedAt *time.Time `gorm:"index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserUpdates func(*User)
|
type UserUpdates func(*User)
|
||||||
@ -102,6 +104,9 @@ func DeleteUserById(id int) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) Insert(inviterId int) error {
|
func (user *User) Insert(inviterId int) error {
|
||||||
|
if RecordExists(&User{}, "username", user.Username, nil) {
|
||||||
|
return errors.New("用户名已存在!")
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
if user.Password != "" {
|
if user.Password != "" {
|
||||||
user.Password, err = common.Password2Hash(user.Password)
|
user.Password, err = common.Password2Hash(user.Password)
|
||||||
@ -134,6 +139,9 @@ func (user *User) Insert(inviterId int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) Update(updatePassword bool) error {
|
func (user *User) Update(updatePassword bool) error {
|
||||||
|
if RecordExists(&User{}, "username", user.Username, user.Id) {
|
||||||
|
return errors.New("用户名已存在!")
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
if updatePassword {
|
if updatePassword {
|
||||||
user.Password, err = common.Password2Hash(user.Password)
|
user.Password, err = common.Password2Hash(user.Password)
|
||||||
|
@ -42,7 +42,7 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
selfRoute.GET("/dashboard", controller.GetUserDashboard)
|
selfRoute.GET("/dashboard", controller.GetUserDashboard)
|
||||||
selfRoute.GET("/self", controller.GetSelf)
|
selfRoute.GET("/self", controller.GetSelf)
|
||||||
selfRoute.PUT("/self", controller.UpdateSelf)
|
selfRoute.PUT("/self", controller.UpdateSelf)
|
||||||
selfRoute.DELETE("/self", controller.DeleteSelf)
|
// selfRoute.DELETE("/self", controller.DeleteSelf)
|
||||||
selfRoute.GET("/token", controller.GenerateAccessToken)
|
selfRoute.GET("/token", controller.GenerateAccessToken)
|
||||||
selfRoute.GET("/aff", controller.GetAffCode)
|
selfRoute.GET("/aff", controller.GetAffCode)
|
||||||
selfRoute.POST("/topup", controller.TopUp)
|
selfRoute.POST("/topup", controller.TopUp)
|
||||||
|
@ -1,21 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import UserCard from 'ui-component/cards/UserCard';
|
import UserCard from 'ui-component/cards/UserCard';
|
||||||
import {
|
import { Card, Button, InputLabel, FormControl, OutlinedInput, Stack, Alert, Divider, Chip, Typography } from '@mui/material';
|
||||||
Card,
|
|
||||||
Button,
|
|
||||||
InputLabel,
|
|
||||||
FormControl,
|
|
||||||
OutlinedInput,
|
|
||||||
Stack,
|
|
||||||
Alert,
|
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
DialogContent,
|
|
||||||
DialogActions,
|
|
||||||
Divider,
|
|
||||||
Chip,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
|
||||||
import Grid from '@mui/material/Unstable_Grid2';
|
import Grid from '@mui/material/Unstable_Grid2';
|
||||||
import SubCard from 'ui-component/cards/SubCard';
|
import SubCard from 'ui-component/cards/SubCard';
|
||||||
import { IconBrandWechat, IconBrandGithub, IconMail, IconBrandTelegram } from '@tabler/icons-react';
|
import { IconBrandWechat, IconBrandGithub, IconMail, IconBrandTelegram } from '@tabler/icons-react';
|
||||||
@ -38,7 +23,6 @@ const validationSchema = Yup.object().shape({
|
|||||||
|
|
||||||
export default function Profile() {
|
export default function Profile() {
|
||||||
const [inputs, setInputs] = useState([]);
|
const [inputs, setInputs] = useState([]);
|
||||||
const [showAccountDeleteModal, setShowAccountDeleteModal] = useState(false);
|
|
||||||
const [turnstileEnabled, setTurnstileEnabled] = useState(false);
|
const [turnstileEnabled, setTurnstileEnabled] = useState(false);
|
||||||
const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
|
const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
|
||||||
const [turnstileToken, setTurnstileToken] = useState('');
|
const [turnstileToken, setTurnstileToken] = useState('');
|
||||||
@ -284,41 +268,11 @@ export default function Profile() {
|
|||||||
{inputs.access_token ? '重置访问令牌' : '生成访问令牌'}
|
{inputs.access_token ? '重置访问令牌' : '生成访问令牌'}
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid xs={12}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
onClick={() => {
|
|
||||||
setShowAccountDeleteModal(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
删除帐号
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</SubCard>
|
</SubCard>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
</UserCard>
|
</UserCard>
|
||||||
<Dialog open={showAccountDeleteModal} onClose={() => setShowAccountDeleteModal(false)} maxWidth={'md'}>
|
|
||||||
<DialogTitle sx={{ margin: '0px', fontWeight: 500, lineHeight: '1.55556', padding: '24px', fontSize: '1.125rem' }}>
|
|
||||||
危险操作
|
|
||||||
</DialogTitle>
|
|
||||||
<Divider />
|
|
||||||
<DialogContent>您正在删除自己的帐户,将清空所有数据且不可恢复</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => setShowAccountDeleteModal(false)}>取消</Button>
|
|
||||||
<Button
|
|
||||||
sx={{ color: 'error.main' }}
|
|
||||||
onClick={async () => {
|
|
||||||
setShowAccountDeleteModal(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
确定
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
<WechatModal open={openWechat} handleClose={handleWechatClose} wechatLogin={bindWeChat} qrCode={status.wechat_qrcode} />
|
<WechatModal open={openWechat} handleClose={handleWechatClose} wechatLogin={bindWeChat} qrCode={status.wechat_qrcode} />
|
||||||
<EmailModal
|
<EmailModal
|
||||||
open={openEmail}
|
open={openEmail}
|
||||||
|
Loading…
Reference in New Issue
Block a user