package config import ( "fmt" "strings" ) func (c Config) Validate() error { if strings.TrimSpace(c.Database.URL) == "" { return fmt.Errorf("invalid config: database.url is required") } if len(c.Auth.Keys) == 0 && len(c.Auth.OAuth.Clients) == 0 { return fmt.Errorf("invalid config: at least one of auth.keys or auth.oauth.clients must be configured") } for i, key := range c.Auth.Keys { if strings.TrimSpace(key.ID) == "" { return fmt.Errorf("invalid config: auth.keys[%d].id is required", i) } if strings.TrimSpace(key.Value) == "" { return fmt.Errorf("invalid config: auth.keys[%d].value is required", i) } } for i, client := range c.Auth.OAuth.Clients { if strings.TrimSpace(client.ClientID) == "" { return fmt.Errorf("invalid config: auth.oauth.clients[%d].client_id is required", i) } if strings.TrimSpace(client.ClientSecret) == "" { return fmt.Errorf("invalid config: auth.oauth.clients[%d].client_secret is required", i) } } if strings.TrimSpace(c.MCP.Path) == "" { return fmt.Errorf("invalid config: mcp.path is required") } switch c.AI.Provider { case "litellm", "ollama", "openrouter": default: return fmt.Errorf("invalid config: unsupported ai.provider %q", c.AI.Provider) } if c.AI.Embeddings.Dimensions <= 0 { return fmt.Errorf("invalid config: ai.embeddings.dimensions must be greater than zero") } switch c.AI.Provider { case "litellm": if strings.TrimSpace(c.AI.LiteLLM.BaseURL) == "" { return fmt.Errorf("invalid config: ai.litellm.base_url is required when ai.provider=litellm") } if strings.TrimSpace(c.AI.LiteLLM.APIKey) == "" { return fmt.Errorf("invalid config: ai.litellm.api_key is required when ai.provider=litellm") } case "ollama": if strings.TrimSpace(c.AI.Ollama.BaseURL) == "" { return fmt.Errorf("invalid config: ai.ollama.base_url is required when ai.provider=ollama") } if strings.TrimSpace(c.AI.Ollama.APIKey) == "" { return fmt.Errorf("invalid config: ai.ollama.api_key is required when ai.provider=ollama") } case "openrouter": if strings.TrimSpace(c.AI.OpenRouter.BaseURL) == "" { return fmt.Errorf("invalid config: ai.openrouter.base_url is required when ai.provider=openrouter") } if strings.TrimSpace(c.AI.OpenRouter.APIKey) == "" { return fmt.Errorf("invalid config: ai.openrouter.api_key is required when ai.provider=openrouter") } } if c.Server.Port <= 0 { return fmt.Errorf("invalid config: server.port must be greater than zero") } if c.Search.DefaultLimit <= 0 { return fmt.Errorf("invalid config: search.default_limit must be greater than zero") } if c.Search.MaxLimit < c.Search.DefaultLimit { return fmt.Errorf("invalid config: search.max_limit must be greater than or equal to search.default_limit") } if strings.TrimSpace(c.Logging.Level) == "" { return fmt.Errorf("invalid config: logging.level is required") } if c.Backfill.Enabled { if c.Backfill.BatchSize <= 0 { return fmt.Errorf("invalid config: backfill.batch_size must be greater than zero when backfill is enabled") } if c.Backfill.MaxPerRun < c.Backfill.BatchSize { return fmt.Errorf("invalid config: backfill.max_per_run must be >= backfill.batch_size") } } if c.MetadataRetry.Enabled { if c.MetadataRetry.MaxPerRun <= 0 { return fmt.Errorf("invalid config: metadata_retry.max_per_run must be greater than zero when metadata_retry is enabled") } } return nil }