2022-01-08 22:54:14 -05:00
package singleton
2019-12-08 03:59:58 -05:00
import (
2024-02-24 08:10:27 -05:00
"fmt"
2022-04-14 04:41:34 -04:00
"log"
2022-03-18 11:45:03 -04:00
"time"
2019-12-09 05:14:31 -05:00
2019-12-08 03:59:58 -05:00
"github.com/patrickmn/go-cache"
2022-04-14 04:41:34 -04:00
"gorm.io/driver/sqlite"
2020-12-19 10:11:16 -05:00
"gorm.io/gorm"
2019-12-08 03:59:58 -05:00
2020-11-10 21:07:45 -05:00
"github.com/naiba/nezha/model"
2022-04-14 04:41:34 -04:00
"github.com/naiba/nezha/pkg/utils"
2019-12-08 03:59:58 -05:00
)
2023-11-28 10:01:37 -05:00
var Version = "debug"
2020-12-19 23:18:27 -05:00
2021-01-23 20:41:35 -05:00
var (
Conf * model . Config
Cache * cache . Cache
DB * gorm . DB
2022-03-18 11:45:03 -04:00
Loc * time . Location
2021-01-23 20:41:35 -05:00
)
2021-01-18 20:59:04 -05:00
2022-10-12 11:06:25 -04:00
func InitTimezoneAndCache ( ) {
2022-03-18 11:45:03 -04:00
var err error
2022-10-12 11:06:25 -04:00
Loc , err = time . LoadLocation ( Conf . Location )
2022-03-18 11:45:03 -04:00
if err != nil {
panic ( err )
}
2021-01-08 08:04:50 -05:00
2022-04-12 01:16:33 -04:00
Cache = cache . New ( 5 * time . Minute , 10 * time . Minute )
2021-01-08 08:04:50 -05:00
}
2021-01-23 20:41:35 -05:00
2022-04-12 01:16:33 -04:00
// LoadSingleton 加载子服务并执行
func LoadSingleton ( ) {
LoadNotifications ( ) // 加载通知服务
LoadServers ( ) // 加载服务器列表
LoadCronTasks ( ) // 加载定时任务
2022-05-17 22:10:35 -04:00
LoadAPI ( )
2021-01-23 20:41:35 -05:00
}
2021-06-21 09:30:42 -04:00
2022-04-12 01:16:33 -04:00
// InitConfigFromPath 从给出的文件路径中加载配置
func InitConfigFromPath ( path string ) {
2022-04-27 22:17:38 -04:00
Conf = & model . Config { }
2022-04-12 01:16:33 -04:00
err := Conf . Read ( path )
if err != nil {
panic ( err )
2021-06-21 09:30:42 -04:00
}
2024-02-24 08:10:27 -05:00
ValidateConfig ( )
}
// ValidateConfig 验证配置文件有效性
func ValidateConfig ( ) {
// 如果DDNS启用则检查Provider是否存在, 不存在直接退出
if Conf . DDNS . Enable {
_ , err := GetDDNSProviderFromString ( Conf . DDNS . Provider )
if err != nil {
panic ( err )
}
if Conf . DDNS . MaxRetries < 1 || Conf . DDNS . MaxRetries > 10 {
panic ( fmt . Errorf ( "DDNS.MaxRetries值域为[1, 10]的整数, 当前为 %d" , Conf . DDNS . MaxRetries ) )
}
}
2021-06-21 09:30:42 -04:00
}
2022-02-19 01:29:06 -05:00
2022-04-12 01:16:33 -04:00
// InitDBFromPath 从给出的文件路径中加载数据库
func InitDBFromPath ( path string ) {
var err error
DB , err = gorm . Open ( sqlite . Open ( path ) , & gorm . Config {
CreateBatchSize : 200 ,
} )
if err != nil {
panic ( err )
}
if Conf . Debug {
DB = DB . Debug ( )
}
err = DB . AutoMigrate ( model . Server { } , model . User { } ,
model . Notification { } , model . AlertRule { } , model . Monitor { } ,
2022-05-16 23:21:27 -04:00
model . MonitorHistory { } , model . Cron { } , model . Transfer { } , model . ApiToken { } )
2022-04-12 01:16:33 -04:00
if err != nil {
panic ( err )
2022-02-19 01:29:06 -05:00
}
}
2022-04-14 04:41:34 -04:00
// RecordTransferHourlyUsage 对流量记录进行打点
func RecordTransferHourlyUsage ( ) {
ServerLock . Lock ( )
defer ServerLock . Unlock ( )
now := time . Now ( )
2022-10-12 11:06:25 -04:00
nowTrimSeconds := time . Date ( now . Year ( ) , now . Month ( ) , now . Day ( ) , now . Hour ( ) , 0 , 0 , 0 , Loc )
2022-04-14 04:41:34 -04:00
var txs [ ] model . Transfer
for id , server := range ServerList {
tx := model . Transfer {
ServerID : id ,
In : server . State . NetInTransfer - uint64 ( server . PrevHourlyTransferIn ) ,
Out : server . State . NetOutTransfer - uint64 ( server . PrevHourlyTransferOut ) ,
}
if tx . In == 0 && tx . Out == 0 {
continue
}
server . PrevHourlyTransferIn = int64 ( server . State . NetInTransfer )
server . PrevHourlyTransferOut = int64 ( server . State . NetOutTransfer )
tx . CreatedAt = nowTrimSeconds
txs = append ( txs , tx )
}
if len ( txs ) == 0 {
return
}
log . Println ( "NEZHA>> Cron 流量统计入库" , len ( txs ) , DB . Create ( txs ) . Error )
}
// CleanMonitorHistory 清理无效或过时的 监控记录 和 流量记录
func CleanMonitorHistory ( ) {
// 清理已被删除的服务器的监控记录与流量记录
DB . Unscoped ( ) . Delete ( & model . MonitorHistory { } , "created_at < ? OR monitor_id NOT IN (SELECT `id` FROM monitors)" , time . Now ( ) . AddDate ( 0 , 0 , - 30 ) )
2024-02-12 01:16:04 -05:00
// 由于网络监控记录的数据较多,并且前端仅使用了 1 天的数据
// 考虑到 sqlite 数据量问题,仅保留一天数据,
// server_id = 0 的数据会用于/service页面的可用性展示
DB . Unscoped ( ) . Delete ( & model . MonitorHistory { } , "(created_at < ? AND server_id != 0) OR monitor_id NOT IN (SELECT `id` FROM monitors)" , time . Now ( ) . AddDate ( 0 , 0 , - 1 ) )
2022-04-14 04:41:34 -04:00
DB . Unscoped ( ) . Delete ( & model . Transfer { } , "server_id NOT IN (SELECT `id` FROM servers)" )
// 计算可清理流量记录的时长
var allServerKeep time . Time
specialServerKeep := make ( map [ uint64 ] time . Time )
var specialServerIDs [ ] uint64
var alerts [ ] model . AlertRule
DB . Find ( & alerts )
2022-04-14 15:13:53 -04:00
for _ , alert := range alerts {
for _ , rule := range alert . Rules {
2022-04-14 04:41:34 -04:00
// 是不是流量记录规则
2022-04-14 15:13:53 -04:00
if ! rule . IsTransferDurationRule ( ) {
2022-04-14 04:41:34 -04:00
continue
}
2022-04-14 15:13:53 -04:00
dataCouldRemoveBefore := rule . GetTransferDurationStart ( )
2022-04-14 04:41:34 -04:00
// 判断规则影响的机器范围
2022-04-14 15:13:53 -04:00
if rule . Cover == model . RuleCoverAll {
2022-04-14 04:41:34 -04:00
// 更新全局可以清理的数据点
if allServerKeep . IsZero ( ) || allServerKeep . After ( dataCouldRemoveBefore ) {
allServerKeep = dataCouldRemoveBefore
}
} else {
// 更新特定机器可以清理数据点
2022-04-14 15:13:53 -04:00
for id := range rule . Ignore {
2022-04-14 04:41:34 -04:00
if specialServerKeep [ id ] . IsZero ( ) || specialServerKeep [ id ] . After ( dataCouldRemoveBefore ) {
specialServerKeep [ id ] = dataCouldRemoveBefore
specialServerIDs = append ( specialServerIDs , id )
}
}
}
}
}
for id , couldRemove := range specialServerKeep {
DB . Unscoped ( ) . Delete ( & model . Transfer { } , "server_id = ? AND created_at < ?" , id , couldRemove )
}
if allServerKeep . IsZero ( ) {
DB . Unscoped ( ) . Delete ( & model . Transfer { } , "server_id NOT IN (?)" , specialServerIDs )
} else {
DB . Unscoped ( ) . Delete ( & model . Transfer { } , "server_id NOT IN (?) AND created_at < ?" , specialServerIDs , allServerKeep )
}
}
// IPDesensitize 根据设置选择是否对IP进行打码处理 返回处理后的IP(关闭打码则返回原IP)
func IPDesensitize ( ip string ) string {
if Conf . EnablePlainIPInNotification {
return ip
}
return utils . IPDesensitize ( ip )
}