Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,984 @@
package nodes
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/events"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/rpc"
"github.com/TeaOSLab/EdgeAPI/internal/setup"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gosock/pkg/gosock"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/status"
"log"
"net"
"os"
"os/exec"
"os/signal"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"syscall"
"time"
// grpc decompression
_ "google.golang.org/grpc/encoding/gzip"
)
var sharedAPIConfig *configs.APIConfig = nil
type APINode struct {
serviceInstanceMap map[string]any
serviceInstanceLocker sync.Mutex
sock *gosock.Sock
isStarting bool
issues []*StartIssue
issuesFile string
progress *utils.Progress
}
func NewAPINode() *APINode {
return &APINode{
serviceInstanceMap: map[string]any{},
sock: gosock.NewTmpSock(teaconst.ProcessName),
issues: []*StartIssue{},
issuesFile: Tea.LogFile("issues.log"),
}
}
func (this *APINode) Start() {
this.isStarting = true
logs.Println("[API_NODE]start api node, pid: " + strconv.Itoa(os.Getpid()))
// 保存启动过程中的问题,以便于查看
defer func() {
this.saveIssues()
}()
// 本地Sock
logs.Println("[API_NODE]listening sock ...")
err := this.listenSock()
if err != nil {
var errString = "start local sock failed: " + err.Error()
logs.Println("[API_NODE]" + errString)
this.addStartIssue("sock", errString, "")
return
}
// 监听信号
this.listenSignals()
// 启动IP库
this.setProgress("IP_LIBRARY", "开始初始化IP库")
remotelogs.Println("API_NODE", "initializing ip library ...")
err = iplibrary.InitDefault()
if err != nil {
// IP库初始化失败不应该阻止启动只记录错误
remotelogs.Error("API_NODE", "initialize ip library failed: "+err.Error())
logs.Println("[API_NODE]WARNING: initialize ip library failed: " + err.Error() + ", will continue with default library")
} else {
remotelogs.Println("API_NODE", "ip library initialized successfully")
}
// 检查数据库连接
this.setProgress("DATABASE", "正在检查数据库连接")
err = this.checkDB()
if err != nil {
var errString = "check database connection failed: " + err.Error()
logs.Println("[API_NODE]" + errString)
this.addStartIssue("db", errString, this.dbIssueSuggestion(err.Error()))
return
}
// 自动升级
logs.Println("[API_NODE]auto upgrading ...")
this.setProgress("DATABASE", "正在升级数据库")
err = this.autoUpgrade()
if err != nil {
var errString = "auto upgrade failed: " + err.Error()
logs.Println("[API_NODE]" + errString)
this.addStartIssue("db", errString, this.dbIssueSuggestion(err.Error()))
return
}
// 自动设置数据库
this.setProgress("DATABASE", "正在设置数据库")
logs.Println("[API_NODE]setup database ...")
err = this.setupDB()
if err != nil {
logs.Println("[API_NODE]setup database '" + err.Error() + "'")
// 不阻断执行
}
// 数据库通知启动
this.setProgress("DATABASE", "正在建立数据库模型")
logs.Println("[API_NODE]notify ready ...")
this.processTableNames()
dbs.NotifyReady()
// 设置时区
this.setProgress("TIMEZONE", "正在设置时区")
this.setupTimeZone()
// 读取配置
this.setProgress("DATABASE", "正在加载API配置")
logs.Println("[API_NODE]reading api config ...")
config, err := configs.SharedAPIConfig()
if err != nil {
var errString = "read api config failed: " + err.Error()
logs.Println("[API_NODE]" + errString)
this.addStartIssue("config", errString, "")
return
}
sharedAPIConfig = config
// 校验
apiNode, err := models.SharedAPINodeDAO.FindEnabledAPINodeWithUniqueIdAndSecret(nil, config.NodeId, config.Secret)
if err != nil {
var errString = "start failed: read api node from database failed: " + err.Error()
logs.Println("[API_NODE]" + errString)
this.addStartIssue("db", errString, "")
return
}
if apiNode == nil {
var errString = "can not start node, wrong 'nodeId' or 'secret'"
logs.Println("[API_NODE]" + errString)
this.addStartIssue("config", errString, "请在api.yaml配置文件中填写正确的`nodeId`和`secret`如果数据库或者管理节点或API节点是从别的服务器迁移过来的请将老的系统配置拷贝到当前节点配置下")
return
}
config.SetNumberId(int64(apiNode.Id))
// 清除上一次启动错误
// 这个错误文件可能不存在,不需要处理错误
_ = os.Remove(this.issuesFile)
// 设置rlimit
_ = utils.SetRLimit(1024 * 1024)
// 状态变更计时器
goman.New(func() {
NewNodeStatusExecutor().Listen()
})
// 访问日志存储管理器
this.setProgress("ACCESS_LOG_STORAGES", "正在启动访问日志存储器")
this.startAccessLogStorages()
// 监听RPC服务
this.setProgress("LISTEN_PORT", "正在启动监听端口")
remotelogs.Println("API_NODE", "starting RPC server ...")
var isListening = this.listenPorts(apiNode)
if !isListening {
var errString = "the api node require at least one listening address"
remotelogs.Error("API_NODE", errString)
this.addStartIssue("config", errString, "请给当前API节点设置一个监听端口")
return
}
// 结束启动
this.isStarting = false
this.progress = nil
// 保持进程
select {}
}
// Daemon 实现守护进程
func (this *APINode) Daemon() {
var path = os.TempDir() + "/" + teaconst.ProcessName + ".sock"
var isDebug = lists.ContainsString(os.Args, "debug")
for {
conn, err := net.DialTimeout("unix", path, 1*time.Second)
if err != nil {
if isDebug {
log.Println("[DAEMON]starting ...")
}
// 尝试启动
err = func() error {
exe, err := os.Executable()
if err != nil {
return err
}
cmd := exec.Command(exe)
err = cmd.Start()
if err != nil {
return err
}
err = cmd.Wait()
if err != nil {
return err
}
return nil
}()
if err != nil {
if isDebug {
log.Println("[DAEMON]", err)
}
time.Sleep(1 * time.Second)
} else {
time.Sleep(5 * time.Second)
}
} else {
_ = conn.Close()
time.Sleep(5 * time.Second)
}
}
}
// InstallSystemService 安装系统服务
func (this *APINode) InstallSystemService() error {
var shortName = teaconst.SystemdServiceName
exe, err := os.Executable()
if err != nil {
return err
}
var manager = utils.NewServiceManager(shortName, teaconst.ProductName)
err = manager.Install(exe, []string{})
if err != nil {
return err
}
return nil
}
// 启动RPC监听
func (this *APINode) listenRPC(listener net.Listener, tlsConfig *tls.Config) error {
var rpcServer *grpc.Server
var options = []grpc.ServerOption{
grpc.MaxRecvMsgSize(512 << 20),
grpc.MaxSendMsgSize(512 << 20),
grpc.UnaryInterceptor(this.unaryInterceptor),
}
if tlsConfig == nil {
remotelogs.Println("API_NODE", "listening GRPC http://"+listener.Addr().String()+" ...")
rpcServer = grpc.NewServer(options...)
} else {
logs.Println("[API_NODE]listening GRPC https://" + listener.Addr().String() + " ...")
options = append(options, grpc.Creds(credentials.NewTLS(tlsConfig)))
rpcServer = grpc.NewServer(options...)
}
this.registerServices(rpcServer)
err := rpcServer.Serve(listener)
if err != nil {
return fmt.Errorf("[API_NODE]start rpc failed: %w", err)
}
return nil
}
// 检查数据库
func (this *APINode) checkDB() error {
logs.Println("[API_NODE]checking database connection ...")
// generate .db.yaml
{
data, err := os.ReadFile(Tea.ConfigFile("db.yaml"))
if err != nil {
return errors.New("could not find database config file 'db.yaml' (at " + Tea.ConfigFile("db.yaml") + ")")
}
simpleConfig, err := configs.ParseSimpleDBConfig(data)
if err == nil && len(simpleConfig.Host) > 0 {
err = simpleConfig.GenerateOldConfig()
if err != nil {
return err
}
}
}
// lookup mysqld_safe process
go dbutils.FindMySQLPathAndRemember()
db, err := dbs.Default()
if err != nil {
return err
}
// 第一次测试连接
_, err = db.Exec("SELECT 1")
if err != nil {
var errString = "check database connection failed: " + err.Error()
logs.Println("[API_NODE]" + errString)
this.addStartIssue("db", errString, this.dbIssueSuggestion(errString))
// 决定是否尝试启动本地的MySQL
if strings.Contains(err.Error(), "connection refused") {
config, _ := db.Config()
if config != nil && (strings.Contains(config.Dsn, "tcp(127.0.0.1:") || strings.Contains(config.Dsn, "tcp(localhost:")) && os.Getgid() == 0 /** ROOT 用户 **/ {
dbutils.StartLocalMySQL()
}
}
// 多次尝试
var maxTries = 600
if Tea.IsTesting() {
maxTries = 600
}
for i := 0; i <= maxTries; i++ {
_, err = db.Exec("SELECT 1")
if err != nil {
if i == maxTries-1 {
return err
} else {
if i%10 == 0 { // 这让提示不会太多
logs.Println("[API_NODE]check database connection failed: " + err.Error() + ", reconnecting to database ...")
}
time.Sleep(1 * time.Second)
}
} else {
logs.Println("[API_NODE]database connected")
return nil
}
}
}
return nil
}
// 自动升级
func (this *APINode) autoUpgrade() error {
if Tea.IsTesting() {
return nil
}
// 执行SQL
config, err := configs.LoadDBConfig()
if err != nil {
return fmt.Errorf("decode database config failed: %w", err)
}
var dbConfig = config.DBs[Tea.Env]
db, err := dbs.NewInstanceFromConfig(dbConfig)
if err != nil {
return fmt.Errorf("load database failed: %w", err)
}
defer func() {
_ = db.Close()
}()
one, err := db.FindOne("SELECT version FROM edgeVersions LIMIT 1")
if err != nil {
return fmt.Errorf("query version failed: %w", err)
}
if one != nil {
// 如果是同样的版本,则直接认为是最新版本
var version = one.GetString("version")
if setup.CompareVersion(version, setup.ComposeSQLVersion()) >= 0 {
return nil
}
}
// 不使用remotelog(),因为此时还没有启动完成
logs.Println("[API_NODE]upgrade database starting ...")
err = setup.NewSQLExecutor(dbConfig).Run(false)
if err != nil {
return fmt.Errorf("execute sql failed: %w", err)
}
// 不使用remotelog
logs.Println("[API_NODE]upgrade database done")
return nil
}
// 自动设置数据库
func (this *APINode) setupDB() error {
db, err := dbs.Default()
if err != nil {
return err
}
// 检查是否为root用户
config, _ := db.Config()
if config == nil {
return nil
}
dsnConfig, err := mysql.ParseDSN(config.Dsn)
if err != nil || dsnConfig == nil {
return err
}
if dsnConfig.User != "root" {
return nil
}
// 设置Innodb事务提交模式
{
result, err := db.FindOne("SHOW VARIABLES WHERE variable_name='innodb_flush_log_at_trx_commit'")
if err == nil && result != nil {
var oldValue = result.GetInt("Value")
if oldValue == 1 {
_, _ = db.Exec("SET GLOBAL innodb_flush_log_at_trx_commit=2")
}
}
}
// 调整预处理语句数量
_ = dbutils.SetGlobalVarMin(db, "max_prepared_stmt_count", 65535)
// 调整binlog过期时间
{
const binlogExpireDays = 7
version, err := db.FindCol(0, "SELECT VERSION()")
if err == nil {
var versionString = types.String(version)
if strings.HasPrefix(versionString, "8.") {
_ = dbutils.SetGlobalVarMax(db, "binlog_expire_logs_seconds", binlogExpireDays*86400)
} else if strings.HasPrefix(versionString, "5.") {
_ = dbutils.SetGlobalVarMax(db, "expire_logs_days", binlogExpireDays)
}
}
}
// 设置binlog_cache_size
_ = dbutils.SetGlobalVarMin(db, "binlog_cache_size", 1*1024*1024)
// 设置binlog_stmt_cache_size
_ = dbutils.SetGlobalVarMin(db, "binlog_stmt_cache_size", 1*1024*1024)
// 设置thread_cache_size
_ = dbutils.SetGlobalVarMin(db, "thread_cache_size", 32)
return nil
}
// 处理表名兼容
func (this *APINode) processTableNames() {
dbs.OnDAOInitError(func(dao dbs.DAOInterface, err error) error {
if err == nil {
return nil
}
if errors.Is(err, dbs.ErrTableNotFound) {
var instance = dao.Object().Instance
if instance == nil {
return err
}
// 查找完全小写的
var lowerTableName = strings.ToLower(dao.Object().Table)
lowerTable, _ := instance.FindTable(lowerTableName)
if lowerTable != nil {
_, err = instance.Exec("RENAME TABLE `" + lowerTableName + "` TO `" + dao.Object().Table + "`")
if err == nil {
logs.Println("[API_NODE]rename table '" + lowerTableName + "' to '" + dao.Object().Table + "'")
return dao.Object().Init()
}
}
}
return err
})
}
// 启动端口
func (this *APINode) listenPorts(apiNode *models.APINode) (isListening bool) {
// HTTP
httpConfig, err := apiNode.DecodeHTTP()
if err != nil {
remotelogs.Error("API_NODE", "decode http config: "+err.Error())
return
}
var ports = []int{}
isListening = false
if httpConfig != nil && httpConfig.IsOn && len(httpConfig.Listen) > 0 {
for _, listen := range httpConfig.Listen {
for _, addr := range listen.Addresses() {
// 收集Port
_, portString, _ := net.SplitHostPort(addr)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
listener, err := net.Listen("tcp", addr)
if err != nil {
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
// 试着只监听端口
_, port, err := net.SplitHostPort(addr)
if err != nil {
continue
}
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ...")
listener, err = net.Listen("tcp", ":"+port)
if err != nil {
remotelogs.Error("API_NODE", "listening ':"+port+"' failed: "+err.Error())
continue
}
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ok")
}
goman.New(func() {
err := this.listenRPC(listener, nil)
if err != nil {
remotelogs.Error("API_NODE", "listening '"+addr+"' rpc: "+err.Error())
return
}
})
isListening = true
}
}
}
// HTTPS
httpsConfig, err := apiNode.DecodeHTTPS(nil, nil)
if err != nil {
remotelogs.Error("API_NODE", "decode https config: "+err.Error())
return
}
if httpsConfig != nil &&
httpsConfig.IsOn &&
len(httpsConfig.Listen) > 0 &&
httpsConfig.SSLPolicy != nil &&
httpsConfig.SSLPolicy.IsOn &&
len(httpsConfig.SSLPolicy.Certs) > 0 {
certs := []tls.Certificate{}
for _, cert := range httpsConfig.SSLPolicy.Certs {
certs = append(certs, *cert.CertObject())
}
for _, listen := range httpsConfig.Listen {
for _, addr := range listen.Addresses() {
// 收集Port
_, portString, _ := net.SplitHostPort(addr)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
listener, err := net.Listen("tcp", addr)
if err != nil {
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
// 试着只监听端口
_, port, err := net.SplitHostPort(addr)
if err != nil {
continue
}
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ...")
listener, err = net.Listen("tcp", ":"+port)
if err != nil {
remotelogs.Error("API_NODE", "listening ':"+port+"' failed: "+err.Error())
continue
}
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ok")
}
goman.New(func() {
err := this.listenRPC(listener, &tls.Config{
Certificates: certs,
})
if err != nil {
remotelogs.Error("API_NODE", "listening '"+addr+"' rpc: "+err.Error())
return
}
})
isListening = true
}
}
}
// Rest HTTP
restHTTPConfig, err := apiNode.DecodeRestHTTP()
if err != nil {
remotelogs.Error("API_NODE", "decode REST http config: "+err.Error())
return
}
if restHTTPConfig != nil && restHTTPConfig.IsOn && len(restHTTPConfig.Listen) > 0 {
for _, listen := range restHTTPConfig.Listen {
for _, addr := range listen.Addresses() {
// 收集Port
_, portString, _ := net.SplitHostPort(addr)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
listener, err := net.Listen("tcp", addr)
if err != nil {
remotelogs.Error("API_NODE", "listening REST 'http://"+addr+"' failed: "+err.Error())
continue
}
goman.New(func() {
remotelogs.Println("API_NODE", "listening REST http://"+addr+" ...")
server := &RestServer{}
err := server.Listen(listener)
if err != nil {
remotelogs.Error("API_NODE", "listening REST 'http://"+addr+"' failed: "+err.Error())
return
}
})
isListening = true
}
}
}
// Rest HTTPS
restHTTPSConfig, err := apiNode.DecodeRestHTTPS(nil, nil)
if err != nil {
remotelogs.Error("API_NODE", "decode REST https config: "+err.Error())
return
}
if restHTTPSConfig != nil &&
restHTTPSConfig.IsOn &&
len(restHTTPSConfig.Listen) > 0 &&
restHTTPSConfig.SSLPolicy != nil &&
restHTTPSConfig.SSLPolicy.IsOn &&
len(restHTTPSConfig.SSLPolicy.Certs) > 0 {
for _, listen := range restHTTPSConfig.Listen {
for _, addr := range listen.Addresses() {
// 收集Port
_, portString, _ := net.SplitHostPort(addr)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
listener, err := net.Listen("tcp", addr)
if err != nil {
remotelogs.Error("API_NODE", "listening REST 'https://"+addr+"' failed: "+err.Error())
continue
}
goman.New(func() {
remotelogs.Println("API_NODE", "listening REST https://"+addr+" ...")
server := &RestServer{}
certs := []tls.Certificate{}
for _, cert := range httpsConfig.SSLPolicy.Certs {
certs = append(certs, *cert.CertObject())
}
err := server.ListenHTTPS(listener, &tls.Config{
Certificates: certs,
})
if err != nil {
remotelogs.Error("API_NODE", "listening REST 'https://"+addr+"' failed: "+err.Error())
return
}
})
isListening = true
}
}
}
// add to local firewall
if len(ports) > 0 {
go utils.AddPortsToFirewall(ports)
}
return
}
// 监听本地sock
func (this *APINode) listenSock() error {
// 检查是否在运行
if this.sock.IsListening() {
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
if err == nil {
return errors.New("error: the process is already running, pid: " + maps.NewMap(reply.Params).GetString("pid"))
} else {
return errors.New("error: the process is already running")
}
}
// 启动监听
goman.New(func() {
this.sock.OnCommand(func(cmd *gosock.Command) {
switch cmd.Code {
case "pid": // 查询PID
_ = cmd.Reply(&gosock.Command{
Code: "pid",
Params: map[string]any{
"pid": os.Getpid(),
},
})
case "info": // 进程相关信息
exePath, _ := os.Executable()
_ = cmd.Reply(&gosock.Command{
Code: "info",
Params: map[string]any{
"pid": os.Getpid(),
"version": teaconst.Version,
"path": exePath,
},
})
case "stop": // 停止
_ = cmd.ReplyOk()
// 退出主进程
events.Notify(events.EventQuit)
os.Exit(0)
case "starting": // 是否正在启动
_ = cmd.Reply(&gosock.Command{
Code: "starting",
Params: map[string]any{
"isStarting": this.isStarting,
"progress": this.progress,
},
})
case "goman":
var posMap = map[string]maps.Map{} // file#line => Map
for _, instance := range goman.List() {
var pos = instance.File + "#" + types.String(instance.Line)
m, ok := posMap[pos]
if ok {
m["count"] = m["count"].(int) + 1
} else {
m = maps.Map{
"pos": pos,
"count": 1,
}
posMap[pos] = m
}
}
var result = []maps.Map{}
for _, m := range posMap {
result = append(result, m)
}
sort.Slice(result, func(i, j int) bool {
return result[i]["count"].(int) > result[j]["count"].(int)
})
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{
"total": runtime.NumGoroutine(),
"result": result,
},
})
case "debug": // 进入|取消调试模式
teaconst.Debug = !teaconst.Debug
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{"debug": teaconst.Debug},
})
case "db.stmt.prepare": // 显示prepared的语句
dbs.ShowPreparedStatements = !dbs.ShowPreparedStatements
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{"isOn": dbs.ShowPreparedStatements},
})
case "db.stmt.count": // 查询prepared语句数量
db, _ := dbs.Default()
if db != nil {
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{"count": db.StmtManager().Len()},
})
} else {
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{"count": 0},
})
}
case "instance": // 获取实例代号
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{
"code": teaconst.InstanceCode,
},
})
case "lookupToken":
var role = maps.NewMap(cmd.Params).GetString("role")
switch role {
case "admin", "user", "api":
tokens, err := models.SharedApiTokenDAO.FindAllEnabledAPITokens(nil, role)
if err != nil {
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{
"isOk": false,
"err": err.Error(),
},
})
} else {
var tokenMaps = []maps.Map{}
for _, token := range tokens {
tokenMaps = append(tokenMaps, maps.Map{
"nodeId": token.NodeId,
"secret": token.Secret,
})
}
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{
"isOk": true,
"tokens": tokenMaps,
},
})
}
default:
_ = cmd.Reply(&gosock.Command{
Params: map[string]any{
"isOk": false,
"err": "unsupported role '" + role + "'",
},
})
}
}
})
err := this.sock.Listen()
if err != nil {
remotelogs.Println("API_NODE", err.Error())
}
})
events.On(events.EventQuit, func() {
remotelogs.Println("API_NODE", "quit unix sock")
_ = this.sock.Close()
})
return nil
}
// 服务过滤器
func (this *APINode) unaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
if teaconst.Debug {
var before = time.Now()
var traceCtx = rpc.NewContext(ctx)
resp, err = handler(traceCtx, req)
var costMs = time.Since(before).Seconds() * 1000
statErr := models.SharedAPIMethodStatDAO.CreateStat(nil, info.FullMethod, "", costMs)
if statErr != nil {
remotelogs.Error("API_NODE", "create method stat failed: "+statErr.Error())
}
var tagMap = traceCtx.TagMap()
for tag, tagCostMs := range tagMap {
statErr = models.SharedAPIMethodStatDAO.CreateStat(nil, info.FullMethod, tag, tagCostMs)
if statErr != nil {
remotelogs.Error("API_NODE", "create method stat failed: "+statErr.Error())
}
}
return
}
result, err := handler(ctx, req)
if err != nil {
statusErr, ok := status.FromError(err)
if ok {
err = status.Error(statusErr.Code(), "'"+info.FullMethod+"()' says: "+err.Error())
} else {
err = errors.New("'" + info.FullMethod + "()' says: " + err.Error())
}
}
return result, err
}
// 添加启动相关的Issue
func (this *APINode) addStartIssue(code string, message string, suggestion string) {
this.issues = append(this.issues, NewStartIssue(code, message, suggestion))
this.saveIssues()
}
// 增加数据库建议
func (this *APINode) dbIssueSuggestion(errString string) string {
// 数据库配置
db, err := dbs.Default()
if err != nil {
return ""
}
config, err := db.Config()
if err != nil {
return ""
}
var dsn = config.Dsn
dsnConfig, err := mysql.ParseDSN(dsn)
if err != nil {
return ""
}
var addr = dsnConfig.Addr
// 配置文件位置
var dbConfigPath = Tea.ConfigFile("db.yaml")
// 连接被拒绝
if strings.Contains(errString, "connection refused") {
// 本机
if strings.HasPrefix(addr, "127.0.0.1:") || strings.HasPrefix(addr, "localhost:") {
return "试图连接到数据库被拒绝请检查1本地数据库服务是否已经启动2数据库IP和端口" + addr + ")是否正确;(当前数据库配置为:" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
} else {
return "试图连接到数据库被拒绝请检查1数据库服务是否已经启动2数据库IP和端口" + addr + "是否正确3防火墙设置当前数据库配置为" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
}
}
// 权限错误
if strings.Contains(errString, "Error 1045") || strings.Contains(errString, "Error 1044") {
return "使用的用户和密码没有权限连接到指定数据库请检查1数据库配置文件中的用户名" + dsnConfig.User + ")和密码(" + dsnConfig.Passwd + "是否正确2使用的用户是否已经在数据库中设置了正确的权限当前数据库配置为" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
}
// 数据库名称错误
if strings.Contains(errString, "Error 1049") {
return "数据库名称配置错误,请检查:数据库配置文件中数据库名称(" + dsnConfig.DBName + ")是否正确;(当前数据库配置为:" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
}
return ""
}
// 保存issues
func (this *APINode) saveIssues() {
issuesJSON, err := json.Marshal(this.issues)
if err == nil {
_ = os.WriteFile(this.issuesFile, issuesJSON, 0666)
}
}
// 设置启动进度
func (this *APINode) setProgress(name, description string) {
this.progress = &utils.Progress{
Name: name,
Description: description,
}
}
// 设置时区
func (this *APINode) setupTimeZone() {
config, err := models.SharedSysSettingDAO.ReadAdminUIConfig(nil, nil)
if err == nil && config != nil {
if len(config.TimeZone) == 0 {
config.TimeZone = nodeconfigs.DefaultTimeZoneLocation
}
location, err := time.LoadLocation(config.TimeZone)
if err == nil && time.Local != location {
time.Local = location
}
}
}
// 监听一些信号
func (this *APINode) listenSignals() {
var queue = make(chan os.Signal, 8)
signal.Notify(queue, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT)
goman.New(func() {
for range queue {
events.Notify(events.EventQuit)
os.Exit(0)
return
}
})
}

