diff --git a/.gitignore b/.gitignore index b6f91ad..eb63246 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,15 @@ Thumbs.db components/*.go !components/components.go +components/providers/source/*.go +!components/providers/source/source.go + +components/providers/destination/*.go +!components/providers/destination/destination.go + +components/providers/common/*.go +!components/providers/common/common.go + # Ignore the data directory data/ @@ -56,9 +65,10 @@ tmp/ configs/ # Ignore Dirs -source/ -destination/ -archive/ +/source/ +/destination/ + +/archive/ # Ignore binaries gomft diff --git a/components/config_form.templ b/components/config_form.templ index a38525e..4eb9632 100644 --- a/components/config_form.templ +++ b/components/config_form.templ @@ -4,6 +4,9 @@ import ( "fmt" "github.com/starfleetcptn/gomft/internal/db" "context" + "github.com/starfleetcptn/gomft/components/providers/source" + "github.com/starfleetcptn/gomft/components/providers/destination" + "github.com/starfleetcptn/gomft/components/providers/common" ) type ConfigFormData struct { @@ -19,6 +22,7 @@ func getConfigFormTitle(isNew bool) string { } func getInitialData(config *db.TransferConfig) string { + // Default values for a new configuration name := "" sourceType := "local" sourcePath := "" @@ -27,24 +31,23 @@ func getInitialData(config *db.TransferConfig) string { sourceUser := "" sourcePassword := "" sourceKeyFile := "" - // S3 fields + sourceAuthType := "password" sourceBucket := "" sourceRegion := "" sourceAccessKey := "" sourceSecretKey := "" sourceEndpoint := "" - // SMB fields sourceShare := "" sourceDomain := "" - // FTP fields - sourcePassiveMode := true - // OneDrive and Google Drive fields + sourcePassiveMode := false sourceClientId := "" sourceClientSecret := "" sourceDriveId := "" sourceTeamDrive := "" - filePattern := "*" - outputPattern := "" + + filePattern := "" + outputPattern := "${filename}" + destinationType := "local" destinationPath := "" destHost := "" @@ -52,27 +55,26 @@ func getInitialData(config *db.TransferConfig) string { destUser := "" destPassword := "" destKeyFile := "" - // S3 fields + destAuthType := "password" destBucket := "" destRegion := "" destAccessKey := "" destSecretKey := "" destEndpoint := "" - // SMB fields destShare := "" destDomain := "" - // FTP fields - destPassiveMode := true - // OneDrive and Google Drive fields + destPassiveMode := false destClientId := "" destClientSecret := "" destDriveId := "" destTeamDrive := "" + archivePath := "" archiveEnabled := false deleteAfterTransfer := false rcloneFlags := "" + // If editing an existing config, populate with those values if config != nil { name = config.Name sourceType = config.SourceType @@ -82,24 +84,25 @@ func getInitialData(config *db.TransferConfig) string { sourceUser = config.SourceUser sourcePassword = config.SourcePassword sourceKeyFile = config.SourceKeyFile - // S3 fields + if sourceKeyFile != "" && sourcePassword == "" { + sourceAuthType = "key" + } sourceBucket = config.SourceBucket sourceRegion = config.SourceRegion sourceAccessKey = config.SourceAccessKey sourceSecretKey = config.SourceSecretKey sourceEndpoint = config.SourceEndpoint - // SMB fields sourceShare = config.SourceShare sourceDomain = config.SourceDomain - // FTP fields sourcePassiveMode = config.SourcePassiveMode - // OneDrive and Google Drive fields sourceClientId = config.SourceClientID sourceClientSecret = config.SourceClientSecret sourceDriveId = config.SourceDriveID sourceTeamDrive = config.SourceTeamDrive + filePattern = config.FilePattern outputPattern = config.OutputPattern + destinationType = config.DestinationType destinationPath = config.DestinationPath destHost = config.DestHost @@ -107,28 +110,29 @@ func getInitialData(config *db.TransferConfig) string { destUser = config.DestUser destPassword = config.DestPassword destKeyFile = config.DestKeyFile - // S3 fields + if destKeyFile != "" && destPassword == "" { + destAuthType = "key" + } destBucket = config.DestBucket destRegion = config.DestRegion destAccessKey = config.DestAccessKey destSecretKey = config.DestSecretKey destEndpoint = config.DestEndpoint - // SMB fields destShare = config.DestShare destDomain = config.DestDomain - // FTP fields destPassiveMode = config.DestPassiveMode - // OneDrive and Google Drive fields destClientId = config.DestClientID destClientSecret = config.DestClientSecret destDriveId = config.DestDriveID destTeamDrive = config.DestTeamDrive + archivePath = config.ArchivePath archiveEnabled = config.ArchiveEnabled deleteAfterTransfer = config.DeleteAfterTransfer rcloneFlags = config.RcloneFlags } + // Return the JSON-formatted string with all the data return fmt.Sprintf(`{ name: '%s', sourceType: '%s', @@ -138,6 +142,7 @@ func getInitialData(config *db.TransferConfig) string { sourceUser: '%s', sourcePassword: '%s', sourceKeyFile: '%s', + sourceAuthType: '%s', sourceBucket: '%s', sourceRegion: '%s', sourceAccessKey: '%s', @@ -150,8 +155,10 @@ func getInitialData(config *db.TransferConfig) string { sourceClientSecret: '%s', sourceDriveId: '%s', sourceTeamDrive: '%s', + filePattern: '%s', outputPattern: '%s', + destinationType: '%s', destinationPath: '%s', destHost: '%s', @@ -159,1825 +166,180 @@ func getInitialData(config *db.TransferConfig) string { destUser: '%s', destPassword: '%s', destKeyFile: '%s', - // S3 fields + destAuthType: '%s', destBucket: '%s', destRegion: '%s', destAccessKey: '%s', destSecretKey: '%s', destEndpoint: '%s', - // SMB fields destShare: '%s', destDomain: '%s', - // FTP fields destPassiveMode: %v, - // OneDrive and Google Drive fields destClientId: '%s', destClientSecret: '%s', destDriveId: '%s', destTeamDrive: '%s', - // Existing fields + archivePath: '%s', archiveEnabled: %v, deleteAfterTransfer: %v, rcloneFlags: '%s', - loading: false, - - validate() { - if (this.sourceType === 'sftp') { - if (!this.sourceHost || !this.sourceUser || (!this.sourcePassword && !this.sourceKeyFile)) { - return false; - } - } else if (this.sourceType === 's3') { - if (!this.sourceBucket || !this.sourceRegion || !this.sourceAccessKey || !this.sourceSecretKey) { - return false; - } - } else if (this.sourceType === 'minio') { - if (!this.sourceBucket || !this.sourceEndpoint || !this.sourceAccessKey || !this.sourceSecretKey) { - return false; - } - } else if (this.sourceType === 'b2') { - if (!this.sourceBucket || !this.sourceAccessKey || !this.sourceSecretKey) { - return false; - } - } else if (this.sourceType === 'smb') { - if (!this.sourceHost || !this.sourceShare || !this.sourceUser || !this.sourcePassword) { - return false; - } - } else if (this.sourceType === 'ftp') { - if (!this.sourceHost || !this.sourceUser || !this.sourcePassword) { - return false; - } - } else if (this.sourceType === 'webdav') { - if (!this.sourceHost || !this.sourceUser || !this.sourcePassword) { - return false; - } - } else if (this.sourceType === 'nextcloud') { - if (!this.sourceHost || !this.sourceUser || !this.sourcePassword) { - return false; - } - } else if (this.sourceType === 'onedrive') { - if (!this.sourceClientId || !this.sourceClientSecret) { - return false; - } - } else if (this.sourceType === 'google_drive') { - if (!this.sourceClientId || !this.sourceClientSecret) { - return false; - } - } - - if (this.destinationType === 'sftp') { - if (!this.destHost || !this.destUser || (!this.destPassword && !this.destKeyFile)) { - return false; - } - } else if (this.destinationType === 's3') { - if (!this.destBucket || !this.destRegion || !this.destAccessKey || !this.destSecretKey) { - return false; - } - } else if (this.destinationType === 'minio') { - if (!this.destBucket || !this.destEndpoint || !this.destAccessKey || !this.destSecretKey) { - return false; - } - } else if (this.destinationType === 'b2') { - if (!this.destBucket || !this.destAccessKey || !this.destSecretKey) { - return false; - } - } else if (this.destinationType === 'smb') { - if (!this.destHost || !this.destShare || !this.destUser || !this.destPassword) { - return false; - } - } else if (this.destinationType === 'ftp') { - if (!this.destHost || !this.destUser || !this.destPassword) { - return false; - } - } else if (this.destinationType === 'webdav') { - if (!this.destHost || !this.destUser || !this.destPassword) { - return false; - } - } else if (this.destinationType === 'nextcloud') { - if (!this.destHost || !this.destUser || !this.destPassword) { - return false; - } - } else if (this.destinationType === 'onedrive') { - if (!this.destClientId || !this.destClientSecret) { - return false; - } - } else if (this.destinationType === 'google_drive') { - if (!this.destClientId || !this.destClientSecret) { - return false; - } - } - return this.name && this.sourcePath && this.filePattern && this.destinationPath && (!this.archiveEnabled || this.archivePath); - } - }`, name, sourceType, sourcePath, sourceHost, sourcePort, sourceUser, sourcePassword, sourceKeyFile, - sourceBucket, sourceRegion, sourceAccessKey, sourceSecretKey, sourceEndpoint, - sourceShare, sourceDomain, sourcePassiveMode, - sourceClientId, sourceClientSecret, sourceDriveId, sourceTeamDrive, - filePattern, outputPattern, destinationType, destinationPath, destHost, destPort, destUser, destPassword, destKeyFile, - destBucket, destRegion, destAccessKey, destSecretKey, destEndpoint, - destShare, destDomain, destPassiveMode, - destClientId, destClientSecret, destDriveId, destTeamDrive, - archivePath, archiveEnabled, deleteAfterTransfer, rcloneFlags) + }`, + name, sourceType, sourcePath, sourceHost, sourcePort, sourceUser, sourcePassword, sourceKeyFile, sourceAuthType, + sourceBucket, sourceRegion, sourceAccessKey, sourceSecretKey, sourceEndpoint, sourceShare, sourceDomain, sourcePassiveMode, + sourceClientId, sourceClientSecret, sourceDriveId, sourceTeamDrive, + filePattern, outputPattern, + destinationType, destinationPath, destHost, destPort, destUser, destPassword, destKeyFile, destAuthType, + destBucket, destRegion, destAccessKey, destSecretKey, destEndpoint, destShare, destDomain, destPassiveMode, + destClientId, destClientSecret, destDriveId, destTeamDrive, + archivePath, archiveEnabled, deleteAfterTransfer, rcloneFlags) } templ ConfigForm(ctx context.Context, data ConfigFormData) { @LayoutWithContext(getConfigFormTitle(data.IsNew), ctx) { -
-
-
-
-
-
- -
-

- if data.IsNew { - Create New Configuration - } else { - Edit Configuration: { data.Config.Name } - } -

-

Set up your file transfer configuration

-
- -
-
-
-
-
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- - - - - - - - - - - - - - - - - - // - - // - -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-

Pattern to match files for transfer (e.g., *.txt, *.csv)

-
- -
- -
-
- -
- -
-

- Pattern for output filenames. Available variables: -
- {`${filename}`} - Original filename without extension (e.g., "report") -
- {`${ext}`} - Original file extension (e.g., ".csv") -
- {`${date:format}`} - Current date using Go's time format: -
- 2006-01-02 → YYYY-MM-DD -
- 20060102 → YYYYMMDD -
- 2006-01-02_15:04:05 → YYYY-MM-DD_HH:MM:SS -
- Example: {`${filename}_[date:2006-01-02]_${ext}`} → "report_2025-03-01.csv" -

-
- -
- -
-
- -
- -
-
- - - - - - - - - - - - - - - - - - // - - // - -
- -
-
- -
- -
-
- -
-
- - - -
-
- -
-
- -
- -
-
-
- -
-
- - - -
-

- - Original files will be permanently deleted from source after successful transfer - and archiving -

-
- -
- -
-
- -
- -
-
-
-
-
- -
-
- - - Cancel - - -
-
-
-
- -
-

- - Configure your file transfer settings carefully for optimal performance -

-
+
+
+
+
+
+ +

+ { getConfigFormTitle(data.IsNew) } +

+

+ Set up your file transfer configuration +

+ +
+ + + @common.NameField() + + + @common.SourceSelection() + + + + + + + + + + + + + + + + + + + + + @common.FilePatternFields() + + + @common.DestinationSelection() + + + + + + + + + + + + + + + + + + + + + @common.ArchiveOptions() + + + @common.RcloneFlags() + + +
+ + Cancel + + +
+
+ +
+

Configure your file transfer settings carefully for optimal performance

+
+
} -} \ No newline at end of file +} \ No newline at end of file diff --git a/components/providers/common/common.templ b/components/providers/common/common.templ new file mode 100644 index 0000000..531052f --- /dev/null +++ b/components/providers/common/common.templ @@ -0,0 +1,170 @@ +package common + +templ NameField() { +
+
+ +
+
+ +
+ +
+
+
+} + +templ FilePatternFields() { +
+
+ +
+
+ +
+ +
+

+ Glob pattern for files to transfer. Leave empty to transfer all files. +

+
+ +
+ +
+
+ +
+ +
+

+ Pattern for output filename. Use variables like ${`filename`}, ${`timestamp`}, ${`date`} +

+
+

+ Pattern for filenames. Available variables:
+ ${`filename`} - Original filename without extension (e.g., "report")
+ ${`ext`} - Original file extension (e.g., "csv")
+ ${`date:format`} - Current date using Go's time format:
+ • 2006-01-02 → YYYY-MM-DD
+ • 20060102 → YYYYMMDD
+ • 2006-01-02 15:04:05 → YYYY-MM-DD_HH:MM:SS
+ Example: ${`filename`}_${`date:2006-01-02`}_${`ext`} → "report_2023-03-01.csv" +

+
+
+
+} + +templ ArchiveOptions() { +
+
+
+ +
+
+ +

Archive files after successful transfer

+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ +

Delete source files after successful transfer

+
+
+
+} + +templ RcloneFlags() { +
+ +
+
+ +
+ +
+

+ Optional: Additional rclone flags for fine-tuning the transfer. +

+
+} + +templ SourceSelection() { +
+ +
+
+ +
+ +
+
+} + +templ DestinationSelection() { +
+ +
+
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/destination/ftp.templ b/components/providers/destination/ftp.templ new file mode 100644 index 0000000..b9ca774 --- /dev/null +++ b/components/providers/destination/ftp.templ @@ -0,0 +1,105 @@ +package destination + +templ FTPDestinationForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+ +
+
+ +
+
+ +

Enable passive mode for FTP connection

+
+
+
+} \ No newline at end of file diff --git a/components/providers/destination/local.templ b/components/providers/destination/local.templ new file mode 100644 index 0000000..deb7465 --- /dev/null +++ b/components/providers/destination/local.templ @@ -0,0 +1,22 @@ +package destination + +templ LocalDestinationForm() { +
+
+ +
+
+ +
+ +
+
+
+} \ No newline at end of file diff --git a/components/providers/destination/minio.templ b/components/providers/destination/minio.templ new file mode 100644 index 0000000..dfbcac2 --- /dev/null +++ b/components/providers/destination/minio.templ @@ -0,0 +1,91 @@ +package destination + +templ MinIODestinationForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. If specified, files will be uploaded to this path in the bucket. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/destination/nextcloud.templ b/components/providers/destination/nextcloud.templ new file mode 100644 index 0000000..9cc4daf --- /dev/null +++ b/components/providers/destination/nextcloud.templ @@ -0,0 +1,56 @@ +package destination + +templ NextCloudDestinationForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/destination/s3.templ b/components/providers/destination/s3.templ new file mode 100644 index 0000000..90ba488 --- /dev/null +++ b/components/providers/destination/s3.templ @@ -0,0 +1,91 @@ +package destination + +templ S3DestinationForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. If specified, files will be uploaded to this path in the bucket. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/destination/sftp.templ b/components/providers/destination/sftp.templ new file mode 100644 index 0000000..e224d59 --- /dev/null +++ b/components/providers/destination/sftp.templ @@ -0,0 +1,118 @@ +package destination + +templ SFTPDestinationForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ + + + +
+} \ No newline at end of file diff --git a/components/providers/destination/smb.templ b/components/providers/destination/smb.templ new file mode 100644 index 0000000..515810e --- /dev/null +++ b/components/providers/destination/smb.templ @@ -0,0 +1,111 @@ +package destination + +templ SMBDestinationForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. Subdirectory within the share. +

+
+ +
+ +
+
+ +
+ +
+

+ Optional. Windows domain for authentication. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/destination/webdav.templ b/components/providers/destination/webdav.templ new file mode 100644 index 0000000..3489cd4 --- /dev/null +++ b/components/providers/destination/webdav.templ @@ -0,0 +1,75 @@ +package destination + +templ WebDAVDestinationForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. Relative to the WebDAV URL. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/source/ftp.templ b/components/providers/source/ftp.templ new file mode 100644 index 0000000..8ec116a --- /dev/null +++ b/components/providers/source/ftp.templ @@ -0,0 +1,105 @@ +package source + +templ FTPSourceForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+ +
+
+ +
+
+ +

Enable passive mode for FTP connection

+
+
+
+} \ No newline at end of file diff --git a/components/providers/source/local.templ b/components/providers/source/local.templ new file mode 100644 index 0000000..0dcffc5 --- /dev/null +++ b/components/providers/source/local.templ @@ -0,0 +1,23 @@ +package source + +templ LocalSourceForm() { +
+
+ +
+
+ +
+ +
+
+
+} \ No newline at end of file diff --git a/components/providers/source/minio.templ b/components/providers/source/minio.templ new file mode 100644 index 0000000..1e4553c --- /dev/null +++ b/components/providers/source/minio.templ @@ -0,0 +1,91 @@ +package source + +templ MinIOSourceForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. If specified, only files in this path will be processed. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/source/nextcloud.templ b/components/providers/source/nextcloud.templ new file mode 100644 index 0000000..420f767 --- /dev/null +++ b/components/providers/source/nextcloud.templ @@ -0,0 +1,75 @@ +package source + +templ NextCloudSourceForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. If left empty, root directory will be used. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/source/s3.templ b/components/providers/source/s3.templ new file mode 100644 index 0000000..2794dfb --- /dev/null +++ b/components/providers/source/s3.templ @@ -0,0 +1,91 @@ +package source + +templ S3SourceForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. If specified, only files in this path will be processed. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/source/sftp.templ b/components/providers/source/sftp.templ new file mode 100644 index 0000000..ab0bb88 --- /dev/null +++ b/components/providers/source/sftp.templ @@ -0,0 +1,135 @@ +package source + +templ SFTPSourceForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ + + + +
+} \ No newline at end of file diff --git a/components/providers/source/smb.templ b/components/providers/source/smb.templ new file mode 100644 index 0000000..651a960 --- /dev/null +++ b/components/providers/source/smb.templ @@ -0,0 +1,111 @@ +package source + +templ SMBSourceForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. Subdirectory within the share. +

+
+ +
+ +
+
+ +
+ +
+

+ Optional. Windows domain for authentication. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file diff --git a/components/providers/source/webdav.templ b/components/providers/source/webdav.templ new file mode 100644 index 0000000..17d9c06 --- /dev/null +++ b/components/providers/source/webdav.templ @@ -0,0 +1,75 @@ +package source + +templ WebDAVSourceForm() { +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+

+ Optional. Relative to the WebDAV URL. +

+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+} \ No newline at end of file