Expand admin UI with full config editing and sp_dc cookie age tracking

- Fix auth status detection to use token validity instead of /me endpoint
- Add SessionCookieSetDate to SpotifyApiSettings for tracking cookie age
- Auto-set cookie date when updating sp_dc via admin UI
- Add edit buttons for all config settings (Spotify, Deezer, Qobuz, SquidWTF, Jellyfin)
- Show cookie age with color-coded expiration warnings (green/yellow/red)
- Display cookie age on both Dashboard and Config tabs
- Add generic edit setting modal supporting text/password/number/toggle/select inputs
- Remove SquidWTF base URL (not configurable)
- Add restart container button with manual restart instructions
This commit is contained in:
2026-02-03 14:52:40 -05:00
parent 71c4241a8a
commit c9895f6d1a
5 changed files with 306 additions and 41 deletions

View File

@@ -24,6 +24,9 @@ public class AdminController : ControllerBase
private readonly SpotifyApiSettings _spotifyApiSettings;
private readonly SpotifyImportSettings _spotifyImportSettings;
private readonly JellyfinSettings _jellyfinSettings;
private readonly DeezerSettings _deezerSettings;
private readonly QobuzSettings _qobuzSettings;
private readonly SquidWTFSettings _squidWtfSettings;
private readonly SpotifyApiClient _spotifyClient;
private readonly SpotifyPlaylistFetcher _playlistFetcher;
private readonly RedisCacheService _cache;
@@ -36,6 +39,9 @@ public class AdminController : ControllerBase
IOptions<SpotifyApiSettings> spotifyApiSettings,
IOptions<SpotifyImportSettings> spotifyImportSettings,
IOptions<JellyfinSettings> jellyfinSettings,
IOptions<DeezerSettings> deezerSettings,
IOptions<QobuzSettings> qobuzSettings,
IOptions<SquidWTFSettings> squidWtfSettings,
SpotifyApiClient spotifyClient,
SpotifyPlaylistFetcher playlistFetcher,
RedisCacheService cache)
@@ -45,6 +51,9 @@ public class AdminController : ControllerBase
_spotifyApiSettings = spotifyApiSettings.Value;
_spotifyImportSettings = spotifyImportSettings.Value;
_jellyfinSettings = jellyfinSettings.Value;
_deezerSettings = deezerSettings.Value;
_qobuzSettings = qobuzSettings.Value;
_squidWtfSettings = squidWtfSettings.Value;
_spotifyClient = spotifyClient;
_playlistFetcher = playlistFetcher;
_cache = cache;
@@ -63,11 +72,24 @@ public class AdminController : ControllerBase
{
try
{
var (success, userId, displayName) = await _spotifyClient.GetCurrentUserAsync();
if (success)
// First check if we can get a valid (non-anonymous) token
var token = await _spotifyClient.GetWebAccessTokenAsync();
if (!string.IsNullOrEmpty(token))
{
spotifyAuthStatus = "authenticated";
spotifyUser = displayName ?? userId;
// Try to get user info (may fail even with valid token)
var (success, userId, displayName) = await _spotifyClient.GetCurrentUserAsync();
if (success)
{
spotifyAuthStatus = "authenticated";
spotifyUser = displayName ?? userId;
}
else
{
// Token is valid but /me endpoint failed - still consider it authenticated
// (the token being non-anonymous is the real indicator)
spotifyAuthStatus = "authenticated";
spotifyUser = "(profile not accessible)";
}
}
else
{
@@ -95,6 +117,7 @@ public class AdminController : ControllerBase
authStatus = spotifyAuthStatus,
user = spotifyUser,
hasCookie = !string.IsNullOrEmpty(_spotifyApiSettings.SessionCookie),
cookieSetDate = _spotifyApiSettings.SessionCookieSetDate,
cacheDurationMinutes = _spotifyApiSettings.CacheDurationMinutes,
preferIsrcMatching = _spotifyApiSettings.PreferIsrcMatching
},
@@ -104,6 +127,20 @@ public class AdminController : ControllerBase
syncTime = $"{_spotifyImportSettings.SyncStartHour:D2}:{_spotifyImportSettings.SyncStartMinute:D2}",
syncWindowHours = _spotifyImportSettings.SyncWindowHours,
playlistCount = _spotifyImportSettings.Playlists.Count
},
deezer = new
{
hasArl = !string.IsNullOrEmpty(_deezerSettings.Arl),
quality = _deezerSettings.Quality ?? "FLAC"
},
qobuz = new
{
hasToken = !string.IsNullOrEmpty(_qobuzSettings.UserAuthToken),
quality = _qobuzSettings.Quality ?? "FLAC"
},
squidWtf = new
{
quality = _squidWtfSettings.Quality ?? "LOSSLESS"
}
});
}
@@ -214,8 +251,8 @@ public class AdminController : ControllerBase
spotifyApi = new
{
enabled = _spotifyApiSettings.Enabled,
clientId = MaskValue(_spotifyApiSettings.ClientId),
sessionCookie = MaskValue(_spotifyApiSettings.SessionCookie, showLast: 8),
sessionCookieSetDate = _spotifyApiSettings.SessionCookieSetDate,
cacheDurationMinutes = _spotifyApiSettings.CacheDurationMinutes,
rateLimitDelayMs = _spotifyApiSettings.RateLimitDelayMs,
preferIsrcMatching = _spotifyApiSettings.PreferIsrcMatching
@@ -238,6 +275,22 @@ public class AdminController : ControllerBase
url = _jellyfinSettings.Url,
apiKey = MaskValue(_jellyfinSettings.ApiKey),
libraryId = _jellyfinSettings.LibraryId
},
deezer = new
{
arl = MaskValue(_deezerSettings.Arl, showLast: 8),
arlFallback = MaskValue(_deezerSettings.ArlFallback, showLast: 8),
quality = _deezerSettings.Quality ?? "FLAC"
},
qobuz = new
{
userAuthToken = MaskValue(_qobuzSettings.UserAuthToken, showLast: 8),
userId = _qobuzSettings.UserId,
quality = _qobuzSettings.Quality ?? "FLAC"
},
squidWtf = new
{
quality = _squidWtfSettings.Quality ?? "LOSSLESS"
}
});
}
@@ -291,6 +344,16 @@ public class AdminController : ControllerBase
envContent[key] = value;
appliedUpdates.Add(key);
_logger.LogInformation(" Setting {Key}", key);
// Auto-set cookie date when Spotify session cookie is updated
if (key == "SPOTIFY_API_SESSION_COOKIE" && !string.IsNullOrEmpty(value))
{
var dateKey = "SPOTIFY_API_SESSION_COOKIE_SET_DATE";
var dateValue = DateTime.UtcNow.ToString("o"); // ISO 8601 format
envContent[dateKey] = dateValue;
appliedUpdates.Add(dateKey);
_logger.LogInformation(" Auto-setting {Key} to {Value}", dateKey, dateValue);
}
}
// Write back to .env file