NextNVR/config.go

132 lines
3.8 KiB
Go

// NextNVR v0.1.0 — Configuration management
// Handles config.yaml parsing with sensible defaults
package main
import (
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
// Config represents the top-level application configuration.
type Config struct {
Server ServerConfig `yaml:"server" json:"server"`
Storage StorageConfig `yaml:"storage" json:"storage"`
Cameras []CameraConfig `yaml:"cameras" json:"cameras"`
Go2RTC Go2RTCConfig `yaml:"go2rtc" json:"go2rtc"`
}
// ServerConfig holds HTTP server settings.
type ServerConfig struct {
Port string `yaml:"port" json:"port"`
BindHost string `yaml:"bind_host" json:"bind_host"`
}
// StorageConfig holds recording and retention settings.
type StorageConfig struct {
RecordingsPath string `yaml:"recordings_path" json:"recordings_path"`
RetentionDays int `yaml:"retention_days" json:"retention_days"`
CleanupIntervalMins int `yaml:"cleanup_interval_mins" json:"cleanup_interval_mins"`
}
// CameraConfig represents a single camera's configuration.
type CameraConfig struct {
ID string `yaml:"id" json:"id"`
Name string `yaml:"name" json:"name"`
IP string `yaml:"ip" json:"ip"`
Username string `yaml:"username" json:"username"`
Password string `yaml:"password" json:"password"`
ONVIFPort int `yaml:"onvif_port" json:"onvif_port,string"`
RTSPMain string `yaml:"rtsp_main" json:"rtsp_main"`
RTSPSub string `yaml:"rtsp_sub" json:"rtsp_sub"`
Description string `yaml:"description" json:"description"`
Enabled bool `yaml:"enabled" json:"enabled"`
Record bool `yaml:"record" json:"record"`
}
// Go2RTCConfig holds go2rtc child process settings.
type Go2RTCConfig struct {
Enabled bool `yaml:"enabled" json:"enabled"`
Port string `yaml:"port" json:"port"`
Binary string `yaml:"binary" json:"binary"`
}
// APIResponse wraps all JSON API responses.
type APIResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
// CameraStatus extends CameraConfig with runtime information.
type CameraStatus struct {
CameraConfig
Online bool `json:"online"`
Uptime string `json:"uptime"`
Streams int `json:"streams"`
}
// DefaultConfig returns a Config with sensible defaults.
func DefaultConfig() Config {
return Config{
Server: ServerConfig{
Port: ":8080",
BindHost: "0.0.0.0",
},
Storage: StorageConfig{
RecordingsPath: "/mnt/recordings",
RetentionDays: 7,
CleanupIntervalMins: 60,
},
Go2RTC: Go2RTCConfig{
Enabled: true,
Port: ":1984",
Binary: "go2rtc",
},
}
}
// LoadConfig reads and parses a YAML configuration file.
// If the file does not exist, it returns DefaultConfig().
func LoadConfig(path string) (Config, error) {
cfg := DefaultConfig()
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return cfg, fmt.Errorf("config file not found: %s", path)
}
return cfg, fmt.Errorf("reading config: %w", err)
}
if err := yaml.Unmarshal(data, &cfg); err != nil {
return cfg, fmt.Errorf("parsing config: %w", err)
}
// Apply path defaults relative to config file location.
configDir := filepath.Dir(path)
if cfg.Storage.RecordingsPath == "" {
cfg.Storage.RecordingsPath = "/mnt/recordings"
}
_ = configDir // reserved for relative path resolution
return cfg, nil
}
// SaveConfig writes the configuration to a YAML file.
func SaveConfig(cfg Config, path string) error {
data, err := yaml.Marshal(&cfg)
if err != nil {
return fmt.Errorf("marshaling config: %w", err)
}
header := []byte("# NextNVR Configuration\n# Generated by NextNVR setup\n\n")
out := append(header, data...)
if err := os.WriteFile(path, out, 0644); err != nil {
return fmt.Errorf("writing config: %w", err)
}
return nil
}