View File

@@ -0,0 +1,8 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package nodes
func (this *APINode) startAccessLogStorages() {
}

View File

@@ -0,0 +1,15 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package nodes
import (
"github.com/TeaOSLab/EdgeAPI/internal/accesslogs"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
)
func (this *APINode) startAccessLogStorages() {
goman.New(func() {
accesslogs.SharedStorageManager.Start()
})
}

View File

@@ -0,0 +1,583 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package nodes
import (
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/clients"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/users"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"google.golang.org/grpc"
"reflect"
"strings"
)
// 注册服务
func (this *APINode) registerServices(server *grpc.Server) {
{
var instance = this.serviceInstance(&services.PingService{}).(*services.PingService)
pb.RegisterPingServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.APITokenService{}).(*services.APITokenService)
pb.RegisterAPITokenServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.AdminService{}).(*services.AdminService)
pb.RegisterAdminServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeGrantService{}).(*services.NodeGrantService)
pb.RegisterNodeGrantServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerService{}).(*services.ServerService)
pb.RegisterServerServiceServer(server, instance)
this.rest(instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeService{}).(*services.NodeService)
pb.RegisterNodeServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeClusterService{}).(*services.NodeClusterService)
pb.RegisterNodeClusterServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeIPAddressService{}).(*services.NodeIPAddressService)
pb.RegisterNodeIPAddressServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeIPAddressLogService{}).(*services.NodeIPAddressLogService)
pb.RegisterNodeIPAddressLogServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeIPAddressThresholdService{}).(*services.NodeIPAddressThresholdService)
pb.RegisterNodeIPAddressThresholdServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.APINodeService{}).(*services.APINodeService)
pb.RegisterAPINodeServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.APIMethodStatService{}).(*services.APIMethodStatService)
pb.RegisterAPIMethodStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.OriginService{}).(*services.OriginService)
pb.RegisterOriginServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPWebService{}).(*services.HTTPWebService)
pb.RegisterHTTPWebServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ReverseProxyService{}).(*services.ReverseProxyService)
pb.RegisterReverseProxyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPGzipService{}).(*services.HTTPGzipService)
pb.RegisterHTTPGzipServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPHeaderPolicyService{}).(*services.HTTPHeaderPolicyService)
pb.RegisterHTTPHeaderPolicyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPHeaderService{}).(*services.HTTPHeaderService)
pb.RegisterHTTPHeaderServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPPageService{}).(*services.HTTPPageService)
pb.RegisterHTTPPageServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPCachePolicyService{}).(*services.HTTPCachePolicyService)
pb.RegisterHTTPCachePolicyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPFirewallPolicyService{}).(*services.HTTPFirewallPolicyService)
pb.RegisterHTTPFirewallPolicyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.FirewallService{}).(*services.FirewallService)
pb.RegisterFirewallServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPLocationService{}).(*services.HTTPLocationService)
pb.RegisterHTTPLocationServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPWebsocketService{}).(*services.HTTPWebsocketService)
pb.RegisterHTTPWebsocketServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPRewriteRuleService{}).(*services.HTTPRewriteRuleService)
pb.RegisterHTTPRewriteRuleServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.SSLCertService{}).(*services.SSLCertService)
pb.RegisterSSLCertServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.SSLPolicyService{}).(*services.SSLPolicyService)
pb.RegisterSSLPolicyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.SysSettingService{}).(*services.SysSettingService)
pb.RegisterSysSettingServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPFirewallRuleGroupService{}).(*services.HTTPFirewallRuleGroupService)
pb.RegisterHTTPFirewallRuleGroupServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPFirewallRuleSetService{}).(*services.HTTPFirewallRuleSetService)
pb.RegisterHTTPFirewallRuleSetServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.DBNodeService{}).(*services.DBNodeService)
pb.RegisterDBNodeServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeLogService{}).(*services.NodeLogService)
pb.RegisterNodeLogServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeLoginService{}).(*services.NodeLoginService)
pb.RegisterNodeLoginServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPAccessLogService{}).(*services.HTTPAccessLogService)
pb.RegisterHTTPAccessLogServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.MessageService{}).(*services.MessageService)
pb.RegisterMessageServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeGroupService{}).(*services.NodeGroupService)
pb.RegisterNodeGroupServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeRegionService{}).(*services.NodeRegionService)
pb.RegisterNodeRegionServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerGroupService{}).(*services.ServerGroupService)
pb.RegisterServerGroupServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.IPLibraryService{}).(*services.IPLibraryService)
pb.RegisterIPLibraryServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.IPLibraryFileService{}).(*services.IPLibraryFileService)
pb.RegisterIPLibraryFileServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.IPLibraryArtifactService{}).(*services.IPLibraryArtifactService)
pb.RegisterIPLibraryArtifactServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.FileChunkService{}).(*services.FileChunkService)
pb.RegisterFileChunkServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.FileService{}).(*services.FileService)
pb.RegisterFileServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.RegionCountryService{}).(*services.RegionCountryService)
pb.RegisterRegionCountryServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.RegionProvinceService{}).(*services.RegionProvinceService)
pb.RegisterRegionProvinceServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.RegionCityService{}).(*services.RegionCityService)
pb.RegisterRegionCityServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.RegionTownService{}).(*services.RegionTownService)
pb.RegisterRegionTownServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.RegionProviderService{}).(*services.RegionProviderService)
pb.RegisterRegionProviderServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.IPListService{}).(*services.IPListService)
pb.RegisterIPListServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.IPItemService{}).(*services.IPItemService)
pb.RegisterIPItemServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.LogService{}).(*services.LogService)
pb.RegisterLogServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.DNSProviderService{}).(*services.DNSProviderService)
pb.RegisterDNSProviderServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.DNSDomainService{}).(*services.DNSDomainService)
pb.RegisterDNSDomainServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.DNSService{}).(*services.DNSService)
pb.RegisterDNSServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ACMEUserService{}).(*services.ACMEUserService)
pb.RegisterACMEUserServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ACMETaskService{}).(*services.ACMETaskService)
pb.RegisterACMETaskServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ACMEAuthenticationService{}).(*services.ACMEAuthenticationService)
pb.RegisterACMEAuthenticationServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ACMEProviderService{}).(*services.ACMEProviderService)
pb.RegisterACMEProviderServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ACMEProviderAccountService{}).(*services.ACMEProviderAccountService)
pb.RegisterACMEProviderAccountServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&users.UserService{}).(*users.UserService)
pb.RegisterUserServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.UserIdentityService{}).(*services.UserIdentityService)
pb.RegisterUserIdentityServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerDailyStatService{}).(*services.ServerDailyStatService)
pb.RegisterServerDailyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.LoginService{}).(*services.LoginService)
pb.RegisterLoginServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.LoginSessionService{}).(*services.LoginSessionService)
pb.RegisterLoginSessionServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.LoginTicketService{}).(*services.LoginTicketService)
pb.RegisterLoginTicketServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.UserAccessKeyService{}).(*services.UserAccessKeyService)
pb.RegisterUserAccessKeyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.SysLockerService{}).(*services.SysLockerService)
pb.RegisterSysLockerServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeTaskService{}).(*services.NodeTaskService)
pb.RegisterNodeTaskServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeValueService{}).(*services.NodeValueService)
pb.RegisterNodeValueServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.DBService{}).(*services.DBService)
pb.RegisterDBServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerRegionCityMonthlyStatService{}).(*services.ServerRegionCityMonthlyStatService)
pb.RegisterServerRegionCityMonthlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerRegionCountryMonthlyStatService{}).(*services.ServerRegionCountryMonthlyStatService)
pb.RegisterServerRegionCountryMonthlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerRegionProvinceMonthlyStatService{}).(*services.ServerRegionProvinceMonthlyStatService)
pb.RegisterServerRegionProvinceMonthlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerRegionProviderMonthlyStatService{}).(*services.ServerRegionProviderMonthlyStatService)
pb.RegisterServerRegionProviderMonthlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&clients.FormalClientSystemService{}).(*clients.FormalClientSystemService)
pb.RegisterFormalClientSystemServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&clients.FormalClientBrowserService{}).(*clients.FormalClientBrowserService)
pb.RegisterFormalClientBrowserServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&clients.ClientAgentIPService{}).(*clients.ClientAgentIPService)
pb.RegisterClientAgentIPServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&clients.ClientAgentService{}).(*clients.ClientAgentService)
pb.RegisterClientAgentServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerClientSystemMonthlyStatService{}).(*services.ServerClientSystemMonthlyStatService)
pb.RegisterServerClientSystemMonthlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerClientBrowserMonthlyStatService{}).(*services.ServerClientBrowserMonthlyStatService)
pb.RegisterServerClientBrowserMonthlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerHTTPFirewallDailyStatService{}).(*services.ServerHTTPFirewallDailyStatService)
pb.RegisterServerHTTPFirewallDailyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.DNSTaskService{}).(*services.DNSTaskService)
pb.RegisterDNSTaskServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeClusterFirewallActionService{}).(*services.NodeClusterFirewallActionService)
pb.RegisterNodeClusterFirewallActionServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.AuthorityNodeService{}).(*services.AuthorityNodeService)
pb.RegisterAuthorityNodeServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.LatestItemService{}).(*services.LatestItemService)
pb.RegisterLatestItemServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeThresholdService{}).(*services.NodeThresholdService)
pb.RegisterNodeThresholdServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPFastcgiService{}).(*services.HTTPFastcgiService)
pb.RegisterHTTPFastcgiServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPAuthPolicyService{}).(*services.HTTPAuthPolicyService)
pb.RegisterHTTPAuthPolicyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.MetricItemService{}).(*services.MetricItemService)
pb.RegisterMetricItemServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.NodeClusterMetricItemService{}).(*services.NodeClusterMetricItemService)
pb.RegisterNodeClusterMetricItemServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.MetricStatService{}).(*services.MetricStatService)
pb.RegisterMetricStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.MetricChartService{}).(*services.MetricChartService)
pb.RegisterMetricChartServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerStatBoardService{}).(*services.ServerStatBoardService)
pb.RegisterServerStatBoardServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerStatBoardChartService{}).(*services.ServerStatBoardChartService)
pb.RegisterServerStatBoardChartServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.PlanService{}).(*services.PlanService)
pb.RegisterPlanServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.UserPlanService{}).(*services.UserPlanService)
pb.RegisterUserPlanServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerDomainHourlyStatService{}).(*services.ServerDomainHourlyStatService)
pb.RegisterServerDomainHourlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.TrafficDailyStatService{}).(*services.TrafficDailyStatService)
pb.RegisterTrafficDailyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPCacheTaskKeyService{}).(*services.HTTPCacheTaskKeyService)
pb.RegisterHTTPCacheTaskKeyServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.HTTPCacheTaskService{}).(*services.HTTPCacheTaskService)
pb.RegisterHTTPCacheTaskServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerBandwidthStatService{}).(*services.ServerBandwidthStatService)
pb.RegisterServerBandwidthStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.UpdatingServerListService{}).(*services.UpdatingServerListService)
pb.RegisterUpdatingServerListServiceServer(server, instance)
this.rest(instance)
}
APINodeServicesRegister(this, server)
// TODO check service names
for serviceName := range server.GetServiceInfo() {
var index = strings.LastIndex(serviceName, ".")
if index >= 0 {
serviceName = serviceName[index+1:]
}
_, ok := restServicesMap[serviceName]
if !ok {
panic("can not find service '" + serviceName + "' in rest")
}
}
}
func (this *APINode) rest(instance interface{}) {
this.serviceInstanceLocker.Lock()
defer this.serviceInstanceLocker.Unlock()
var name = reflect.TypeOf(instance).String()
var index = strings.LastIndex(name, ".")
if index >= 0 {
name = name[index+1:]
}
_, ok := restServicesMap[name]
if ok {
return
}
restServicesMap[name] = reflect.ValueOf(instance)
}
func (this *APINode) serviceInstance(instance interface{}) interface{} {
this.serviceInstanceLocker.Lock()
defer this.serviceInstanceLocker.Unlock()
var typeName = reflect.TypeOf(instance).String()
result, ok := this.serviceInstanceMap[typeName]
if ok {
return result
}
this.serviceInstanceMap[typeName] = instance
return instance
}

