diff --git a/apps/cmd/reset.go b/apps/cmd/reset.go
index bdff36b..2861b3d 100644
--- a/apps/cmd/reset.go
+++ b/apps/cmd/reset.go
@@ -66,8 +66,8 @@ func runReset(cmd *cobra.Command, args []string) {
tokenVersion = *admin.TokenVersion + 1
}
if err = dao.UpdateAdminUser([]int64{*admin.Id}, map[string]interface{}{
- "username": username,
- "password_hash": func() string {
+ "username": username,
+ "password_hash": func() string {
hash, _ := util.HashPassword(password)
return hash
}(),
diff --git a/apps/controller/admin_user.go b/apps/controller/admin_user.go
index d83c368..a9e6f19 100644
--- a/apps/controller/admin_user.go
+++ b/apps/controller/admin_user.go
@@ -36,4 +36,3 @@ func AdminSecurity(c *gin.Context) {
}
vo.Success(gin.H{"forcePasswordChange": info.ForcePasswordChange}, c)
}
-
diff --git a/apps/controller/dashboard.go b/apps/controller/dashboard.go
index 48a79a6..8ef93f4 100644
--- a/apps/controller/dashboard.go
+++ b/apps/controller/dashboard.go
@@ -50,4 +50,3 @@ func DashboardSecurity(c *gin.Context) {
}
vo.Success(data, c)
}
-
diff --git a/apps/controller/peer.go b/apps/controller/peer.go
index a1f2253..0034dbc 100644
--- a/apps/controller/peer.go
+++ b/apps/controller/peer.go
@@ -222,4 +222,3 @@ func PeerClientConfig(c *gin.Context) {
}
vo.Success(data, c)
}
-
diff --git a/apps/dao/admin_user.go b/apps/dao/admin_user.go
index e8e06c3..853deaa 100644
--- a/apps/dao/admin_user.go
+++ b/apps/dao/admin_user.go
@@ -40,4 +40,3 @@ func UpdateAdminUser(ids []int64, updates map[string]interface{}) error {
}
return nil
}
-
diff --git a/apps/dao/dashboard.go b/apps/dao/dashboard.go
index 7d1202d..dda0a81 100644
--- a/apps/dao/dashboard.go
+++ b/apps/dao/dashboard.go
@@ -152,4 +152,3 @@ func DashboardSystemTimeseries(fromMs int64, toMs int64) ([]vo.DashboardSeriesPo
}
return rows, nil
}
-
diff --git a/apps/dao/peer.go b/apps/dao/peer.go
index 80a0cec..a1a8fe3 100644
--- a/apps/dao/peer.go
+++ b/apps/dao/peer.go
@@ -80,23 +80,3 @@ func PagePeer(peerPageDto dto.PeerPageDto) ([]entity.Peer, int64, error) {
}
return peers, total, nil
}
-
-func UpdatePeerTraffic(name string, download int64, upload int64) error {
- if upload == 0 && download == 0 {
- return nil
- }
- updates := map[string]interface{}{}
- if download != 0 {
- updates["download_bytes"] = gorm.Expr("download_bytes + ?", download)
- }
- if upload != 0 {
- updates["upload_bytes"] = gorm.Expr("upload_bytes + ?", upload)
- }
- updates["update_time"] = time.Now().Format("2006-01-02 15:04:05")
- if tx := sqliteDB.Model(&entity.Peer{}).Where("name = ?", name).Updates(updates); tx.Error != nil {
- logrus.Errorf("%v", tx.Error)
- return errors.New(constant.SysError)
- }
- return nil
-}
-
diff --git a/apps/dao/sqlite.go b/apps/dao/sqlite.go
index 6e8f1ce..ab1be53 100644
--- a/apps/dao/sqlite.go
+++ b/apps/dao/sqlite.go
@@ -289,8 +289,8 @@ func migrateLegacyAccounts() error {
if !tableExists("account") {
return nil
}
- var accounts []entity.Account
- if tx := sqliteDB.Model(&entity.Account{}).Order("id asc").Find(&accounts); tx.Error != nil {
+ var accounts []entity.LegacyAccount
+ if tx := sqliteDB.Model(&entity.LegacyAccount{}).Order("id asc").Find(&accounts); tx.Error != nil {
logrus.Errorf("sqlite legacy account query err: %v", tx.Error)
return errors.New("sqlite legacy account query err")
}
@@ -615,17 +615,17 @@ func CloseSqliteDB() error {
}
func IsSqliteReady() bool {
- if sqliteDB == nil {
- return false
- }
- db, err := sqliteDB.DB()
- if err != nil {
- return false
- }
- if err := db.Ping(); err != nil {
- return false
- }
- return true
+ if sqliteDB == nil {
+ return false
+ }
+ db, err := sqliteDB.DB()
+ if err != nil {
+ return false
+ }
+ if err := db.Ping(); err != nil {
+ return false
+ }
+ return true
}
func Paginate(pageNum *int64, pageSize *int64) func(db *gorm.DB) *gorm.DB {
@@ -641,4 +641,3 @@ func Paginate(pageNum *int64, pageSize *int64) func(db *gorm.DB) *gorm.DB {
return db.Offset(int((num - 1) * size)).Limit(int(size))
}
}
-
diff --git a/apps/dao/traffic.go b/apps/dao/traffic.go
index cd725e5..eb83720 100644
--- a/apps/dao/traffic.go
+++ b/apps/dao/traffic.go
@@ -72,4 +72,3 @@ func UpsertTrafficAggregateDaily(peerId int64, dayStart int64, rxBytes int64, tx
func gormExprAdd(column string, delta int64) interface{} {
return gorm.Expr(fmt.Sprintf("%s + ?", column), delta)
}
-
diff --git a/apps/frontend/src/lang/package/en.ts b/apps/frontend/src/lang/package/en.ts
index 5164b76..d832df1 100644
--- a/apps/frontend/src/lang/package/en.ts
+++ b/apps/frontend/src/lang/package/en.ts
@@ -25,6 +25,18 @@ export default {
stale: "Dashboard data is stale. Retrying automatically...",
topPeers24h: "Top peers (24h)",
refreshFailed: "Failed to refresh dashboard data",
+ cpu: "CPU",
+ ram: "RAM",
+ disk: "Disk",
+ peers: "Peers",
+ onlinePeers: "Online peers",
+ onlineDevices: "Online devices",
+ todayDownload: "Today download",
+ todayUpload: "Today upload",
+ trafficChart: "Traffic & System Timeseries",
+ download: "Download",
+ upload: "Upload",
+ total: "Total",
},
admin: {
changePasswordTitle: "Change password",
@@ -102,8 +114,22 @@ export default {
"I want to be a shooting star, cutting through the darkness, just to illuminate your dreams, good night🌛!",
},
peer: {
+ name: "Peer",
remark: "Remark",
username: "Username",
+ secret: "Secret",
+ maxDevices: "Max devices",
+ disabled: "Disabled",
+ status: "Status",
+ traffic: "Traffic",
+ devices: "Devices",
+ lastConnectionAt: "Last connection",
+ overview: "Overview",
+ authId: "Auth ID",
+ clientUri: "Client URI",
+ clientQr: "Client QR",
+ unlimited: "Unlimited",
+ secretRotateConfirm: "Rotate peer secret? Existing client configurations will stop working until updated.",
pass: "Pass",
conPass: "ConPass",
quota: "Quota",
diff --git a/apps/frontend/src/lang/package/ru.ts b/apps/frontend/src/lang/package/ru.ts
index b1159d8..1b9ce36 100644
--- a/apps/frontend/src/lang/package/ru.ts
+++ b/apps/frontend/src/lang/package/ru.ts
@@ -23,6 +23,18 @@ export default {
stale: "Данные дашборда устарели. Выполняется автоматическая повторная попытка...",
topPeers24h: "Топ пиров (24ч)",
refreshFailed: "Не удалось обновить данные дашборда",
+ cpu: "CPU",
+ ram: "RAM",
+ disk: "Диск",
+ peers: "Пиры",
+ onlinePeers: "Пиры онлайн",
+ onlineDevices: "Устройства онлайн",
+ todayDownload: "Скачано за сегодня",
+ todayUpload: "Отдано за сегодня",
+ trafficChart: "Трафик и системные метрики",
+ download: "Скачано",
+ upload: "Отдано",
+ total: "Всего",
},
admin: {
changePasswordTitle: "Смена пароля",
@@ -97,8 +109,22 @@ export default {
greeting5: "Доброй ночи,",
},
peer: {
+ name: "Пир",
remark: "Комментарий",
username: "Логин",
+ secret: "Секрет",
+ maxDevices: "Лимит устройств",
+ disabled: "Отключён",
+ status: "Статус",
+ traffic: "Трафик",
+ devices: "Устройства",
+ lastConnectionAt: "Последнее подключение",
+ overview: "Обзор",
+ authId: "Auth ID",
+ clientUri: "URI клиента",
+ clientQr: "QR клиента",
+ unlimited: "Безлимит",
+ secretRotateConfirm: "Сменить секрет пира? Текущие клиентские конфиги перестанут работать до обновления.",
pass: "Пароль входа",
conPass: "Пароль подключения",
quota: "Квота",
diff --git a/apps/frontend/src/types/components.d.ts b/apps/frontend/src/types/components.d.ts
index 8ba1500..66a8e3c 100644
--- a/apps/frontend/src/types/components.d.ts
+++ b/apps/frontend/src/types/components.d.ts
@@ -14,6 +14,7 @@ declare module '@vue/runtime-core' {
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
+ ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
@@ -26,6 +27,9 @@ declare module '@vue/runtime-core' {
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
+ ElProgress: typeof import('element-plus/es')['ElProgress']
+ ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
+ ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
diff --git a/apps/frontend/src/views/dashboard/index.vue b/apps/frontend/src/views/dashboard/index.vue
index ed55cc2..4994096 100644
--- a/apps/frontend/src/views/dashboard/index.vue
+++ b/apps/frontend/src/views/dashboard/index.vue
@@ -29,27 +29,48 @@
/>
- CPU: {{ summary.system.cpuPercent }}%
- RAM: {{ summary.system.memPercent }}%
- Disk: {{ summary.system.diskPercent }}%
- Peers: {{ summary.peers.total }}
- Online peers: {{ summary.peers.onlinePeers }}
- Online devices: {{ summary.peers.onlineDevices }}
- Today download: {{ formatBytes(summary.traffic.todayDownloadBytes || 0) }}
- Today upload: {{ formatBytes(summary.traffic.todayUploadBytes || 0) }}
+ {{ $t("dashboard.cpu") }}: {{ summary.system.cpuPercent }}%
+ {{ $t("dashboard.ram") }}: {{ summary.system.memPercent }}%
+ {{ $t("dashboard.disk") }}: {{ summary.system.diskPercent }}%
+ {{ $t("dashboard.peers") }}: {{ summary.peers.total }}
+ {{ $t("dashboard.onlinePeers") }}: {{ summary.peers.onlinePeers }}
+ {{ $t("dashboard.onlineDevices") }}: {{ summary.peers.onlineDevices }}
+ {{ $t("dashboard.todayDownload") }}: {{ formatBytes(summary.traffic.todayDownloadBytes || 0) }}
+ {{ $t("dashboard.todayUpload") }}: {{ formatBytes(summary.traffic.todayUploadBytes || 0) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $t("dashboard.topPeers24h") }}
-
-
+
+
{{ formatBytes(scope.row.download || 0) }}
-
+
{{ formatBytes(scope.row.upload || 0) }}
-
+
{{ formatBytes(scope.row.total || 0) }}
@@ -60,9 +81,10 @@
+
+
diff --git a/apps/middleware/admin.go b/apps/middleware/admin.go
index 370f4d2..5ee91ac 100644
--- a/apps/middleware/admin.go
+++ b/apps/middleware/admin.go
@@ -2,21 +2,27 @@ package middleware
import (
"github.com/gin-gonic/gin"
+ "hy2xs-admin/model/bo"
"hy2xs-admin/model/constant"
"hy2xs-admin/model/vo"
- "hy2xs-admin/service"
"hy2xs-admin/util"
)
func AdminHandler() gin.HandlerFunc {
return func(c *gin.Context) {
- myClaims, err := service.ParseToken(service.GetToken(c))
- if err != nil {
- vo.Fail(err.Error(), c)
+ claimsRaw, ok := c.Get("adminClaims")
+ if !ok {
+ vo.Fail(constant.UnauthorizedError, c)
c.Abort()
return
}
- if !util.ArrContain(myClaims.Admin.Roles, "admin") {
+ claims, castOK := claimsRaw.(bo.AccountBo)
+ if !castOK {
+ vo.Fail(constant.IllegalTokenError, c)
+ c.Abort()
+ return
+ }
+ if !util.ArrContain(claims.Roles, "admin") {
vo.Fail(constant.ForbiddenError, c)
c.Abort()
return
diff --git a/apps/middleware/jwt.go b/apps/middleware/jwt.go
index 26a19c2..e960bf4 100644
--- a/apps/middleware/jwt.go
+++ b/apps/middleware/jwt.go
@@ -48,6 +48,7 @@ func JWTHandler() gin.HandlerFunc {
c.Abort()
return
}
+ c.Set("adminClaims", myClaims.Admin)
c.Next()
}
}
diff --git a/apps/model/bo/account.go b/apps/model/bo/account.go
index 20dc32d..61e3d0f 100644
--- a/apps/model/bo/account.go
+++ b/apps/model/bo/account.go
@@ -1,7 +1,5 @@
package bo
-import "time"
-
type AccountBo struct {
Id int64 `json:"id"`
Username string `json:"username"`
@@ -10,26 +8,6 @@ type AccountBo struct {
TokenVersion int64 `json:"tokenVersion"`
}
-type AccountExport struct {
- Id int64 `json:"id"`
- Username string `json:"username"`
- Pass string `json:"pass"`
- ConPass string `json:"conPass"`
- Quota int64 `json:"quota"`
- Download int64 `json:"download"`
- Upload int64 `json:"upload"`
- ExpireTime int64 `json:"expireTime"`
- DeviceNo int64 `json:"deviceNo"`
- KickUtilTime int64 `json:"kickUtilTime"`
- Role string `json:"role"`
- Deleted int64 `json:"deleted"`
- CreateTime time.Time `json:"createTime"`
- UpdateTime time.Time `json:"updateTime"`
- LoginAt int64 `json:"loginAt"`
- ConAt int64 `json:"conAt"`
- Remark string `json:"remark"`
-}
-
type PeerExport struct {
Id int64 `json:"id,omitempty"`
AuthId string `json:"authId,omitempty"`
diff --git a/apps/model/dto/admin_user.go b/apps/model/dto/admin_user.go
index fef84e9..a5fc22f 100644
--- a/apps/model/dto/admin_user.go
+++ b/apps/model/dto/admin_user.go
@@ -4,4 +4,3 @@ type AdminChangePasswordDto struct {
OldPassword *string `json:"oldPassword" form:"oldPassword" validate:"required,min=6,max=64"`
NewPassword *string `json:"newPassword" form:"newPassword" validate:"required,min=6,max=64"`
}
-
diff --git a/apps/model/dto/auth.go b/apps/model/dto/auth.go
index aa5f9ea..a19643f 100644
--- a/apps/model/dto/auth.go
+++ b/apps/model/dto/auth.go
@@ -4,4 +4,3 @@ type LoginDto struct {
Username *string `json:"username" form:"username" validate:"required,min=6,max=32,validateStr"`
Pass *string `json:"pass" form:"pass" validate:"required,min=6,max=64"`
}
-
diff --git a/apps/model/dto/peer.go b/apps/model/dto/peer.go
index a30341c..ee96497 100644
--- a/apps/model/dto/peer.go
+++ b/apps/model/dto/peer.go
@@ -31,4 +31,3 @@ type PeerUpdateDto struct {
type PeerKickDto struct {
BannedUntil *int64 `json:"bannedUntil" form:"bannedUntil" validate:"required,min=0"`
}
-
diff --git a/apps/model/entity/account.go b/apps/model/entity/account.go
index fe3da3d..2a1a204 100644
--- a/apps/model/entity/account.go
+++ b/apps/model/entity/account.go
@@ -1,6 +1,6 @@
package entity
-type Account struct {
+type LegacyAccount struct {
Username *string `gorm:"column:username;default:''" json:"username"`
Pass *string `gorm:"column:pass;default:''" json:"pass"`
ConPass *string `gorm:"column:con_pass;default:''" json:"conPass"`
@@ -19,3 +19,7 @@ type Account struct {
Remark *string `gorm:"column:remark;default:''" json:"remark"`
ForcePasswordChange *int64 `gorm:"column:force_password_change;default:0" json:"forcePasswordChange"`
}
+
+func (LegacyAccount) TableName() string {
+ return "account"
+}
diff --git a/apps/model/entity/admin_user.go b/apps/model/entity/admin_user.go
index bef6390..9db6c68 100644
--- a/apps/model/entity/admin_user.go
+++ b/apps/model/entity/admin_user.go
@@ -14,4 +14,3 @@ type AdminUser struct {
func (AdminUser) TableName() string {
return "admin_user"
}
-
diff --git a/apps/model/entity/metric_sample.go b/apps/model/entity/metric_sample.go
index 2b1a0c7..7e8649c 100644
--- a/apps/model/entity/metric_sample.go
+++ b/apps/model/entity/metric_sample.go
@@ -2,22 +2,21 @@ package entity
type MetricSample struct {
BaseEntity
- SampledAt *int64 `gorm:"column:sampled_at"`
- CpuPercent *float64 `gorm:"column:cpu_percent"`
- Load1 *float64 `gorm:"column:load1"`
- MemUsedBytes *int64 `gorm:"column:mem_used_bytes"`
- MemTotalBytes *int64 `gorm:"column:mem_total_bytes"`
- MemPercent *float64 `gorm:"column:mem_percent"`
- DiskPath *string `gorm:"column:disk_path"`
- DiskUsedBytes *int64 `gorm:"column:disk_used_bytes"`
- DiskTotalBytes *int64 `gorm:"column:disk_total_bytes"`
- DiskPercent *float64 `gorm:"column:disk_percent"`
- HysteriaRunning *int64 `gorm:"column:hysteria_running"`
- OnlinePeers *int64 `gorm:"column:online_peers"`
- OnlineDevices *int64 `gorm:"column:online_devices"`
+ SampledAt *int64 `gorm:"column:sampled_at"`
+ CpuPercent *float64 `gorm:"column:cpu_percent"`
+ Load1 *float64 `gorm:"column:load1"`
+ MemUsedBytes *int64 `gorm:"column:mem_used_bytes"`
+ MemTotalBytes *int64 `gorm:"column:mem_total_bytes"`
+ MemPercent *float64 `gorm:"column:mem_percent"`
+ DiskPath *string `gorm:"column:disk_path"`
+ DiskUsedBytes *int64 `gorm:"column:disk_used_bytes"`
+ DiskTotalBytes *int64 `gorm:"column:disk_total_bytes"`
+ DiskPercent *float64 `gorm:"column:disk_percent"`
+ HysteriaRunning *int64 `gorm:"column:hysteria_running"`
+ OnlinePeers *int64 `gorm:"column:online_peers"`
+ OnlineDevices *int64 `gorm:"column:online_devices"`
}
func (MetricSample) TableName() string {
return "metric_sample"
}
-
diff --git a/apps/model/entity/peer.go b/apps/model/entity/peer.go
index 6e6c691..99b6de1 100644
--- a/apps/model/entity/peer.go
+++ b/apps/model/entity/peer.go
@@ -20,4 +20,3 @@ type Peer struct {
func (Peer) TableName() string {
return "peer"
}
-
diff --git a/apps/model/entity/traffic_aggregate_daily.go b/apps/model/entity/traffic_aggregate_daily.go
index 035a76d..55ef02b 100644
--- a/apps/model/entity/traffic_aggregate_daily.go
+++ b/apps/model/entity/traffic_aggregate_daily.go
@@ -11,4 +11,3 @@ type TrafficAggregateDaily struct {
func (TrafficAggregateDaily) TableName() string {
return "traffic_aggregate_daily"
}
-
diff --git a/apps/model/entity/traffic_aggregate_hourly.go b/apps/model/entity/traffic_aggregate_hourly.go
index cec11cf..762e5aa 100644
--- a/apps/model/entity/traffic_aggregate_hourly.go
+++ b/apps/model/entity/traffic_aggregate_hourly.go
@@ -11,4 +11,3 @@ type TrafficAggregateHourly struct {
func (TrafficAggregateHourly) TableName() string {
return "traffic_aggregate_hourly"
}
-
diff --git a/apps/model/entity/traffic_sample.go b/apps/model/entity/traffic_sample.go
index 0ccec5c..11dd77f 100644
--- a/apps/model/entity/traffic_sample.go
+++ b/apps/model/entity/traffic_sample.go
@@ -12,4 +12,3 @@ type TrafficSample struct {
func (TrafficSample) TableName() string {
return "traffic_sample"
}
-
diff --git a/apps/model/vo/admin_user.go b/apps/model/vo/admin_user.go
index 8c0a5cc..cd88837 100644
--- a/apps/model/vo/admin_user.go
+++ b/apps/model/vo/admin_user.go
@@ -6,4 +6,3 @@ type AdminInfoVo struct {
Roles []string `json:"roles"`
ForcePasswordChange bool `json:"forcePasswordChange"`
}
-
diff --git a/apps/model/vo/dashboard.go b/apps/model/vo/dashboard.go
index 59934bc..1c88f14 100644
--- a/apps/model/vo/dashboard.go
+++ b/apps/model/vo/dashboard.go
@@ -1,13 +1,13 @@
package vo
type DashboardSummaryVo struct {
- CollectedAt int64 `json:"collectedAt"`
- System DashboardSystemVo `json:"system"`
+ CollectedAt int64 `json:"collectedAt"`
+ System DashboardSystemVo `json:"system"`
Hysteria DashboardHysteriaVo `json:"hysteria"`
- Peers DashboardPeerVo `json:"peers"`
- Traffic DashboardTrafficVo `json:"traffic"`
- Health DashboardHealthVo `json:"health"`
- SecurityRisks []SecurityRiskVo `json:"securityRisks"`
+ Peers DashboardPeerVo `json:"peers"`
+ Traffic DashboardTrafficVo `json:"traffic"`
+ Health DashboardHealthVo `json:"health"`
+ SecurityRisks []SecurityRiskVo `json:"securityRisks"`
}
type DashboardHealthVo struct {
@@ -22,13 +22,13 @@ type DataHealthVo struct {
}
type DashboardSystemVo struct {
- CpuPercent float64 `json:"cpuPercent"`
- MemUsedBytes uint64 `json:"memUsedBytes"`
- MemTotalBytes uint64 `json:"memTotalBytes"`
- MemPercent float64 `json:"memPercent"`
- DiskUsedBytes uint64 `json:"diskUsedBytes"`
- DiskTotalBytes uint64 `json:"diskTotalBytes"`
- DiskPercent float64 `json:"diskPercent"`
+ CpuPercent float64 `json:"cpuPercent"`
+ MemUsedBytes uint64 `json:"memUsedBytes"`
+ MemTotalBytes uint64 `json:"memTotalBytes"`
+ MemPercent float64 `json:"memPercent"`
+ DiskUsedBytes uint64 `json:"diskUsedBytes"`
+ DiskTotalBytes uint64 `json:"diskTotalBytes"`
+ DiskPercent float64 `json:"diskPercent"`
}
type DashboardHysteriaVo struct {
@@ -48,11 +48,11 @@ type DashboardPeerVo struct {
}
type DashboardTrafficVo struct {
- DownloadBytes int64 `json:"downloadBytes"`
- UploadBytes int64 `json:"uploadBytes"`
- TotalBytes int64 `json:"totalBytes"`
- TodayDownloadBytes int64 `json:"todayDownloadBytes"`
- TodayUploadBytes int64 `json:"todayUploadBytes"`
+ DownloadBytes int64 `json:"downloadBytes"`
+ UploadBytes int64 `json:"uploadBytes"`
+ TotalBytes int64 `json:"totalBytes"`
+ TodayDownloadBytes int64 `json:"todayDownloadBytes"`
+ TodayUploadBytes int64 `json:"todayUploadBytes"`
SinceResetDownloadBytes int64 `json:"sinceResetDownloadBytes"`
SinceResetUploadBytes int64 `json:"sinceResetUploadBytes"`
}
@@ -65,12 +65,12 @@ type SecurityRiskVo struct {
}
type DashboardTopPeerVo struct {
- PeerId int64 `json:"peerId"`
- Name string `json:"name"`
- Remark string `json:"remark"`
- Download int64 `json:"download"`
- Upload int64 `json:"upload"`
- Total int64 `json:"total"`
+ PeerId int64 `json:"peerId"`
+ Name string `json:"name"`
+ Remark string `json:"remark"`
+ Download int64 `json:"download"`
+ Upload int64 `json:"upload"`
+ Total int64 `json:"total"`
}
type DashboardSeriesPointVo struct {
@@ -82,9 +82,8 @@ type DashboardSeriesPointVo struct {
}
type DashboardTimeseriesVo struct {
- Range string `json:"range"`
- Traffic []DashboardSeriesPointVo `json:"traffic"`
- System []DashboardSeriesPointVo `json:"system"`
- CollectedAt int64 `json:"collectedAt"`
+ Range string `json:"range"`
+ Traffic []DashboardSeriesPointVo `json:"traffic"`
+ System []DashboardSeriesPointVo `json:"system"`
+ CollectedAt int64 `json:"collectedAt"`
}
-
diff --git a/apps/model/vo/peer.go b/apps/model/vo/peer.go
index 44ed449..1b5b413 100644
--- a/apps/model/vo/peer.go
+++ b/apps/model/vo/peer.go
@@ -27,4 +27,3 @@ type PeerClientConfigVo struct {
Url string `json:"url"`
QrCode []byte `json:"qrCode"`
}
-
diff --git a/apps/router/admin_user.go b/apps/router/admin_user.go
index 86ae7f7..01a6840 100644
--- a/apps/router/admin_user.go
+++ b/apps/router/admin_user.go
@@ -13,4 +13,3 @@ func initAdminRouter(adminApi *gin.RouterGroup) {
admin.GET("/security", controller.AdminSecurity)
}
}
-
diff --git a/apps/router/dashboard.go b/apps/router/dashboard.go
index ac37d9c..7f19691 100644
--- a/apps/router/dashboard.go
+++ b/apps/router/dashboard.go
@@ -14,4 +14,3 @@ func initDashboardRouter(api *gin.RouterGroup) {
dashboard.GET("/security", controller.DashboardSecurity)
}
}
-
diff --git a/apps/router/router.go b/apps/router/router.go
index c27fce1..4fd9d3f 100644
--- a/apps/router/router.go
+++ b/apps/router/router.go
@@ -20,9 +20,9 @@ func Router(router *gin.Engine, huiWebContext *string) {
if !sqliteReady || !configReadable {
c.JSON(503, gin.H{
- "ok": false,
- "service": "hy2xs-admin",
- "sqlite_ready": sqliteReady,
+ "ok": false,
+ "service": "hy2xs-admin",
+ "sqlite_ready": sqliteReady,
"config_readable": configReadable,
})
return
diff --git a/apps/service/admin_user.go b/apps/service/admin_user.go
index a3c6188..4189eb7 100644
--- a/apps/service/admin_user.go
+++ b/apps/service/admin_user.go
@@ -13,6 +13,18 @@ import (
"hy2xs-admin/util"
)
+func adminClaimsFromContext(c *gin.Context) (bo.AccountBo, bool) {
+ if c == nil {
+ return bo.AccountBo{}, false
+ }
+ v, ok := c.Get("adminClaims")
+ if !ok {
+ return bo.AccountBo{}, false
+ }
+ claims, castOK := v.(bo.AccountBo)
+ return claims, castOK
+}
+
func Login(username string, plainPassword string) (string, bool, error) {
admin, err := dao.GetAdminUser("username = ? and status = 1", username)
if err != nil {
@@ -45,11 +57,15 @@ func Login(username string, plainPassword string) (string, bool, error) {
}
func GetAdminInfo(c *gin.Context) (vo.AdminInfoVo, error) {
- myClaims, err := ParseToken(GetToken(c))
- if err != nil {
- return vo.AdminInfoVo{}, err
+ claims, ok := adminClaimsFromContext(c)
+ if !ok {
+ myClaims, err := ParseToken(GetToken(c))
+ if err != nil {
+ return vo.AdminInfoVo{}, err
+ }
+ claims = myClaims.Admin
}
- admin, err := dao.GetAdminUser("id = ?", myClaims.Admin.Id)
+ admin, err := dao.GetAdminUser("id = ?", claims.Id)
if err != nil {
return vo.AdminInfoVo{}, err
}
@@ -57,7 +73,7 @@ func GetAdminInfo(c *gin.Context) (vo.AdminInfoVo, error) {
return vo.AdminInfoVo{}, errors.New("this account has been disabled")
}
force := admin.ForcePasswordChange != nil && *admin.ForcePasswordChange != 0
- return vo.AdminInfoVo{Id: myClaims.Admin.Id, Username: myClaims.Admin.Username, Roles: myClaims.Admin.Roles, ForcePasswordChange: force}, nil
+ return vo.AdminInfoVo{Id: claims.Id, Username: claims.Username, Roles: claims.Roles, ForcePasswordChange: force}, nil
}
func UpdateAdminLastLoginAt(id int64, loginAt int64) error {
@@ -100,4 +116,3 @@ func ChangeAdminPassword(c *gin.Context, oldPassword string, newPassword string)
func GetAdminForTokenValidation(id int64) (entity.AdminUser, error) {
return dao.GetAdminUser("id = ?", id)
}
-
diff --git a/apps/service/dashboard.go b/apps/service/dashboard.go
index 1bb45da..9041d44 100644
--- a/apps/service/dashboard.go
+++ b/apps/service/dashboard.go
@@ -102,4 +102,3 @@ func DashboardSecurityRisks(summary vo.DashboardSummaryVo) []vo.SecurityRiskVo {
}
return risks
}
-
diff --git a/apps/service/hysteria2_api.go b/apps/service/hysteria2_api.go
index a0492c8..e4badb4 100644
--- a/apps/service/hysteria2_api.go
+++ b/apps/service/hysteria2_api.go
@@ -2,6 +2,7 @@ package service
import (
"errors"
+ "github.com/sirupsen/logrus"
"hy2xs-admin/dao"
"hy2xs-admin/model/constant"
"hy2xs-admin/proxy"
@@ -51,7 +52,8 @@ func Hysteria2Auth(conPass string) (int64, string, error) {
// Ограничение количества устройств
onlineUsers, err := Hysteria2Online()
if err != nil {
- return 0, "", err
+ logrus.WithError(err).Warn("hysteria2 online users unavailable; skip device-limit check")
+ return *peer.Id, *peer.AuthId, nil
}
device, exist := onlineUsers[*peer.AuthId]
if exist && *peer.MaxDevices <= device {
diff --git a/apps/service/hysteria2_api_test.go b/apps/service/hysteria2_api_test.go
index 7b8e3b3..c88bbc1 100644
--- a/apps/service/hysteria2_api_test.go
+++ b/apps/service/hysteria2_api_test.go
@@ -71,4 +71,3 @@ func TestBuildHysteria2Url_MinimalConfig(t *testing.T) {
t.Fatalf("unexpected optional query params in minimal config: %s", parsed.RawQuery)
}
}
-
diff --git a/apps/service/metrics_collector.go b/apps/service/metrics_collector.go
index a5b2659..47db022 100644
--- a/apps/service/metrics_collector.go
+++ b/apps/service/metrics_collector.go
@@ -9,6 +9,8 @@ import (
"time"
)
+const hysteriaVersionCacheTTL = 15 * time.Minute
+
type metricsSnapshot struct {
CollectedAt int64
System vo.DashboardSystemVo
@@ -23,12 +25,41 @@ var metricsStore = struct {
sync.RWMutex
snapshot metricsSnapshot
lastSuccessAt int64
+ version string
+ versionAt time.Time
}{}
+func getCachedHysteriaVersion(now time.Time) string {
+ metricsStore.RLock()
+ if metricsStore.version != "" && now.Sub(metricsStore.versionAt) < hysteriaVersionCacheTTL {
+ v := metricsStore.version
+ metricsStore.RUnlock()
+ return v
+ }
+ metricsStore.RUnlock()
+
+ content, err := util.Exec(util.GetHysteria2BinPath() + " version")
+ if err != nil {
+ metricsStore.RLock()
+ v := metricsStore.version
+ metricsStore.RUnlock()
+ if v != "" {
+ return v
+ }
+ return "-"
+ }
+
+ metricsStore.Lock()
+ metricsStore.version = content
+ metricsStore.versionAt = now
+ metricsStore.Unlock()
+ return content
+}
+
func CollectMetricsSnapshot() {
nowMs := time.Now().UnixMilli()
s := metricsSnapshot{
- CollectedAt: nowMs,
+ CollectedAt: nowMs,
CollectorState: vo.DataHealthVo{Status: "ok", LastSuccessAt: nowMs},
HysteriaState: vo.DataHealthVo{Status: "ok", LastSuccessAt: nowMs},
}
@@ -52,10 +83,7 @@ func CollectMetricsSnapshot() {
}
s.Hysteria.Running = Hysteria2IsRunning()
- s.Hysteria.Version = "-"
- if content, err := util.Exec(util.GetHysteria2BinPath() + " version"); err == nil {
- s.Hysteria.Version = content
- }
+ s.Hysteria.Version = getCachedHysteriaVersion(time.Now())
if onlineMap, err := Hysteria2Online(); err == nil {
s.Hysteria.ApiReachable = true
@@ -80,6 +108,9 @@ func CollectMetricsSnapshot() {
running = 1
}
diskPath := "/"
+ if configured := util.GetEnvDiskPath(); configured != "" {
+ diskPath = configured
+ }
metric := entity.MetricSample{
SampledAt: &nowMs,
CpuPercent: &s.System.CpuPercent,
@@ -108,4 +139,3 @@ func DashboardSnapshot() metricsSnapshot {
defer metricsStore.RUnlock()
return metricsStore.snapshot
}
-
diff --git a/apps/service/peer.go b/apps/service/peer.go
index 73c858d..2ccec08 100644
--- a/apps/service/peer.go
+++ b/apps/service/peer.go
@@ -24,7 +24,7 @@ func PagePeer(peerPageDto dto.PeerPageDto) ([]vo.PeerVo, int64, error) {
result := make([]vo.PeerVo, 0, len(peers))
for _, p := range peers {
item := vo.PeerVo{
- BaseVo: vo.BaseVo{Id: *p.Id, CreateTime: *p.CreateTime},
+ BaseVo: vo.BaseVo{Id: *p.Id, CreateTime: *p.CreateTime},
Name: strVal(p.Name),
Remark: strVal(p.Remark),
AuthId: strVal(p.AuthId),
@@ -137,7 +137,7 @@ func GetPeerVo(id int64) (vo.PeerVo, error) {
return vo.PeerVo{}, err
}
return vo.PeerVo{
- BaseVo: vo.BaseVo{Id: *p.Id, CreateTime: *p.CreateTime},
+ BaseVo: vo.BaseVo{Id: *p.Id, CreateTime: *p.CreateTime},
Name: strVal(p.Name),
Remark: strVal(p.Remark),
AuthId: strVal(p.AuthId),
@@ -152,8 +152,12 @@ func GetPeerVo(id int64) (vo.PeerVo, error) {
}, nil
}
-func ResetPeerTraffic(id int64) error { return dao.UpdatePeer([]int64{id}, map[string]interface{}{"download_bytes": 0, "upload_bytes": 0}) }
-func ReleaseKickPeer(id int64) error { return dao.UpdatePeer([]int64{id}, map[string]interface{}{"banned_until": 0}) }
+func ResetPeerTraffic(id int64) error {
+ return dao.UpdatePeer([]int64{id}, map[string]interface{}{"download_bytes": 0, "upload_bytes": 0})
+}
+func ReleaseKickPeer(id int64) error {
+ return dao.UpdatePeer([]int64{id}, map[string]interface{}{"banned_until": 0})
+}
func KickPeer(id int64, bannedUntil int64) error {
if err := dao.UpdatePeer([]int64{id}, map[string]interface{}{"banned_until": bannedUntil}); err != nil {
@@ -324,6 +328,15 @@ func UpdatePeerLastConnectionAt(id int64, conAt int64) error {
return dao.UpdatePeer([]int64{id}, map[string]interface{}{"last_connection_at": conAt})
}
-func strVal(v *string) string { if v == nil { return "" }; return *v }
-func int64Val(v *int64) int64 { if v == nil { return 0 }; return *v }
-
+func strVal(v *string) string {
+ if v == nil {
+ return ""
+ }
+ return *v
+}
+func int64Val(v *int64) int64 {
+ if v == nil {
+ return 0
+ }
+ return *v
+}
diff --git a/apps/service/peer_secret.go b/apps/service/peer_secret.go
index cec5a71..d245d6c 100644
--- a/apps/service/peer_secret.go
+++ b/apps/service/peer_secret.go
@@ -82,4 +82,3 @@ func DecryptPeerSecret(stored string) (string, error) {
}
return util.DecryptAESGCM(stored, key)
}
-
diff --git a/apps/util/encrypt.go b/apps/util/encrypt.go
index 8679c79..7b20863 100644
--- a/apps/util/encrypt.go
+++ b/apps/util/encrypt.go
@@ -60,7 +60,6 @@ func HmacSHA256Hex(payload string, secret string) string {
return str
}
-
func DecodeBase64Key(raw string, expectedLen int) ([]byte, error) {
raw = strings.TrimSpace(raw)
if raw == "" {
@@ -122,4 +121,3 @@ func DecryptAESGCM(cipherText string, key []byte) (string, error) {
}
return string(plain), nil
}
-
diff --git a/apps/util/linux.go b/apps/util/linux.go
index ddc436c..2277bb4 100644
--- a/apps/util/linux.go
+++ b/apps/util/linux.go
@@ -11,9 +11,21 @@ import (
"os"
"os/exec"
"strconv"
+ "strings"
"time"
)
+func resolveDiskPath() string {
+ if configured := strings.TrimSpace(os.Getenv("HY2XS_DISK_PATH")); configured != "" {
+ return configured
+ }
+ return "/"
+}
+
+func GetEnvDiskPath() string {
+ return strings.TrimSpace(os.Getenv("HY2XS_DISK_PATH"))
+}
+
func Exec(cmd string) (string, error) {
command := exec.Command("bash", "-c", cmd)
command.Env = os.Environ()
@@ -83,22 +95,23 @@ func GetMemInfo() (*mem.VirtualMemoryStat, error) {
}
func GetDiskPercent() (float64, error) {
- var err error
- parts, err := disk.Partitions(true)
- diskInfo, err := disk.Usage(parts[0].Mountpoint)
+ diskInfo, err := disk.Usage(resolveDiskPath())
+ if err != nil {
+ return 0, err
+ }
value, err := strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.UsedPercent), 64)
return value, err
}
func GetDiskInfo() (*disk.UsageStat, error) {
- parts, err := disk.Partitions(true)
+ usage, err := disk.Usage(resolveDiskPath())
if err != nil {
return nil, err
}
- if len(parts) == 0 {
- return nil, errors.New("disk partition not found")
+ if usage == nil {
+ return nil, errors.New("disk usage not found")
}
- return disk.Usage(parts[0].Mountpoint)
+ return usage, nil
}
func VerifyPort(port string) error {