yanghui
2021-06-28 b568d251606f82968b8103c485a0d70027c1d57e
initial version
14个文件已添加
1164 ■■■■■ 已修改文件
api/api-def.go 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/config.go 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/static/docker-compose.yml 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/static/env 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
controller/node-manager.go 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
go.mod 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
go.sum 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.go 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/bee-node.go 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/db.go 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
monitor/status_sync.go 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
routers/routers.go 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/node-service.go 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/cmd.go 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/api-def.go
New file
@@ -0,0 +1,51 @@
package api
import (
    "basic.com/valib/logger.git"
    "bee-config/controller"
    "bee-config/routers"
    "github.com/gin-gonic/gin"
    "net/http"
    "sync"
)
var (
    routines []routers.IRouter = []routers.IRouter{}
)
func init() {
    routines = append(routines, controller.AddNode, controller.QueryNode)
}
func InitRouter(wg *sync.WaitGroup) *http.Server {
    r := gin.Default()
    for _, v := range routines {
        methods := v.GetMethods()
        if len(methods) > 0 && len(methods) > 0 {
            for _, m := range methods {
                r.Handle(m, v.GetPath(), v.GetHandles()...)
            }
        }
    }
    server := &http.Server{
        Addr:              ":8080",
        Handler:           r,
    }
    wg.Add(1)
    go func() {
        if err := server.ListenAndServe(); nil != err {
            if err != http.ErrServerClosed {
                logger.Fatal("InitRouter failed with error:", err)
            } else {
                logger.Info("InitRouter exited by user")
            }
        }
        wg.Done()
    }()
    return server
}
config/config.go
New file
@@ -0,0 +1,195 @@
package config
import (
    "basic.com/valib/logger.git"
    "bytes"
    "embed"
    "fmt"
    "gopkg.in/ini.v1"
    "io"
    "io/ioutil"
    "os"
    "path"
    "path/filepath"
    "strconv"
)
var (
    baseDir        string
    configFile     string
    baseApiPort    uint16
    baseP2pPort    uint16
    baseDebugPort  uint16
)
const (
    defaultRootDir = "/var/lib/bee"
    nodePrefix = "node"
    defaultPortStep  uint16    = 10
    defaultApiPort   uint16    = 1633
    defaultP2pPort   uint16    = 1634
    defaultDebugPort uint16    = 1635
)
//go:embed static
var local embed.FS
func init() {
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
    if nil != err {
        os.Exit(1)
    }
    configFile = path.Join(dir, "config.ini")
    cfg, err := ini.LooseLoad(configFile)
    if nil != err {
        logger.Error("failed to load ini file, err:", err)
        os.Exit(2)
    }
    baseDir := cfg.Section("data").Key("dir").MustString(defaultRootDir);
    err = os.MkdirAll(baseDir, os.ModePerm)
    if nil != err {
        logger.Error("failed to mkdir path:", baseDir, ", err:", err)
        os.Exit(3)
    }
    fmt.Println("base dir:", baseDir)
    baseApiPort = uint16(cfg.Section("port").Key("api").MustUint(uint(defaultApiPort)))
    baseP2pPort = uint16(cfg.Section("port").Key("p2p").MustUint(uint(defaultP2pPort)))
    baseDebugPort = uint16(cfg.Section("port").Key("debug").MustUint(uint(defaultDebugPort)))
}
func GetNodeDir(id uint8) string {
    dir := fmt.Sprintf("%s%d", nodePrefix, id)
    return path.Join(baseDir, dir)
}
func GetApiPort(id uint8) uint16 {
    port := baseApiPort + (uint16(id) - 1) * defaultPortStep
    return port
}
func GetP2pPort(id uint8) uint16 {
    port := baseP2pPort + (uint16(id) - 1) * defaultPortStep
    return port
}
func GetDebugPort(id uint8) uint16 {
    port := baseDebugPort + (uint16(id) - 1) * defaultPortStep
    return port
}
func GenerateDockerFile(dir string, id uint8) error {
    dockerFile := path.Join(dir, "docker-compose.yml")
    err := copyResource(dockerFile, "static/docker-compose.yml", "")
    if nil != err {
        logger.Error("copyResource failed to read static resource, err:", err)
        return err
    }
    multipleLine := generateEnvVariable(id)
    envFile := path.Join(dir, ".env")
    err = copyResource(envFile, "static/env", multipleLine)
    return nil
}
func generateEnvVariable(id uint8) string {
    var b bytes.Buffer
    apiPort := strconv.Itoa(int(GetApiPort(id)))
    p2pPort := strconv.Itoa(int(GetP2pPort(id)))
    debugPort := strconv.Itoa(int(GetDebugPort(id)))
    b.WriteString("\r\n")
    b.WriteString("\r\n")
    b.WriteString(`## HTTP API listen "address" (default :1633)`)
    b.WriteString("\r\n")
    b.WriteString(` BEE_API_ADDR=:`)
    b.WriteString(apiPort)
    b.WriteString("\r\n")
    b.WriteString("\r\n")
    b.WriteString(`## P2P listen address (default :1634)`)
    b.WriteString("\r\n")
    b.WriteString(` BEE_P2P_ADDR=:`)
    b.WriteString(p2pPort)
    b.WriteString("\r\n")
    b.WriteString("\r\n")
    b.WriteString(`## debug HTTP API listen address (default :1635)`)
    b.WriteString("\r\n")
    b.WriteString(` BEE_DEBUG_API_ADDR=:`)
    b.WriteString(debugPort)
    b.WriteString("\r\n")
    b.WriteString("\r\n")
    b.WriteString(` API_ADDR=:`)
    b.WriteString(apiPort)
    b.WriteString("\r\n")
    b.WriteString("\r\n")
    b.WriteString(` P2P_ADDR=:`)
    b.WriteString(p2pPort)
    b.WriteString("\r\n")
    b.WriteString("\r\n")
    b.WriteString(` DEBUG_API_ADDR=:`)
    b.WriteString(debugPort)
    b.WriteString("\r\n")
    b.WriteString("\r\n")
    return b.String()
}
func copyResource(dest, src string, content string) error {
    in, err := local.Open(src)
    if nil != err {
        logger.Error("copyResource failed to read static resource, err:", err)
        return err
    }
    defer in.Close()
    out, err := os.Create(dest)
    if nil != err {
        logger.Error("copyResource failed to create local copy, err:", err)
        return err
    }
    defer out.Close()
    if len(content) > 0 {
        bytes, err := ioutil.ReadAll(in)
        if nil != err {
            logger.Error("copyResource failed to read from source file, err:", err)
            return err
        }
        _, err = out.WriteString(string(bytes))
        if nil != err {
            logger.Error("copyResource failed to write , err:", err)
            return err
        }
        _, err = out.WriteString(content)
        if nil != err {
            logger.Error("copyResource failed to read from source file, err:", err)
            return err
        }
    } else {
        _, err = io.Copy(out, in)
        if nil != err {
            logger.Error("copyResource failed to copy file, err:", err)
            return err
        }
    }
    return nil
}
config/static/docker-compose.yml
New file
@@ -0,0 +1,69 @@
version: "3"
services:
  clef-1:
    image: ethersphere/clef:0.4.12
    restart: unless-stopped
    environment:
      - CLEF_CHAINID
    volumes:
      - clef-1:/app/data
    command: full
  bee-1:
    image: ethersphere/bee:beta
    restart: unless-stopped
    environment:
      - BEE_API_ADDR
      - BEE_BOOTNODE
      - BEE_BOOTNODE_MODE
      - BEE_CLEF_SIGNER_ENABLE
      - BEE_CLEF_SIGNER_ENDPOINT=http://clef-1:8550
      - BEE_CONFIG
      - BEE_CORS_ALLOWED_ORIGINS
      - BEE_DATA_DIR
      - BEE_CACHE_CAPACITY
      - BEE_DB_OPEN_FILES_LIMIT
      - BEE_DB_BLOCK_CACHE_CAPACITY
      - BEE_DB_WRITE_BUFFER_SIZE
      - BEE_DB_DISABLE_SEEKS_COMPACTION
      - BEE_DEBUG_API_ADDR
      - BEE_DEBUG_API_ENABLE
      - BEE_GATEWAY_MODE
      - BEE_GLOBAL_PINNING_ENABLE
      - BEE_NAT_ADDR
      - BEE_NETWORK_ID
      - BEE_P2P_ADDR
      - BEE_P2P_QUIC_ENABLE
      - BEE_P2P_WS_ENABLE
      - BEE_PASSWORD
      - BEE_PASSWORD_FILE
      - BEE_PAYMENT_EARLY
      - BEE_PAYMENT_THRESHOLD
      - BEE_PAYMENT_TOLERANCE
      - BEE_RESOLVER_OPTIONS
      - BEE_STANDALONE
      - BEE_SWAP_ENABLE
      - BEE_SWAP_ENDPOINT
      - BEE_SWAP_FACTORY_ADDRESS
      - BEE_SWAP_INITIAL_DEPOSIT
      - BEE_TRACING_ENABLE
      - BEE_TRACING_ENDPOINT
      - BEE_TRACING_SERVICE_NAME
      - BEE_VERBOSITY
      - BEE_WELCOME_MESSAGE
      - BEE_FULL_NODE
    ports:
      - "${API_ADDR:-1833}${BEE_API_ADDR:-:1833}"
      - "${P2P_ADDR:-1834}${BEE_P2P_ADDR:-:1834}"
      - "${DEBUG_API_ADDR:-127.0.0.1:1835}${BEE_DEBUG_API_ADDR:-:1835}"
    volumes:
      - bee-1:/home/bee/.bee
    command: start
    depends_on:
      - clef-1
