This commit is contained in:
MQjehovah 2024-01-09 21:00:48 +08:00
commit 64732cb9e9
10 changed files with 905 additions and 0 deletions

View File

@ -0,0 +1,11 @@
{
"folders": [
{
"path": "."
},
{
"path": "../server"
}
],
"settings": {}
}

10
client/go.mod Normal file
View File

@ -0,0 +1,10 @@
module main
go 1.21.5
require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/urfave/cli/v2 v2.27.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
)

8
client/go.sum Normal file
View File

@ -0,0 +1,8 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=

272
client/main.go Normal file
View File

@ -0,0 +1,272 @@
package main
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"net"
"os"
"strings"
"time"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
)
type Config struct {
AddrServer string
}
const HEART_BEAT_INTERVAL = time.Second * 5 // 心跳超时时间
const (
MSG_TYPE_UNKOWNM = iota
MSG_TYPE_HEARTBEAT
MSG_TYPE_REGISTER
MSG_TYPE_SESSION_CREATE
MSG_TYPE_SESSION_DATA
MSG_TYPE_SESSION_DESTORY
MSG_TYPE_TUNNEL_CREATE
MSG_TYPE_MAX
)
type Device struct {
id string
category string
desc string /* description of the device */
conn net.Conn
create_time int64 /* connection time */
active time.Time
uptime uint32
token string
registered bool
closed uint32
send chan []byte // Buffered channel of outbound messages.
}
func main() {
app := &cli.App{
Name: "XZRobot Ops Server",
Usage: "The Server Side For xzrobot ops",
Version: "1.0.0",
Commands: []*cli.Command{
{
Name: "run",
Usage: "Run Server",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "log",
Value: "log.txt",
Usage: "log file path",
},
&cli.StringFlag{
Name: "conf",
Aliases: []string{"c"},
Value: "./rttys.conf",
Usage: "config file to load",
},
&cli.StringFlag{
Name: "addr-dev",
Value: ":9011",
Usage: "address to listen device",
},
&cli.StringFlag{
Name: "addr-user",
Value: ":9012",
Usage: "address to listen user",
},
&cli.StringFlag{
Name: "db",
Value: "sqlite://database.db",
Usage: "database source",
},
},
Action: func(c *cli.Context) error {
runClient(c)
return nil
},
},
},
Action: func(c *cli.Context) error {
c.App.Command("run").Run(c)
return nil
},
}
err := app.Run(os.Args)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func runClient(c *cli.Context) {
cfg := &Config{
AddrServer: "localhost:9011",
}
tcpConn, err := createTcpConn(cfg.AddrServer)
if err != nil {
fmt.Println("TCP Connect Error! " + err.Error())
return
}
fmt.Println(tcpConn.LocalAddr().String() + " : Client Connected")
reader := bufio.NewReader(tcpConn)
for {
s, err := reader.ReadString('\n')
if err != nil || err == io.EOF {
break
} else {
//接收到new的指令的时候新建一个tcp连接
if s == "new\n" {
}
if s == "hi" {
//忽略掉hi的请求
}
}
}
}
func readLoop(conn *net.TCPConn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
b, err := reader.Peek(5)
if err != nil {
if err != io.EOF && !strings.Contains(err.Error(), "use of closed network connection") {
log.Error().Msg(err.Error())
}
return
}
reader.Discard(5)
msg_type := b[0]
if msg_type >= MSG_TYPE_MAX {
log.Error().Msgf("invalid msg type: %d", msg_type)
return
}
msg_length := binary.BigEndian.Uint32(b[1:])
data := make([]byte, msg_length)
_, err = io.ReadFull(reader, data)
if err != nil {
log.Error().Msg(err.Error())
return
}
// dev.active = time.Now()
switch msg_type {
case MSG_TYPE_HEARTBEAT:
parseHeartbeat(b)
default:
log.Error().Msgf("invalid msg type: %d", msg_type)
}
}
}
func parseHeartbeat(b []byte) {
uptime := binary.BigEndian.Uint32(b[:4])
log.Info().Msgf("Heartbeat Time: %d", uptime)
}
func writeLoop(conn *net.TCPConn) {
ticker := time.NewTicker(time.Second)
defer conn.Close()
ninactive := 0
lastHeartbeat := time.Now()
for {
select {
case msg, ok := <-dev.send:
if !ok {
return
}
_, err := dev.conn.Write(msg)
if err != nil {
log.Error().Msg(err.Error())
return
}
case <-ticker.C:
now := time.Now()
if now.Sub(dev.active) > HEART_BEAT_INTERVAL*3/2 {
if dev.id == "" {
return
}
log.Error().Msgf("Inactive device in long time: %s", dev.id)
if ninactive > 3 {
log.Error().Msgf("Inactive 3 times, now kill it: %s", dev.id)
return
}
ninactive = ninactive + 1
}
if now.Sub(lastHeartbeat) > HEART_BEAT_INTERVAL-1 {
lastHeartbeat = now
if len(dev.send) < 1 {
dev.WriteMsg(MSG_TYPE_HEARTBEAT, []byte{})
}
}
}
}
}
func createTcpConn(addr string) (*net.TCPConn, error) {
tcpConn, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
fmt.Println("TCP Address error ! " + err.Error())
return nil, err
}
conn, err := net.DialTCP("tcp", nil, tcpConn)
if err != nil {
fmt.Println("TCP Connect error ! " + err.Error())
return nil, err
}
return conn, nil
}
func createTunnel(localAddr string, remoteAddr string) {
localConn, err := createTcpConn(localAddr)
if err != nil {
fmt.Println("Create LocalConn Error! " + err.Error())
return
}
remoteConn, err := createTcpConn(remoteAddr)
if err != nil {
fmt.Println("Create remoteAddr Error! " + err.Error())
return
}
defer localConn.Close()
defer remoteConn.Close()
go func() {
_, err := io.Copy(localConn, remoteConn)
if err != nil {
fmt.Println("Copy error! " + err.Error())
return
}
}()
go func() {
_, err := io.Copy(remoteConn, localConn)
if err != nil {
fmt.Println("Copy error! " + err.Error())
return
}
}()
}

