package service import ( "errors" "fmt" "github.com/gin-gonic/gin" "hy2xs-admin/dao" "hy2xs-admin/model/bo" "hy2xs-admin/model/constant" "hy2xs-admin/model/dto" "hy2xs-admin/model/entity" "hy2xs-admin/model/vo" "hy2xs-admin/util" "time" ) func Login(username string, plainPassword string) (string, bool, error) { account, err := dao.GetAdminUser("username = ? and status = 1", username) if err != nil { return "", false, err } verified, legacy := util.VerifyPassword(plainPassword, *account.PasswordHash) if !verified { return "", false, errors.New(constant.WrongPassword) } if legacy { hash, hashErr := util.HashPassword(plainPassword) if hashErr == nil { _ = dao.UpdateAdminUser([]int64{*account.Id}, map[string]interface{}{"password_hash": hash}) } } accountBo := bo.AccountBo{ Id: *account.Id, Username: *account.Username, Roles: []string{"admin"}, Deleted: 0, } token, tokenErr := GenToken(accountBo) if tokenErr != nil { return "", false, tokenErr } requirePasswordChange := legacy if account.ForcePasswordChange != nil { requirePasswordChange = *account.ForcePasswordChange != 0 } return token, requirePasswordChange, nil } func PagePeer(peerPageDto dto.PeerPageDto) ([]entity.Account, int64, error) { peers, total, err := dao.PagePeer(peerPageDto) if err != nil { return nil, 0, err } accounts := make([]entity.Account, 0, len(peers)) for _, p := range peers { role := "user" acc := entity.Account{ BaseEntity: p.BaseEntity, Username: p.Name, ConPass: p.SecretCiphertext, Quota: p.QuotaBytes, Download: p.DownloadBytes, Upload: p.UploadBytes, ExpireTime: p.ExpiresAt, KickUtilTime: p.BannedUntil, DeviceNo: p.MaxDevices, Role: &role, Deleted: p.Disabled, ConAt: p.LastConnectionAt, Remark: p.Remark, } accounts = append(accounts, acc) } return accounts, total, nil } func SavePeer(account entity.Account) error { if account.Username == nil || *account.Username == "" { return errors.New(constant.InvalidError) } secret := "" if account.ConPass != nil && *account.ConPass != "" { secret = *account.ConPass } else { generated, genErr := util.RandomString(24) if genErr != nil { return genErr } secret = fmt.Sprintf("%s.%s", *account.Username, generated) } authId, authErr := util.RandomString(18) if authErr != nil { return authErr } secretDigest := util.PeerSecretDigest(secret) peer := entity.Peer{ Name: account.Username, Remark: account.Remark, AuthId: &authId, SecretDigest: &secretDigest, SecretCiphertext: &secret, QuotaBytes: account.Quota, ExpiresAt: account.ExpireTime, MaxDevices: account.DeviceNo, Disabled: account.Deleted, } _, err := dao.SavePeer(peer) return err } func DeletePeer(ids []int64) error { return dao.DeletePeer(ids) } func UpdatePeer(account entity.Account) error { updates := map[string]interface{}{} if account.Username != nil && *account.Username != "" { updates["username"] = *account.Username } _ = account.Pass if account.ConPass != nil && *account.ConPass != "" { updates["secret_ciphertext"] = *account.ConPass updates["secret_digest"] = util.PeerSecretDigest(*account.ConPass) } if account.Quota != nil { updates["quota_bytes"] = *account.Quota } if account.ExpireTime != nil { updates["expires_at"] = *account.ExpireTime } if account.Download != nil { updates["download_bytes"] = *account.Download } if account.Upload != nil { updates["upload_bytes"] = *account.Upload } if account.DeviceNo != nil { updates["max_devices"] = *account.DeviceNo } if account.Deleted != nil { updates["disabled"] = *account.Deleted } if account.LoginAt != nil && *account.LoginAt > 0 { updates["login_at"] = *account.LoginAt } if account.ConAt != nil && *account.ConAt > 0 { updates["last_connection_at"] = *account.ConAt } if account.Remark != nil { updates["remark"] = *account.Remark } return dao.UpdatePeer([]int64{*account.Id}, updates) } func ResetTraffic(id int64) error { return dao.UpdatePeer([]int64{id}, map[string]interface{}{"download_bytes": 0, "upload_bytes": 0}) } func ExistPeerName(username string, id int64) bool { var err error if id != 0 { _, err = dao.GetPeer("name = ? and id != ?", username, id) } else { _, err = dao.GetPeer("name = ?", username) } if err != nil { if err.Error() == constant.WrongPassword { return false } } return true } func GetPeer(id int64) (entity.Account, error) { peer, err := dao.GetPeer("id = ?", id) if err != nil { return entity.Account{}, err } role := "user" return entity.Account{ BaseEntity: peer.BaseEntity, Username: peer.Name, ConPass: peer.SecretCiphertext, Quota: peer.QuotaBytes, Download: peer.DownloadBytes, Upload: peer.UploadBytes, ExpireTime: peer.ExpiresAt, DeviceNo: peer.MaxDevices, KickUtilTime: peer.BannedUntil, ConAt: peer.LastConnectionAt, Deleted: peer.Disabled, Remark: peer.Remark, Role: &role, }, nil } func GetAdminAccount(id int64) (entity.AdminUser, error) { return dao.GetAdminUser("id = ?", id) } func ListExportPeer() ([]bo.AccountExport, error) { peers, err := dao.ListPeer("1=1") if err != nil { return nil, errors.New(constant.SysError) } var accountExports []bo.AccountExport for _, item := range peers { role := "user" conPass := "" if item.SecretCiphertext != nil { conPass = *item.SecretCiphertext } accountExport := bo.AccountExport{ Id: *item.Id, Username: *item.Name, Pass: "", ConPass: conPass, Quota: *item.QuotaBytes, Download: *item.DownloadBytes, Upload: *item.UploadBytes, ExpireTime: *item.ExpiresAt, DeviceNo: *item.MaxDevices, KickUtilTime: *item.BannedUntil, Role: role, Deleted: *item.Disabled, CreateTime: *item.CreateTime, UpdateTime: *item.UpdateTime, LoginAt: 0, ConAt: *item.LastConnectionAt, Remark: *item.Remark, } accountExports = append(accountExports, accountExport) } return accountExports, nil } func ReleaseKickPeer(id int64) error { return dao.UpdatePeer([]int64{id}, map[string]interface{}{"banned_until": 0}) } func UpsertPeer(accounts []entity.Account) error { for _, account := range accounts { if account.Id != nil && *account.Id > 0 { if err := UpdatePeer(account); err != nil { return err } continue } if err := SavePeer(account); err != nil { return err } } return nil } func GetAdminInfo(c *gin.Context) (vo.AccountInfoVo, error) { myClaims, err := ParseToken(GetToken(c)) if err != nil { return vo.AccountInfoVo{}, err } if myClaims.Admin.Deleted != 0 { return vo.AccountInfoVo{}, errors.New("this account has been disabled") } admin, err := dao.GetAdminUser("id = ?", myClaims.Admin.Id) if err != nil { return vo.AccountInfoVo{}, err } if admin.Status != nil && *admin.Status == 0 { return vo.AccountInfoVo{}, errors.New("this account has been disabled") } return vo.AccountInfoVo{ Id: myClaims.Admin.Id, Username: myClaims.Admin.Username, Roles: myClaims.Admin.Roles, }, nil } func UpdatePeerLastConnectionAt(id int64, conAt int64) error { return dao.UpdatePeer([]int64{id}, map[string]interface{}{"last_connection_at": conAt}) } func UpdateAdminLastLoginAt(id int64, loginAt int64) error { return dao.UpdateAdminUser([]int64{id}, map[string]interface{}{"last_login_at": loginAt}) } func ChangeAdminPassword(c *gin.Context, oldPassword string, newPassword string) error { info, err := GetAdminInfo(c) if err != nil { return err } admin, err := dao.GetAdminUser("id = ?", info.Id) if err != nil { return err } if admin.PasswordHash == nil { return errors.New(constant.SysError) } verified, _ := util.VerifyPassword(oldPassword, *admin.PasswordHash) if !verified { return errors.New(constant.WrongPassword) } hash, hashErr := util.HashPassword(newPassword) if hashErr != nil { return hashErr } nowMs := time.Now().UnixMilli() currentTokenVersion := int64(1) if admin.TokenVersion != nil && *admin.TokenVersion > 0 { currentTokenVersion = *admin.TokenVersion } return dao.UpdateAdminUser([]int64{info.Id}, map[string]interface{}{ "password_hash": hash, "force_password_change": 0, "password_changed_at": nowMs, "token_version": currentTokenVersion + 1, }) }