package config import ( "fmt" "os" "strings" "gopkg.in/yaml.v3" ) // Config represents the application configuration type Config struct { WikiIndex string `yaml:"wiki_index"` BlogIndex string `yaml:"blog_index"` RepoAnnouncement string `yaml:"repo_announcement"` // naddr for kind 30617 repo announcement Relays struct { Feeds string `yaml:"feeds"` // Single relay for feeds Profiles string `yaml:"profiles"` // Comma-separated list of relays for profiles and deletion events ContactForm string `yaml:"contactform"` // Comma-separated list of relays for contact form } `yaml:"relays"` LinkBaseURL string `yaml:"link_base_url"` Cache struct { RefreshIntervalMinutes int `yaml:"refresh_interval_minutes"` } `yaml:"cache"` Feed struct { Relay string `yaml:"relay"` PollIntervalMinutes int `yaml:"poll_interval_minutes"` MaxEvents int `yaml:"max_events"` } `yaml:"feed"` Server struct { Port int `yaml:"port"` EnableCompression bool `yaml:"enable_compression"` } `yaml:"server"` SEO struct { SiteName string `yaml:"site_name"` SiteURL string `yaml:"site_url"` DefaultImage string `yaml:"default_image"` } `yaml:"seo"` } // LoadConfig loads configuration from a YAML file func LoadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read config file: %w", err) } var config Config if err := yaml.Unmarshal(data, &config); err != nil { return nil, fmt.Errorf("failed to parse config file: %w", err) } // Set defaults if config.Relays.Feeds == "" { config.Relays.Feeds = "wss://theforest.nostr1.com" } if config.Relays.Profiles == "" { config.Relays.Profiles = "wss://theforest.nostr1.com,wss://nostr.land,wss://thecitadel.nostr1.com" } if config.Relays.ContactForm == "" { config.Relays.ContactForm = "wss://thecitadel.nostr1.com,wss://relay.damus.io,wss://freelay.sovbit.host,wss://relay.primal.net" } if config.LinkBaseURL == "" { config.LinkBaseURL = "https://alexandria.gitcitadel.eu" } if config.Cache.RefreshIntervalMinutes == 0 { config.Cache.RefreshIntervalMinutes = 30 } if config.Feed.Relay == "" { config.Feed.Relay = config.Relays.Feeds } if config.Feed.PollIntervalMinutes == 0 { config.Feed.PollIntervalMinutes = 5 } if config.Feed.MaxEvents == 0 { config.Feed.MaxEvents = 30 } if config.Server.Port == 0 { config.Server.Port = 8080 } if config.SEO.SiteName == "" { config.SEO.SiteName = "GitCitadel" } if config.SEO.SiteURL == "" { config.SEO.SiteURL = "https://gitcitadel.com" } if config.SEO.DefaultImage == "" { config.SEO.DefaultImage = "/static/GitCitadel_Graphic_Landscape.png" } // Validate required fields if config.WikiIndex == "" { return nil, fmt.Errorf("wiki_index is required") } return &config, nil } // parseRelayList parses a comma-separated relay string into a slice func parseRelayList(relayStr string) []string { if relayStr == "" { return []string{} } relays := strings.Split(relayStr, ",") result := make([]string, 0, len(relays)) for _, relay := range relays { relay = strings.TrimSpace(relay) // Remove quotes if present relay = strings.Trim(relay, "\"'") if relay != "" { result = append(result, relay) } } return result } // GetProfilesRelays parses the comma-separated profiles relay string into a slice func (c *Config) GetProfilesRelays() []string { return parseRelayList(c.Relays.Profiles) } // GetContactFormRelays parses the comma-separated contact form relay string into a slice func (c *Config) GetContactFormRelays() []string { return parseRelayList(c.Relays.ContactForm) } // Validate validates the configuration func (c *Config) Validate() error { if c.WikiIndex == "" { return fmt.Errorf("wiki_index is required") } if c.Relays.Feeds == "" { return fmt.Errorf("relays.feeds is required") } return nil }