159
server/broker.go Normal file
View File

@ -0,0 +1,159 @@
package main
import (
"sync/atomic"
"time"
"github.com/gorilla/websocket"
"github.com/rs/zerolog/log"
)
type Session struct {
dev Client
user Client
confirmed uint32
}
type Broker struct {
cfg *Config
devices map[string]Client
sessions map[string]*Session
register chan Client
unregister chan Client
deviceMessageChan chan deviceMessage
// userMessageChan chan userMessage
}
func newBroker(cfg *Config) *Broker {
return &Broker{
cfg: cfg,
devices: make(map[string]Client),
sessions: make(map[string]*Session),
register: make(chan Client, 1000),
unregister: make(chan Client, 1000),
deviceMessageChan: make(chan deviceMessage, 1000),
// userMessageChan: make(chan userMessage, 1000),
}
}
func (br *Broker) run() {
for {
select {
case c := <-br.register:
devid := c.DeviceID()
if c.DeviceCategory() == "device" {
dev := c.(*Device)
err := byte(0)
msg := "OK"
if _, ok := br.devices[devid]; ok {
log.Error().Msg("Device ID conflicting: " + devid)
msg = "ID conflicting"
err = 1
} else {
dev.registered = true
br.devices[devid] = c
log.Info().Msgf("Device '%s' registered", devid)
}
c.WriteMsg(MSG_TYPE_REGISTER, append([]byte{err}, msg...))
if err > 0 {
// ensure the last packet was sent
time.AfterFunc(time.Millisecond*100, func() {
dev.Close()
})
}
} else {
if dev, ok := br.devices[devid]; ok {
sid := GenUniqueID("sid")
s := &Session{
dev: dev,
user: c,
}
time.AfterFunc(time.Second*3, func() {
if atomic.LoadUint32(&s.confirmed) == 0 {
c.Close()
}
})
br.sessions[sid] = s
dev.WriteMsg(MSG_TYPE_SESSION_CREATE, []byte(sid))
log.Info().Msg("New session: " + sid)
} else {
log.Error().Msgf("Not found the device '%s'", devid)
}
}
case c := <-br.unregister:
devid := c.DeviceID()
c.Close()
if c.DeviceCategory() == "device" {
if !c.(*Device).registered {
break
}
delete(br.devices, devid)
for sid, s := range br.sessions {
if s.dev == c {
s.user.Close()
delete(br.sessions, sid)
log.Info().Msg("Delete session: " + sid)
}
}
log.Info().Msgf("Device '%s' unregistered", devid)
} else {
sid := c.(*Device).DeviceID()
if _, ok := br.sessions[sid]; ok {
delete(br.sessions, sid)
if dev, ok := br.devices[devid]; ok {
dev.WriteMsg(MSG_TYPE_SESSION_DESTORY, []byte(sid))
}
log.Info().Msg("Delete session: " + sid)
}
}
case msg := <-br.deviceMessageChan:
if msg.typ == MSG_TYPE_SESSION_CREATE {
if s, ok := br.sessions[msg.sid]; ok {
atomic.StoreUint32(&s.confirmed, 1)
// u := s.user.(*user)
// u.sid = msg.sid
// userLoginAck(loginErrorNone, s.user)
}
}
if msg.typ == MSG_TYPE_SESSION_DATA {
if s, ok := br.sessions[msg.sid]; ok {
s.user.WriteMsg(websocket.BinaryMessage, msg.data)
}
}
// case msg := <-br.userMessageChan:
// if s, ok := br.sessions[msg.sid]; ok {
// if dev, ok := br.devices[s.dev.DeviceID()]; ok {
// data := msg.data
// if msg.typ == websocket.BinaryMessage {
// dev.WriteMsg(MSG_TYPE_SESSION_DATA, append([]byte(msg.sid), data[1:]...))
// }
// }
// }
}
}
}

