mirror of
https://github.com/sigmasternchen/iot-relay
synced 2025-03-15 06:28:55 +00:00
initial commit
This commit is contained in:
commit
6257896d1a
12 changed files with 307 additions and 0 deletions
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
9
.idea/iot-relay.iml
Normal file
9
.idea/iot-relay.iml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
24
cmd/main.go
Normal file
24
cmd/main.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
. "iot-relay/internal/client"
|
||||
. "iot-relay/internal/config"
|
||||
. "iot-relay/internal/server"
|
||||
"log"
|
||||
)
|
||||
|
||||
const configFile = "config.json"
|
||||
|
||||
func main() {
|
||||
log.Println("reading config file")
|
||||
config, err := ReadConfig(configFile)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
log.Println("starting server")
|
||||
err = Listen(config, GetHandler(config))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
12
config.json
Normal file
12
config.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"server": {
|
||||
"bind": "0.0.0.0:20159",
|
||||
"timeout": 30
|
||||
},
|
||||
"client": {
|
||||
"address": "http://localhost:8086",
|
||||
"db": "grafana",
|
||||
"measurement": "iot",
|
||||
"host": "iot-relay"
|
||||
}
|
||||
}
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module iot-relay
|
||||
|
||||
go 1.17
|
58
internal/client/client.go
Normal file
58
internal/client/client.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iot-relay/internal/config"
|
||||
"iot-relay/internal/types"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func lineProtocol(request types.Request, config config.Config) string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString(config.Client.Measurement)
|
||||
builder.WriteString(",host=")
|
||||
builder.WriteString(config.Client.Host)
|
||||
builder.WriteString(",ip=")
|
||||
builder.WriteString(request.IP)
|
||||
builder.WriteString(",id=")
|
||||
builder.WriteString(request.ID)
|
||||
builder.WriteString(",loc=")
|
||||
builder.WriteString(request.Location)
|
||||
builder.WriteString(" ")
|
||||
|
||||
for key, value := range request.Data {
|
||||
builder.WriteString(key)
|
||||
builder.WriteString("=")
|
||||
builder.WriteString(value)
|
||||
builder.WriteString(" ")
|
||||
}
|
||||
|
||||
builder.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func GetHandler(config config.Config) types.Callback {
|
||||
return func(request types.Request) error {
|
||||
line := lineProtocol(request, config)
|
||||
|
||||
url := config.Client.Address + "/write?db=" + config.Client.DB
|
||||
|
||||
resp, err := http.Post(url, "application/octet-stream", strings.NewReader(line))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 204 {
|
||||
err = fmt.Errorf("influxdb responded with %d", resp.StatusCode)
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
41
internal/config/config.go
Normal file
41
internal/config/config.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server struct {
|
||||
Bind string `json:"bind"`
|
||||
Timeout int `json:"timeout"` // in seconds
|
||||
} `json:"server"`
|
||||
Client struct {
|
||||
Address string `json:"address"`
|
||||
DB string `json:"db"`
|
||||
Measurement string `json:"measurement"`
|
||||
Host string `json:"host"`
|
||||
} `json:"client"`
|
||||
}
|
||||
|
||||
func ReadConfig(filename string) (Config, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
var config Config
|
||||
|
||||
err = json.Unmarshal(content, &config)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
57
internal/server/connection.go
Normal file
57
internal/server/connection.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"iot-relay/internal/types"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func connectionHandler(conn net.Conn) {
|
||||
reader := bufio.NewReader(conn)
|
||||
|
||||
defer func() {
|
||||
log.Println("closing connection")
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
var request types.Request
|
||||
request.Data = make(map[string]string)
|
||||
request.IP = strings.Split(conn.RemoteAddr().String(), ":")[0]
|
||||
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
|
||||
if err != nil {
|
||||
log.Printf("connection error: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
if len(line) == 1 {
|
||||
// end of request
|
||||
|
||||
if !request.IsValid() {
|
||||
log.Println("protocol error")
|
||||
_, _ = conn.Write([]byte("bad\n"))
|
||||
break
|
||||
}
|
||||
|
||||
err = callback(request)
|
||||
if err != nil {
|
||||
// should be logged by callback
|
||||
_, _ = conn.Write([]byte("fail\n"))
|
||||
} else {
|
||||
_, _ = conn.Write([]byte("ok\n"))
|
||||
}
|
||||
break
|
||||
} else {
|
||||
err = parseLine(line, &request)
|
||||
if err != nil {
|
||||
log.Printf("parsing error: %v", err)
|
||||
_, _ = conn.Write([]byte("bad\n"))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
internal/server/parser.go
Normal file
38
internal/server/parser.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"iot-relay/internal/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var forbiddenKeys = []string{
|
||||
"ip",
|
||||
}
|
||||
|
||||
func parseLine(line string, request *types.Request) error {
|
||||
token := strings.Split(line, "=")
|
||||
if len(token) != 2 {
|
||||
return errors.New("malformed; missing =")
|
||||
}
|
||||
|
||||
key := strings.ToLower(token[0])
|
||||
value := strings.TrimRight(token[1], "\n")
|
||||
|
||||
for _, forbidden := range forbiddenKeys {
|
||||
if forbidden == key {
|
||||
return errors.New("forbidden key")
|
||||
}
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "id":
|
||||
request.ID = value
|
||||
case "loc":
|
||||
request.Location = value
|
||||
default:
|
||||
request.Data[key] = value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
32
internal/server/server.go
Normal file
32
internal/server/server.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"iot-relay/internal/config"
|
||||
"iot-relay/internal/types"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
var callback types.Callback
|
||||
|
||||
func Listen(config config.Config, _callback types.Callback) error {
|
||||
callback = _callback
|
||||
|
||||
listener, err := net.Listen("tcp", config.Server.Bind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
connection, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Printf("connection from %v", connection.RemoteAddr())
|
||||
|
||||
_ = connection.SetReadDeadline(time.Now().Add(time.Second * time.Duration(config.Server.Timeout)))
|
||||
go connectionHandler(connection)
|
||||
}
|
||||
}
|
||||
}
|
3
internal/types/callback.go
Normal file
3
internal/types/callback.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package types
|
||||
|
||||
type Callback func(request Request) error
|
22
internal/types/request.go
Normal file
22
internal/types/request.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package types
|
||||
|
||||
type Request struct {
|
||||
ID string
|
||||
Location string
|
||||
IP string
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
func (r Request) IsValid() bool {
|
||||
if len(r.IP) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(r.ID) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(r.Location) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
Loading…
Reference in a new issue