- Replace automatic auth controls with developer-placed .insertr-gate elements - Add OAuth-ready authentication flow with mock implementation - Support any HTML element as gate with custom styling - Implement proper gate restoration after authentication - Move auth controls to bottom-right corner for better UX - Add editor gates to demo pages (footer link and styled button) - Maintain gates visible by default with hideGatesAfterAuth option - Prevent duplicate authentication attempts with loading states This enables small business owners to access editor via discrete footer links or custom-styled elements placed anywhere by developers.
217 lines
5.6 KiB
Go
217 lines
5.6 KiB
Go
package content
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/net/html"
|
|
|
|
"github.com/insertr/cli/pkg/parser"
|
|
)
|
|
|
|
// Enhancer combines parsing and content injection
|
|
type Enhancer struct {
|
|
parser *parser.Parser
|
|
injector *Injector
|
|
}
|
|
|
|
// NewEnhancer creates a new HTML enhancer
|
|
func NewEnhancer(client ContentClient, siteID string) *Enhancer {
|
|
return &Enhancer{
|
|
parser: parser.New(),
|
|
injector: NewInjector(client, siteID),
|
|
}
|
|
}
|
|
|
|
// EnhanceFile processes an HTML file and injects content
|
|
func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error {
|
|
// Use parser to get elements from file
|
|
result, err := e.parser.ParseDirectory(filepath.Dir(inputPath))
|
|
if err != nil {
|
|
return fmt.Errorf("parsing file: %w", err)
|
|
}
|
|
|
|
// Filter elements for this specific file
|
|
var fileElements []parser.Element
|
|
inputBaseName := filepath.Base(inputPath)
|
|
for _, elem := range result.Elements {
|
|
elemBaseName := filepath.Base(elem.FilePath)
|
|
if elemBaseName == inputBaseName {
|
|
fileElements = append(fileElements, elem)
|
|
}
|
|
}
|
|
|
|
if len(fileElements) == 0 {
|
|
// No insertr elements found, copy file as-is
|
|
return e.copyFile(inputPath, outputPath)
|
|
}
|
|
|
|
// Read and parse HTML for modification
|
|
htmlContent, err := os.ReadFile(inputPath)
|
|
if err != nil {
|
|
return fmt.Errorf("reading file %s: %w", inputPath, err)
|
|
}
|
|
|
|
doc, err := html.Parse(strings.NewReader(string(htmlContent)))
|
|
if err != nil {
|
|
return fmt.Errorf("parsing HTML: %w", err)
|
|
}
|
|
|
|
// Find and inject content for each element
|
|
for _, elem := range fileElements {
|
|
// Find the node in the parsed document
|
|
// Note: This is a simplified approach - in production we'd need more robust node matching
|
|
if err := e.injectElementContent(doc, elem); err != nil {
|
|
fmt.Printf("⚠️ Warning: failed to inject content for %s: %v\n", elem.ContentID, err)
|
|
}
|
|
}
|
|
|
|
// Inject editor assets for development
|
|
libraryScript := GetLibraryScript(false) // Use non-minified for development debugging
|
|
e.injector.InjectEditorAssets(doc, true, libraryScript)
|
|
|
|
// Write enhanced HTML
|
|
if err := e.writeHTML(doc, outputPath); err != nil {
|
|
return fmt.Errorf("writing enhanced HTML: %w", err)
|
|
}
|
|
|
|
fmt.Printf("✅ Enhanced: %s → %s (%d elements)\n",
|
|
filepath.Base(inputPath),
|
|
filepath.Base(outputPath),
|
|
len(fileElements))
|
|
|
|
return nil
|
|
}
|
|
|
|
// injectElementContent finds and injects content for a specific element
|
|
func (e *Enhancer) injectElementContent(doc *html.Node, elem parser.Element) error {
|
|
// Fetch content from database
|
|
contentItem, err := e.injector.client.GetContent(e.injector.siteID, elem.ContentID)
|
|
if err != nil {
|
|
return fmt.Errorf("fetching content: %w", err)
|
|
}
|
|
|
|
// Find nodes with insertr class and inject content
|
|
e.findAndInjectNodes(doc, elem, contentItem)
|
|
return nil
|
|
}
|
|
|
|
// findAndInjectNodes recursively finds nodes and injects content
|
|
func (e *Enhancer) findAndInjectNodes(node *html.Node, elem parser.Element, contentItem *ContentItem) {
|
|
if node.Type == html.ElementNode {
|
|
// Check if this node matches our element criteria
|
|
classes := getClasses(node)
|
|
if containsClass(classes, "insertr") && node.Data == elem.Tag {
|
|
// This might be our target node - inject content
|
|
e.injector.addContentAttributes(node, elem.ContentID, string(elem.Type))
|
|
|
|
if contentItem != nil {
|
|
switch elem.Type {
|
|
case parser.ContentText:
|
|
e.injector.injectTextContent(node, contentItem.Value)
|
|
case parser.ContentMarkdown:
|
|
e.injector.injectMarkdownContent(node, contentItem.Value)
|
|
case parser.ContentLink:
|
|
e.injector.injectLinkContent(node, contentItem.Value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recursively process children
|
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
|
e.findAndInjectNodes(child, elem, contentItem)
|
|
}
|
|
}
|
|
|
|
// Helper functions from parser package
|
|
func getClasses(node *html.Node) []string {
|
|
for _, attr := range node.Attr {
|
|
if attr.Key == "class" {
|
|
return strings.Fields(attr.Val)
|
|
}
|
|
}
|
|
return []string{}
|
|
}
|
|
|
|
func containsClass(classes []string, target string) bool {
|
|
for _, class := range classes {
|
|
if class == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// EnhanceDirectory processes all HTML files in a directory
|
|
func (e *Enhancer) EnhanceDirectory(inputDir, outputDir string) error {
|
|
// Create output directory
|
|
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
|
return fmt.Errorf("creating output directory: %w", err)
|
|
}
|
|
|
|
// Walk input directory
|
|
return filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Calculate relative path and output path
|
|
relPath, err := filepath.Rel(inputDir, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
outputPath := filepath.Join(outputDir, relPath)
|
|
|
|
// Handle directories
|
|
if info.IsDir() {
|
|
return os.MkdirAll(outputPath, info.Mode())
|
|
}
|
|
|
|
// Handle HTML files
|
|
if strings.HasSuffix(strings.ToLower(path), ".html") {
|
|
return e.EnhanceFile(path, outputPath)
|
|
}
|
|
|
|
// Copy other files as-is
|
|
return e.copyFile(path, outputPath)
|
|
})
|
|
}
|
|
|
|
// copyFile copies a file from src to dst
|
|
func (e *Enhancer) copyFile(src, dst string) error {
|
|
// Create directory for destination
|
|
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Read source
|
|
data, err := os.ReadFile(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write destination
|
|
return os.WriteFile(dst, data, 0644)
|
|
}
|
|
|
|
// writeHTML writes an HTML document to a file
|
|
func (e *Enhancer) writeHTML(doc *html.Node, outputPath string) error {
|
|
// Create directory for output
|
|
if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create output file
|
|
file, err := os.Create(outputPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
// Write HTML
|
|
return html.Render(file, doc)
|
|
}
|