View File

@@ -0,0 +1,10 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build !plus
// +build !plus
package nodes
import "google.golang.org/grpc"
func APINodeServicesRegister(node *APINode, server *grpc.Server) {
}

View File

@@ -0,0 +1,316 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nodes
import (
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/accounts"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/anti-ddos"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/nameservers"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/posts"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/reporters"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/tickets"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/trafficpackages"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/users"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"google.golang.org/grpc"
)
func APINodeServicesRegister(node *APINode, server *grpc.Server) {
{
var instance = node.serviceInstance(&nameservers.NSClusterService{}).(*nameservers.NSClusterService)
pb.RegisterNSClusterServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSNodeService{}).(*nameservers.NSNodeService)
pb.RegisterNSNodeServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSDomainService{}).(*nameservers.NSDomainService)
pb.RegisterNSDomainServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSDomainGroupService{}).(*nameservers.NSDomainGroupService)
pb.RegisterNSDomainGroupServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSRecordService{}).(*nameservers.NSRecordService)
pb.RegisterNSRecordServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSRouteService{}).(*nameservers.NSRouteService)
pb.RegisterNSRouteServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSRouteCategoryService{}).(*nameservers.NSRouteCategoryService)
pb.RegisterNSRouteCategoryServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSKeyService{}).(*nameservers.NSKeyService)
pb.RegisterNSKeyServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSAccessLogService{}).(*nameservers.NSAccessLogService)
pb.RegisterNSAccessLogServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSRecordHourlyStatService{}).(*nameservers.NSRecordHourlyStatService)
pb.RegisterNSRecordHourlyStatServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSQuestionOptionService{}).(*nameservers.NSQuestionOptionService)
pb.RegisterNSQuestionOptionServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSPlanService{}).(*nameservers.NSPlanService)
pb.RegisterNSPlanServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSUserPlanService{}).(*nameservers.NSUserPlanService)
pb.RegisterNSUserPlanServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&nameservers.NSService{}).(*nameservers.NSService)
pb.RegisterNSServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.AuthorityKeyService{}).(*services.AuthorityKeyService)
pb.RegisterAuthorityKeyServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&reporters.ReportNodeService{}).(*reporters.ReportNodeService)
pb.RegisterReportNodeServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&reporters.ReportNodeGroupService{}).(*reporters.ReportNodeGroupService)
pb.RegisterReportNodeGroupServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&reporters.ReportResultService{}).(*reporters.ReportResultService)
pb.RegisterReportResultServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.UserNodeService{}).(*services.UserNodeService)
pb.RegisterUserNodeServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&accounts.UserAccountService{}).(*accounts.UserAccountService)
pb.RegisterUserAccountServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&accounts.UserAccountLogService{}).(*accounts.UserAccountLogService)
pb.RegisterUserAccountLogServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&accounts.UserAccountDailyStatService{}).(*accounts.UserAccountDailyStatService)
pb.RegisterUserAccountDailyStatServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&accounts.UserOrderService{}).(*accounts.UserOrderService)
pb.RegisterUserOrderServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&accounts.OrderMethodService{}).(*accounts.OrderMethodService)
pb.RegisterOrderMethodServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.ScriptService{}).(*services.ScriptService)
pb.RegisterScriptServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.UserScriptService{}).(*services.UserScriptService)
pb.RegisterUserScriptServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&tickets.UserTicketService{}).(*tickets.UserTicketService)
pb.RegisterUserTicketServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&tickets.UserTicketCategoryService{}).(*tickets.UserTicketCategoryService)
pb.RegisterUserTicketCategoryServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&tickets.UserTicketLogService{}).(*tickets.UserTicketLogService)
pb.RegisterUserTicketLogServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.HTTPAccessLogPolicyService{}).(*services.HTTPAccessLogPolicyService)
pb.RegisterHTTPAccessLogPolicyServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.UserBillService{}).(*services.UserBillService)
pb.RegisterUserBillServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.ServerBillService{}).(*services.ServerBillService)
pb.RegisterServerBillServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.PriceService{}).(*services.PriceService)
pb.RegisterPriceServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.NodePriceItemService{}).(*services.NodePriceItemService)
pb.RegisterNodePriceItemServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.UserTrafficBillService{}).(*services.UserTrafficBillService)
pb.RegisterUserTrafficBillServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&trafficpackages.TrafficPackageService{}).(*trafficpackages.TrafficPackageService)
pb.RegisterTrafficPackageServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&trafficpackages.TrafficPackagePeriodService{}).(*trafficpackages.TrafficPackagePeriodService)
pb.RegisterTrafficPackagePeriodServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&trafficpackages.TrafficPackagePriceService{}).(*trafficpackages.TrafficPackagePriceService)
pb.RegisterTrafficPackagePriceServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&trafficpackages.UserTrafficPackageService{}).(*trafficpackages.UserTrafficPackageService)
pb.RegisterUserTrafficPackageServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&users.UserEmailVerificationService{}).(*users.UserEmailVerificationService)
pb.RegisterUserEmailVerificationServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&users.UserMobileVerificationService{}).(*users.UserMobileVerificationService)
pb.RegisterUserMobileVerificationServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&users.UserVerifyCodeService{}).(*users.UserVerifyCodeService)
pb.RegisterUserVerifyCodeServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.MessageMediaService{}).(*services.MessageMediaService)
pb.RegisterMessageMediaServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&antiddos.ADNetworkService{}).(*antiddos.ADNetworkService)
pb.RegisterADNetworkServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&antiddos.ADPackageService{}).(*antiddos.ADPackageService)
pb.RegisterADPackageServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&antiddos.ADPackageInstanceService{}).(*antiddos.ADPackageInstanceService)
pb.RegisterADPackageInstanceServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&antiddos.ADPackagePeriodService{}).(*antiddos.ADPackagePeriodService)
pb.RegisterADPackagePeriodServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&antiddos.ADPackagePriceService{}).(*antiddos.ADPackagePriceService)
pb.RegisterADPackagePriceServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&antiddos.UserADInstanceService{}).(*antiddos.UserADInstanceService)
pb.RegisterUserADInstanceServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.NodeActionService{}).(*services.NodeActionService)
pb.RegisterNodeActionServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.MessageRecipientService{}).(*services.MessageRecipientService)
pb.RegisterMessageRecipientServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.MessageReceiverService{}).(*services.MessageReceiverService)
pb.RegisterMessageReceiverServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.MessageRecipientGroupService{}).(*services.MessageRecipientGroupService)
pb.RegisterMessageRecipientGroupServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.MessageMediaInstanceService{}).(*services.MessageMediaInstanceService)
pb.RegisterMessageMediaInstanceServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.MessageTaskService{}).(*services.MessageTaskService)
pb.RegisterMessageTaskServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.MessageTaskLogService{}).(*services.MessageTaskLogService)
pb.RegisterMessageTaskLogServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&services.SMSSenderService{}).(*services.SMSSenderService)
pb.RegisterSMSSenderServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&posts.PostService{}).(*posts.PostService)
pb.RegisterPostServiceServer(server, instance)
node.rest(instance)
}
{
var instance = node.serviceInstance(&posts.PostCategoryService{}).(*posts.PostCategoryService)
pb.RegisterPostCategoryServiceServer(server, instance)
node.rest(instance)
}
}

