From 1fa607c47c73b48c77bc0fb6d1224a8be79b9874 Mon Sep 17 00:00:00 2001 From: Joakim Date: Tue, 16 Sep 2025 22:23:41 +0200 Subject: [PATCH] Fix site_id isolation for demo sites - Auto-derive site_id from demo directory paths (demos/demo-site -> 'demo', demos/simple/test-simple -> 'simple') - Add validation requiring explicit site_id for non-demo paths with helpful error messages - Remove JavaScript 'demo' fallback and add proper error messaging for missing site_id - Ensure each demo site uses isolated content namespace to prevent content mixing Resolves issue where /sites/simple and /sites/demo both used site_id=demo --- cmd/enhance.go | 25 ++++++++++++ internal/content/enhancer.go | 78 +++++++++++++++++++++++++++++++++++- lib/src/core/api-client.js | 18 ++++++++- lib/src/index.js | 2 +- 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/cmd/enhance.go b/cmd/enhance.go index b6fe93f..a793515 100644 --- a/cmd/enhance.go +++ b/cmd/enhance.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "os" + "strings" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -49,6 +50,30 @@ func runEnhance(cmd *cobra.Command, args []string) { siteID := viper.GetString("cli.site_id") outputDir := viper.GetString("cli.output") + // Auto-derive site_id for demo paths or validate for production + if strings.Contains(inputDir, "/demos/") { + // Auto-derive site_id from demo path + siteID = content.DeriveOrValidateSiteID(inputDir, siteID) + } else { + // Validate site_id for non-demo paths + if siteID == "" || siteID == "demo" { + log.Fatalf(`❌ site_id must be explicitly configured for non-demo sites. + +💡 Examples: + # Set via command line: + insertr enhance --site-id mysite /path/to/site + + # Set in insertr.yaml: + cli: + site_id: "mysite" + + # Set via environment: + INSERTR_CLI_SITE_ID=mysite insertr enhance /path/to/site + +🚀 For demo sites under demos/, site_id is auto-derived from the directory name.`) + } + } + // Create content client var client engine.ContentClient if apiURL != "" { diff --git a/internal/content/enhancer.go b/internal/content/enhancer.go index d040db9..6e9ad47 100644 --- a/internal/content/enhancer.go +++ b/internal/content/enhancer.go @@ -194,13 +194,89 @@ func (e *Enhancer) enhanceWithEngine(htmlContent []byte, filePath string) ([]byt // EnhanceInPlace performs in-place enhancement of static site files func (e *Enhancer) EnhanceInPlace(sitePath string, siteID string) error { - // Update the enhancer's site ID for this operation + // Use the provided siteID (derivation should happen at CLI level) e.siteID = siteID // Use EnhanceDirectory with same input and output (in-place) return e.EnhanceDirectory(sitePath, sitePath) } +// DeriveOrValidateSiteID automatically derives site_id for demo paths or validates for production +func DeriveOrValidateSiteID(sitePath string, configSiteID string) string { + // Check if this is a demo path + if strings.Contains(sitePath, "/demos/") { + return deriveDemoSiteID(sitePath) + } + + // For non-demo paths, return the configured site_id + // Validation of non-demo site_id will be handled at the CLI level + return configSiteID +} + +// deriveDemoSiteID extracts site_id from demo directory structure +func deriveDemoSiteID(sitePath string) string { + // Convert to absolute path and clean it + absPath, err := filepath.Abs(sitePath) + if err != nil { + absPath = sitePath + } + absPath = filepath.Clean(absPath) + + // Look for patterns in demo paths: + // demos/demo-site_enhanced -> "demo" + // demos/simple/test-simple_enhanced -> "simple" + // demos/simple/dan-eden-portfolio -> "dan-eden" + + parts := strings.Split(absPath, string(filepath.Separator)) + + // Find the demos directory index + demosIndex := -1 + for i, part := range parts { + if part == "demos" { + demosIndex = i + break + } + } + + if demosIndex == -1 || demosIndex >= len(parts)-1 { + // Fallback if demos not found in path + return "demo" + } + + // Get the segment after demos/ + nextSegment := parts[demosIndex+1] + + // Handle different demo directory patterns: + if strings.HasPrefix(nextSegment, "demo-site") { + return "demo" + } + + if nextSegment == "simple" && len(parts) > demosIndex+2 { + // For demos/simple/something, use the something part + finalSegment := parts[demosIndex+2] + + // Extract meaningful name from directory + if strings.HasPrefix(finalSegment, "test-") { + // test-simple_enhanced -> simple + return "simple" + } + if strings.Contains(finalSegment, "dan-eden") { + return "dan-eden" + } + + // Generic case: use the directory name as-is + return strings.TrimSuffix(finalSegment, "_enhanced") + } + + // Default case: use the immediate subdirectory of demos/ + result := strings.TrimSuffix(nextSegment, "_enhanced") + + // Clean up common suffixes + result = strings.TrimSuffix(result, "-site") + + return result +} + // copyFile copies a file from src to dst func (e *Enhancer) copyFile(src, dst string) error { // Create directory for destination diff --git a/lib/src/core/api-client.js b/lib/src/core/api-client.js index 8b08a1c..b41a530 100644 --- a/lib/src/core/api-client.js +++ b/lib/src/core/api-client.js @@ -10,7 +10,7 @@ export class ApiClient { : '/api/content'; // Production: same-origin API this.baseUrl = options.apiEndpoint || defaultEndpoint; - this.siteId = options.siteId || 'demo'; + this.siteId = options.siteId || this.handleMissingSiteId(); // Log API configuration in development if (isDevelopment && !options.apiEndpoint) { @@ -276,6 +276,22 @@ export class ApiClient { return false; } + /** + * Handle missing site_id configuration + * @returns {string} Site ID or throws error + */ + handleMissingSiteId() { + console.error('❌ No site_id configured for Insertr.'); + console.log('💡 Add data-site-id attribute to your script tag:'); + console.log(' '); + console.log(''); + console.log('🚀 For demos under demos/, site_id is auto-derived from directory name.'); + console.log('🏭 For production sites, you must explicitly set your site_id.'); + + // Return a placeholder that will cause API calls to fail gracefully + return '__MISSING_SITE_ID__'; + } + /** * Get current file path from URL for consistent ID generation * @returns {string} File path like "index.html", "about.html" diff --git a/lib/src/index.js b/lib/src/index.js index a7ddc97..aee8c13 100644 --- a/lib/src/index.js +++ b/lib/src/index.js @@ -84,7 +84,7 @@ function autoInitialize() { const config = {}; if (insertrScript) { - config.siteId = insertrScript.getAttribute('data-site-id') || 'demo'; + config.siteId = insertrScript.getAttribute('data-site-id'); // No fallback - let ApiClient handle missing values config.apiEndpoint = insertrScript.getAttribute('data-api-endpoint') || '/api/content'; config.mockAuth = insertrScript.getAttribute('data-mock-auth') === 'true'; config.debug = insertrScript.getAttribute('data-debug') === 'true';