Fix avatar URL token handling and improve user avatar download timeout management

This commit is contained in:
Leon Bösche
2026-01-29 22:55:46 +01:00
parent 9b6f5c960a
commit 00a585e2c1
2 changed files with 25 additions and 5 deletions

View File

@@ -131,7 +131,9 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
_avatarUrl = response['avatarUrl'] as String?; _avatarUrl = response['avatarUrl'] as String?;
final authState = context.read<AuthBloc>().state; final authState = context.read<AuthBloc>().state;
if (authState is AuthAuthenticated && _avatarUrl != null) { if (authState is AuthAuthenticated && _avatarUrl != null) {
_avatarUrl = "$_avatarUrl&token=${authState.token}"; if (!_avatarUrl!.contains('token=')) {
_avatarUrl = "$_avatarUrl&token=${authState.token}";
}
} }
if (_currentUser != null && _avatarUrl != null) { if (_currentUser != null && _avatarUrl != null) {
final updatedUser = _currentUser!.copyWith( final updatedUser = _currentUser!.copyWith(
@@ -412,8 +414,10 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
if (authState is AuthAuthenticated) { if (authState is AuthAuthenticated) {
_currentUser = authState.user; _currentUser = authState.user;
_avatarUrl = _currentUser?.avatarUrl; _avatarUrl = _currentUser?.avatarUrl;
if (_avatarUrl != null) { if (_avatarUrl != null && authState.token.isNotEmpty) {
_avatarUrl = "$_avatarUrl&token=${authState.token}"; if (!_avatarUrl!.contains('token=')) {
_avatarUrl = "$_avatarUrl&token=${authState.token}";
}
} }
} }

View File

@@ -6,6 +6,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
stderrors "errors"
"fmt" "fmt"
"io" "io"
"log" "log"
@@ -4185,18 +4186,33 @@ func getUserAvatarHandler(w http.ResponseWriter, r *http.Request, db *database.D
return return
} }
remotePath := strings.TrimPrefix(*avatarURL, client.BaseURL+"/") remotePath := strings.TrimPrefix(*avatarURL, client.BaseURL+"/")
resp, err := client.Download(r.Context(), remotePath, "")
// Use a short timeout to avoid hanging behind proxies; return 404 if WebDAV is slow or times out
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
resp, err := client.Download(ctx, remotePath, "")
if err != nil { if err != nil {
// If download timed out or gateway returned 504, treat as "no avatar" so frontend shows default icon
if strings.Contains(err.Error(), "504") || stderrors.Is(err, context.DeadlineExceeded) {
fmt.Printf("[ERROR] Avatar download timeout for remotePath=%s: %v\n", remotePath, err)
w.WriteHeader(http.StatusNotFound)
return
}
errors.LogError(r, err, "Failed to download avatar") errors.LogError(r, err, "Failed to download avatar")
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError) errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
// Copy headers // Copy headers but ensure sensible caching
for k, v := range resp.Header { for k, v := range resp.Header {
w.Header()[k] = v w.Header()[k] = v
} }
// Opt into short caching; client-side cache is also versioned via v=
w.Header().Set("Cache-Control", "public, max-age=300")
w.WriteHeader(resp.StatusCode) w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body) io.Copy(w, resp.Body)
} }