Correct compilation error
This commit is contained in:
parent
b3578ab982
commit
1d06437fe9
@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backea/internal/backup/config"
|
|
||||||
"backea/internal/backup/core"
|
"backea/internal/backup/core"
|
||||||
|
"backea/internal/backup/models"
|
||||||
"backea/internal/backup/strategy"
|
"backea/internal/backup/strategy"
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
@ -31,7 +31,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(*configPath)
|
cfg, err := models.LoadConfig(*configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load configuration: %v", err)
|
log.Fatalf("Failed to load configuration: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,8 @@ func main() {
|
|||||||
log.Printf("Warning: Error loading .env file: %v", err)
|
log.Printf("Warning: Error loading .env file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
factoryBuilder := &strategy.DefaultFactoryBuilder{}
|
// cfg, _ := models.LoadConfig(configPath)
|
||||||
factory, err := factoryBuilder.BuildFactory(*configPath)
|
factory, _ := strategy.NewFactory(configPath)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to create backup factory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config := factory.Config
|
|
||||||
|
|
||||||
// Process each service group
|
// Process each service group
|
||||||
for groupName, serviceGroup := range factory.Config.Services {
|
for groupName, serviceGroup := range factory.Config.Services {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backea/internal/backup"
|
"backea/internal/backup/strategy"
|
||||||
"backea/internal/server"
|
"backea/internal/server"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@ -15,10 +15,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create backup factory
|
// Create backup factory
|
||||||
backupFactory, _ := backup.NewBackupFactory("config.yml")
|
configPath := "config.yml"
|
||||||
|
// cfg, _ := models.LoadConfig(configPath)
|
||||||
|
backupFactory, _ := strategy.NewFactory(configPath)
|
||||||
|
|
||||||
// Initialize and start the server
|
// Initialize and start the server
|
||||||
srv := server.New(backupFactory)
|
srv := server.New(*backupFactory)
|
||||||
log.Printf("Starting server on port %s", port)
|
log.Printf("Starting server on port %s", port)
|
||||||
if err := srv.Start(":" + port); err != nil {
|
if err := srv.Start(":" + port); err != nil {
|
||||||
log.Fatalf("Failed to start server: %v", err)
|
log.Fatalf("Failed to start server: %v", err)
|
||||||
|
@ -11,7 +11,7 @@ services:
|
|||||||
backealocal:
|
backealocal:
|
||||||
source:
|
source:
|
||||||
host: "local"
|
host: "local"
|
||||||
path: "/home/sirir/Images"
|
path: "/home/gevo/Images"
|
||||||
hooks:
|
hooks:
|
||||||
before_hook: "ls"
|
before_hook: "ls"
|
||||||
after_hook: ""
|
after_hook: ""
|
||||||
@ -21,18 +21,18 @@ services:
|
|||||||
type: "kopia"
|
type: "kopia"
|
||||||
provider: "local"
|
provider: "local"
|
||||||
destination:
|
destination:
|
||||||
path: "/home/sirir/backup/image1"
|
path: "/home/gevo/backup/image1"
|
||||||
2:
|
2:
|
||||||
backup_strategy:
|
backup_strategy:
|
||||||
type: "kopia"
|
type: "kopia"
|
||||||
provider: "local"
|
provider: "local"
|
||||||
destination:
|
destination:
|
||||||
path: "/home/sirir/backup/images2"
|
path: "/home/gevo/backup/images2"
|
||||||
|
|
||||||
imageslocal:
|
imageslocal:
|
||||||
source:
|
source:
|
||||||
host: "local"
|
host: "local"
|
||||||
path: "/home/sirir/Images/"
|
path: "/home/gevo/Images/"
|
||||||
hooks:
|
hooks:
|
||||||
before_hook: "ls"
|
before_hook: "ls"
|
||||||
after_hook: ""
|
after_hook: ""
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backea/internal/backup/config"
|
"backea/internal/backup/models"
|
||||||
"backea/internal/backup/strategy"
|
"backea/internal/backup/strategy"
|
||||||
"backea/internal/mail"
|
"backea/internal/mail"
|
||||||
"context"
|
"context"
|
||||||
@ -12,14 +12,14 @@ import (
|
|||||||
|
|
||||||
// Executor handles the execution of backups for multiple services
|
// Executor handles the execution of backups for multiple services
|
||||||
type Executor struct {
|
type Executor struct {
|
||||||
Config *config.Configuration
|
Config *models.Configuration
|
||||||
Factory strategy.Factory
|
Factory strategy.Factory
|
||||||
Mailer *mail.Mailer
|
Mailer *mail.Mailer
|
||||||
concurrency int
|
concurrency int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExecutor creates a new backup executor
|
// NewExecutor creates a new backup executor
|
||||||
func NewExecutor(config *config.Configuration, factory strategy.Factory) *Executor {
|
func NewExecutor(config *models.Configuration, factory strategy.Factory) *Executor {
|
||||||
return &Executor{
|
return &Executor{
|
||||||
Config: config,
|
Config: config,
|
||||||
Factory: factory,
|
Factory: factory,
|
||||||
|
112
internal/backup/models/config.go
Normal file
112
internal/backup/models/config.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configuration holds the complete application configuration
|
||||||
|
type Configuration struct {
|
||||||
|
Defaults DefaultsConfig `mapstructure:"defaults"`
|
||||||
|
Services map[string]ServiceGroup `mapstructure:"services"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultsConfig holds the default settings for all services
|
||||||
|
type DefaultsConfig struct {
|
||||||
|
Retention RetentionConfig `mapstructure:"retention"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceGroup represents a service group with a common directory and multiple backup configurations
|
||||||
|
type ServiceGroup struct {
|
||||||
|
Directory string `mapstructure:"directory,omitempty"`
|
||||||
|
Source SourceConfig `mapstructure:"source"`
|
||||||
|
Hooks HooksConfig `mapstructure:"hooks"`
|
||||||
|
BackupConfigs map[string]BackupConfig `mapstructure:"backup_configs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourceConfig represents source configuration for backups
|
||||||
|
type SourceConfig struct {
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
Path string `mapstructure:"path"`
|
||||||
|
SSHKey string `mapstructure:"ssh_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HooksConfig contains the hooks to run before and after the backup
|
||||||
|
type HooksConfig struct {
|
||||||
|
BeforeHook string `mapstructure:"before_hook"`
|
||||||
|
AfterHook string `mapstructure:"after_hook"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupConfig represents a specific backup configuration
|
||||||
|
type BackupConfig struct {
|
||||||
|
BackupStrategy StrategyConfig `mapstructure:"backup_strategy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrategyConfig represents a backup strategy configuration
|
||||||
|
type StrategyConfig struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
Provider string `mapstructure:"provider"`
|
||||||
|
Options string `mapstructure:"options,omitempty"`
|
||||||
|
Retention RetentionConfig `mapstructure:"retention,omitempty"`
|
||||||
|
Destination DestConfig `mapstructure:"destination"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetentionConfig represents retention policy configuration
|
||||||
|
type RetentionConfig struct {
|
||||||
|
KeepLatest int `mapstructure:"keep_latest"`
|
||||||
|
KeepHourly int `mapstructure:"keep_hourly"`
|
||||||
|
KeepDaily int `mapstructure:"keep_daily"`
|
||||||
|
KeepWeekly int `mapstructure:"keep_weekly"`
|
||||||
|
KeepMonthly int `mapstructure:"keep_monthly"`
|
||||||
|
KeepYearly int `mapstructure:"keep_yearly"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestConfig represents destination configuration for remote backups
|
||||||
|
type DestConfig struct {
|
||||||
|
Host string `mapstructure:"host,omitempty"`
|
||||||
|
Path string `mapstructure:"path"`
|
||||||
|
SSHKey string `mapstructure:"ssh_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig loads the application configuration from a file
|
||||||
|
func LoadConfig(configPath string) (*Configuration, error) {
|
||||||
|
if err := godotenv.Load(); err != nil {
|
||||||
|
log.Printf("Warning: Error loading .env file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
viper.SetConfigFile(configPath)
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Configuration
|
||||||
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply default retention settings where needed
|
||||||
|
for serviceName, service := range config.Services {
|
||||||
|
for configID, backupConfig := range service.BackupConfigs {
|
||||||
|
// If retention is not set, use defaults
|
||||||
|
if isRetentionEmpty(backupConfig.BackupStrategy.Retention) {
|
||||||
|
backupConfig.BackupStrategy.Retention = config.Defaults.Retention
|
||||||
|
service.BackupConfigs[configID] = backupConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.Services[serviceName] = service
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRetentionEmpty checks if a retention config is empty (all zeros)
|
||||||
|
func isRetentionEmpty(retention RetentionConfig) bool {
|
||||||
|
return retention.KeepLatest == 0 &&
|
||||||
|
retention.KeepHourly == 0 &&
|
||||||
|
retention.KeepDaily == 0 &&
|
||||||
|
retention.KeepWeekly == 0 &&
|
||||||
|
retention.KeepMonthly == 0 &&
|
||||||
|
retention.KeepYearly == 0
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package strategy
|
package strategy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backea/internal/backup/config"
|
"backea/internal/backup/models"
|
||||||
"backea/internal/backup/strategy/kopia"
|
"backea/internal/backup/strategy/kopia"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -12,13 +12,13 @@ import (
|
|||||||
|
|
||||||
// Factory handles backup strategy creation
|
// Factory handles backup strategy creation
|
||||||
type Factory struct {
|
type Factory struct {
|
||||||
Config *config.Configuration
|
Config *models.Configuration
|
||||||
ConfigPath string
|
ConfigPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFactory initializes and returns a Factory
|
// NewFactory initializes and returns a Factory
|
||||||
func NewFactory(configPath string) (*Factory, error) {
|
func NewFactory(configPath string) (*Factory, error) {
|
||||||
cfg, err := config.LoadConfig(configPath)
|
cfg, err := models.LoadConfig(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load config: %w", err)
|
return nil, fmt.Errorf("failed to load config: %w", err)
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ func (f *Factory) CreateBackupStrategyForService(groupName, serviceIndex string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createKopiaStrategy initializes a Kopia strategy
|
// createKopiaStrategy initializes a Kopia strategy
|
||||||
func (f *Factory) createKopiaStrategy(serviceConfig *config.BackupConfig, sourcePath string) (Strategy, error) {
|
func (f *Factory) createKopiaStrategy(serviceConfig *models.BackupConfig, sourcePath string) (Strategy, error) {
|
||||||
provider, err := f.createKopiaProvider(serviceConfig.BackupStrategy)
|
provider, err := f.createKopiaProvider(serviceConfig.BackupStrategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create kopia provider: %w", err)
|
return nil, fmt.Errorf("failed to create kopia provider: %w", err)
|
||||||
@ -58,7 +58,7 @@ func (f *Factory) createKopiaStrategy(serviceConfig *config.BackupConfig, source
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createKopiaProvider returns a Kopia storage provider
|
// createKopiaProvider returns a Kopia storage provider
|
||||||
func (f *Factory) createKopiaProvider(strategyConfig config.StrategyConfig) (kopia.Provider, error) {
|
func (f *Factory) createKopiaProvider(strategyConfig models.StrategyConfig) (kopia.Provider, error) {
|
||||||
switch strategyConfig.Provider {
|
switch strategyConfig.Provider {
|
||||||
case "local":
|
case "local":
|
||||||
return kopia.NewLocalProvider(getDefaultPath(strategyConfig.Destination.Path)), nil
|
return kopia.NewLocalProvider(getDefaultPath(strategyConfig.Destination.Path)), nil
|
||||||
@ -123,7 +123,7 @@ func (f *Factory) getServicePath(groupName string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getServiceConfig returns a service's config
|
// getServiceConfig returns a service's config
|
||||||
func (f *Factory) getServiceConfig(groupName, serviceIndex string) (*config.BackupConfig, error) {
|
func (f *Factory) getServiceConfig(groupName, serviceIndex string) (*models.BackupConfig, error) {
|
||||||
if serviceGroup, exists := f.Config.Services[groupName]; exists {
|
if serviceGroup, exists := f.Config.Services[groupName]; exists {
|
||||||
if backupConfig, exists := serviceGroup.BackupConfigs[serviceIndex]; exists {
|
if backupConfig, exists := serviceGroup.BackupConfigs[serviceIndex]; exists {
|
||||||
return &backupConfig, nil
|
return &backupConfig, nil
|
||||||
@ -134,7 +134,7 @@ func (f *Factory) getServiceConfig(groupName, serviceIndex string) (*config.Back
|
|||||||
|
|
||||||
// ReloadConfig reloads configuration
|
// ReloadConfig reloads configuration
|
||||||
func (f *Factory) ReloadConfig() error {
|
func (f *Factory) ReloadConfig() error {
|
||||||
cfg, err := config.LoadConfig(f.ConfigPath)
|
cfg, err := models.LoadConfig(f.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not reload config: %w", err)
|
return fmt.Errorf("could not reload config: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backea/internal/backup/config"
|
"backea/internal/backup/models"
|
||||||
"backea/internal/backup/strategy"
|
"backea/internal/backup/strategy"
|
||||||
"backea/templates"
|
"backea/templates"
|
||||||
"context"
|
"context"
|
||||||
@ -29,20 +29,18 @@ func NewHomepageHandler(factory strategy.Factory) *HomepageHandler {
|
|||||||
// Home handles the homepage request and displays latest backups by service
|
// Home handles the homepage request and displays latest backups by service
|
||||||
func (h *HomepageHandler) Home(c echo.Context) error {
|
func (h *HomepageHandler) Home(c echo.Context) error {
|
||||||
// Create the data structures
|
// Create the data structures
|
||||||
serviceBackups := make(map[string]map[string][]strategy.BackupInfo)
|
serviceBackups := make(map[string]map[string][]models.BackupInfo)
|
||||||
serviceConfigs := make(map[string]map[string]templates.ServiceProviderInfo)
|
serviceConfigs := make(map[string]map[string]templates.ServiceProviderInfo)
|
||||||
groupDirectories := make(map[string]string) // Store directories by group name
|
groupDirectories := make(map[string]string) // Store directories by group name
|
||||||
|
|
||||||
// Get the configuration from the factory
|
// Get the configuration from the factory
|
||||||
factoryConfig, err := h.getConfig()
|
|
||||||
if err != nil {
|
factoryConfig := h.getConfig()
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to get configuration: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process each service group
|
// Process each service group
|
||||||
for groupName, serviceGroup := range factoryConfig.Services {
|
for groupName, serviceGroup := range factoryConfig.Services {
|
||||||
// Initialize maps for this group
|
// Initialize maps for this group
|
||||||
serviceBackups[groupName] = make(map[string][]strategy.BackupInfo)
|
serviceBackups[groupName] = make(map[string][]models.BackupInfo)
|
||||||
serviceConfigs[groupName] = make(map[string]templates.ServiceProviderInfo)
|
serviceConfigs[groupName] = make(map[string]templates.ServiceProviderInfo)
|
||||||
|
|
||||||
// Store the directory at the group level in a separate map
|
// Store the directory at the group level in a separate map
|
||||||
@ -76,10 +74,7 @@ func (h *HomepageHandler) ServiceGroupHeader(c echo.Context) error {
|
|||||||
groupName := c.Param("groupName")
|
groupName := c.Param("groupName")
|
||||||
|
|
||||||
// Get the configuration
|
// Get the configuration
|
||||||
factoryConfig, err := h.getConfig()
|
factoryConfig := h.getConfig()
|
||||||
if err != nil {
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to get configuration: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the service group exists
|
// Check if the service group exists
|
||||||
serviceGroup, exists := factoryConfig.Services[groupName]
|
serviceGroup, exists := factoryConfig.Services[groupName]
|
||||||
@ -88,7 +83,7 @@ func (h *HomepageHandler) ServiceGroupHeader(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create data structures
|
// Create data structures
|
||||||
serviceBackups := make(map[string][]strategy.BackupInfo)
|
serviceBackups := make(map[string][]models.BackupInfo)
|
||||||
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
|
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
|
||||||
|
|
||||||
// Setup synchronization
|
// Setup synchronization
|
||||||
@ -153,10 +148,7 @@ func (h *HomepageHandler) ServiceGroupBackups(c echo.Context) error {
|
|||||||
groupName := c.Param("groupName")
|
groupName := c.Param("groupName")
|
||||||
|
|
||||||
// Get the configuration
|
// Get the configuration
|
||||||
factoryConfig, err := h.getConfig()
|
factoryConfig := h.getConfig()
|
||||||
if err != nil {
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to get configuration: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the service group exists
|
// Check if the service group exists
|
||||||
serviceGroup, exists := factoryConfig.Services[groupName]
|
serviceGroup, exists := factoryConfig.Services[groupName]
|
||||||
@ -165,7 +157,7 @@ func (h *HomepageHandler) ServiceGroupBackups(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create data structures
|
// Create data structures
|
||||||
serviceBackups := make(map[string][]strategy.BackupInfo)
|
serviceBackups := make(map[string][]models.BackupInfo)
|
||||||
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
|
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
|
||||||
|
|
||||||
// Setup synchronization
|
// Setup synchronization
|
||||||
@ -216,7 +208,7 @@ func (h *HomepageHandler) ServiceGroupBackups(c echo.Context) error {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
// Create a map with just the group for the template
|
// Create a map with just the group for the template
|
||||||
groupServiceBackups := make(map[string]map[string][]strategy.BackupInfo)
|
groupServiceBackups := make(map[string]map[string][]models.BackupInfo)
|
||||||
groupServiceBackups[groupName] = serviceBackups
|
groupServiceBackups[groupName] = serviceBackups
|
||||||
|
|
||||||
// Create a map with just the group configs for the template
|
// Create a map with just the group configs for the template
|
||||||
@ -233,10 +225,7 @@ func (h *HomepageHandler) ServiceGroupAllBackups(c echo.Context) error {
|
|||||||
groupName := c.Param("groupName")
|
groupName := c.Param("groupName")
|
||||||
|
|
||||||
// Get the configuration
|
// Get the configuration
|
||||||
factoryConfig, err := h.getConfig()
|
factoryConfig := h.getConfig()
|
||||||
if err != nil {
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to get configuration: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the service group exists
|
// Check if the service group exists
|
||||||
serviceGroup, exists := factoryConfig.Services[groupName]
|
serviceGroup, exists := factoryConfig.Services[groupName]
|
||||||
@ -245,7 +234,7 @@ func (h *HomepageHandler) ServiceGroupAllBackups(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create data structures
|
// Create data structures
|
||||||
serviceBackups := make(map[string][]strategy.BackupInfo)
|
serviceBackups := make(map[string][]models.BackupInfo)
|
||||||
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
|
serviceConfigs := make(map[string]templates.ServiceProviderInfo)
|
||||||
|
|
||||||
// Setup synchronization
|
// Setup synchronization
|
||||||
@ -296,7 +285,7 @@ func (h *HomepageHandler) ServiceGroupAllBackups(c echo.Context) error {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
// Create a map with just the group for the template
|
// Create a map with just the group for the template
|
||||||
groupServiceBackups := make(map[string]map[string][]strategy.BackupInfo)
|
groupServiceBackups := make(map[string]map[string][]models.BackupInfo)
|
||||||
groupServiceBackups[groupName] = serviceBackups
|
groupServiceBackups[groupName] = serviceBackups
|
||||||
|
|
||||||
// Create a map with just the group configs for the template
|
// Create a map with just the group configs for the template
|
||||||
@ -341,13 +330,8 @@ func (h *HomepageHandler) ServiceGroupAllBackups(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to get configuration from factory
|
// Helper method to get configuration from factory
|
||||||
func (h *HomepageHandler) getConfig() (*config.Configuration, error) {
|
func (h *HomepageHandler) getConfig() *models.Configuration {
|
||||||
// Type assert to get the concrete FactoryImpl type which has Config field
|
// Type assert to get the concrete FactoryImpl type which has Config field
|
||||||
if factoryImpl, ok := h.backupFactory.(*strategy.FactoryImpl); ok {
|
return h.backupFactory.Config
|
||||||
return factoryImpl.Config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can't access the Config directly, get it through factory methods
|
|
||||||
// This is where we'd need to add methods to the Factory interface if not already there
|
|
||||||
return nil, fmt.Errorf("cannot access configuration from factory")
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package templates
|
package templates
|
||||||
import (
|
import (
|
||||||
"backea/internal/backup"
|
|
||||||
"backea/templates/layouts"
|
"backea/templates/layouts"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
"backea/internal/backup/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FormatSize formats byte size to human-readable format
|
// FormatSize formats byte size to human-readable format
|
||||||
@ -59,7 +59,7 @@ func FormatServiceName(groupName, serviceIndex string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CalculateTotalSize calculates total size of all backups
|
// CalculateTotalSize calculates total size of all backups
|
||||||
func CalculateTotalSize(backups []backup.BackupInfo) int64 {
|
func CalculateTotalSize(backups []models.BackupInfo) int64 {
|
||||||
var total int64
|
var total int64
|
||||||
for _, b := range backups {
|
for _, b := range backups {
|
||||||
total += b.Size
|
total += b.Size
|
||||||
@ -68,7 +68,7 @@ func CalculateTotalSize(backups []backup.BackupInfo) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CalculateGroupTotalSize calculates total size of all backups for a service group
|
// CalculateGroupTotalSize calculates total size of all backups for a service group
|
||||||
func CalculateGroupTotalSize(serviceGroup map[string][]backup.BackupInfo) int64 {
|
func CalculateGroupTotalSize(serviceGroup map[string][]models.BackupInfo) int64 {
|
||||||
var total int64
|
var total int64
|
||||||
for _, backups := range serviceGroup {
|
for _, backups := range serviceGroup {
|
||||||
for _, b := range backups {
|
for _, b := range backups {
|
||||||
@ -79,7 +79,7 @@ func CalculateGroupTotalSize(serviceGroup map[string][]backup.BackupInfo) int64
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupTotalBackupCount returns the total number of backups across all services in a group
|
// GetGroupTotalBackupCount returns the total number of backups across all services in a group
|
||||||
func GetGroupTotalBackupCount(serviceGroup map[string][]backup.BackupInfo) int {
|
func GetGroupTotalBackupCount(serviceGroup map[string][]models.BackupInfo) int {
|
||||||
count := 0
|
count := 0
|
||||||
for _, backups := range serviceGroup {
|
for _, backups := range serviceGroup {
|
||||||
count += len(backups)
|
count += len(backups)
|
||||||
@ -88,7 +88,7 @@ func GetGroupTotalBackupCount(serviceGroup map[string][]backup.BackupInfo) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLatestBackupTime returns the most recent backup time for a service group
|
// GetLatestBackupTime returns the most recent backup time for a service group
|
||||||
func GetLatestBackupTime(serviceGroup map[string][]backup.BackupInfo) (time.Time, bool) {
|
func GetLatestBackupTime(serviceGroup map[string][]models.BackupInfo) (time.Time, bool) {
|
||||||
var latestTime time.Time
|
var latestTime time.Time
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ func GetLatestBackupTime(serviceGroup map[string][]backup.BackupInfo) (time.Time
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupStatus returns the status of a service group based on the most recent backup
|
// GetGroupStatus returns the status of a service group based on the most recent backup
|
||||||
func GetGroupStatus(serviceGroup map[string][]backup.BackupInfo) string {
|
func GetGroupStatus(serviceGroup map[string][]models.BackupInfo) string {
|
||||||
latestTime, found := GetLatestBackupTime(serviceGroup)
|
latestTime, found := GetLatestBackupTime(serviceGroup)
|
||||||
if !found {
|
if !found {
|
||||||
return "No Backups"
|
return "No Backups"
|
||||||
@ -121,11 +121,11 @@ type ServiceProviderInfo struct {
|
|||||||
// BackupWithService represents a backup with its service identifier
|
// BackupWithService represents a backup with its service identifier
|
||||||
type BackupWithService struct {
|
type BackupWithService struct {
|
||||||
ServiceIndex string
|
ServiceIndex string
|
||||||
Backup backup.BackupInfo
|
Backup models.BackupInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSortedBackups collects all backups from a service group and sorts them by time
|
// GetSortedBackups collects all backups from a service group and sorts them by time
|
||||||
func GetSortedBackups(serviceGroup map[string][]backup.BackupInfo) []BackupWithService {
|
func GetSortedBackups(serviceGroup map[string][]models.BackupInfo) []BackupWithService {
|
||||||
var allBackups []BackupWithService
|
var allBackups []BackupWithService
|
||||||
|
|
||||||
// Collect all backups with their service indices
|
// Collect all backups with their service indices
|
||||||
@ -147,7 +147,7 @@ func GetSortedBackups(serviceGroup map[string][]backup.BackupInfo) []BackupWithS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Home renders the homepage with lazy-loaded backup information
|
// Home renders the homepage with lazy-loaded backup information
|
||||||
templ Home(serviceBackups map[string]map[string][]backup.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo, sortedGroupNames []string, groupDirectories map[string]string) {
|
templ Home(serviceBackups map[string]map[string][]models.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo, sortedGroupNames []string, groupDirectories map[string]string) {
|
||||||
@layouts.Base("Backea - Backup Dashboard") {
|
@layouts.Base("Backea - Backup Dashboard") {
|
||||||
<div class="responsive gruvbox-dark">
|
<div class="responsive gruvbox-dark">
|
||||||
<div class="gruvbox-bg-hard round padding margin-bottom">
|
<div class="gruvbox-bg-hard round padding margin-bottom">
|
||||||
@ -276,7 +276,7 @@ templ Home(serviceBackups map[string]map[string][]backup.BackupInfo, serviceConf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GroupHeaderComponent renders just the group header with up-to-date stats
|
// GroupHeaderComponent renders just the group header with up-to-date stats
|
||||||
templ GroupHeaderComponent(groupName string, serviceBackups map[string][]backup.BackupInfo, serviceConfigs map[string]ServiceProviderInfo, directory string) {
|
templ GroupHeaderComponent(groupName string, serviceBackups map[string][]models.BackupInfo, serviceConfigs map[string]ServiceProviderInfo, directory string) {
|
||||||
<div id={ fmt.Sprintf("group-header-%s", groupName) } class="gruvbox-bg1 round padding margin-bottom">
|
<div id={ fmt.Sprintf("group-header-%s", groupName) } class="gruvbox-bg1 round padding margin-bottom">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
<h3 class="medium">
|
<h3 class="medium">
|
||||||
@ -347,7 +347,7 @@ templ GroupHeaderComponent(groupName string, serviceBackups map[string][]backup.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Updated table templates with action column
|
// Updated table templates with action column
|
||||||
templ ServiceGroupBackupsTable(groupName string, serviceGroup map[string][]backup.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) {
|
templ ServiceGroupBackupsTable(groupName string, serviceGroup map[string][]models.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) {
|
||||||
<div class="group-backups-table" id={ fmt.Sprintf("backups-%s", groupName) }>
|
<div class="group-backups-table" id={ fmt.Sprintf("backups-%s", groupName) }>
|
||||||
if GetGroupTotalBackupCount(serviceGroup) > 0 {
|
if GetGroupTotalBackupCount(serviceGroup) > 0 {
|
||||||
<div class="overflow">
|
<div class="overflow">
|
||||||
@ -395,7 +395,7 @@ templ ServiceGroupBackupsTable(groupName string, serviceGroup map[string][]backu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ServiceGroupAllBackupsTable template for showing all backups
|
// ServiceGroupAllBackupsTable template for showing all backups
|
||||||
templ ServiceGroupAllBackupsTable(groupName string, serviceGroup map[string][]backup.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) {
|
templ ServiceGroupAllBackupsTable(groupName string, serviceGroup map[string][]models.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) {
|
||||||
<!-- We're returning just the INNER content now, not the whole container -->
|
<!-- We're returning just the INNER content now, not the whole container -->
|
||||||
if GetGroupTotalBackupCount(serviceGroup) > 0 {
|
if GetGroupTotalBackupCount(serviceGroup) > 0 {
|
||||||
<div class="overflow">
|
<div class="overflow">
|
||||||
@ -499,7 +499,7 @@ templ renderSortedBackups(groupName string, sortedBackups []BackupWithService, l
|
|||||||
|
|
||||||
|
|
||||||
// backupsTableRowsSorted renders backup rows sorted by creation time across all services
|
// backupsTableRowsSorted renders backup rows sorted by creation time across all services
|
||||||
templ backupsTableRowsSorted(groupName string, serviceGroup map[string][]backup.BackupInfo, limit int, serviceConfigs map[string]map[string]ServiceProviderInfo) {
|
templ backupsTableRowsSorted(groupName string, serviceGroup map[string][]models.BackupInfo, limit int, serviceConfigs map[string]map[string]ServiceProviderInfo) {
|
||||||
@renderSortedBackups(groupName, GetSortedBackups(serviceGroup), limit, serviceConfigs)
|
@renderSortedBackups(groupName, GetSortedBackups(serviceGroup), limit, serviceConfigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,16 +5,15 @@ package templates
|
|||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backea/internal/backup"
|
"backea/internal/backup/models"
|
||||||
"backea/internal/backup/strategy"
|
|
||||||
"backea/templates/layouts"
|
"backea/templates/layouts"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/a-h/templ"
|
|
||||||
templruntime "github.com/a-h/templ/runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FormatSize formats byte size to human-readable format
|
// FormatSize formats byte size to human-readable format
|
||||||
@ -69,7 +68,7 @@ func FormatServiceName(groupName, serviceIndex string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CalculateTotalSize calculates total size of all backups
|
// CalculateTotalSize calculates total size of all backups
|
||||||
func CalculateTotalSize(backups []backup.BackupInfo) int64 {
|
func CalculateTotalSize(backups []models.BackupInfo) int64 {
|
||||||
var total int64
|
var total int64
|
||||||
for _, b := range backups {
|
for _, b := range backups {
|
||||||
total += b.Size
|
total += b.Size
|
||||||
@ -78,7 +77,7 @@ func CalculateTotalSize(backups []backup.BackupInfo) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CalculateGroupTotalSize calculates total size of all backups for a service group
|
// CalculateGroupTotalSize calculates total size of all backups for a service group
|
||||||
func CalculateGroupTotalSize(serviceGroup map[string][]backup.BackupInfo) int64 {
|
func CalculateGroupTotalSize(serviceGroup map[string][]models.BackupInfo) int64 {
|
||||||
var total int64
|
var total int64
|
||||||
for _, backups := range serviceGroup {
|
for _, backups := range serviceGroup {
|
||||||
for _, b := range backups {
|
for _, b := range backups {
|
||||||
@ -89,7 +88,7 @@ func CalculateGroupTotalSize(serviceGroup map[string][]backup.BackupInfo) int64
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupTotalBackupCount returns the total number of backups across all services in a group
|
// GetGroupTotalBackupCount returns the total number of backups across all services in a group
|
||||||
func GetGroupTotalBackupCount(serviceGroup map[string][]backup.BackupInfo) int {
|
func GetGroupTotalBackupCount(serviceGroup map[string][]models.BackupInfo) int {
|
||||||
count := 0
|
count := 0
|
||||||
for _, backups := range serviceGroup {
|
for _, backups := range serviceGroup {
|
||||||
count += len(backups)
|
count += len(backups)
|
||||||
@ -98,7 +97,7 @@ func GetGroupTotalBackupCount(serviceGroup map[string][]backup.BackupInfo) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLatestBackupTime returns the most recent backup time for a service group
|
// GetLatestBackupTime returns the most recent backup time for a service group
|
||||||
func GetLatestBackupTime(serviceGroup map[string][]backup.BackupInfo) (time.Time, bool) {
|
func GetLatestBackupTime(serviceGroup map[string][]models.BackupInfo) (time.Time, bool) {
|
||||||
var latestTime time.Time
|
var latestTime time.Time
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
@ -113,7 +112,7 @@ func GetLatestBackupTime(serviceGroup map[string][]backup.BackupInfo) (time.Time
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupStatus returns the status of a service group based on the most recent backup
|
// GetGroupStatus returns the status of a service group based on the most recent backup
|
||||||
func GetGroupStatus(serviceGroup map[string][]backup.BackupInfo) string {
|
func GetGroupStatus(serviceGroup map[string][]models.BackupInfo) string {
|
||||||
latestTime, found := GetLatestBackupTime(serviceGroup)
|
latestTime, found := GetLatestBackupTime(serviceGroup)
|
||||||
if !found {
|
if !found {
|
||||||
return "No Backups"
|
return "No Backups"
|
||||||
@ -131,11 +130,11 @@ type ServiceProviderInfo struct {
|
|||||||
// BackupWithService represents a backup with its service identifier
|
// BackupWithService represents a backup with its service identifier
|
||||||
type BackupWithService struct {
|
type BackupWithService struct {
|
||||||
ServiceIndex string
|
ServiceIndex string
|
||||||
Backup backup.BackupInfo
|
Backup models.BackupInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSortedBackups collects all backups from a service group and sorts them by time
|
// GetSortedBackups collects all backups from a service group and sorts them by time
|
||||||
func GetSortedBackups(serviceGroup map[string][]backup.BackupInfo) []BackupWithService {
|
func GetSortedBackups(serviceGroup map[string][]models.BackupInfo) []BackupWithService {
|
||||||
var allBackups []BackupWithService
|
var allBackups []BackupWithService
|
||||||
|
|
||||||
// Collect all backups with their service indices
|
// Collect all backups with their service indices
|
||||||
@ -157,7 +156,7 @@ func GetSortedBackups(serviceGroup map[string][]backup.BackupInfo) []BackupWithS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Home renders the homepage with lazy-loaded backup information
|
// Home renders the homepage with lazy-loaded backup information
|
||||||
func Home(serviceBackups map[string]map[string][]strategy.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo, sortedGroupNames []string, groupDirectories map[string]string) templ.Component {
|
func Home(serviceBackups map[string]map[string][]models.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo, sortedGroupNames []string, groupDirectories map[string]string) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@ -433,7 +432,7 @@ func Home(serviceBackups map[string]map[string][]strategy.BackupInfo, serviceCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GroupHeaderComponent renders just the group header with up-to-date stats
|
// GroupHeaderComponent renders just the group header with up-to-date stats
|
||||||
func GroupHeaderComponent(groupName string, serviceBackups map[string][]backup.BackupInfo, serviceConfigs map[string]ServiceProviderInfo, directory string) templ.Component {
|
func GroupHeaderComponent(groupName string, serviceBackups map[string][]models.BackupInfo, serviceConfigs map[string]ServiceProviderInfo, directory string) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@ -667,7 +666,7 @@ func GroupHeaderComponent(groupName string, serviceBackups map[string][]backup.B
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Updated table templates with action column
|
// Updated table templates with action column
|
||||||
func ServiceGroupBackupsTable(groupName string, serviceGroup map[string][]backup.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) templ.Component {
|
func ServiceGroupBackupsTable(groupName string, serviceGroup map[string][]models.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@ -808,7 +807,7 @@ func ServiceGroupBackupsTable(groupName string, serviceGroup map[string][]backup
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ServiceGroupAllBackupsTable template for showing all backups
|
// ServiceGroupAllBackupsTable template for showing all backups
|
||||||
func ServiceGroupAllBackupsTable(groupName string, serviceGroup map[string][]backup.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) templ.Component {
|
func ServiceGroupAllBackupsTable(groupName string, serviceGroup map[string][]models.BackupInfo, serviceConfigs map[string]map[string]ServiceProviderInfo) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@ -1200,7 +1199,7 @@ func renderSortedBackups(groupName string, sortedBackups []BackupWithService, li
|
|||||||
}
|
}
|
||||||
|
|
||||||
// backupsTableRowsSorted renders backup rows sorted by creation time across all services
|
// backupsTableRowsSorted renders backup rows sorted by creation time across all services
|
||||||
func backupsTableRowsSorted(groupName string, serviceGroup map[string][]backup.BackupInfo, limit int, serviceConfigs map[string]map[string]ServiceProviderInfo) templ.Component {
|
func backupsTableRowsSorted(groupName string, serviceGroup map[string][]models.BackupInfo, limit int, serviceConfigs map[string]map[string]ServiceProviderInfo) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user