backea/internal/web/handlers/homepage_handler.go
2025-03-21 17:54:43 +01:00

338 lines
10 KiB
Go

package handlers
import (
"backea/internal/backup/models"
"backea/internal/backup/strategy"
"backea/templates"
"context"
"fmt"
"log"
"net/http"
"os"
"sort"
"sync"
"time"
"github.com/labstack/echo/v4"
)
type HomepageHandler struct {
backupFactory strategy.Factory
}
func NewHomepageHandler(factory strategy.Factory) *HomepageHandler {
return &HomepageHandler{
backupFactory: factory,
}
}
// Home handles the homepage request and displays latest backups by service
func (h *HomepageHandler) Home(c echo.Context) error {
// Create the data structures
serviceBackups := make(map[string]map[string][]models.BackupInfo)
serviceConfigs := make(map[string]map[string]templates.ServiceProviderInfo)
groupDirectories := make(map[string]string) // Store directories by group name
// Get the configuration from the factory
factoryConfig := h.getConfig()
// Process each service group
for groupName, serviceGroup := range factoryConfig.Services {
// Initialize maps for this group
serviceBackups[groupName] = make(map[string][]models.BackupInfo)
serviceConfigs[groupName] = make(map[string]templates.ServiceProviderInfo)
// Store the directory at the group level in a separate map
groupDirectories[groupName] = serviceGroup.Source.Path
// Process each backup config in the group
for configIndex, backupConfig := range serviceGroup.BackupConfigs {
// Store service configuration
serviceConfigs[groupName][configIndex] = templates.ServiceProviderInfo{
Type: backupConfig.BackupStrategy.Type,
Provider: backupConfig.BackupStrategy.Provider,
Directory: serviceGroup.Source.Path,
}
}
}
// Get sorted group names for alphabetical ordering
var sortedGroupNames []string
for groupName := range serviceBackups {
sortedGroupNames = append(sortedGroupNames, groupName)
}
sort.Strings(sortedGroupNames)
// Render the template with sorted group names and directories
component := templates.Home(serviceBackups, serviceConfigs, sortedGroupNames, groupDirectories)
return component.Render(c.Request().Context(), c.Response().Writer)
}
// ServiceGroupHeader returns the header section for a specific service group (HTMX endpoint)
func (h *HomepageHandler) ServiceGroupHeader(c echo.Context) error {
groupName := c.Param("groupName")
// Get the configuration
factoryConfig := h.getConfig()
// Check if the service group exists
serviceGroup, exists := factoryConfig.Services[groupName]
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "Service group not found")
}
// Create data structures
serviceBackups := make(map[string][]models.BackupInfo)
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
// Setup synchronization
var wg sync.WaitGroup
var mu sync.Mutex
// Process each backup config in the group
for configIndex, backupConfig := range serviceGroup.BackupConfigs {
// Store service configuration
serviceConfigs[configIndex] = templates.ServiceProviderInfo{
Type: backupConfig.BackupStrategy.Type,
Provider: backupConfig.BackupStrategy.Provider,
Directory: serviceGroup.Source.Path,
}
// Fetch backups in parallel
wg.Add(1)
go func(index string) {
defer wg.Done()
// Get backup strategy
strategy, err := h.backupFactory.CreateBackupStrategyForService(groupName, index)
if err != nil {
log.Printf("Error creating strategy for %s.%s: %v", groupName, index, err)
return
}
// Get backups
backups, err := strategy.ListBackups(context.Background(), groupName+"."+index)
if err != nil {
log.Printf("Error listing backups for %s.%s: %v", groupName, index, err)
return
}
// Sort backups by time (newest first)
sort.Slice(backups, func(i, j int) bool {
return backups[i].CreationTime.After(backups[j].CreationTime)
})
// Store result
mu.Lock()
serviceBackups[index] = backups
mu.Unlock()
}(configIndex)
}
// Wait for all goroutines to finish
wg.Wait()
// Render just the header component
component := templates.GroupHeaderComponent(
groupName,
serviceBackups,
serviceConfigs,
serviceGroup.Source.Path,
)
return component.Render(c.Request().Context(), c.Response().Writer)
}
// ServiceGroupBackups returns just the backups table for a specific service group (HTMX endpoint)
func (h *HomepageHandler) ServiceGroupBackups(c echo.Context) error {
groupName := c.Param("groupName")
// Get the configuration
factoryConfig := h.getConfig()
// Check if the service group exists
serviceGroup, exists := factoryConfig.Services[groupName]
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "Service group not found")
}
// Create data structures
serviceBackups := make(map[string][]models.BackupInfo)
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
// Setup synchronization
var wg sync.WaitGroup
var mu sync.Mutex
// Process each backup config in the group
for configIndex, backupConfig := range serviceGroup.BackupConfigs {
// Store service configuration
serviceConfigs[configIndex] = templates.ServiceProviderInfo{
Type: backupConfig.BackupStrategy.Type,
Provider: backupConfig.BackupStrategy.Provider,
Directory: serviceGroup.Source.Path,
}
// Fetch backups in parallel
wg.Add(1)
go func(index string) {
defer wg.Done()
// Get backup strategy
strategy, err := h.backupFactory.CreateBackupStrategyForService(groupName, index)
if err != nil {
log.Printf("Error creating strategy for %s.%s: %v", groupName, index, err)
return
}
// Get backups
backups, err := strategy.ListBackups(context.Background(), groupName+"."+index)
if err != nil {
log.Printf("Error listing backups for %s.%s: %v", groupName, index, err)
return
}
// Sort backups by time (newest first)
sort.Slice(backups, func(i, j int) bool {
return backups[i].CreationTime.After(backups[j].CreationTime)
})
// Store result
mu.Lock()
serviceBackups[index] = backups
mu.Unlock()
}(configIndex)
}
// Wait for all goroutines to finish
wg.Wait()
// Create a map with just the group for the template
groupServiceBackups := make(map[string]map[string][]models.BackupInfo)
groupServiceBackups[groupName] = serviceBackups
// Create a map with just the group configs for the template
groupServiceConfigs := make(map[string]map[string]templates.ServiceProviderInfo)
groupServiceConfigs[groupName] = serviceConfigs
// Render only the backups table component
component := templates.ServiceGroupBackupsTable(groupName, serviceBackups, groupServiceConfigs)
return component.Render(c.Request().Context(), c.Response().Writer)
}
// ServiceGroupAllBackups returns all backups for a specific service group (HTMX endpoint)
func (h *HomepageHandler) ServiceGroupAllBackups(c echo.Context) error {
groupName := c.Param("groupName")
// Get the configuration
factoryConfig := h.getConfig()
// Check if the service group exists
serviceGroup, exists := factoryConfig.Services[groupName]
if !exists {
return echo.NewHTTPError(http.StatusNotFound, "Service group not found")
}
// Create data structures
serviceBackups := make(map[string][]models.BackupInfo)
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
// Setup synchronization
var wg sync.WaitGroup
var mu sync.Mutex
// Process each backup config in the group
for configIndex, backupConfig := range serviceGroup.BackupConfigs {
// Store service configuration
serviceConfigs[configIndex] = templates.ServiceProviderInfo{
Type: backupConfig.BackupStrategy.Type,
Provider: backupConfig.BackupStrategy.Provider,
Directory: serviceGroup.Source.Path,
}
// Fetch backups in parallel
wg.Add(1)
go func(index string) {
defer wg.Done()
// Get backup strategy
strategy, err := h.backupFactory.CreateBackupStrategyForService(groupName, index)
if err != nil {
log.Printf("Error creating strategy for %s.%s: %v", groupName, index, err)
return
}
// Get backups
backups, err := strategy.ListBackups(context.Background(), groupName+"."+index)
if err != nil {
log.Printf("Error listing backups for %s.%s: %v", groupName, index, err)
return
}
// Sort backups by time (newest first)
sort.Slice(backups, func(i, j int) bool {
return backups[i].CreationTime.After(backups[j].CreationTime)
})
// Store result
mu.Lock()
serviceBackups[index] = backups
mu.Unlock()
}(configIndex)
}
// Wait for all goroutines to finish
wg.Wait()
// Create a map with just the group for the template
groupServiceBackups := make(map[string]map[string][]models.BackupInfo)
groupServiceBackups[groupName] = serviceBackups
// Create a map with just the group configs for the template
groupServiceConfigs := make(map[string]map[string]templates.ServiceProviderInfo)
groupServiceConfigs[groupName] = serviceConfigs
// Also trigger a header update to refresh stats
go func() {
// Create a new context since the original one might be cancelled
ctx := context.Background()
// Sleep briefly to ensure the table loads first
time.Sleep(100 * time.Millisecond)
// Make an HTTP request to refresh the header
url := fmt.Sprintf("http://localhost:%s/api/service-group/%s/header",
os.Getenv("PORT"),
groupName,
)
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
log.Printf("Error creating request to refresh header: %v", err)
return
}
// Set HTMX headers to target the correct element
req.Header.Set("HX-Request", "true")
req.Header.Set("HX-Target", fmt.Sprintf("group-header-%s", groupName))
// Make the request
client := &http.Client{}
_, err = client.Do(req)
if err != nil {
log.Printf("Error refreshing header: %v", err)
}
}()
// Render the all backups table component
component := templates.ServiceGroupAllBackupsTable(groupName, serviceBackups, groupServiceConfigs)
return component.Render(c.Request().Context(), c.Response().Writer)
}
// Helper method to get configuration from factory
func (h *HomepageHandler) getConfig() *models.Configuration {
// Type assert to get the concrete FactoryImpl type which has Config field
return h.backupFactory.Config
}