nezha/service/rpc/nezha.go

232 lines
7.7 KiB
Go
Raw Normal View History

2019-12-08 16:59:58 +08:00
package rpc
2019-12-07 18:14:40 +08:00
import (
"context"
"fmt"
"log"
"net"
"sync"
2020-10-24 21:29:05 +08:00
"time"
2019-12-07 18:14:40 +08:00
"github.com/naiba/nezha/pkg/ddns"
"github.com/naiba/nezha/pkg/geoip"
"github.com/naiba/nezha/pkg/grpcx"
"github.com/naiba/nezha/pkg/utils"
"github.com/jinzhu/copier"
2020-11-11 10:07:45 +08:00
"github.com/naiba/nezha/model"
pb "github.com/naiba/nezha/proto"
2022-01-09 11:54:14 +08:00
"github.com/naiba/nezha/service/singleton"
2019-12-07 18:14:40 +08:00
)
var NezhaHandlerSingleton *NezhaHandler
2019-12-07 18:14:40 +08:00
type NezhaHandler struct {
Auth *authHandler
ioStreams map[string]*ioStreamContext
ioStreamMutex *sync.RWMutex
}
func NewNezhaHandler() *NezhaHandler {
return &NezhaHandler{
Auth: &authHandler{},
ioStreamMutex: new(sync.RWMutex),
ioStreams: make(map[string]*ioStreamContext),
}
2019-12-07 18:14:40 +08:00
}
func (s *NezhaHandler) ReportTask(c context.Context, r *pb.TaskResult) (*pb.Receipt, error) {
2019-12-09 18:14:31 +08:00
var err error
var clientID uint64
if clientID, err = s.Auth.Check(c); err != nil {
return nil, err
}
2021-09-27 21:18:09 +08:00
if r.GetType() == model.TaskTypeCommand {
// 处理上报的计划任务
2022-01-09 11:54:14 +08:00
singleton.CronLock.RLock()
defer singleton.CronLock.RUnlock()
cr := singleton.Crons[r.GetId()]
if cr != nil {
2022-01-09 11:54:14 +08:00
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()
// 保存当前服务器状态信息
curServer := model.Server{}
copier.Copy(&curServer, singleton.ServerList[clientID])
if cr.PushSuccessful && r.GetSuccessful() {
2024-11-01 05:07:04 +08:00
singleton.SendNotification(cr.NotificationGroupID, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.T("Scheduled Task Executed Successfully"),
2024-10-27 13:10:07 +08:00
cr.Name, singleton.ServerList[clientID].Name, r.GetData()), nil, &curServer)
}
if !r.GetSuccessful() {
2024-11-01 05:07:04 +08:00
singleton.SendNotification(cr.NotificationGroupID, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.T("Scheduled Task Executed Failed"),
2024-10-27 13:10:07 +08:00
cr.Name, singleton.ServerList[clientID].Name, r.GetData()), nil, &curServer)
}
2022-01-09 11:54:14 +08:00
singleton.DB.Model(cr).Updates(model.Cron{
LastExecutedAt: time.Now().Add(time.Second * -1 * time.Duration(r.GetDelay())),
LastResult: r.GetSuccessful(),
})
}
2021-09-27 21:18:09 +08:00
} else if model.IsServiceSentinelNeeded(r.GetType()) {
2022-01-09 11:54:14 +08:00
singleton.ServiceSentinelShared.Dispatch(singleton.ReportData{
2021-09-27 21:18:09 +08:00
Data: r,
Reporter: clientID,
})
}
2019-12-07 18:14:40 +08:00
return &pb.Receipt{Proced: true}, nil
}
func (s *NezhaHandler) RequestTask(h *pb.Host, stream pb.NezhaService_RequestTaskServer) error {
2021-01-08 21:04:50 +08:00
var clientID uint64
2019-12-09 18:14:31 +08:00
var err error
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
2019-12-07 18:14:40 +08:00
return err
}
2019-12-10 17:57:57 +08:00
closeCh := make(chan error)
2022-01-09 11:54:14 +08:00
singleton.ServerLock.RLock()
2024-08-12 10:06:55 +08:00
singleton.ServerList[clientID].TaskCloseLock.Lock()
// 修复不断的请求 task 但是没有 return 导致内存泄漏
2022-01-09 11:54:14 +08:00
if singleton.ServerList[clientID].TaskClose != nil {
close(singleton.ServerList[clientID].TaskClose)
}
2022-01-09 11:54:14 +08:00
singleton.ServerList[clientID].TaskStream = stream
singleton.ServerList[clientID].TaskClose = closeCh
2024-08-12 10:06:55 +08:00
singleton.ServerList[clientID].TaskCloseLock.Unlock()
2022-01-09 11:54:14 +08:00
singleton.ServerLock.RUnlock()
return <-closeCh
2019-12-07 18:14:40 +08:00
}
func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Receipt, error) {
var clientID uint64
var err error
if clientID, err = s.Auth.Check(c); err != nil {
return nil, err
}
state := model.PB2State(r)
2022-01-09 11:54:14 +08:00
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()
singleton.ServerList[clientID].LastActive = time.Now()
singleton.ServerList[clientID].State = &state
// 应对 dashboard 重启的情况,如果从未记录过,先打点,等到小时时间点时入库
if singleton.ServerList[clientID].PrevTransferInSnapshot == 0 || singleton.ServerList[clientID].PrevTransferOutSnapshot == 0 {
singleton.ServerList[clientID].PrevTransferInSnapshot = int64(state.NetInTransfer)
singleton.ServerList[clientID].PrevTransferOutSnapshot = int64(state.NetOutTransfer)
}
return &pb.Receipt{Proced: true}, nil
}
func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
2021-01-08 21:04:50 +08:00
var clientID uint64
2019-12-09 18:14:31 +08:00
var err error
if clientID, err = s.Auth.Check(c); err != nil {
2019-12-07 18:14:40 +08:00
return nil, err
}
2019-12-13 17:56:14 +08:00
host := model.PB2Host(r)
2022-01-09 11:54:14 +08:00
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()
// 检查并更新DDNS
if singleton.ServerList[clientID].EnableDDNS && host.IP != "" &&
(singleton.ServerList[clientID].Host == nil || singleton.ServerList[clientID].Host.IP != host.IP) {
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
providers, err := singleton.GetDDNSProvidersFromProfiles(singleton.ServerList[clientID].DDNSProfiles, &ddns.IP{Ipv4Addr: ipv4, Ipv6Addr: ipv6})
if err == nil {
for _, provider := range providers {
go func(provider *ddns.Provider) {
provider.UpdateDomain(context.Background())
}(provider)
}
} else {
log.Printf("NEZHA>> 获取DDNS配置时发生错误: %v", err)
}
}
// 发送IP变动通知
if singleton.ServerList[clientID].Host != nil && singleton.Conf.EnableIPChangeNotification &&
2022-01-09 11:54:14 +08:00
((singleton.Conf.Cover == model.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
singleton.ServerList[clientID].Host.IP != "" &&
host.IP != "" &&
2022-01-09 11:54:14 +08:00
singleton.ServerList[clientID].Host.IP != host.IP {
2024-10-27 13:10:07 +08:00
singleton.SendNotification(singleton.Conf.IPChangeNotificationGroupID,
fmt.Sprintf(
"[%s] %s, %s => %s",
2024-11-01 05:07:04 +08:00
singleton.Localizer.T("IP Changed"),
2024-10-27 13:10:07 +08:00
singleton.ServerList[clientID].Name, singleton.IPDesensitize(singleton.ServerList[clientID].Host.IP),
singleton.IPDesensitize(host.IP),
),
nil)
}
2021-07-16 18:09:50 +08:00
/**
* 这里的 singleton 中的数据都是关机前的旧数据
* agent 重启时bootTime 变大agent 端会先上报 host 信息然后上报 state 信息
* 这是可以借助上报顺序的空档将停机前的流量统计数据标记下来加到下一个小时的数据点上
*/
if singleton.ServerList[clientID].Host != nil && singleton.ServerList[clientID].Host.BootTime < host.BootTime {
singleton.ServerList[clientID].PrevTransferInSnapshot = singleton.ServerList[clientID].PrevTransferInSnapshot - int64(singleton.ServerList[clientID].State.NetInTransfer)
singleton.ServerList[clientID].PrevTransferOutSnapshot = singleton.ServerList[clientID].PrevTransferOutSnapshot - int64(singleton.ServerList[clientID].State.NetOutTransfer)
2021-07-16 18:09:50 +08:00
}
2024-07-28 14:39:14 +08:00
// 不要冲掉国家码
if singleton.ServerList[clientID].Host != nil {
host.CountryCode = singleton.ServerList[clientID].Host.CountryCode
}
2022-01-09 11:54:14 +08:00
singleton.ServerList[clientID].Host = &host
2019-12-07 18:14:40 +08:00
return &pb.Receipt{Proced: true}, nil
}
func (s *NezhaHandler) IOStream(stream pb.NezhaService_IOStreamServer) error {
if _, err := s.Auth.Check(stream.Context()); err != nil {
return err
}
id, err := stream.Recv()
if err != nil {
return err
}
if id == nil || len(id.Data) < 4 || (id.Data[0] != 0xff && id.Data[1] != 0x05 && id.Data[2] != 0xff && id.Data[3] == 0x05) {
return fmt.Errorf("invalid stream id")
}
streamId := string(id.Data[4:])
if _, err := s.GetStream(streamId); err != nil {
return err
}
iw := grpcx.NewIOStreamWrapper(stream)
if err := s.AgentConnected(streamId, iw); err != nil {
return err
}
iw.Wait()
return nil
}
func (s *NezhaHandler) LookupGeoIP(c context.Context, r *pb.GeoIP) (*pb.GeoIP, error) {
var clientID uint64
var err error
if clientID, err = s.Auth.Check(c); err != nil {
return nil, err
}
// 根据内置数据库查询 IP 地理位置
ip := r.GetIp()
netIP := net.ParseIP(ip)
2024-10-31 03:34:25 +08:00
location, err := geoip.Lookup(netIP)
if err != nil {
return nil, err
}
// 将地区码写入到 Host
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()
if singleton.ServerList[clientID].Host == nil {
return nil, fmt.Errorf("host not found")
2024-07-28 14:39:14 +08:00
}
singleton.ServerList[clientID].Host.CountryCode = location
return &pb.GeoIP{Ip: ip, CountryCode: location}, nil
}