mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-19 21:10:53 +02:00
Implement Job Configuration Ordering in Job Form
- Added functionality to allow users to select and reorder job configurations in both new and edit job forms. - Introduced JavaScript logic to handle the display and ordering of selected configurations, including move up/down buttons. - Updated backend to process and store the order of configurations when creating or updating jobs. - Enhanced logging for job creation and update processes to include configuration order details.
This commit is contained in:
+197
-10
@@ -72,6 +72,172 @@ templ configSearchScript() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle job ordering
|
||||
const setupJobOrdering = (configListId, selectedListId, formId, savedOrder) => {
|
||||
const configList = document.getElementById(configListId);
|
||||
const selectedList = document.getElementById(selectedListId);
|
||||
const form = document.getElementById(formId);
|
||||
|
||||
if (!configList || !selectedList || !form) return;
|
||||
|
||||
// Get saved order if available
|
||||
const orderedIds = savedOrder ? savedOrder.split(',').map(id => id.trim()) : [];
|
||||
console.log('Initial saved order:', orderedIds);
|
||||
|
||||
// Initialize selected items from checked checkboxes
|
||||
const updateSelectedItems = (initialLoad = false) => {
|
||||
// Clear current list
|
||||
selectedList.innerHTML = '';
|
||||
|
||||
// Get all checked checkboxes
|
||||
const checkedItems = configList.querySelectorAll('input[type="checkbox"]:checked');
|
||||
|
||||
if (checkedItems.length === 0) {
|
||||
selectedList.innerHTML = '<div class="text-center py-4 text-secondary-500 dark:text-secondary-400">No configurations selected</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a map of config items for easy access
|
||||
const configItems = {};
|
||||
checkedItems.forEach(checkbox => {
|
||||
configItems[checkbox.value] = {
|
||||
checkbox: checkbox,
|
||||
configId: checkbox.value,
|
||||
configName: checkbox.nextElementSibling.textContent.trim()
|
||||
};
|
||||
});
|
||||
|
||||
// If we have a saved order and this is the initial load, use that order
|
||||
let itemsToShow = [];
|
||||
if (initialLoad && orderedIds.length > 0) {
|
||||
// First add items in the saved order
|
||||
orderedIds.forEach(id => {
|
||||
if (configItems[id]) {
|
||||
itemsToShow.push(configItems[id]);
|
||||
delete configItems[id]; // Remove from map to avoid duplicates
|
||||
}
|
||||
});
|
||||
|
||||
// Then add any remaining checked items not in the saved order
|
||||
Object.values(configItems).forEach(item => {
|
||||
itemsToShow.push(item);
|
||||
});
|
||||
} else {
|
||||
// Just add all checked items in their current order
|
||||
itemsToShow = Object.values(configItems);
|
||||
}
|
||||
|
||||
// Add each item to the selected list
|
||||
itemsToShow.forEach((item, index) => {
|
||||
const configId = item.configId;
|
||||
const configName = item.configName;
|
||||
|
||||
const listItem = document.createElement('div');
|
||||
listItem.className = 'flex items-center justify-between p-2 mb-2 bg-white dark:bg-secondary-800 border border-secondary-200 dark:border-secondary-700 rounded-lg';
|
||||
listItem.setAttribute('data-id', configId);
|
||||
|
||||
listItem.innerHTML = `
|
||||
<div class="flex items-center">
|
||||
<span class="inline-flex items-center justify-center h-6 w-6 rounded-full bg-primary-100 dark:bg-primary-900 mr-2 text-primary-700 dark:text-primary-300 text-sm">${index + 1}</span>
|
||||
<span class="font-medium text-secondary-700 dark:text-secondary-300">${configName}</span>
|
||||
</div>
|
||||
<div class="flex space-x-1">
|
||||
<button type="button" class="move-up p-1 rounded hover:bg-secondary-100 dark:hover:bg-secondary-700" title="Move up">
|
||||
<i class="fas fa-arrow-up text-secondary-500"></i>
|
||||
</button>
|
||||
<button type="button" class="move-down p-1 rounded hover:bg-secondary-100 dark:hover:bg-secondary-700" title="Move down">
|
||||
<i class="fas fa-arrow-down text-secondary-500"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
selectedList.appendChild(listItem);
|
||||
});
|
||||
|
||||
// Update hidden order inputs
|
||||
updateOrderInputs();
|
||||
};
|
||||
|
||||
// Update hidden inputs with the current order
|
||||
const updateOrderInputs = () => {
|
||||
const items = selectedList.querySelectorAll('.flex.items-center.justify-between');
|
||||
if (items.length === 0) return;
|
||||
|
||||
// Remove any existing order input to avoid duplicates
|
||||
const existingOrderInput = form.querySelector('input[name="config_order"]');
|
||||
if (existingOrderInput) {
|
||||
existingOrderInput.remove();
|
||||
}
|
||||
|
||||
// Create a new input with the current order
|
||||
const orderedIds = Array.from(items).map(item => item.getAttribute('data-id'));
|
||||
|
||||
// Create a hidden input to store the order
|
||||
const configOrderInput = document.createElement('input');
|
||||
configOrderInput.type = 'hidden';
|
||||
configOrderInput.name = 'config_order';
|
||||
configOrderInput.value = orderedIds.join(',');
|
||||
|
||||
// Add the input to the form
|
||||
form.appendChild(configOrderInput);
|
||||
|
||||
// Update the visible order numbers
|
||||
items.forEach((item, index) => {
|
||||
const orderNum = index + 1;
|
||||
const orderSpan = item.querySelector('span.rounded-full');
|
||||
if (orderSpan) {
|
||||
orderSpan.textContent = orderNum;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Updated order input:', configOrderInput.value);
|
||||
};
|
||||
|
||||
// Initialize the selected list with saved order if available
|
||||
updateSelectedItems(true);
|
||||
|
||||
// Handle checkbox changes
|
||||
configList.addEventListener('change', (e) => {
|
||||
if (e.target.matches('input[type="checkbox"]')) {
|
||||
updateSelectedItems(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle reordering
|
||||
selectedList.addEventListener('click', (e) => {
|
||||
const listItem = e.target.closest('.flex.items-center.justify-between');
|
||||
if (!listItem) return;
|
||||
|
||||
if (e.target.closest('.move-up')) {
|
||||
const prev = listItem.previousElementSibling;
|
||||
if (prev) {
|
||||
selectedList.insertBefore(listItem, prev);
|
||||
updateOrderInputs();
|
||||
}
|
||||
} else if (e.target.closest('.move-down')) {
|
||||
const next = listItem.nextElementSibling;
|
||||
if (next) {
|
||||
selectedList.insertBefore(next, listItem);
|
||||
updateOrderInputs();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure the order input is updated before submission
|
||||
form.addEventListener('submit', function(e) {
|
||||
updateOrderInputs();
|
||||
console.log('Form submitted with order:', form.querySelector('input[name="config_order"]')?.value);
|
||||
});
|
||||
};
|
||||
|
||||
// Setup ordering for new job form
|
||||
setupJobOrdering('config-list', 'selected-configs', 'new-job-form', null);
|
||||
|
||||
// Setup ordering for edit job form
|
||||
const editJobForm = document.getElementById('edit-job-form');
|
||||
const savedOrderEdit = editJobForm ? editJobForm.getAttribute('data-config-order') : null;
|
||||
setupJobOrdering('config-list-edit', 'selected-configs-edit', 'edit-job-form', savedOrderEdit);
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@@ -95,6 +261,7 @@ templ JobForm(ctx context.Context, data JobFormData) {
|
||||
|
||||
if data.IsNew {
|
||||
<form
|
||||
id="new-job-form"
|
||||
class="space-y-6"
|
||||
hx-post="/jobs"
|
||||
hx-target="body"
|
||||
@@ -161,10 +328,19 @@ templ JobForm(ctx context.Context, data JobFormData) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-2 text-sm text-secondary-500 dark:text-secondary-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Select one or more configurations to run on this schedule.
|
||||
</p>
|
||||
<!-- Selected Configurations Order List -->
|
||||
<div class="mt-4">
|
||||
<label class="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-2">
|
||||
<i class="fas fa-sort-amount-down mr-1"></i> Execution Order
|
||||
</label>
|
||||
<div id="selected-configs" class="border border-secondary-300 dark:border-secondary-700 rounded-md p-3 min-h-20 bg-secondary-50 dark:bg-secondary-900">
|
||||
<!-- Selected items will be populated by JavaScript -->
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-secondary-500 dark:text-secondary-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Use the arrows to change the order in which configurations will execute.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -341,10 +517,12 @@ templ JobForm(ctx context.Context, data JobFormData) {
|
||||
</form>
|
||||
} else {
|
||||
<form
|
||||
id="edit-job-form"
|
||||
class="space-y-6"
|
||||
hx-post={ fmt.Sprintf("/jobs/%d", data.Job.ID) }
|
||||
hx-target="body"
|
||||
hx-boost="true">
|
||||
hx-boost="true"
|
||||
data-config-order={ data.Job.ConfigIDs }>
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-1">Job Name</label>
|
||||
@@ -411,10 +589,19 @@ templ JobForm(ctx context.Context, data JobFormData) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-2 text-sm text-secondary-500 dark:text-secondary-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Select one or more configurations to run on this schedule.
|
||||
</p>
|
||||
<!-- Selected Configurations Order List for edit -->
|
||||
<div class="mt-4">
|
||||
<label class="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-2">
|
||||
<i class="fas fa-sort-amount-down mr-1"></i> Execution Order
|
||||
</label>
|
||||
<div id="selected-configs-edit" class="border border-secondary-300 dark:border-secondary-700 rounded-md p-3 min-h-20 bg-secondary-50 dark:bg-secondary-900">
|
||||
<!-- Selected items will be populated by JavaScript -->
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-secondary-500 dark:text-secondary-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Use the arrows to change the order in which configurations will execute.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -599,7 +786,7 @@ templ JobForm(ctx context.Context, data JobFormData) {
|
||||
<div class="px-8 py-4 bg-secondary-50 dark:bg-secondary-800 border-t border-secondary-200 dark:border-secondary-700 text-center">
|
||||
<p class="text-sm text-secondary-600 dark:text-secondary-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Jobs will run according to their schedule and execute the selected transfer configuration
|
||||
Jobs will run according to their schedule and execute the selected transfer configurations in the order specified
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+37
-2
@@ -2,6 +2,7 @@ package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -186,6 +187,9 @@ func (j *Job) SetConfigIDsList(ids []uint) {
|
||||
// Join with commas
|
||||
j.ConfigIDs = strings.Join(strIDs, ",")
|
||||
|
||||
// Debug log the final ConfigIDs string
|
||||
log.Printf("SetConfigIDsList: Setting ConfigIDs to: %s (from %v)", j.ConfigIDs, ids)
|
||||
|
||||
// If there's at least one ID, set ConfigID to the first one for backward compatibility
|
||||
if len(ids) > 0 {
|
||||
j.ConfigID = ids[0]
|
||||
@@ -376,8 +380,25 @@ func (db *DB) GetJob(id uint) (*Job, error) {
|
||||
}
|
||||
|
||||
func (db *DB) UpdateJob(job *Job) error {
|
||||
log.Printf("UpdateJob: Updating job ID: %d, ConfigIDs: %s", job.ID, job.ConfigIDs)
|
||||
|
||||
// Use Omit to prevent GORM from updating or creating a new config
|
||||
return db.Omit("Config").Save(job).Error
|
||||
return db.Model(&Job{}).
|
||||
Where("id = ?", job.ID).
|
||||
Omit("Config").
|
||||
Updates(map[string]interface{}{
|
||||
"name": job.Name,
|
||||
"config_id": job.ConfigID,
|
||||
"config_ids": job.ConfigIDs, // Explicitly update config_ids
|
||||
"schedule": job.Schedule,
|
||||
"enabled": job.Enabled,
|
||||
"webhook_enabled": job.WebhookEnabled,
|
||||
"webhook_url": job.WebhookURL,
|
||||
"webhook_secret": job.WebhookSecret,
|
||||
"webhook_headers": job.WebhookHeaders,
|
||||
"notify_on_success": job.NotifyOnSuccess,
|
||||
"notify_on_failure": job.NotifyOnFailure,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (db *DB) DeleteJob(id uint) error {
|
||||
@@ -965,7 +986,21 @@ func (db *DB) GetConfigsForJob(jobID uint) ([]TransferConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
// Create a map for quick lookup
|
||||
configMap := make(map[uint]TransferConfig)
|
||||
for _, config := range configs {
|
||||
configMap[config.ID] = config
|
||||
}
|
||||
|
||||
// Create a new slice with configs in the correct order
|
||||
orderedConfigs := make([]TransferConfig, 0, len(configs))
|
||||
for _, configID := range configIDs {
|
||||
if config, exists := configMap[configID]; exists {
|
||||
orderedConfigs = append(orderedConfigs, config)
|
||||
}
|
||||
}
|
||||
|
||||
return orderedConfigs, nil
|
||||
}
|
||||
|
||||
// GetSkipProcessedFiles returns the value of SkipProcessedFiles with a default if nil
|
||||
|
||||
@@ -347,14 +347,45 @@ func (s *Scheduler) executeJob(jobID uint) {
|
||||
return
|
||||
}
|
||||
|
||||
s.log.LogDebug("Processing %d configurations: %+v", len(configs), configs)
|
||||
s.log.LogDebug("Loaded %d configurations for job %d", len(configs), jobID)
|
||||
|
||||
if len(configs) == 0 {
|
||||
s.log.LogError("Error: job %d has no associated configurations", jobID)
|
||||
return
|
||||
}
|
||||
|
||||
s.log.LogInfo("Loaded job %d with %d configurations", jobID, len(configs))
|
||||
// Get the ordered config IDs from the job
|
||||
orderedConfigIDs := job.GetConfigIDsList()
|
||||
s.log.LogDebug("Ordered config IDs for job %d: %v", jobID, orderedConfigIDs)
|
||||
|
||||
// Create a map of configs for easy lookup
|
||||
configMap := make(map[uint]db.TransferConfig)
|
||||
for _, config := range configs {
|
||||
configMap[config.ID] = config
|
||||
}
|
||||
|
||||
// Process configurations in the specified order
|
||||
var orderedConfigs []db.TransferConfig
|
||||
|
||||
// First, add configs in the order specified in the job's ConfigIDs
|
||||
for _, configID := range orderedConfigIDs {
|
||||
if config, exists := configMap[configID]; exists {
|
||||
orderedConfigs = append(orderedConfigs, config)
|
||||
delete(configMap, configID) // Remove from map to avoid duplicates
|
||||
}
|
||||
}
|
||||
|
||||
// Add any remaining configs not in the ordered list (shouldn't happen, but just in case)
|
||||
for _, config := range configMap {
|
||||
orderedConfigs = append(orderedConfigs, config)
|
||||
}
|
||||
|
||||
s.log.LogInfo("Processing job %d with %d configurations in specified order", jobID, len(orderedConfigs))
|
||||
|
||||
// Log the order of execution
|
||||
for i, config := range orderedConfigs {
|
||||
s.log.LogDebug("Execution order %d/%d: Config ID %d (%s)", i+1, len(orderedConfigs), config.ID, config.Name)
|
||||
}
|
||||
|
||||
// Update job last run time
|
||||
startTime := time.Now()
|
||||
@@ -363,9 +394,9 @@ func (s *Scheduler) executeJob(jobID uint) {
|
||||
s.log.LogError("Error updating job last run time for job %d: %v", jobID, err)
|
||||
}
|
||||
|
||||
// Process each configuration
|
||||
for i, config := range configs {
|
||||
s.processConfiguration(&job, &config, i+1, len(configs))
|
||||
// Process each configuration in the specified order
|
||||
for i, config := range orderedConfigs {
|
||||
s.processConfiguration(&job, &config, i+1, len(orderedConfigs))
|
||||
}
|
||||
|
||||
// Update next run time after execution
|
||||
|
||||
@@ -2,8 +2,10 @@ package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/starfleetcptn/gomft/components"
|
||||
@@ -141,6 +143,9 @@ func (h *Handlers) HandleEditJob(c *gin.Context) {
|
||||
func (h *Handlers) HandleCreateJob(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleCreateJob: Form data received: %v", c.Request.PostForm)
|
||||
|
||||
// Parse form data
|
||||
var job db.Job
|
||||
if err := c.ShouldBind(&job); err != nil {
|
||||
@@ -148,6 +153,9 @@ func (h *Handlers) HandleCreateJob(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleCreateJob: Job after binding: %+v", job)
|
||||
|
||||
// Get multiple config IDs from form
|
||||
configIDs := c.PostFormArray("config_ids[]")
|
||||
if len(configIDs) == 0 {
|
||||
@@ -155,35 +163,83 @@ func (h *Handlers) HandleCreateJob(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleCreateJob: config_ids[]: %v", configIDs)
|
||||
|
||||
// Process config IDs
|
||||
var configIDsList []uint
|
||||
for _, configIDStr := range configIDs {
|
||||
configID, err := strconv.ParseUint(configIDStr, 10, 32)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration ID format")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify that the config exists and belongs to the user
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, configID).Error; err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration selected")
|
||||
return
|
||||
}
|
||||
// Check if we have an explicit order specified
|
||||
configOrder := c.PostForm("config_order")
|
||||
log.Printf("HandleCreateJob: config_order: %s", configOrder)
|
||||
|
||||
// Check if the config belongs to the user
|
||||
if config.CreatedBy != userID {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.String(http.StatusForbidden, "You do not have permission to use this configuration")
|
||||
if configOrder != "" {
|
||||
// Parse the ordered list
|
||||
orderStrings := strings.Split(configOrder, ",")
|
||||
log.Printf("HandleCreateJob: order strings: %v", orderStrings)
|
||||
|
||||
for _, configIDStr := range orderStrings {
|
||||
configID, err := strconv.ParseUint(configIDStr, 10, 32)
|
||||
if err != nil {
|
||||
log.Printf("HandleCreateJob: Error parsing config ID: %v", err)
|
||||
c.String(http.StatusBadRequest, "Invalid configuration ID format in order")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
configIDsList = append(configIDsList, uint(configID))
|
||||
// Verify that the config exists and belongs to the user
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, configID).Error; err != nil {
|
||||
log.Printf("HandleCreateJob: Invalid config ID: %d, error: %v", configID, err)
|
||||
c.String(http.StatusBadRequest, "Invalid configuration selected")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the config belongs to the user
|
||||
if config.CreatedBy != userID {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.String(http.StatusForbidden, "You do not have permission to use this configuration")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
configIDsList = append(configIDsList, uint(configID))
|
||||
}
|
||||
} else {
|
||||
// Fall back to unordered config IDs
|
||||
log.Printf("HandleCreateJob: No config_order found, using checkbox order")
|
||||
for _, configIDStr := range configIDs {
|
||||
configID, err := strconv.ParseUint(configIDStr, 10, 32)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration ID format")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify that the config exists and belongs to the user
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, configID).Error; err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration selected")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the config belongs to the user
|
||||
if config.CreatedBy != userID {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.String(http.StatusForbidden, "You do not have permission to use this configuration")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
configIDsList = append(configIDsList, uint(configID))
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleCreateJob: Final configIDsList: %v", configIDsList)
|
||||
|
||||
// Set the first config ID for backward compatibility
|
||||
if len(configIDsList) > 0 {
|
||||
job.ConfigID = configIDsList[0]
|
||||
@@ -214,6 +270,10 @@ func (h *Handlers) HandleCreateJob(c *gin.Context) {
|
||||
// Set the config IDs list
|
||||
job.SetConfigIDsList(configIDsList)
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleCreateJob: Job after setting ConfigIDsList: %+v", job)
|
||||
log.Printf("HandleCreateJob: Job.ConfigIDs: %s", job.ConfigIDs)
|
||||
|
||||
// Set the boolean fields - handle both "on" and "true" values for checkboxes
|
||||
enabledVal := c.Request.FormValue("enabled")
|
||||
jobEnabledValue := enabledVal == "on" || enabledVal == "true"
|
||||
@@ -239,10 +299,13 @@ func (h *Handlers) HandleCreateJob(c *gin.Context) {
|
||||
|
||||
// Create the job
|
||||
if err := h.DB.CreateJob(&job); err != nil {
|
||||
log.Printf("HandleCreateJob: Error creating job: %v", err)
|
||||
c.String(http.StatusInternalServerError, "Failed to create job")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("HandleCreateJob: Job successfully created with ID: %d", job.ID)
|
||||
|
||||
// Schedule the job with the scheduler
|
||||
if err := h.Scheduler.ScheduleJob(&job); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Job created but scheduling failed: "+err.Error())
|
||||
@@ -257,8 +320,13 @@ func (h *Handlers) HandleUpdateJob(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
userID := c.GetUint("userID")
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleUpdateJob: Updating job ID: %s", id)
|
||||
log.Printf("HandleUpdateJob: Form data received: %v", c.Request.PostForm)
|
||||
|
||||
var job db.Job
|
||||
if err := h.DB.First(&job, id).Error; err != nil {
|
||||
log.Printf("HandleUpdateJob: Job not found: %v", err)
|
||||
c.String(http.StatusNotFound, "Job not found")
|
||||
return
|
||||
}
|
||||
@@ -275,49 +343,102 @@ func (h *Handlers) HandleUpdateJob(c *gin.Context) {
|
||||
|
||||
// Get the old job values for comparison
|
||||
oldJob := job
|
||||
log.Printf("HandleUpdateJob: Original job: %+v", oldJob)
|
||||
log.Printf("HandleUpdateJob: Original job ConfigIDs: %s", oldJob.ConfigIDs)
|
||||
|
||||
// Parse form data
|
||||
if err := c.ShouldBind(&job); err != nil {
|
||||
log.Printf("HandleUpdateJob: Error binding form data: %v", err)
|
||||
c.String(http.StatusBadRequest, "Invalid form data")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("HandleUpdateJob: Job after binding: %+v", job)
|
||||
|
||||
// Get multiple config IDs from form
|
||||
configIDs := c.PostFormArray("config_ids[]")
|
||||
if len(configIDs) == 0 {
|
||||
log.Printf("HandleUpdateJob: No config_ids[] found in form data")
|
||||
c.String(http.StatusBadRequest, "At least one configuration must be selected")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("HandleUpdateJob: config_ids[]: %v", configIDs)
|
||||
|
||||
// Process config IDs
|
||||
var configIDsList []uint
|
||||
for _, configIDStr := range configIDs {
|
||||
configID, err := strconv.ParseUint(configIDStr, 10, 32)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration ID format")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify that the config exists
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, configID).Error; err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration selected")
|
||||
return
|
||||
}
|
||||
// Check if we have an explicit order specified
|
||||
configOrder := c.PostForm("config_order")
|
||||
log.Printf("HandleUpdateJob: config_order: %s", configOrder)
|
||||
|
||||
// Check if the config belongs to the user
|
||||
if config.CreatedBy != userID {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.String(http.StatusForbidden, "You do not have permission to use this configuration")
|
||||
if configOrder != "" {
|
||||
// Parse the ordered list
|
||||
orderStrings := strings.Split(configOrder, ",")
|
||||
log.Printf("HandleUpdateJob: order strings: %v", orderStrings)
|
||||
|
||||
for _, configIDStr := range orderStrings {
|
||||
configID, err := strconv.ParseUint(configIDStr, 10, 32)
|
||||
if err != nil {
|
||||
log.Printf("HandleUpdateJob: Error parsing config ID: %v", err)
|
||||
c.String(http.StatusBadRequest, "Invalid configuration ID format in order")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
configIDsList = append(configIDsList, uint(configID))
|
||||
// Verify that the config exists
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, configID).Error; err != nil {
|
||||
log.Printf("HandleUpdateJob: Invalid config ID: %d, error: %v", configID, err)
|
||||
c.String(http.StatusBadRequest, "Invalid configuration selected")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the config belongs to the user
|
||||
if config.CreatedBy != userID {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.String(http.StatusForbidden, "You do not have permission to use this configuration")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
configIDsList = append(configIDsList, uint(configID))
|
||||
}
|
||||
} else {
|
||||
// Fall back to unordered config IDs
|
||||
log.Printf("HandleUpdateJob: No config_order found, using checkbox order")
|
||||
for _, configIDStr := range configIDs {
|
||||
configID, err := strconv.ParseUint(configIDStr, 10, 32)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration ID format")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify that the config exists
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, configID).Error; err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid configuration selected")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the config belongs to the user
|
||||
if config.CreatedBy != userID {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.String(http.StatusForbidden, "You do not have permission to use this configuration")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
configIDsList = append(configIDsList, uint(configID))
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleUpdateJob: Final configIDsList: %v", configIDsList)
|
||||
|
||||
// Set the first config ID for backward compatibility
|
||||
if len(configIDsList) > 0 {
|
||||
job.ConfigID = configIDsList[0]
|
||||
@@ -332,6 +453,10 @@ func (h *Handlers) HandleUpdateJob(c *gin.Context) {
|
||||
// Set the config IDs list
|
||||
job.SetConfigIDsList(configIDsList)
|
||||
|
||||
// Debug logging
|
||||
log.Printf("HandleUpdateJob: Job after setting ConfigIDsList: %+v", job)
|
||||
log.Printf("HandleUpdateJob: Job.ConfigIDs: %s", job.ConfigIDs)
|
||||
|
||||
// Set the boolean fields - handle both "on" and "true" values for checkboxes
|
||||
enabledVal := c.Request.FormValue("enabled")
|
||||
jobEnabledValue := enabledVal == "on" || enabledVal == "true"
|
||||
@@ -357,10 +482,13 @@ func (h *Handlers) HandleUpdateJob(c *gin.Context) {
|
||||
job.Config = db.TransferConfig{}
|
||||
|
||||
if err := h.DB.UpdateJob(&job); err != nil {
|
||||
log.Printf("HandleUpdateJob: Error updating job: %v", err)
|
||||
c.String(http.StatusInternalServerError, "Failed to update job")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("HandleUpdateJob: Job successfully updated")
|
||||
|
||||
// Reschedule the job with the scheduler
|
||||
if err := h.Scheduler.ScheduleJob(&job); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Job updated but scheduling failed: "+err.Error())
|
||||
|
||||
Reference in New Issue
Block a user