Migrate to Chi router and add Norwegian Devigo demo

Major improvements:
- Replace Gorilla Mux with Chi v5 router for better performance and cleaner code
- Fix CSS/JS MIME type issues that prevented proper asset loading
- Add built-in CORS middleware replacing manual OPTIONS handlers
- Simplify routing with nested route syntax
- Update URL parameter extraction from mux.Vars to chi.URLParam

New Devigo demo:
- Add production Norwegian sales training website (devigo.no)
- Real-world Hugo-generated content with TailwindCSS
- 39 insertr-enhanced elements for comprehensive CMS testing
- Demonstrates international language support and B2B use cases
- Fixed asset paths for localhost serving compatibility

Technical benefits:
- Automatic MIME type detection for static files
- Reduced code complexity with built-in middleware
- Better performance with lighter dependency stack
- Production-ready CORS handling
This commit is contained in:
2025-09-17 13:34:36 +02:00
parent 12c6ec8048
commit cd202ebb1d
87 changed files with 7737 additions and 71 deletions

View File

@@ -8,7 +8,9 @@ import (
"os/signal"
"syscall"
"github.com/gorilla/mux"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@@ -120,55 +122,54 @@ func runServe(cmd *cobra.Command, args []string) {
contentHandler := api.NewContentHandler(database, authService)
contentHandler.SetSiteManager(siteManager)
// Setup router
router := mux.NewRouter()
// Setup Chi router
router := chi.NewRouter()
// Add middleware
router.Use(api.CORSMiddleware)
router.Use(api.LoggingMiddleware)
// Add Chi middleware
router.Use(middleware.Logger)
router.Use(middleware.Recoverer)
router.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"*"}, // In dev mode, allow all origins
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"*"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300, // Maximum value not ignored by any of major browsers
}))
router.Use(api.ContentTypeMiddleware)
// Health check endpoint
router.HandleFunc("/health", api.HealthMiddleware())
// API routes
apiRouter := router.PathPrefix("/api").Subrouter()
// Content endpoints
contentRouter := apiRouter.PathPrefix("/content").Subrouter()
contentRouter.HandleFunc("/bulk", contentHandler.GetBulkContent).Methods("GET")
contentRouter.HandleFunc("/{id}", contentHandler.GetContent).Methods("GET")
contentRouter.HandleFunc("", contentHandler.GetAllContent).Methods("GET")
contentRouter.HandleFunc("", contentHandler.CreateContent).Methods("POST")
// Version control endpoints
contentRouter.HandleFunc("/{id}/versions", contentHandler.GetContentVersions).Methods("GET")
contentRouter.HandleFunc("/{id}/rollback", contentHandler.RollbackContent).Methods("POST")
// Site enhancement endpoint
apiRouter.HandleFunc("/enhance", contentHandler.EnhanceSite).Methods("POST")
router.Get("/health", api.HealthMiddleware())
// Static library serving (for demo sites)
router.HandleFunc("/insertr.js", contentHandler.ServeInsertrJS).Methods("GET")
router.Get("/insertr.js", contentHandler.ServeInsertrJS)
// API routes
router.Route("/api", func(apiRouter chi.Router) {
// Site enhancement endpoint
apiRouter.Post("/enhance", contentHandler.EnhanceSite)
// Content endpoints
apiRouter.Route("/content", func(contentRouter chi.Router) {
contentRouter.Get("/bulk", contentHandler.GetBulkContent)
contentRouter.Get("/{id}", contentHandler.GetContent)
contentRouter.Get("/", contentHandler.GetAllContent)
contentRouter.Post("/", contentHandler.CreateContent)
// Version control endpoints
contentRouter.Get("/{id}/versions", contentHandler.GetContentVersions)
contentRouter.Post("/{id}/rollback", contentHandler.RollbackContent)
})
})
// Static site serving - serve registered sites at /sites/{site_id}
siteRouter := router.PathPrefix("/sites").Subrouter()
// This fixes the MIME type issues with Chi's FileServer
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))))
fileServer := http.FileServer(http.Dir(siteConfig.Path))
router.Handle("/sites/"+siteID+"/*", http.StripPrefix("/sites/"+siteID+"/", fileServer))
}
// Handle CORS preflight requests explicitly
contentRouter.HandleFunc("/{id}", api.CORSPreflightHandler).Methods("OPTIONS")
contentRouter.HandleFunc("", api.CORSPreflightHandler).Methods("OPTIONS")
contentRouter.HandleFunc("/bulk", api.CORSPreflightHandler).Methods("OPTIONS")
contentRouter.HandleFunc("/{id}/versions", api.CORSPreflightHandler).Methods("OPTIONS")
contentRouter.HandleFunc("/{id}/rollback", api.CORSPreflightHandler).Methods("OPTIONS")
apiRouter.HandleFunc("/enhance", api.CORSPreflightHandler).Methods("OPTIONS")
// Start server
addr := fmt.Sprintf(":%d", port)
mode := "production"