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:
13
cmd/serve.go
13
cmd/serve.go
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
185
justfile
@@ -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 ""
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user