|
|
@ -1,5 +1,7 @@ |
|
|
|
package main |
|
|
|
|
|
|
|
//go:generate go-bindata -o assets/assets_gen.go -pkg assets public/...
|
|
|
|
|
|
|
|
import ( |
|
|
|
"container/list" |
|
|
|
"encoding/json" |
|
|
@ -7,12 +9,15 @@ import ( |
|
|
|
"fmt" |
|
|
|
"log" |
|
|
|
"net" |
|
|
|
"net/http" |
|
|
|
"os" |
|
|
|
"os/user" |
|
|
|
"path/filepath" |
|
|
|
"strings" |
|
|
|
"sync" |
|
|
|
"time" |
|
|
|
|
|
|
|
"varia.zone/unnamed-project/assets" |
|
|
|
) |
|
|
|
|
|
|
|
const help = `unnamed-project: WIP |
|
|
@ -52,11 +57,11 @@ func main() { |
|
|
|
} |
|
|
|
|
|
|
|
conf := &config{ |
|
|
|
nodeID: nodeIDFlag, |
|
|
|
webPort: webPortFlag, |
|
|
|
filePort: filePortFlag, |
|
|
|
sharePath: pathFlag, |
|
|
|
debug: debugFlag, |
|
|
|
NodeID: nodeIDFlag, |
|
|
|
WebPort: webPortFlag, |
|
|
|
FilePort: filePortFlag, |
|
|
|
SharePath: pathFlag, |
|
|
|
Debug: debugFlag, |
|
|
|
} |
|
|
|
|
|
|
|
serve(conf) |
|
|
@ -74,11 +79,11 @@ func handleCliFlags(username, sharePath string) { |
|
|
|
} |
|
|
|
|
|
|
|
type config struct { |
|
|
|
nodeID string |
|
|
|
webPort int |
|
|
|
filePort int |
|
|
|
sharePath string |
|
|
|
debug bool |
|
|
|
NodeID string `json:"nodeID"` |
|
|
|
WebPort int `json:"webPort"` |
|
|
|
FilePort int `json:"filePort"` |
|
|
|
SharePath string `json:"sharePath"` |
|
|
|
Debug bool `json:"debug"` |
|
|
|
} |
|
|
|
|
|
|
|
type announcer struct { |
|
|
@ -86,10 +91,10 @@ type announcer struct { |
|
|
|
} |
|
|
|
|
|
|
|
type nodeInfo struct { |
|
|
|
nodeID string `json:"nodeID"` |
|
|
|
addr string `json:"addr"` |
|
|
|
webPort int `json:"webPort"` |
|
|
|
lastMulticast int64 `json:"lastMulticast"` |
|
|
|
NodeID string `json:"nodeID"` |
|
|
|
Addr string `json:"addr"` |
|
|
|
WebPort int `json:"webPort"` |
|
|
|
LastMulticast int64 `json:"lastMulticast"` |
|
|
|
} |
|
|
|
|
|
|
|
var ( |
|
|
@ -152,7 +157,7 @@ func announcedNodeHandler(ninfo *nodeInfo, nodeList *list.List) { |
|
|
|
|
|
|
|
fmt.Print("[") |
|
|
|
for el := nodeList.Front(); el != nil; el = el.Next() { |
|
|
|
fmt.Print(el.Value.(*nodeInfo).nodeID, " ") |
|
|
|
fmt.Print(el.Value.(*nodeInfo).NodeID, " ") |
|
|
|
} |
|
|
|
fmt.Print("]\n\n") |
|
|
|
} |
|
|
@ -163,9 +168,9 @@ func updateNodeList(ninfo *nodeInfo, nodeList *list.List) { |
|
|
|
tmp := el.Value.(*nodeInfo) |
|
|
|
|
|
|
|
// Already in list
|
|
|
|
if tmp.nodeID == ninfo.nodeID { |
|
|
|
tmp.lastMulticast = time.Now().Unix() |
|
|
|
fmt.Printf("Updating node %s multicast\n", ninfo.nodeID) |
|
|
|
if tmp.NodeID == ninfo.NodeID { |
|
|
|
tmp.LastMulticast = time.Now().Unix() |
|
|
|
fmt.Printf("Updating node %s multicast\n", ninfo.NodeID) |
|
|
|
nodeExists = true |
|
|
|
break |
|
|
|
} |
|
|
@ -175,20 +180,20 @@ func updateNodeList(ninfo *nodeInfo, nodeList *list.List) { |
|
|
|
for el := nodeList.Front(); el != nil; el = el.Next() { |
|
|
|
tmp := el.Value.(*nodeInfo) |
|
|
|
if isNodeExpired(tmp, expireTimeoutSec) { |
|
|
|
fmt.Println("Node expired, removing: ", tmp.nodeID) |
|
|
|
fmt.Println("Node expired, removing: ", tmp.NodeID) |
|
|
|
nodeList.Remove(el) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if !nodeExists { |
|
|
|
fmt.Printf("Adding new node! %p %s\n", ninfo, ninfo.nodeID) |
|
|
|
ninfo.lastMulticast = time.Now().Unix() |
|
|
|
fmt.Printf("Adding new node! %p %s\n", ninfo, ninfo.NodeID) |
|
|
|
ninfo.LastMulticast = time.Now().Unix() |
|
|
|
nodeList.PushBack(ninfo) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func isNodeExpired(nodeInfo *nodeInfo, timeout int) bool { |
|
|
|
diff := time.Now().Unix() - nodeInfo.lastMulticast |
|
|
|
diff := time.Now().Unix() - nodeInfo.LastMulticast |
|
|
|
return diff > int64(timeout) |
|
|
|
} |
|
|
|
|
|
|
@ -202,7 +207,7 @@ func parseAnnouncePacket(size int, addr *net.UDPAddr, packet []byte) (*nodeInfo, |
|
|
|
} |
|
|
|
|
|
|
|
if string(packet[len(header):len(header)+1]) != nodeAnnounceCommand[0:] { |
|
|
|
return nil, fmt.Errorf("Command different than NODE_ANNOUNCE_COMMAND") |
|
|
|
return nil, fmt.Errorf("Command different than nodeAnnounceCommand") |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Println("Packet command is nodeAnnounceCommand") |
|
|
@ -213,8 +218,8 @@ func parseAnnouncePacket(size int, addr *net.UDPAddr, packet []byte) (*nodeInfo, |
|
|
|
nodeInfo := &nodeInfo{} |
|
|
|
|
|
|
|
err := json.Unmarshal([]byte(payload), nodeInfo) |
|
|
|
nodeInfo.addr = addr.IP.String() |
|
|
|
nodeInfo.nodeID = fmt.Sprintf("%s-%s", nodeInfo.nodeID, nodeInfo.addr) |
|
|
|
nodeInfo.Addr = addr.IP.String() |
|
|
|
nodeInfo.NodeID = fmt.Sprintf("%s-%s", nodeInfo.NodeID, nodeInfo.Addr) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
@ -249,7 +254,7 @@ func listenForNodes(nodeList *list.List) { |
|
|
|
fmt.Println(err) |
|
|
|
continue |
|
|
|
} |
|
|
|
fmt.Printf("Received multicast packet from %s Id: %s\n", udpAddr.String(), nodeInfo.nodeID) |
|
|
|
fmt.Printf("Received multicast packet from %s Id: %s\n", udpAddr.String(), nodeInfo.NodeID) |
|
|
|
|
|
|
|
go announcedNodeHandler(nodeInfo, nodeList) |
|
|
|
} |
|
|
@ -257,10 +262,10 @@ func listenForNodes(nodeList *list.List) { |
|
|
|
|
|
|
|
func (a *announcer) Start(nodeList *list.List) { |
|
|
|
nodeInfo := &nodeInfo{ |
|
|
|
nodeID: a.conf.nodeID, |
|
|
|
addr: "", |
|
|
|
webPort: a.conf.webPort, |
|
|
|
lastMulticast: 0, |
|
|
|
NodeID: a.conf.NodeID, |
|
|
|
Addr: "", |
|
|
|
WebPort: a.conf.WebPort, |
|
|
|
LastMulticast: 0, |
|
|
|
} |
|
|
|
|
|
|
|
go announceNode(nodeInfo) |
|
|
@ -277,12 +282,68 @@ func serve(conf *config) { |
|
|
|
|
|
|
|
go startAnnouncer(conf, nodeList) |
|
|
|
|
|
|
|
// TODO: next steps...
|
|
|
|
// go fileServe(conf)
|
|
|
|
// go dashboardServe(conf, nodeList)
|
|
|
|
go fileServe(conf) |
|
|
|
go dashboardServe(conf, nodeList) |
|
|
|
|
|
|
|
for { |
|
|
|
// TODO: do this context cancel trick to escape cleanly?
|
|
|
|
time.Sleep(time.Minute * 15) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func fileServe(conf *config) { |
|
|
|
fileMux := http.NewServeMux() |
|
|
|
fileMux.Handle("/", http.FileServer(http.Dir(conf.SharePath))) |
|
|
|
http.ListenAndServe(fmt.Sprintf("0.0.0.0:%v", conf.WebPort), fileMux) |
|
|
|
} |
|
|
|
|
|
|
|
func configHandler(conf *config) func(w http.ResponseWriter, r *http.Request) { |
|
|
|
return func(w http.ResponseWriter, r *http.Request) { |
|
|
|
data, err := json.Marshal(conf) |
|
|
|
if err != nil { |
|
|
|
w.WriteHeader(http.StatusInternalServerError) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
w.Header().Add("Content-Type", "application/json") |
|
|
|
w.Write(data) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func nodesHandler(nodeList *list.List) func(http.ResponseWriter, *http.Request) { |
|
|
|
return func(w http.ResponseWriter, r *http.Request) { |
|
|
|
nodes := make([]*nodeInfo, 0) |
|
|
|
for el := nodeList.Front(); el != nil; el = el.Next() { |
|
|
|
tmp := el.Value.(*nodeInfo) |
|
|
|
nodes = append(nodes, tmp) |
|
|
|
} |
|
|
|
|
|
|
|
data, err := json.Marshal(nodes) |
|
|
|
if err != nil { |
|
|
|
w.WriteHeader(http.StatusInternalServerError) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
w.Header().Add("Content-Type", "application/json") |
|
|
|
w.Write(data) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func dashboardServe(conf *config, nodeList *list.List) { |
|
|
|
dashboardMux := http.NewServeMux() |
|
|
|
dashboardMux.Handle("/", http.FileServer(assets.AssetFS())) |
|
|
|
dashboardMux.HandleFunc("/api/config", configHandler(conf)) |
|
|
|
dashboardMux.HandleFunc("/api/nodes", nodesHandler(nodeList)) |
|
|
|
|
|
|
|
// We don't want the dashboard to be public
|
|
|
|
address := "localhost" |
|
|
|
if conf.Debug { |
|
|
|
address = "0.0.0.0" |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Printf("Starting dashboard at %s:%v\n", address, conf.FilePort) |
|
|
|
err := http.ListenAndServe(fmt.Sprintf("%s:%v", address, conf.FilePort), dashboardMux) |
|
|
|
if err != nil { |
|
|
|
log.Fatal(err) |
|
|
|
} |
|
|
|
} |
|
|
|