volumes:
  clef-1:
  bee-1:
config/static/env
New file
@@ -0,0 +1,98 @@
# Copy this file to .env, then update it with your own settings
### CLEF
## chain id to use for signing (1=mainnet, 3=ropsten, 4=rinkeby, 5=goerli) (default: 12345)
CLEF_CHAINID=5
### BEE
## HTTP API listen address (default :1633)
# BEE_API_ADDR=:1633
## chain block time (default 15)
# BEE_BLOCK_TIME=15
## initial nodes to connect to (default [/dnsaddr/bootnode.ethswarm.org])
# BEE_BOOTNODE=[/dnsaddr/bootnode.ethswarm.org]
## cause the node to always accept incoming connections
# BEE_BOOTNODE_MODE=false
## enable clef signer
BEE_CLEF_SIGNER_ENABLE=true
## clef signer endpoint
# BEE_CLEF_SIGNER_ENDPOINT=
## config file (default is /home/<user>/.bee.yaml)
# BEE_CONFIG=/home/bee/.bee.yaml
## origins with CORS headers enabled
# BEE_CORS_ALLOWED_ORIGINS=[]
## data directory (default /home/<user>/.bee)
# BEE_DATA_DIR=/home/bee/.bee
## cache capacity in chunks, multiply by 4096 to get approximate capacity in bytes
# BEE_CACHE_CAPACITY=1000000
## number of open files allowed by database
# BEE_DB_OPEN_FILES_LIMIT=200
## size of block cache of the database in bytes
# BEE_DB_BLOCK_CACHE_CAPACITY=33554432
## size of the database write buffer in bytes
# BEE_DB_WRITE_BUFFER_SIZE=33554432
## disables db compactions triggered by seeks
# BEE_DB_DISABLE_SEEKS_COMPACTION=false
## debug HTTP API listen address (default :1635)
 BEE_DEBUG_API_ADDR=:1635