View File

@@ -0,0 +1,187 @@
package nodes
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/events"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/iwind/TeaGo/lists"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"os"
"runtime"
"strings"
"time"
)
type NodeStatusExecutor struct {
isFirstTime bool
cpuUpdatedTime time.Time
cpuLogicalCount int
cpuPhysicalCount int
}
func NewNodeStatusExecutor() *NodeStatusExecutor {
return &NodeStatusExecutor{}
}
func (this *NodeStatusExecutor) Listen() {
this.isFirstTime = true
this.cpuUpdatedTime = time.Now()
this.update()
// TODO 这个时间间隔可以配置
ticker := time.NewTicker(30 * time.Second)
events.On(events.EventQuit, func() {
remotelogs.Println("NODE_STATUS", "quit executor")
ticker.Stop()
})
for range ticker.C {
this.isFirstTime = false
this.update()
}
}
func (this *NodeStatusExecutor) update() {
if sharedAPIConfig == nil {
return
}
status := &nodeconfigs.NodeStatus{}
status.BuildVersion = teaconst.Version
status.BuildVersionCode = utils.VersionToLong(teaconst.Version)
status.OS = runtime.GOOS
status.Arch = runtime.GOARCH
exe, _ := os.Executable()
status.ExePath = exe
status.ConfigVersion = 0
status.IsActive = true
status.ConnectionCount = 0 // TODO 实现连接数计算
hostname, _ := os.Hostname()
status.Hostname = hostname
this.updateCPU(status)
this.updateMem(status)
this.updateLoad(status)
this.updateDisk(status)
status.UpdatedAt = time.Now().Unix()
// 发送数据
jsonData, err := json.Marshal(status)
if err != nil {
remotelogs.Error("NODE_STATUS", "serial NodeStatus fail: "+err.Error())
return
}
err = models.SharedAPINodeDAO.UpdateAPINodeStatus(nil, sharedAPIConfig.NumberId(), jsonData)
if err != nil {
remotelogs.Error("NODE_STATUS", "rpc UpdateNodeStatus() failed: "+err.Error())
return
}
}
// 更新CPU
func (this *NodeStatusExecutor) updateCPU(status *nodeconfigs.NodeStatus) {
duration := time.Duration(0)
if this.isFirstTime {
duration = 100 * time.Millisecond
}
percents, err := cpu.Percent(duration, false)
if err != nil {
status.Error = "cpu.Percent(): " + err.Error()
return
}
if len(percents) == 0 {
return
}
status.CPUUsage = percents[0] / 100
if time.Since(this.cpuUpdatedTime) > 300*time.Second { // 每隔5分钟才会更新一次
this.cpuUpdatedTime = time.Now()
status.CPULogicalCount, err = cpu.Counts(true)
if err != nil {
status.Error = "cpu.Counts(): " + err.Error()
return
}
status.CPUPhysicalCount, err = cpu.Counts(false)
if err != nil {
status.Error = "cpu.Counts(): " + err.Error()
return
}
this.cpuLogicalCount = status.CPULogicalCount
this.cpuPhysicalCount = status.CPUPhysicalCount
} else {
status.CPULogicalCount = this.cpuLogicalCount
status.CPUPhysicalCount = this.cpuPhysicalCount
}
}
// 更新硬盘
func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
partitions, err := disk.Partitions(false)
if err != nil {
remotelogs.Error("NODE_STATUS", err.Error())
return
}
lists.Sort(partitions, func(i int, j int) bool {
p1 := partitions[i]
p2 := partitions[j]
return p1.Mountpoint > p2.Mountpoint
})
// 当前TeaWeb所在的fs
var rootFS = ""
var rootTotal = uint64(0)
if lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
for _, p := range partitions {
if p.Mountpoint == "/" {
rootFS = p.Fstype
usage, _ := disk.Usage(p.Mountpoint)
if usage != nil {
rootTotal = usage.Total
}
break
}
}
}
var total = rootTotal
var totalUsage = uint64(0)
var maxUsage = float64(0)
for _, partition := range partitions {
if runtime.GOOS != "windows" && !strings.Contains(partition.Device, "/") && !strings.Contains(partition.Device, "\\") {
continue
}
// 跳过不同fs的
if len(rootFS) > 0 && rootFS != partition.Fstype {
continue
}
usage, err := disk.Usage(partition.Mountpoint)
if err != nil {
continue
}
if partition.Mountpoint != "/" && (usage.Total != rootTotal || total == 0) {
total += usage.Total
}
totalUsage += usage.Used
if usage.UsedPercent >= maxUsage {
maxUsage = usage.UsedPercent
status.DiskMaxUsagePartition = partition.Mountpoint
}
}
status.DiskTotal = total
if total > 0 {
status.DiskUsage = float64(totalUsage) / float64(total)
}
status.DiskMaxUsage = maxUsage / 100
}

