338 lines
10 KiB
Go
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
|
|
|
|
}
|