## enable debug HTTP API
 BEE_DEBUG_API_ENABLE=true
## disable a set of sensitive features in the api
# BEE_GATEWAY_MODE=false
## enable global pinning
# BEE_GLOBAL_PINNING_ENABLE=false
## cause the node to start in full mode
 BEE_FULL_NODE=true
## NAT exposed address
# BEE_NAT_ADDR=
## ID of the Swarm network (default 1)
# BEE_NETWORK_ID=1
## P2P listen address (default :1634)
# BEE_P2P_ADDR=:1634
## enable P2P QUIC protocol
# BEE_P2P_QUIC_ENABLE=false
## enable P2P WebSocket transport
# BEE_P2P_WS_ENABLE=false
## password for decrypting keys
 BEE_PASSWORD=123
## path to a file that contains password for decrypting keys
# BEE_PASSWORD_FILE=
## amount in BZZ below the peers payment threshold when we initiate settlement (default 1000000000000)
# BEE_PAYMENT_EARLY=1000000000000
## threshold in BZZ where you expect to get paid from your peers (default 10000000000000)
# BEE_PAYMENT_THRESHOLD=10000000000000
## excess debt above payment threshold in BZZ where you disconnect from your peer (default 10000000000000)
# BEE_PAYMENT_TOLERANCE=10000000000000
## postage stamp contract address
# BEE_POSTAGE_STAMP_ADDRESS=
## ENS compatible API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url
# BEE_RESOLVER_OPTIONS=[]
## whether we want the node to start with no listen addresses for p2p
# BEE_STANDALONE=false
## enable swap (default true)
# BEE_SWAP_ENABLE=true
## swap ethereum blockchain endpoint (default ws://localhost:8546)
 BEE_SWAP_ENDPOINT=wss://goerli.infura.io/ws/v3/a2ff57de9e7f43d4a06dd3328bb4ecf6
