bugfixes-clearing-router-and-merge

This commit is contained in:
hhftechnologies 2026-01-23 12:46:21 +05:30
parent 54c741b56a
commit e578ec6a81
4 changed files with 92 additions and 29 deletions

View file

@ -24,13 +24,24 @@ func NewServiceHandler(db *sql.DB) *ServiceHandler {
// GetServices returns all service configurations
// Supports pagination via ?page=N&page_size=M query parameters
// By default only returns active services; use ?status=all to include disabled
func (h *ServiceHandler) GetServices(c *gin.Context) {
usePagination := IsPaginationRequested(c)
params := GetPaginationParams(c)
// Filter by status - default to active only
statusFilter := c.DefaultQuery("status", "active")
statusCondition := "WHERE status = 'active'"
if statusFilter == "all" {
statusCondition = ""
} else if statusFilter == "disabled" {
statusCondition = "WHERE status = 'disabled'"
}
var total int
if usePagination {
err := h.DB.QueryRow("SELECT COUNT(*) FROM services").Scan(&total)
countQuery := "SELECT COUNT(*) FROM services " + statusCondition
err := h.DB.QueryRow(countQuery).Scan(&total)
if err != nil {
log.Printf("Error counting services: %v", err)
ResponseWithError(c, http.StatusInternalServerError, "Failed to count services")
@ -38,7 +49,7 @@ func (h *ServiceHandler) GetServices(c *gin.Context) {
}
}
query := "SELECT id, name, type, config FROM services ORDER BY name"
query := "SELECT id, name, type, config, COALESCE(status, 'active') as status, COALESCE(source_type, '') as source_type FROM services " + statusCondition + " ORDER BY name"
var rows *sql.Rows
var err error
@ -58,8 +69,8 @@ func (h *ServiceHandler) GetServices(c *gin.Context) {
services := []map[string]interface{}{}
for rows.Next() {
var id, name, typ, configStr string
if err := rows.Scan(&id, &name, &typ, &configStr); err != nil {
var id, name, typ, configStr, status, sourceType string
if err := rows.Scan(&id, &name, &typ, &configStr, &status, &sourceType); err != nil {
log.Printf("Error scanning service row: %v", err)
continue
}
@ -71,10 +82,12 @@ func (h *ServiceHandler) GetServices(c *gin.Context) {
}
services = append(services, map[string]interface{}{
"id": id,
"name": name,
"type": typ,
"config": config,
"id": id,
"name": name,
"type": typ,
"config": config,
"status": status,
"source_type": sourceType,
})
}
@ -150,7 +163,7 @@ func (h *ServiceHandler) CreateService(c *gin.Context) {
id, service.Name, service.Type)
result, txErr := tx.Exec(
"INSERT INTO services (id, name, type, config) VALUES (?, ?, ?, ?)",
"INSERT INTO services (id, name, type, config, status, source_type) VALUES (?, ?, ?, ?, 'active', 'manual')",
id, service.Name, service.Type, string(configJSON),
)
@ -192,8 +205,11 @@ func (h *ServiceHandler) GetService(c *gin.Context) {
return
}
var name, typ, configStr string
err := h.DB.QueryRow("SELECT name, type, config FROM services WHERE id = ?", id).Scan(&name, &typ, &configStr)
var name, typ, configStr, status, sourceType string
err := h.DB.QueryRow(
"SELECT name, type, config, COALESCE(status, 'active'), COALESCE(source_type, '') FROM services WHERE id = ?",
id,
).Scan(&name, &typ, &configStr, &status, &sourceType)
if err == sql.ErrNoRows {
ResponseWithError(c, http.StatusNotFound, "Service not found")
return
@ -210,10 +226,12 @@ func (h *ServiceHandler) GetService(c *gin.Context) {
}
c.JSON(http.StatusOK, gin.H{
"id": id,
"name": name,
"type": typ,
"config": config,
"id": id,
"name": name,
"type": typ,
"config": config,
"status": status,
"source_type": sourceType,
})
}

View file

@ -578,6 +578,45 @@ func runPostMigrationUpdates(db *sql.DB) error {
// Create index on host for faster lookups when matching by host
_, _ = db.Exec("CREATE INDEX IF NOT EXISTS idx_resources_host ON resources(host)")
// Check for status column in services table (for tracking sync state)
var hasServicesStatusColumn bool
err = db.QueryRow(`
SELECT COUNT(*) > 0
FROM pragma_table_info('services')
WHERE name = 'status'
`).Scan(&hasServicesStatusColumn)
if err != nil {
return fmt.Errorf("failed to check if services.status column exists: %w", err)
}
if !hasServicesStatusColumn {
log.Println("Adding status column to services table")
if _, err := db.Exec("ALTER TABLE services ADD COLUMN status TEXT NOT NULL DEFAULT 'active'"); err != nil {
return fmt.Errorf("failed to add status column to services: %w", err)
}
log.Println("Successfully added status column to services table")
}
// Check for source_type column in services table (for tracking sync origin)
var hasServicesSourceTypeColumn bool
err = db.QueryRow(`
SELECT COUNT(*) > 0
FROM pragma_table_info('services')
WHERE name = 'source_type'
`).Scan(&hasServicesSourceTypeColumn)
if err != nil {
return fmt.Errorf("failed to check if services.source_type column exists: %w", err)
}
if !hasServicesSourceTypeColumn {
log.Println("Adding source_type column to services table")
if _, err := db.Exec("ALTER TABLE services ADD COLUMN source_type TEXT DEFAULT ''"); err != nil {
return fmt.Errorf("failed to add source_type column to services: %w", err)
}
log.Println("Successfully added source_type column to services table")
}
// Create index on services status for faster filtering
_, _ = db.Exec("CREATE INDEX IF NOT EXISTS idx_services_status ON services(status)")
return nil
}

View file

@ -73,6 +73,8 @@ CREATE TABLE IF NOT EXISTS services (
name TEXT NOT NULL,
type TEXT NOT NULL,
config TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
source_type TEXT DEFAULT '', -- 'pangolin', 'traefik', 'manual', etc.
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View file

@ -122,9 +122,10 @@ func (sw *ServiceWatcher) checkServices() error {
return fmt.Errorf("failed to fetch services: %w", err)
}
// Get all existing services from the database
// Get all existing active Pangolin-synced services from the database
// We only track Pangolin-synced services for cleanup (source_type = 'pangolin')
var existingServices []string
rows, err := sw.db.Query("SELECT id FROM services")
rows, err := sw.db.Query("SELECT id FROM services WHERE status = 'active' AND source_type = 'pangolin'")
if err != nil {
return fmt.Errorf("failed to query existing services: %w", err)
}
@ -167,19 +168,22 @@ func (sw *ServiceWatcher) checkServices() error {
foundServices[normalizedID] = true
}
// Optionally, mark services as "inactive" if they no longer exist in the data source
// This is commented out by default to avoid deleting user-created services
/*
// Mark Pangolin-synced services as disabled if they no longer exist in the data source
// Only affects services with source_type = 'pangolin' (already filtered in the query above)
for _, serviceID := range existingServices {
normalizedID := util.NormalizeID(serviceID)
if !foundServices[normalizedID] {
log.Printf("Service %s no longer exists in data source, consider marking as inactive", serviceID)
// Optional: You could update a status field if you add one to the services table
// _, err := sw.db.Exec("UPDATE services SET status = 'inactive' WHERE id = ?", serviceID)
log.Printf("Service %s no longer exists in Pangolin, marking as disabled", serviceID)
_, err := sw.db.Exec(
"UPDATE services SET status = 'disabled', updated_at = ? WHERE id = ?",
time.Now(), serviceID,
)
if err != nil {
log.Printf("Error marking service as disabled: %v", err)
}
}
}
*/
return nil
}
@ -449,9 +453,9 @@ func (sw *ServiceWatcher) createService(service models.Service) error {
return fmt.Errorf("error checking service existence in transaction: %w", err)
}
// Insert the service
// Insert the service with source_type for tracking origin
_, err = tx.Exec(
"INSERT INTO services (id, name, type, config, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
"INSERT INTO services (id, name, type, config, status, source_type, created_at, updated_at) VALUES (?, ?, ?, ?, 'active', 'pangolin', ?, ?)",
service.ID, service.Name, service.Type, string(configJSON), time.Now(), time.Now(),
)
@ -504,9 +508,9 @@ func (sw *ServiceWatcher) updateService(service models.Service, existingID strin
// Update the service using a transaction
return sw.db.WithTransaction(func(tx *sql.Tx) error {
// Update the service using the existing ID
// Update the service using the existing ID, ensure status is active and source_type is pangolin
result, err := tx.Exec(
"UPDATE services SET name = ?, type = ?, config = ?, updated_at = ? WHERE id = ?",
"UPDATE services SET name = ?, type = ?, config = ?, status = 'active', source_type = 'pangolin', updated_at = ? WHERE id = ?",
service.Name, service.Type, string(configJSON), time.Now(), existingID,
)