265
server/client.go Normal file
View File

@ -0,0 +1,265 @@
package main
import (
"bufio"
"bytes"
"encoding/binary"
"io"
"net"
"strings"
"sync/atomic"
"time"
"github.com/rs/zerolog/log"
)
type Client interface {
DeviceID() string
DeviceCategory() string
WriteMsg(typ int, data []byte)
Close()
}
const HEART_BEAT_INTERVAL = time.Second * 5 // 心跳超时时间
const (
MSG_TYPE_UNKOWNM = iota
MSG_TYPE_HEARTBEAT
MSG_TYPE_REGISTER
MSG_TYPE_SESSION_CREATE
MSG_TYPE_SESSION_DATA
MSG_TYPE_SESSION_DESTORY
MSG_TYPE_TUNNEL_CREATE
MSG_TYPE_MAX
)
type Device struct {
br *Broker
id string
category string
desc string /* description of the device */
conn net.Conn
create_time int64 /* connection time */
active time.Time
uptime uint32
token string
registered bool
closed uint32
send chan []byte // Buffered channel of outbound messages.
}
type deviceMessage struct {
sid string
typ int
data []byte
}
func (dev *Device) DeviceID() string {
return dev.id
}
func (dev *Device) DeviceCategory() string {
return dev.category
}
// 往设备写数据
func (dev *Device) WriteMsg(typ int, data []byte) {
b := []byte{byte(typ), 0, 0}
binary.BigEndian.PutUint16(b[1:], uint16(len(data)))
dev.send <- append(b, data...)
}
func (dev *Device) Close() {
if atomic.LoadUint32(&dev.closed) == 1 {
return
}
atomic.StoreUint32(&dev.closed, 1)
log.Debug().Msgf("Device '%s' disconnected", dev.conn.RemoteAddr())
dev.conn.Close()
close(dev.send)
}
// 解析设备信息
func parseDeviceInfo(dev *Device, b []byte) bool {
fields := bytes.Split(b, []byte{0})
if len(fields) < 3 {
log.Error().Msg("msgTypeRegister: invalid")
return false
}
dev.id = string(fields[0])
dev.desc = string(fields[1])
dev.token = string(fields[2])
return true
}
// 解析设备心跳
func parseHeartbeat(dev *Device, b []byte) {
dev.uptime = binary.BigEndian.Uint32(b[:4])
}
func (dev *Device) readLoop() {
defer func() {
dev.br.unregister <- dev
}()
reader := bufio.NewReader(dev.conn)
for {
b, err := reader.Peek(5)
if err != nil {
if err != io.EOF && !strings.Contains(err.Error(), "use of closed network connection") {
log.Error().Msg(err.Error())
}
return
}
reader.Discard(5)
msg_type := b[0]
if msg_type >= MSG_TYPE_MAX {
log.Error().Msgf("invalid msg type: %d", msg_type)
return
}
msg_length := binary.BigEndian.Uint32(b[1:])
data := make([]byte, msg_length)
_, err = io.ReadFull(reader, data)
if err != nil {
log.Error().Msg(err.Error())
return
}
dev.active = time.Now()
switch msg_type {
case MSG_TYPE_REGISTER:
if msg_length < 2 {
log.Error().Msg("msgTypeRegister: invalid")
return
}
if !parseDeviceInfo(dev, data) {
return
}
dev.br.register <- dev
case MSG_TYPE_SESSION_CREATE:
if msg_length < 33 {
log.Error().Msg("msgTypeLogin: invalid")
return
}
dev.br.deviceMessageChan <- deviceMessage{string(data[:32]), MSG_TYPE_SESSION_CREATE, data[32:]}
case MSG_TYPE_SESSION_DATA:
if msg_length < 32 {
log.Error().Msg("msgTypeTermData|msgTypeFile: invalid")
return
}
dev.br.deviceMessageChan <- deviceMessage{string(data[:32]), MSG_TYPE_SESSION_DATA, data[32:]}
case MSG_TYPE_HEARTBEAT:
parseHeartbeat(dev, b)
default:
log.Error().Msgf("invalid msg type: %d", msg_type)
}
}
}
func (dev *Device) writeLoop() {
ticker := time.NewTicker(time.Second)
defer func() {
ticker.Stop()
dev.br.unregister <- dev
}()
ninactive := 0
lastHeartbeat := time.Now()
for {
select {
case msg, ok := <-dev.send:
if !ok {
return
}
_, err := dev.conn.Write(msg)
if err != nil {
log.Error().Msg(err.Error())
return
}
case <-ticker.C:
now := time.Now()
if now.Sub(dev.active) > HEART_BEAT_INTERVAL*3/2 {
if dev.id == "" {
return
}
log.Error().Msgf("Inactive device in long time: %s", dev.id)
if ninactive > 3 {
log.Error().Msgf("Inactive 3 times, now kill it: %s", dev.id)
return
}
ninactive = ninactive + 1
}
if now.Sub(lastHeartbeat) > HEART_BEAT_INTERVAL-1 {
lastHeartbeat = now
if len(dev.send) < 1 {
dev.WriteMsg(MSG_TYPE_HEARTBEAT, []byte{})
}
}
}
}
}
func listenDevice(br *Broker) {
ln, err := net.Listen("tcp", br.cfg.AddrDev)
if err != nil {
log.Error().Msgf(err.Error())
}
log.Info().Msgf("Listen Device on: %s", br.cfg.AddrDev)
go func() {
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
log.Error().Msg(err.Error())
continue
}
log.Debug().Msgf("Device '%s' connected", conn.RemoteAddr())
dev := &Device{
br: br,
conn: conn,
create_time: time.Now().Unix(),
send: make(chan []byte, 256),
}
go dev.readLoop()
go dev.writeLoop()
}
}()
}

