Simplify development workflow and fix content editing conflicts

- Replace complex multi-server setup (live-server + API) with unified Go server
- Serve all sites at /sites/{site_id} endpoints, eliminating port conflicts
- Fix content-type middleware to serve proper MIME types for static files
- Prevent script injection duplication with future-proof CDN-compatible detection
- Remove auto page reload from enhance button to eliminate editing interruptions
- Enable seamless content editing workflow with manual enhancement control

Development now requires only 'just dev' instead of complex demo commands.
All sites immediately available at localhost:8080 without hot reload conflicts.
This commit is contained in:
2025-09-16 19:10:57 +02:00
parent eabb7b16e8
commit a3fc3089d2
6 changed files with 90 additions and 160 deletions

View File

@@ -144,6 +144,15 @@ func runServe(cmd *cobra.Command, args []string) {
// Static library serving (for demo sites)
router.HandleFunc("/insertr.js", contentHandler.ServeInsertrJS).Methods("GET")
// Static site serving - serve registered sites at /sites/{site_id}
siteRouter := router.PathPrefix("/sites").Subrouter()
for siteID, siteConfig := range siteManager.GetAllSites() {
log.Printf("📁 Serving site %s from %s at /sites/%s/", siteID, siteConfig.Path, siteID)
siteRouter.PathPrefix("/" + siteID + "/").Handler(
http.StripPrefix("/sites/"+siteID+"/",
http.FileServer(http.Dir(siteConfig.Path))))
}
// Handle CORS preflight requests explicitly
contentRouter.HandleFunc("/{id}", api.CORSPreflightHandler).Methods("OPTIONS")
contentRouter.HandleFunc("", api.CORSPreflightHandler).Methods("OPTIONS")
@@ -171,6 +180,10 @@ func runServe(cmd *cobra.Command, args []string) {
fmt.Printf(" PUT /api/content/{id}\n")
fmt.Printf(" GET /api/content/{id}/versions?site_id={site}\n")
fmt.Printf(" POST /api/content/{id}/rollback\n")
fmt.Printf("🌐 Static sites:\n")
for siteID, _ := range siteManager.GetAllSites() {
fmt.Printf(" %s: http://localhost%s/sites/%s/\n", siteID, addr, siteID)
}
fmt.Printf("\n🔄 Press Ctrl+C to shutdown gracefully\n\n")
// Setup graceful shutdown

View File

@@ -57,8 +57,11 @@ func (rw *responseWriter) WriteHeader(code int) {
// ContentTypeMiddleware ensures JSON responses have proper content type
func ContentTypeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Set default content type for API responses
if r.URL.Path != "/" && (r.Method == "GET" || r.Method == "POST" || r.Method == "PUT") {
// Set default content type for API responses only, not static sites
if r.URL.Path != "/" &&
!strings.HasPrefix(r.URL.Path, "/sites/") &&
!strings.HasPrefix(r.URL.Path, "/insertr.js") &&
(r.Method == "GET" || r.Method == "POST" || r.Method == "PUT") {
w.Header().Set("Content-Type", "application/json")
}

View File

@@ -352,6 +352,11 @@ func (i *Injector) InjectDemoGateIfNeeded(doc *html.Node) {
// InjectEditorScript injects the insertr.js library and initialization script
func (i *Injector) InjectEditorScript(doc *html.Node) {
// Check if script is already injected
if i.hasInsertrScript(doc) {
return
}
// Find the head element for the script tag
headNode := i.findHeadElement(doc)
if headNode == nil {
@@ -360,8 +365,8 @@ func (i *Injector) InjectEditorScript(doc *html.Node) {
}
// Create script element that loads insertr.js from our server
scriptHTML := fmt.Sprintf(`<script src="http://localhost:8080/insertr.js"></script>
<script type="text/javascript">
scriptHTML := fmt.Sprintf(`<script src="http://localhost:8080/insertr.js" data-insertr-injected="true"></script>
<script type="text/javascript" data-insertr-injected="true">
// Initialize insertr for demo sites
document.addEventListener('DOMContentLoaded', function() {
if (typeof window.Insertr !== 'undefined') {
@@ -461,6 +466,28 @@ func (i *Injector) hasInsertrGate(node *html.Node) bool {
return false
}
// hasInsertrScript checks if document already has insertr script injected
// Uses data-insertr-injected attribute for reliable detection that works with:
// - CDN URLs with version numbers (jsdelivr.net/npm/@insertr/lib@1.2.3/insertr.js)
// - Minified versions (insertr.min.js)
// - Query parameters (insertr.js?v=abc123)
// - Different CDN domains (unpkg.com, cdn.example.com)
func (i *Injector) hasInsertrScript(node *html.Node) bool {
if node.Type == html.ElementNode && node.Data == "script" {
for _, attr := range node.Attr {
if attr.Key == "data-insertr-injected" {
return true
}
}
}
for child := node.FirstChild; child != nil; child = child.NextSibling {
if i.hasInsertrScript(child) {
return true
}
}
return false
}
// findBodyElement finds the <body> element
func (i *Injector) findBodyElement(node *html.Node) *html.Node {
if node.Type == html.ElementNode && node.Data == "body" {

185
justfile
View File

@@ -25,171 +25,57 @@ dev: build-lib build
fi
echo ""
echo "📝 Unified logs below (API server + Demo site):"
echo "🔌 [SERVER] = API server logs"
echo "🌐 [DEMO] = Demo site logs"
echo "🔌 Starting Insertr server with all sites..."
echo ""
# Function to cleanup background processes
cleanup() {
echo ""
echo "🛑 Shutting down servers..."
kill $SERVER_PID $DEMO_PID 2>/dev/null || true
wait $SERVER_PID $DEMO_PID 2>/dev/null || true
echo "🛑 Shutting down server..."
kill $SERVER_PID 2>/dev/null || true
wait $SERVER_PID 2>/dev/null || true
echo "✅ Shutdown complete"
exit 0
}
trap cleanup SIGINT SIGTERM
# Start API server with prefixed output
echo "🔌 Starting API server (localhost:8080)..."
INSERTR_DATABASE_PATH=./insertr.db ./insertr serve --dev-mode 2>&1 | sed 's/^/🔌 [SERVER] /' &
SERVER_PID=$!
# Wait for server startup
echo "⏳ Waiting for API server startup..."
sleep 3
# Check server health
if curl -s http://localhost:8080/health > /dev/null 2>&1; then
echo "✅ API server ready!"
else
echo "⚠️ API server may not be ready yet"
fi
echo ""
echo "🌐 Starting demo site (localhost:3000)..."
echo "📝 Full-stack ready - edit content with real-time persistence!"
echo ""
# Start enhanced demo site with prefixed output (this will block) - use local installation
cd {{justfile_directory()}} && npx --prefer-offline live-server test-sites/demo-site_enhanced --port=3000 --host=localhost --open=/index.html 2>&1 | sed 's/^/🌐 [DEMO] /' &
DEMO_PID=$!
# Wait for both processes
wait $DEMO_PID $SERVER_PID
# Demo site only (for specific use cases)
# Start development server for about page
dev-about: build-lib build
#!/usr/bin/env bash
echo "🚀 Starting full-stack development (about page)..."
# Start server (serves API + all static sites)
INSERTR_DATABASE_PATH=./insertr.db ./insertr serve --dev-mode &
SERVER_PID=$!
sleep 3
npx --prefer-offline live-server test-sites/demo-site --port=3000 --host=localhost --open=/about.html
kill $SERVER_PID 2>/dev/null || true
echo ""
echo "🌐 All sites available at:"
echo " Demo site: http://localhost:8080/sites/demo/"
echo " Simple site: http://localhost:8080/sites/simple/"
echo " Dan Eden site: http://localhost:8080/sites/dan-eden/"
echo ""
echo "📝 Full-stack ready - edit content with real-time persistence!"
echo "🔄 Press Ctrl+C to shutdown"
echo ""
# Wait for server process
wait $SERVER_PID
# Start development server for about page
dev-about: build-lib build
#!/usr/bin/env bash
echo "🚀 Starting full-stack development..."
echo "🌐 About page available at: http://localhost:8080/sites/demo/about.html"
INSERTR_DATABASE_PATH=./insertr.db ./insertr serve --dev-mode
# Check project status and validate setup
check:
npm run check
# Start demo for a specific test site
demo site="":
#!/usr/bin/env bash
if [ "{{site}}" = "" ]; then
echo "📋 Available Insertr Demo Sites:"
echo "=================================="
echo ""
echo "🏠 Built-in Demo:"
echo " default - Default insertr demo site"
echo ""
echo "🌐 Test Site Demos:"
echo " dan-eden - Dan Eden's portfolio"
echo " simple - Simple test site"
echo ""
echo "📝 Usage:"
echo " just demo default - Start default demo"
echo " just demo dan-eden - Start Dan Eden portfolio demo"
echo " just demo simple - Start simple test site demo"
echo ""
echo "💡 Note: Sites are auto-enhanced on first run"
elif [ "{{site}}" = "default" ] || [ "{{site}}" = "demo" ]; then
if [ ! -d "./test-sites/demo-site_enhanced" ]; then
echo "🔧 Default demo not ready - enhancing now..."
just build
./insertr enhance test-sites/demo-site --output test-sites/demo-site_enhanced --config test-sites/demo-site/insertr.yaml
fi
echo "🚀 Starting default demo site..."
just demo-site "demo" "./test-sites/demo-site_enhanced" "3000"
elif [ "{{site}}" = "dan-eden" ]; then
if [ ! -d "./test-sites/simple/dan-eden-portfolio_enhanced" ]; then
echo "🔧 Dan Eden demo not ready - enhancing now..."
just build
./insertr enhance test-sites/simple/dan-eden-portfolio --output test-sites/simple/dan-eden-portfolio_enhanced --config test-sites/simple/dan-eden-portfolio/insertr.yaml
fi
echo "🚀 Starting Dan Eden portfolio demo..."
just demo-site "dan-eden" "./test-sites/simple/dan-eden-portfolio_enhanced" "3000"
elif [ "{{site}}" = "simple" ]; then
if [ ! -d "./test-sites/simple/test-simple_enhanced" ]; then
echo "🔧 Simple demo not ready - enhancing now..."
just build
./insertr enhance test-sites/simple/test-simple --output test-sites/simple/test-simple_enhanced --config test-sites/simple/test-simple/insertr.yaml
fi
echo "🚀 Starting simple test site demo..."
just demo-site "simple" "./test-sites/simple/test-simple_enhanced" "3000"
else
echo "❌ Unknown demo site: {{site}}"
echo ""
echo "📋 Available demo sites:"
echo " default - Default demo site"
echo " dan-eden - Dan Eden portfolio"
echo " simple - Simple test site"
echo ""
echo "🔧 Other commands:"
echo " just demo - Show all demo sites"
exit 1
fi
# Generic demo site launcher (internal command)
demo-site site_id path port="3000": build
#!/usr/bin/env bash
echo "🚀 Starting {{site_id}} demo..."
echo "📁 Path: {{path}}"
echo "🌐 Port: {{port}}"
echo "================================================"
echo ""
# Function to cleanup background processes
cleanup() {
echo ""
echo "🛑 Shutting down servers..."
kill $SERVER_PID $DEMO_PID 2>/dev/null || true
wait $SERVER_PID $DEMO_PID 2>/dev/null || true
echo "✅ Shutdown complete"
exit 0
}
trap cleanup SIGINT SIGTERM
# Start API server
echo "🔌 Starting API server (localhost:8080)..."
INSERTR_DATABASE_PATH=./insertr.db ./insertr serve --dev-mode 2>&1 | sed 's/^/🔌 [{{site_id}}] /' &
SERVER_PID=$!
# Wait for server startup
echo "⏳ Waiting for API server startup..."
sleep 3
# Check server health
if curl -s http://localhost:8080/health > /dev/null 2>&1; then
echo "✅ API server ready!"
else
echo "⚠️ API server may not be ready yet"
fi
echo ""
echo "🌐 Starting {{site_id}} (localhost:{{port}})..."
echo "📝 Demo ready - test insertr functionality!"
echo ""
# Start demo site
npx --prefer-offline live-server "{{path}}" --port={{port}} --host=localhost --open=/index.html 2>&1 | sed 's/^/🌐 [{{site_id}}] /' &
DEMO_PID=$!
# Wait for both processes
wait $DEMO_PID $SERVER_PID
# Simple demo launcher - all sites now served from main server
demo:
@echo "🌐 All demo sites are served from the main server:"
@echo " http://localhost:8080/sites/demo/ - Main demo site"
@echo " http://localhost:8080/sites/simple/ - Simple test site"
@echo " http://localhost:8080/sites/dan-eden/ - Dan Eden portfolio"
@echo ""
@echo "🚀 To start the development server:"
@echo " just dev"
# Build the entire project (library + unified binary)
build:
@@ -281,8 +167,7 @@ status:
@echo ""
@echo "🚀 Development Commands:"
@echo " just dev - Full-stack development (recommended)"
@echo " just demo [site] - Start specific demo site (or show available demos)"
@echo " just demo - Show available demo sites"
@echo " just serve - API server only (localhost:8080)"
@echo " just enhance - Build-time content injection"
@echo ""

View File

@@ -644,10 +644,11 @@ export class InsertrAuth {
// Show success state briefly
enhanceBtn.textContent = '✅ Enhanced!';
// Optional: Trigger page reload to show enhanced files
// Reset button after success (no page reload needed)
setTimeout(() => {
window.location.reload();
}, 1000);
enhanceBtn.textContent = '🔄 Enhance';
enhanceBtn.disabled = false;
}, 2000);
} catch (error) {
console.error('❌ Enhancement failed:', error);

View File

@@ -3716,10 +3716,11 @@ Please report this to https://github.com/markedjs/marked.`,e){let r="<p>An error
// Show success state briefly
enhanceBtn.textContent = '✅ Enhanced!';
// Optional: Trigger page reload to show enhanced files
// Reset button after success (no page reload needed)
setTimeout(() => {
window.location.reload();
}, 1000);
enhanceBtn.textContent = '🔄 Enhance';
enhanceBtn.disabled = false;
}, 2000);
} catch (error) {
console.error('❌ Enhancement failed:', error);