diff --git a/COMMANDS.md b/COMMANDS.md
index d3aebec..d3b51e5 100644
--- a/COMMANDS.md
+++ b/COMMANDS.md
@@ -104,23 +104,9 @@ When running `insertr serve`, the server automatically:
- **Auto-updates files** when content changes via API
- **Creates backups** of original files (if enabled)
-**Development vs Production Behavior:**
-
-**Development Mode** (`--dev-mode`):
-- ✅ Content changes saved to database
-- ❌ File enhancement **disabled** (prevents live-reload loops)
-- ✅ Editor loads content dynamically from API
-- ✅ Perfect for `just dev` workflow
-
-**Production Mode** (no `--dev-mode`):
-- ✅ Content changes saved to database
-- ✅ File enhancement **enabled**
-- ✅ Static files updated immediately
-- ✅ Changes live instantly
-
-**Live Enhancement Process (Production):**
+**Live Enhancement Process:**
1. Content updated via API → Database updated
-2. If site has `auto_enhance: true` AND not in dev mode → File enhancement triggered
+2. If site has `auto_enhance: true` → File enhancement triggered
3. Static files updated in-place → Changes immediately live
### API Endpoints
diff --git a/README.md b/README.md
index 16cfea9..592aab6 100644
--- a/README.md
+++ b/README.md
@@ -436,33 +436,14 @@ server:
backup_originals: true
```
-### **Development vs Production Modes**
-
-**Development Mode** (recommended for development):
-```bash
-# Development: Content updates save to database only
-# NO file enhancement to prevent live-reload loops
-./insertr serve --dev-mode
-just dev # Uses dev mode automatically
-```
-
-**Production Mode** (for deployment):
-```bash
-# Production: Content updates trigger immediate file enhancement
-./insertr serve # No --dev-mode flag
-```
-
### **Quick Start**
```bash
# 1. Configure sites in insertr.yaml
-# 2. Start development server (no file enhancement)
+# 2. Start the server
./insertr serve --dev-mode
-# 3. Editor loads content from database dynamically
-# 4. No unwanted file modifications or page reloads during development
-
-# 5. For production deployment:
-./insertr serve # Enables automatic file enhancement
+# 3. Your sites are automatically registered and enhanced
+# 4. Content changes via editor immediately update static files
```
## ⚙️ Configuration
diff --git a/cmd/serve.go b/cmd/serve.go
index 96fa14d..0388355 100644
--- a/cmd/serve.go
+++ b/cmd/serve.go
@@ -124,25 +124,30 @@ func runServe(cmd *cobra.Command, args []string) {
router.HandleFunc("/health", api.HealthMiddleware())
// API routes
- apiRouter := router.PathPrefix("/api/content").Subrouter()
+ apiRouter := router.PathPrefix("/api").Subrouter()
- // Content endpoints matching the expected API contract
- apiRouter.HandleFunc("/bulk", contentHandler.GetBulkContent).Methods("GET")
- apiRouter.HandleFunc("/{id}", contentHandler.GetContent).Methods("GET")
- apiRouter.HandleFunc("/{id}", contentHandler.UpdateContent).Methods("PUT")
- apiRouter.HandleFunc("", contentHandler.GetAllContent).Methods("GET")
- apiRouter.HandleFunc("", contentHandler.CreateContent).Methods("POST")
+ // Content endpoints
+ contentRouter := apiRouter.PathPrefix("/content").Subrouter()
+ contentRouter.HandleFunc("/bulk", contentHandler.GetBulkContent).Methods("GET")
+ contentRouter.HandleFunc("/{id}", contentHandler.GetContent).Methods("GET")
+ contentRouter.HandleFunc("/{id}", contentHandler.UpdateContent).Methods("PUT")
+ contentRouter.HandleFunc("", contentHandler.GetAllContent).Methods("GET")
+ contentRouter.HandleFunc("", contentHandler.CreateContent).Methods("POST")
// Version control endpoints
- apiRouter.HandleFunc("/{id}/versions", contentHandler.GetContentVersions).Methods("GET")
- apiRouter.HandleFunc("/{id}/rollback", contentHandler.RollbackContent).Methods("POST")
+ contentRouter.HandleFunc("/{id}/versions", contentHandler.GetContentVersions).Methods("GET")
+ contentRouter.HandleFunc("/{id}/rollback", contentHandler.RollbackContent).Methods("POST")
+
+ // Site enhancement endpoint
+ apiRouter.HandleFunc("/enhance", contentHandler.EnhanceSite).Methods("POST")
// Handle CORS preflight requests explicitly
- apiRouter.HandleFunc("/{id}", api.CORSPreflightHandler).Methods("OPTIONS")
- apiRouter.HandleFunc("", api.CORSPreflightHandler).Methods("OPTIONS")
- apiRouter.HandleFunc("/bulk", api.CORSPreflightHandler).Methods("OPTIONS")
- apiRouter.HandleFunc("/{id}/versions", api.CORSPreflightHandler).Methods("OPTIONS")
- apiRouter.HandleFunc("/{id}/rollback", api.CORSPreflightHandler).Methods("OPTIONS")
+ contentRouter.HandleFunc("/{id}", api.CORSPreflightHandler).Methods("OPTIONS")
+ contentRouter.HandleFunc("", api.CORSPreflightHandler).Methods("OPTIONS")
+ contentRouter.HandleFunc("/bulk", api.CORSPreflightHandler).Methods("OPTIONS")
+ contentRouter.HandleFunc("/{id}/versions", api.CORSPreflightHandler).Methods("OPTIONS")
+ contentRouter.HandleFunc("/{id}/rollback", api.CORSPreflightHandler).Methods("OPTIONS")
+ apiRouter.HandleFunc("/enhance", api.CORSPreflightHandler).Methods("OPTIONS")
// Start server
addr := fmt.Sprintf(":%d", port)
diff --git a/internal/api/handlers.go b/internal/api/handlers.go
index e34aa3c..e29f7fa 100644
--- a/internal/api/handlers.go
+++ b/internal/api/handlers.go
@@ -40,6 +40,53 @@ func (h *ContentHandler) SetSiteManager(siteManager *content.SiteManager) {
h.siteManager = siteManager
}
+// EnhanceSite handles POST /api/enhance - manual site enhancement trigger
+func (h *ContentHandler) EnhanceSite(w http.ResponseWriter, r *http.Request) {
+ siteID := r.URL.Query().Get("site_id")
+ if siteID == "" {
+ http.Error(w, "site_id parameter is required", http.StatusBadRequest)
+ return
+ }
+
+ if h.siteManager == nil {
+ http.Error(w, "Site manager not available", http.StatusServiceUnavailable)
+ return
+ }
+
+ // Check if site is registered
+ site, exists := h.siteManager.GetSite(siteID)
+ if !exists {
+ http.Error(w, fmt.Sprintf("Site %s is not registered", siteID), http.StatusNotFound)
+ return
+ }
+
+ // Perform enhancement
+ err := h.siteManager.EnhanceSite(siteID)
+ if err != nil {
+ log.Printf("❌ Manual enhancement failed for site %s: %v", siteID, err)
+ http.Error(w, fmt.Sprintf("Enhancement failed: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ // Get enhancement statistics
+ stats := h.siteManager.GetStats()
+
+ // Return success response with details
+ response := map[string]interface{}{
+ "success": true,
+ "site_id": siteID,
+ "site_path": site.Path,
+ "message": fmt.Sprintf("Successfully enhanced site %s", siteID),
+ "stats": stats,
+ "timestamp": time.Now().Format(time.RFC3339),
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(response)
+
+ log.Printf("✅ Manual enhancement completed for site %s", siteID)
+}
+
// GetContent handles GET /api/content/{id}
func (h *ContentHandler) GetContent(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
diff --git a/internal/content/site_manager.go b/internal/content/site_manager.go
index cb31c8c..4ca9dc4 100644
--- a/internal/content/site_manager.go
+++ b/internal/content/site_manager.go
@@ -105,23 +105,12 @@ func (sm *SiteManager) GetAllSites() map[string]*SiteConfig {
}
// IsAutoEnhanceEnabled checks if a site has auto-enhancement enabled
-// Returns false in development mode to prevent unwanted file modifications and live-reload loops
func (sm *SiteManager) IsAutoEnhanceEnabled(siteID string) bool {
- sm.mutex.RLock()
- defer sm.mutex.RUnlock()
-
- // Disable auto-enhancement in development mode to prevent file modification conflicts with live-reload
+ // Never auto-enhance in development mode - use manual enhance button instead
if sm.devMode {
return false
}
- site, exists := sm.sites[siteID]
- return exists && site.AutoEnhance
-}
-
-// ForceEnhanceEnabled allows testing production behavior in development mode
-// This method bypasses the dev_mode check for testing purposes
-func (sm *SiteManager) ForceEnhanceEnabled(siteID string) bool {
sm.mutex.RLock()
defer sm.mutex.RUnlock()
diff --git a/lib/src/core/auth.js b/lib/src/core/auth.js
index 2cb1875..037800c 100644
--- a/lib/src/core/auth.js
+++ b/lib/src/core/auth.js
@@ -330,11 +330,15 @@ export class InsertrAuth {
Visitor Mode
+
`;
document.body.insertAdjacentHTML('beforeend', statusHtml);
this.statusIndicator = document.getElementById('insertr-status');
+ this.setupEnhanceButton();
this.updateStatusIndicator();
}
@@ -357,6 +361,9 @@ export class InsertrAuth {
statusText.textContent = 'Authenticated';
statusDot.className = 'insertr-status-dot insertr-status-authenticated';
}
+
+ // Update enhance button visibility
+ this.updateEnhanceButtonVisibility();
}
/**
@@ -398,6 +405,28 @@ export class InsertrAuth {
body.insertr-hide-gates .insertr-gate {
display: none !important;
}
+
+ /* Enhance button styles */
+ .insertr-enhance-btn {
+ background: #2563eb;
+ color: white;
+ border: none;
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 11px;
+ margin-left: 8px;
+ cursor: pointer;
+ transition: background 0.2s ease;
+ }
+
+ .insertr-enhance-btn:hover {
+ background: #1d4ed8;
+ }
+
+ .insertr-enhance-btn:disabled {
+ background: #9ca3af;
+ cursor: not-allowed;
+ }
`;
const styleSheet = document.createElement('style');
@@ -556,4 +585,85 @@ export class InsertrAuth {
console.log('✅ OAuth authentication successful');
}, 1000);
}
+
+ /**
+ * Setup enhance button functionality
+ */
+ setupEnhanceButton() {
+ const enhanceBtn = document.getElementById('insertr-enhance-btn');
+ if (!enhanceBtn) return;
+
+ enhanceBtn.addEventListener('click', async () => {
+ await this.enhanceFiles();
+ });
+
+ // Show enhance button only in development/authenticated mode
+ this.updateEnhanceButtonVisibility();
+ }
+
+ /**
+ * Update enhance button visibility based on authentication state
+ */
+ updateEnhanceButtonVisibility() {
+ const enhanceBtn = document.getElementById('insertr-enhance-btn');
+ if (!enhanceBtn) return;
+
+ // Show enhance button when authenticated (indicates dev mode)
+ if (this.state.isAuthenticated) {
+ enhanceBtn.style.display = 'inline-block';
+ } else {
+ enhanceBtn.style.display = 'none';
+ }
+ }
+
+ /**
+ * Trigger manual file enhancement
+ */
+ async enhanceFiles() {
+ const enhanceBtn = document.getElementById('insertr-enhance-btn');
+ if (!enhanceBtn) return;
+
+ // Get site ID from window context or configuration
+ const siteId = window.insertrConfig?.siteId || this.options.siteId || 'demo';
+
+ try {
+ // Show loading state
+ enhanceBtn.textContent = '⏳ Enhancing...';
+ enhanceBtn.disabled = true;
+
+ // Call enhance API
+ const response = await fetch(`/api/enhance?site_id=${siteId}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${this.state.currentUser?.token || 'mock-token'}`
+ }
+ });
+
+ if (!response.ok) {
+ throw new Error(`Enhancement failed: ${response.status} ${response.statusText}`);
+ }
+
+ const result = await response.json();
+ console.log('✅ Files enhanced successfully:', result);
+
+ // Show success state briefly
+ enhanceBtn.textContent = '✅ Enhanced!';
+
+ // Optional: Trigger page reload to show enhanced files
+ setTimeout(() => {
+ window.location.reload();
+ }, 1000);
+
+ } catch (error) {
+ console.error('❌ Enhancement failed:', error);
+ enhanceBtn.textContent = '❌ Failed';
+
+ // Reset button after error
+ setTimeout(() => {
+ enhanceBtn.textContent = '🔄 Enhance';
+ enhanceBtn.disabled = false;
+ }, 2000);
+ }
+ }
}
\ No newline at end of file