View File

@@ -0,0 +1,27 @@
package nodes
import (
"github.com/shirou/gopsutil/v3/cpu"
"testing"
"time"
)
func TestNodeStatusExecutor_CPU(t *testing.T) {
countLogicCPU, err := cpu.Counts(true)
if err != nil {
t.Fatal(err)
}
t.Log("logic count:", countLogicCPU)
countPhysicalCPU, err := cpu.Counts(false)
if err != nil {
t.Fatal(err)
}
t.Log("physical count:", countPhysicalCPU)
percents, err := cpu.Percent(100*time.Millisecond, false)
if err != nil {
t.Fatal(err)
}
t.Log(percents)
}

View File

@@ -0,0 +1,56 @@
//go:build !windows
// +build !windows
package nodes
import (
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"runtime"
"runtime/debug"
)
// 更新内存
func (this *NodeStatusExecutor) updateMem(status *nodeconfigs.NodeStatus) {
stat, err := mem.VirtualMemory()
if err != nil {
return
}
// 重新计算内存
if stat.Total > 0 {
stat.Used = stat.Total - stat.Free - stat.Buffers - stat.Cached
status.MemoryUsage = float64(stat.Used) / float64(stat.Total)
}
status.MemoryTotal = stat.Total
// 内存严重不足时自动释放内存
if stat.Total > 0 {
var minFreeMemory = stat.Total / 8
if minFreeMemory > 1<<30 {
minFreeMemory = 1 << 30
}
if stat.Available > 0 && stat.Available < minFreeMemory {
runtime.GC()
debug.FreeOSMemory()
}
}
}
// 更新负载
func (this *NodeStatusExecutor) updateLoad(status *nodeconfigs.NodeStatus) {
stat, err := load.Avg()
if err != nil {
status.Error = err.Error()
return
}
if stat == nil {
status.Error = "load is nil"
return
}
status.Load1m = stat.Load1
status.Load5m = stat.Load5
status.Load15m = stat.Load15
}

