145 lines
3.4 KiB
Go
145 lines
3.4 KiB
Go
// Package config handles loading and validation of application configuration.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
// Config holds all application configuration.
|
|
type Config struct {
|
|
Server ServerConfig
|
|
Calendar CalendarConfig
|
|
Diary DiaryConfig
|
|
CalDAV CalDAVConfig
|
|
Photos PhotosConfig
|
|
}
|
|
|
|
// ServerConfig holds HTTP server settings.
|
|
type ServerConfig struct {
|
|
Port int
|
|
}
|
|
|
|
// CalendarConfig holds calendar ICS file settings.
|
|
type CalendarConfig struct {
|
|
LocalICS string
|
|
}
|
|
|
|
// DiaryConfig holds diary ICS file settings.
|
|
type DiaryConfig struct {
|
|
ICSFile string
|
|
}
|
|
|
|
// CalDAVConfig holds CalDAV remote calendar settings.
|
|
type CalDAVConfig struct {
|
|
Enabled bool
|
|
URL string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
// PhotosConfig holds photo storage settings.
|
|
type PhotosConfig struct {
|
|
RootFolder string
|
|
}
|
|
|
|
// Load reads configuration from the specified INI file.
|
|
func Load(path string) (*Config, error) {
|
|
cfg, err := ini.Load(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load config file: %w", err)
|
|
}
|
|
|
|
config := &Config{
|
|
Server: ServerConfig{
|
|
Port: cfg.Section("server").Key("port").MustInt(8080),
|
|
},
|
|
Calendar: CalendarConfig{
|
|
LocalICS: cfg.Section("calendar").Key("local_ics").String(),
|
|
},
|
|
Diary: DiaryConfig{
|
|
ICSFile: cfg.Section("diary").Key("ics_file").String(),
|
|
},
|
|
CalDAV: CalDAVConfig{
|
|
Enabled: cfg.Section("caldav").Key("enabled").MustBool(false),
|
|
URL: cfg.Section("caldav").Key("url").String(),
|
|
Username: cfg.Section("caldav").Key("username").String(),
|
|
Password: cfg.Section("caldav").Key("password").String(),
|
|
},
|
|
Photos: PhotosConfig{
|
|
RootFolder: cfg.Section("photos").Key("root_folder").String(),
|
|
},
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// Validate checks that all required configuration values are set and valid.
|
|
func (c *Config) Validate() error {
|
|
if c.Server.Port <= 0 || c.Server.Port > 65535 {
|
|
return fmt.Errorf("invalid server port: %d", c.Server.Port)
|
|
}
|
|
|
|
// Validate calendar ICS path if specified
|
|
if c.Calendar.LocalICS != "" {
|
|
if err := validateFileReadable(c.Calendar.LocalICS); err != nil {
|
|
return fmt.Errorf("calendar ICS file: %w", err)
|
|
}
|
|
}
|
|
|
|
// Validate diary ICS path if specified
|
|
if c.Diary.ICSFile != "" {
|
|
dir := filepath.Dir(c.Diary.ICSFile)
|
|
if err := validateDirExists(dir); err != nil {
|
|
return fmt.Errorf("diary ICS directory: %w", err)
|
|
}
|
|
}
|
|
|
|
// Validate CalDAV settings if enabled
|
|
if c.CalDAV.Enabled {
|
|
if c.CalDAV.URL == "" {
|
|
return fmt.Errorf("caldav URL is required when caldav is enabled")
|
|
}
|
|
}
|
|
|
|
// Validate photos folder if specified
|
|
if c.Photos.RootFolder != "" {
|
|
if err := validateDirExists(c.Photos.RootFolder); err != nil {
|
|
return fmt.Errorf("photos root folder: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateFileReadable(path string) error {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("file does not exist: %s", path)
|
|
}
|
|
return fmt.Errorf("cannot access file: %w", err)
|
|
}
|
|
if info.IsDir() {
|
|
return fmt.Errorf("path is a directory, not a file: %s", path)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateDirExists(path string) error {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("directory does not exist: %s", path)
|
|
}
|
|
return fmt.Errorf("cannot access directory: %w", err)
|
|
}
|
|
if !info.IsDir() {
|
|
return fmt.Errorf("path is not a directory: %s", path)
|
|
}
|
|
return nil
|
|
}
|