|
|
@@ -2,15 +2,16 @@ package broadcast
|
|
|
|
|
|
import (
|
|
|
"encoding/json"
|
|
|
+ "fmt"
|
|
|
"goseg/config"
|
|
|
"goseg/docker"
|
|
|
+ "goseg/startram"
|
|
|
"goseg/structs"
|
|
|
"log/slog"
|
|
|
"os"
|
|
|
"reflect"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
- "fmt"
|
|
|
|
|
|
"github.com/gorilla/websocket"
|
|
|
)
|
|
|
@@ -27,7 +28,7 @@ func init() {
|
|
|
config := config.Conf()
|
|
|
broadcast, err := bootstrapBroadcastState(config)
|
|
|
if err != nil {
|
|
|
- errmsg := fmt.Sprintf("Unable to initialize broadcast: %v",err)
|
|
|
+ errmsg := fmt.Sprintf("Unable to initialize broadcast: %v", err)
|
|
|
panic(errmsg)
|
|
|
}
|
|
|
broadcastState = broadcast
|
|
|
@@ -54,18 +55,24 @@ func UnregisterClient(conn *websocket.Conn) {
|
|
|
|
|
|
// take in config file and addt'l info to initialize broadcast
|
|
|
func bootstrapBroadcastState(config structs.SysConfig) (structs.AuthBroadcast, error) {
|
|
|
+ logger.Info("Bootstrapping state")
|
|
|
var res structs.AuthBroadcast
|
|
|
+ currentState := GetState()
|
|
|
+ // get a list of piers from config
|
|
|
piers := config.Piers
|
|
|
+ // this returns a map of ship:running status
|
|
|
+ logger.Info("Resolving pier status")
|
|
|
pierStatus, err := docker.GetShipStatus(piers)
|
|
|
if err != nil {
|
|
|
- errmsg := fmt.Sprintf("Unable to bootstrap urbit states: %v",err)
|
|
|
+ errmsg := fmt.Sprintf("Unable to bootstrap urbit states: %v", err)
|
|
|
logger.Error(errmsg)
|
|
|
return res, err
|
|
|
}
|
|
|
updates := make(map[string]structs.Urbit)
|
|
|
+ // convert the running status into bools
|
|
|
for pier, status := range pierStatus {
|
|
|
urbit := structs.Urbit{}
|
|
|
- if existingUrbit, exists := broadcastState.Urbits[pier]; exists {
|
|
|
+ if existingUrbit, exists := currentState.Urbits[pier]; exists {
|
|
|
// If the ship already exists in broadcastState, use its current state
|
|
|
urbit = existingUrbit
|
|
|
}
|
|
|
@@ -82,26 +89,53 @@ func bootstrapBroadcastState(config structs.SysConfig) (structs.AuthBroadcast, e
|
|
|
logger.Error(errmsg)
|
|
|
return res, err
|
|
|
}
|
|
|
+ currentState = GetState()
|
|
|
+ // get startram regions
|
|
|
+ logger.Info("Retrieving StarTram region info")
|
|
|
+ regions, err := startram.GetRegions()
|
|
|
+ if err != nil {
|
|
|
+ logger.Warn("Couldn't get StarTram regions")
|
|
|
+ } else {
|
|
|
+ updates := map[string]interface{}{
|
|
|
+ "Profile": map[string]interface{}{
|
|
|
+ "Startram": map[string]interface{}{
|
|
|
+ "Info": map[string]interface{}{
|
|
|
+ "Regions": regions,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+ err := UpdateBroadcastState(updates)
|
|
|
+ if err != nil {
|
|
|
+ errmsg := fmt.Sprintf("Error updating broadcast state:", err)
|
|
|
+ logger.Error(errmsg)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // return the boostrapped result
|
|
|
res = GetState()
|
|
|
return res, nil
|
|
|
}
|
|
|
|
|
|
// update broadcastState with a map of items
|
|
|
func UpdateBroadcastState(values map[string]interface{}) error {
|
|
|
- mu.Lock()
|
|
|
- defer mu.Unlock()
|
|
|
- v := reflect.ValueOf(&broadcastState).Elem()
|
|
|
- for key, value := range values {
|
|
|
- field := v.FieldByName(key)
|
|
|
- if !field.IsValid() || !field.CanSet() {
|
|
|
- return fmt.Errorf("field %s does not exist or is not settable", key)
|
|
|
- }
|
|
|
- if err := recursiveUpdate(field, reflect.ValueOf(value)); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- BroadcastToClients()
|
|
|
- return nil
|
|
|
+ mu.Lock()
|
|
|
+ defer mu.Unlock()
|
|
|
+ v := reflect.ValueOf(&broadcastState).Elem()
|
|
|
+ for key, value := range values {
|
|
|
+ field := v.FieldByName(key)
|
|
|
+ if !field.IsValid() || !field.CanSet() {
|
|
|
+ return fmt.Errorf("field %s does not exist or is not settable", key)
|
|
|
+ }
|
|
|
+ val := reflect.ValueOf(value)
|
|
|
+ if val.Kind() == reflect.Interface {
|
|
|
+ val = val.Elem() // Extract the underlying value from the interface
|
|
|
+ }
|
|
|
+ if err := recursiveUpdate(field, val); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ BroadcastToClients()
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
// this allows us to insert stuff into nested vals and not overwrite the existing contents
|
|
|
@@ -109,6 +143,30 @@ func recursiveUpdate(dst, src reflect.Value) error {
|
|
|
if !dst.CanSet() {
|
|
|
return fmt.Errorf("field is not settable")
|
|
|
}
|
|
|
+ // If dst is a struct and src is a map, handle them field by field
|
|
|
+ if dst.Kind() == reflect.Struct && src.Kind() == reflect.Map {
|
|
|
+ for _, key := range src.MapKeys() {
|
|
|
+ dstField := dst.FieldByName(key.String())
|
|
|
+ if !dstField.IsValid() {
|
|
|
+ return fmt.Errorf("field %s does not exist in the struct", key.String())
|
|
|
+ }
|
|
|
+ // Initialize the map if it's nil and we're trying to set a map
|
|
|
+ if dstField.Kind() == reflect.Map && dstField.IsNil() && src.MapIndex(key).Kind() == reflect.Map {
|
|
|
+ dstField.Set(reflect.MakeMap(dstField.Type()))
|
|
|
+ }
|
|
|
+ if !dstField.CanSet() {
|
|
|
+ return fmt.Errorf("field %s is not settable in the struct", key.String())
|
|
|
+ }
|
|
|
+ srcVal := src.MapIndex(key)
|
|
|
+ if srcVal.Kind() == reflect.Interface {
|
|
|
+ srcVal = srcVal.Elem()
|
|
|
+ }
|
|
|
+ if err := recursiveUpdate(dstField, srcVal); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
// If both dst and src are maps, handle them recursively
|
|
|
if dst.Kind() == reflect.Map && src.Kind() == reflect.Map {
|
|
|
for _, key := range src.MapKeys() {
|
|
|
@@ -118,18 +176,19 @@ func recursiveUpdate(dst, src reflect.Value) error {
|
|
|
if !dstVal.IsValid() {
|
|
|
dstVal = reflect.New(dst.Type().Elem()).Elem()
|
|
|
}
|
|
|
- // Recursive call to handle potential nested maps
|
|
|
+ // Recursive call to handle potential nested maps or structs
|
|
|
if err := recursiveUpdate(dstVal, srcVal); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if dst.IsNil() {
|
|
|
- dst.Set(reflect.MakeMap(dst.Type()))
|
|
|
- }
|
|
|
+ // Initialize the map if it's nil
|
|
|
+ if dst.IsNil() {
|
|
|
+ dst.Set(reflect.MakeMap(dst.Type()))
|
|
|
+ }
|
|
|
dst.SetMapIndex(key, dstVal)
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
- // For non-map fields or direct updates
|
|
|
+ // For non-map or non-struct fields, or for direct updates
|
|
|
if dst.Type() != src.Type() {
|
|
|
return fmt.Errorf("type mismatch: expected %s, got %s", dst.Type(), src.Type())
|
|
|
}
|