mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-10-27 07:20:27 +01:00
Add delete after transfer option and enhanced job/config deletion UX
This commit is contained in:
1 parent
f4c8c2121c
commit
81557840dd
11 files changed
+659
-23
No files matched your search
@@ -91,6 +91,7 @@ type TransferConfig struct {
|
||||
ArchivePath string `form:"archive_path"`
|
||||
ArchiveEnabled bool `gorm:"default:false" form:"archive_enabled"`
|
||||
RcloneFlags string `form:"rclone_flags"`
|
||||
DeleteAfterTransfer bool `gorm:"default:false" form:"delete_after_transfer"`
|
||||
CreatedBy uint
|
||||
User User `gorm:"foreignkey:CreatedBy"`
|
||||
CreatedAt time.Time
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AddDeleteAfterTransferColumn adds the delete_after_transfer column to transfer_configs table
|
||||
func AddDeleteAfterTransferColumn() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "add_delete_after_transfer_column",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Exec("ALTER TABLE transfer_configs ADD COLUMN delete_after_transfer BOOLEAN NOT NULL DEFAULT false").Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("ALTER TABLE transfer_configs DROP COLUMN delete_after_transfer").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// InitMigrations initializes the migrations
|
||||
func InitMigrations() *gormigrate.Gormigrate {
|
||||
migrations := []*gormigrate.Migration{
|
||||
// ... existing migrations
|
||||
AddDeleteAfterTransferColumn(),
|
||||
}
|
||||
|
||||
return gormigrate.New(db, gormigrate.DefaultOptions, migrations)
|
||||
}
|
||||
@@ -161,7 +161,7 @@ func (s *Scheduler) executeJob(jobID uint) {
|
||||
"--config", configPath,
|
||||
"size",
|
||||
"--include", job.Config.FilePattern,
|
||||
job.Config.SourcePath,
|
||||
fmt.Sprintf("source_%d:%s", job.Config.ID, job.Config.SourcePath),
|
||||
}
|
||||
// Get the rclone path from the environment variable or use the default path
|
||||
rclonePath := os.Getenv("RCLONE_PATH")
|
||||
@@ -169,9 +169,16 @@ func (s *Scheduler) executeJob(jobID uint) {
|
||||
rclonePath = "rclone"
|
||||
}
|
||||
output, err := exec.Command(rclonePath, sizeArgs...).CombinedOutput()
|
||||
fmt.Printf("Running rclone size: %v\nOutput: %s\n", sizeArgs, output)
|
||||
fmt.Printf("Running rclone size: %s %s\nOutput: %s\n", rclonePath, strings.Join(sizeArgs, " "), output)
|
||||
if err != nil {
|
||||
fmt.Printf("Error running rclone size: %v\nOutput: %s\n", err, output)
|
||||
// Update job history with error
|
||||
history.Status = "failed"
|
||||
history.ErrorMessage = fmt.Sprintf("Size calculation error: %v\nOutput: %s", err, string(output))
|
||||
history.EndTime = &startTime // Use start time as end time for a quick failure
|
||||
if err := s.db.UpdateJobHistory(history); err != nil {
|
||||
fmt.Printf("Error updating job history for job %d: %v\n", jobID, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -232,7 +239,7 @@ func (s *Scheduler) executeJob(jobID uint) {
|
||||
// Prepare moveto command for transfer
|
||||
transferArgs := []string{
|
||||
"--config", configPath,
|
||||
"moveto",
|
||||
"copyto",
|
||||
"--progress",
|
||||
"--stats-one-line",
|
||||
"--verbose",
|
||||
@@ -317,6 +324,21 @@ func (s *Scheduler) executeJob(jobID uint) {
|
||||
fmt.Sprintf("Archive error for file %s: %v", file, archiveErr))
|
||||
}
|
||||
}
|
||||
if job.Config.DeleteAfterTransfer {
|
||||
fmt.Printf("Deleting file %s for job %d\n", file, jobID)
|
||||
deleteArgs := []string{
|
||||
"--config", configPath,
|
||||
"deletefile",
|
||||
sourcePath, }
|
||||
deleteCmd := exec.Command(rclonePath, deleteArgs...)
|
||||
deleteOutput, deleteErr := deleteCmd.CombinedOutput()
|
||||
fmt.Printf("Output for file %s: %s\n", file, string(deleteOutput))
|
||||
if deleteErr != nil {
|
||||
fmt.Printf("Error deleting file %s for job %d: %v\n", file, jobID, deleteErr)
|
||||
transferErrors = append(transferErrors,
|
||||
fmt.Sprintf("Delete error for file %s: %v", file, deleteErr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -248,8 +248,48 @@ func (h *Handlers) HandleAPIDeleteJob(c *gin.Context) {
|
||||
|
||||
// HandleAPIRunJob handles the API run job request
|
||||
func (h *Handlers) HandleAPIRunJob(c *gin.Context) {
|
||||
// Implementation will be moved from the old handlers.go
|
||||
c.JSON(http.StatusOK, gin.H{"message": "API run job handler stub"})
|
||||
id := c.Param("id")
|
||||
userID := c.GetUint("userID")
|
||||
|
||||
var job db.Job
|
||||
if err := h.DB.First(&job, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Job not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if user owns this job
|
||||
if job.CreatedBy != userID {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "You do not have permission to run this job"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Determine job name for response
|
||||
jobName := job.Name
|
||||
if jobName == "" {
|
||||
// If job name is empty, try to get config name
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, job.ConfigID).Error; err == nil {
|
||||
jobName = config.Name
|
||||
} else {
|
||||
jobName = fmt.Sprintf("Job #%d", job.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the job immediately using the scheduler
|
||||
if err := h.Scheduler.RunJobNow(job.ID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to run job: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Job started successfully",
|
||||
"jobId": job.ID,
|
||||
"jobName": jobName,
|
||||
})
|
||||
}
|
||||
|
||||
// HandleAPIHistory handles the API history request
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -274,7 +275,8 @@ func (h *Handlers) HandleRunJob(c *gin.Context) {
|
||||
|
||||
var job db.Job
|
||||
if err := h.DB.First(&job, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Job not found"})
|
||||
c.Header("Content-Type", "text/html")
|
||||
c.String(http.StatusNotFound, "<script>window.notyfInstance.error('Job not found')</script>")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -283,19 +285,37 @@ func (h *Handlers) HandleRunJob(c *gin.Context) {
|
||||
// Check if user is admin
|
||||
isAdmin, exists := c.Get("isAdmin")
|
||||
if !exists || isAdmin != true {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "You do not have permission to run this job"})
|
||||
c.Header("Content-Type", "text/html")
|
||||
c.String(http.StatusForbidden, "<script>window.notyfInstance.error('You do not have permission to run this job')</script>")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Determine job name for response
|
||||
jobName := job.Name
|
||||
if jobName == "" {
|
||||
// If job name is empty, try to get config name
|
||||
var config db.TransferConfig
|
||||
if err := h.DB.First(&config, job.ConfigID).Error; err == nil {
|
||||
jobName = config.Name
|
||||
} else {
|
||||
jobName = fmt.Sprintf("Job #%d", job.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the job immediately using the scheduler
|
||||
if err := h.Scheduler.RunJobNow(job.ID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to run job: " + err.Error()})
|
||||
c.Header("Content-Type", "text/html")
|
||||
errorMsg := fmt.Sprintf("<script>window.notyfInstance.error('Failed to run job: %s')</script>", err.Error())
|
||||
c.String(http.StatusInternalServerError, errorMsg)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Job started successfully",
|
||||
"jobId": job.ID,
|
||||
})
|
||||
// Set custom header with job name for HTMX to use in the toast notification
|
||||
c.Header("HX-Job-Name", jobName)
|
||||
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)
|
||||
c.String(http.StatusOK, successScript)
|
||||
}
|
||||
Reference in new issue
Block a user