feat: complete HTML-first architecture implementation (Phase 1 & 2)
Major architectural simplification removing content type complexity: Database Schema: - Remove 'type' field from content and content_versions tables - Simplify to pure HTML storage with html_content + original_template - Regenerate all sqlc models for SQLite and PostgreSQL API Simplification: - Remove content type routing and validation - Eliminate type-specific handlers (text/markdown/structured) - Unified HTML-first approach for all content operations - Simplify CreateContent and UpdateContent to HTML-only Backend Enhancements: - Update enhancer to only generate data-content-id (no data-content-type) - Improve container expansion utilities with comprehensive block/inline rules - Add Phase 3 preparation with boundary-respecting traversal logic - Strengthen element classification for viable children detection Documentation: - Update TODO.md to reflect Phase 1-3 completion status - Add WORKING_ON.md documenting the architectural transformation - Mark container expansion and HTML-first architecture as complete This completes the transition to a unified HTML-first content management system with automatic style detection and element-based behavior, eliminating the complex multi-type system in favor of semantic HTML-driven editing.
This commit is contained in:
40
TODO.md
40
TODO.md
@@ -10,12 +10,15 @@
|
|||||||
- **Authentication System**: Mock (development) + Authentik OIDC (production)
|
- **Authentication System**: Mock (development) + Authentik OIDC (production)
|
||||||
- **Build-Time Enhancement**: Content injection from database to static HTML
|
- **Build-Time Enhancement**: Content injection from database to static HTML
|
||||||
- **Development Workflow**: Hot reload, auto-enhanced demo sites, seamless testing
|
- **Development Workflow**: Hot reload, auto-enhanced demo sites, seamless testing
|
||||||
|
- **Container Transformation**: CLASSES.md syntactic sugar - containers auto-expand to viable children
|
||||||
|
|
||||||
### **🏗️ Architecture Achievements**
|
### **🏗️ Architecture Achievements**
|
||||||
- **Zero Configuration**: Just add `class="insertr"` to any element
|
- **Zero Configuration**: Just add `class="insertr"` to any element
|
||||||
- **Framework Agnostic**: Works with any static site generator
|
- **Framework Agnostic**: Works with any static site generator
|
||||||
- **Performance First**: Regular visitors get pure static HTML with zero CMS overhead
|
- **Performance First**: Regular visitors get pure static HTML with zero CMS overhead
|
||||||
- **HTML-First**: No lossy markdown conversion - perfect attribute preservation
|
- **HTML-First**: No lossy markdown conversion - perfect attribute preservation
|
||||||
|
- **Unified System**: Single HTML preservation path for all content types
|
||||||
|
- **Element-Based Behavior**: Automatic editing interface based on HTML tag semantics
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,7 +27,7 @@
|
|||||||
### **🔴 Phase 1: Editor Integration Polish** (High Priority)
|
### **🔴 Phase 1: Editor Integration Polish** (High Priority)
|
||||||
|
|
||||||
#### **Frontend-Backend Integration**
|
#### **Frontend-Backend Integration**
|
||||||
- [ ] **Editor-API Connection**: Wire up StyleAware editor saves to actual HTTP API
|
- [x] **Editor-API Connection**: StyleAware editor saves successfully to HTTP API
|
||||||
- [ ] **Error Handling**: Proper error states, loading indicators, offline handling
|
- [ ] **Error Handling**: Proper error states, loading indicators, offline handling
|
||||||
- [ ] **Content Validation**: Client-side validation before API calls
|
- [ ] **Content Validation**: Client-side validation before API calls
|
||||||
- [ ] **Save Feedback**: Professional save/error feedback in editor interface
|
- [ ] **Save Feedback**: Professional save/error feedback in editor interface
|
||||||
@@ -49,7 +52,40 @@
|
|||||||
- [ ] **Content Approval**: Editorial workflows and publishing controls
|
- [ ] **Content Approval**: Editorial workflows and publishing controls
|
||||||
- [ ] **Performance Monitoring**: Analytics and optimization tools
|
- [ ] **Performance Monitoring**: Analytics and optimization tools
|
||||||
|
|
||||||
### **🟢 Phase 3: Advanced CMS Features** (Low Priority)
|
### **✅ Phase 3: Container Expansion Intelligence** (Complete)
|
||||||
|
|
||||||
|
#### **Element Classification and Boundaries**
|
||||||
|
- [x] **HTML Semantics Approach**: Use HTML tag semantics for block vs inline detection
|
||||||
|
- [x] **Framework Agnostic Processing**: No special framework container detection
|
||||||
|
- [x] **Boundary Rules**: Only `.insertr` elements are boundaries, traverse all other containers
|
||||||
|
- [x] **Block/Inline Classification**: Clear rules for when elements get `.insertr` vs formatting
|
||||||
|
|
||||||
|
#### **Implementation Status**
|
||||||
|
- [x] **Backend Container Transformation**: Implemented syntactic sugar transformation in `internal/engine/engine.go`
|
||||||
|
- [x] **Frontend Container Logic Removal**: Cleaned up `lib/src/core/insertr.js` - frontend finds enhanced elements only
|
||||||
|
- [x] **Backend Viable Children**: Updated `internal/engine/utils.go` with comprehensive block/inline logic
|
||||||
|
- [x] **Recursive Traversal**: Deep nesting support with proper boundary respect implemented
|
||||||
|
- [x] **CLASSES.md Compliance**: Container expansion now follows specification exactly
|
||||||
|
|
||||||
|
#### **Complex Element Handling** (Deferred)
|
||||||
|
- [ ] **Table Editing**: Complex hierarchy needs separate planning for `<table>`, `<tr>`, `<td>` elements
|
||||||
|
- Tables have nested semantic structure that doesn't fit simple block/inline model
|
||||||
|
- Need to determine: Should individual cells be editable? Entire table? Row-level?
|
||||||
|
- Consider: Table headers, captions, complex layouts, accessibility concerns
|
||||||
|
|
||||||
|
- [ ] **Form Element Editing**: Interactive form controls need specialized editors
|
||||||
|
- `<input>` fields: Different types need different editing interfaces (text, email, etc.)
|
||||||
|
- `<textarea>`: Should get rich text editing or preserve plain text?
|
||||||
|
- `<select>` options: Need dynamic option management interface
|
||||||
|
- `<form>` containers: Validation rules, action URLs, method selection
|
||||||
|
- Consider: Form submission handling, validation, accessibility
|
||||||
|
|
||||||
|
- [ ] **Self-Closing Element Management**: Media and input elements
|
||||||
|
- `<img>`: Alt text, src, responsive image sets, lazy loading
|
||||||
|
- `<video>/<audio>`: Multiple sources, controls, accessibility features
|
||||||
|
- `<input>`: Type-specific validation, placeholder text, required fields
|
||||||
|
|
||||||
|
### **🟢 Phase 4: Advanced CMS Features** (Low Priority)
|
||||||
|
|
||||||
#### **Content Management Enhancements**
|
#### **Content Management Enhancements**
|
||||||
- [ ] **Media Management**: Image upload, asset management, optimization
|
- [ ] **Media Management**: Image upload, asset management, optimization
|
||||||
|
|||||||
187
WORKING_ON.md
Normal file
187
WORKING_ON.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# Insertr Structural Refactoring - Work in Progress
|
||||||
|
|
||||||
|
## Current Status: Major Simplification Opportunity Identified
|
||||||
|
|
||||||
|
We've identified that the current Insertr codebase has remnants from multiple refactoring phases and can be significantly simplified by fully embracing the HTML-first philosophy outlined in CLASSES.md.
|
||||||
|
|
||||||
|
## Key Insights Discovered
|
||||||
|
|
||||||
|
### 1. **Obsolete Content Type System**
|
||||||
|
- **Problem**: Currently has 3 content types (`'text'`, `'html'`, `'structured'`) with complex routing logic
|
||||||
|
- **HTML-First Reality**: Only need HTML preservation with automatic style detection
|
||||||
|
- **Opportunity**: Remove `data-content-type` attributes entirely and detect behavior from HTML element tags
|
||||||
|
|
||||||
|
### 2. **Multiple Editor Systems (Legacy)**
|
||||||
|
- **Current**: Complex strategy detection choosing between simple vs rich editors
|
||||||
|
- **HTML-First**: Always use StyleAwareEditor with HTML preservation and remove legacy code
|
||||||
|
- **Benefit**: Massive code reduction, consistent behavior everywhere
|
||||||
|
|
||||||
|
### 3. **Element-Based Behavior Detection**
|
||||||
|
- **Instead of**: `data-content-type="link"` attributes
|
||||||
|
- **Use**: Element tag analysis (`<a>` = link behavior, `<p>` = text behavior)
|
||||||
|
- **Source**: Behavior rules already defined in CLASSES.md
|
||||||
|
|
||||||
|
### 4. **UX Optimization Opportunity**
|
||||||
|
- **Direct Multi-Property Editing**: `<a class="insertr">` should open URL+text editor immediately
|
||||||
|
- **Rich Text Editing**: `<p class="insertr">` with nested elements should use StyleAwareEditor
|
||||||
|
- **Context-Appropriate UX**: Right editor for the right element type
|
||||||
|
|
||||||
|
## Planned Refactoring (Major Changes)
|
||||||
|
Keep in mind that we need no legacy code preserved or backwards compatibility. Database changes can be done to the schema, not with ALTER statements. We have no data to preserve and no users of any current insertr versions.
|
||||||
|
|
||||||
|
### Phase 1: Frontend Simplification
|
||||||
|
- [ ] **Remove content type routing** from StyleAwareEditor
|
||||||
|
- [ ] **Eliminate 'text' and 'structured' handlers** - keep only HTML preservation
|
||||||
|
- [ ] **Remove simple/textarea editor** that strips HTML formatting
|
||||||
|
- [ ] **Implement element-based behavior detection** using tag names
|
||||||
|
- [ ] **Create direct multi-property editors** for `<a>`, `<button>`, `<img>`
|
||||||
|
|
||||||
|
### Phase 2: Backend Simplification
|
||||||
|
- [ ] **Remove `data-content-type` generation** from enhancer
|
||||||
|
- [ ] **Simplify API** to be purely HTML-based
|
||||||
|
- [ ] **Keep only `data-content-id`** for content tracking
|
||||||
|
- [ ] **Update database schema** remove type field
|
||||||
|
|
||||||
|
### Phase 3: Container Expansion Intelligence
|
||||||
|
- [ ] **Improve viable children detection** with element-type-aware rules
|
||||||
|
- [ ] **Smart stopping rules** for multi-property elements (`<a>`, `<button>`, `<img>`)
|
||||||
|
- [ ] **Semantic container expansion** respecting HTML element purposes
|
||||||
|
- [ ] **Framework/technical container filtering** (React, Gatsby artifacts)
|
||||||
|
|
||||||
|
## Technical Architecture Changes
|
||||||
|
|
||||||
|
### Before (Complex Multi-System):
|
||||||
|
```javascript
|
||||||
|
// Multiple content types with routing
|
||||||
|
switch (content.type) {
|
||||||
|
case 'text': // Strips HTML - obsolete
|
||||||
|
case 'html': // HTML preservation
|
||||||
|
case 'structured': // Legacy template system
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple editor strategies
|
||||||
|
switch (analysis.strategy) {
|
||||||
|
case 'simple': // Textarea - strips HTML
|
||||||
|
case 'rich': // Rich editor with styles
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### After (Unified HTML-First):
|
||||||
|
```javascript
|
||||||
|
// Single path for ALL .insertr elements
|
||||||
|
class StyleAwareEditor {
|
||||||
|
async initialize() {
|
||||||
|
// 1. Always extract HTML with preservation
|
||||||
|
this.originalContent = this.htmlEngine.extractForEditing(this.element);
|
||||||
|
|
||||||
|
// 2. Always detect styles from nested elements
|
||||||
|
this.detectedStyles = this.styleEngine.detectStyles(this.element);
|
||||||
|
|
||||||
|
// 3. Element-based UX routing
|
||||||
|
if (this.isMultiPropertyElement()) {
|
||||||
|
this.createDirectEditor(); // URL+text for links
|
||||||
|
} else {
|
||||||
|
this.createRichEditor(); // Rich text with styles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Container Expansion Rules (New Logic)
|
||||||
|
Container expansions are syntactic sugar for applying the insertr class to viable children. Our autodetection might need to be revised. Some anchor tags for instance can be a formatting style inside of content, or it could be a standalone navigation link. So for instance a p>a element might be formatting, while div>a could be a link. This might resolve itself when we respect boundaries, but would need further investigation.
|
||||||
|
|
||||||
|
### Stopping Rules:
|
||||||
|
- **Stop**: Element has `.insertr` (respect boundaries)
|
||||||
|
- **Stop**: Multi-property element found (`<a>`, `<button>`, `<img>` get direct editing)
|
||||||
|
- **Stop**: Framework containers (React, Gatsby artifacts)
|
||||||
|
- **Continue**: Semantic containers with viable children
|
||||||
|
|
||||||
|
## Benefits of This Refactoring
|
||||||
|
|
||||||
|
### 🚀 **Massive Code Reduction**
|
||||||
|
- Remove 3 content type handlers → 1 HTML handler
|
||||||
|
- Remove 2 editor types → 1 unified system
|
||||||
|
- Remove strategy detection → linear flow
|
||||||
|
- Remove attribute generation → tag-based detection
|
||||||
|
|
||||||
|
### 🎯 **Consistent Behavior**
|
||||||
|
- `<h1>Our <em>Services</em></h1>` always preserves formatting
|
||||||
|
- Style detection always runs
|
||||||
|
- No more edge cases between strategies
|
||||||
|
|
||||||
|
### ⚡ **Better Performance**
|
||||||
|
- No strategy detection overhead
|
||||||
|
- No conditional branching complexity
|
||||||
|
- Simpler initialization paths
|
||||||
|
|
||||||
|
### 🛠️ **Easier Maintenance**
|
||||||
|
- Single code path to test and debug
|
||||||
|
- Unified behavior aligns with CLASSES.md
|
||||||
|
- HTML-first philosophy consistently applied
|
||||||
|
|
||||||
|
## Current Working Files
|
||||||
|
|
||||||
|
### Fixed in This Session:
|
||||||
|
- ✅ **Style preview system** - Now uses CSS isolation for perfect previews
|
||||||
|
- ✅ **Simple demo cleanup** - Removed unsupported examples 6 & 8
|
||||||
|
- ✅ **Default demo preparation** - Stripped data attributes for clean enhancement
|
||||||
|
|
||||||
|
### Next Session Priorities:
|
||||||
|
1. **Remove content type routing** from StyleAwareEditor
|
||||||
|
2. **Implement element-based behavior detection**
|
||||||
|
3. **Create direct multi-property editors** for better UX
|
||||||
|
4. **Enhance container expansion logic** with smart stopping rules
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
✅ **Phase 1, 2 & 3 COMPLETED** - Complete HTML-first architecture with container transformation!
|
||||||
|
|
||||||
|
### ✅ **Completed Across Multiple Sessions**:
|
||||||
|
|
||||||
|
#### **Phase 1 & 2: HTML-First Architecture**
|
||||||
|
- **Removed content type routing** from StyleAwareEditor - now uses unified HTML-first approach
|
||||||
|
- **Eliminated simple/textarea editor** that stripped HTML formatting
|
||||||
|
- **Implemented element-based behavior detection** using tag names (no more `data-content-type` attributes)
|
||||||
|
- **Created direct multi-property editors** for `<a>`, `<button>`, `<img>` elements
|
||||||
|
- **Removed `data-content-type` generation** from backend enhancer
|
||||||
|
- **Updated database schema** - completely removed type field from content and content_versions tables
|
||||||
|
- **Regenerated all sqlc code** with new schema
|
||||||
|
- **Simplified API layer** to be purely HTML-based
|
||||||
|
|
||||||
|
#### **Phase 3: Container Transformation**
|
||||||
|
- **Implemented backend container transformation** following CLASSES.md syntactic sugar specification
|
||||||
|
- **Added addClass/removeClass methods** to ContentEngine for proper class manipulation
|
||||||
|
- **Removed frontend container expansion logic** - frontend now only finds enhanced elements
|
||||||
|
- **Fixed StyleAwareEditor runtime error** (hasMultiPropertyElements method)
|
||||||
|
- **Verified container transformation** works correctly: `<section class="insertr">` → children get `.insertr`
|
||||||
|
|
||||||
|
### 🎯 **Results Achieved**:
|
||||||
|
- **Massive code reduction**: 3 content handlers → 1 unified system
|
||||||
|
- **Consistent behavior**: All content now uses HTML preservation with style detection
|
||||||
|
- **Element-based UX**: `<a>` tags get direct URL+text editor, `<p>` tags get rich text with formatting
|
||||||
|
- **Container transformation**: Syntactic sugar working per CLASSES.md specification
|
||||||
|
- **Simplified frontend**: No runtime container expansion - backend handles transformation
|
||||||
|
- **Zero breaking changes**: User-facing functionality maintained while simplifying backend
|
||||||
|
|
||||||
|
### 🚀 **System Now Works As**:
|
||||||
|
```javascript
|
||||||
|
// Single path for ALL .insertr elements
|
||||||
|
class StyleAwareEditor {
|
||||||
|
async initialize() {
|
||||||
|
// 1. Always extract HTML with preservation
|
||||||
|
this.originalContent = this.htmlEngine.extractForEditing(this.element);
|
||||||
|
|
||||||
|
// 2. Always detect styles from nested elements
|
||||||
|
this.detectedStyles = this.styleEngine.detectStyles(this.element);
|
||||||
|
|
||||||
|
// 3. Element-based UX routing
|
||||||
|
if (this.isMultiPropertyElement()) {
|
||||||
|
this.createDirectEditor(); // URL+text for links
|
||||||
|
} else {
|
||||||
|
this.createRichEditor(); // Rich text with styles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Confidence Level**: High - All changes successfully implemented and tested. Server starts correctly, builds complete successfully.
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -15,11 +16,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var enhanceCmd = &cobra.Command{
|
var enhanceCmd = &cobra.Command{
|
||||||
Use: "enhance [input-dir]",
|
Use: "enhance [input-path]",
|
||||||
Short: "Enhance HTML files by injecting content from database",
|
Short: "Enhance HTML files by injecting content from database",
|
||||||
Long: `Enhance processes HTML files and injects latest content from the database
|
Long: `Enhance processes HTML files and injects latest content from the database
|
||||||
while adding editing capabilities. This is the core build-time enhancement
|
while adding editing capabilities. This is the core build-time enhancement
|
||||||
process that transforms static HTML into an editable CMS.`,
|
process that transforms static HTML into an editable CMS.
|
||||||
|
|
||||||
|
The input can be either a directory (to enhance all HTML files) or a single
|
||||||
|
HTML file. Non-HTML files in directories are copied as-is.`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
Run: runEnhance,
|
Run: runEnhance,
|
||||||
}
|
}
|
||||||
@@ -36,11 +40,23 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runEnhance(cmd *cobra.Command, args []string) {
|
func runEnhance(cmd *cobra.Command, args []string) {
|
||||||
inputDir := args[0]
|
inputPath := args[0]
|
||||||
|
|
||||||
// Validate input directory
|
// Validate input path and determine if it's a file or directory
|
||||||
if _, err := os.Stat(inputDir); os.IsNotExist(err) {
|
inputStat, err := os.Stat(inputPath)
|
||||||
log.Fatalf("Input directory does not exist: %s", inputDir)
|
if os.IsNotExist(err) {
|
||||||
|
log.Fatalf("Input path does not exist: %s", inputPath)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error accessing input path: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
isFile := !inputStat.IsDir()
|
||||||
|
if isFile {
|
||||||
|
// Validate that single files are HTML files
|
||||||
|
if !strings.HasSuffix(strings.ToLower(inputPath), ".html") {
|
||||||
|
log.Fatalf("Single file input must be an HTML file (.html extension): %s", inputPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get configuration values
|
// Get configuration values
|
||||||
@@ -51,9 +67,9 @@ func runEnhance(cmd *cobra.Command, args []string) {
|
|||||||
outputDir := viper.GetString("cli.output")
|
outputDir := viper.GetString("cli.output")
|
||||||
|
|
||||||
// Auto-derive site_id for demo paths or validate for production
|
// Auto-derive site_id for demo paths or validate for production
|
||||||
if strings.Contains(inputDir, "/demos/") || strings.Contains(inputDir, "./demos/") {
|
if strings.Contains(inputPath, "/demos/") || strings.Contains(inputPath, "./demos/") {
|
||||||
// Auto-derive site_id from demo path
|
// Auto-derive site_id from demo path
|
||||||
siteID = content.DeriveOrValidateSiteID(inputDir, siteID)
|
siteID = content.DeriveOrValidateSiteID(inputPath, siteID)
|
||||||
} else {
|
} else {
|
||||||
// Validate site_id for non-demo paths
|
// Validate site_id for non-demo paths
|
||||||
if siteID == "" || siteID == "demo" {
|
if siteID == "" || siteID == "demo" {
|
||||||
@@ -137,13 +153,27 @@ func runEnhance(cmd *cobra.Command, args []string) {
|
|||||||
enhancer := content.NewEnhancer(client, siteID, enhancementConfig)
|
enhancer := content.NewEnhancer(client, siteID, enhancementConfig)
|
||||||
|
|
||||||
fmt.Printf("🚀 Starting enhancement process...\n")
|
fmt.Printf("🚀 Starting enhancement process...\n")
|
||||||
fmt.Printf("📁 Input: %s\n", inputDir)
|
fmt.Printf("📁 Input: %s\n", inputPath)
|
||||||
fmt.Printf("📁 Output: %s\n", outputDir)
|
fmt.Printf("📁 Output: %s\n", outputDir)
|
||||||
fmt.Printf("🏷️ Site ID: %s\n\n", siteID)
|
fmt.Printf("🏷️ Site ID: %s\n\n", siteID)
|
||||||
|
|
||||||
// Enhance directory
|
// Enhance based on input type
|
||||||
if err := enhancer.EnhanceDirectory(inputDir, outputDir); err != nil {
|
if isFile {
|
||||||
log.Fatalf("Enhancement failed: %v", err)
|
// For single files, determine the output file path
|
||||||
|
outputFilePath := outputDir
|
||||||
|
if stat, err := os.Stat(outputDir); err == nil && stat.IsDir() {
|
||||||
|
// Output is a directory, use input filename in output directory
|
||||||
|
outputFilePath = filepath.Join(outputDir, filepath.Base(inputPath))
|
||||||
|
}
|
||||||
|
// If output doesn't exist or is a file, use it as-is as the output file path
|
||||||
|
|
||||||
|
if err := enhancer.EnhanceFile(inputPath, outputFilePath); err != nil {
|
||||||
|
log.Fatalf("Enhancement failed: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := enhancer.EnhanceDirectory(inputPath, outputDir); err != nil {
|
||||||
|
log.Fatalf("Enhancement failed: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n✅ Enhancement complete! Enhanced files available in: %s\n", outputDir)
|
fmt.Printf("\n✅ Enhancement complete! Enhanced files available in: %s\n", outputDir)
|
||||||
|
|||||||
@@ -314,14 +314,7 @@ func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine content type: use provided type, fallback to existing type, default to "text"
|
// HTML-first approach: no content type needed
|
||||||
contentType := req.Type
|
|
||||||
if contentType == "" && contentExists {
|
|
||||||
contentType = h.getContentType(existingContent)
|
|
||||||
}
|
|
||||||
if contentType == "" {
|
|
||||||
contentType = "text" // default type for new content
|
|
||||||
}
|
|
||||||
|
|
||||||
var content interface{}
|
var content interface{}
|
||||||
var err error
|
var err error
|
||||||
@@ -333,7 +326,6 @@ func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
|
|||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
HtmlContent: req.HTMLContent,
|
HtmlContent: req.HTMLContent,
|
||||||
OriginalTemplate: toNullString(req.OriginalTemplate),
|
OriginalTemplate: toNullString(req.OriginalTemplate),
|
||||||
Type: contentType,
|
|
||||||
LastEditedBy: userID,
|
LastEditedBy: userID,
|
||||||
})
|
})
|
||||||
case "postgresql":
|
case "postgresql":
|
||||||
@@ -342,7 +334,6 @@ func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
|
|||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
HtmlContent: req.HTMLContent,
|
HtmlContent: req.HTMLContent,
|
||||||
OriginalTemplate: toNullString(req.OriginalTemplate),
|
OriginalTemplate: toNullString(req.OriginalTemplate),
|
||||||
Type: contentType,
|
|
||||||
LastEditedBy: userID,
|
LastEditedBy: userID,
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
@@ -447,7 +438,6 @@ func (h *ContentHandler) UpdateContent(w http.ResponseWriter, r *http.Request) {
|
|||||||
case "sqlite3":
|
case "sqlite3":
|
||||||
updatedContent, err = h.database.GetSQLiteQueries().UpdateContent(context.Background(), sqlite.UpdateContentParams{
|
updatedContent, err = h.database.GetSQLiteQueries().UpdateContent(context.Background(), sqlite.UpdateContentParams{
|
||||||
HtmlContent: req.HTMLContent,
|
HtmlContent: req.HTMLContent,
|
||||||
Type: h.getContentType(existingContent),
|
|
||||||
LastEditedBy: userID,
|
LastEditedBy: userID,
|
||||||
ID: contentID,
|
ID: contentID,
|
||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
@@ -455,7 +445,6 @@ func (h *ContentHandler) UpdateContent(w http.ResponseWriter, r *http.Request) {
|
|||||||
case "postgresql":
|
case "postgresql":
|
||||||
updatedContent, err = h.database.GetPostgreSQLQueries().UpdateContent(context.Background(), postgresql.UpdateContentParams{
|
updatedContent, err = h.database.GetPostgreSQLQueries().UpdateContent(context.Background(), postgresql.UpdateContentParams{
|
||||||
HtmlContent: req.HTMLContent,
|
HtmlContent: req.HTMLContent,
|
||||||
Type: h.getContentType(existingContent),
|
|
||||||
LastEditedBy: userID,
|
LastEditedBy: userID,
|
||||||
ID: contentID,
|
ID: contentID,
|
||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
@@ -664,7 +653,6 @@ func (h *ContentHandler) RollbackContent(w http.ResponseWriter, r *http.Request)
|
|||||||
sqliteVersion := targetVersion.(sqlite.ContentVersion)
|
sqliteVersion := targetVersion.(sqlite.ContentVersion)
|
||||||
updatedContent, err = h.database.GetSQLiteQueries().UpdateContent(context.Background(), sqlite.UpdateContentParams{
|
updatedContent, err = h.database.GetSQLiteQueries().UpdateContent(context.Background(), sqlite.UpdateContentParams{
|
||||||
HtmlContent: sqliteVersion.HtmlContent,
|
HtmlContent: sqliteVersion.HtmlContent,
|
||||||
Type: sqliteVersion.Type,
|
|
||||||
LastEditedBy: userID,
|
LastEditedBy: userID,
|
||||||
ID: contentID,
|
ID: contentID,
|
||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
@@ -673,7 +661,6 @@ func (h *ContentHandler) RollbackContent(w http.ResponseWriter, r *http.Request)
|
|||||||
pgVersion := targetVersion.(postgresql.ContentVersion)
|
pgVersion := targetVersion.(postgresql.ContentVersion)
|
||||||
updatedContent, err = h.database.GetPostgreSQLQueries().UpdateContent(context.Background(), postgresql.UpdateContentParams{
|
updatedContent, err = h.database.GetPostgreSQLQueries().UpdateContent(context.Background(), postgresql.UpdateContentParams{
|
||||||
HtmlContent: pgVersion.HtmlContent,
|
HtmlContent: pgVersion.HtmlContent,
|
||||||
Type: pgVersion.Type,
|
|
||||||
LastEditedBy: userID,
|
LastEditedBy: userID,
|
||||||
ID: contentID,
|
ID: contentID,
|
||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
@@ -704,7 +691,6 @@ func (h *ContentHandler) convertToAPIContent(content interface{}) ContentItem {
|
|||||||
SiteID: c.SiteID,
|
SiteID: c.SiteID,
|
||||||
HTMLContent: c.HtmlContent,
|
HTMLContent: c.HtmlContent,
|
||||||
OriginalTemplate: fromNullString(c.OriginalTemplate),
|
OriginalTemplate: fromNullString(c.OriginalTemplate),
|
||||||
Type: c.Type,
|
|
||||||
CreatedAt: time.Unix(c.CreatedAt, 0),
|
CreatedAt: time.Unix(c.CreatedAt, 0),
|
||||||
UpdatedAt: time.Unix(c.UpdatedAt, 0),
|
UpdatedAt: time.Unix(c.UpdatedAt, 0),
|
||||||
LastEditedBy: c.LastEditedBy,
|
LastEditedBy: c.LastEditedBy,
|
||||||
@@ -716,7 +702,6 @@ func (h *ContentHandler) convertToAPIContent(content interface{}) ContentItem {
|
|||||||
SiteID: c.SiteID,
|
SiteID: c.SiteID,
|
||||||
HTMLContent: c.HtmlContent,
|
HTMLContent: c.HtmlContent,
|
||||||
OriginalTemplate: fromNullString(c.OriginalTemplate),
|
OriginalTemplate: fromNullString(c.OriginalTemplate),
|
||||||
Type: c.Type,
|
|
||||||
CreatedAt: time.Unix(c.CreatedAt, 0),
|
CreatedAt: time.Unix(c.CreatedAt, 0),
|
||||||
UpdatedAt: time.Unix(c.UpdatedAt, 0),
|
UpdatedAt: time.Unix(c.UpdatedAt, 0),
|
||||||
LastEditedBy: c.LastEditedBy,
|
LastEditedBy: c.LastEditedBy,
|
||||||
@@ -757,7 +742,6 @@ func (h *ContentHandler) convertToAPIVersionList(versionList interface{}) []Cont
|
|||||||
SiteID: version.SiteID,
|
SiteID: version.SiteID,
|
||||||
HTMLContent: version.HtmlContent,
|
HTMLContent: version.HtmlContent,
|
||||||
OriginalTemplate: fromNullString(version.OriginalTemplate),
|
OriginalTemplate: fromNullString(version.OriginalTemplate),
|
||||||
Type: version.Type,
|
|
||||||
CreatedAt: time.Unix(version.CreatedAt, 0),
|
CreatedAt: time.Unix(version.CreatedAt, 0),
|
||||||
CreatedBy: version.CreatedBy,
|
CreatedBy: version.CreatedBy,
|
||||||
}
|
}
|
||||||
@@ -773,7 +757,6 @@ func (h *ContentHandler) convertToAPIVersionList(versionList interface{}) []Cont
|
|||||||
SiteID: version.SiteID,
|
SiteID: version.SiteID,
|
||||||
HTMLContent: version.HtmlContent,
|
HTMLContent: version.HtmlContent,
|
||||||
OriginalTemplate: fromNullString(version.OriginalTemplate),
|
OriginalTemplate: fromNullString(version.OriginalTemplate),
|
||||||
Type: version.Type,
|
|
||||||
CreatedAt: time.Unix(version.CreatedAt, 0),
|
CreatedAt: time.Unix(version.CreatedAt, 0),
|
||||||
CreatedBy: version.CreatedBy,
|
CreatedBy: version.CreatedBy,
|
||||||
}
|
}
|
||||||
@@ -792,7 +775,6 @@ func (h *ContentHandler) createContentVersion(content interface{}) error {
|
|||||||
SiteID: c.SiteID,
|
SiteID: c.SiteID,
|
||||||
HtmlContent: c.HtmlContent,
|
HtmlContent: c.HtmlContent,
|
||||||
OriginalTemplate: c.OriginalTemplate,
|
OriginalTemplate: c.OriginalTemplate,
|
||||||
Type: c.Type,
|
|
||||||
CreatedBy: c.LastEditedBy,
|
CreatedBy: c.LastEditedBy,
|
||||||
})
|
})
|
||||||
case "postgresql":
|
case "postgresql":
|
||||||
@@ -802,23 +784,12 @@ func (h *ContentHandler) createContentVersion(content interface{}) error {
|
|||||||
SiteID: c.SiteID,
|
SiteID: c.SiteID,
|
||||||
HtmlContent: c.HtmlContent,
|
HtmlContent: c.HtmlContent,
|
||||||
OriginalTemplate: c.OriginalTemplate,
|
OriginalTemplate: c.OriginalTemplate,
|
||||||
Type: c.Type,
|
|
||||||
CreatedBy: c.LastEditedBy,
|
CreatedBy: c.LastEditedBy,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return fmt.Errorf("unsupported database type")
|
return fmt.Errorf("unsupported database type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ContentHandler) getContentType(content interface{}) string {
|
|
||||||
switch h.database.GetDBType() {
|
|
||||||
case "sqlite3":
|
|
||||||
return content.(sqlite.Content).Type
|
|
||||||
case "postgresql":
|
|
||||||
return content.(postgresql.Content).Type
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ContentHandler) versionMatches(version interface{}, contentID, siteID string) bool {
|
func (h *ContentHandler) versionMatches(version interface{}, contentID, siteID string) bool {
|
||||||
switch h.database.GetDBType() {
|
switch h.database.GetDBType() {
|
||||||
case "sqlite3":
|
case "sqlite3":
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ type ContentItem struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HTMLContent string `json:"html_content"`
|
HTMLContent string `json:"html_content"`
|
||||||
OriginalTemplate string `json:"original_template"`
|
OriginalTemplate string `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
@@ -20,7 +19,6 @@ type ContentVersion struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HTMLContent string `json:"html_content"`
|
HTMLContent string `json:"html_content"`
|
||||||
OriginalTemplate string `json:"original_template"`
|
OriginalTemplate string `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
CreatedBy string `json:"created_by"`
|
CreatedBy string `json:"created_by"`
|
||||||
}
|
}
|
||||||
@@ -48,7 +46,6 @@ type CreateContentRequest struct {
|
|||||||
FilePath string `json:"file_path"` // File path for consistent ID generation
|
FilePath string `json:"file_path"` // File path for consistent ID generation
|
||||||
HTMLContent string `json:"html_content"` // HTML content value
|
HTMLContent string `json:"html_content"` // HTML content value
|
||||||
OriginalTemplate string `json:"original_template"` // Original template markup
|
OriginalTemplate string `json:"original_template"` // Original template markup
|
||||||
Type string `json:"type"` // Content type
|
|
||||||
SiteID string `json:"site_id,omitempty"` // Site identifier
|
SiteID string `json:"site_id,omitempty"` // Site identifier
|
||||||
CreatedBy string `json:"created_by,omitempty"` // User who created the content
|
CreatedBy string `json:"created_by,omitempty"` // User who created the content
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ func (c *HTTPClient) GetAllContent(siteID string) (map[string]engine.ContentItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateContent creates a new content item via HTTP API
|
// CreateContent creates a new content item via HTTP API
|
||||||
func (c *HTTPClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*engine.ContentItem, error) {
|
func (c *HTTPClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
|
||||||
// For now, HTTPClient CreateContent is not implemented for enhancer use
|
// For now, HTTPClient CreateContent is not implemented for enhancer use
|
||||||
// This would typically be used in API-driven enhancement scenarios
|
// This would typically be used in API-driven enhancement scenarios
|
||||||
return nil, fmt.Errorf("CreateContent not implemented for HTTPClient - use DatabaseClient for enhancement")
|
return nil, fmt.Errorf("CreateContent not implemented for HTTPClient - use DatabaseClient for enhancement")
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ func (d *DatabaseClient) convertToContentItem(content interface{}) engine.Conten
|
|||||||
SiteID: c.SiteID,
|
SiteID: c.SiteID,
|
||||||
HTMLContent: c.HtmlContent,
|
HTMLContent: c.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
||||||
Type: c.Type,
|
|
||||||
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
case "postgresql":
|
case "postgresql":
|
||||||
@@ -154,7 +153,6 @@ func (d *DatabaseClient) convertToContentItem(content interface{}) engine.Conten
|
|||||||
SiteID: c.SiteID,
|
SiteID: c.SiteID,
|
||||||
HTMLContent: c.HtmlContent,
|
HTMLContent: c.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
||||||
Type: c.Type,
|
|
||||||
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +181,7 @@ func (d *DatabaseClient) convertToContentItemList(contentList interface{}) []eng
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateContent creates a new content item
|
// CreateContent creates a new content item
|
||||||
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*engine.ContentItem, error) {
|
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
|
||||||
switch c.db.GetDBType() {
|
switch c.db.GetDBType() {
|
||||||
case "sqlite3":
|
case "sqlite3":
|
||||||
content, err := c.db.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
content, err := c.db.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
||||||
@@ -191,7 +189,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
HtmlContent: htmlContent,
|
HtmlContent: htmlContent,
|
||||||
OriginalTemplate: toNullString(originalTemplate),
|
OriginalTemplate: toNullString(originalTemplate),
|
||||||
Type: contentType,
|
|
||||||
LastEditedBy: lastEditedBy,
|
LastEditedBy: lastEditedBy,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -202,7 +199,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
@@ -212,7 +208,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
HtmlContent: htmlContent,
|
HtmlContent: htmlContent,
|
||||||
OriginalTemplate: toNullString(originalTemplate),
|
OriginalTemplate: toNullString(originalTemplate),
|
||||||
Type: contentType,
|
|
||||||
LastEditedBy: lastEditedBy,
|
LastEditedBy: lastEditedBy,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -223,7 +218,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
|
|||||||
@@ -20,15 +20,15 @@ func NewMockClient() *MockClient {
|
|||||||
ID: "navbar-logo-2b10ad",
|
ID: "navbar-logo-2b10ad",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "Acme Consulting Solutions",
|
HTMLContent: "Acme Consulting Solutions",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
"navbar-logo-2b10ad-a44bad": {
|
"navbar-logo-2b10ad-a44bad": {
|
||||||
ID: "navbar-logo-2b10ad-a44bad",
|
ID: "navbar-logo-2b10ad-a44bad",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "Acme Business Advisors",
|
HTMLContent: "Acme Business Advisors",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Hero Section - index.html (updated with actual IDs)
|
// Hero Section - index.html (updated with actual IDs)
|
||||||
@@ -36,22 +36,22 @@ func NewMockClient() *MockClient {
|
|||||||
ID: "hero-title-7cfeea",
|
ID: "hero-title-7cfeea",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "Transform Your Business with Strategic Expertise",
|
HTMLContent: "Transform Your Business with Strategic Expertise",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
"hero-lead-e47475": {
|
"hero-lead-e47475": {
|
||||||
ID: "hero-lead-e47475",
|
ID: "hero-lead-e47475",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "We help <strong>ambitious businesses</strong> grow through strategic planning, process optimization, and digital transformation. Our team brings 20+ years of experience to accelerate your success.",
|
HTMLContent: "We help <strong>ambitious businesses</strong> grow through strategic planning, process optimization, and digital transformation. Our team brings 20+ years of experience to accelerate your success.",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
"hero-link-76c620": {
|
"hero-link-76c620": {
|
||||||
ID: "hero-link-76c620",
|
ID: "hero-link-76c620",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "Schedule Free Consultation",
|
HTMLContent: "Schedule Free Consultation",
|
||||||
Type: "link",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Hero Section - about.html
|
// Hero Section - about.html
|
||||||
@@ -59,15 +59,15 @@ func NewMockClient() *MockClient {
|
|||||||
ID: "hero-title-c70343",
|
ID: "hero-title-c70343",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "About Our Consulting Expertise",
|
HTMLContent: "About Our Consulting Expertise",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
"hero-lead-673026": {
|
"hero-lead-673026": {
|
||||||
ID: "hero-lead-673026",
|
ID: "hero-lead-673026",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "We're a team of <strong>experienced consultants</strong> dedicated to helping small businesses thrive in today's competitive marketplace through proven strategies.",
|
HTMLContent: "We're a team of <strong>experienced consultants</strong> dedicated to helping small businesses thrive in today's competitive marketplace through proven strategies.",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Services Section
|
// Services Section
|
||||||
@@ -75,15 +75,15 @@ func NewMockClient() *MockClient {
|
|||||||
ID: "services-subtitle-c8927c",
|
ID: "services-subtitle-c8927c",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "Our Story",
|
HTMLContent: "Our Story",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
"services-text-0d96da": {
|
"services-text-0d96da": {
|
||||||
ID: "services-text-0d96da",
|
ID: "services-text-0d96da",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "<strong>Founded in 2020</strong>, Acme Consulting emerged from a simple observation: small businesses needed access to the same high-quality strategic advice that large corporations receive, but in a format that was accessible, affordable, and actionable.",
|
HTMLContent: "<strong>Founded in 2020</strong>, Acme Consulting emerged from a simple observation: small businesses needed access to the same high-quality strategic advice that large corporations receive, but in a format that was accessible, affordable, and actionable.",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Default fallback for any missing content
|
// Default fallback for any missing content
|
||||||
@@ -91,8 +91,8 @@ func NewMockClient() *MockClient {
|
|||||||
ID: "default",
|
ID: "default",
|
||||||
SiteID: "demo",
|
SiteID: "demo",
|
||||||
HTMLContent: "[Enhanced Content]",
|
HTMLContent: "[Enhanced Content]",
|
||||||
Type: "text",
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,14 +140,13 @@ func (m *MockClient) GetAllContent(siteID string) (map[string]engine.ContentItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateContent creates a new mock content item
|
// CreateContent creates a new mock content item
|
||||||
func (m *MockClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*engine.ContentItem, error) {
|
func (m *MockClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
|
||||||
// For mock client, just create and store the item
|
// For mock client, just create and store the item
|
||||||
item := engine.ContentItem{
|
item := engine.ContentItem{
|
||||||
ID: contentID,
|
ID: contentID,
|
||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
HTMLContent: htmlContent,
|
HTMLContent: htmlContent,
|
||||||
OriginalTemplate: originalTemplate,
|
OriginalTemplate: originalTemplate,
|
||||||
Type: contentType,
|
|
||||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||||
LastEditedBy: lastEditedBy,
|
LastEditedBy: lastEditedBy,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const createContent = `-- name: CreateContent :one
|
const createContent = `-- name: CreateContent :one
|
||||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateContentParams struct {
|
type CreateContentParams struct {
|
||||||
@@ -22,7 +22,6 @@ type CreateContentParams struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +31,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
|||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.OriginalTemplate,
|
arg.OriginalTemplate,
|
||||||
arg.Type,
|
|
||||||
arg.LastEditedBy,
|
arg.LastEditedBy,
|
||||||
)
|
)
|
||||||
var i Content
|
var i Content
|
||||||
@@ -41,7 +39,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -75,7 +72,7 @@ func (q *Queries) DeleteContent(ctx context.Context, arg DeleteContentParams) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAllContent = `-- name: GetAllContent :many
|
const getAllContent = `-- name: GetAllContent :many
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE site_id = $1
|
WHERE site_id = $1
|
||||||
ORDER BY updated_at DESC
|
ORDER BY updated_at DESC
|
||||||
@@ -95,7 +92,6 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -114,7 +110,7 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getBulkContent = `-- name: GetBulkContent :many
|
const getBulkContent = `-- name: GetBulkContent :many
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE site_id = $1 AND id IN ($2)
|
WHERE site_id = $1 AND id IN ($2)
|
||||||
`
|
`
|
||||||
@@ -149,7 +145,6 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -168,7 +163,7 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getContent = `-- name: GetContent :one
|
const getContent = `-- name: GetContent :one
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE id = $1 AND site_id = $2
|
WHERE id = $1 AND site_id = $2
|
||||||
`
|
`
|
||||||
@@ -186,7 +181,6 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -196,14 +190,13 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
|||||||
|
|
||||||
const updateContent = `-- name: UpdateContent :one
|
const updateContent = `-- name: UpdateContent :one
|
||||||
UPDATE content
|
UPDATE content
|
||||||
SET html_content = $1, type = $2, last_edited_by = $3
|
SET html_content = $1, last_edited_by = $2
|
||||||
WHERE id = $4 AND site_id = $5
|
WHERE id = $3 AND site_id = $4
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateContentParams struct {
|
type UpdateContentParams struct {
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
Type string `json:"type"`
|
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
@@ -212,7 +205,6 @@ type UpdateContentParams struct {
|
|||||||
func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error) {
|
func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error) {
|
||||||
row := q.db.QueryRowContext(ctx, updateContent,
|
row := q.db.QueryRowContext(ctx, updateContent,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.Type,
|
|
||||||
arg.LastEditedBy,
|
arg.LastEditedBy,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
@@ -223,7 +215,6 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -232,13 +223,12 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
|||||||
}
|
}
|
||||||
|
|
||||||
const upsertContent = `-- name: UpsertContent :one
|
const upsertContent = `-- name: UpsertContent :one
|
||||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||||
html_content = EXCLUDED.html_content,
|
html_content = EXCLUDED.html_content,
|
||||||
type = EXCLUDED.type,
|
|
||||||
last_edited_by = EXCLUDED.last_edited_by
|
last_edited_by = EXCLUDED.last_edited_by
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpsertContentParams struct {
|
type UpsertContentParams struct {
|
||||||
@@ -246,7 +236,6 @@ type UpsertContentParams struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +245,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
|||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.OriginalTemplate,
|
arg.OriginalTemplate,
|
||||||
arg.Type,
|
|
||||||
arg.LastEditedBy,
|
arg.LastEditedBy,
|
||||||
)
|
)
|
||||||
var i Content
|
var i Content
|
||||||
@@ -265,7 +253,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ type Content struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
@@ -25,7 +24,6 @@ type ContentVersion struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
CreatedBy string `json:"created_by"`
|
CreatedBy string `json:"created_by"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ CREATE TABLE content (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||||
updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||||
@@ -19,7 +18,6 @@ CREATE TABLE content_versions (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||||
created_by TEXT DEFAULT 'system' NOT NULL
|
created_by TEXT DEFAULT 'system' NOT NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ CREATE TABLE IF NOT EXISTS content (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
|
||||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||||
@@ -18,7 +17,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||||
created_by TEXT DEFAULT 'system' NOT NULL
|
created_by TEXT DEFAULT 'system' NOT NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ CREATE TABLE IF NOT EXISTS content (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
|
||||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||||
@@ -77,7 +76,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||||
created_by TEXT DEFAULT 'system' NOT NULL
|
created_by TEXT DEFAULT 'system' NOT NULL
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const createContentVersion = `-- name: CreateContentVersion :exec
|
const createContentVersion = `-- name: CreateContentVersion :exec
|
||||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, type, created_by)
|
INSERT INTO content_versions (content_id, site_id, html_content, original_template, created_by)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateContentVersionParams struct {
|
type CreateContentVersionParams struct {
|
||||||
@@ -20,7 +20,6 @@ type CreateContentVersionParams struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedBy string `json:"created_by"`
|
CreatedBy string `json:"created_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ func (q *Queries) CreateContentVersion(ctx context.Context, arg CreateContentVer
|
|||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.OriginalTemplate,
|
arg.OriginalTemplate,
|
||||||
arg.Type,
|
|
||||||
arg.CreatedBy,
|
arg.CreatedBy,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
@@ -53,7 +51,7 @@ func (q *Queries) DeleteOldVersions(ctx context.Context, arg DeleteOldVersionsPa
|
|||||||
|
|
||||||
const getAllVersionsForSite = `-- name: GetAllVersionsForSite :many
|
const getAllVersionsForSite = `-- name: GetAllVersionsForSite :many
|
||||||
SELECT
|
SELECT
|
||||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.type, cv.created_at, cv.created_by,
|
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.created_at, cv.created_by,
|
||||||
c.html_content as current_html_content
|
c.html_content as current_html_content
|
||||||
FROM content_versions cv
|
FROM content_versions cv
|
||||||
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
||||||
@@ -73,7 +71,6 @@ type GetAllVersionsForSiteRow struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
CreatedBy string `json:"created_by"`
|
CreatedBy string `json:"created_by"`
|
||||||
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
||||||
@@ -94,7 +91,6 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.CreatedBy,
|
&i.CreatedBy,
|
||||||
&i.CurrentHtmlContent,
|
&i.CurrentHtmlContent,
|
||||||
@@ -113,7 +109,7 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getContentVersion = `-- name: GetContentVersion :one
|
const getContentVersion = `-- name: GetContentVersion :one
|
||||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||||
FROM content_versions
|
FROM content_versions
|
||||||
WHERE version_id = $1
|
WHERE version_id = $1
|
||||||
`
|
`
|
||||||
@@ -127,7 +123,6 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int32) (Conte
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.CreatedBy,
|
&i.CreatedBy,
|
||||||
)
|
)
|
||||||
@@ -135,7 +130,7 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int32) (Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getContentVersionHistory = `-- name: GetContentVersionHistory :many
|
const getContentVersionHistory = `-- name: GetContentVersionHistory :many
|
||||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||||
FROM content_versions
|
FROM content_versions
|
||||||
WHERE content_id = $1 AND site_id = $2
|
WHERE content_id = $1 AND site_id = $2
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
@@ -163,7 +158,6 @@ func (q *Queries) GetContentVersionHistory(ctx context.Context, arg GetContentVe
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.CreatedBy,
|
&i.CreatedBy,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|||||||
@@ -1,38 +1,37 @@
|
|||||||
-- name: GetContent :one
|
-- name: GetContent :one
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);
|
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);
|
||||||
|
|
||||||
-- name: GetAllContent :many
|
-- name: GetAllContent :many
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE site_id = sqlc.arg(site_id)
|
WHERE site_id = sqlc.arg(site_id)
|
||||||
ORDER BY updated_at DESC;
|
ORDER BY updated_at DESC;
|
||||||
|
|
||||||
-- name: GetBulkContent :many
|
-- name: GetBulkContent :many
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE site_id = sqlc.arg(site_id) AND id IN (sqlc.slice('ids'));
|
WHERE site_id = sqlc.arg(site_id) AND id IN (sqlc.slice('ids'));
|
||||||
|
|
||||||
-- name: CreateContent :one
|
-- name: CreateContent :one
|
||||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(type), sqlc.arg(last_edited_by))
|
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(last_edited_by))
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by;
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by;
|
||||||
|
|
||||||
-- name: UpdateContent :one
|
-- name: UpdateContent :one
|
||||||
UPDATE content
|
UPDATE content
|
||||||
SET html_content = sqlc.arg(html_content), type = sqlc.arg(type), last_edited_by = sqlc.arg(last_edited_by)
|
SET html_content = sqlc.arg(html_content), last_edited_by = sqlc.arg(last_edited_by)
|
||||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id)
|
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id)
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by;
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by;
|
||||||
|
|
||||||
-- name: UpsertContent :one
|
-- name: UpsertContent :one
|
||||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(type), sqlc.arg(last_edited_by))
|
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(last_edited_by))
|
||||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||||
html_content = EXCLUDED.html_content,
|
html_content = EXCLUDED.html_content,
|
||||||
type = EXCLUDED.type,
|
|
||||||
last_edited_by = EXCLUDED.last_edited_by
|
last_edited_by = EXCLUDED.last_edited_by
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by;
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by;
|
||||||
|
|
||||||
-- name: DeleteContent :exec
|
-- name: DeleteContent :exec
|
||||||
DELETE FROM content
|
DELETE FROM content
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
-- name: CreateContentVersion :exec
|
-- name: CreateContentVersion :exec
|
||||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, type, created_by)
|
INSERT INTO content_versions (content_id, site_id, html_content, original_template, created_by)
|
||||||
VALUES (sqlc.arg(content_id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(type), sqlc.arg(created_by));
|
VALUES (sqlc.arg(content_id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(created_by));
|
||||||
|
|
||||||
-- name: GetContentVersionHistory :many
|
-- name: GetContentVersionHistory :many
|
||||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||||
FROM content_versions
|
FROM content_versions
|
||||||
WHERE content_id = sqlc.arg(content_id) AND site_id = sqlc.arg(site_id)
|
WHERE content_id = sqlc.arg(content_id) AND site_id = sqlc.arg(site_id)
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT sqlc.arg(limit_count);
|
LIMIT sqlc.arg(limit_count);
|
||||||
|
|
||||||
-- name: GetContentVersion :one
|
-- name: GetContentVersion :one
|
||||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||||
FROM content_versions
|
FROM content_versions
|
||||||
WHERE version_id = sqlc.arg(version_id);
|
WHERE version_id = sqlc.arg(version_id);
|
||||||
|
|
||||||
-- name: GetAllVersionsForSite :many
|
-- name: GetAllVersionsForSite :many
|
||||||
SELECT
|
SELECT
|
||||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.type, cv.created_at, cv.created_by,
|
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.created_at, cv.created_by,
|
||||||
c.html_content as current_html_content
|
c.html_content as current_html_content
|
||||||
FROM content_versions cv
|
FROM content_versions cv
|
||||||
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const createContent = `-- name: CreateContent :one
|
const createContent = `-- name: CreateContent :one
|
||||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateContentParams struct {
|
type CreateContentParams struct {
|
||||||
@@ -22,7 +22,6 @@ type CreateContentParams struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +31,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
|||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.OriginalTemplate,
|
arg.OriginalTemplate,
|
||||||
arg.Type,
|
|
||||||
arg.LastEditedBy,
|
arg.LastEditedBy,
|
||||||
)
|
)
|
||||||
var i Content
|
var i Content
|
||||||
@@ -41,7 +39,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -75,7 +72,7 @@ func (q *Queries) DeleteContent(ctx context.Context, arg DeleteContentParams) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAllContent = `-- name: GetAllContent :many
|
const getAllContent = `-- name: GetAllContent :many
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE site_id = ?1
|
WHERE site_id = ?1
|
||||||
ORDER BY updated_at DESC
|
ORDER BY updated_at DESC
|
||||||
@@ -95,7 +92,6 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -114,7 +110,7 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getBulkContent = `-- name: GetBulkContent :many
|
const getBulkContent = `-- name: GetBulkContent :many
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE site_id = ?1 AND id IN (/*SLICE:ids*/?)
|
WHERE site_id = ?1 AND id IN (/*SLICE:ids*/?)
|
||||||
`
|
`
|
||||||
@@ -149,7 +145,6 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -168,7 +163,7 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getContent = `-- name: GetContent :one
|
const getContent = `-- name: GetContent :one
|
||||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
FROM content
|
FROM content
|
||||||
WHERE id = ?1 AND site_id = ?2
|
WHERE id = ?1 AND site_id = ?2
|
||||||
`
|
`
|
||||||
@@ -186,7 +181,6 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -196,14 +190,13 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
|||||||
|
|
||||||
const updateContent = `-- name: UpdateContent :one
|
const updateContent = `-- name: UpdateContent :one
|
||||||
UPDATE content
|
UPDATE content
|
||||||
SET html_content = ?1, type = ?2, last_edited_by = ?3
|
SET html_content = ?1, last_edited_by = ?2
|
||||||
WHERE id = ?4 AND site_id = ?5
|
WHERE id = ?3 AND site_id = ?4
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateContentParams struct {
|
type UpdateContentParams struct {
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
Type string `json:"type"`
|
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
@@ -212,7 +205,6 @@ type UpdateContentParams struct {
|
|||||||
func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error) {
|
func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error) {
|
||||||
row := q.db.QueryRowContext(ctx, updateContent,
|
row := q.db.QueryRowContext(ctx, updateContent,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.Type,
|
|
||||||
arg.LastEditedBy,
|
arg.LastEditedBy,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
@@ -223,7 +215,6 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
@@ -232,13 +223,12 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
|||||||
}
|
}
|
||||||
|
|
||||||
const upsertContent = `-- name: UpsertContent :one
|
const upsertContent = `-- name: UpsertContent :one
|
||||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||||
html_content = EXCLUDED.html_content,
|
html_content = EXCLUDED.html_content,
|
||||||
type = EXCLUDED.type,
|
|
||||||
last_edited_by = EXCLUDED.last_edited_by
|
last_edited_by = EXCLUDED.last_edited_by
|
||||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpsertContentParams struct {
|
type UpsertContentParams struct {
|
||||||
@@ -246,7 +236,6 @@ type UpsertContentParams struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +245,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
|||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.OriginalTemplate,
|
arg.OriginalTemplate,
|
||||||
arg.Type,
|
|
||||||
arg.LastEditedBy,
|
arg.LastEditedBy,
|
||||||
)
|
)
|
||||||
var i Content
|
var i Content
|
||||||
@@ -265,7 +253,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.LastEditedBy,
|
&i.LastEditedBy,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ type Content struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
LastEditedBy string `json:"last_edited_by"`
|
LastEditedBy string `json:"last_edited_by"`
|
||||||
@@ -25,7 +24,6 @@ type ContentVersion struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
CreatedBy string `json:"created_by"`
|
CreatedBy string `json:"created_by"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ CREATE TABLE content (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||||
@@ -19,7 +18,6 @@ CREATE TABLE content_versions (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
created_by TEXT DEFAULT 'system' NOT NULL
|
created_by TEXT DEFAULT 'system' NOT NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ CREATE TABLE IF NOT EXISTS content (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
|
||||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||||
@@ -18,7 +17,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
created_by TEXT DEFAULT 'system' NOT NULL
|
created_by TEXT DEFAULT 'system' NOT NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ CREATE TABLE IF NOT EXISTS content (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
|
||||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||||
@@ -35,7 +34,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
|||||||
site_id TEXT NOT NULL,
|
site_id TEXT NOT NULL,
|
||||||
html_content TEXT NOT NULL,
|
html_content TEXT NOT NULL,
|
||||||
original_template TEXT,
|
original_template TEXT,
|
||||||
type TEXT NOT NULL,
|
|
||||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||||
created_by TEXT DEFAULT 'system' NOT NULL
|
created_by TEXT DEFAULT 'system' NOT NULL
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const createContentVersion = `-- name: CreateContentVersion :exec
|
const createContentVersion = `-- name: CreateContentVersion :exec
|
||||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, type, created_by)
|
INSERT INTO content_versions (content_id, site_id, html_content, original_template, created_by)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateContentVersionParams struct {
|
type CreateContentVersionParams struct {
|
||||||
@@ -20,7 +20,6 @@ type CreateContentVersionParams struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedBy string `json:"created_by"`
|
CreatedBy string `json:"created_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ func (q *Queries) CreateContentVersion(ctx context.Context, arg CreateContentVer
|
|||||||
arg.SiteID,
|
arg.SiteID,
|
||||||
arg.HtmlContent,
|
arg.HtmlContent,
|
||||||
arg.OriginalTemplate,
|
arg.OriginalTemplate,
|
||||||
arg.Type,
|
|
||||||
arg.CreatedBy,
|
arg.CreatedBy,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
@@ -53,7 +51,7 @@ func (q *Queries) DeleteOldVersions(ctx context.Context, arg DeleteOldVersionsPa
|
|||||||
|
|
||||||
const getAllVersionsForSite = `-- name: GetAllVersionsForSite :many
|
const getAllVersionsForSite = `-- name: GetAllVersionsForSite :many
|
||||||
SELECT
|
SELECT
|
||||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.type, cv.created_at, cv.created_by,
|
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.created_at, cv.created_by,
|
||||||
c.html_content as current_html_content
|
c.html_content as current_html_content
|
||||||
FROM content_versions cv
|
FROM content_versions cv
|
||||||
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
||||||
@@ -73,7 +71,6 @@ type GetAllVersionsForSiteRow struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HtmlContent string `json:"html_content"`
|
HtmlContent string `json:"html_content"`
|
||||||
OriginalTemplate sql.NullString `json:"original_template"`
|
OriginalTemplate sql.NullString `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
CreatedBy string `json:"created_by"`
|
CreatedBy string `json:"created_by"`
|
||||||
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
||||||
@@ -94,7 +91,6 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.CreatedBy,
|
&i.CreatedBy,
|
||||||
&i.CurrentHtmlContent,
|
&i.CurrentHtmlContent,
|
||||||
@@ -113,7 +109,7 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getContentVersion = `-- name: GetContentVersion :one
|
const getContentVersion = `-- name: GetContentVersion :one
|
||||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||||
FROM content_versions
|
FROM content_versions
|
||||||
WHERE version_id = ?1
|
WHERE version_id = ?1
|
||||||
`
|
`
|
||||||
@@ -127,7 +123,6 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int64) (Conte
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.CreatedBy,
|
&i.CreatedBy,
|
||||||
)
|
)
|
||||||
@@ -135,7 +130,7 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int64) (Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getContentVersionHistory = `-- name: GetContentVersionHistory :many
|
const getContentVersionHistory = `-- name: GetContentVersionHistory :many
|
||||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||||
FROM content_versions
|
FROM content_versions
|
||||||
WHERE content_id = ?1 AND site_id = ?2
|
WHERE content_id = ?1 AND site_id = ?2
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
@@ -163,7 +158,6 @@ func (q *Queries) GetContentVersionHistory(ctx context.Context, arg GetContentVe
|
|||||||
&i.SiteID,
|
&i.SiteID,
|
||||||
&i.HtmlContent,
|
&i.HtmlContent,
|
||||||
&i.OriginalTemplate,
|
&i.OriginalTemplate,
|
||||||
&i.Type,
|
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.CreatedBy,
|
&i.CreatedBy,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ func (c *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, err
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
@@ -63,7 +62,6 @@ func (c *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, err
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
@@ -91,7 +89,6 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +110,6 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +136,6 @@ func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +154,6 @@ func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,7 +165,7 @@ func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateContent creates a new content item
|
// CreateContent creates a new content item
|
||||||
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*ContentItem, error) {
|
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error) {
|
||||||
switch c.database.GetDBType() {
|
switch c.database.GetDBType() {
|
||||||
case "sqlite3":
|
case "sqlite3":
|
||||||
content, err := c.database.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
content, err := c.database.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
||||||
@@ -179,7 +173,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
HtmlContent: htmlContent,
|
HtmlContent: htmlContent,
|
||||||
OriginalTemplate: toNullString(originalTemplate),
|
OriginalTemplate: toNullString(originalTemplate),
|
||||||
Type: contentType,
|
|
||||||
LastEditedBy: lastEditedBy,
|
LastEditedBy: lastEditedBy,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -190,7 +183,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
@@ -200,7 +192,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: siteID,
|
SiteID: siteID,
|
||||||
HtmlContent: htmlContent,
|
HtmlContent: htmlContent,
|
||||||
OriginalTemplate: toNullString(originalTemplate),
|
OriginalTemplate: toNullString(originalTemplate),
|
||||||
Type: contentType,
|
|
||||||
LastEditedBy: lastEditedBy,
|
LastEditedBy: lastEditedBy,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -211,7 +202,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
|||||||
SiteID: content.SiteID,
|
SiteID: content.SiteID,
|
||||||
HTMLContent: content.HtmlContent,
|
HTMLContent: content.HtmlContent,
|
||||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||||
Type: content.Type,
|
|
||||||
LastEditedBy: content.LastEditedBy,
|
LastEditedBy: content.LastEditedBy,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ type ContentResult struct {
|
|||||||
type ProcessedElement struct {
|
type ProcessedElement struct {
|
||||||
Node *html.Node // HTML node
|
Node *html.Node // HTML node
|
||||||
ID string // Generated content ID
|
ID string // Generated content ID
|
||||||
Type string // Content type (text, link)
|
|
||||||
Content string // Injected content (if any)
|
Content string // Injected content (if any)
|
||||||
Generated bool // Whether ID was generated (vs existing)
|
Generated bool // Whether ID was generated (vs existing)
|
||||||
Tag string // Element tag name
|
Tag string // Element tag name
|
||||||
@@ -48,7 +47,7 @@ type ContentClient interface {
|
|||||||
GetContent(siteID, contentID string) (*ContentItem, error)
|
GetContent(siteID, contentID string) (*ContentItem, error)
|
||||||
GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error)
|
GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error)
|
||||||
GetAllContent(siteID string) (map[string]ContentItem, error)
|
GetAllContent(siteID string) (map[string]ContentItem, error)
|
||||||
CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*ContentItem, error)
|
CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContentItem represents a piece of content from the database
|
// ContentItem represents a piece of content from the database
|
||||||
@@ -57,7 +56,6 @@ type ContentItem struct {
|
|||||||
SiteID string `json:"site_id"`
|
SiteID string `json:"site_id"`
|
||||||
HTMLContent string `json:"html_content"`
|
HTMLContent string `json:"html_content"`
|
||||||
OriginalTemplate string `json:"original_template"`
|
OriginalTemplate string `json:"original_template"`
|
||||||
Type string `json:"type"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
UpdatedAt string `json:"updated_at"`
|
||||||
LastEditedBy string `json:"last_edited_by,omitempty"`
|
LastEditedBy string `json:"last_edited_by,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,40 +179,149 @@ func isContainer(node *html.Node) bool {
|
|||||||
"main": true,
|
"main": true,
|
||||||
"aside": true,
|
"aside": true,
|
||||||
"nav": true,
|
"nav": true,
|
||||||
|
"ul": true, // Phase 3: Lists are containers
|
||||||
|
"ol": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return containerTags[node.Data]
|
return containerTags[node.Data]
|
||||||
}
|
}
|
||||||
|
|
||||||
// findViableChildren finds all child elements that are viable for editing
|
// findViableChildren finds all descendant elements that should get .insertr class
|
||||||
|
// Phase 3: Recursive traversal with block/inline classification and boundary respect
|
||||||
func findViableChildren(node *html.Node) []*html.Node {
|
func findViableChildren(node *html.Node) []*html.Node {
|
||||||
var viable []*html.Node
|
var viable []*html.Node
|
||||||
|
traverseForViableElements(node, &viable)
|
||||||
|
return viable
|
||||||
|
}
|
||||||
|
|
||||||
|
// traverseForViableElements recursively traverses all descendants, stopping at .insertr boundaries
|
||||||
|
func traverseForViableElements(node *html.Node, viable *[]*html.Node) {
|
||||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||||
// Skip whitespace-only text nodes
|
|
||||||
if child.Type == html.TextNode {
|
|
||||||
if strings.TrimSpace(child.Data) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only consider element nodes
|
// Only consider element nodes
|
||||||
if child.Type != html.ElementNode {
|
if child.Type != html.ElementNode {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip self-closing elements for now
|
// BOUNDARY: Stop if element already has .insertr class
|
||||||
if isSelfClosing(child) {
|
if hasInsertrClass(child) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if element has editable content (improved logic)
|
// Skip deferred complex elements (tables, forms)
|
||||||
if hasEditableContent(child) {
|
if isDeferredElement(child) {
|
||||||
viable = append(viable, child)
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if this element should get .insertr
|
||||||
|
if shouldGetInsertrClass(child) {
|
||||||
|
*viable = append(*viable, child)
|
||||||
|
// Don't traverse children - they're handled by this element's expansion
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue traversing if this is just a container
|
||||||
|
traverseForViableElements(child, viable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 3: Block vs Inline element classification
|
||||||
|
func isBlockElement(node *html.Node) bool {
|
||||||
|
blockTags := map[string]bool{
|
||||||
|
// Content blocks
|
||||||
|
"h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true,
|
||||||
|
"p": true, "div": true, "article": true, "section": true, "nav": true,
|
||||||
|
"header": true, "footer": true, "main": true, "aside": true,
|
||||||
|
// Lists
|
||||||
|
"ul": true, "ol": true, "li": true,
|
||||||
|
// Interactive (when at block level)
|
||||||
|
"button": true, "a": true, "img": true, "video": true, "audio": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return viable
|
return blockTags[node.Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInlineElement checks if element is inline formatting (never gets .insertr)
|
||||||
|
func isInlineElement(node *html.Node) bool {
|
||||||
|
inlineTags := map[string]bool{
|
||||||
|
"strong": true, "b": true, "em": true, "i": true, "span": true,
|
||||||
|
"code": true, "small": true, "sub": true, "sup": true, "br": true,
|
||||||
|
"mark": true, "kbd": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return inlineTags[node.Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
// isContextSensitive checks if element can be block or inline (a, button)
|
||||||
|
func isContextSensitive(node *html.Node) bool {
|
||||||
|
contextTags := map[string]bool{
|
||||||
|
"a": true,
|
||||||
|
"button": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return contextTags[node.Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInBlockContext determines if context-sensitive element should be treated as block
|
||||||
|
func isInBlockContext(node *html.Node) bool {
|
||||||
|
parent := node.Parent
|
||||||
|
if parent == nil || parent.Type != html.ElementNode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If parent is a content element, this is inline formatting
|
||||||
|
contentElements := map[string]bool{
|
||||||
|
"p": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true,
|
||||||
|
"li": true, "td": true, "th": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return !contentElements[parent.Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldGetInsertrClass determines if element should receive .insertr class
|
||||||
|
func shouldGetInsertrClass(node *html.Node) bool {
|
||||||
|
// Always block elements get .insertr
|
||||||
|
if isBlockElement(node) && !isContextSensitive(node) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context-sensitive elements depend on parent context
|
||||||
|
if isContextSensitive(node) {
|
||||||
|
return isInBlockContext(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline elements never get .insertr
|
||||||
|
if isInlineElement(node) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self-closing elements - only img gets .insertr when block-level
|
||||||
|
if isSelfClosing(node) {
|
||||||
|
return node.Data == "img" && isInBlockContext(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDeferredElement checks for complex elements that need separate planning
|
||||||
|
func isDeferredElement(node *html.Node) bool {
|
||||||
|
deferredTags := map[string]bool{
|
||||||
|
"table": true, "tr": true, "td": true, "th": true,
|
||||||
|
"thead": true, "tbody": true, "tfoot": true,
|
||||||
|
"form": true, "input": true, "textarea": true, "select": true, "option": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferredTags[node.Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasInsertrClass checks if node has class="insertr"
|
||||||
|
func hasInsertrClass(node *html.Node) bool {
|
||||||
|
classes := GetClasses(node)
|
||||||
|
for _, class := range classes {
|
||||||
|
if class == "insertr" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// findViableChildrenLegacy uses the old text-only logic for backwards compatibility
|
// findViableChildrenLegacy uses the old text-only logic for backwards compatibility
|
||||||
|
|||||||
Reference in New Issue
Block a user