cf094f6e6f
Проход по операциям, которые делают не то, что обещает их имя. P0. Удаление bootstrap-admin-peer не было отзывом доступа. Признаком «создавать пир или нет» служило наличие строки в таблице, а HY2XS_ADMIN_CON_PASS продолжает жить в /etc/hy2xs/hy2xs.env — его читает systemd-юнит. Оператор удалял пира, доступ исчезал, и ближайший restart возвращал того же пира с тем же секретом. Молча. Признаком стала отметка BOOTSTRAP_PEER_SEEDED в таблице config: «создавался когда-либо», а не «существует сейчас». Отметка и пир пишутся одной транзакцией. P1. Резервная копия с includeSecrets=true проглатывала и ошибку расшифровки, и отсутствие шифртекста, отдавая пира с пустым secret и успешный ответ. Теперь недоступный секрет любого пира отклоняет весь запрос с указанием имени. P1. DecryptPeerSecret возвращала содержимое колонки как расшифрованный секрет, если оно не начиналось с v1: — остаток поколения с открытыми секретами. P1. doctor перезапускал hysteria-server и hy2xs-admin: диагностика подозрения на проблему обрывала все живые соединения. P1. Админка сама генерировала HYSTERIA2_TRAFFIC_STATS_SECRET, записать который в /etc/hysteria/config.yaml она не может. Сервис объявлял себя здоровым, а machine auth переставал совпадать. P1. Обходы проверки зависимостей (accepted-risk/skipped) не могли произвести артефакт: приёмка требует dependency_security_gate=true. Удалены из сборки и документации, отсутствие проверяется приёмкой. P2. UPDATE по отсутствующей строке config считался успехом, и cron перепланировался при несохранённом значении. Решение по RowsAffected. P2. Слой данных не отличал «записи нет» от «база не ответила»: sentinel-значения ErrPeerNotFound / ErrAdminUserNotFound / ErrConfigNotFound / ErrStorage. P2. Удалены алиасы /:id/client-url и /:id/qr. Контракт разработки: apps/go.mod объявляет toolchain go1.26.7 (директива go — языковой baseline, а не выбор компилятора), tools/dev/doctor.sh|.ps1 сверяют среду с versions.env.
101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
package dao
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/sirupsen/logrus"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
"hy2xs-admin/model/entity"
|
|
)
|
|
|
|
func SaveTrafficSample(sample entity.TrafficSample) error {
|
|
if tx := sqliteDB.Save(&sample); tx.Error != nil {
|
|
logrus.Errorf("%v", tx.Error)
|
|
return ErrStorage
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func UpsertTrafficAggregateHourly(peerId int64, hourStart int64, rxBytes int64, txBytes int64) error {
|
|
if rxBytes == 0 && txBytes == 0 {
|
|
return nil
|
|
}
|
|
agg := entity.TrafficAggregateHourly{
|
|
PeerId: &peerId,
|
|
HourStart: &hourStart,
|
|
RxBytes: &rxBytes,
|
|
TxBytes: &txBytes,
|
|
}
|
|
if tx := sqliteDB.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "peer_id"}, {Name: "hour_start"}},
|
|
DoUpdates: clause.Assignments(map[string]interface{}{
|
|
"rx_bytes": gormExprAdd("rx_bytes", rxBytes),
|
|
"tx_bytes": gormExprAdd("tx_bytes", txBytes),
|
|
}),
|
|
}).Create(&agg); tx.Error != nil {
|
|
logrus.Errorf("%v", tx.Error)
|
|
return ErrStorage
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func UpsertTrafficAggregateDaily(peerId int64, dayStart int64, rxBytes int64, txBytes int64) error {
|
|
if rxBytes == 0 && txBytes == 0 {
|
|
return nil
|
|
}
|
|
agg := entity.TrafficAggregateDaily{
|
|
PeerId: &peerId,
|
|
DayStart: &dayStart,
|
|
RxBytes: &rxBytes,
|
|
TxBytes: &txBytes,
|
|
}
|
|
if tx := sqliteDB.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "peer_id"}, {Name: "day_start"}},
|
|
DoUpdates: clause.Assignments(map[string]interface{}{
|
|
"rx_bytes": gormExprAdd("rx_bytes", rxBytes),
|
|
"tx_bytes": gormExprAdd("tx_bytes", txBytes),
|
|
}),
|
|
}).Create(&agg); tx.Error != nil {
|
|
logrus.Errorf("%v", tx.Error)
|
|
return ErrStorage
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func gormExprAdd(column string, delta int64) interface{} {
|
|
return gorm.Expr(fmt.Sprintf("%s + ?", column), delta)
|
|
}
|
|
|
|
func CleanupTrafficSample(olderThanMs int64) error {
|
|
if !tableExists("traffic_sample") {
|
|
return nil
|
|
}
|
|
if tx := sqliteDB.Exec("DELETE FROM traffic_sample WHERE sampled_at < ?", olderThanMs); tx.Error != nil {
|
|
logrus.Errorf("%v", tx.Error)
|
|
return ErrStorage
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func CleanupTrafficAggregateHourly(olderThanMs int64) error {
|
|
if !tableExists("traffic_aggregate_hourly") {
|
|
return nil
|
|
}
|
|
if tx := sqliteDB.Exec("DELETE FROM traffic_aggregate_hourly WHERE hour_start < ?", olderThanMs); tx.Error != nil {
|
|
logrus.Errorf("%v", tx.Error)
|
|
return ErrStorage
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func CleanupTrafficAggregateDaily(olderThanMs int64) error {
|
|
if !tableExists("traffic_aggregate_daily") {
|
|
return nil
|
|
}
|
|
if tx := sqliteDB.Exec("DELETE FROM traffic_aggregate_daily WHERE day_start < ?", olderThanMs); tx.Error != nil {
|
|
logrus.Errorf("%v", tx.Error)
|
|
return ErrStorage
|
|
}
|
|
return nil
|
|
}
|