Refactor avatar upload and download handlers to use internal WebDAV client for server-to-server operations

This commit is contained in:
Leon Bösche
2026-01-31 23:48:08 +01:00
parent 9ac649105a
commit 17ac65a493

View File

@@ -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/<user-id>.<ext>
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