package main import ( "crypto/sha256" "database/sql" "encoding/json" "errors" "fmt" "os" "reflect" "regexp" "strings" "time" "github.com/gofiber/fiber/v2" _ "github.com/mattn/go-sqlite3" ) type Config struct { db *sql.DB HomeAssistant HomeAssistant `json:"home_assistant"` Sensors Sensors `json:"sensors"` Administrator Administrator `json:"administrator"` Dashboard Dashboard `json:"dashboard"` } type HomeAssistant struct { InstallationDate time.Time `json:"installation_date"` BaseURL string `json:"base_url"` APIKey string `json:"api_key"` } type Sensors struct { PolledSmartEnergySummation string `json:"polled_smart_energy_summation"` FossilPercentage string `json:"fossil_percentage"` } type Administrator struct { Username string `json:"username"` PasswordHash string `json:"password_hash"` } type Dashboard struct { Name string `json:"name"` Theme string `json:"theme"` FooterLinks []Link `json:"footer_links"` HeaderLinks []Link `json:"header_links"` } var errBadHAFormat = errors.New("HomeAssistant base URL is badly formatted") func formatURL(url string) (string, error) { // the URL we want is: protocol://hostname[:port] without a final / if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { url = "http://" + url } url = strings.TrimSuffix(url, "/") test := regexp.MustCompile(`(?m)https?://[^/]*`).ReplaceAllString(url, "") if test != "" { return "", errBadHAFormat } return url, nil } func loadConfig() (config *Config, isFirstRun bool, err error) { db, err := sql.Open("sqlite3", "./database.db") if err != nil { return &Config{}, false, err } _, err = db.Exec(`CREATE TABLE IF NOT EXISTS "cache" ( "time" NUMERIC NOT NULL, "green_energy_percentage" REAL NOT NULL, "energy_consumption" REAL NOT NULL, PRIMARY KEY("time") );`) if err != nil { return &Config{}, false, err } defaultConfig := &Config{} defaultConfig.Dashboard.Theme = "default" defaultConfig.Dashboard.Name = "EcoDash" defaultConfig.Dashboard.HeaderLinks = append(defaultConfig.Dashboard.HeaderLinks, Link{ Label: "Admin", Destination: "/admin", }, Link{ Label: "EcoDash", Destination: "https://ecodash.xyz", NewTab: true, Primary: true, }) defaultConfig.db = db data, err := os.ReadFile("config.json") if err != nil { // if the data file doesn't exist, we consider it a first run if os.IsNotExist(err) { return defaultConfig, true, nil } return &Config{}, false, err } // if the data file is empty, we consider it as a first run if len(data) == 0 { return defaultConfig, true, nil } conf := &Config{} err = json.Unmarshal(data, &conf) if err != nil { return &Config{}, false, err } conf.db = db return conf, false, nil } // just a little utility function to SHA256 strings (for hashing passwords). func hash(toHash string) string { return fmt.Sprintf("%x", sha256.Sum256([]byte(toHash))) } func (config *Config) isAuthorized(c *fiber.Ctx) bool { if config.Administrator.PasswordHash == "" { return true } return c.Cookies("admin_username") == config.Administrator.Username && c.Cookies("admin_password_hash") == config.Administrator.PasswordHash } func (config *Config) Equals(c *Config) bool { return reflect.DeepEqual(c.HomeAssistant, config.HomeAssistant) && reflect.DeepEqual(c.Sensors, config.Sensors) && reflect.DeepEqual(c.Administrator, config.Administrator) && reflect.DeepEqual(c.Dashboard, config.Dashboard) }