## swap factory address
# BEE_SWAP_FACTORY_ADDRESS=
## legacy swap factory addresses
# BEE_SWAP_LEGACY_FACTORY_ADDRESSES=
## initial deposit if deploying a new chequebook (default 10000000000000000)
# BEE_SWAP_INITIAL_DEPOSIT=10000000000000000
## gas price in wei to use for deployment and funding (default "")
# BEE_SWAP_DEPLOYMENT_GAS_PRICE=
## enable tracing
# BEE_TRACING_ENABLE=false
## endpoint to send tracing data (default 127.0.0.1:6831)
# BEE_TRACING_ENDPOINT=127.0.0.1:6831
## service name identifier for tracing (default bee)
# BEE_TRACING_SERVICE_NAME=bee
## proof-of-identity transaction hash
# BEE_TRANSACTION=
## log verbosity level 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=trace (default info)
# BEE_VERBOSITY=info
## send a welcome message string during handshakes
 BEE_WELCOME_MESSAGE=Hello,BZZ
controller/node-manager.go
New file
@@ -0,0 +1,87 @@
package controller
import (
    "bee-config/models"
    "bee-config/routers"
    "bee-config/service"
    "github.com/gin-gonic/gin"
    "net/http"
    "path"
)
type (
    NodeRouter struct {
        routers.Router
    }
)
const (
    basePath = "/nodes"
)
var (
    AddNode *NodeRouter
    QueryNode *NodeRouter
)
func init() {
    AddNode = &NodeRouter{
        routers.Router{
            Methods: []string{http.MethodGet, http.MethodPost},
            Path:    "add",
            Handles: []gin.HandlerFunc{addNode},
        },
    }
    QueryNode = &NodeRouter{
        routers.Router{
            Methods: []string{http.MethodGet},
            Path:    "query",
            Handles: []gin.HandlerFunc{queryNode},
        },
    }
}
func (r *NodeRouter)GetPath() string {
    return path.Join(basePath, r.Path)
}
func addNode(c *gin.Context) {
    address, err := service.AddNode()
    if nil != err {
        c.JSON(http.StatusOK, gin.H {
            "code": 600,
            "msg": err.Error(),
            "data": &struct {
            }{},
        })
    } else {
        data := map[string]interface{}{}
        data["address"] = address
        c.JSON(http.StatusOK, gin.H {
            "code": http.StatusOK,
            "msg": "ok",
            "data": data,
        })
    }
}
func queryNode(c *gin.Context) {
    var node models.BeeNode
    arr, err := node.FindAll()
    if nil != err {
        c.JSON(http.StatusOK, gin.H {
            "code": 600,
            "msg": err.Error(),
            "data": &struct {
            }{},
        })
    } else {
        c.JSON(http.StatusOK, gin.H {
            "code": http.StatusOK,
            "msg": "ok",
            "data": arr,
        })
    }
}
go.mod
New file
@@ -0,0 +1,16 @@
module bee-config
go 1.16
require (
    basic.com/valib/logger.git v0.0.0-20201221081153-494dab566ec8
    github.com/BurntSushi/toml v0.3.1 // indirect
    github.com/gin-gonic/gin v1.7.2
    github.com/jinzhu/gorm v1.9.16
    github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
    github.com/smartystreets/goconvey v1.6.4 // indirect
    go.uber.org/zap v1.17.0 // indirect
    golang.org/x/tools v0.1.2
    gopkg.in/ini.v1 v1.62.0
    gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
)
go.sum
New file
@@ -0,0 +1,130 @@
basic.com/valib/logger.git v0.0.0-20201221081153-494dab566ec8 h1:tM91RTIBqt+PoMEVC4ojUNpp1DB+558DLHj5GbhIck0=
basic.com/valib/logger.git v0.0.0-20201221081153-494dab566ec8/go.mod h1:SPlOGUUlxCscwF1dkqmLb0oJXVqg1uJ8hsPXLFxrw1M=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
main.go
New file
@@ -0,0 +1,59 @@
package main
import (
    "basic.com/valib/logger.git"
    "bee-config/api"
    "bee-config/models"
    "bee-config/monitor"
    "context"
    "flag"
    "os"
    "os/signal"
    "path"
    "path/filepath"
    "sync"
    "syscall"
)
func init()  {
    flag.Parse()
    executable := filepath.Base(os.Args[0])
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
    if nil != err {
        dir = "./"
    }
    dir = path.Join(dir, "logs")
    if _, err := os.Stat(dir); os.IsNotExist(err) {
        _ = os.MkdirAll(dir, os.ModePerm)
    }
    var logFile = path.Join(dir, executable + ".log")
    logger.InitLogger(logFile, logger.InfoLevel, 128, 10, 3)
    logger.Info("log init success !")
}
func main() {
    models.Init()
    defer models.CloseDB()
    wg := &sync.WaitGroup{}
    q := make(chan os.Signal, 1)
    signal.Notify(q, os.Interrupt, os.Kill, syscall.SIGTERM)
    ctx, cancel := context.WithCancel(context.Background())
    monitor.Init(ctx, wg)
    server := api.InitRouter(wg)
    <-q
    logger.Info("user signalled...")
    // shutdown http service gracefully
    if err := server.Shutdown(ctx); nil != err {
        logger.Fatal("server shutdown failed, err:", err)
    }
    cancel()
    wg.Wait()
    logger.Info("all sub threads exited...")
}
models/bee-node.go
New file
@@ -0,0 +1,56 @@
package models
import (
    "sync"
)
const (
    RUNNING_Currupted = 0
    RUNNING_Stopped   = 1
    RUNNING_Started   = 2
)
type BeeNode struct {
    Id                 int         `gorm:"column:id;primary_key;unique;not null;autoIncrement" json:"id"`   // 节点id
    Address            string         `gorm:"column:address;not null;default:''" json:"address"`               // 程序目录
    Dir                string         `gorm:"column:dir;not null;default:''" json:"dir"`                       // 程序目录
    ApiPort         uint16      `gorm:"column:api_port" json:"apiPort"`                                  // api port number
    P2PPort         uint16      `gorm:"column:p2p_port" json:"p2pPort"`                                  // p2p port number
    DebugPort       uint16      `gorm:"column:debug_port" json:"debugPort"`                              // debug port number
    Status          uint8       `gorm:"column:status" json:"status"`                                     // status
    Del             uint8       `gorm:"column:del" json:"del"`                                           // del
}
var (
    m  sync.RWMutex
)
func (BeeNode) TableName() string {
    return "t_node"
}
func (a *BeeNode) FindAll() ([]BeeNode, error) {
    var rows []BeeNode
    m.RLock()
    defer m.RUnlock()
    if err := db.Table(a.TableName()).Find(&rows).Error; err != nil {
        return nil, err
    }
    return rows, nil
}
func (a *BeeNode) Save() error {
    m.Lock()
    defer m.Unlock()
    return db.Create(a).Error
}
func (a *BeeNode) Update(status uint8) error {
    m.Lock()
    defer m.Unlock()
    return db.Model(a).Update("Status", status).Error
}
models/db.go
New file
@@ -0,0 +1,31 @@
package models
import (
    "basic.com/valib/logger.git"
    "github.com/jinzhu/gorm"
)
var db *gorm.DB
func Init() {
    var err error
    db, err = gorm.Open("sqlite3", "./version.db")
    if err != nil {
        logger.Error("Db Init failed", err)
        return;
    } else {
        logger.Info("Db Init ok", err)
    }
    db.LogMode(true)
    db.AutoMigrate(&BeeNode{})
}
func GetDB() *gorm.DB {
    return db
}
func CloseDB() {
    _ = db.Close()
}
monitor/status_sync.go
New file
@@ -0,0 +1,103 @@
package monitor
import (
    "basic.com/valib/logger.git"
    "bee-config/config"
    "bee-config/models"
    "bee-config/utils"
    "context"
    "os"
    "strings"
    "sync"
    "time"
)
func Init(ctx context.Context, wg *sync.WaitGroup) {
    wg.Add(1)
    go monitorLoop(ctx, wg)
}
func monitorLoop(ctx context.Context, wg *sync.WaitGroup) {
    t := time.NewTicker(time.Second * 2)
    for {
        select {
        case <-ctx.Done():
            t.Stop()
            wg.Done()
            return
        case <- t.C:
            syncStatus()
        }
    }
}
func syncStatus() {
    var node models.BeeNode
    arr, err := node.FindAll()
    if nil != err {
        logger.Error("syncStatus failed to read all data, err:", err)
        return
    }
    nodes := make([]*models.BeeNode, len(arr))
    for _, v := range arr {
        nodes = append(nodes, &v)
        id := uint8(v.Id)
        dir := config.GetNodeDir(id)
        if _, err := os.Stat(dir); os.IsNotExist(err) {
            continue
        }
        output, err := utils.ShellExecute(dir, "docker-compose", "ps", "-a")
        if nil != err {
            logger.Error("syncStatus failed to invoke docker-compose, err:", err)
            continue
        }
        containers := parseDockerCompose(output)
        if len(containers) < 2 {
            logger.Error("syncStatus no enough containers")
            continue
        }
        var beeStatus, clefStatus string
        for _, v := range containers {
            words := strings.Split(v, " ")
            if len(words) < 5 {
                continue
            }
            if strings.Contains(words[0], "bee-1") {
                beeStatus = words[3]
            }
            if strings.Contains(words[0], "clef-1") {
                clefStatus = words[3]
            }
        }
        if len(beeStatus) < 1 {
            logger.Error("syncStatus failed to find bee container")
            continue
        }
        if len(clefStatus) < 1 {
            logger.Error("syncStatus failed to find clefStatus container")
            continue
        }
    }
}
func parseDockerCompose(output string) []string {
    lines := strings.Split(output, "\n")
    for i, v := range lines {
        if strings.HasPrefix(v, "------") {
            index := i + 1
            return lines[index:]
        }
    }
    return []string{}
}
routers/routers.go
New file
@@ -0,0 +1,31 @@
package routers
import (
    "github.com/gin-gonic/gin"
)
type (
    IRouter interface {
        GetMethods() []string
        GetPath()    string
        GetHandles() gin.HandlersChain
    }
    Router struct {
        Methods    []string
        Path       string
        Handles    gin.HandlersChain
    }
)
func (r *Router)GetMethods() []string  {
    return r.Methods
}
func (r *Router)GetPath() string {
    return r.Path
}
func (r *Router)GetHandles() gin.HandlersChain {
    return r.Handles
}
service/node-service.go
New file
@@ -0,0 +1,117 @@
package service
import (
    "basic.com/valib/logger.git"
    "bee-config/config"
    "bee-config/models"
    "bee-config/utils"
    "os"
    "strings"
    "time"
)
func AddNode() (string, error) {
    var node models.BeeNode
    arr, err := node.FindAll()
    if nil != err {
        logger.Error("AddNode failed to read all data, err:", err)
        return "", err
    }
    id := uint8(getNextId(arr))
    dir := config.GetNodeDir(id)
    if _, err := os.Stat(dir); os.IsNotExist(err) {
        _ = os.MkdirAll(dir, os.ModePerm)
    }
    err = config.GenerateDockerFile(dir, id)
    if nil != err {
        logger.Error("AddNode failed to generate docker file, err:", err)
        return "", err
    }
    _, err = utils.ShellExecute(dir, "docker-compose", "up", "-d")
    if nil != err {
        logger.Error("AddNode failed to invoke docker-compose up, err:", err)
        return "", err
    }
    address, err := utils.ShellExecuteEx(1 * time.Minute, terminate, dir, "docker-compose", "logs", "-f", "bee-1")
    if nil != err {
        logger.Error("AddNode failed to invoke docker-compose logs, err:", err)
        return "", err
    }
    n := models.BeeNode {
        Id:        int(id),
        Address:   address,
        Dir:       dir,
        ApiPort:   uint16(config.GetApiPort(id)),
        P2PPort:   uint16(config.GetP2pPort(id)),
        DebugPort: uint16(config.GetDebugPort(id)),
        Status:    0,
        Del:       0,
    }
    err = n.Save()
    if nil != err {
        logger.Error("AddNode failed to save to db, err:", err)
        return "", err
    }
    return address, nil
}
func terminate(line string) string {
    token := "using ethereum address"
    length := len("b02363e9ec1b2eba9415786a4fab459122d50aa1")
    index := strings.Index(line, token)
    if -1 == index {
        return ""
    }
    index += len(token)
    substring := line[index:]
    index = 0
    for {
        if substring[index] == ' ' {
            index++
            continue
        }
        break;
    }
    substring = substring[index:]
    substring = substring[:length]
    return substring
}
func getNextId(arr []models.BeeNode) int {
    nextId := len(arr) + 1
    if len(arr) == 0 {
        return nextId
    }
    maxId := arr[0].Id
    conflicts := false
    for _, v := range arr {
        if v.Id == nextId {
            conflicts = true
        }
        if v.Id > maxId {
            maxId = v.Id
        }
    }
    if conflicts {
        nextId = maxId + 1
    }
    return nextId
}
utils/cmd.go
New file
@@ -0,0 +1,121 @@
package utils
import (
    "bufio"
    "bytes"
    "errors"
    "fmt"
    "io"
    "os"
    "os/exec"
    "time"
)
func ShellExecute(dir, exe string, args ...string) (string, error) {
    cmd := exec.Command(exe, args...)
    stdout, err := cmd.StdoutPipe();
    if nil != err {
        msg := fmt.Sprintf("ShellExecute cmd.StdoutPipe err:%v", err)
        return "", errors.New(msg)
    }
    cmd.Stderr = os.Stderr
    if "" != dir {
        cmd.Dir = dir
    }
    err = cmd.Start()
    if nil != err {
        msg := fmt.Sprintf("ShellExecute cmd.Start err:%v", err)
        return "", errors.New(msg)
    }
    var buffer bytes.Buffer
    reader := bufio.NewReader(stdout)
    for {
        line, err2 := reader.ReadString('\n')
        if io.EOF == err2 {
            break
        }
        if nil != err2 {
            msg := fmt.Sprintf("ShellExecute reader.ReadString err:%v", err2)
            return "", errors.New(msg)
        }
        buffer.WriteString(line)
    }
    err = cmd.Wait()
    if nil != err {
        msg := fmt.Sprintf("ShellExecute cmd.Wait err:%v", err)
        return "", errors.New(msg)
    }
    return buffer.String(), nil
}
type TerminateFunc func(string) string
func ShellExecuteEx(d time.Duration, t TerminateFunc, dir, exe string, args ...string) (string, error) {
    cmd := exec.Command(exe, args...)
    stdout, err := cmd.StdoutPipe();
    if nil != err {
        msg := fmt.Sprintf("ShellExecuteEx cmd.StdoutPipe err:%v", err)
        return "", errors.New(msg)
    }
    cmd.Stderr = os.Stderr
    if "" != dir {
        cmd.Dir = dir
    }
    err = cmd.Start()
    if nil != err {
        msg := fmt.Sprintf("ShellExecuteEx cmd.Start err:%v", err)
        return "", errors.New(msg)
    }
    ch := make(chan string)
    go func(out io.ReadCloser, command *exec.Cmd) {
        reader := bufio.NewReader(out)
        for {
            line, err2 := reader.ReadString('\n')
            if io.EOF == err2 {
                ch <- ""
                break
            }
            if nil != err2 {
                break
            }
            address := t(line)
            if len(address) > 0 {
                err := cmd.Process.Signal(os.Kill)
                if nil != err {
                    fmt.Println("failed to send signal to child process", err)
                }
                ch <- address
                break
            }
        }
    }(stdout, cmd)
    var address string
    select {
    case address = <-ch:
        err = nil
        break
    case <-time.After(d):
        _ = cmd.Process.Signal(os.Kill)
        err = errors.New("Timeout error")
        break
    }
    _ = cmd.Wait()
    return address, err
}