Skip to content

Commit

Permalink
✨ Add proper recursive page loading from subdirectories
Browse files Browse the repository at this point in the history
  • Loading branch information
wesen committed Feb 22, 2025
1 parent 73dda76 commit 8647199
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 297 deletions.
23 changes: 16 additions & 7 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -1201,10 +1201,19 @@ Improved logging in the UI server by:
- Implemented glazed command pattern for better CLI structure
- Added proper log levels and context fields

## Cow-themed Example Pages

Added new example pages showcasing the UI DSL capabilities with a fun cow theme:
- Added cow-facts.yaml with interactive cow facts and newsletter signup
- Added build-a-cow.yaml with a form to create custom cows
- Added dairy-farm-guide.yaml with farm areas, visitor guidelines and tour booking
- Added cow-quiz.yaml featuring an interactive cow knowledge quiz
## Fixed Page Handler Registration

Fixed issues with page handler registration in UI server:
- Added mutex protection for concurrent access to pages and handlers
- Created single ServeMux instance instead of recreating on each request
- Fixed handler registration to avoid conflicts during page reloading
- Added proper locking around map access and handler registration
- Improved logging to show both file path and URL path

## Improved Page Loading

Enhanced page loading in UI server:
- Added proper recursive page loading from subdirectories
- Clear existing pages before reloading to avoid stale entries
- Added debug logging for directory traversal
- Improved error handling for directory walking
64 changes: 41 additions & 23 deletions cmd/ui-server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"time"

"github.com/go-go-golems/clay/pkg/watcher"
Expand All @@ -19,6 +20,8 @@ type Server struct {
dir string
pages map[string]UIDefinition
watcher *watcher.Watcher
mux *http.ServeMux
mu sync.RWMutex
}

type UIDefinition struct {
Expand All @@ -29,6 +32,7 @@ func NewServer(dir string) *Server {
s := &Server{
dir: dir,
pages: make(map[string]UIDefinition),
mux: http.NewServeMux(),
}

// Create a watcher for the pages directory
Expand All @@ -41,6 +45,13 @@ func NewServer(dir string) *Server {
)

s.watcher = w

// Set up static file handler
s.mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

// Set up index handler
s.mux.HandleFunc("/", s.handleIndex())

return s
}

Expand All @@ -60,7 +71,7 @@ func (s *Server) Start(ctx context.Context, port int) error {

srv := &http.Server{
Addr: fmt.Sprintf(":%d", port),
Handler: s.Handler(),
Handler: s.mux,
}

// Start server in a goroutine
Expand Down Expand Up @@ -92,32 +103,29 @@ func (s *Server) Start(ctx context.Context, port int) error {
}

func (s *Server) Handler() http.Handler {
mux := http.NewServeMux()

// Serve static files
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

// Index page
mux.HandleFunc("/", s.handleIndex())

// Individual pages
for name := range s.pages {
pagePath := "/" + strings.TrimSuffix(name, ".yaml")
mux.HandleFunc(pagePath, s.handlePage(name))
}

return mux
return s.mux
}

func (s *Server) loadPages() error {
log.Debug().Str("directory", s.dir).Msg("Loading pages from directory")
log.Debug().Str("directory", s.dir).Msg("Loading pages recursively from directory")

// First, clear existing pages
s.mu.Lock()
s.pages = make(map[string]UIDefinition)
s.mu.Unlock()

return filepath.WalkDir(s.dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
log.Error().Err(err).Str("path", path).Msg("Error walking directory")
return err
}

if !d.IsDir() && strings.HasSuffix(d.Name(), ".yaml") {
if d.IsDir() {
log.Debug().Str("directory", path).Msg("Entering directory")
return nil
}

if strings.HasSuffix(d.Name(), ".yaml") {
log.Debug().Str("path", path).Msg("Found YAML page")
if err := s.loadPage(path); err != nil {
log.Error().Err(err).Str("path", path).Msg("Failed to load page")
Expand Down Expand Up @@ -152,12 +160,12 @@ func (s *Server) loadPage(path string) error {
urlPath := "/" + strings.TrimSuffix(relPath, ".yaml")
urlPath = strings.ReplaceAll(urlPath, string(os.PathSeparator), "/")

s.mu.Lock()
s.pages[relPath] = def
log.Info().Str("path", relPath).Msg("Loaded page")

// Register the page handler for the URL path
http.HandleFunc(urlPath, s.handlePage(relPath))
s.mux.HandleFunc(urlPath, s.handlePage(relPath))
s.mu.Unlock()

log.Info().Str("path", relPath).Str("url", urlPath).Msg("Loaded page")
return nil
}

Expand All @@ -182,7 +190,10 @@ func (s *Server) handleFileRemove(path string) error {
return fmt.Errorf("failed to get relative path for %s: %w", path, err)
}

s.mu.Lock()
delete(s.pages, relPath)
s.mu.Unlock()

log.Info().Str("path", relPath).Msg("Removed page")
return nil
}
Expand All @@ -194,7 +205,11 @@ func (s *Server) handleIndex() http.HandlerFunc {
return
}

component := indexTemplate(s.pages)
s.mu.RLock()
pages := s.pages
s.mu.RUnlock()

component := indexTemplate(pages)
if err := component.Render(r.Context(), w); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
Expand All @@ -203,7 +218,10 @@ func (s *Server) handleIndex() http.HandlerFunc {

func (s *Server) handlePage(name string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s.mu.RLock()
def, ok := s.pages[name]
s.mu.RUnlock()

if !ok {
http.NotFound(w, r)
return
Expand Down
76 changes: 0 additions & 76 deletions examples/pages/cow/build-a-cow.yaml

This file was deleted.

53 changes: 0 additions & 53 deletions examples/pages/cow/cow-facts.yaml

This file was deleted.

65 changes: 0 additions & 65 deletions examples/pages/cow/cow-quiz.yaml

This file was deleted.

Loading

0 comments on commit 8647199

Please sign in to comment.