Some checks failed
ci/woodpecker/manual/woodpecker Pipeline failed
- Now the cache isn't cleared and fetched from zero each update, but only when there's missing information for a day - Fixed a bug that prevented new users from saving changes in the config NOTE: Turns out that HomeAssistant's API only returns data for the last 10 days. Looks like we will have to improve the caching system to work around this.
132 lines
3.2 KiB
Go
132 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/gofiber/fiber/v2"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
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 {
|
|
BaseURL string `json:"base_url"`
|
|
ApiKey string `json:"api_key"`
|
|
InstallationDate time.Time `json:"installation_date"`
|
|
}
|
|
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"`
|
|
}
|
|
|
|
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
|
|
}
|
|
if strings.HasSuffix(url, "/") {
|
|
url = url[0 : len(url)-1]
|
|
}
|
|
|
|
test := regexp.MustCompile(`(?m)https?:\/\/[^/]*`).ReplaceAllString(url, "")
|
|
if test != "" {
|
|
return "", errors.New("HomeAssistant base URL is badly formatted")
|
|
}
|
|
|
|
return url, nil
|
|
|
|
}
|
|
|
|
func loadConfig() (config Config, err error, isFirstRun bool) {
|
|
|
|
db, err := sql.Open("sqlite3", "./database.db")
|
|
if err != nil {
|
|
return Config{}, err, false
|
|
}
|
|
|
|
_, 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{}, err, false
|
|
}
|
|
|
|
var defaultConfig = Config{}
|
|
defaultConfig.Dashboard.Theme = "default"
|
|
defaultConfig.Dashboard.Name = "EcoDash"
|
|
defaultConfig.Dashboard.HeaderLinks = append(defaultConfig.Dashboard.HeaderLinks, Link{
|
|
Label: "Admin",
|
|
Destination: "/admin",
|
|
}, Link{
|
|
Label: "Docs",
|
|
Destination: "https://gitea.massivebox.net/massivebox/ecodash",
|
|
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, nil, true
|
|
}
|
|
return Config{}, err, false
|
|
}
|
|
|
|
// if the data file is empty, we consider it as a first run
|
|
if string(data) == "" {
|
|
return defaultConfig, nil, true
|
|
}
|
|
|
|
var conf Config
|
|
err = json.Unmarshal(data, &conf)
|
|
if err != nil {
|
|
return Config{}, err, false
|
|
}
|
|
conf.db = db
|
|
|
|
return conf, nil, false
|
|
|
|
}
|
|
|
|
// 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
|
|
}
|