From 17ac65a493e69f26b41f9a4cceb0fc40e8c01529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20B=C3=B6sche?= Date: Sat, 31 Jan 2026 23:48:08 +0100 Subject: [PATCH] Refactor avatar upload and download handlers to use internal WebDAV client for server-to-server operations --- go_cloud/internal/http/routes.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/go_cloud/internal/http/routes.go b/go_cloud/internal/http/routes.go index 073352b..b01fe20 100644 --- a/go_cloud/internal/http/routes.go +++ b/go_cloud/internal/http/routes.go @@ -4195,16 +4195,19 @@ func uploadUserAvatarHandler(w http.ResponseWriter, r *http.Request, db *databas filename := fmt.Sprintf("%s%s", userID.String(), ext) // Upload to Nextcloud at .avatars/. - client := storage.NewWebDAVClient(cfg) + // Use internal Nextcloud WebDAV endpoint for server-to-server operations to avoid external TLS/timeouts + internalClient := storage.NewUserWebDAVClient(cfg.NextcloudURL, cfg.NextcloudUser, cfg.NextcloudPass) avatarPath := fmt.Sprintf(".avatars/%s", filename) - err = client.Upload(r.Context(), avatarPath, bytes.NewReader(fileBytes), header.Size) + err = internalClient.Upload(r.Context(), avatarPath, bytes.NewReader(fileBytes), header.Size) if err != nil { - errors.LogError(r, err, "Failed to upload avatar") + errors.LogError(r, err, "Failed to upload avatar (internal)") errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError) return } - webdavURL := fmt.Sprintf("%s/%s", client.BaseURL, avatarPath) + // Store external-facing avatar URL in DB (so other components can reference it) + externalClient := storage.NewWebDAVClient(cfg) + webdavURL := fmt.Sprintf("%s/%s", strings.TrimRight(externalClient.BaseURL, "/"), avatarPath) // Update user profile with avatar URL and updated_at _, err = db.ExecContext(r.Context(), @@ -4222,7 +4225,7 @@ func uploadUserAvatarHandler(w http.ResponseWriter, r *http.Request, db *databas verifyTimeout := 5 // seconds for i := 0; i < verifyRetries; i++ { vctx, vcancel := context.WithTimeout(r.Context(), time.Duration(verifyTimeout)*time.Second) - resp, derr := client.Download(vctx, avatarPath, "") + resp, derr := internalClient.Download(vctx, avatarPath, "") vcancel() if derr == nil && resp != nil { resp.Body.Close() @@ -4368,12 +4371,16 @@ func getUserAvatarHandler(w http.ResponseWriter, r *http.Request, db *database.D } // Download from WebDAV with retries and backoff - client := storage.NewWebDAVClient(cfg) - if client == nil { + // Use external client instance only to compute remotePath from stored avatar URL + externalClient := storage.NewWebDAVClient(cfg) + if externalClient == nil { errors.WriteError(w, errors.CodeInternal, "WebDAV client not configured", http.StatusInternalServerError) return } - remotePath := strings.TrimPrefix(*avatarURL, client.BaseURL+"/") + remotePath := strings.TrimPrefix(*avatarURL, externalClient.BaseURL+"/") + + // Use internal admin WebDAV client to actually fetch the avatar (server-to-server) + internalClient := storage.NewUserWebDAVClient(cfg.NextcloudURL, cfg.NextcloudUser, cfg.NextcloudPass) // Configure retry & timeout from config timeoutSeconds := cfg.AvatarDownloadTimeoutSeconds @@ -4390,7 +4397,8 @@ func getUserAvatarHandler(w http.ResponseWriter, r *http.Request, db *database.D var dlErr error for attempt := 0; attempt < attempts; attempt++ { ctx, cancel := context.WithTimeout(r.Context(), time.Duration(timeoutSeconds)*time.Second) - resp, dlErr = client.Download(ctx, remotePath, "") + // Use internal client to avoid external network/TLS overhead + resp, dlErr = internalClient.Download(ctx, remotePath, "") cancel() if dlErr == nil { break