mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
feat: Implement command flag management for rclone configurations
- Added functionality to manage command flags and their values in the configuration form. - Updated the database schema to include a new field for storing command flag values. - Enhanced backend logic to process and store flag values for non-boolean flags during configuration creation and updates. - Improved the user interface to dynamically require destination fields based on selected commands. - Refactored related templates and handlers to support the new command flag management features.
This commit is contained in:
@@ -126,6 +126,7 @@ type TransferConfig struct {
|
||||
// Rclone command fields
|
||||
CommandID uint `gorm:"default:1" form:"command_id"` // Default to 'copy' command ID (1)
|
||||
CommandFlags string `form:"command_flags"` // JSON string of selected flags
|
||||
CommandFlagValues string `form:"command_flag_values"` // JSON string of flag values by ID
|
||||
DeleteAfterTransfer *bool `gorm:"default:false" form:"delete_after_transfer"`
|
||||
SkipProcessedFiles *bool `gorm:"default:true" form:"skip_processed_files"`
|
||||
MaxConcurrentTransfers int `gorm:"default:4" form:"max_concurrent_transfers"` // Number of concurrent file transfers
|
||||
|
||||
@@ -78,6 +78,10 @@ func AddRcloneCommandToConfig() *gormigrate.Migration {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Exec(`ALTER TABLE transfer_configs ADD COLUMN command_flag_values TEXT DEFAULT NULL`).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
@@ -90,6 +94,10 @@ func AddRcloneCommandToConfig() *gormigrate.Migration {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Exec(`ALTER TABLE transfer_configs DROP COLUMN command_flag_values`).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1929,7 +1929,7 @@ func determineCommandType(commandName string) string {
|
||||
return "transfer"
|
||||
}
|
||||
|
||||
// executeSimpleCommand executes rclone commands that don't require file-by-file processing
|
||||
// executeSimpleCommand executes a simple command (non file-by-file transfer)
|
||||
func (s *Scheduler) executeSimpleCommand(cmdName string, cmdType string, job db.Job, config db.TransferConfig, history *db.JobHistory, configPath string) {
|
||||
s.log.LogInfo("Executing simple command '%s' of type '%s' for job %d, config %d", cmdName, cmdType, job.ID, config.ID)
|
||||
|
||||
@@ -2139,3 +2139,75 @@ func (s *Scheduler) executeSimpleCommand(cmdName string, cmdType string, job db.
|
||||
// Send webhook notification
|
||||
s.sendWebhookNotification(&job, history, &config)
|
||||
}
|
||||
|
||||
// prepareBaseArguments prepares the base arguments for a command
|
||||
func (s *Scheduler) prepareBaseArguments(command string, config *db.TransferConfig, progressCallback func(string)) []string {
|
||||
args := []string{command}
|
||||
|
||||
// Add rclone flags from the config
|
||||
if config.CommandFlags != "" {
|
||||
var flagIDs []uint
|
||||
if err := json.Unmarshal([]byte(config.CommandFlags), &flagIDs); err != nil {
|
||||
s.log.LogError("Error parsing command flags: %v", err)
|
||||
} else {
|
||||
// Get all available flags for this command and their values
|
||||
flagsMap, err := s.db.GetRcloneCommandFlagsMap(config.CommandID)
|
||||
if err != nil {
|
||||
s.log.LogError("Error getting flags map: %v", err)
|
||||
} else {
|
||||
// Parse flag values if available
|
||||
var flagValues map[uint]string
|
||||
if config.CommandFlagValues != "" {
|
||||
if err := json.Unmarshal([]byte(config.CommandFlagValues), &flagValues); err != nil {
|
||||
s.log.LogError("Error parsing flag values: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add each selected flag
|
||||
for _, flagID := range flagIDs {
|
||||
if flag, ok := flagsMap[flagID]; ok {
|
||||
if flag.DataType == "bool" {
|
||||
// Boolean flags don't have values
|
||||
args = append(args, flag.Name)
|
||||
} else if flagValues != nil {
|
||||
// Check if we have a value for this flag
|
||||
if value, ok := flagValues[flagID]; ok && value != "" {
|
||||
args = append(args, flag.Name, value)
|
||||
} else {
|
||||
// If there's a default value, use it
|
||||
if flag.DefaultValue != "" {
|
||||
args = append(args, flag.Name, flag.DefaultValue)
|
||||
} else {
|
||||
// Skip flags without values
|
||||
s.log.LogError("Skipping flag %s: no value provided", flag.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add any additional rclone flags specified by the user
|
||||
if config.RcloneFlags != "" {
|
||||
additionalFlags := strings.Fields(config.RcloneFlags)
|
||||
args = append(args, additionalFlags...)
|
||||
}
|
||||
|
||||
// Add common rclone options
|
||||
args = append(args, "--progress")
|
||||
args = append(args, "--stats", "1s")
|
||||
|
||||
// Add config file location
|
||||
configPath := s.db.GetConfigRclonePath(config)
|
||||
args = append(args, "--config", configPath)
|
||||
|
||||
// Add progress callback
|
||||
args = append(args, "--stats-one-line")
|
||||
|
||||
// Set JSON output for easier parsing
|
||||
args = append(args, "--json")
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -156,6 +157,37 @@ func (h *Handlers) HandleCreateConfig(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Process flag values for non-boolean flags
|
||||
flagValues := make(map[uint]string)
|
||||
for key, values := range c.Request.PostForm {
|
||||
// Check if key is a flag value field (format: flag_value_ID)
|
||||
if strings.HasPrefix(key, "flag_value_") {
|
||||
flagIDStr := strings.TrimPrefix(key, "flag_value_")
|
||||
flagID, err := strconv.ParseUint(flagIDStr, 10, 64)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing flag value ID: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Only process if the corresponding enable checkbox is checked
|
||||
enableKey := fmt.Sprintf("flag_enable_%s", flagIDStr)
|
||||
enableValue := c.Request.PostForm.Get(enableKey)
|
||||
if enableValue == "on" && len(values) > 0 && values[0] != "" {
|
||||
flagValues[uint(flagID)] = values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store flag values as JSON if any exist
|
||||
if len(flagValues) > 0 {
|
||||
flagValuesJSON, err := json.Marshal(flagValues)
|
||||
if err != nil {
|
||||
log.Printf("Error marshaling flag values: %v", err)
|
||||
} else {
|
||||
config.CommandFlagValues = string(flagValuesJSON)
|
||||
}
|
||||
}
|
||||
|
||||
// Process builtin auth settings
|
||||
useBuiltinAuthSourceVal := c.Request.FormValue("use_builtin_auth_source")
|
||||
useBuiltinAuthSourceValue := useBuiltinAuthSourceVal == "on" || useBuiltinAuthSourceVal == "true"
|
||||
@@ -343,6 +375,37 @@ func (h *Handlers) HandleUpdateConfig(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Process flag values for non-boolean flags
|
||||
flagValues := make(map[uint]string)
|
||||
for key, values := range c.Request.PostForm {
|
||||
// Check if key is a flag value field (format: flag_value_ID)
|
||||
if strings.HasPrefix(key, "flag_value_") {
|
||||
flagIDStr := strings.TrimPrefix(key, "flag_value_")
|
||||
flagID, err := strconv.ParseUint(flagIDStr, 10, 64)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing flag value ID: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Only process if the corresponding enable checkbox is checked
|
||||
enableKey := fmt.Sprintf("flag_enable_%s", flagIDStr)
|
||||
enableValue := c.Request.PostForm.Get(enableKey)
|
||||
if enableValue == "on" && len(values) > 0 && values[0] != "" {
|
||||
flagValues[uint(flagID)] = values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store flag values as JSON if any exist
|
||||
if len(flagValues) > 0 {
|
||||
flagValuesJSON, err := json.Marshal(flagValues)
|
||||
if err != nil {
|
||||
log.Printf("Error marshaling flag values: %v", err)
|
||||
} else {
|
||||
config.CommandFlagValues = string(flagValuesJSON)
|
||||
}
|
||||
}
|
||||
|
||||
// Process builtin auth settings
|
||||
useBuiltinAuthSourceVal := c.Request.FormValue("use_builtin_auth_source")
|
||||
useBuiltinAuthSourceValue := useBuiltinAuthSourceVal == "on" || useBuiltinAuthSourceVal == "true"
|
||||
|
||||
@@ -675,7 +675,7 @@ func (h *Handlers) HandleRunJob(c *gin.Context) {
|
||||
var job db.Job
|
||||
if err := h.DB.First(&job, id).Error; err != nil {
|
||||
c.Header("Content-Type", "text/html")
|
||||
c.String(http.StatusNotFound, "<script>window.notyfInstance.error('Job not found')</script>")
|
||||
c.String(http.StatusNotFound, "Job not found")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -685,7 +685,7 @@ func (h *Handlers) HandleRunJob(c *gin.Context) {
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.Header("Content-Type", "text/html")
|
||||
c.String(http.StatusForbidden, "<script>window.notyfInstance.error('You do not have permission to run this job')</script>")
|
||||
c.String(http.StatusForbidden, "You do not have permission to run this job")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -720,7 +720,7 @@ func (h *Handlers) HandleRunJob(c *gin.Context) {
|
||||
// Run the job immediately using the scheduler
|
||||
if err := h.Scheduler.RunJobNow(job.ID); err != nil {
|
||||
c.Header("Content-Type", "text/html")
|
||||
errorMsg := fmt.Sprintf("<script>window.notyfInstance.error('Failed to run job: %s')</script>", err.Error())
|
||||
errorMsg := fmt.Sprintf("%s", err.Error())
|
||||
c.String(http.StatusInternalServerError, errorMsg)
|
||||
return
|
||||
}
|
||||
@@ -730,7 +730,7 @@ func (h *Handlers) HandleRunJob(c *gin.Context) {
|
||||
c.Header("Content-Type", "text/html")
|
||||
|
||||
// Return HTML with JavaScript to trigger the notification
|
||||
successScript := fmt.Sprintf("<script>window.notyfInstance.success('Job \"%s\" has been started successfully')</script>", jobName)
|
||||
successScript := fmt.Sprintf("Job \"%s\" has been started successfully", jobName)
|
||||
c.String(http.StatusOK, successScript)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -76,6 +77,11 @@ func (h *RcloneHandler) RcloneCommandFlags(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the flags alphabetically by name
|
||||
sort.Slice(command.Flags, func(i, j int) bool {
|
||||
return command.Flags[i].Name < command.Flags[j].Name
|
||||
})
|
||||
|
||||
_ = common.RcloneCommandFlagsContent(command).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user