package config import ( "fmt" "os" "strconv" ) // Config holds all application configuration type Config struct { // API Keys TodoistAPIKey string PlanToEatAPIKey string PlanToEatSession string // Session cookie for web scraping TrelloAPIKey string TrelloToken string // Google Calendar GoogleCredentialsFile string GoogleCalendarID string // Google Tasks GoogleTasksListID string // Paths DatabasePath string MigrationDir string TemplateDir string StaticDir string // Server Port string CacheTTLMinutes int Debug bool // Display Timezone string // IANA timezone name (e.g., "Pacific/Honolulu") } // Load reads configuration from environment variables func Load() (*Config, error) { cfg := &Config{ // API Keys TodoistAPIKey: os.Getenv("TODOIST_API_KEY"), PlanToEatAPIKey: os.Getenv("PLANTOEAT_API_KEY"), PlanToEatSession: os.Getenv("PLANTOEAT_SESSION"), TrelloAPIKey: os.Getenv("TRELLO_API_KEY"), TrelloToken: os.Getenv("TRELLO_TOKEN"), // Google Calendar GoogleCredentialsFile: os.Getenv("GOOGLE_CREDENTIALS_FILE"), GoogleCalendarID: getEnvWithDefault("GOOGLE_CALENDAR_ID", "primary"), // Google Tasks GoogleTasksListID: getEnvWithDefault("GOOGLE_TASKS_LIST_ID", "@default"), // Paths DatabasePath: getEnvWithDefault("DATABASE_PATH", "./dashboard.db"), MigrationDir: getEnvWithDefault("MIGRATION_DIR", "migrations"), TemplateDir: getEnvWithDefault("TEMPLATE_DIR", "web/templates"), StaticDir: getEnvWithDefault("STATIC_DIR", "web/static"), // Server Port: getEnvWithDefault("PORT", "8080"), CacheTTLMinutes: getEnvAsInt("CACHE_TTL_MINUTES", 5), Debug: getEnvAsBool("DEBUG", false), // Display Timezone: getEnvWithDefault("TIMEZONE", "Pacific/Honolulu"), } // Validate required fields if err := cfg.Validate(); err != nil { return nil, err } return cfg, nil } // Validate checks that required configuration is present func (c *Config) Validate() error { // Require both Todoist and Trello (primary task management systems) if c.TodoistAPIKey == "" { return fmt.Errorf("TODOIST_API_KEY is required") } if c.TrelloAPIKey == "" { return fmt.Errorf("TRELLO_API_KEY is required") } if c.TrelloToken == "" { return fmt.Errorf("TRELLO_TOKEN is required") } return nil } // HasPlanToEat checks if PlanToEat is configured (API key or session) func (c *Config) HasPlanToEat() bool { return c.PlanToEatAPIKey != "" || c.PlanToEatSession != "" } // HasPlanToEatSession checks if PlanToEat session cookie is configured func (c *Config) HasPlanToEatSession() bool { return c.PlanToEatSession != "" } // HasTrello checks if Trello is configured func (c *Config) HasTrello() bool { return c.TrelloAPIKey != "" && c.TrelloToken != "" } // HasGoogleCalendar checks if Google Calendar is configured func (c *Config) HasGoogleCalendar() bool { return c.GoogleCredentialsFile != "" } // HasGoogleTasks checks if Google Tasks is configured func (c *Config) HasGoogleTasks() bool { return c.GoogleCredentialsFile != "" && c.GoogleTasksListID != "" } // getEnvWithDefault returns environment variable value or default if not set func getEnvWithDefault(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } // getEnvAsInt returns environment variable as int or default if not set or invalid func getEnvAsInt(key string, defaultValue int) int { valueStr := os.Getenv(key) if valueStr == "" { return defaultValue } value, err := strconv.Atoi(valueStr) if err != nil { return defaultValue } return value } // getEnvAsBool returns environment variable as bool or default if not set func getEnvAsBool(key string, defaultValue bool) bool { valueStr := os.Getenv(key) if valueStr == "" { return defaultValue } value, err := strconv.ParseBool(valueStr) if err != nil { return defaultValue } return value }