feat: Complete HTML-first architecture implementation with API integration
- Replace value field with html_content for direct HTML storage - Add original_template field for style detection preservation - Remove all markdown processing from injector (delete markdown.go) - Fix critical content extraction/injection bugs in engine - Add missing UpdateContent PUT handler for content persistence - Fix API client field names and add updateContent() method - Resolve content type validation (only allow text/link types) - Add UUID-based ID generation to prevent collisions - Complete first-pass processing workflow for unprocessed elements - Verify end-to-end: Enhancement → Database → API → Editor → Persistence All 37 files updated for HTML-first content management system. Phase 3a implementation complete and production ready.
This commit is contained in:
@@ -17,14 +17,17 @@ type ContentEngine struct {
|
||||
idGenerator *IDGenerator
|
||||
client ContentClient
|
||||
authProvider *AuthProvider
|
||||
injector *Injector
|
||||
}
|
||||
|
||||
// NewContentEngine creates a new content processing engine
|
||||
func NewContentEngine(client ContentClient) *ContentEngine {
|
||||
authProvider := &AuthProvider{Type: "mock"} // default
|
||||
return &ContentEngine{
|
||||
idGenerator: NewIDGenerator(),
|
||||
client: client,
|
||||
authProvider: &AuthProvider{Type: "mock"}, // default
|
||||
authProvider: authProvider,
|
||||
injector: NewInjector(client, ""), // siteID will be set per operation
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +40,7 @@ func NewContentEngineWithAuth(client ContentClient, authProvider *AuthProvider)
|
||||
idGenerator: NewIDGenerator(),
|
||||
client: client,
|
||||
authProvider: authProvider,
|
||||
injector: NewInjectorWithAuth(client, "", authProvider), // siteID will be set per operation
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +88,20 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
|
||||
|
||||
// Add/update content attributes to the node
|
||||
e.addContentAttributes(elem.Node, id, elem.Type)
|
||||
|
||||
// Store content and template for newly discovered elements (first-pass)
|
||||
if wasGenerated && (input.Mode == Enhancement || input.Mode == ContentInjection) {
|
||||
// Extract content and template from the unprocessed element
|
||||
htmlContent := e.extractHTMLContent(elem.Node)
|
||||
originalTemplate := e.extractOriginalTemplate(elem.Node)
|
||||
|
||||
// Store in database via content client
|
||||
_, err := e.client.CreateContent(input.SiteID, id, htmlContent, originalTemplate, elem.Type, "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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Inject content if required by mode
|
||||
@@ -157,7 +175,7 @@ func (e *ContentEngine) determineContentType(node *html.Node) string {
|
||||
case "h1", "h2", "h3", "h4", "h5", "h6":
|
||||
return "text"
|
||||
case "p", "div", "section", "article", "span":
|
||||
return "markdown"
|
||||
return "text"
|
||||
default:
|
||||
return "text"
|
||||
}
|
||||
@@ -211,28 +229,35 @@ func (e *ContentEngine) injectContent(elements []ProcessedElement, siteID string
|
||||
|
||||
if contentItem != nil {
|
||||
// Inject the content into the element
|
||||
elem.Content = contentItem.Value
|
||||
e.injectContentIntoNode(elem.Node, contentItem.Value, contentItem.Type)
|
||||
elem.Content = contentItem.HTMLContent
|
||||
|
||||
// Update injector siteID for this operation
|
||||
e.injector.siteID = siteID
|
||||
e.injector.injectHTMLContent(elem.Node, contentItem.HTMLContent)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// injectContentIntoNode injects content value into an HTML node
|
||||
func (e *ContentEngine) injectContentIntoNode(node *html.Node, content, contentType string) {
|
||||
// Clear existing text content
|
||||
for child := node.FirstChild; child != nil; {
|
||||
next := child.NextSibling
|
||||
if child.Type == html.TextNode {
|
||||
node.RemoveChild(child)
|
||||
// extractHTMLContent extracts the inner HTML content from a node
|
||||
func (e *ContentEngine) extractHTMLContent(node *html.Node) string {
|
||||
var content strings.Builder
|
||||
|
||||
// Render all child nodes in order to preserve HTML structure
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
if err := html.Render(&content, child); err == nil {
|
||||
// All nodes (text and element) rendered in correct order
|
||||
}
|
||||
child = next
|
||||
}
|
||||
|
||||
// Add new text content
|
||||
textNode := &html.Node{
|
||||
Type: html.TextNode,
|
||||
Data: content,
|
||||
}
|
||||
node.AppendChild(textNode)
|
||||
return strings.TrimSpace(content.String())
|
||||
}
|
||||
|
||||
// extractOriginalTemplate extracts the outer HTML of the element (including the element itself)
|
||||
func (e *ContentEngine) extractOriginalTemplate(node *html.Node) string {
|
||||
var buf strings.Builder
|
||||
if err := html.Render(&buf, node); err != nil {
|
||||
return ""
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user