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
This commit is contained in:
2025-10-08 19:34:21 +02:00
parent 38c2897ece
commit 01b921bfa3
16 changed files with 785 additions and 712 deletions

View File

@@ -1,9 +1,11 @@
package engine
import (
"context"
"fmt"
"strings"
"github.com/insertr/insertr/internal/db"
"golang.org/x/net/html"
)
@@ -15,13 +17,13 @@ type AuthProvider struct {
// ContentEngine is the unified content processing engine
type ContentEngine struct {
idGenerator *IDGenerator
client ContentClient
client db.ContentRepository
authProvider *AuthProvider
injector *Injector
}
// NewContentEngine creates a new content processing engine
func NewContentEngine(client ContentClient) *ContentEngine {
func NewContentEngine(client db.ContentRepository) *ContentEngine {
authProvider := &AuthProvider{Type: "mock"} // default
return &ContentEngine{
idGenerator: NewIDGenerator(),
@@ -32,7 +34,7 @@ func NewContentEngine(client ContentClient) *ContentEngine {
}
// NewContentEngineWithAuth creates a new content processing engine with auth config
func NewContentEngineWithAuth(client ContentClient, authProvider *AuthProvider) *ContentEngine {
func NewContentEngineWithAuth(client db.ContentRepository, authProvider *AuthProvider) *ContentEngine {
if authProvider == nil {
authProvider = &AuthProvider{Type: "mock"}
}
@@ -64,7 +66,7 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
id := e.idGenerator.Generate(elem.Node, input.FilePath)
// Database-first approach: Check if content already exists
existingContent, err := e.client.GetContent(input.SiteID, id)
existingContent, err := e.client.GetContent(context.Background(), input.SiteID, id)
contentExists := (err == nil && existingContent != nil)
generatedIDs[fmt.Sprintf("element_%d", i)] = id
@@ -87,7 +89,7 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
originalTemplate := e.extractOriginalTemplate(elem.Node)
// Store in database via content client
_, err := e.client.CreateContent(input.SiteID, id, htmlContent, originalTemplate, "system")
_, err := e.client.CreateContent(context.Background(), input.SiteID, id, htmlContent, originalTemplate, "system")
if err != nil {
// Log error but don't fail the enhancement - content just won't be stored
fmt.Printf("⚠️ Failed to store content for %s: %v\n", id, err)
@@ -343,7 +345,7 @@ func (e *ContentEngine) injectContent(elements []ProcessedElement, siteID string
elem := &elements[i]
// Try to get content from database
contentItem, err := e.client.GetContent(siteID, elem.ID)
contentItem, err := e.client.GetContent(context.Background(), siteID, elem.ID)
if err != nil {
// Content not found is not an error - element just won't have injected content
continue
@@ -467,14 +469,14 @@ func (e *ContentEngine) getPlaceholderForElement(elementType string) string {
// processCollection handles collection detection, persistence and reconstruction
func (e *ContentEngine) processCollection(collectionNode *html.Node, collectionID, siteID string) error {
// 1. Check if collection exists in database
existingCollection, err := e.client.GetCollection(siteID, collectionID)
existingCollection, err := e.client.GetCollection(context.Background(), siteID, collectionID)
collectionExists := (err == nil && existingCollection != nil)
if !collectionExists {
// 2. New collection: extract container HTML and create collection record
containerHTML := e.extractOriginalTemplate(collectionNode)
_, err := e.client.CreateCollection(siteID, collectionID, containerHTML, "system")
_, err := e.client.CreateCollection(context.Background(), siteID, collectionID, containerHTML, "system")
if err != nil {
return fmt.Errorf("failed to create collection %s: %w", collectionID, err)
}
@@ -494,7 +496,7 @@ func (e *ContentEngine) processCollection(collectionNode *html.Node, collectionI
}
// Get final item count for logging
existingItems, _ := e.client.GetCollectionItems(siteID, collectionID)
existingItems, _ := e.client.GetCollectionItems(context.Background(), siteID, collectionID)
fmt.Printf("✅ Reconstructed collection: %s from database (%d items)\n", collectionID, len(existingItems))
}
@@ -515,7 +517,7 @@ func (e *ContentEngine) extractAndStoreTemplatesAndItems(collectionNode *html.No
if len(templateElements) == 0 {
// No existing children - create a default empty template
_, err := e.client.CreateCollectionTemplate(siteID, collectionID, "default", "<div>New item</div>", true)
_, err := e.client.CreateCollectionTemplate(context.Background(), siteID, collectionID, "default", "<div>New item</div>", true)
if err != nil {
return fmt.Errorf("failed to create default template: %w", err)
}
@@ -530,7 +532,7 @@ func (e *ContentEngine) extractAndStoreTemplatesAndItems(collectionNode *html.No
templateName := fmt.Sprintf("template-%d", i+1)
isDefault := (i == 0) // First template is default
template, err := e.client.CreateCollectionTemplate(siteID, collectionID, templateName, templateHTML, isDefault)
template, err := e.client.CreateCollectionTemplate(context.Background(), siteID, collectionID, templateName, templateHTML, isDefault)
if err != nil {
return fmt.Errorf("failed to create template %s: %w", templateName, err)
}
@@ -557,13 +559,13 @@ func (e *ContentEngine) extractAndStoreTemplatesAndItems(collectionNode *html.No
// reconstructCollectionItems rebuilds collection items from database and adds them to DOM
func (e *ContentEngine) reconstructCollectionItems(collectionNode *html.Node, collectionID, siteID string) error {
// Get all items for this collection from database
items, err := e.client.GetCollectionItems(siteID, collectionID)
items, err := e.client.GetCollectionItems(context.Background(), siteID, collectionID)
if err != nil {
return fmt.Errorf("failed to get collection items: %w", err)
}
// Get templates for this collection
templates, err := e.client.GetCollectionTemplates(siteID, collectionID)
templates, err := e.client.GetCollectionTemplates(context.Background(), siteID, collectionID)
if err != nil {
return fmt.Errorf("failed to get collection templates: %w", err)
}
@@ -652,7 +654,7 @@ func (e *ContentEngine) processChildElementsAsContent(childElement *html.Node, s
actualContent := ExtractTextContent(n)
// Store as individual content entry (unified .insertr approach)
_, err := e.client.CreateContent(siteID, contentID, actualContent, "", "system")
_, err := e.client.CreateContent(context.Background(), siteID, contentID, actualContent, "", "system")
if err != nil {
fmt.Printf("⚠️ Failed to create content %s: %v\n", contentID, err)
return
@@ -737,7 +739,7 @@ func (e *ContentEngine) CreateCollectionItemFromTemplate(
templateID int,
templateHTML string,
lastEditedBy string,
) (*CollectionItemWithTemplate, error) {
) (*db.CollectionItemWithTemplate, error) {
// Create virtual element from template (like enhancement path)
virtualElement, err := e.createVirtualElementFromTemplate(templateHTML)
if err != nil {
@@ -763,7 +765,7 @@ func (e *ContentEngine) CreateCollectionItemFromTemplate(
}
// Create collection item with structural template
collectionItem, err := e.client.CreateCollectionItem(
collectionItem, err := e.client.CreateCollectionItem(context.Background(),
siteID, collectionID, itemID, templateID, structuralTemplate, 0, lastEditedBy,
)
if err != nil {
@@ -838,7 +840,7 @@ func (e *ContentEngine) storeChildrenAsCollectionItems(collectionNode *html.Node
templateID := templateIDs[i%len(templateIDs)]
// Store structural template in collection_items (content lives in content table)
_, err = e.client.CreateCollectionItem(siteID, collectionID, itemID, templateID, structuralTemplate, i+1, "system")
_, err = e.client.CreateCollectionItem(context.Background(), siteID, collectionID, itemID, templateID, structuralTemplate, i+1, "system")
if err != nil {
return fmt.Errorf("failed to create collection item %s: %w", itemID, err)
}