2023-04-22 12:39:27 +00:00
package model
import (
"errors"
2023-06-10 08:31:40 +00:00
"fmt"
2024-01-28 11:38:58 +00:00
"github.com/songquanpeng/one-api/common"
2024-03-10 07:56:19 +00:00
"github.com/songquanpeng/one-api/common/blacklist"
2024-01-28 11:38:58 +00:00
"github.com/songquanpeng/one-api/common/config"
2024-06-12 16:20:48 +00:00
"github.com/songquanpeng/one-api/common/helper"
2024-01-28 11:38:58 +00:00
"github.com/songquanpeng/one-api/common/logger"
2024-04-05 17:02:35 +00:00
"github.com/songquanpeng/one-api/common/random"
2023-04-26 13:40:56 +00:00
"gorm.io/gorm"
2023-04-26 12:54:39 +00:00
"strings"
2023-04-22 12:39:27 +00:00
)
2024-04-05 18:03:59 +00:00
const (
RoleGuestUser = 0
RoleCommonUser = 1
RoleAdminUser = 10
RoleRootUser = 100
)
const (
UserStatusEnabled = 1 // don't use 0, 0 is the default value!
UserStatusDisabled = 2 // also don't use 0
UserStatusDeleted = 3
)
2023-04-22 12:39:27 +00:00
// 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!
type User struct {
Id int ` json:"id" `
Username string ` json:"username" gorm:"unique;index" validate:"max=12" `
Password string ` json:"password" gorm:"not null;" validate:"min=8,max=20" `
DisplayName string ` json:"display_name" gorm:"index" validate:"max=20" `
2024-01-14 11:21:03 +00:00
Role int ` json:"role" gorm:"type:int;default:1" ` // admin, util
2023-04-22 12:39:27 +00:00
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" `
WeChatId string ` json:"wechat_id" gorm:"column:wechat_id;index" `
2024-04-05 04:10:43 +00:00
LarkId string ` json:"lark_id" gorm:"column:lark_id;index" `
2024-09-21 15:03:20 +00:00
OidcId string ` json:"oidc_id" gorm:"column:oidc_id;index" `
2023-05-21 02:01:02 +00:00
VerificationCode string ` json:"verification_code" gorm:"-:all" ` // this field is only for Email verification, don't save it to database!
2023-04-27 01:32:20 +00:00
AccessToken string ` json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex" ` // this token is for system management
2024-03-17 11:09:44 +00:00
Quota int64 ` json:"quota" gorm:"bigint;default:0" `
UsedQuota int64 ` json:"used_quota" gorm:"bigint;default:0;column:used_quota" ` // used quota
RequestCount int ` json:"request_count" gorm:"type:int;default:0;" ` // request number
2023-06-07 15:26:00 +00:00
Group string ` json:"group" gorm:"type:varchar(32);default:'default'" `
2023-06-17 10:12:58 +00:00
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" `
2023-04-22 12:39:27 +00:00
}
func GetMaxUserId ( ) int {
var user User
DB . Last ( & user )
return user . Id
}
2024-03-17 11:25:36 +00:00
func GetAllUsers ( startIdx int , num int , order string ) ( users [ ] * User , err error ) {
2024-04-05 18:03:59 +00:00
query := DB . Limit ( num ) . Offset ( startIdx ) . Omit ( "password" ) . Where ( "status != ?" , UserStatusDeleted )
2024-04-05 04:10:43 +00:00
switch order {
case "quota" :
query = query . Order ( "quota desc" )
case "used_quota" :
query = query . Order ( "used_quota desc" )
case "request_count" :
query = query . Order ( "request_count desc" )
default :
query = query . Order ( "id desc" )
}
err = query . Find ( & users ) . Error
return users , err
2023-04-22 12:39:27 +00:00
}
func SearchUsers ( keyword string ) ( users [ ] * User , err error ) {
2023-12-24 08:42:00 +00:00
if ! common . UsingPostgreSQL {
err = DB . Omit ( "password" ) . Where ( "id = ? or username LIKE ? or email LIKE ? or display_name LIKE ?" , keyword , keyword + "%" , keyword + "%" , keyword + "%" ) . Find ( & users ) . Error
} else {
err = DB . Omit ( "password" ) . Where ( "username LIKE ? or email LIKE ? or display_name LIKE ?" , keyword + "%" , keyword + "%" , keyword + "%" ) . Find ( & users ) . Error
}
2023-04-22 12:39:27 +00:00
return users , err
}
func GetUserById ( id int , selectAll bool ) ( * User , error ) {
if id == 0 {
return nil , errors . New ( "id 为空!" )
}
user := User { Id : id }
var err error = nil
if selectAll {
err = DB . First ( & user , "id = ?" , id ) . Error
} else {
2023-04-23 03:31:00 +00:00
err = DB . Omit ( "password" ) . First ( & user , "id = ?" , id ) . Error
2023-04-22 12:39:27 +00:00
}
return & user , err
}
2023-06-17 10:12:58 +00:00
func GetUserIdByAffCode ( affCode string ) ( int , error ) {
if affCode == "" {
return 0 , errors . New ( "affCode 为空!" )
}
var user User
err := DB . Select ( "id" ) . First ( & user , "aff_code = ?" , affCode ) . Error
return user . Id , err
}
2023-04-22 12:39:27 +00:00
func DeleteUserById ( id int ) ( err error ) {
if id == 0 {
return errors . New ( "id 为空!" )
}
user := User { Id : id }
return user . Delete ( )
}
2023-06-17 10:12:58 +00:00
func ( user * User ) Insert ( inviterId int ) error {
2023-04-22 12:39:27 +00:00
var err error
if user . Password != "" {
user . Password , err = common . Password2Hash ( user . Password )
if err != nil {
return err
}
}
2024-01-21 15:21:42 +00:00
user . Quota = config . QuotaForNewUser
2024-04-05 17:02:35 +00:00
user . AccessToken = random . GetUUID ( )
user . AffCode = random . GetRandomString ( 4 )
2023-06-10 08:31:40 +00:00
result := DB . Create ( user )
if result . Error != nil {
return result . Error
}
2024-01-21 15:21:42 +00:00
if config . QuotaForNewUser > 0 {
RecordLog ( user . Id , LogTypeSystem , fmt . Sprintf ( "新用户注册赠送 %s" , common . LogQuota ( config . QuotaForNewUser ) ) )
2023-06-10 08:31:40 +00:00
}
2023-06-17 10:12:58 +00:00
if inviterId != 0 {
2024-01-21 15:21:42 +00:00
if config . QuotaForInvitee > 0 {
_ = IncreaseUserQuota ( user . Id , config . QuotaForInvitee )
RecordLog ( user . Id , LogTypeSystem , fmt . Sprintf ( "使用邀请码赠送 %s" , common . LogQuota ( config . QuotaForInvitee ) ) )
2023-06-17 10:12:58 +00:00
}
2024-01-21 15:21:42 +00:00
if config . QuotaForInviter > 0 {
_ = IncreaseUserQuota ( inviterId , config . QuotaForInviter )
RecordLog ( inviterId , LogTypeSystem , fmt . Sprintf ( "邀请用户赠送 %s" , common . LogQuota ( config . QuotaForInviter ) ) )
2023-06-17 10:12:58 +00:00
}
}
2024-06-12 16:20:48 +00:00
// create default token
cleanToken := Token {
UserId : user . Id ,
Name : "default" ,
Key : random . GenerateKey ( ) ,
CreatedTime : helper . GetTimestamp ( ) ,
AccessedTime : helper . GetTimestamp ( ) ,
ExpiredTime : - 1 ,
RemainQuota : - 1 ,
UnlimitedQuota : true ,
}
result . Error = cleanToken . Insert ( )
if result . Error != nil {
// do not block
logger . SysError ( fmt . Sprintf ( "create default token for user %d failed: %s" , user . Id , result . Error . Error ( ) ) )
}
2023-06-10 08:31:40 +00:00
return nil
2023-04-22 12:39:27 +00:00
}
func ( user * User ) Update ( updatePassword bool ) error {
var err error
if updatePassword {
user . Password , err = common . Password2Hash ( user . Password )
if err != nil {
return err
}
}
2024-04-05 18:03:59 +00:00
if user . Status == UserStatusDisabled {
2024-03-10 07:56:19 +00:00
blacklist . BanUser ( user . Id )
2024-04-05 18:03:59 +00:00
} else if user . Status == UserStatusEnabled {
2024-03-10 07:56:19 +00:00
blacklist . UnbanUser ( user . Id )
}
2023-04-22 12:39:27 +00:00
err = DB . Model ( user ) . Updates ( user ) . Error
return err
}
func ( user * User ) Delete ( ) error {
if user . Id == 0 {
return errors . New ( "id 为空!" )
}
2024-03-10 07:56:19 +00:00
blacklist . BanUser ( user . Id )
2024-04-05 17:02:35 +00:00
user . Username = fmt . Sprintf ( "deleted_%s" , random . GetUUID ( ) )
2024-04-05 18:03:59 +00:00
user . Status = UserStatusDeleted
2024-03-10 07:56:19 +00:00
err := DB . Model ( user ) . Updates ( user ) . Error
2023-04-22 12:39:27 +00:00
return err
}
// ValidateAndFill check password & user status
func ( user * User ) ValidateAndFill ( ) ( err error ) {
// When querying with struct, GORM will only query with non-zero fields,
// that means if your field’ s value is 0, '', false or other zero values,
// it won’ t be used to build query conditions
password := user . Password
if user . Username == "" || password == "" {
return errors . New ( "用户名或密码为空" )
}
2024-01-14 06:08:39 +00:00
err = DB . Where ( "username = ?" , user . Username ) . First ( user ) . Error
if err != nil {
// we must make sure check username firstly
// consider this case: a malicious user set his username as other's email
err := DB . Where ( "email = ?" , user . Username ) . First ( user ) . Error
if err != nil {
return errors . New ( "用户名或密码错误,或用户已被封禁" )
}
}
2023-04-22 12:39:27 +00:00
okay := common . ValidatePasswordAndHash ( password , user . Password )
2024-04-05 18:03:59 +00:00
if ! okay || user . Status != UserStatusEnabled {
2023-04-22 12:39:27 +00:00
return errors . New ( "用户名或密码错误,或用户已被封禁" )
}
return nil
}
func ( user * User ) FillUserById ( ) error {
if user . Id == 0 {
return errors . New ( "id 为空!" )
}
DB . Where ( User { Id : user . Id } ) . First ( user )
return nil
}
func ( user * User ) FillUserByEmail ( ) error {
if user . Email == "" {
return errors . New ( "email 为空!" )
}
DB . Where ( User { Email : user . Email } ) . First ( user )
return nil
}
func ( user * User ) FillUserByGitHubId ( ) error {
if user . GitHubId == "" {
return errors . New ( "GitHub id 为空!" )
}
DB . Where ( User { GitHubId : user . GitHubId } ) . First ( user )
return nil
}
2024-04-05 04:10:43 +00:00
func ( user * User ) FillUserByLarkId ( ) error {
if user . LarkId == "" {
return errors . New ( "lark id 为空!" )
}
DB . Where ( User { LarkId : user . LarkId } ) . First ( user )
return nil
}
2024-09-21 15:03:20 +00:00
func ( user * User ) FillUserByOidcId ( ) error {
if user . OidcId == "" {
return errors . New ( "oidc id 为空!" )
}
DB . Where ( User { OidcId : user . OidcId } ) . First ( user )
return nil
}
2023-04-22 12:39:27 +00:00
func ( user * User ) FillUserByWeChatId ( ) error {
if user . WeChatId == "" {
return errors . New ( "WeChat id 为空!" )
}
DB . Where ( User { WeChatId : user . WeChatId } ) . First ( user )
return nil
}
func ( user * User ) FillUserByUsername ( ) error {
if user . Username == "" {
return errors . New ( "username 为空!" )
}
DB . Where ( User { Username : user . Username } ) . First ( user )
return nil
}
func IsEmailAlreadyTaken ( email string ) bool {
return DB . Where ( "email = ?" , email ) . Find ( & User { } ) . RowsAffected == 1
}
func IsWeChatIdAlreadyTaken ( wechatId string ) bool {
return DB . Where ( "wechat_id = ?" , wechatId ) . Find ( & User { } ) . RowsAffected == 1
}
func IsGitHubIdAlreadyTaken ( githubId string ) bool {
return DB . Where ( "github_id = ?" , githubId ) . Find ( & User { } ) . RowsAffected == 1
}
2024-04-05 04:10:43 +00:00
func IsLarkIdAlreadyTaken ( githubId string ) bool {
return DB . Where ( "lark_id = ?" , githubId ) . Find ( & User { } ) . RowsAffected == 1
}
2024-09-21 15:03:20 +00:00
func IsOidcIdAlreadyTaken ( oidcId string ) bool {
return DB . Where ( "oidc_id = ?" , oidcId ) . Find ( & User { } ) . RowsAffected == 1
}
2023-04-22 12:39:27 +00:00
func IsUsernameAlreadyTaken ( username string ) bool {
return DB . Where ( "username = ?" , username ) . Find ( & User { } ) . RowsAffected == 1
}
func ResetUserPasswordByEmail ( email string , password string ) error {
if email == "" || password == "" {
return errors . New ( "邮箱地址或密码为空!" )
}
hashedPassword , err := common . Password2Hash ( password )
if err != nil {
return err
}
err = DB . Model ( & User { } ) . Where ( "email = ?" , email ) . Update ( "password" , hashedPassword ) . Error
return err
}
2023-04-26 06:49:27 +00:00
func IsAdmin ( userId int ) bool {
if userId == 0 {
return false
}
var user User
err := DB . Where ( "id = ?" , userId ) . Select ( "role" ) . Find ( & user ) . Error
if err != nil {
2024-01-21 15:21:42 +00:00
logger . SysError ( "no such user " + err . Error ( ) )
2023-04-26 06:49:27 +00:00
return false
}
2024-04-05 18:03:59 +00:00
return user . Role >= RoleAdminUser
2023-04-26 06:49:27 +00:00
}
2023-04-26 12:54:39 +00:00
2023-09-03 13:31:58 +00:00
func IsUserEnabled ( userId int ) ( bool , error ) {
2023-04-27 07:05:33 +00:00
if userId == 0 {
2023-09-03 13:31:58 +00:00
return false , errors . New ( "user id is empty" )
2023-04-27 07:05:33 +00:00
}
var user User
err := DB . Where ( "id = ?" , userId ) . Select ( "status" ) . Find ( & user ) . Error
if err != nil {
2023-09-03 13:31:58 +00:00
return false , err
2023-04-27 07:05:33 +00:00
}
2024-04-05 18:03:59 +00:00
return user . Status == UserStatusEnabled , nil
2023-04-27 07:05:33 +00:00
}
2023-04-26 12:54:39 +00:00
func ValidateAccessToken ( token string ) ( user * User ) {
if token == "" {
return nil
}
token = strings . Replace ( token , "Bearer " , "" , 1 )
user = & User { }
if DB . Where ( "access_token = ?" , token ) . First ( user ) . RowsAffected == 1 {
return user
}
return nil
}
2023-04-26 13:40:56 +00:00
2024-03-13 12:00:51 +00:00
func GetUserQuota ( id int ) ( quota int64 , err error ) {
2023-04-26 13:40:56 +00:00
err = DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Select ( "quota" ) . Find ( & quota ) . Error
return quota , err
}
2024-03-13 12:00:51 +00:00
func GetUserUsedQuota ( id int ) ( quota int64 , err error ) {
2023-06-20 12:09:17 +00:00
err = DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Select ( "used_quota" ) . Find ( & quota ) . Error
return quota , err
}
2023-05-16 03:26:09 +00:00
func GetUserEmail ( id int ) ( email string , err error ) {
err = DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Select ( "email" ) . Find ( & email ) . Error
return email , err
}
2023-06-07 15:26:00 +00:00
func GetUserGroup ( id int ) ( group string , err error ) {
2023-10-22 10:38:29 +00:00
groupCol := "`group`"
if common . UsingPostgreSQL {
groupCol = ` "group" `
}
err = DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Select ( groupCol ) . Find ( & group ) . Error
2023-06-07 15:26:00 +00:00
return group , err
}
2024-03-13 12:00:51 +00:00
func IncreaseUserQuota ( id int , quota int64 ) ( err error ) {
2023-05-16 03:26:09 +00:00
if quota < 0 {
return errors . New ( "quota 不能为负数!" )
}
2024-01-21 15:21:42 +00:00
if config . BatchUpdateEnabled {
2023-09-03 06:58:20 +00:00
addNewRecord ( BatchUpdateTypeUserQuota , id , quota )
return nil
}
return increaseUserQuota ( id , quota )
}
2024-03-13 12:00:51 +00:00
func increaseUserQuota ( id int , quota int64 ) ( err error ) {
2023-08-12 16:51:48 +00:00
err = DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Update ( "quota" , gorm . Expr ( "quota + ?" , quota ) ) . Error
2023-05-04 02:20:39 +00:00
return err
}
2024-03-13 12:00:51 +00:00
func DecreaseUserQuota ( id int , quota int64 ) ( err error ) {
2023-05-16 03:26:09 +00:00
if quota < 0 {
return errors . New ( "quota 不能为负数!" )
}
2024-01-21 15:21:42 +00:00
if config . BatchUpdateEnabled {
2023-09-03 06:58:20 +00:00
addNewRecord ( BatchUpdateTypeUserQuota , id , - quota )
return nil
}
return decreaseUserQuota ( id , quota )
}
2024-03-13 12:00:51 +00:00
func decreaseUserQuota ( id int , quota int64 ) ( err error ) {
2023-08-12 16:51:48 +00:00
err = DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Update ( "quota" , gorm . Expr ( "quota - ?" , quota ) ) . Error
2023-04-26 13:40:56 +00:00
return err
}
2023-05-15 04:36:55 +00:00
func GetRootUserEmail ( ) ( email string ) {
2024-04-05 18:03:59 +00:00
DB . Model ( & User { } ) . Where ( "role = ?" , RoleRootUser ) . Select ( "email" ) . Find ( & email )
2023-05-15 04:36:55 +00:00
return email
}
2023-06-16 07:20:06 +00:00
2024-03-13 12:00:51 +00:00
func UpdateUserUsedQuotaAndRequestCount ( id int , quota int64 ) {
2024-01-21 15:21:42 +00:00
if config . BatchUpdateEnabled {
2023-10-14 07:04:52 +00:00
addNewRecord ( BatchUpdateTypeUsedQuota , id , quota )
addNewRecord ( BatchUpdateTypeRequestCount , id , 1 )
2023-09-03 06:58:20 +00:00
return
}
updateUserUsedQuotaAndRequestCount ( id , quota , 1 )
}
2024-03-13 12:00:51 +00:00
func updateUserUsedQuotaAndRequestCount ( id int , quota int64 , count int ) {
2023-08-12 16:51:48 +00:00
err := DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Updates (
2023-06-16 07:20:06 +00:00
map [ string ] interface { } {
"used_quota" : gorm . Expr ( "used_quota + ?" , quota ) ,
2023-09-03 06:58:20 +00:00
"request_count" : gorm . Expr ( "request_count + ?" , count ) ,
2023-06-16 07:20:06 +00:00
} ,
) . Error
if err != nil {
2024-01-21 15:21:42 +00:00
logger . SysError ( "failed to update user used quota and request count: " + err . Error ( ) )
2023-06-16 07:20:06 +00:00
}
}
2023-06-24 07:28:11 +00:00
2024-03-13 12:00:51 +00:00
func updateUserUsedQuota ( id int , quota int64 ) {
2023-10-14 07:04:52 +00:00
err := DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Updates (
map [ string ] interface { } {
"used_quota" : gorm . Expr ( "used_quota + ?" , quota ) ,
} ,
) . Error
if err != nil {
2024-01-21 15:21:42 +00:00
logger . SysError ( "failed to update user used quota: " + err . Error ( ) )
2023-10-14 07:04:52 +00:00
}
}
func updateUserRequestCount ( id int , count int ) {
err := DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Update ( "request_count" , gorm . Expr ( "request_count + ?" , count ) ) . Error
if err != nil {
2024-01-21 15:21:42 +00:00
logger . SysError ( "failed to update user request count: " + err . Error ( ) )
2023-10-14 07:04:52 +00:00
}
}
2023-06-24 07:28:11 +00:00
func GetUsernameById ( id int ) ( username string ) {
DB . Model ( & User { } ) . Where ( "id = ?" , id ) . Select ( "username" ) . Find ( & username )
return username
}