cb20d8d28f
Отзыв секрета не сходился: `auth_id` при смене секрета оставался прежним, поэтому сессия, установленная по отозванным учётным данным, была неотличима от законной, и цикл учёта не имел признака, по которому её следовало завершить. У состояния есть путь без единой неудачи — Hysteria регистрирует соединение в Traffic Stats API только после возврата backend-auth, поэтому успешный /kick может пройти мимо. Новое поколение credentials получает новый auth_id, kick идёт по старому, пережившая сессия становится orphan. Адрес Traffic Stats API имел два контракта: оркестратор принимал любой IPv4, админка всегда шла на loopback. Валидная по всем гейтам конфигурация выключала лимит устройств, учёт трафика и принудительное отключение разом. Адрес зафиксирован, а расхождение файла с ним админка называет. Состояние службы стало трёхзначным: util.Exec выбрасывал вывод systemctl при ненулевом коде, поэтому «остановлена» и «спросить не удалось» приходили одним значением, а доступность Traffic Stats API выводилась из него же. Журнал Hysteria разбирается в фактическом формате upstream (time — дробное число), страница конфигурации показывает файл вместо дефолтов UI и не возит секреты в браузер, санитайзер выгрузки следует по YAML-якорям. Разбор: docs/acceptance/2026-09-02-v1.0.0-rc4-preflight-findings.md
103 lines
4.0 KiB
Go
103 lines
4.0 KiB
Go
package vo
|
|
|
|
type DashboardSummaryVo struct {
|
|
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"`
|
|
}
|
|
|
|
type DashboardHealthVo struct {
|
|
Collector DataHealthVo `json:"collector"`
|
|
Hysteria DataHealthVo `json:"hysteria"`
|
|
}
|
|
|
|
type DataHealthVo struct {
|
|
Status string `json:"status"`
|
|
MessageKey string `json:"messageKey,omitempty"`
|
|
LastSuccessAt int64 `json:"lastSuccessAt,omitempty"`
|
|
}
|
|
|
|
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"`
|
|
}
|
|
|
|
// DashboardHysteriaVo — состояние Hysteria из ДВУХ независимых источников.
|
|
//
|
|
// `serviceState` отвечает systemd, `apiReachable` — фактическое обращение к
|
|
// Traffic Stats API. Раньше второе выводилось из первого: сборщик метрик
|
|
// получал от общего Hysteria2Online пустую карту без ошибки, когда systemctl
|
|
// говорил «неактивна», и выставлял `apiReachable = true`, ни разу не сходив в
|
|
// API. Дашборд показывал «служба остановлена» и «API доступен» одновременно.
|
|
//
|
|
// `running` сохранён для совместимости отображения и означает ровно
|
|
// `serviceState == active`. Решения на нём не строятся: «неизвестно» — это не
|
|
// «остановлена», и путать их продукту уже дорого обходилось.
|
|
type DashboardHysteriaVo struct {
|
|
Version string `json:"version"`
|
|
Running bool `json:"running"`
|
|
// ServiceState — active | inactive | unknown.
|
|
ServiceState string `json:"serviceState"`
|
|
ApiReachable bool `json:"apiReachable"`
|
|
LastApiError string `json:"lastApiError,omitempty"`
|
|
}
|
|
|
|
type DashboardPeerVo struct {
|
|
Total int64 `json:"total"`
|
|
Enabled int64 `json:"enabled"`
|
|
Disabled int64 `json:"disabled"`
|
|
Expired int64 `json:"expired"`
|
|
OnlinePeers int64 `json:"onlinePeers"`
|
|
OnlineDevices int64 `json:"onlineDevices"`
|
|
}
|
|
|
|
type DashboardTrafficVo struct {
|
|
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"`
|
|
}
|
|
|
|
type SecurityRiskVo struct {
|
|
Key string `json:"key"`
|
|
Severity string `json:"severity"`
|
|
ActionRoute string `json:"actionRoute,omitempty"`
|
|
Dismissible bool `json:"dismissible"`
|
|
}
|
|
|
|
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"`
|
|
}
|
|
|
|
type DashboardSeriesPointVo struct {
|
|
Ts int64 `json:"ts"`
|
|
Download int64 `json:"download,omitempty"`
|
|
Upload int64 `json:"upload,omitempty"`
|
|
Cpu float64 `json:"cpu,omitempty"`
|
|
Mem float64 `json:"mem,omitempty"`
|
|
}
|
|
|
|
type DashboardTimeseriesVo struct {
|
|
Range string `json:"range"`
|
|
Traffic []DashboardSeriesPointVo `json:"traffic"`
|
|
System []DashboardSeriesPointVo `json:"system"`
|
|
CollectedAt int64 `json:"collectedAt"`
|
|
}
|