23
server/go.mod Normal file
View File

@ -0,0 +1,23 @@
module main
go 1.21.5
require github.com/rs/zerolog v1.31.0
require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/term v0.13.0 // indirect
)
require (
github.com/gorilla/websocket v1.5.1
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/urfave/cli/v2 v2.27.1
golang.org/x/sys v0.13.0 // indirect
)

34
server/go.sum Normal file
View File

@ -0,0 +1,34 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=

82
server/main.go Normal file
View File

@ -0,0 +1,82 @@
package main
import (
"fmt"
"os"
"github.com/urfave/cli/v2"
)
type Config struct {
AddrDev string
AddrUser string
}
func runServer(c *cli.Context) {
cfg := &Config{
AddrDev: "0.0.0.0:9011",
AddrUser: "0.0.0.0:9012",
}
br := newBroker(cfg)
listenDevice(br)
br.run()
}
func main() {
app := &cli.App{
Name: "XZRobot Ops Server",
Usage: "The Server Side For xzrobot ops",
Version: "1.0.0",
Commands: []*cli.Command{
{
Name: "run",
Usage: "Run Server",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "log",
Value: "log.txt",
Usage: "log file path",
},
&cli.StringFlag{
Name: "conf",
Aliases: []string{"c"},
Value: "./rttys.conf",
Usage: "config file to load",
},
&cli.StringFlag{
Name: "addr-dev",
Value: ":9011",
Usage: "address to listen device",
},
&cli.StringFlag{
Name: "addr-user",
Value: ":9012",
Usage: "address to listen user",
},
&cli.StringFlag{
Name: "db",
Value: "sqlite://database.db",
Usage: "database source",
},
},
Action: func(c *cli.Context) error {
runServer(c)
return nil
},
},
},
Action: func(c *cli.Context) error {
c.App.Command("run").Run(c)
return nil
},
}
err := app.Run(os.Args)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

41
server/utils.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"crypto/md5"
"crypto/rand"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"os"
"time"
"github.com/howeyc/gopass"
"github.com/rs/zerolog/log"
)
// GenUniqueID generate a unique ID
func GenUniqueID(extra string) string {
buf := make([]byte, 20)
binary.BigEndian.PutUint32(buf, uint32(time.Now().Unix()))
io.ReadFull(rand.Reader, buf[4:])
h := md5.New()
h.Write(buf)
h.Write([]byte(extra))
return hex.EncodeToString(h.Sum(nil))
}
// GenToken generate a token
func GenToken() {
password, err := gopass.GetPasswdPrompt("Please set a password:", true, os.Stdin, os.Stdout)
if err != nil {
log.Fatal().Msg(err.Error())
}
token := GenUniqueID(string(password))
fmt.Println("Your token is:", token)
}