diff --git a/README.md b/README.md index 15625bc..901f0a7 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ GoMFT is a web-based managed file transfer application built with Go, leveraging rclone for robust file transfer capabilities. It provides a user-friendly interface for configuring, scheduling, and monitoring file transfers across various storage providers. +

+ + Join our Discord server! + +

+ > [!WARNING] > This application is actively under development. As such, any aspect of the application—including configurations, data structures, and database fields—may change rapidly and without prior notice. Please review all release notes thoroughly before updating. @@ -363,8 +369,13 @@ Log files contain detailed information about file transfers, job execution, and 5. Create jobs using your configurations: - Navigate to "Jobs" section - Select an existing transfer config - - Set up a schedule using cron expressions or run manually - - Enable/disable as needed + - Use the visual schedule builder to set your timing preferences: + - Choose from common presets (hourly, daily, weekly, monthly) + - Customize with specific days, times, or intervals + - See a plain-language description of your schedule + - View upcoming run times on the interactive calendar + - Switch to advanced mode for direct cron expression input if needed + - Enable/disable jobs with a single click 6. Monitor transfers: - View active and completed transfers on the Dashboard @@ -575,9 +586,14 @@ The following fields have been added to the `users` table: - Adaptive processing based on source/destination capabilities 7. **Schedule Options**: - - Cron expressions for flexible scheduling - - Manual execution - - Enable/disable schedules + - **Visual Schedule Builder**: Intuitive interface for setting schedule preferences + - **Natural Language Description**: Plain-language description of schedule + - **Interactive Calendar**: Visual representation of upcoming runs + - **Common Presets**: Hourly, daily, weekly, monthly schedules + - **Advanced Mode**: Manual cron expression input for complex schedules + - **Schedule Validation**: Preview and confirm schedule + - **Enable/Disable**: One-click enable/disable + - **Time Zone Support**: Accurate scheduling based on user's time zone 8. **Notification Options**: - **Email Notifications**: Receive job status updates via email @@ -771,92 +787,4 @@ volumes: - /host/path/backups:/app/backups # For database backups ``` -These paths can be customized using the environment variables `DATA_DIR`, `BACKUP_DIR`, and `LOGS_DIR`. - ---- - -## Security Considerations - -### Running as a Non-Root User - -By default, Docker containers run as the root user, which can pose security risks. GoMFT supports running as a non-root user, which is recommended for production environments. - -#### Benefits of Running as Non-Root - -- **Improved Security**: Limits the potential damage if the container is compromised -- **Better File Permissions**: Files created by the container will match your host user permissions -- **Compliance**: Many security policies and best practices require containers to run as non-root - -#### Methods to Run as Non-Root - -1. **Using PUID/PGID environment variables (recommended)**: - ```bash - # Using current user's ID - docker run -e PUID=$(id -u) -e PGID=$(id -g) starfleetcptn/gomft:latest - - # Or in docker-compose.yml - environment: - - PUID=1000 - - PGID=1000 - ``` - This is the most flexible method as it allows changing the user at runtime without rebuilding the image. - -2. **Using the `--user` flag with Docker run**: - ```bash - docker run --user $(id -u):$(id -g) starfleetcptn/gomft:latest - ``` - -3. **Using Docker Compose with environment variables for `user` directive**: - ```yaml - services: - gomft: - image: starfleetcptn/gomft:latest - user: "${UID:-1000}:${GID:-1000}" - ``` - -4. **Building a custom image with specified UID/GID**: - ```yaml - services: - gomft: - build: - context: . - args: - UID: ${UID:-1000} - GID: ${GID:-1000} - ``` - -#### Environment Variables for User Management - -| Variable | Description | Default | -|----------|-------------|---------| -| `PUID` | User ID to run as | Built-in user ID (1000) | -| `PGID` | Group ID to run as | Built-in group ID (1000) | -| `USERNAME` | Username to use | `gomft` | - -These environment variables allow you to change the user/group IDs at runtime without rebuilding the image. - -#### Volume Permissions - -When mounting volumes, ensure that the directories on the host have appropriate permissions for the container user: - -```bash -# Create directories with correct ownership -mkdir -p data backups -chown -R $(id -u):$(id -g) data backups - -# Or adjust permissions to allow the container user to write -mkdir -p data backups -chmod -R 777 data backups # Less secure, but easier for testing -``` - ---- - -## License - -[MIT License](LICENSE) - see the full license terms - -The GoMFT logo is licensed under the Creative Commons Attribution 4.0 International Public License. - -The gopher design is from https://github.com/egonelbre/gophers. - -The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/). +These paths can be customized using the environment variables ` \ No newline at end of file diff --git a/components/config_form.templ b/components/config_form.templ index 14d2957..547a2c1 100644 --- a/components/config_form.templ +++ b/components/config_form.templ @@ -167,6 +167,9 @@ func getInitialData(config *db.TransferConfig) string { deleteAfterTransfer = config.GetDeleteAfterTransfer() skipProcessedFiles = config.GetSkipProcessedFiles() maxConcurrentTransfers = config.MaxConcurrentTransfers + if maxConcurrentTransfers <= 0 { + maxConcurrentTransfers = 1 // Ensure at least 1 concurrent transfer + } rcloneFlags = config.RcloneFlags commandId = config.CommandID commandFlags = config.CommandFlags @@ -377,6 +380,7 @@ templ ConfigForm(ctx context.Context, data ConfigFormData) {
+ + +
@@ -450,13 +469,16 @@ templ ConfigForm(ctx context.Context, data ConfigFormData) {
- + @@ -703,4 +725,215 @@ templ ConfigForm(ctx context.Context, data ConfigFormData) {
} +} + +templ formvalidation() { + } \ No newline at end of file diff --git a/components/dashboard.templ b/components/dashboard.templ index db5ca3c..9a8b6ac 100644 --- a/components/dashboard.templ +++ b/components/dashboard.templ @@ -16,6 +16,8 @@ type DashboardData struct { FailedTransfers int Configs map[uint]db.TransferConfig RcloneVersion string + LatestVersion string + CurrentVersion string } // GetRcloneVersion executes the rclone --version command and returns the version string @@ -41,6 +43,53 @@ func GetRcloneVersion() string { return "Unknown" } +// isNewerVersionAvailable checks if the latest version is newer than the current version +func isNewerVersionAvailable(current, latest string) bool { + // If either version is empty, we can't do a comparison + if current == "" || latest == "" { + return false + } + + // Strip 'v' prefix if present for comparison + if strings.HasPrefix(current, "v") { + current = current[1:] + } + if strings.HasPrefix(latest, "v") { + latest = latest[1:] + } + + // Split versions into components + currentParts := strings.Split(current, ".") + latestParts := strings.Split(latest, ".") + + // Compare major, minor, patch versions + for i := 0; i < len(currentParts) && i < len(latestParts); i++ { + // Parse to integers + currentNum, err1 := strconv.Atoi(currentParts[i]) + latestNum, err2 := strconv.Atoi(latestParts[i]) + + // If either can't be parsed, do string comparison + if err1 != nil || err2 != nil { + if currentParts[i] < latestParts[i] { + return true + } else if currentParts[i] > latestParts[i] { + return false + } + continue + } + + // Compare numbers + if latestNum > currentNum { + return true + } else if latestNum < currentNum { + return false + } + } + + // If all components are equal but latest has more components, it's newer + return len(latestParts) > len(currentParts) +} + templ Dashboard(ctx context.Context, data DashboardData) { @LayoutWithContext("Dashboard", ctx) {
@@ -253,6 +302,31 @@ templ Dashboard(ctx context.Context, data DashboardData) { }
+
+ Application Version + if data.CurrentVersion != "" && data.LatestVersion != "" && isNewerVersionAvailable(data.CurrentVersion, data.LatestVersion) { +
+ + + { data.CurrentVersion } + + + + { data.LatestVersion } Available + +
+ } else { + + + if data.CurrentVersion != "" { + { data.CurrentVersion } + } else { + Up to date + } + + } +
diff --git a/components/dashboard_notifications.templ b/components/dashboard_notifications.templ index 741069c..22dad7e 100644 --- a/components/dashboard_notifications.templ +++ b/components/dashboard_notifications.templ @@ -177,12 +177,15 @@ templ NotificationDropdown(data NotificationsData) { templ NotificationCount(count int64) { if count > 0 { -
+
if count > 99 { 99+ } else { { fmt.Sprintf("%d", count) } }
+ } else { + + } } \ No newline at end of file diff --git a/components/job_form.templ b/components/job_form.templ index ddc5314..4d2b8cd 100644 --- a/components/job_form.templ +++ b/components/job_form.templ @@ -242,9 +242,734 @@ templ configSearchScript() { } +templ scheduleBuilderScript() { + +} + +templ formValidationScript() { + +} + templ JobForm(ctx context.Context, data JobFormData) { @LayoutWithContext(getJobFormTitle(data.IsNew), ctx) { @configSearchScript() + @scheduleBuilderScript() + @formValidationScript()
@@ -279,6 +1004,16 @@ templ JobForm(ctx context.Context, data JobFormData) { if data.IsNew { + + +

@@ -300,6 +1035,7 @@ templ JobForm(ctx context.Context, data JobFormData) { id="name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Daily Production Backup" + maxlength="100" />

@@ -310,10 +1046,212 @@ templ JobForm(ctx context.Context, data JobFormData) {

-
} else {
+ + +

@@ -447,7 +1397,7 @@ templ JobForm(ctx context.Context, data JobFormData) {
-
diff --git a/components/layout.templ b/components/layout.templ index c318405..eccb25f 100644 --- a/components/layout.templ +++ b/components/layout.templ @@ -151,27 +151,27 @@ templ LayoutWithContext(title string, ctx context.Context) {