Remove API and simplify webserver
This commit is contained in:
parent
c66e1120d3
commit
4cc93891ee
@ -1,6 +1,5 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.7
|
||||
- tip
|
||||
install:
|
||||
- go get -t github.com/FreifunkBremen/respond-collector/...
|
||||
@ -8,3 +7,5 @@ install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
script:
|
||||
- ./.test-coverage
|
||||
- go install github.com/FreifunkBremen/respond-collector/cmd/respond-collector
|
||||
- go install github.com/FreifunkBremen/respond-collector/cmd/respond-query
|
||||
|
@ -1,75 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/FreifunkBremen/respond-collector/models"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GEOROUND : 7 nachkommerstellen sollten genug sein (7cm genau)
|
||||
// http://blog.3960.org/post/7309573249/genauigkeit-bei-geo-koordinaten
|
||||
const GEOROUND = 0.0000001
|
||||
|
||||
func geoEqual(a, b float64) bool {
|
||||
if (a-b) < GEOROUND && (b-a) < GEOROUND {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AliasesAPI struct for API
|
||||
type AliasesAPI struct {
|
||||
aliases *models.Aliases
|
||||
config *models.Config
|
||||
nodes *models.Nodes
|
||||
}
|
||||
|
||||
// NewAliases Bind to API
|
||||
func NewAliases(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) {
|
||||
api := &AliasesAPI{
|
||||
aliases: models.NewAliases(config),
|
||||
nodes: nodes,
|
||||
config: config,
|
||||
}
|
||||
router.GET(prefix, api.GetAll)
|
||||
router.GET(prefix+"/ansible", api.Ansible)
|
||||
router.GET(prefix+"/alias/:nodeid", api.GetOne)
|
||||
router.POST(prefix+"/alias/:nodeid", BasicAuth(api.SaveOne, []byte(config.Webserver.API.Passphrase)))
|
||||
}
|
||||
|
||||
// GetAll request for get all aliases
|
||||
func (api *AliasesAPI) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
jsonOutput(w, r, api.aliases.List)
|
||||
}
|
||||
|
||||
// GetOne request for get one alias
|
||||
func (api *AliasesAPI) GetOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if alias := api.aliases.List[ps.ByName("nodeid")]; alias != nil {
|
||||
jsonOutput(w, r, alias)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, "Not found: ", ps.ByName("nodeid"), "\n")
|
||||
}
|
||||
|
||||
// SaveOne request for save a alias
|
||||
func (api *AliasesAPI) SaveOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
var alias models.Alias
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&alias)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
fmt.Fprint(w, "Decode: ", ps.ByName("nodeid"), "\n")
|
||||
return
|
||||
}
|
||||
api.aliases.Update(ps.ByName("nodeid"), &alias)
|
||||
fmt.Print("[api] node updated '", ps.ByName("nodeid"), "'\n")
|
||||
jsonOutput(w, r, alias)
|
||||
}
|
||||
|
||||
// Ansible json output
|
||||
func (api *AliasesAPI) Ansible(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
fmt.Print("[api] ansible\n")
|
||||
jsonOutput(w, r, models.GenerateAnsible(api.nodes, api.aliases.List))
|
||||
}
|
63
api/lib.go
63
api/lib.go
@ -1,63 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
func jsonOutput(w http.ResponseWriter, r *http.Request, data interface{}) {
|
||||
js, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if origin := r.Header.Get("Origin"); origin != "" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
// BasicAuth for API request
|
||||
func BasicAuth(h httprouter.Handle, pass []byte) httprouter.Handle {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if origin := r.Header.Get("Origin"); origin != "" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
const basicAuthPrefix string = "Basic "
|
||||
|
||||
// Get the Basic Authentication credentials
|
||||
auth := r.Header.Get("Authorization")
|
||||
if strings.HasPrefix(auth, basicAuthPrefix) {
|
||||
// Check credentials
|
||||
payload, err := base64.StdEncoding.DecodeString(auth[len(basicAuthPrefix):])
|
||||
if err == nil {
|
||||
pair := bytes.SplitN(payload, []byte(":"), 2)
|
||||
if len(pair) == 2 &&
|
||||
bytes.Equal(pair[1], pass) {
|
||||
|
||||
// Delegate request to the given handle
|
||||
h(w, r, ps)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request Basic Authentication otherwise
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
}
|
||||
}
|
27
api/nodes.go
27
api/nodes.go
@ -1,27 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/FreifunkBremen/respond-collector/models"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// NodesAPI struct for API
|
||||
type NodesAPI struct {
|
||||
config *models.Config
|
||||
nodes *models.Nodes
|
||||
}
|
||||
|
||||
// NewNodes Bind to API
|
||||
func NewNodes(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) {
|
||||
api := &NodesAPI{
|
||||
nodes: nodes,
|
||||
config: config,
|
||||
}
|
||||
router.GET(prefix, api.GetAll)
|
||||
}
|
||||
|
||||
// GetAll request for get all nodes
|
||||
func (api *NodesAPI) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
jsonOutput(w, r, api.nodes.List)
|
||||
}
|
@ -3,20 +3,15 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/FreifunkBremen/respond-collector/api"
|
||||
"github.com/FreifunkBremen/respond-collector/database"
|
||||
"github.com/FreifunkBremen/respond-collector/models"
|
||||
"github.com/FreifunkBremen/respond-collector/respond"
|
||||
"github.com/FreifunkBremen/respond-collector/rrd"
|
||||
"github.com/FreifunkBremen/respond-collector/webserver"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -54,21 +49,9 @@ func main() {
|
||||
}
|
||||
|
||||
if config.Webserver.Enable {
|
||||
router := httprouter.New()
|
||||
if config.Webserver.API.NewNodes {
|
||||
api.NewNodes(config, router, "/api/nodes", nodes)
|
||||
log.Println("api nodes started")
|
||||
}
|
||||
if config.Webserver.API.Aliases {
|
||||
api.NewAliases(config, router, "/api/aliases", nodes)
|
||||
log.Println("api aliases started")
|
||||
}
|
||||
router.NotFound = gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webserver.Webroot)))
|
||||
|
||||
address := net.JoinHostPort(config.Webserver.Address, config.Webserver.Port)
|
||||
log.Println("starting webserver on", address)
|
||||
// TODO bad
|
||||
log.Fatal(http.ListenAndServe(address, router))
|
||||
log.Println("starting webserver on", config.Webserver.Bind)
|
||||
srv := webserver.New(config.Webserver.Bind, config.Webserver.Webroot)
|
||||
go srv.Close()
|
||||
}
|
||||
|
||||
// Wait for INT/TERM
|
||||
|
@ -3,18 +3,18 @@ enable = true
|
||||
interface = "eth0"
|
||||
collect_interval = "1m"
|
||||
|
||||
|
||||
[webserver]
|
||||
enable = false
|
||||
port = "8080"
|
||||
address = "127.0.0.1"
|
||||
bind = "127.0.0.1:8080"
|
||||
webroot = "webroot"
|
||||
|
||||
|
||||
[nodes]
|
||||
enable = true
|
||||
nodes_version = 2
|
||||
nodes_path = "/var/www/html/meshviewer/data/nodes_all.json"
|
||||
graphs_path = "/var/www/html/meshviewer/data/graph.json"
|
||||
aliases_path = "/var/www/html/meshviewer/data/aliases.json"
|
||||
graph_path = "/var/www/html/meshviewer/data/graph.json"
|
||||
|
||||
# Export nodes and graph periodically
|
||||
save_interval = "5s"
|
||||
|
@ -1,77 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/FreifunkBremen/respond-collector/data"
|
||||
)
|
||||
|
||||
// Alias a change request for other nodes
|
||||
type Alias struct {
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
Location *data.Location `json:"location,omitempty"`
|
||||
Wireless *data.Wireless `json:"wireless,omitempty"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
}
|
||||
|
||||
// Aliases struct: cache DB of Node's structs
|
||||
type Aliases struct {
|
||||
List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID
|
||||
config *Config
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewAliases create Nodes structs
|
||||
func NewAliases(config *Config) *Aliases {
|
||||
aliases := &Aliases{
|
||||
List: make(map[string]*Alias),
|
||||
config: config,
|
||||
}
|
||||
|
||||
if config.Nodes.AliasesPath != "" {
|
||||
aliases.load()
|
||||
}
|
||||
go aliases.worker()
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
// Update a alias in aliases cache
|
||||
func (e *Aliases) Update(nodeID string, newalias *Alias) {
|
||||
e.Lock()
|
||||
e.List[nodeID] = newalias
|
||||
e.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (e *Aliases) load() {
|
||||
path := e.config.Nodes.AliasesPath
|
||||
log.Println("loading", path)
|
||||
|
||||
if data, err := ioutil.ReadFile(path); err == nil {
|
||||
if err = json.Unmarshal(data, e); err == nil {
|
||||
log.Println("loaded", len(e.List), "aliases")
|
||||
} else {
|
||||
log.Println("failed to unmarshal nodes:", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Println("failed loading cached nodes:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Periodically saves the cached DB to json file
|
||||
func (e *Aliases) worker() {
|
||||
c := time.Tick(time.Second * 5)
|
||||
|
||||
for range c {
|
||||
log.Println("saving", len(e.List), "aliases")
|
||||
e.Lock()
|
||||
save(e, e.config.Nodes.AliasesPath)
|
||||
e.Unlock()
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package models
|
||||
|
||||
// Ansible struct
|
||||
type Ansible struct {
|
||||
Nodes []string `json:"nodes"`
|
||||
Meta struct {
|
||||
HostVars map[string]*AnsibleHostVars `json:"hostvars,omitempty"`
|
||||
} `json:"_meta"`
|
||||
}
|
||||
|
||||
// AnsibleHostVars new values for a node
|
||||
type AnsibleHostVars struct {
|
||||
Address string `json:"ansible_ssh_host"`
|
||||
Hostname string `json:"node_name,omitempty"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Channel24 uint32 `json:"radio24_channel,omitempty"`
|
||||
TxPower24 uint32 `json:"radio24_txpower,omitempty"`
|
||||
Channel5 uint32 `json:"radio5_channel,omitempty"`
|
||||
TxPower5 uint32 `json:"radio5_txpower,omitempty"`
|
||||
GeoLatitude float64 `json:"geo_latitude,omitempty"`
|
||||
GeoLongitude float64 `json:"geo_longitude,omitempty"`
|
||||
}
|
||||
|
||||
// GenerateAnsible but nodes and aliases together to a ansible change output
|
||||
func GenerateAnsible(nodes *Nodes, aliases map[string]*Alias) *Ansible {
|
||||
ansible := &Ansible{Nodes: make([]string, 0)}
|
||||
ansible.Meta.HostVars = make(map[string]*AnsibleHostVars)
|
||||
for nodeid, alias := range aliases {
|
||||
if node := nodes.List[nodeid]; node != nil {
|
||||
|
||||
ansible.Nodes = append(ansible.Nodes, nodeid)
|
||||
|
||||
vars := &AnsibleHostVars{
|
||||
Hostname: alias.Hostname,
|
||||
Owner: alias.Owner,
|
||||
}
|
||||
if node.Nodeinfo.Network.Addresses != nil {
|
||||
vars.Address = node.Nodeinfo.Network.Addresses[0]
|
||||
}
|
||||
if alias.Wireless != nil {
|
||||
vars.Channel24 = alias.Wireless.Channel24
|
||||
vars.TxPower24 = alias.Wireless.TxPower24
|
||||
vars.Channel5 = alias.Wireless.Channel5
|
||||
vars.TxPower5 = alias.Wireless.TxPower5
|
||||
}
|
||||
if alias.Location != nil {
|
||||
vars.GeoLatitude = alias.Location.Latitude
|
||||
vars.GeoLongitude = alias.Location.Longtitude
|
||||
}
|
||||
ansible.Meta.HostVars[nodeid] = vars
|
||||
|
||||
}
|
||||
}
|
||||
return ansible
|
||||
}
|
@ -15,22 +15,15 @@ type Config struct {
|
||||
}
|
||||
Webserver struct {
|
||||
Enable bool
|
||||
Port string
|
||||
Address string
|
||||
Bind string
|
||||
Webroot string
|
||||
API struct {
|
||||
Passphrase string
|
||||
NewNodes bool
|
||||
Aliases bool
|
||||
}
|
||||
}
|
||||
Nodes struct {
|
||||
Enable bool
|
||||
NodesDynamicPath string
|
||||
NodesPath string
|
||||
NodesVersion int
|
||||
GraphsPath string
|
||||
AliasesPath string
|
||||
NodesPath string
|
||||
NodesDynamicPath string
|
||||
GraphPath string
|
||||
SaveInterval Duration // Save nodes periodically
|
||||
PruneAfter Duration // Remove nodes after n days of inactivity
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ func (nodes *Nodes) save() {
|
||||
}
|
||||
}
|
||||
|
||||
if path := nodes.config.Nodes.GraphsPath; path != "" {
|
||||
if path := nodes.config.Nodes.GraphPath; path != "" {
|
||||
save(nodes.BuildGraph(), path)
|
||||
}
|
||||
}
|
||||
|
24
webserver/webserver.go
Normal file
24
webserver/webserver.go
Normal file
@ -0,0 +1,24 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/NYTimes/gziphandler"
|
||||
)
|
||||
|
||||
// New creates a new webserver and starts it
|
||||
func New(bindAddr, webroot string) *http.Server {
|
||||
srv := &http.Server{
|
||||
Addr: bindAddr,
|
||||
Handler: gziphandler.GzipHandler(http.FileServer(http.Dir(webroot))),
|
||||
}
|
||||
|
||||
go func() {
|
||||
// service connections
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return srv
|
||||
}
|
Loading…
Reference in New Issue
Block a user