From 5eac95818ebb9c420e2d54bb30d749eb1c286295 Mon Sep 17 00:00:00 2001
From: StarFleetCPTN
Date: Mon, 10 Mar 2025 16:05:30 -0700
Subject: [PATCH] feat: Add support for skipping processed files in transfer
configs
- Introduce new `skip_processed_files` option for transfer configurations
- Add database migration to support the new column
- Update config form and UI to include skip processed files toggle
- Enhance scheduler to respect skip processed files setting
- Modify file processing logic to optionally skip previously processed files
- Add environment variable support for skip processed files configuration
---
.gitignore | 4 +
README.md | 114 ++++++++-----
components/config_form.templ | 5 +-
components/providers/common/common.templ | 20 +++
example.config.json | 18 ---
go.mod | 1 +
go.sum | 2 +
internal/config/config.go | 153 ++++++++++++------
internal/db/db.go | 12 ++
.../db/migrations/add_skip_processed_files.go | 21 +++
internal/db/migrations/migrations.go | 5 +-
internal/email/email.go | 18 +--
internal/scheduler/scheduler.go | 66 ++++++--
screenshots/file.metadata.gomft.png | Bin 0 -> 261160 bytes
14 files changed, 305 insertions(+), 134 deletions(-)
delete mode 100644 example.config.json
create mode 100644 internal/db/migrations/add_skip_processed_files.go
create mode 100644 screenshots/file.metadata.gomft.png
diff --git a/.gitignore b/.gitignore
index eb63246..8257975 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,10 @@ components/providers/common/*.go
# Ignore the data directory
data/
+# Ignore .env files
+.env
+*.env
+
# Ignore the tmp directory
tmp/
diff --git a/README.md b/README.md
index b317aef..fb1a042 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,6 @@ GoMFT is a web-based managed file transfer application built with Go, leveraging
- **Multiple Storage Support**: Leverage rclone's extensive support for cloud storage providers:
- Amazon S3
- MinIO
- - Backblaze B2
- NextCloud
- WebDAV
- SFTP
@@ -114,7 +113,7 @@ docker run -d \
#### Docker Compose Example
-For production deployments, you can use Docker Compose:
+For production deployments, you can use Docker Compose with environment variables:
```yaml
version: '3'
@@ -130,6 +129,39 @@ services:
- ./backups:/app/data/gomft/backups
environment:
- TZ=UTC
+ - SERVER_ADDRESS=:8080
+ - DATA_DIR=/app/data/gomft
+ - BACKUP_DIR=/app/data/gomft/backups
+ - JWT_SECRET=change_this_to_a_secure_random_string
+ - BASE_URL=http://localhost:8080
+ - EMAIL_ENABLED=true
+ - EMAIL_HOST=smtp.example.com
+ - EMAIL_PORT=587
+ - EMAIL_FROM_EMAIL=gomft@example.com
+ - EMAIL_FROM_NAME=GoMFT
+ - EMAIL_ENABLE_TLS=true
+ - EMAIL_REQUIRE_AUTH=true
+ - EMAIL_USERNAME=smtp_username
+ - EMAIL_PASSWORD=smtp_password
+```
+
+Alternatively, you can mount your own .env file to the container:
+
+```yaml
+version: '3'
+services:
+ gomft:
+ image: starfleetcptn/gomft:latest
+ container_name: gomft
+ restart: unless-stopped
+ ports:
+ - "8080:8080"
+ volumes:
+ - ./data:/app/data
+ - ./backups:/app/data/gomft/backups
+ - ./.env:/app/.env
+ environment:
+ - TZ=UTC
```
Save this as `docker-compose.yml` and run:
@@ -142,48 +174,46 @@ For more information and available tags, visit the [GoMFT Docker Hub page](https
## Configuration
-GoMFT uses a configuration file located at `./data/gomft/config.json`. On first run, a default configuration will be created:
+GoMFT uses an environment file located at `.env` in the root directory of the application. On first run, a default configuration will be created:
-```json
-{
- "server_address": ":8080",
- "data_dir": "./data/gomft",
- "backup_dir": "./data/gomft/backups",
- "jwt_secret": "your-secret-key",
- "base_url": "http://localhost:8080",
- "email": {
- "enabled": false,
- "host": "smtp.example.com",
- "port": 587,
- "username": "user@example.com",
- "password": "your-password",
- "from_email": "gomft@example.com",
- "from_name": "GoMFT",
- "reply_to": "",
- "enable_tls": true,
- "require_auth": true
- }
-}
+```
+SERVER_ADDRESS=:8080
+DATA_DIR=./data/gomft
+BACKUP_DIR=./data/gomft/backups
+JWT_SECRET=change_this_to_a_secure_random_string
+BASE_URL=http://localhost:8080
+
+# Email configuration
+EMAIL_ENABLED=true
+EMAIL_HOST=smtp.example.com
+EMAIL_PORT=587
+EMAIL_FROM_EMAIL=gomft@example.com
+EMAIL_FROM_NAME=GoMFT
+EMAIL_REPLY_TO=
+EMAIL_ENABLE_TLS=true
+EMAIL_REQUIRE_AUTH=true
+EMAIL_USERNAME=smtp_username
+EMAIL_PASSWORD=smtp_password
```
### Configuration Options
-- `server_address`: The address and port to run the server on
-- `data_dir`: Directory for storing application data
-- `backup_dir`: Directory for storing database backups
-- `jwt_secret`: Secret key for JWT token generation
-- `base_url`: Base URL for generating links in emails (e.g., password reset links)
-- `email`: Email configuration settings for system notifications and password resets
- - `enabled`: Set to `true` to enable email functionality
- - `host`: SMTP server hostname
- - `port`: SMTP server port (usually 587 for TLS, 465 for SSL, or 25 for non-secure)
- - `username`: Username for SMTP authentication
- - `password`: Password for SMTP authentication
- - `from_email`: Email address used as sender
- - `from_name`: Name displayed as the sender
- - `reply_to`: Optional reply-to email address
- - `enable_tls`: Set to `true` to use TLS for secure email transmission
- - `require_auth`: Set to `true` to require authentication for SMTP connections, or `false` for servers that don't need authentication
+- `SERVER_ADDRESS`: The address and port to run the server on
+- `DATA_DIR`: Directory for storing application data
+- `BACKUP_DIR`: Directory for storing database backups
+- `JWT_SECRET`: Secret key for JWT token generation
+- `BASE_URL`: Base URL for generating links in emails (e.g., password reset links)
+- Email configuration settings for system notifications and password resets:
+ - `EMAIL_ENABLED`: Set to `true` to enable email functionality
+ - `EMAIL_HOST`: SMTP server hostname
+ - `EMAIL_PORT`: SMTP server port (usually 587 for TLS, 465 for SSL, or 25 for non-secure)
+ - `EMAIL_USERNAME`: Username for SMTP authentication
+ - `EMAIL_PASSWORD`: Password for SMTP authentication
+ - `EMAIL_FROM_EMAIL`: Email address used as sender
+ - `EMAIL_FROM_NAME`: Name displayed as the sender
+ - `EMAIL_REPLY_TO`: Optional reply-to email address
+ - `EMAIL_ENABLE_TLS`: Set to `true` to use TLS for secure email transmission
+ - `EMAIL_REQUIRE_AUTH`: Set to `true` to require authentication for SMTP connections, or `false` for servers that don't need authentication
## Usage
@@ -284,9 +314,9 @@ GoMFT supports email notifications for various features:
To configure email functionality:
-1. Edit the `config.json` file and provide your SMTP server details
-2. Set `"enabled": true` in the email configuration section
-3. Ensure the `base_url` setting is configured correctly for your deployment
+1. Edit the `.env` file and provide your SMTP server details
+2. Set `EMAIL_ENABLED=true` in the email configuration section
+3. Ensure the `BASE_URL` setting is configured correctly for your deployment
## Development
diff --git a/components/config_form.templ b/components/config_form.templ
index 72b90a3..5d28504 100644
--- a/components/config_form.templ
+++ b/components/config_form.templ
@@ -72,6 +72,7 @@ func getInitialData(config *db.TransferConfig) string {
archivePath := ""
archiveEnabled := false
deleteAfterTransfer := false
+ skipProcessedFiles := true
rcloneFlags := ""
// If editing an existing config, populate with those values
@@ -129,6 +130,7 @@ func getInitialData(config *db.TransferConfig) string {
archivePath = config.ArchivePath
archiveEnabled = config.ArchiveEnabled
deleteAfterTransfer = config.DeleteAfterTransfer
+ skipProcessedFiles = config.SkipProcessedFiles
rcloneFlags = config.RcloneFlags
}
@@ -183,6 +185,7 @@ func getInitialData(config *db.TransferConfig) string {
archivePath: '%s',
archiveEnabled: %v,
deleteAfterTransfer: %v,
+ skipProcessedFiles: %v,
rcloneFlags: '%s',
}`,
name, sourceType, sourcePath, sourceHost, sourcePort, sourceUser, sourcePassword, sourceKeyFile, sourceAuthType,
@@ -192,7 +195,7 @@ func getInitialData(config *db.TransferConfig) string {
destinationType, destinationPath, destHost, destPort, destUser, destPassword, destKeyFile, destAuthType,
destBucket, destRegion, destAccessKey, destSecretKey, destEndpoint, destShare, destDomain, destPassiveMode,
destClientId, destClientSecret, destDriveId, destTeamDrive,
- archivePath, archiveEnabled, deleteAfterTransfer, rcloneFlags)
+ archivePath, archiveEnabled, deleteAfterTransfer, skipProcessedFiles, rcloneFlags)
}
templ ConfigForm(ctx context.Context, data ConfigFormData) {
diff --git a/components/providers/common/common.templ b/components/providers/common/common.templ
index f5ddf86..5e51803 100644
--- a/components/providers/common/common.templ
+++ b/components/providers/common/common.templ
@@ -121,6 +121,26 @@ templ ArchiveOptions() {
Warning: This will permanently delete the original files
+
+
+
+
+ Files with the same hash that have been successfully processed before will be skipped
+
+
}
diff --git a/example.config.json b/example.config.json
deleted file mode 100644
index fa032f3..0000000
--- a/example.config.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "server_address": ":8080",
- "data_dir": "/app/data/gomft",
- "backup_dir": "/app/data/gomft/backups",
- "jwt_secret": "change_this_to_a_secure_random_string",
- "email": {
- "enabled": true,
- "host": "smtp.example.com",
- "port": 587,
- "from_email": "gomft@example.com",
- "from_name": "GoMFT",
- "reply_to": "",
- "enable_tls": true,
- "require_auth": true,
- "username": "smtp_username",
- "password": "smtp_password"
- }
-}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 1d0621e..8335dce 100644
--- a/go.mod
+++ b/go.mod
@@ -28,6 +28,7 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
+ github.com/joho/godotenv v1.5.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
diff --git a/go.sum b/go.sum
index 74630b2..8a7f379 100644
--- a/go.sum
+++ b/go.sum
@@ -48,6 +48,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
diff --git a/internal/config/config.go b/internal/config/config.go
index 245c9b9..5361abe 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -1,31 +1,34 @@
package config
import (
- "encoding/json"
"os"
"path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/joho/godotenv"
)
type Config struct {
- ServerAddress string `json:"server_address"`
- DataDir string `json:"data_dir"`
- BackupDir string `json:"backup_dir"`
- JWTSecret string `json:"jwt_secret"`
+ ServerAddress string `json:"server_address"`
+ DataDir string `json:"data_dir"`
+ BackupDir string `json:"backup_dir"`
+ JWTSecret string `json:"jwt_secret"`
Email EmailConfig `json:"email"`
- BaseURL string `json:"base_url"` // Base URL for generating links in emails
+ BaseURL string `json:"base_url"` // Base URL for generating links in emails
}
type EmailConfig struct {
- Enabled bool `json:"enabled"`
- Host string `json:"host"`
- Port int `json:"port"`
- Username string `json:"username"`
- Password string `json:"password"`
- FromEmail string `json:"from_email"`
- FromName string `json:"from_name"`
- ReplyTo string `json:"reply_to,omitempty"`
- EnableTLS bool `json:"enable_tls"`
- RequireAuth bool `json:"require_auth"`
+ Enabled bool `json:"enabled"`
+ Host string `json:"host"`
+ Port int `json:"port"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+ FromEmail string `json:"from_email"`
+ FromName string `json:"from_name"`
+ ReplyTo string `json:"reply_to,omitempty"`
+ EnableTLS bool `json:"enable_tls"`
+ RequireAuth bool `json:"require_auth"`
}
func Load() (*Config, error) {
@@ -37,48 +40,106 @@ func Load() (*Config, error) {
JWTSecret: "change_this_to_a_secure_random_string",
BaseURL: "http://localhost:8080",
Email: EmailConfig{
- Enabled: false,
- Host: "smtp.example.com",
- Port: 587,
- Username: "user@example.com",
- Password: "your-password",
- FromEmail: "gomft@example.com",
- FromName: "GoMFT",
- EnableTLS: true,
+ Enabled: false,
+ Host: "smtp.example.com",
+ Port: 587,
+ Username: "user@example.com",
+ Password: "your-password",
+ FromEmail: "gomft@example.com",
+ FromName: "GoMFT",
+ EnableTLS: true,
RequireAuth: true,
},
}
- // Check if config file exists
- configPath := filepath.Join(cfg.DataDir, "config.json")
- if _, err := os.Stat(configPath); err == nil {
- // Read configuration file
- data, err := os.ReadFile(configPath)
- if err != nil {
- return nil, err
- }
-
- // Parse configuration
- if err := json.Unmarshal(data, cfg); err != nil {
- return nil, err
- }
- } else if !os.IsNotExist(err) {
- return nil, err
- }
-
// Ensure data directory exists
if err := os.MkdirAll(cfg.DataDir, 0755); err != nil {
return nil, err
}
- // Save configuration if it doesn't exist
- if _, err := os.Stat(configPath); os.IsNotExist(err) {
- data, err := json.MarshalIndent(cfg, "", " ")
- if err != nil {
+ // First try to load .env from the root directory
+ envPath := ".env"
+ if _, err := os.Stat(envPath); err == nil {
+ // Load .env file
+ if err := godotenv.Load(envPath); err != nil {
return nil, err
}
- if err := os.WriteFile(configPath, data, 0644); err != nil {
+ // Override configuration with environment variables
+ if serverAddr := os.Getenv("SERVER_ADDRESS"); serverAddr != "" {
+ cfg.ServerAddress = serverAddr
+ }
+ if dataDir := os.Getenv("DATA_DIR"); dataDir != "" {
+ cfg.DataDir = dataDir
+ }
+ if backupDir := os.Getenv("BACKUP_DIR"); backupDir != "" {
+ cfg.BackupDir = backupDir
+ }
+ if jwtSecret := os.Getenv("JWT_SECRET"); jwtSecret != "" {
+ cfg.JWTSecret = jwtSecret
+ }
+ if baseURL := os.Getenv("BASE_URL"); baseURL != "" {
+ cfg.BaseURL = baseURL
+ }
+
+ // Email configuration
+ if emailEnabled := os.Getenv("EMAIL_ENABLED"); emailEnabled != "" {
+ cfg.Email.Enabled = strings.ToLower(emailEnabled) == "true"
+ }
+ if emailHost := os.Getenv("EMAIL_HOST"); emailHost != "" {
+ cfg.Email.Host = emailHost
+ }
+ if emailPort := os.Getenv("EMAIL_PORT"); emailPort != "" {
+ if port, err := strconv.Atoi(emailPort); err == nil {
+ cfg.Email.Port = port
+ }
+ }
+ if emailUsername := os.Getenv("EMAIL_USERNAME"); emailUsername != "" {
+ cfg.Email.Username = emailUsername
+ }
+ if emailPassword := os.Getenv("EMAIL_PASSWORD"); emailPassword != "" {
+ cfg.Email.Password = emailPassword
+ }
+ if emailFromEmail := os.Getenv("EMAIL_FROM_EMAIL"); emailFromEmail != "" {
+ cfg.Email.FromEmail = emailFromEmail
+ }
+ if emailFromName := os.Getenv("EMAIL_FROM_NAME"); emailFromName != "" {
+ cfg.Email.FromName = emailFromName
+ }
+ if emailReplyTo := os.Getenv("EMAIL_REPLY_TO"); emailReplyTo != "" {
+ cfg.Email.ReplyTo = emailReplyTo
+ }
+ if emailEnableTLS := os.Getenv("EMAIL_ENABLE_TLS"); emailEnableTLS != "" {
+ cfg.Email.EnableTLS = strings.ToLower(emailEnableTLS) == "true"
+ }
+ if emailRequireAuth := os.Getenv("EMAIL_REQUIRE_AUTH"); emailRequireAuth != "" {
+ cfg.Email.RequireAuth = strings.ToLower(emailRequireAuth) == "true"
+ }
+ } else if !os.IsNotExist(err) {
+ return nil, err
+ } else {
+ // Create default .env file in root directory if it doesn't exist
+ envContent := []string{
+ "SERVER_ADDRESS=" + cfg.ServerAddress,
+ "DATA_DIR=" + cfg.DataDir,
+ "BACKUP_DIR=" + cfg.BackupDir,
+ "JWT_SECRET=" + cfg.JWTSecret,
+ "BASE_URL=" + cfg.BaseURL,
+ "",
+ "# Email configuration",
+ "EMAIL_ENABLED=" + strconv.FormatBool(cfg.Email.Enabled),
+ "EMAIL_HOST=" + cfg.Email.Host,
+ "EMAIL_PORT=" + strconv.Itoa(cfg.Email.Port),
+ "EMAIL_FROM_EMAIL=" + cfg.Email.FromEmail,
+ "EMAIL_FROM_NAME=" + cfg.Email.FromName,
+ "EMAIL_REPLY_TO=" + cfg.Email.ReplyTo,
+ "EMAIL_ENABLE_TLS=" + strconv.FormatBool(cfg.Email.EnableTLS),
+ "EMAIL_REQUIRE_AUTH=" + strconv.FormatBool(cfg.Email.RequireAuth),
+ "EMAIL_USERNAME=" + cfg.Email.Username,
+ "EMAIL_PASSWORD=" + cfg.Email.Password,
+ }
+
+ if err := os.WriteFile(envPath, []byte(strings.Join(envContent, "\n")), 0644); err != nil {
return nil, err
}
}
diff --git a/internal/db/db.go b/internal/db/db.go
index 7a6a12d..c157835 100644
--- a/internal/db/db.go
+++ b/internal/db/db.go
@@ -102,6 +102,7 @@ type TransferConfig struct {
ArchiveEnabled bool `gorm:"default:false" form:"archive_enabled"`
RcloneFlags string `form:"rclone_flags"`
DeleteAfterTransfer bool `gorm:"default:false" form:"delete_after_transfer"`
+ SkipProcessedFiles bool `gorm:"default:true" form:"skip_processed_files"`
CreatedBy uint
User User `gorm:"foreignkey:CreatedBy"`
CreatedAt time.Time
@@ -378,10 +379,21 @@ func (db *DB) DeleteFileMetadata(id uint) error {
return db.Delete(&FileMetadata{}, id).Error
}
+// GetConfigRclonePath returns the path to the rclone config file for a given transfer config
func (db *DB) GetConfigRclonePath(config *TransferConfig) string {
return filepath.Join("configs", fmt.Sprintf("config_%d.conf", config.ID))
}
+// GetSkipProcessedFilesValue gets the current value of SkipProcessedFiles for a config
+func (db *DB) GetSkipProcessedFilesValue(configID uint) (bool, error) {
+ var value bool
+ err := db.Model(&TransferConfig{}).
+ Where("id = ?", configID).
+ Select("skip_processed_files").
+ Scan(&value).Error
+ return value, err
+}
+
func (db *DB) GenerateRcloneConfig(config *TransferConfig) error {
configPath := db.GetConfigRclonePath(config)
diff --git a/internal/db/migrations/add_skip_processed_files.go b/internal/db/migrations/add_skip_processed_files.go
new file mode 100644
index 0000000..f6939b1
--- /dev/null
+++ b/internal/db/migrations/add_skip_processed_files.go
@@ -0,0 +1,21 @@
+package migrations
+
+import (
+ "github.com/go-gormigrate/gormigrate/v2"
+ "gorm.io/gorm"
+)
+
+// AddSkipProcessedFilesColumn adds the skip_processed_files column to transfer_configs table
+func AddSkipProcessedFilesColumn() *gormigrate.Migration {
+ return &gormigrate.Migration{
+ ID: "20250310_add_skip_processed_files",
+ Migrate: func(tx *gorm.DB) error {
+ // Add skip_processed_files column with default value of true
+ return tx.Exec("ALTER TABLE transfer_configs ADD COLUMN skip_processed_files BOOLEAN DEFAULT true").Error
+ },
+ Rollback: func(tx *gorm.DB) error {
+ // Drop the column if needed
+ return tx.Exec("ALTER TABLE transfer_configs DROP COLUMN skip_processed_files").Error
+ },
+ }
+}
diff --git a/internal/db/migrations/migrations.go b/internal/db/migrations/migrations.go
index c7705a7..49e99a9 100644
--- a/internal/db/migrations/migrations.go
+++ b/internal/db/migrations/migrations.go
@@ -11,7 +11,8 @@ func InitMigrations(db *gorm.DB) *gormigrate.Gormigrate {
// ... existing migrations
AddDeleteAfterTransferColumn(),
AddCloudStorageFields(),
+ AddSkipProcessedFilesColumn(),
}
-
+
return gormigrate.New(db, gormigrate.DefaultOptions, migrations)
-}
\ No newline at end of file
+}
diff --git a/internal/email/email.go b/internal/email/email.go
index bcc969d..7d7b9da 100644
--- a/internal/email/email.go
+++ b/internal/email/email.go
@@ -26,12 +26,12 @@ func NewService(cfg *config.Config) *Service {
func (s *Service) SendPasswordResetEmail(toEmail, username, resetToken string) error {
if !s.Config.Email.Enabled {
// If email is not enabled, just log it (you can redirect to the default logging logic)
- return fmt.Errorf("email service is disabled, reset link would be: %s/reset-password?token=%s",
+ return fmt.Errorf("email service is disabled, reset link would be: %s/reset-password?token=%s",
s.Config.BaseURL, resetToken)
}
resetLink := fmt.Sprintf("%s/reset-password?token=%s", s.Config.BaseURL, resetToken)
-
+
// Create email data for template
data := map[string]interface{}{
"Username": username,
@@ -124,7 +124,7 @@ func (s *Service) generatePasswordResetEmailHTML(data map[string]interface{}) (s
text-align: center;
}
.btn:hover {
- background-color: #4338ca;
+ background-color:rgb(55, 113, 236);
}
.reset-link {
margin: 20px 0;
@@ -210,7 +210,7 @@ func (s *Service) sendEmail(toEmail, subject, htmlContent string) error {
headers["Subject"] = subject
headers["MIME-Version"] = "1.0"
headers["Content-Type"] = "text/html; charset=UTF-8"
-
+
if s.Config.Email.ReplyTo != "" {
headers["Reply-To"] = s.Config.Email.ReplyTo
}
@@ -224,7 +224,7 @@ func (s *Service) sendEmail(toEmail, subject, htmlContent string) error {
// Set up the SMTP server address
addr := fmt.Sprintf("%s:%d", s.Config.Email.Host, s.Config.Email.Port)
-
+
// Check if authentication is required
if s.Config.Email.RequireAuth {
// Use authenticated SMTP
@@ -237,7 +237,7 @@ func (s *Service) sendEmail(toEmail, subject, htmlContent string) error {
return fmt.Errorf("failed to connect to SMTP server: %v", err)
}
defer client.Close()
-
+
// Set up TLS if enabled
if s.Config.Email.EnableTLS {
if err := client.StartTLS(nil); err != nil {
@@ -252,7 +252,7 @@ func (s *Service) sendEmail(toEmail, subject, htmlContent string) error {
if err := client.Rcpt(toEmail); err != nil {
return fmt.Errorf("failed to set recipient: %v", err)
}
-
+
// Send the email body
w, err := client.Data()
if err != nil {
@@ -266,7 +266,7 @@ func (s *Service) sendEmail(toEmail, subject, htmlContent string) error {
if err != nil {
return fmt.Errorf("failed to close data writer: %v", err)
}
-
+
return client.Quit()
}
-}
\ No newline at end of file
+}
diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go
index 401221f..2604ebe 100644
--- a/internal/scheduler/scheduler.go
+++ b/internal/scheduler/scheduler.go
@@ -128,12 +128,23 @@ func (s *Scheduler) executeJob(jobID uint) {
return
}
- fmt.Printf("Loaded job %d with config: source=%s:%s, dest=%s:%s\n",
+ // Add explicit database reload of the config to ensure we have the latest values
+ var config db.TransferConfig
+ if err := s.db.First(&config, job.Config.ID).Error; err != nil {
+ fmt.Printf("Error loading config %d: %v\n", job.Config.ID, err)
+ return
+ }
+ // Replace the job's config with the freshly loaded one
+ job.Config = config
+
+ // Now the rest of your code will use the correct value
+ fmt.Printf("Loaded job %d with config: source=%s:%s, dest=%s:%s, skipProcessedFiles=%v\n",
jobID,
job.Config.SourceType,
job.Config.SourcePath,
job.Config.DestinationType,
job.Config.DestinationPath,
+ job.Config.SkipProcessedFiles,
)
// Create job history entry
@@ -157,6 +168,12 @@ func (s *Scheduler) executeJob(jobID uint) {
fmt.Printf("Error updating job last run time for job %d: %v\n", jobID, err)
}
+ // Reload the job from the database to get the latest values
+ if err := s.db.Preload("Config").First(&job, jobID).Error; err != nil {
+ fmt.Printf("Error reloading job %d: %v\n", jobID, err)
+ return
+ }
+
// Track files already processed in this job execution to prevent duplicates
processedFiles := make(map[string]bool)
@@ -317,6 +334,8 @@ func (s *Scheduler) executeJob(jobID uint) {
}
}
+ skipFiles := job.Config.SkipProcessedFiles
+
// Check if this file has been processed before (by hash)
if fileHash != "" {
processed, prevMetadata, _ := s.hasFileBeenProcessed(jobID, fileHash)
@@ -324,13 +343,22 @@ func (s *Scheduler) executeJob(jobID uint) {
fmt.Printf("File %s has been processed before (hash: %s, previous file: %s)\n",
fileName, fileHash, prevMetadata.FileName)
- // Skip previously processed files with the same hash if they were processed successfully
- if prevMetadata.Status == "processed" ||
- prevMetadata.Status == "archived" ||
- prevMetadata.Status == "deleted" ||
- prevMetadata.Status == "archived_and_deleted" {
+ // Determine if we should skip this file
+ shouldSkip := false
+ if skipFiles {
+ if prevMetadata.Status == "processed" ||
+ prevMetadata.Status == "archived" ||
+ prevMetadata.Status == "deleted" ||
+ prevMetadata.Status == "archived_and_deleted" {
+ shouldSkip = true
+ }
+ }
+
+ if shouldSkip {
fmt.Printf("Skipping unchanged file %s (hash matches previous processing)\n", fileName)
continue
+ } else {
+ fmt.Printf("Re-processing file %s despite previous processing (skipProcessedFiles=%v)\n", fileName, skipFiles)
}
}
}
@@ -341,18 +369,24 @@ func (s *Scheduler) executeJob(jobID uint) {
fmt.Printf("File %s was previously processed on %s with status: %s\n",
fileName, prevMetadata.ProcessedTime.Format(time.RFC3339), prevMetadata.Status)
- // If the file was previously processed successfully and the hash hasn't changed,
- // we could skip processing
- if prevMetadata.Status == "processed" ||
- prevMetadata.Status == "archived" ||
- prevMetadata.Status == "deleted" ||
- prevMetadata.Status == "archived_and_deleted" {
- if fileHash != "" && fileHash == prevMetadata.FileHash {
- fmt.Printf("Skipping unchanged file %s (hash matches previous processing)\n", fileName)
- // Skip this file and continue to the next one
- continue
+ // Determine if we should skip this file based on name+hash match
+ shouldSkip := false
+ if skipFiles && fileHash != "" && fileHash == prevMetadata.FileHash {
+ if prevMetadata.Status == "processed" ||
+ prevMetadata.Status == "archived" ||
+ prevMetadata.Status == "deleted" ||
+ prevMetadata.Status == "archived_and_deleted" {
+ shouldSkip = true
}
}
+
+ if shouldSkip {
+ fmt.Printf("Skipping unchanged file %s (hash matches previous processing)\n", fileName)
+ // Skip this file and continue to the next one
+ continue
+ } else if fileHash != "" && fileHash == prevMetadata.FileHash {
+ fmt.Printf("Re-processing file %s despite matching hash (skipProcessedFiles=%v)\n", fileName, skipFiles)
+ }
}
// Prepare moveto command for transfer
diff --git a/screenshots/file.metadata.gomft.png b/screenshots/file.metadata.gomft.png
new file mode 100644
index 0000000000000000000000000000000000000000..129092d6c12893c09617a0d179fb5602ead72726
GIT binary patch
literal 261160
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