Полная зачистка legacy + закрытие fix24.1/fix24.2 + обновление логотипа
This commit is contained in:
@@ -1,181 +0,0 @@
|
||||
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"
|
||||
)
|
||||
|
||||
func Login(username string, plainPassword string) (string, bool, error) {
|
||||
account, err := dao.GetAccount("username = ? and role = 'admin' and deleted = 0", username)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
verified, legacy := util.VerifyPassword(plainPassword, *account.Pass)
|
||||
if !verified {
|
||||
return "", false, errors.New(constant.WrongPassword)
|
||||
}
|
||||
|
||||
if legacy {
|
||||
hash, hashErr := util.HashPassword(plainPassword)
|
||||
if hashErr == nil {
|
||||
_ = dao.UpdateAccount([]int64{*account.Id}, map[string]interface{}{"pass": hash})
|
||||
}
|
||||
}
|
||||
|
||||
accountBo := bo.AccountBo{
|
||||
Id: *account.Id,
|
||||
Username: *account.Username,
|
||||
Roles: []string{*account.Role},
|
||||
Deleted: *account.Deleted,
|
||||
}
|
||||
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 PageAccount(accountPageDto dto.AccountPageDto) ([]entity.Account, int64, error) {
|
||||
return dao.PageAccount(accountPageDto)
|
||||
}
|
||||
|
||||
func SaveAccount(account entity.Account) error {
|
||||
_, err := dao.SaveAccount(account)
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteAccount(ids []int64) error {
|
||||
return dao.DeleteAccount(ids)
|
||||
}
|
||||
|
||||
func UpdateAccount(account entity.Account) error {
|
||||
updates := map[string]interface{}{}
|
||||
if account.Username != nil && *account.Username != "" {
|
||||
updates["username"] = *account.Username
|
||||
}
|
||||
if account.Pass != nil && *account.Pass != "" {
|
||||
updates["pass"] = *account.Pass
|
||||
}
|
||||
if account.ConPass != nil && *account.ConPass != "" {
|
||||
updates["con_pass"] = fmt.Sprintf("%s.%s", *account.Username, *account.ConPass)
|
||||
}
|
||||
if account.Quota != nil {
|
||||
updates["quota"] = *account.Quota
|
||||
}
|
||||
if account.ExpireTime != nil {
|
||||
updates["expire_time"] = *account.ExpireTime
|
||||
}
|
||||
if account.Download != nil {
|
||||
updates["download"] = *account.Download
|
||||
}
|
||||
if account.Upload != nil {
|
||||
updates["upload"] = *account.Upload
|
||||
}
|
||||
if account.DeviceNo != nil {
|
||||
updates["device_no"] = *account.DeviceNo
|
||||
}
|
||||
if account.Deleted != nil {
|
||||
updates["deleted"] = *account.Deleted
|
||||
}
|
||||
if account.LoginAt != nil && *account.LoginAt > 0 {
|
||||
updates["login_at"] = *account.LoginAt
|
||||
}
|
||||
if account.ConAt != nil && *account.ConAt > 0 {
|
||||
updates["con_at"] = *account.ConAt
|
||||
}
|
||||
if account.Remark != nil {
|
||||
updates["remark"] = *account.Remark
|
||||
}
|
||||
return dao.UpdateAccount([]int64{*account.Id}, updates)
|
||||
}
|
||||
|
||||
func ResetTraffic(id int64) error {
|
||||
return dao.UpdateAccount([]int64{id}, map[string]interface{}{"download": 0, "upload": 0})
|
||||
}
|
||||
|
||||
func ExistAccountUsername(username string, id int64) bool {
|
||||
var err error
|
||||
if id != 0 {
|
||||
_, err = dao.GetAccount("username = ? and id != ?", username, id)
|
||||
} else {
|
||||
_, err = dao.GetAccount("username = ?", username)
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() == constant.WrongPassword {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func GetAccount(id int64) (entity.Account, error) {
|
||||
return dao.GetAccount("id = ?", id)
|
||||
}
|
||||
|
||||
func ListExportAccount() ([]bo.AccountExport, error) {
|
||||
accounts, err := dao.ListAccount(nil, nil)
|
||||
if err != nil {
|
||||
return nil, errors.New(constant.SysError)
|
||||
}
|
||||
var accountExports []bo.AccountExport
|
||||
for _, item := range accounts {
|
||||
accountExport := bo.AccountExport{
|
||||
Id: *item.Id,
|
||||
Username: *item.Username,
|
||||
Pass: *item.Pass,
|
||||
ConPass: *item.ConPass,
|
||||
Quota: *item.Quota,
|
||||
Download: *item.Download,
|
||||
Upload: *item.Upload,
|
||||
ExpireTime: *item.ExpireTime,
|
||||
DeviceNo: *item.DeviceNo,
|
||||
KickUtilTime: *item.KickUtilTime,
|
||||
Role: *item.Role,
|
||||
Deleted: *item.Deleted,
|
||||
CreateTime: *item.CreateTime,
|
||||
UpdateTime: *item.UpdateTime,
|
||||
LoginAt: *item.LoginAt,
|
||||
ConAt: *item.ConAt,
|
||||
Remark: *item.Remark,
|
||||
}
|
||||
accountExports = append(accountExports, accountExport)
|
||||
}
|
||||
return accountExports, nil
|
||||
}
|
||||
|
||||
func ReleaseKickAccount(id int64) error {
|
||||
return dao.UpdateAccount([]int64{id}, map[string]interface{}{"kick_util_time": 0})
|
||||
}
|
||||
|
||||
func UpsertAccount(accounts []entity.Account) error {
|
||||
return dao.UpsertAccount(accounts)
|
||||
}
|
||||
|
||||
func GetAccountInfo(c *gin.Context) (vo.AccountInfoVo, error) {
|
||||
myClaims, err := ParseToken(GetToken(c))
|
||||
if err != nil {
|
||||
return vo.AccountInfoVo{}, err
|
||||
}
|
||||
if myClaims.AccountBo.Deleted != 0 {
|
||||
return vo.AccountInfoVo{}, errors.New("this account has been disabled")
|
||||
}
|
||||
return vo.AccountInfoVo{
|
||||
Id: myClaims.AccountBo.Id,
|
||||
Username: myClaims.AccountBo.Username,
|
||||
Roles: myClaims.AccountBo.Roles,
|
||||
}, nil
|
||||
}
|
||||
+61
-34
@@ -2,12 +2,12 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
"hy2xs-admin/dao"
|
||||
"hy2xs-admin/model/bo"
|
||||
"hy2xs-admin/model/constant"
|
||||
"hy2xs-admin/model/entity"
|
||||
"hy2xs-admin/proxy"
|
||||
"hy2xs-admin/util"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -40,17 +40,17 @@ func CronHandleAccount() {
|
||||
}
|
||||
|
||||
func CronResetTraffic() {
|
||||
accounts, err := dao.ListAccount(nil, nil)
|
||||
peers, err := dao.ListPeer("1=1")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var ids []int64
|
||||
for _, item := range accounts {
|
||||
for _, item := range peers {
|
||||
ids = append(ids, *item.Id)
|
||||
}
|
||||
idsList := util.SplitArr(ids, 100)
|
||||
for _, item := range idsList {
|
||||
if err := dao.UpdateAccount(item, map[string]interface{}{"download": 0, "upload": 0}); err != nil {
|
||||
if err := dao.UpdatePeer(item, map[string]interface{}{"download_bytes": 0, "upload_bytes": 0}); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -62,38 +62,65 @@ func saveAccountTraffic(apiPort int64, trafficStatsSecret string) {
|
||||
}
|
||||
defer trafficMutex.Unlock()
|
||||
|
||||
hysteria2TrafficTime, err := dao.GetConfig("key = ?", constant.Hysteria2TrafficTime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
hysteria2TrafficTimeFloat, err := strconv.ParseFloat(*hysteria2TrafficTime.Value, 64)
|
||||
if err != nil {
|
||||
logrus.Errorf("hysteria2TrafficTime string conv int64 err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := proxy.NewHysteria2Api(apiPort).ListUsers(true, trafficStatsSecret)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(users) > 0 {
|
||||
userLists := util.SplitMap(users, 10)
|
||||
var wg sync.WaitGroup
|
||||
for _, userList := range userLists {
|
||||
wg.Add(1)
|
||||
go func(userList map[string]bo.Hysteria2UserTraffic) {
|
||||
defer wg.Done()
|
||||
for username, traffic := range userList {
|
||||
if err = dao.UpdateAccountTraffic(username, int64(float64(traffic.Rx)*hysteria2TrafficTimeFloat), int64(float64(traffic.Tx)*hysteria2TrafficTimeFloat)); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}(userList)
|
||||
if len(users) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
nowMs := time.Now().UnixMilli()
|
||||
hourStart := nowMs - (nowMs % int64(time.Hour/time.Millisecond))
|
||||
dayStart := nowMs - (nowMs % int64(24*time.Hour/time.Millisecond))
|
||||
|
||||
for key, traffic := range users {
|
||||
rxBytes := traffic.Rx
|
||||
txBytes := traffic.Tx
|
||||
if rxBytes == 0 && txBytes == 0 {
|
||||
continue
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
peer, peerErr := dao.GetPeer("auth_id = ?", key)
|
||||
if peerErr != nil {
|
||||
peer, peerErr = dao.GetPeer("name = ?", key)
|
||||
if peerErr != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if peer.Id == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
authId := key
|
||||
if peer.AuthId != nil && *peer.AuthId != "" {
|
||||
authId = *peer.AuthId
|
||||
}
|
||||
|
||||
sample := entity.TrafficSample{
|
||||
PeerId: peer.Id,
|
||||
AuthId: &authId,
|
||||
RxBytes: &rxBytes,
|
||||
TxBytes: &txBytes,
|
||||
SampledAt: &nowMs,
|
||||
}
|
||||
if err = dao.SaveTrafficSample(sample); err != nil {
|
||||
logrus.Errorf("save traffic_sample failed: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = dao.UpdatePeer([]int64{*peer.Id}, map[string]interface{}{
|
||||
"download_bytes": gorm.Expr("download_bytes + ?", rxBytes),
|
||||
"upload_bytes": gorm.Expr("upload_bytes + ?", txBytes),
|
||||
}); err != nil {
|
||||
logrus.Errorf("update peer traffic failed: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
_ = dao.UpsertTrafficAggregateHourly(*peer.Id, hourStart, rxBytes, txBytes)
|
||||
_ = dao.UpsertTrafficAggregateDaily(*peer.Id, dayStart, rxBytes, txBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func kickAccount(apiPort int64, trafficStatsSecret string) {
|
||||
if !kickMutex.TryLock() {
|
||||
return
|
||||
@@ -118,14 +145,14 @@ func kickAccount(apiPort int64, trafficStatsSecret string) {
|
||||
go func(usernameList []string) {
|
||||
defer wg.Done()
|
||||
now := time.Now().UnixMilli()
|
||||
accounts, err := dao.ListAccount("username in ? and (deleted = 1 or (quota > 0 and quota < download + upload)) or ? > expire_time or ? < kick_util_time", usernameList, now, now)
|
||||
peers, err := dao.ListPeer("name in ? and (disabled = 1 or (quota_bytes > 0 and quota_bytes < download_bytes + upload_bytes) or ? > expires_at or ? < banned_until)", usernameList, now, now)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kickUsernames := make([]string, len(accounts))
|
||||
kickUsernames := make([]string, len(peers))
|
||||
j := 0
|
||||
for _, item := range accounts {
|
||||
kickUsernames[j] = *item.Username
|
||||
for _, item := range peers {
|
||||
kickUsernames[j] = *item.Name
|
||||
j++
|
||||
}
|
||||
if err = proxy.NewHysteria2Api(apiPort).KickUsers(kickUsernames, trafficStatsSecret); err != nil {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"hy2xs-admin/dao"
|
||||
"hy2xs-admin/model/vo"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DashboardSummary() (vo.DashboardSummaryVo, error) {
|
||||
nowMs := time.Now().UnixMilli()
|
||||
summary := vo.DashboardSummaryVo{CollectedAt: nowMs}
|
||||
summary.Health.Collector = vo.DataHealthVo{Status: "stale", MessageKey: "dashboard.error.collectorStale"}
|
||||
summary.Health.Hysteria = vo.DataHealthVo{Status: "ok"}
|
||||
|
||||
snapshot := DashboardSnapshot()
|
||||
if snapshot.CollectedAt > 0 {
|
||||
summary.CollectedAt = snapshot.CollectedAt
|
||||
summary.System = snapshot.System
|
||||
summary.Hysteria = snapshot.Hysteria
|
||||
summary.Peers.OnlinePeers = snapshot.OnlinePeers
|
||||
summary.Peers.OnlineDevices = snapshot.OnlineDevices
|
||||
summary.Health.Collector = snapshot.CollectorState
|
||||
summary.Health.Hysteria = snapshot.HysteriaState
|
||||
if nowMs-snapshot.CollectedAt > int64(90*time.Second/time.Millisecond) {
|
||||
summary.Health.Collector = vo.DataHealthVo{Status: "stale", MessageKey: "dashboard.error.collectorStale", LastSuccessAt: snapshot.CollectedAt}
|
||||
}
|
||||
}
|
||||
|
||||
if peerSummary, err := dao.DashboardPeerSummary(nowMs); err == nil {
|
||||
summary.Peers.Total = peerSummary.Total
|
||||
summary.Peers.Enabled = peerSummary.Enabled
|
||||
summary.Peers.Disabled = peerSummary.Disabled
|
||||
summary.Peers.Expired = peerSummary.Expired
|
||||
}
|
||||
if trafficSummary, err := dao.DashboardTrafficSummary(); err == nil {
|
||||
summary.Traffic = trafficSummary
|
||||
}
|
||||
|
||||
summary.SecurityRisks = DashboardSecurityRisks(summary)
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func DashboardTimeseries(rangeKey string) (vo.DashboardTimeseriesVo, error) {
|
||||
nowMs := time.Now().UnixMilli()
|
||||
fromMs := nowMs - int64(24*time.Hour/time.Millisecond)
|
||||
r := strings.TrimSpace(rangeKey)
|
||||
if r == "1h" {
|
||||
fromMs = nowMs - int64(time.Hour/time.Millisecond)
|
||||
} else if r == "7d" {
|
||||
fromMs = nowMs - int64(7*24*time.Hour/time.Millisecond)
|
||||
} else if r == "30d" {
|
||||
fromMs = nowMs - int64(30*24*time.Hour/time.Millisecond)
|
||||
r = "30d"
|
||||
} else if r == "" {
|
||||
r = "24h"
|
||||
}
|
||||
|
||||
traffic, err := dao.DashboardTrafficTimeseries(fromMs, nowMs)
|
||||
if err != nil {
|
||||
return vo.DashboardTimeseriesVo{}, err
|
||||
}
|
||||
systemRows, err := dao.DashboardSystemTimeseries(fromMs, nowMs)
|
||||
if err != nil {
|
||||
return vo.DashboardTimeseriesVo{}, err
|
||||
}
|
||||
|
||||
return vo.DashboardTimeseriesVo{
|
||||
Range: r,
|
||||
Traffic: traffic,
|
||||
System: systemRows,
|
||||
CollectedAt: nowMs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DashboardTopPeers(rangeKey string, limit int) ([]vo.DashboardTopPeerVo, error) {
|
||||
nowMs := time.Now().UnixMilli()
|
||||
fromMs := nowMs - int64(24*time.Hour/time.Millisecond)
|
||||
if strings.TrimSpace(rangeKey) == "7d" {
|
||||
fromMs = nowMs - int64(7*24*time.Hour/time.Millisecond)
|
||||
} else if strings.TrimSpace(rangeKey) == "30d" {
|
||||
fromMs = nowMs - int64(30*24*time.Hour/time.Millisecond)
|
||||
}
|
||||
return dao.DashboardTopPeers(fromMs, nowMs, limit)
|
||||
}
|
||||
|
||||
func DashboardSecurity() ([]vo.SecurityRiskVo, error) {
|
||||
summary, err := DashboardSummary()
|
||||
if err != nil {
|
||||
return []vo.SecurityRiskVo{}, err
|
||||
}
|
||||
return summary.SecurityRisks, nil
|
||||
}
|
||||
|
||||
func DashboardSecurityRisks(summary vo.DashboardSummaryVo) []vo.SecurityRiskVo {
|
||||
risks := make([]vo.SecurityRiskVo, 0)
|
||||
if !summary.Hysteria.Running {
|
||||
risks = append(risks, vo.SecurityRiskVo{Key: "dashboard.security.hysteriaStopped", Severity: "critical", Dismissible: false})
|
||||
}
|
||||
if !summary.Hysteria.ApiReachable {
|
||||
risks = append(risks, vo.SecurityRiskVo{Key: "dashboard.security.trafficApiUnavailable", Severity: "warning", Dismissible: false})
|
||||
}
|
||||
return risks
|
||||
}
|
||||
|
||||
+12
-147
@@ -2,12 +2,10 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"hy2xs-admin/dao"
|
||||
"hy2xs-admin/model/bo"
|
||||
"hy2xs-admin/model/constant"
|
||||
"hy2xs-admin/proxy"
|
||||
"hy2xs-admin/util"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -38,7 +36,8 @@ func Hysteria2Auth(conPass string) (int64, string, error) {
|
||||
}
|
||||
|
||||
now := time.Now().UnixMilli()
|
||||
account, err := dao.GetAccount("con_pass = ? and deleted = 0 and (quota < 0 or quota > download + upload) and ? < expire_time and ? > kick_util_time", conPass, now, now)
|
||||
secretDigest := util.PeerSecretDigest(conPass)
|
||||
peer, err := dao.GetPeer("secret_digest = ? and disabled = 0 and (quota_bytes < 0 or quota_bytes > download_bytes + upload_bytes) and ? < expires_at and ? > banned_until", secretDigest, now, now)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
@@ -48,12 +47,12 @@ func Hysteria2Auth(conPass string) (int64, string, error) {
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
device, exist := onlineUsers[*account.Username]
|
||||
if exist && *account.DeviceNo <= device {
|
||||
device, exist := onlineUsers[*peer.Name]
|
||||
if exist && *peer.MaxDevices <= device {
|
||||
return 0, "", errors.New("device limited")
|
||||
}
|
||||
|
||||
return *account.Id, *account.Username, nil
|
||||
return *peer.Id, *peer.Name, nil
|
||||
}
|
||||
|
||||
func Hysteria2Online() (map[string]int64, error) {
|
||||
@@ -79,17 +78,17 @@ func Hysteria2Kick(ids []int64, kickUtilTime int64) error {
|
||||
if !Hysteria2IsRunning() {
|
||||
return errors.New("hysteria2 is not running")
|
||||
}
|
||||
if err := dao.UpdateAccount(ids, map[string]interface{}{"kick_util_time": kickUtilTime}); err != nil {
|
||||
if err := dao.UpdatePeer(ids, map[string]interface{}{"banned_until": kickUtilTime}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accounts, err := dao.ListAccount("id in ?", ids)
|
||||
peers, err := dao.ListPeer("id in ?", ids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var keys []string
|
||||
for _, item := range accounts {
|
||||
keys = append(keys, *item.Username)
|
||||
for _, item := range peers {
|
||||
keys = append(keys, *item.Name)
|
||||
}
|
||||
apiPort, err := GetHysteria2ApiPort()
|
||||
if err != nil {
|
||||
@@ -105,140 +104,6 @@ func Hysteria2Kick(ids []int64, kickUtilTime int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Hysteria2SubscribeUrl(accountId int64, protocol string) (string, error) {
|
||||
account, err := dao.GetAccount("id = ?", accountId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
publicHost, publicPort, err := resolvePublicEndpoint()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
config, err := dao.GetConfig("key = ?", constant.HUIWebContext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
webContext := ""
|
||||
if config.Value != nil && *config.Value != "/" && strings.HasPrefix(*config.Value, "/") {
|
||||
webContext = *config.Value
|
||||
}
|
||||
return fmt.Sprintf("%s//%s:%d%s/hui/%s", protocol, publicHost, publicPort, webContext, url.QueryEscape(*account.ConPass)), nil
|
||||
}
|
||||
|
||||
func Hysteria2Subscribe(conPass string, clientType string) (string, string, error) {
|
||||
hysteria2Config, err := GetHysteria2Config()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if hysteria2Config.Listen == nil || *hysteria2Config.Listen == "" {
|
||||
return "", "", errors.New("hysteria2 config is empty")
|
||||
}
|
||||
|
||||
account, err := dao.GetAccount("con_pass = ?", conPass)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
publicHost, publicPort, err := resolvePublicEndpoint()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
hysteria2Name := "hysteria2"
|
||||
hysteria2ConfigRemark, err := dao.GetConfig("key = ?", constant.Hysteria2ConfigRemark)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if *hysteria2ConfigRemark.Value != "" {
|
||||
hysteria2Name = *hysteria2ConfigRemark.Value
|
||||
}
|
||||
|
||||
userInfo := ""
|
||||
configStr := ""
|
||||
if clientType == constant.Shadowrocket || clientType == constant.Clash {
|
||||
userInfo = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d",
|
||||
*account.Upload,
|
||||
*account.Download,
|
||||
*account.Quota,
|
||||
*account.ExpireTime/1000)
|
||||
|
||||
hysteria2 := bo.Hysteria2{
|
||||
Name: hysteria2Name,
|
||||
Type: "hysteria2",
|
||||
Server: publicHost,
|
||||
Port: strconv.Itoa(publicPort),
|
||||
Password: conPass,
|
||||
}
|
||||
|
||||
if hysteria2Config.Bandwidth != nil {
|
||||
if hysteria2Config.Bandwidth.Up != nil &&
|
||||
*hysteria2Config.Bandwidth.Up != "" {
|
||||
hysteria2.Up = *hysteria2Config.Bandwidth.Up
|
||||
}
|
||||
if hysteria2Config.Bandwidth.Down != nil &&
|
||||
*hysteria2Config.Bandwidth.Down != "" {
|
||||
hysteria2.Down = *hysteria2Config.Bandwidth.Down
|
||||
}
|
||||
}
|
||||
|
||||
if hysteria2Config.Obfs != nil &&
|
||||
hysteria2Config.Obfs.Type != nil &&
|
||||
*hysteria2Config.Obfs.Type == "salamander" &&
|
||||
hysteria2Config.Obfs.Salamander != nil &&
|
||||
hysteria2Config.Obfs.Salamander.Password != nil &&
|
||||
*hysteria2Config.Obfs.Salamander.Password != "" {
|
||||
if clientType == constant.Shadowrocket {
|
||||
hysteria2.Obfs = *hysteria2Config.Obfs.Salamander.Password
|
||||
} else {
|
||||
hysteria2.Obfs = "salamander"
|
||||
hysteria2.ObfsPassword = *hysteria2Config.Obfs.Salamander.Password
|
||||
}
|
||||
}
|
||||
|
||||
if hysteria2Config.ACME != nil &&
|
||||
hysteria2Config.ACME.Domains != nil &&
|
||||
len(hysteria2Config.ACME.Domains) > 0 {
|
||||
hysteria2.Sni = hysteria2Config.ACME.Domains[0]
|
||||
}
|
||||
|
||||
hysteria2.SkipCertVerify = false
|
||||
|
||||
proxyGroup := bo.ProxyGroup{
|
||||
Name: "PROXY",
|
||||
Type: "select",
|
||||
Proxies: []string{hysteria2Name},
|
||||
}
|
||||
|
||||
clashConfig := bo.ClashConfig{
|
||||
ProxyGroups: []bo.ProxyGroup{
|
||||
proxyGroup,
|
||||
},
|
||||
Proxies: []interface{}{hysteria2},
|
||||
}
|
||||
clashConfigYaml, err := yaml.Marshal(&clashConfig)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
configStr = string(clashConfigYaml)
|
||||
if clientType == constant.Clash {
|
||||
clashExtension, err := GetConfig(constant.ClashExtension)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if clashExtension.Value != nil && *clashExtension.Value != "" {
|
||||
configStr = fmt.Sprintf("%s%s", configStr, *clashExtension.Value)
|
||||
}
|
||||
}
|
||||
} else if clientType == constant.V2rayN {
|
||||
hysteria2Url, err := Hysteria2Url(*account.Id)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
configStr = hysteria2Url
|
||||
}
|
||||
|
||||
return userInfo, configStr, nil
|
||||
}
|
||||
|
||||
func Hysteria2Url(accountId int64) (string, error) {
|
||||
hysteria2Config, err := GetHysteria2Config()
|
||||
if err != nil {
|
||||
@@ -252,7 +117,7 @@ func Hysteria2Url(accountId int64) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
account, err := dao.GetAccount("id = ?", accountId)
|
||||
peer, err := dao.GetPeer("id = ?", accountId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -281,7 +146,7 @@ func Hysteria2Url(accountId int64) (string, error) {
|
||||
sni = hysteria2Config.ACME.Domains[0]
|
||||
}
|
||||
|
||||
return buildHysteria2Url(*account.ConPass, hostname, port, obfsType, obfsPassword, sni, remark), nil
|
||||
return buildHysteria2Url(*peer.SecretCiphertext, hostname, port, obfsType, obfsPassword, sni, remark), nil
|
||||
}
|
||||
|
||||
func buildHysteria2Url(conPass string, hostname string, port int, obfsType string, obfsPassword string, sni string, remark string) string {
|
||||
|
||||
+2
-2
@@ -14,13 +14,13 @@ import (
|
||||
const TokenExpireDuration = time.Hour * 24
|
||||
|
||||
type MyClaims struct {
|
||||
AccountBo bo.AccountBo `json:"account"`
|
||||
Admin bo.AccountBo `json:"admin"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func GenToken(accountBo bo.AccountBo) (string, error) {
|
||||
c := MyClaims{
|
||||
AccountBo: accountBo,
|
||||
Admin: accountBo,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
|
||||
Issuer: "hy2xs-admin",
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"hy2xs-admin/dao"
|
||||
"hy2xs-admin/model/entity"
|
||||
"hy2xs-admin/model/vo"
|
||||
"hy2xs-admin/util"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type metricsSnapshot struct {
|
||||
CollectedAt int64
|
||||
System vo.DashboardSystemVo
|
||||
Hysteria vo.DashboardHysteriaVo
|
||||
OnlinePeers int64
|
||||
OnlineDevices int64
|
||||
CollectorState vo.DataHealthVo
|
||||
HysteriaState vo.DataHealthVo
|
||||
}
|
||||
|
||||
var metricsStore = struct {
|
||||
sync.RWMutex
|
||||
snapshot metricsSnapshot
|
||||
lastSuccessAt int64
|
||||
}{}
|
||||
|
||||
func CollectMetricsSnapshot() {
|
||||
nowMs := time.Now().UnixMilli()
|
||||
s := metricsSnapshot{
|
||||
CollectedAt: nowMs,
|
||||
CollectorState: vo.DataHealthVo{Status: "ok", LastSuccessAt: nowMs},
|
||||
HysteriaState: vo.DataHealthVo{Status: "ok", LastSuccessAt: nowMs},
|
||||
}
|
||||
|
||||
if cpuPercent, err := util.GetCpuPercent(); err == nil {
|
||||
s.System.CpuPercent = cpuPercent
|
||||
}
|
||||
if memPercent, err := util.GetMemPercent(); err == nil {
|
||||
s.System.MemPercent = memPercent
|
||||
}
|
||||
if memInfo, err := util.GetMemInfo(); err == nil {
|
||||
s.System.MemUsedBytes = memInfo.Used
|
||||
s.System.MemTotalBytes = memInfo.Total
|
||||
}
|
||||
if diskPercent, err := util.GetDiskPercent(); err == nil {
|
||||
s.System.DiskPercent = diskPercent
|
||||
}
|
||||
if diskInfo, err := util.GetDiskInfo(); err == nil {
|
||||
s.System.DiskUsedBytes = diskInfo.Used
|
||||
s.System.DiskTotalBytes = diskInfo.Total
|
||||
}
|
||||
|
||||
s.Hysteria.Running = Hysteria2IsRunning()
|
||||
s.Hysteria.Version = "-"
|
||||
if content, err := util.Exec(util.GetHysteria2BinPath() + " version"); err == nil {
|
||||
s.Hysteria.Version = content
|
||||
}
|
||||
|
||||
if onlineMap, err := Hysteria2Online(); err == nil {
|
||||
s.Hysteria.ApiReachable = true
|
||||
s.OnlinePeers = int64(len(onlineMap))
|
||||
for _, c := range onlineMap {
|
||||
s.OnlineDevices += c
|
||||
}
|
||||
} else {
|
||||
s.Hysteria.ApiReachable = false
|
||||
s.Hysteria.LastApiError = "dashboard.error.trafficApiUnavailable"
|
||||
s.HysteriaState.Status = "error"
|
||||
s.HysteriaState.MessageKey = "dashboard.error.trafficApiUnavailable"
|
||||
}
|
||||
|
||||
metricsStore.Lock()
|
||||
metricsStore.snapshot = s
|
||||
metricsStore.lastSuccessAt = nowMs
|
||||
metricsStore.Unlock()
|
||||
|
||||
running := int64(0)
|
||||
if s.Hysteria.Running {
|
||||
running = 1
|
||||
}
|
||||
diskPath := "/"
|
||||
metric := entity.MetricSample{
|
||||
SampledAt: &nowMs,
|
||||
CpuPercent: &s.System.CpuPercent,
|
||||
Load1: func() *float64 { v := float64(0); return &v }(),
|
||||
MemUsedBytes: func() *int64 { v := int64(s.System.MemUsedBytes); return &v }(),
|
||||
MemTotalBytes: func() *int64 { v := int64(s.System.MemTotalBytes); return &v }(),
|
||||
MemPercent: &s.System.MemPercent,
|
||||
DiskPath: &diskPath,
|
||||
DiskUsedBytes: func() *int64 { v := int64(s.System.DiskUsedBytes); return &v }(),
|
||||
DiskTotalBytes: func() *int64 { v := int64(s.System.DiskTotalBytes); return &v }(),
|
||||
DiskPercent: &s.System.DiskPercent,
|
||||
HysteriaRunning: &running,
|
||||
OnlinePeers: &s.OnlinePeers,
|
||||
OnlineDevices: &s.OnlineDevices,
|
||||
}
|
||||
_ = dao.SaveMetricSample(metric)
|
||||
}
|
||||
|
||||
func CleanupMetricsRetention() {
|
||||
cutoff := time.Now().Add(-72 * time.Hour).UnixMilli()
|
||||
_ = dao.CleanupMetricSample(cutoff)
|
||||
}
|
||||
|
||||
func DashboardSnapshot() metricsSnapshot {
|
||||
metricsStore.RLock()
|
||||
defer metricsStore.RUnlock()
|
||||
return metricsStore.snapshot
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hy2xs-admin/model/constant"
|
||||
"hy2xs-admin/model/vo"
|
||||
"hy2xs-admin/util"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func MonitorSystem() (vo.SystemMonitorVo, error) {
|
||||
cpuPercent, err := util.GetCpuPercent()
|
||||
if err != nil {
|
||||
return vo.SystemMonitorVo{}, errors.New("cpu query failed")
|
||||
}
|
||||
memPercent, err := util.GetMemPercent()
|
||||
if err != nil {
|
||||
return vo.SystemMonitorVo{}, errors.New("mem query failed")
|
||||
}
|
||||
diskPercent, err := util.GetDiskPercent()
|
||||
if err != nil {
|
||||
return vo.SystemMonitorVo{}, errors.New("disk query failed")
|
||||
}
|
||||
return vo.SystemMonitorVo{
|
||||
HUIVersion: constant.Version,
|
||||
CpuPercent: cpuPercent,
|
||||
MemPercent: memPercent,
|
||||
DiskPercent: diskPercent,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func MonitorHysteria2() (vo.Hysteria2MonitorVo, error) {
|
||||
var hysteria2MonitorVo vo.Hysteria2MonitorVo
|
||||
onlineUsers, err := Hysteria2Online()
|
||||
if err != nil {
|
||||
return hysteria2MonitorVo, err
|
||||
}
|
||||
|
||||
if len(onlineUsers) > 0 {
|
||||
hysteria2MonitorVo.UserTotal = int64(len(onlineUsers))
|
||||
var deviceTotal int64 = 0
|
||||
for _, value := range onlineUsers {
|
||||
deviceTotal += value
|
||||
}
|
||||
hysteria2MonitorVo.DeviceTotal = deviceTotal
|
||||
}
|
||||
|
||||
hysteria2MonitorVo.Version = "-"
|
||||
content, err := util.Exec(fmt.Sprintf("%s version", util.GetHysteria2BinPath()))
|
||||
if err == nil {
|
||||
pattern := `v\d+\.\d+\.\d+`
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllString(strings.TrimSpace(content), -1)
|
||||
if len(matches) > 0 {
|
||||
hysteria2MonitorVo.Version = matches[0]
|
||||
}
|
||||
}
|
||||
|
||||
running := Hysteria2IsRunning()
|
||||
hysteria2MonitorVo.Running = running
|
||||
return hysteria2MonitorVo, nil
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user