View File

@@ -0,0 +1,103 @@
//go:build windows
// +build windows
package nodes
import (
//"context"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
//"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/mem"
"math"
"sync"
"time"
)
type WindowsLoadValue struct {
Timestamp int64
Value int
}
var windowsLoadValues = []*WindowsLoadValue{}
var windowsLoadLocker = &sync.Mutex{}
// 更新内存
func (this *NodeStatusExecutor) updateMem(status *nodeconfigs.NodeStatus) {
stat, err := mem.VirtualMemory()
if err != nil {
status.Error = err.Error()
return
}
status.MemoryUsage = stat.UsedPercent
status.MemoryTotal = stat.Total
}
// 更新负载
func (this *NodeStatusExecutor) updateLoad(status *nodeconfigs.NodeStatus) {
timestamp := time.Now().Unix()
currentLoad := 0
//info, err := cpu.ProcInfo()
//if err == nil && len(info) > 0 && info[0].ProcessorQueueLength < 1000 {
// currentLoad = int(info[0].ProcessorQueueLength)
//}
// 删除15分钟之前的数据
windowsLoadLocker.Lock()
result := []*WindowsLoadValue{}
for _, v := range windowsLoadValues {
if timestamp-v.Timestamp > 15*60 {
continue
}
result = append(result, v)
}
result = append(result, &WindowsLoadValue{
Timestamp: timestamp,
Value: currentLoad,
})
windowsLoadValues = result
total1 := 0
count1 := 0
total5 := 0
count5 := 0
total15 := 0
count15 := 0
for _, v := range result {
if timestamp-v.Timestamp <= 60 {
total1 += v.Value
count1++
}
if timestamp-v.Timestamp <= 300 {
total5 += v.Value
count5++
}
total15 += v.Value
count15++
}
load1 := float64(0)
load5 := float64(0)
load15 := float64(0)
if count1 > 0 {
load1 = math.Round(float64(total1*100)/float64(count1)) / 100
}
if count5 > 0 {
load5 = math.Round(float64(total5*100)/float64(count5)) / 100
}
if count15 > 0 {
load15 = math.Round(float64(total15*100)/float64(count15)) / 100
}
windowsLoadLocker.Unlock()
// 在老Windows上不显示错误
//if err == context.DeadlineExceeded {
// err = nil
//}
status.Load1m = load1
status.Load5m = load5
status.Load15m = load15
}

