diff --git a/beszel/go.mod b/beszel/go.mod index 6d86a5f..1ab1dc8 100644 --- a/beszel/go.mod +++ b/beszel/go.mod @@ -15,6 +15,7 @@ require ( github.com/spf13/cast v1.7.0 github.com/spf13/cobra v1.8.1 golang.org/x/crypto v0.28.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( diff --git a/beszel/go.sum b/beszel/go.sum index 5b6f6d3..b6c45cc 100644 --- a/beszel/go.sum +++ b/beszel/go.sum @@ -398,6 +398,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/beszel/internal/hub/config.go b/beszel/internal/hub/config.go new file mode 100644 index 0000000..c62e777 --- /dev/null +++ b/beszel/internal/hub/config.go @@ -0,0 +1,135 @@ +package hub + +import ( + "beszel/internal/entities/system" + "fmt" + "log" + "os" + "path/filepath" + + "github.com/pocketbase/pocketbase/models" + "gopkg.in/yaml.v3" +) + +type Config struct { + Systems []SystemConfig `yaml:"systems"` +} + +type SystemConfig struct { + Name string `yaml:"name"` + Host string `yaml:"host"` + Port string `yaml:"port"` + Users []string `yaml:"users"` +} + +// Syncs systems with the config.yml file +func (h *Hub) syncSystemsWithConfig() error { + configPath := filepath.Join(h.app.DataDir(), "config.yml") + configData, err := os.ReadFile(configPath) + if err != nil { + return nil + } + + var config Config + err = yaml.Unmarshal(configData, &config) + if err != nil { + return fmt.Errorf("failed to parse config.yml: %v", err) + } + + if len(config.Systems) == 0 { + log.Println("No systems defined in config.yml.") + return nil + } + + var firstUser *models.Record + + // Create a map of email to user ID + userEmailToID := make(map[string]string) + users, err := h.app.Dao().FindRecordsByFilter("users", "id != ''", "created", -1, 0) + if err != nil { + return err + } + if len(users) > 0 { + firstUser = users[0] + for _, user := range users { + userEmailToID[user.GetString("email")] = user.Id + } + } + + // add default settings for systems if not defined in config + for i := range config.Systems { + system := &config.Systems[i] + if system.Port == "" { + system.Port = "45876" + } + if len(users) > 0 && len(system.Users) == 0 { + // default to first user if none are defined + system.Users = []string{firstUser.Id} + } else { + // Convert email addresses to user IDs + userIDs := make([]string, 0, len(system.Users)) + for _, email := range system.Users { + if id, ok := userEmailToID[email]; ok { + userIDs = append(userIDs, id) + } else { + log.Printf("User %s not found", email) + } + } + system.Users = userIDs + } + } + + // Get existing systems + existingSystems, err := h.app.Dao().FindRecordsByFilter("systems", "id != ''", "", -1, 0) + if err != nil { + return err + } + + // Create a map of existing systems for easy lookup + existingSystemsMap := make(map[string]*models.Record) + for _, system := range existingSystems { + key := system.GetString("host") + ":" + system.GetString("port") + existingSystemsMap[key] = system + } + + // Process systems from config + for _, sysConfig := range config.Systems { + key := sysConfig.Host + ":" + sysConfig.Port + if existingSystem, ok := existingSystemsMap[key]; ok { + // Update existing system + existingSystem.Set("name", sysConfig.Name) + existingSystem.Set("users", sysConfig.Users) + existingSystem.Set("port", sysConfig.Port) + if err := h.app.Dao().SaveRecord(existingSystem); err != nil { + return err + } + delete(existingSystemsMap, key) + } else { + // Create new system + systemsCollection, err := h.app.Dao().FindCollectionByNameOrId("systems") + if err != nil { + return fmt.Errorf("failed to find systems collection: %v", err) + } + newSystem := models.NewRecord(systemsCollection) + newSystem.Set("name", sysConfig.Name) + newSystem.Set("host", sysConfig.Host) + newSystem.Set("port", sysConfig.Port) + newSystem.Set("users", sysConfig.Users) + newSystem.Set("info", system.Info{}) + newSystem.Set("status", "pending") + if err := h.app.Dao().SaveRecord(newSystem); err != nil { + return fmt.Errorf("failed to create new system: %v", err) + } + } + } + + // Delete systems not in config + for _, system := range existingSystemsMap { + if err := h.app.Dao().DeleteRecord(system); err != nil { + return err + } + } + + log.Println("Systems synced with config.yml") + return nil +} diff --git a/beszel/internal/hub/hub.go b/beszel/internal/hub/hub.go index 4871f59..cabc0df 100644 --- a/beszel/internal/hub/hub.go +++ b/beszel/internal/hub/hub.go @@ -56,14 +56,10 @@ func NewHub(app *pocketbase.PocketBase) *Hub { } func (h *Hub) Run() { - // rm := records.NewRecordManager(h.app) - // am := alerts.NewAlertManager(h.app) - // um := users.NewUserManager(h.app) - // loosely check if it was executed using "go run" isGoRun := strings.HasPrefix(os.Args[0], os.TempDir()) - // // enable auto creation of migration files when making collection changes in the Admin UI + // enable auto creation of migration files when making collection changes in the Admin UI migratecmd.MustRegister(h.app, h.app.RootCmd, migratecmd.Config{ // (the isGoRun check is to enable it only during development) Automigrate: isGoRun, @@ -93,7 +89,8 @@ func (h *Hub) Run() { if err := h.app.Dao().SaveCollection(usersCollection); err != nil { return err } - return nil + // sync systems with config + return h.syncSystemsWithConfig() }) // serve web ui diff --git a/beszel/internal/records/records.go b/beszel/internal/records/records.go index 788c4a8..f097339 100644 --- a/beszel/internal/records/records.go +++ b/beszel/internal/records/records.go @@ -112,14 +112,6 @@ func (rm *RecordManager) CreateLongerRecords() { // get shorter records from the past x minutes var stats RecordStats - // allShorterRecords, err := txDao.FindRecordsByExpr( - // collection, - // dbx.NewExp( - // "type = {:type} AND system = {:system} AND created > {:created}", - // dbx.Params{"type": recordData.shorterType, "system": system.Id, "created": shorterRecordPeriod}, - // ), - // ) - err := txDao.DB(). Select("stats"). From(collection.Name).