nezha/service/rpc/nezha.go

284 lines
8.1 KiB
Go
Raw Normal View History

2019-12-08 03:59:58 -05:00
package rpc
2019-12-07 05:14:40 -05:00
import (
"context"
"fmt"
"log"
"net"
"sync"
2020-10-24 09:29:05 -04:00
"time"
2019-12-07 05:14:40 -05:00
2024-12-04 11:11:34 -05:00
"github.com/jinzhu/copier"
2024-11-28 06:38:54 -05:00
"github.com/nezhahq/nezha/pkg/ddns"
geoipx "github.com/nezhahq/nezha/pkg/geoip"
"github.com/nezhahq/nezha/pkg/grpcx"
2024-11-28 06:38:54 -05:00
"github.com/nezhahq/nezha/model"
pb "github.com/nezhahq/nezha/proto"
"github.com/nezhahq/nezha/service/singleton"
2019-12-07 05:14:40 -05:00
)
var _ pb.NezhaServiceServer = (*NezhaHandler)(nil)
var NezhaHandlerSingleton *NezhaHandler
2019-12-07 05:14:40 -05: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 05:14:40 -05:00
}
2024-12-04 11:11:34 -05:00
func (s *NezhaHandler) RequestTask(stream pb.NezhaService_RequestTaskServer) error {
2021-01-08 08:04:50 -05:00
var clientID uint64
2019-12-09 05:14:31 -05:00
var err error
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
2019-12-07 05:14:40 -05:00
return err
}
2024-12-04 11:11:34 -05:00
2022-01-08 22:54:14 -05:00
singleton.ServerLock.RLock()
singleton.ServerList[clientID].TaskStream = stream
singleton.ServerLock.RUnlock()
2024-12-04 11:11:34 -05:00
var result *pb.TaskResult
for {
result, err = stream.Recv()
if err != nil {
log.Printf("NEZHA>> RequestTask error: %v, clientID: %d\n", err, clientID)
return nil
}
if result.GetType() == model.TaskTypeCommand {
// 处理上报的计划任务
singleton.CronLock.RLock()
cr := singleton.Crons[result.GetId()]
singleton.CronLock.RUnlock()
if cr != nil {
// 保存当前服务器状态信息
var curServer model.Server
singleton.ServerLock.RLock()
copier.Copy(&curServer, singleton.ServerList[clientID])
singleton.ServerLock.RUnlock()
if cr.PushSuccessful && result.GetSuccessful() {
singleton.SendNotification(cr.NotificationGroupID, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.T("Scheduled Task Executed Successfully"),
cr.Name, singleton.ServerList[clientID].Name, result.GetData()), nil, &curServer)
}
if !result.GetSuccessful() {
singleton.SendNotification(cr.NotificationGroupID, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.T("Scheduled Task Executed Failed"),
cr.Name, singleton.ServerList[clientID].Name, result.GetData()), nil, &curServer)
}
singleton.DB.Model(cr).Updates(model.Cron{
LastExecutedAt: time.Now().Add(time.Second * -1 * time.Duration(result.GetDelay())),
LastResult: result.GetSuccessful(),
})
}
} else if model.IsServiceSentinelNeeded(result.GetType()) {
singleton.ServiceSentinelShared.Dispatch(singleton.ReportData{
Data: result,
Reporter: clientID,
})
}
}
2019-12-07 05:14:40 -05:00
}
func (s *NezhaHandler) ReportSystemState(stream pb.NezhaService_ReportSystemStateServer) error {
var err error
var clientID uint64
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
return err
}
var state *pb.State
for {
state, err = stream.Recv()
if err != nil {
log.Printf("NEZHA>> ReportSystemState eror: %v, clientID: %d\n", err, clientID)
return nil
}
state := model.PB2State(state)
singleton.ServerLock.RLock()
2025-01-03 12:10:16 -05:00
server, ok := singleton.ServerList[clientID]
singleton.ServerLock.RUnlock()
2025-01-03 12:10:16 -05:00
if !ok || server == nil {
return nil
}
2025-01-03 12:10:16 -05:00
server.LastActive = time.Now()
server.State = &state
// 应对 dashboard 重启的情况,如果从未记录过,先打点,等到小时时间点时入库
2025-01-03 12:10:16 -05:00
if server.PrevTransferInSnapshot == 0 || server.PrevTransferOutSnapshot == 0 {
server.PrevTransferInSnapshot = int64(state.NetInTransfer)
server.PrevTransferOutSnapshot = int64(state.NetOutTransfer)
}
stream.Send(&pb.Receipt{Proced: true})
}
}
2024-12-06 22:06:42 -05:00
func (s *NezhaHandler) onReportSystemInfo(c context.Context, r *pb.Host) error {
2021-01-08 08:04:50 -05:00
var clientID uint64
2019-12-09 05:14:31 -05:00
var err error
if clientID, err = s.Auth.Check(c); err != nil {
2024-12-06 22:06:42 -05:00
return err
2019-12-07 05:14:40 -05:00
}
2019-12-13 04:56:14 -05:00
host := model.PB2Host(r)
2025-01-03 12:10:16 -05:00
2022-01-08 22:54:14 -05:00
singleton.ServerLock.RLock()
defer singleton.ServerLock.RUnlock()
2025-01-03 23:32:29 -05:00
server, ok := singleton.ServerList[clientID]
if !ok || server == nil {
return fmt.Errorf("server not found")
}
/**
* 这里的 singleton 中的数据都是关机前的旧数据
* agent 重启时bootTime 变大agent 端会先上报 host 信息然后上报 state 信息
* 这是可以借助上报顺序的空档将停机前的流量统计数据标记下来加到下一个小时的数据点上
*/
2025-01-03 23:32:29 -05:00
if server.Host != nil && server.Host.BootTime < host.BootTime {
server.PrevTransferInSnapshot = server.PrevTransferInSnapshot - int64(server.State.NetInTransfer)
server.PrevTransferOutSnapshot = server.PrevTransferOutSnapshot - int64(server.State.NetOutTransfer)
2021-07-16 06:09:50 -04:00
}
2025-01-03 23:32:29 -05:00
server.Host = &host
2024-12-06 22:06:42 -05:00
return nil
}
func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
s.onReportSystemInfo(c, r)
2019-12-07 05:14:40 -05:00
return &pb.Receipt{Proced: true}, nil
}
2024-12-06 23:19:48 -05:00
func (s *NezhaHandler) ReportSystemInfo2(c context.Context, r *pb.Host) (*pb.Uint64Receipt, error) {
2024-12-06 22:06:42 -05:00
s.onReportSystemInfo(c, r)
2024-12-06 23:19:48 -05:00
return &pb.Uint64Receipt{Data: singleton.DashboardBootTime}, nil
2024-12-06 22:06:42 -05:00
}
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
}
2024-12-04 11:11:34 -05:00
// ff05ff05 是 Nezha 的魔数,用于标识流 ID
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")
}
2024-12-04 11:11:34 -05:00
go func() {
for {
if err := stream.Send(&pb.IOStreamData{Data: []byte{}}); err != nil {
log.Printf("NEZHA>> IOStream keepAlive error: %v\n", err)
return
}
time.Sleep(time.Second * 30)
}
}()
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) ReportGeoIP(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
}
geoip := model.PB2GeoIP(r)
use6 := r.GetUse6()
if geoip.IP.IPv4Addr == "" && geoip.IP.IPv6Addr == "" {
ip, _ := c.Value(model.CtxKeyRealIP{}).(string)
if ip == "" {
ip, _ = c.Value(model.CtxKeyConnectingIP{}).(string)
}
geoip.IP.IPv4Addr = ip
}
joinedIP := geoip.IP.Join()
singleton.ServerLock.RLock()
2025-01-03 12:10:16 -05:00
server, ok := singleton.ServerList[clientID]
singleton.ServerLock.RUnlock()
if !ok || server == nil {
return nil, fmt.Errorf("server not found")
}
// 检查并更新DDNS
2025-01-03 12:10:16 -05:00
if server.EnableDDNS && joinedIP != "" &&
(server.GeoIP == nil || server.GeoIP.IP != geoip.IP) {
ipv4 := geoip.IP.IPv4Addr
ipv6 := geoip.IP.IPv6Addr
2025-01-03 12:10:16 -05:00
providers, err := singleton.GetDDNSProvidersFromProfiles(server.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变动通知
2025-01-03 12:10:16 -05:00
if server.GeoIP != nil && singleton.Conf.EnableIPChangeNotification &&
((singleton.Conf.Cover == model.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
2025-01-03 12:10:16 -05:00
server.GeoIP.IP.Join() != "" &&
joinedIP != "" &&
2025-01-03 12:10:16 -05:00
server.GeoIP.IP != geoip.IP {
singleton.SendNotification(singleton.Conf.IPChangeNotificationGroupID,
fmt.Sprintf(
"[%s] %s, %s => %s",
singleton.Localizer.T("IP Changed"),
2025-01-03 12:10:16 -05:00
server.Name, singleton.IPDesensitize(server.GeoIP.IP.Join()),
singleton.IPDesensitize(joinedIP),
),
nil)
}
// 根据内置数据库查询 IP 地理位置
var ip string
if geoip.IP.IPv6Addr != "" && (use6 || geoip.IP.IPv4Addr == "") {
ip = geoip.IP.IPv6Addr
} else {
ip = geoip.IP.IPv4Addr
}
netIP := net.ParseIP(ip)
location, err := geoipx.Lookup(netIP)
if err != nil {
log.Printf("NEZHA>> geoip.Lookup: %v", err)
}
geoip.CountryCode = location
// 将地区码写入到 Host
2025-01-03 12:10:16 -05:00
server.GeoIP = &geoip
return &pb.GeoIP{Ip: nil, CountryCode: location}, nil
}