add some changes

This commit is contained in:
2026-01-15 09:44:57 +00:00
parent 9b1f85479b
commit 1d9406c93a
19 changed files with 4922 additions and 887 deletions

View File

@@ -156,6 +156,19 @@ func (h *Handler) ListClients(c *gin.Context) {
// Parse search query
opts.Search = c.Query("search")
// Parse category filter
if category := c.Query("category"); category != "" {
// Validate category
validCategories := map[string]bool{
"File": true,
"Database": true,
"Virtual": true,
}
if validCategories[category] {
opts.Category = category
}
}
clients, err := h.service.ListClients(c.Request.Context(), opts)
if err != nil {
h.logger.Error("Failed to list clients", "error", err)

View File

@@ -84,6 +84,7 @@ type Client struct {
Name string `json:"name"`
Uname *string `json:"uname,omitempty"`
Enabled bool `json:"enabled"`
Category *string `json:"category,omitempty"` // "File", "Database", "Virtual"
AutoPrune *bool `json:"auto_prune,omitempty"`
FileRetention *int64 `json:"file_retention,omitempty"`
JobRetention *int64 `json:"job_retention,omitempty"`
@@ -95,8 +96,9 @@ type Client struct {
// ListClientsOptions represents filtering options for clients
type ListClientsOptions struct {
Enabled *bool // Filter by enabled status (nil = all)
Search string // Search by client name
Enabled *bool // Filter by enabled status (nil = all)
Search string // Search by client name
Category string // Filter by category: "File", "Database", "Virtual" (empty = all)
}
// DashboardStats represents statistics for the backup dashboard
@@ -312,7 +314,8 @@ func (s *Service) queryBaculaDirect(ctx context.Context) ([]Job, error) {
// syncFromBconsole syncs jobs using bconsole command (fallback method)
func (s *Service) syncFromBconsole(ctx context.Context) error {
// Execute bconsole command to list jobs
cmd := exec.CommandContext(ctx, "sh", "-c", "echo -e 'list jobs\nquit' | bconsole")
bconsoleConfig := "/opt/calypso/conf/bacula/bconsole.conf"
cmd := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("echo -e 'list jobs\nquit' | bconsole -c %s", bconsoleConfig))
output, err := cmd.CombinedOutput()
if err != nil {
@@ -507,7 +510,8 @@ func (s *Service) parseBconsoleOutput(ctx context.Context, output string) []Job
// getClientNameFromJob gets client name from job details using bconsole
func (s *Service) getClientNameFromJob(ctx context.Context, jobID int) string {
// Execute bconsole to get job details
cmd := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("echo -e 'list job jobid=%d\nquit' | bconsole", jobID))
bconsoleConfig := "/opt/calypso/conf/bacula/bconsole.conf"
cmd := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("echo -e 'list job jobid=%d\nquit' | bconsole -c %s", jobID, bconsoleConfig))
output, err := cmd.CombinedOutput()
if err != nil {
@@ -550,7 +554,9 @@ func (s *Service) ExecuteBconsoleCommand(ctx context.Context, command string) (s
escapedCommand := strings.ReplaceAll(commandWithQuit, "'", "'\"'\"'")
// Execute bconsole command using printf to avoid echo -e issues
cmd := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("printf '%%s\\n' '%s' | bconsole", escapedCommand))
// Use -c flag to specify config file location
bconsoleConfig := "/opt/calypso/conf/bacula/bconsole.conf"
cmd := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("printf '%%s\\n' '%s' | bconsole -c %s", escapedCommand, bconsoleConfig))
output, err := cmd.CombinedOutput()
if err != nil {
@@ -728,10 +734,94 @@ func (s *Service) queryClientsFromDatabase(ctx context.Context, opts ListClients
return nil, fmt.Errorf("error iterating client rows: %w", err)
}
// Load categories from Calypso database
if err := s.loadClientCategories(ctx, clients); err != nil {
s.logger.Warn("Failed to load client categories", "error", err)
// Continue without categories, set default to "File"
for i := range clients {
if clients[i].Category == nil {
defaultCategory := "File"
clients[i].Category = &defaultCategory
}
}
}
// Filter by category if specified
if opts.Category != "" {
filtered := []Client{}
for _, client := range clients {
if client.Category != nil && *client.Category == opts.Category {
filtered = append(filtered, client)
}
}
clients = filtered
}
s.logger.Debug("Queried clients from Bacula database", "count", len(clients))
return clients, nil
}
// loadClientCategories loads category information from Calypso database
func (s *Service) loadClientCategories(ctx context.Context, clients []Client) error {
if len(clients) == 0 {
return nil
}
// Build query to get categories for all client IDs
clientIDs := make([]interface{}, len(clients))
for i, client := range clients {
clientIDs[i] = client.ClientID
}
// Create placeholders for IN clause
placeholders := make([]string, len(clientIDs))
args := make([]interface{}, len(clientIDs))
for i := range clientIDs {
placeholders[i] = fmt.Sprintf("$%d", i+1)
args[i] = clientIDs[i]
}
query := fmt.Sprintf(`
SELECT client_id, category
FROM client_metadata
WHERE client_id IN (%s)
`, strings.Join(placeholders, ","))
rows, err := s.db.QueryContext(ctx, query, args...)
if err != nil {
// Table might not exist yet (migration not run), return nil error
if strings.Contains(err.Error(), "does not exist") {
return nil
}
return fmt.Errorf("failed to query client categories: %w", err)
}
defer rows.Close()
// Create map of client_id -> category
categoryMap := make(map[int]string)
for rows.Next() {
var clientID int
var category string
if err := rows.Scan(&clientID, &category); err != nil {
continue
}
categoryMap[clientID] = category
}
// Assign categories to clients
for i := range clients {
if category, exists := categoryMap[clients[i].ClientID]; exists {
clients[i].Category = &category
} else {
// Default to "File" if no category found
defaultCategory := "File"
clients[i].Category = &defaultCategory
}
}
return nil
}
// queryClientsFromBconsole queries clients using bconsole command (fallback method)
func (s *Service) queryClientsFromBconsole(ctx context.Context, opts ListClientsOptions) ([]Client, error) {
// Execute bconsole command to list clients
@@ -752,6 +842,18 @@ func (s *Service) queryClientsFromBconsole(ctx context.Context, opts ListClients
clients := s.parseBconsoleClientsOutput(output)
s.logger.Debug("Parsed clients from bconsole", "count", len(clients))
// Load categories from Calypso database
if err := s.loadClientCategories(ctx, clients); err != nil {
s.logger.Warn("Failed to load client categories", "error", err)
// Continue without categories, set default to "File"
for i := range clients {
if clients[i].Category == nil {
defaultCategory := "File"
clients[i].Category = &defaultCategory
}
}
}
// Apply filters
filtered := []Client{}
for _, client := range clients {
@@ -761,6 +863,11 @@ func (s *Service) queryClientsFromBconsole(ctx context.Context, opts ListClients
if opts.Search != "" && !strings.Contains(strings.ToLower(client.Name), strings.ToLower(opts.Search)) {
continue
}
if opts.Category != "" {
if client.Category == nil || *client.Category != opts.Category {
continue
}
}
filtered = append(filtered, client)
}