- Implement complete mushroom foraging blog with chanterelles article - Add rich demonstration of .insertr and .insertr-add functionality - Include comprehensive documentation for future .insertr-content vision - Update project styling and configuration to support blog demo - Enhance engine and API handlers for improved content management
13 KiB
Feature Document: Draft/Publish System for Insertr CMS
Overview
This document outlines the design and implementation plan for adding draft/publish functionality to Insertr CMS. Currently, all content changes are immediately reflected when enhancement is triggered manually. This feature will introduce a proper editorial workflow with draft states and controlled publishing.
Problem Statement
Current State
- All content stored in database is immediately available for enhancement
- Manual "Enhance" button triggers immediate file updates with latest content
- No separation between working drafts and production-ready content
- Not suitable for production environments where editorial approval is needed
User Pain Points
- Editors cannot safely make changes without affecting live site
- No preview workflow for reviewing changes before going live
- All-or-nothing manual enhancement process
- No rollback mechanism for published content
Industry Research: How Other CMS Handle Drafts
WordPress
Approach: Single table (wp_posts) with state field (post_status)
States: draft, pending, publish, future, private, trash, auto-draft
Storage: All content in one table, differentiated by status field
Pros: Simple schema, easy queries, unified storage
Cons: No separation between draft and live data, potential performance issues
Drupal
Approach: Content moderation module with workflow states
States: Configurable (draft, needs_review, published, archived, etc.)
Storage: Moderation state entities linked to content revisions
Pros: Flexible workflows, proper revision tracking, role-based transitions
Cons: Complex architecture, steep learning curve
Contentful (Headless)
Approach: Separate published/draft versions with sync API
States: draft, published, changed, archived
Storage: Maintains both draft and published versions simultaneously
Pros: Performance optimized, global CDN delivery, precise change tracking
Cons: Complex API, higher storage overhead, sync complexity
Ghost
Approach: Single table with status field plus scheduled publishing
States: draft, published, scheduled, sent
Storage: Uses status field + published_at timestamp
Pros: Simple but effective, good scheduling support
Cons: Limited editorial workflow, no approval processes
Strapi
Approach: Draft & Publish feature with timestamp-based differentiation
States: draft, published
Storage: Single table with published_at field (null = draft)
Pros: Clean API separation, optional feature, good performance
Cons: Limited workflow states, manual schema management
Requirements
Functional Requirements
- FR1: Editors can save draft content without affecting published site
- FR2: Editors can preview changes before publishing
- FR3: Authorized users can publish draft content to live site
- FR4: System supports rollback to previous published versions
- FR5: Clear visual indication of draft vs published state
- FR6: Auto-save functionality to prevent content loss
Non-Functional Requirements
- NFR1: Backward compatibility with existing content
- NFR2: Minimal performance impact on content editing
- NFR3: Support for concurrent editing workflows
- NFR4: Audit trail for all publishing actions
Recommended Solution: State-Based Approach
Based on industry research and our existing architecture, we recommend following the WordPress/Ghost pattern with a state field approach. This provides the best balance of simplicity, performance, and functionality.
Schema Changes
Core Change: Add state tracking to existing content_versions table:
-- Add state column to existing content_versions table
ALTER TABLE content_versions ADD COLUMN state TEXT DEFAULT 'history' NOT NULL
CHECK (state IN ('history', 'draft', 'live'));
-- Create index for efficient state-based queries
CREATE INDEX idx_content_versions_state ON content_versions(content_id, site_id, state);
-- Ensure only one draft and one live version per content item
CREATE UNIQUE INDEX idx_content_versions_unique_draft
ON content_versions(content_id, site_id) WHERE state = 'draft';
CREATE UNIQUE INDEX idx_content_versions_unique_live
ON content_versions(content_id, site_id) WHERE state = 'live';
Migration Strategy:
- All existing
content_versionsentries becomestate='history' - Current
contenttable entries migrate tocontent_versionswithstate='live' - Drop
contenttable after migration (everything now incontent_versions)
Content States
| State | Description | Query Pattern |
|---|---|---|
history |
Previous versions, for rollback | WHERE state = 'history' ORDER BY created_at DESC |
draft |
Current working version, not published | WHERE state = 'draft' |
live |
Currently published version | WHERE state = 'live' |
Workflow Logic
Auto-save Process:
- User edits content → Auto-save creates/updates
state='draft'version - Only one draft version exists per content item (upsert pattern)
- Previous draft becomes
state='history'
Publishing Process:
- User clicks "Publish" → Current draft version updated to
state='live' - Previous live version becomes
state='history' - Enhancement triggered with all
state='live'content
Rollback Process:
- User selects historical version → Copy to new
state='live'version - Previous live version becomes
state='history' - Enhancement triggered
API Design
New Endpoints:
GET /api/content/{id}?state=draft|live|history # Get content in specific state
POST /api/content/{id}/save-draft # Save as draft (auto-save)
POST /api/content/{id}/publish # Publish draft to live
POST /api/content/{id}/rollback/{version_id} # Rollback to specific version
GET /api/content/{id}/diff # Compare draft vs live
POST /api/enhancement/preview # Preview site with draft content
GET /api/status/changes # List all unpublished changes
POST /api/content/bulk-publish # Publish multiple items
Enhanced Endpoints:
PUT /api/content/{id} # Now saves as draft by default
POST /api/enhancement # Only processes 'live' content
Repository Layer Changes
Core Queries:
// Get current live content for enhancement
func (r *Repository) GetLiveContent(siteID, contentID string) (*Content, error) {
return r.queryContent(siteID, contentID, "live")
}
// Get current draft for editing
func (r *Repository) GetDraftContent(siteID, contentID string) (*Content, error) {
return r.queryContent(siteID, contentID, "draft")
}
// Save as draft (upsert pattern)
func (r *Repository) SaveDraft(content *Content) error {
// Mark existing draft as history
r.updateState(content.ID, content.SiteID, "draft", "history")
// Insert new draft
return r.insertContentVersion(content, "draft")
}
// Publish draft to live
func (r *Repository) PublishDraft(siteID, contentID, publishedBy string) error {
// Mark existing live as history
r.updateState(contentID, siteID, "live", "history")
// Update draft to live
return r.updateState(contentID, siteID, "draft", "live")
}
Strengths of This Approach
1. Simplicity
- Single table with state field (WordPress/Ghost pattern)
- Minimal schema changes to existing system
- Easy to understand and maintain
2. Performance
- Efficient state-based queries with proper indexing
- No complex joins between draft/live tables
- Leverages existing version history system
3. Backward Compatibility
- Existing content migrates cleanly to 'live' state
- Current APIs work with minimal changes
- Gradual rollout possible
4. Storage Efficiency
- No duplicate content storage (unlike Contentful approach)
- Reuses existing version infrastructure
- History naturally maintained
5. Query Simplicity
-- Get all draft content for a site
SELECT * FROM content_versions WHERE site_id = ? AND state = 'draft';
-- Get all live content for enhancement
SELECT * FROM content_versions WHERE site_id = ? AND state = 'live';
-- Check if content has unpublished changes
SELECT COUNT(*) FROM content_versions
WHERE content_id = ? AND site_id = ? AND state = 'draft';
Weaknesses and Potential Roadblocks
1. State Management Complexity
Risk: Ensuring state transitions are atomic and consistent
Mitigation:
- Use database transactions for state changes
- Implement state validation triggers
- Add comprehensive error handling
2. Concurrent Editing Conflicts
Risk: Multiple editors creating conflicting draft versions
Mitigation:
- Unique constraints prevent multiple drafts
- Last-writer-wins with conflict detection
- Consider optimistic locking for future enhancement
3. Auto-save Performance
Risk: Frequent auto-save creating too many history versions
Mitigation:
- Implement debounced auto-save (30-second intervals)
- Consider version consolidation for excessive history
- Monitor database growth patterns
4. Migration Risk
Risk: Data loss or corruption during content table migration
Mitigation:
- Comprehensive backup before migration
- Gradual migration with validation steps
- Rollback plan if migration fails
5. Limited Workflow States
Risk: Only 3 states may be insufficient for complex editorial workflows
Mitigation:
- Start simple, extend states later if needed
- Most CMS start with basic draft/live model
- Consider "scheduled" state for future enhancement
UI/UX Changes
Control Panel Updates
- Replace "🔄 Enhance" with "💾 Save Draft" / "🚀 Publish"
- Add state indicators: 🟡 Draft Pending, 🟢 Published, 🔴 Error
- Add "👁️ Preview Changes" button for draft enhancement
- Show "📊 Publishing Status" with count of unpublished changes
New UI Components
- Diff viewer showing draft vs published changes
- Publishing confirmation dialog with change summary
- Bulk publishing interface for multiple content items
- Version history with rollback capability
Implementation Plan
Phase 1: Database Foundation (Week 1)
- Add
statecolumn tocontent_versionstable - Create state-based indexes and constraints
- Write migration script for existing content
- Test migration on demo sites
Phase 2: Repository Layer (Week 2)
- Update repository interfaces for state-based queries
- Implement draft save/publish/rollback operations
- Add state transition validation
- Update existing content operations
Phase 3: API Integration (Week 3)
- Implement new draft/publish endpoints
- Update existing endpoints for state handling
- Add preview enhancement functionality
- Implement bulk publishing API
Phase 4: UI Implementation (Week 4)
- Update control panel with new buttons and states
- Implement auto-save functionality
- Add diff viewer and publishing dialogs
- Create publishing status dashboard
Phase 5: Testing & Polish (Week 5)
- Comprehensive testing across demo sites
- Performance optimization and monitoring
- Error handling and edge cases
- Documentation and migration guides
Testing Strategy
Migration Testing
- Test content migration with various demo site configurations
- Validate data integrity before/after migration
- Test rollback procedures if migration fails
Workflow Testing
- Draft save/publish cycles with various content types
- Concurrent editing scenarios
- Auto-save reliability under different conditions
- Enhancement preview vs live comparison
Performance Testing
- State-based query performance with large datasets
- Auto-save frequency impact on database
- Enhancement speed with draft vs live content
Success Metrics
Functional Success
- ✅ Zero data loss during migration
- ✅ All demo sites work without modification post-migration
- ✅ Draft/publish workflow completes in <5 seconds
- ✅ Auto-save prevents content loss in all scenarios
User Experience Success
- ✅ Clear visual distinction between draft and published states
- ✅ Intuitive publishing workflow requiring minimal training
- ✅ Preview functionality accurately reflects published output
Technical Success
- ✅ State-based queries perform within 100ms
- ✅ Database size increase <10% due to state optimization
- ✅ 100% test coverage for new draft/publish functionality
Future Enhancements
Near-term (Next 6 months)
- Scheduled Publishing: Add
scheduledstate withpublish_attimestamp - Bulk Operations: Enhanced multi-content publishing interface
- Content Conflicts: Optimistic locking for concurrent editing
Long-term (6+ months)
- Approval Workflows: Multi-step editorial approval process
- Content Branching: Multiple draft versions per content item
- Real-time Collaboration: Live editing with conflict resolution
This approach follows industry best practices from WordPress and Ghost while leveraging Insertr's existing version infrastructure for maximum simplicity and reliability.