2022-10-15 17:58:44 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
2022-12-07 16:54:46 +00:00
|
|
|
"database/sql"
|
2022-10-15 17:58:44 +00:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
2022-12-07 16:54:46 +00:00
|
|
|
_ "github.com/mattn/go-sqlite3"
|
2022-10-15 17:58:44 +00:00
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
2022-12-04 11:50:11 +00:00
|
|
|
"time"
|
2022-10-15 17:58:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
2022-12-07 16:54:46 +00:00
|
|
|
db *sql.DB
|
2022-10-15 17:58:44 +00:00
|
|
|
HomeAssistant HomeAssistant `json:"home_assistant"`
|
|
|
|
Sensors Sensors `json:"sensors"`
|
|
|
|
Administrator Administrator `json:"administrator"`
|
|
|
|
Dashboard Dashboard `json:"dashboard"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type HomeAssistant struct {
|
2022-12-04 11:50:11 +00:00
|
|
|
BaseURL string `json:"base_url"`
|
|
|
|
ApiKey string `json:"api_key"`
|
|
|
|
InstallationDate time.Time `json:"installation_date"`
|
2022-10-15 17:58:44 +00:00
|
|
|
}
|
|
|
|
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) {
|
|
|
|
|
|
|
|
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,
|
|
|
|
})
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-12-07 16:54:46 +00:00
|
|
|
db, err := sql.Open("sqlite3", "./database.db")
|
|
|
|
if err != nil {
|
|
|
|
return Config{}, err, false
|
|
|
|
}
|
|
|
|
conf.db = db
|
|
|
|
|
|
|
|
_, 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
|
|
|
|
}
|
|
|
|
|
2022-10-15 17:58:44 +00:00
|
|
|
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
|
|
|
|
}
|