View File

@@ -0,0 +1,282 @@
package nodes
import (
"context"
"crypto/tls"
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/sizes"
"github.com/iwind/TeaGo/maps"
"io"
"net"
"net/http"
"reflect"
"regexp"
"strings"
"time"
)
var servicePathReg = regexp.MustCompile(`^/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)$`)
var restServicesMap = map[string]reflect.Value{
"APIAccessTokenService": reflect.ValueOf(new(services.APIAccessTokenService)),
"ServerService": reflect.ValueOf(new(services.ServerService)),
}
type RestServer struct{}
func (this *RestServer) Listen(listener net.Listener) error {
var mux = http.NewServeMux()
mux.HandleFunc("/", this.handle)
var server = &http.Server{}
server.Handler = mux
return server.Serve(listener)
}
func (this *RestServer) ListenHTTPS(listener net.Listener, tlsConfig *tls.Config) error {
var mux = http.NewServeMux()
mux.HandleFunc("/", this.handle)
server := &http.Server{}
server.Handler = mux
server.TLSConfig = tlsConfig
return server.ServeTLS(listener, "", "")
}
func (this *RestServer) handle(writer http.ResponseWriter, req *http.Request) {
var path = req.URL.Path
// 是否显示Pretty后的JSON
var shouldPretty = req.Header.Get("X-Edge-Response-Pretty") == "on"
// 兼容老的Header
var oldShouldPretty = req.Header.Get("Edge-Response-Pretty")
if len(oldShouldPretty) > 0 {
shouldPretty = oldShouldPretty == "on"
}
// 欢迎页
if path == "/" {
this.writeJSON(writer, maps.Map{
"code": 200,
"message": "Welcome to API",
"data": maps.Map{},
}, shouldPretty)
return
}
var matches = servicePathReg.FindStringSubmatch(path)
if len(matches) != 3 {
writer.WriteHeader(http.StatusNotFound)
this.writeJSON(writer, maps.Map{
"code": "404",
"message": "invalid api path '" + path + "'",
"data": maps.Map{},
}, shouldPretty)
return
}
var serviceName = matches[1]
var methodName = matches[2]
serviceType, ok := restServicesMap[serviceName]
if !ok {
writer.WriteHeader(http.StatusNotFound)
this.writeJSON(writer, maps.Map{
"code": "404",
"message": "service '" + serviceName + "' not found",
"data": maps.Map{},
}, shouldPretty)
return
}
if len(methodName) == 0 {
writer.WriteHeader(http.StatusNotFound)
this.writeJSON(writer, maps.Map{
"code": "404",
"message": "method '" + methodName + "' not found",
"data": maps.Map{},
}, shouldPretty)
return
}
// 再次查找
methodName = strings.ToUpper(string(methodName[0])) + methodName[1:]
var method = serviceType.MethodByName(methodName)
if !method.IsValid() {
// 兼容Enabled
if strings.Contains(methodName, "Enabled") {
methodName = strings.Replace(methodName, "Enabled", "", 1)
method = serviceType.MethodByName(methodName)
if !method.IsValid() {
writer.WriteHeader(http.StatusNotFound)
this.writeJSON(writer, maps.Map{
"code": "404",
"message": "method '" + methodName + "' not found",
"data": maps.Map{},
}, shouldPretty)
return
}
} else {
writer.WriteHeader(http.StatusNotFound)
this.writeJSON(writer, maps.Map{
"code": "404",
"message": "method '" + methodName + "' not found",
"data": maps.Map{},
}, shouldPretty)
return
}
}
if method.Type().NumIn() != 2 || method.Type().NumOut() != 2 {
writer.WriteHeader(http.StatusNotFound)
this.writeJSON(writer, maps.Map{
"code": "404",
"message": "method '" + methodName + "' not found",
"data": maps.Map{},
}, shouldPretty)
return
}
if method.Type().In(0).Name() != "Context" {
writer.WriteHeader(http.StatusNotFound)
this.writeJSON(writer, maps.Map{
"code": "404",
"message": "method '" + methodName + "' not found (or invalid context)",
"data": maps.Map{},
}, shouldPretty)
return
}
// 上下文
var ctx = context.Background()
if serviceName != "APIAccessTokenService" || (methodName != "GetAPIAccessToken" && methodName != "getAPIAccessToken") {
// 校验TOKEN
var token = req.Header.Get("X-Edge-Access-Token")
if len(token) == 0 {
token = req.Header.Get("Edge-Access-Token")
if len(token) == 0 {
this.writeJSON(writer, maps.Map{
"code": 400,
"data": maps.Map{},
"message": "require 'X-Edge-Access-Token' header",
}, shouldPretty)
return
}
}
accessToken, err := models.SharedAPIAccessTokenDAO.FindAccessToken(nil, token)
if err != nil {
this.writeJSON(writer, maps.Map{
"code": 400,
"data": maps.Map{},
"message": "server error: " + err.Error(),
}, shouldPretty)
return
}
if accessToken == nil || int64(accessToken.ExpiredAt) < time.Now().Unix() {
this.writeJSON(writer, maps.Map{
"code": 400,
"data": maps.Map{},
"message": "invalid access token",
}, shouldPretty)
return
}
if accessToken.UserId > 0 {
ctx = rpcutils.NewPlainContext("user", int64(accessToken.UserId))
} else if accessToken.AdminId > 0 {
ctx = rpcutils.NewPlainContext("admin", int64(accessToken.AdminId))
} else {
// TODO 支持更多类型的角色
this.writeJSON(writer, maps.Map{
"code": 400,
"data": maps.Map{},
"message": "not supported role",
}, shouldPretty)
return
}
}
// TODO 可以设置最大可接收内容尺寸
body, err := io.ReadAll(io.LimitReader(req.Body, 32*sizes.M))
if err != nil {
writer.WriteHeader(http.StatusBadRequest)
this.writeJSON(writer, maps.Map{
"code": 400,
"message": err.Error(),
"data": maps.Map{},
}, shouldPretty)
return
}
// 如果为空,表示传的数据为空
if len(body) == 0 {
body = []byte("{}")
}
// 请求数据
var reqValue = reflect.New(method.Type().In(1).Elem()).Interface()
err = json.Unmarshal(body, reqValue)
if err != nil {
writer.WriteHeader(http.StatusBadRequest)
this.writeJSON(writer, maps.Map{
"code": 400,
"message": "Decode request failed: " + err.Error() + ". Request body should be a valid JSON data",
"data": maps.Map{},
}, shouldPretty)
return
}
var result = method.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(reqValue)})
var resultErr = result[1].Interface()
if resultErr != nil {
e, ok := resultErr.(error)
if ok {
this.writeJSON(writer, maps.Map{
"code": 400,
"message": e.Error(),
"data": maps.Map{},
}, shouldPretty)
} else {
this.writeJSON(writer, maps.Map{
"code": 500,
"message": "server error: server should return a error object, but return a " + result[1].Type().String(),
"data": maps.Map{},
}, shouldPretty)
}
} else { // 没有返回错误
var data = maps.Map{
"code": 200,
"message": "ok",
"data": result[0].Interface(),
}
var dataJSON []byte
if shouldPretty {
dataJSON = data.AsPrettyJSON()
} else {
dataJSON = data.AsJSON()
}
if err != nil {
this.writeJSON(writer, maps.Map{
"code": 500,
"message": "server error: marshal json failed: " + err.Error(),
"data": maps.Map{},
}, shouldPretty)
} else {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
_, _ = writer.Write(dataJSON)
}
}
}
func (this *RestServer) writeJSON(writer http.ResponseWriter, v maps.Map, pretty bool) {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
if pretty {
_, _ = writer.Write(v.AsPrettyJSON())
} else {
_, _ = writer.Write(v.AsJSON())
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package nodes
type StartIssue struct {
Code string `json:"code"`
Message string `json:"message"`
Suggestion string `json:"suggestion"`
}
func NewStartIssue(code string, message string, suggestion string) *StartIssue {
return &StartIssue{
Code: code,
Message: message,
Suggestion: suggestion,
}
}