- Add complete domain layer: Note, Vault, WikiLink, Tag, Frontmatter, Graph entities - Implement repository interfaces for data access abstraction - Create comprehensive configuration system with YAML and env support - Add CLI entry point with signal handling and graceful shutdown - Fix mermaid diagram syntax in design.md (array notation) - Add CLAUDE.md for development guidance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
241 lines
5.5 KiB
Go
241 lines
5.5 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Config struct {
|
|
Server ServerConfig `yaml:"server"`
|
|
Vault VaultConfig `yaml:"vault"`
|
|
Git GitConfig `yaml:"git"`
|
|
Index IndexConfig `yaml:"index"`
|
|
Search SearchConfig `yaml:"search"`
|
|
Cache CacheConfig `yaml:"cache"`
|
|
Logging LoggingConfig `yaml:"logging"`
|
|
Performance PerformanceConfig `yaml:"performance"`
|
|
Security SecurityConfig `yaml:"security"`
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Name string `yaml:"name"`
|
|
Version string `yaml:"version"`
|
|
Transport string `yaml:"transport"` // "stdio" or "sse"
|
|
}
|
|
|
|
type VaultConfig struct {
|
|
Path string `yaml:"path"`
|
|
ExcludePaths []string `yaml:"exclude_paths"`
|
|
WatchChanges bool `yaml:"watch_changes"`
|
|
}
|
|
|
|
type GitConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
AutoPull bool `yaml:"auto_pull"`
|
|
AutoPush bool `yaml:"auto_push"`
|
|
AutoCommit bool `yaml:"auto_commit"`
|
|
CommitPrefix string `yaml:"commit_prefix"`
|
|
Remote string `yaml:"remote"`
|
|
Branch string `yaml:"branch"`
|
|
}
|
|
|
|
type IndexConfig struct {
|
|
BuildOnStartup bool `yaml:"build_on_startup"`
|
|
RebuildInterval time.Duration `yaml:"rebuild_interval"`
|
|
MaxNotesInMemory int `yaml:"max_notes_in_memory"`
|
|
}
|
|
|
|
type SearchConfig struct {
|
|
MaxResults int `yaml:"max_results"`
|
|
ContextLines int `yaml:"context_lines"`
|
|
CaseSensitive bool `yaml:"case_sensitive"`
|
|
}
|
|
|
|
type CacheConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
MaxSize int `yaml:"max_size"`
|
|
TTL time.Duration `yaml:"ttl"`
|
|
}
|
|
|
|
type LoggingConfig struct {
|
|
Level string `yaml:"level"` // debug, info, warn, error
|
|
Format string `yaml:"format"` // text or json
|
|
File string `yaml:"file"`
|
|
}
|
|
|
|
type PerformanceConfig struct {
|
|
MaxConcurrentOperations int `yaml:"max_concurrent_operations"`
|
|
ReadTimeout time.Duration `yaml:"read_timeout"`
|
|
WriteTimeout time.Duration `yaml:"write_timeout"`
|
|
}
|
|
|
|
type RateLimitConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
RequestsPerSecond int `yaml:"requests_per_second"`
|
|
Burst int `yaml:"burst"`
|
|
}
|
|
|
|
type SecurityConfig struct {
|
|
RateLimit RateLimitConfig `yaml:"rate_limit"`
|
|
MaxFileSize string `yaml:"max_file_size"`
|
|
}
|
|
|
|
func NewDefaultConfig() *Config {
|
|
return &Config{
|
|
Server: ServerConfig{
|
|
Name: "obsidian-mcp",
|
|
Version: "1.0.0",
|
|
Transport: "stdio",
|
|
},
|
|
Vault: VaultConfig{
|
|
Path: "",
|
|
ExcludePaths: []string{
|
|
".obsidian/",
|
|
".git/",
|
|
".trash/",
|
|
},
|
|
WatchChanges: true,
|
|
},
|
|
Git: GitConfig{
|
|
Enabled: true,
|
|
AutoPull: true,
|
|
AutoPush: false,
|
|
AutoCommit: true,
|
|
CommitPrefix: "[MCP]",
|
|
Remote: "origin",
|
|
Branch: "main",
|
|
},
|
|
Index: IndexConfig{
|
|
BuildOnStartup: true,
|
|
RebuildInterval: 5 * time.Minute,
|
|
MaxNotesInMemory: 10000,
|
|
},
|
|
Search: SearchConfig{
|
|
MaxResults: 50,
|
|
ContextLines: 2,
|
|
CaseSensitive: false,
|
|
},
|
|
Cache: CacheConfig{
|
|
Enabled: true,
|
|
MaxSize: 1000,
|
|
TTL: 5 * time.Minute,
|
|
},
|
|
Logging: LoggingConfig{
|
|
Level: "info",
|
|
Format: "text",
|
|
File: "",
|
|
},
|
|
Performance: PerformanceConfig{
|
|
MaxConcurrentOperations: 10,
|
|
ReadTimeout: 30 * time.Second,
|
|
WriteTimeout: 30 * time.Second,
|
|
},
|
|
Security: SecurityConfig{
|
|
RateLimit: RateLimitConfig{
|
|
Enabled: true,
|
|
RequestsPerSecond: 10,
|
|
Burst: 20,
|
|
},
|
|
MaxFileSize: "10MB",
|
|
},
|
|
}
|
|
}
|
|
|
|
func LoadConfig(path string) (*Config, error) {
|
|
config := NewDefaultConfig()
|
|
|
|
if path == "" {
|
|
return config, nil
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return config, nil
|
|
}
|
|
return nil, fmt.Errorf("failed to read config file: %w", err)
|
|
}
|
|
|
|
if err := yaml.Unmarshal(data, config); err != nil {
|
|
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
|
}
|
|
|
|
if err := config.Validate(); err != nil {
|
|
return nil, fmt.Errorf("invalid config: %w", err)
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
func LoadConfigFromEnv() (*Config, error) {
|
|
config := NewDefaultConfig()
|
|
|
|
if vaultPath := os.Getenv("VAULT_PATH"); vaultPath != "" {
|
|
config.Vault.Path = vaultPath
|
|
}
|
|
|
|
if logLevel := os.Getenv("LOG_LEVEL"); logLevel != "" {
|
|
config.Logging.Level = logLevel
|
|
}
|
|
|
|
if gitEnabled := os.Getenv("GIT_ENABLED"); gitEnabled == "false" {
|
|
config.Git.Enabled = false
|
|
}
|
|
|
|
if gitAutoPush := os.Getenv("GIT_AUTO_PUSH"); gitAutoPush == "true" {
|
|
config.Git.AutoPush = true
|
|
}
|
|
|
|
if cacheEnabled := os.Getenv("CACHE_ENABLED"); cacheEnabled == "false" {
|
|
config.Cache.Enabled = false
|
|
}
|
|
|
|
return config, config.Validate()
|
|
}
|
|
|
|
func (c *Config) Validate() error {
|
|
if c.Vault.Path == "" {
|
|
return fmt.Errorf("vault path is required")
|
|
}
|
|
|
|
if c.Server.Transport != "stdio" && c.Server.Transport != "sse" {
|
|
return fmt.Errorf("invalid transport: %s", c.Server.Transport)
|
|
}
|
|
|
|
if c.Search.MaxResults <= 0 {
|
|
return fmt.Errorf("max results must be positive")
|
|
}
|
|
|
|
if c.Cache.MaxSize <= 0 {
|
|
return fmt.Errorf("cache max size must be positive")
|
|
}
|
|
|
|
validLogLevels := map[string]bool{
|
|
"debug": true,
|
|
"info": true,
|
|
"warn": true,
|
|
"error": true,
|
|
}
|
|
|
|
if !validLogLevels[c.Logging.Level] {
|
|
return fmt.Errorf("invalid log level: %s", c.Logging.Level)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) SaveToFile(path string) error {
|
|
data, err := yaml.Marshal(c)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal config: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(path, data, 0644); err != nil {
|
|
return fmt.Errorf("failed to write config file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
} |