use tea model for showing server runnign and closing

This commit is contained in:
Robin Olsen
2026-03-11 11:09:38 +01:00
parent 6cbd9d1bd2
commit a7b2698309
2 changed files with 97 additions and 22 deletions

View File

@@ -6,14 +6,19 @@ import (
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"charm.land/lipgloss/v2"
"github.com/LazyBachelor/LazyPM/internal/app"
"github.com/LazyBachelor/LazyPM/internal/models"
"github.com/LazyBachelor/LazyPM/internal/utils/browser"
"github.com/LazyBachelor/LazyPM/pkg/web/handler"
"github.com/LazyBachelor/LazyPM/pkg/web/server"
tea "github.com/charmbracelet/bubbletea"
)
type Config = models.Config
@@ -32,12 +37,50 @@ func New() *Web {
//go:embed assets/*
var assets embed.FS
type tuiModel struct {
width int
height int
address string
quitChan chan struct{}
}
func (m tuiModel) Init() tea.Cmd {
return nil
}
func (m tuiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
return m, nil
case tea.KeyMsg:
switch msg.String() {
case "q", "esc", "ctrl+c":
select {
case <-m.quitChan:
default:
close(m.quitChan)
}
return m, tea.Quit
}
}
return m, nil
}
func (m tuiModel) View() string {
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, fmt.Sprintf(
"Web server running at %s\n\nPress q, esc, or Ctrl+C to stop the task and server.\n",
m.address,
))
}
func (w *Web) Run(ctx context.Context, config Config) error {
app, cleanup, err := app.New(ctx, config)
if err != nil {
return err
}
defer cleanup()
httpServer := server.NewServer(server.Server{
@@ -46,21 +89,39 @@ func (w *Web) Run(ctx context.Context, config Config) error {
App: app,
})
fmt.Printf("Starting web server on %s...\n", config.WebAddress)
address := config.WebAddress
if !strings.Contains(address, "http") {
address = "http://localhost" + address
}
err = browser.Open(address)
if err != nil {
return fmt.Errorf("failed to open browser: %w", err)
}
serverErr := make(chan error, 1)
uiQuitChan := make(chan struct{})
screen := tea.NewProgram(
tuiModel{
address: address,
quitChan: uiQuitChan,
},
tea.WithAltScreen(),
)
screenDone := make(chan struct{})
go func() {
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
defer close(screenDone)
if _, err := screen.Run(); err != nil {
fmt.Printf("failed to start terminal UI: %s\n", err)
}
}()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(sigChan)
go func() {
if err := httpServer.ListenAndServe(); err != nil &&
!errors.Is(err, http.ErrServerClosed) {
serverErr <- err
}
}()
@@ -77,17 +138,36 @@ func (w *Web) Run(ctx context.Context, config Config) error {
handler.SetSubmitChan(w.submitChan)
}
if err := browser.Open(address); err != nil {
fmt.Printf("failed to open browser: %s\n", err)
}
var shutdownMsg string
select {
case <-w.quitChan:
fmt.Println("Task completed! Shutting down server...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return httpServer.Shutdown(shutdownCtx)
case err := <-serverErr:
return err
shutdownMsg = "Task completed! Ending task and server..."
case <-uiQuitChan:
shutdownMsg = "Quit key pressed. Ending task and server..."
case sig := <-sigChan:
shutdownMsg = fmt.Sprintf("Received signal %s. Ending task and server...", sig)
case <-ctx.Done():
return httpServer.Shutdown(context.Background())
shutdownMsg = "Context cancelled. Ending task and server..."
case err := <-serverErr:
screen.Quit()
<-screenDone
return err
}
screen.Quit()
<-screenDone
fmt.Println(shutdownMsg)
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return httpServer.Shutdown(shutdownCtx)
}
func (w *Web) SetChannels(feedbackChan chan ValidationFeedback, quitChan chan bool) {