Complete API handlers refactoring to eliminate type switching and use repository pattern consistently

This commit is contained in:
2025-10-08 20:36:20 +02:00
parent 01b921bfa3
commit bbf728d110
16 changed files with 268 additions and 2654 deletions

View File

@@ -29,7 +29,7 @@ func NewContentEngine(client db.ContentRepository) *ContentEngine {
idGenerator: NewIDGenerator(),
client: client,
authProvider: authProvider,
injector: NewInjector(client, ""), // siteID will be set per operation
injector: NewInjector(client, "", nil), // siteID will be set per operation
}
}
@@ -42,7 +42,7 @@ func NewContentEngineWithAuth(client db.ContentRepository, authProvider *AuthPro
idGenerator: NewIDGenerator(),
client: client,
authProvider: authProvider,
injector: NewInjectorWithAuth(client, "", authProvider), // siteID will be set per operation
injector: NewInjectorWithAuth(client, "", authProvider, nil), // siteID will be set per operation
}
}
@@ -130,7 +130,7 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
// 6. Inject editor assets for enhancement mode (development)
if input.Mode == Enhancement {
injector := NewInjectorWithAuth(e.client, input.SiteID, e.authProvider)
injector := NewInjectorWithAuth(e.client, input.SiteID, e.authProvider, nil)
injector.InjectEditorAssets(doc, true, "")
}
@@ -601,7 +601,7 @@ func (e *ContentEngine) reconstructCollectionItems(collectionNode *html.Node, co
if structuralBody != nil {
// Process each .insertr element using Injector pattern (unified approach)
injector := NewInjector(e.client, siteID)
injector := NewInjector(e.client, siteID, nil)
// Walk through structural elements and hydrate with content from content table
e.walkNodes(structuralBody, func(n *html.Node) {

View File

@@ -6,8 +6,10 @@ import (
"log"
"strings"
"github.com/insertr/insertr/internal/config"
"github.com/insertr/insertr/internal/db"
"golang.org/x/net/html"
"slices"
)
// Injector handles content injection into HTML elements
@@ -15,19 +17,21 @@ type Injector struct {
client db.ContentRepository
siteID string
authProvider *AuthProvider
config *config.Config
}
// NewInjector creates a new content injector
func NewInjector(client db.ContentRepository, siteID string) *Injector {
func NewInjector(client db.ContentRepository, siteID string, cfg *config.Config) *Injector {
return &Injector{
client: client,
siteID: siteID,
authProvider: &AuthProvider{Type: "mock"}, // default
config: cfg,
}
}
// NewInjectorWithAuth creates a new content injector with auth provider
func NewInjectorWithAuth(client db.ContentRepository, siteID string, authProvider *AuthProvider) *Injector {
func NewInjectorWithAuth(client db.ContentRepository, siteID string, authProvider *AuthProvider, cfg *config.Config) *Injector {
if authProvider == nil {
authProvider = &AuthProvider{Type: "mock"}
}
@@ -35,6 +39,7 @@ func NewInjectorWithAuth(client db.ContentRepository, siteID string, authProvide
client: client,
siteID: siteID,
authProvider: authProvider,
config: cfg,
}
}
@@ -200,7 +205,7 @@ func (i *Injector) setAttribute(node *html.Node, key, value string) {
// Remove existing attribute if present
for idx, attr := range node.Attr {
if attr.Key == key {
node.Attr = append(node.Attr[:idx], node.Attr[idx+1:]...)
node.Attr = slices.Delete(node.Attr, idx, idx+1)
break
}
}
@@ -232,10 +237,8 @@ func (i *Injector) addClass(node *html.Node, className string) {
}
// Check if class already exists
for _, class := range classes {
if class == className {
return // Class already exists
}
if slices.Contains(classes, className) {
return // Class already exists
}
// Add new class
@@ -327,8 +330,15 @@ func (i *Injector) InjectEditorScript(doc *html.Node) {
if i.authProvider != nil {
authProvider = i.authProvider.Type
}
insertrHTML := fmt.Sprintf(`<link rel="stylesheet" href="http://localhost:8080/insertr.css" data-insertr-injected="true">
<script src="http://localhost:8080/insertr.js" data-insertr-injected="true" data-site-id="%s" data-api-endpoint="http://localhost:8080/api/content" data-auth-provider="%s" data-debug="true"></script>`, i.siteID, authProvider)
// Generate configurable URLs for library assets
cssURL := i.getLibraryURL("insertr.css")
jsURL := i.getLibraryURL("insertr.js")
apiURL := i.getAPIURL()
insertrHTML := fmt.Sprintf(`<link rel="stylesheet" href="%s" data-insertr-injected="true">
<script src="%s" data-insertr-injected="true" data-site-id="%s" data-api-endpoint="%s" data-auth-provider="%s" data-debug="%t"></script>`,
cssURL, jsURL, i.siteID, apiURL, authProvider, i.isDebugMode())
// Parse and inject the CSS and script elements
insertrDoc, err := html.Parse(strings.NewReader(insertrHTML))
@@ -346,38 +356,6 @@ func (i *Injector) InjectEditorScript(doc *html.Node) {
log.Printf("✅ Insertr.js library injected with site configuration")
}
// injectAllScriptElements finds and injects all script elements from parsed HTML
func (i *Injector) injectAllScriptElements(doc *html.Node, targetNode *html.Node) error {
scripts := i.findAllScriptElements(doc)
for _, script := range scripts {
// Remove from original parent
if script.Parent != nil {
script.Parent.RemoveChild(script)
}
// Add to target node
targetNode.AppendChild(script)
}
return nil
}
// findAllScriptElements recursively finds all script elements
func (i *Injector) findAllScriptElements(node *html.Node) []*html.Node {
var scripts []*html.Node
if node.Type == html.ElementNode && node.Data == "script" {
scripts = append(scripts, node)
}
for child := node.FirstChild; child != nil; child = child.NextSibling {
childScripts := i.findAllScriptElements(child)
scripts = append(scripts, childScripts...)
}
return scripts
}
// injectAllHeadElements finds and injects all head elements (link, script) from parsed HTML
func (i *Injector) injectAllHeadElements(doc *html.Node, targetNode *html.Node) error {
elements := i.findAllHeadElements(doc)
@@ -479,15 +457,69 @@ func (i *Injector) extractElementByClass(node *html.Node, className string) *htm
return nil
}
// extractElementByTag finds element with specific tag
func (i *Injector) extractElementByTag(node *html.Node, tagName string) *html.Node {
if node.Type == html.ElementNode && node.Data == tagName {
return node
// getLibraryURL returns the appropriate URL for library assets (CSS/JS)
func (i *Injector) getLibraryURL(filename string) string {
if i.config == nil {
// Fallback to localhost if no config
return fmt.Sprintf("http://localhost:8080/%s", filename)
}
for child := node.FirstChild; child != nil; child = child.NextSibling {
if result := i.extractElementByTag(child, tagName); result != nil {
return result
// Check if we should use CDN
if i.config.Library.UseCDN && i.config.Library.CDNBaseURL != "" {
// Production: Use CDN
suffix := ""
if i.config.Library.Minified && filename == "insertr.js" {
suffix = ".min"
}
baseName := strings.TrimSuffix(filename, ".js")
baseName = strings.TrimSuffix(baseName, ".css")
return fmt.Sprintf("%s@%s/dist/%s%s", i.config.Library.CDNBaseURL, i.config.Library.Version, baseName, suffix+getFileExtension(filename))
}
return nil
// Development: Use local server
baseURL := i.config.Library.BaseURL
if baseURL == "" {
baseURL = fmt.Sprintf("http://localhost:%d", i.config.Server.Port)
}
suffix := ""
if i.config.Library.Minified && filename == "insertr.js" {
suffix = ".min"
}
baseName := strings.TrimSuffix(filename, ".js")
baseName = strings.TrimSuffix(baseName, ".css")
return fmt.Sprintf("%s/%s%s", baseURL, baseName, suffix+getFileExtension(filename))
}
// getAPIURL returns the API endpoint URL
func (i *Injector) getAPIURL() string {
if i.config == nil {
return "http://localhost:8080/api/content"
}
baseURL := i.config.Library.BaseURL
if baseURL == "" {
baseURL = fmt.Sprintf("http://localhost:%d", i.config.Server.Port)
}
return fmt.Sprintf("%s/api/content", baseURL)
}
// isDebugMode returns true if in development mode
func (i *Injector) isDebugMode() bool {
if i.config == nil {
return true
}
return i.config.Auth.DevMode
}
// getFileExtension returns the file extension including the dot
func getFileExtension(filename string) string {
if strings.HasSuffix(filename, ".js") {
return ".js"
}
if strings.HasSuffix(filename, ".css") {
return ".css"
}
return ""
}