From 0f46b20fc4e5764c49fb5233e3e4bc35e5d37df8 Mon Sep 17 00:00:00 2001 From: StarFleetCPTN Date: Mon, 7 Apr 2025 11:05:08 -0700 Subject: [PATCH] refactor: Update form field identifiers and improve URL handling for NextCloud and WebDAV - Changed input field IDs and names from 'dest_endpoint' and 'source_endpoint' to 'dest_host' and 'source_host' for consistency across NextCloud and WebDAV forms. - Updated placeholder texts for username, password, and file path fields to be more user-friendly. - Enhanced the URL parsing and validation logic in the transfer configuration generation to ensure proper handling of WebDAV and NextCloud URLs, improving robustness and error handling. --- .../providers/destination/nextcloud.templ | 12 +-- components/providers/destination/webdav.templ | 8 +- components/providers/source/nextcloud.templ | 12 +-- components/providers/source/webdav.templ | 8 +- internal/db/transfer_config_store.go | 87 ++++++++++++++++++- internal/web/handlers/config_handlers.go | 1 + 6 files changed, 106 insertions(+), 22 deletions(-) diff --git a/components/providers/destination/nextcloud.templ b/components/providers/destination/nextcloud.templ index bb4c42b..a7b97a2 100644 --- a/components/providers/destination/nextcloud.templ +++ b/components/providers/destination/nextcloud.templ @@ -10,12 +10,12 @@ templ NextCloudDestinationForm() {
- +
-
@@ -32,7 +32,7 @@ templ NextCloudDestinationForm() {
+ placeholder="username" />

Your NextCloud username @@ -47,7 +47,7 @@ templ NextCloudDestinationForm() { + placeholder="password" />

Your NextCloud account password @@ -62,10 +62,10 @@ templ NextCloudDestinationForm() { + placeholder="/path/to/files" />

- Path to your files in NextCloud. Usually starts with "remote.php/dav/files/username/" + Path to your files in NextCloud

diff --git a/components/providers/destination/webdav.templ b/components/providers/destination/webdav.templ index 15caa59..c77bc48 100644 --- a/components/providers/destination/webdav.templ +++ b/components/providers/destination/webdav.templ @@ -10,12 +10,12 @@ templ WebDAVDestinationForm() {
- +
-
@@ -32,7 +32,7 @@ templ WebDAVDestinationForm() {
+ placeholder="username" />

Your WebDAV username @@ -47,7 +47,7 @@ templ WebDAVDestinationForm() { + placeholder="password" />

Your WebDAV account password diff --git a/components/providers/source/nextcloud.templ b/components/providers/source/nextcloud.templ index a34366c..a683789 100644 --- a/components/providers/source/nextcloud.templ +++ b/components/providers/source/nextcloud.templ @@ -10,12 +10,12 @@ templ NextCloudSourceForm() {

- +
-
@@ -32,7 +32,7 @@ templ NextCloudSourceForm() {
+ placeholder="username" />

Your NextCloud username @@ -47,7 +47,7 @@ templ NextCloudSourceForm() { + placeholder="password" />

Your NextCloud account password @@ -62,10 +62,10 @@ templ NextCloudSourceForm() { + placeholder="/path/to/files" />

- Path to your files in NextCloud. Usually starts with "remote.php/dav/files/username/" + Path to your files in NextCloud

diff --git a/components/providers/source/webdav.templ b/components/providers/source/webdav.templ index e79e8ae..7a9a819 100644 --- a/components/providers/source/webdav.templ +++ b/components/providers/source/webdav.templ @@ -10,12 +10,12 @@ templ WebDAVSourceForm() {
- +
-
@@ -32,7 +32,7 @@ templ WebDAVSourceForm() {
+ placeholder="username" />

Your WebDAV username @@ -47,7 +47,7 @@ templ WebDAVSourceForm() { + placeholder="password" />

Your WebDAV account password diff --git a/internal/db/transfer_config_store.go b/internal/db/transfer_config_store.go index 1d43f86..3712782 100644 --- a/internal/db/transfer_config_store.go +++ b/internal/db/transfer_config_store.go @@ -2,6 +2,7 @@ package db import ( "fmt" + "net/url" "os" "os/exec" "path/filepath" @@ -149,7 +150,49 @@ func (db *DB) GenerateRcloneConfig(config *TransferConfig) error { if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to create source config (minio): %v\nOutput: %s", err, output) } - // ... (Add cases for other source types: b2, smb, ftp, webdav, nextcloud, onedrive, gdrive, gphotos) ... + case "webdav", "nextcloud": // Handle both webdav and nextcloud similarly + // Construct the WebDAV URL + // Parse the provided source URL, assuming it includes the scheme + inputURL := config.SourceHost + parsedURL, err := url.Parse(inputURL) + if err != nil { + return fmt.Errorf("failed to parse source URL '%s': %v", inputURL, err) + } + // Validate that both scheme and host are present + if parsedURL.Scheme == "" || parsedURL.Host == "" { + return fmt.Errorf("invalid source URL '%s': must include scheme (http/https) and host", inputURL) + } + // Use the scheme and host from the parsed URL + webdavURL := fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host) + + // Determine vendor based on type + vendor := "other" // Default vendor + if config.SourceType == "nextcloud" { + vendor = "nextcloud" + + // Construct the full Nextcloud path using the parsed base URL + webdavURL = fmt.Sprintf("%s/remote.php/dav/files/%s/", webdavURL, config.SourceUser) + } + + args := []string{ + "config", "create", sourceName, "webdav", + "url", webdavURL, + "vendor", vendor, + "user", config.SourceUser, + "pass", config.SourcePassword, // rclone obscures this + "--non-interactive", + "--config", configPath, + "--log-level", "ERROR", + } + cmd := exec.Command(rclonePath, args...) + if output, err := cmd.CombinedOutput(); err != nil { + errorMsg := fmt.Sprintf("failed to create source config (%s): %v", config.SourceType, err) + // Check if output contains useful info, especially for auth errors + if len(output) > 0 { + errorMsg += fmt.Sprintf("\nOutput: %s", output) + } + return fmt.Errorf(errorMsg) + } case "local": // For local source, ensure the section exists but might not need specific rclone config create content := fmt.Sprintf("[%s]\ntype = local\n\n", sourceName) @@ -224,7 +267,47 @@ func (db *DB) GenerateRcloneConfig(config *TransferConfig) error { if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to create destination config (minio): %v\nOutput: %s", err, output) } - // ... (Add cases for other destination types: b2, smb, ftp, webdav, nextcloud, onedrive, gdrive, gphotos) ... + case "webdav", "nextcloud": // Combined case for WebDAV and Nextcloud + // Parse and reconstruct the WebDAV URL robustly + // Parse the provided destination URL, assuming it includes the scheme + inputURL := config.DestHost + parsedURL, err := url.Parse(inputURL) + if err != nil { + return fmt.Errorf("failed to parse destination URL '%s': %v", inputURL, err) + } + // Validate that both scheme and host are present + if parsedURL.Scheme == "" || parsedURL.Host == "" { + return fmt.Errorf("invalid destination URL '%s': must include scheme (http/https) and host", inputURL) + } + // Use the scheme and host from the parsed URL + webdavURL := fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host) + + // Determine vendor based on type + vendor := "other" // Default vendor + if config.DestinationType == "nextcloud" { + vendor = "nextcloud" + + webdavURL = fmt.Sprintf("%s/remote.php/dav/files/%s/", webdavURL, config.DestUser) // Corrected variable + } + + args := []string{ + "config", "create", destName, "webdav", + "url", webdavURL, // Use the parsed and reconstructed URL + "vendor", vendor, + "user", config.DestUser, + "pass", config.DestPassword, // rclone obscures this + "--non-interactive", + "--config", configPath, + "--log-level", "ERROR", + } + cmd := exec.Command(rclonePath, args...) + if output, err := cmd.CombinedOutput(); err != nil { + errorMsg := fmt.Sprintf("failed to create destination config (%s): %v", config.DestinationType, err) + if len(output) > 0 { + errorMsg += fmt.Sprintf("\nOutput: %s", output) + } + return fmt.Errorf(errorMsg) + } case "local": // Append local config section content := fmt.Sprintf("\n[%s]\ntype = local\n", destName) diff --git a/internal/web/handlers/config_handlers.go b/internal/web/handlers/config_handlers.go index 016b567..6c006f7 100644 --- a/internal/web/handlers/config_handlers.go +++ b/internal/web/handlers/config_handlers.go @@ -286,6 +286,7 @@ func (h *Handlers) HandleCreateConfig(c *gin.Context) { } // Generate rclone config file + if err := h.DB.GenerateRcloneConfig(&config); err != nil { log.Printf("Warning: Failed to generate rclone config: %v", err) // Continue anyway, as the config was created in the database