Files
insertr/internal/content/site_manager.go
Joakim 01b921bfa3 Refactor database layer to eliminate type switching and simplify architecture
- Replace type switching with clean repository pattern using sqlc-generated code
- Move ContentRepository interface and domain models to db package
- Create separate SQLiteRepository and PostgreSQLRepository implementations
- Remove unnecessary RepositoryAdapter and ContentClient interface duplication
- Update all clients (HTTP, Mock) to implement db.ContentRepository directly
- Add context.Context parameters to all repository methods (Go best practice)
- Eliminate duplicate domain models and type conversions
- Remove type aliases - use db package types directly throughout codebase
- Update engine, content managers, and API handlers to use repositories directly

Benefits:
- Zero runtime type switching overhead
- Single source of truth for domain models
- Clean package boundaries and separation of concerns
- Standard Go interface patterns with context support
- Easier testing with mockable repository interface
- Maintainable: adding new database types requires no changes to existing code
2025-10-08 19:34:21 +02:00

231 lines
6.4 KiB
Go

package content
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sync"
"github.com/insertr/insertr/internal/config"
"github.com/insertr/insertr/internal/db"
"github.com/insertr/insertr/internal/engine"
)
// Type alias for backward compatibility
type SiteConfig = config.SiteConfig
// SiteManager handles registration and enhancement of static sites
type SiteManager struct {
sites map[string]*SiteConfig
enhancer *Enhancer
mutex sync.RWMutex
devMode bool
contentClient db.ContentRepository
authProvider *engine.AuthProvider
}
// NewSiteManager creates a new site manager
func NewSiteManager(contentClient db.ContentRepository, devMode bool) *SiteManager {
return &SiteManager{
sites: make(map[string]*SiteConfig),
enhancer: NewDefaultEnhancer(contentClient, ""), // siteID will be set per operation
devMode: devMode,
contentClient: contentClient,
authProvider: &engine.AuthProvider{Type: "mock"}, // default
}
}
// NewSiteManagerWithAuth creates a new site manager with auth provider
func NewSiteManagerWithAuth(contentClient db.ContentRepository, devMode bool, authProvider *engine.AuthProvider) *SiteManager {
if authProvider == nil {
authProvider = &engine.AuthProvider{Type: "mock"}
}
return &SiteManager{
sites: make(map[string]*SiteConfig),
contentClient: contentClient,
authProvider: authProvider,
devMode: devMode,
}
}
// RegisterSite adds a site to the manager
func (sm *SiteManager) RegisterSite(config *SiteConfig) error {
sm.mutex.Lock()
defer sm.mutex.Unlock()
// Validate site configuration
if config.SiteID == "" {
return fmt.Errorf("site_id is required")
}
if config.Path == "" {
return fmt.Errorf("path is required for site %s", config.SiteID)
}
// Check if path exists, auto-create enhancement directories
if _, err := os.Stat(config.Path); os.IsNotExist(err) {
// Auto-create directory if it appears to be an enhancement target
if strings.HasSuffix(config.Path, "_enhanced") {
log.Printf("📁 Creating enhancement directory: %s", config.Path)
if err := os.MkdirAll(config.Path, 0755); err != nil {
return fmt.Errorf("failed to create enhancement directory %s: %w", config.Path, err)
}
} else {
return fmt.Errorf("site path does not exist: %s", config.Path)
}
}
// Convert to absolute path
absPath, err := filepath.Abs(config.Path)
if err != nil {
return fmt.Errorf("failed to resolve absolute path for %s: %w", config.Path, err)
}
config.Path = absPath
sm.sites[config.SiteID] = config
log.Printf("📁 Registered site %s at %s", config.SiteID, config.Path)
return nil
}
// RegisterSites bulk registers multiple sites from configuration
func (sm *SiteManager) RegisterSites(configs []*SiteConfig) error {
for _, config := range configs {
if err := sm.RegisterSite(config); err != nil {
return fmt.Errorf("failed to register site %s: %w", config.SiteID, err)
}
}
return nil
}
// GetSite returns a registered site configuration
func (sm *SiteManager) GetSite(siteID string) (*SiteConfig, bool) {
sm.mutex.RLock()
defer sm.mutex.RUnlock()
site, exists := sm.sites[siteID]
return site, exists
}
// GetAllSites returns all registered sites
func (sm *SiteManager) GetAllSites() map[string]*SiteConfig {
sm.mutex.RLock()
defer sm.mutex.RUnlock()
// Return a copy to prevent external modification
result := make(map[string]*SiteConfig)
for id, site := range sm.sites {
result[id] = site
}
return result
}
// IsAutoEnhanceEnabled checks if a site has auto-enhancement enabled
func (sm *SiteManager) IsAutoEnhanceEnabled(siteID string) bool {
sm.mutex.RLock()
defer sm.mutex.RUnlock()
site, exists := sm.sites[siteID]
return exists && site.AutoEnhance
}
// EnhanceSite performs enhancement from source to output directory
func (sm *SiteManager) EnhanceSite(siteID string) error {
sm.mutex.RLock()
site, exists := sm.sites[siteID]
sm.mutex.RUnlock()
if !exists {
return fmt.Errorf("site %s is not registered", siteID)
}
// Use source path if available, otherwise use main path (for backwards compatibility)
sourcePath := site.SourcePath
if sourcePath == "" {
sourcePath = site.Path
}
outputPath := site.Path
log.Printf("🔄 Enhancing site %s from %s to %s", siteID, sourcePath, outputPath)
// Create output directory if it doesn't exist
if err := os.MkdirAll(outputPath, 0755); err != nil {
return fmt.Errorf("failed to create output directory %s: %w", outputPath, err)
}
// Create enhancer with auth provider for this operation
// Discovery disabled by default - developers should explicitly mark elements with class="insertr"
discoveryConfig := DiscoveryConfig{
Enabled: false, // Changed from true - respect developer intent
Aggressive: false,
Containers: true,
Individual: true,
}
// Override with site-specific discovery config if provided
if site.Discovery != nil {
discoveryConfig = *site.Discovery
log.Printf("🔧 Using site-specific discovery config for %s: enabled=%v, aggressive=%v",
siteID, discoveryConfig.Enabled, discoveryConfig.Aggressive)
}
config := EnhancementConfig{
Discovery: discoveryConfig,
ContentInjection: true,
GenerateIDs: true,
}
enhancer := NewEnhancerWithAuth(sm.contentClient, siteID, config, sm.authProvider)
// Perform enhancement from source to output
if err := enhancer.EnhanceDirectory(sourcePath, outputPath); err != nil {
return fmt.Errorf("failed to enhance site %s: %w", siteID, err)
}
log.Printf("✅ Successfully enhanced site %s", siteID)
return nil
}
// EnhanceAllSites enhances all registered sites that have auto-enhancement enabled
func (sm *SiteManager) EnhanceAllSites() error {
sm.mutex.RLock()
sites := make([]*SiteConfig, 0, len(sm.sites))
for _, site := range sm.sites {
if site.AutoEnhance {
sites = append(sites, site)
}
}
sm.mutex.RUnlock()
var errors []error
for _, site := range sites {
if err := sm.EnhanceSite(site.SiteID); err != nil {
errors = append(errors, err)
}
}
if len(errors) > 0 {
return fmt.Errorf("enhancement failed for some sites: %v", errors)
}
return nil
}
// GetStats returns statistics about registered sites
func (sm *SiteManager) GetStats() map[string]interface{} {
sm.mutex.RLock()
defer sm.mutex.RUnlock()
autoEnhanceCount := 0
for _, site := range sm.sites {
if site.AutoEnhance {
autoEnhanceCount++
}
}
return map[string]interface{}{
"total_sites": len(sm.sites),
"auto_enhance_sites": autoEnhanceCount,
}
}