mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Move admin endpoints to internal port 5275 for security
This commit is contained in:
@@ -2015,6 +2015,284 @@ public class AdminController : ControllerBase
|
|||||||
GC.Collect(2, GCCollectionMode.Optimized, blocking: false);
|
GC.Collect(2, GCCollectionMode.Optimized, blocking: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Spotify Admin Endpoints
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manual trigger endpoint to force fetch Spotify missing tracks.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("spotify/sync")]
|
||||||
|
public async Task<IActionResult> TriggerSpotifySync([FromServices] IEnumerable<IHostedService> hostedServices)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_spotifyImportSettings.Enabled)
|
||||||
|
{
|
||||||
|
return BadRequest(new { error = "Spotify Import is not enabled" });
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Manual Spotify sync triggered via admin endpoint");
|
||||||
|
|
||||||
|
// Find the SpotifyMissingTracksFetcher service
|
||||||
|
var fetcherService = hostedServices
|
||||||
|
.OfType<allstarr.Services.Spotify.SpotifyMissingTracksFetcher>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (fetcherService == null)
|
||||||
|
{
|
||||||
|
return BadRequest(new { error = "SpotifyMissingTracksFetcher service not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the sync in background
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Use reflection to call the private ExecuteOnceAsync method
|
||||||
|
var method = fetcherService.GetType().GetMethod("ExecuteOnceAsync",
|
||||||
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||||
|
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
await (Task)method.Invoke(fetcherService, new object[] { CancellationToken.None })!;
|
||||||
|
_logger.LogInformation("Manual Spotify sync completed successfully");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("Could not find ExecuteOnceAsync method on SpotifyMissingTracksFetcher");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error during manual Spotify sync");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(new {
|
||||||
|
message = "Spotify sync started in background",
|
||||||
|
timestamp = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error triggering Spotify sync");
|
||||||
|
return StatusCode(500, new { error = "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manual trigger endpoint to force Spotify track matching.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("spotify/match")]
|
||||||
|
public async Task<IActionResult> TriggerSpotifyMatch([FromServices] IEnumerable<IHostedService> hostedServices)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_spotifyApiSettings.Enabled)
|
||||||
|
{
|
||||||
|
return BadRequest(new { error = "Spotify API is not enabled" });
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Manual Spotify track matching triggered via admin endpoint");
|
||||||
|
|
||||||
|
// Find the SpotifyTrackMatchingService
|
||||||
|
var matchingService = hostedServices
|
||||||
|
.OfType<allstarr.Services.Spotify.SpotifyTrackMatchingService>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (matchingService == null)
|
||||||
|
{
|
||||||
|
return BadRequest(new { error = "SpotifyTrackMatchingService not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger matching in background
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Use reflection to call the private ExecuteOnceAsync method
|
||||||
|
var method = matchingService.GetType().GetMethod("ExecuteOnceAsync",
|
||||||
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||||
|
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
await (Task)method.Invoke(matchingService, new object[] { CancellationToken.None })!;
|
||||||
|
_logger.LogInformation("Manual Spotify track matching completed successfully");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("Could not find ExecuteOnceAsync method on SpotifyTrackMatchingService");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error during manual Spotify track matching");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(new {
|
||||||
|
message = "Spotify track matching started in background",
|
||||||
|
timestamp = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error triggering Spotify track matching");
|
||||||
|
return StatusCode(500, new { error = "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear Spotify playlist cache to force re-matching.
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("spotify/clear-cache")]
|
||||||
|
public async Task<IActionResult> ClearSpotifyCache()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var clearedKeys = new List<string>();
|
||||||
|
|
||||||
|
// Clear Redis cache for all configured playlists
|
||||||
|
foreach (var playlist in _spotifyImportSettings.Playlists)
|
||||||
|
{
|
||||||
|
var keys = new[]
|
||||||
|
{
|
||||||
|
$"spotify:playlist:{playlist.Name}",
|
||||||
|
$"spotify:playlist:items:{playlist.Name}",
|
||||||
|
$"spotify:matched:{playlist.Name}"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
await _cache.DeleteAsync(key);
|
||||||
|
clearedKeys.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Cleared Spotify cache for {Count} keys via admin endpoint", clearedKeys.Count);
|
||||||
|
|
||||||
|
return Ok(new {
|
||||||
|
message = "Spotify cache cleared successfully",
|
||||||
|
clearedKeys = clearedKeys,
|
||||||
|
timestamp = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error clearing Spotify cache");
|
||||||
|
return StatusCode(500, new { error = "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Debug Endpoints
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets endpoint usage statistics from the log file.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("debug/endpoint-usage")]
|
||||||
|
public async Task<IActionResult> GetEndpointUsage(
|
||||||
|
[FromQuery] int top = 100,
|
||||||
|
[FromQuery] string? since = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logFile = "/app/cache/endpoint-usage/endpoints.csv";
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(logFile))
|
||||||
|
{
|
||||||
|
return Ok(new {
|
||||||
|
message = "No endpoint usage data available",
|
||||||
|
endpoints = new object[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = await System.IO.File.ReadAllLinesAsync(logFile);
|
||||||
|
var usage = new Dictionary<string, int>();
|
||||||
|
DateTime? sinceDate = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(since) && DateTime.TryParse(since, out var parsedDate))
|
||||||
|
{
|
||||||
|
sinceDate = parsedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var line in lines.Skip(1)) // Skip header
|
||||||
|
{
|
||||||
|
var parts = line.Split(',');
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
var timestamp = parts[0];
|
||||||
|
var endpoint = parts[1];
|
||||||
|
|
||||||
|
// Filter by date if specified
|
||||||
|
if (sinceDate.HasValue && DateTime.TryParse(timestamp, out var logDate))
|
||||||
|
{
|
||||||
|
if (logDate < sinceDate.Value)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
usage[endpoint] = usage.GetValueOrDefault(endpoint, 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var topEndpoints = usage
|
||||||
|
.OrderByDescending(kv => kv.Value)
|
||||||
|
.Take(top)
|
||||||
|
.Select(kv => new { endpoint = kv.Key, count = kv.Value })
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return Ok(new {
|
||||||
|
totalEndpoints = usage.Count,
|
||||||
|
totalRequests = usage.Values.Sum(),
|
||||||
|
since = since,
|
||||||
|
top = top,
|
||||||
|
endpoints = topEndpoints
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting endpoint usage");
|
||||||
|
return StatusCode(500, new { error = "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the endpoint usage log file.
|
||||||
|
/// </summary>
|
||||||
|
[HttpDelete("debug/endpoint-usage")]
|
||||||
|
public IActionResult ClearEndpointUsage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logFile = "/app/cache/endpoint-usage/endpoints.csv";
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(logFile))
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(logFile);
|
||||||
|
_logger.LogInformation("Cleared endpoint usage log via admin endpoint");
|
||||||
|
|
||||||
|
return Ok(new {
|
||||||
|
message = "Endpoint usage log cleared successfully",
|
||||||
|
timestamp = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Ok(new {
|
||||||
|
message = "No endpoint usage log file found",
|
||||||
|
timestamp = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error clearing endpoint usage log");
|
||||||
|
return StatusCode(500, new { error = "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConfigUpdateRequest
|
public class ConfigUpdateRequest
|
||||||
|
|||||||
@@ -3979,284 +3979,8 @@ public class JellyfinController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manual trigger endpoint to force fetch Spotify missing tracks.
|
|
||||||
/// GET /spotify/sync?api_key=YOUR_KEY
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("spotify/sync", Order = 1)]
|
|
||||||
[ServiceFilter(typeof(ApiKeyAuthFilter))]
|
|
||||||
public async Task<IActionResult> TriggerSpotifySync([FromServices] IEnumerable<IHostedService> hostedServices)
|
|
||||||
{
|
|
||||||
if (!_spotifySettings.Enabled)
|
|
||||||
{
|
|
||||||
return BadRequest(new { error = "Spotify Import is not enabled" });
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("Manual Spotify sync triggered");
|
|
||||||
|
|
||||||
// Find the SpotifyMissingTracksFetcher service
|
|
||||||
var fetcherService = hostedServices
|
|
||||||
.OfType<allstarr.Services.Spotify.SpotifyMissingTracksFetcher>()
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (fetcherService == null)
|
|
||||||
{
|
|
||||||
return StatusCode(500, new { error = "SpotifyMissingTracksFetcher not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger fetch manually
|
|
||||||
await fetcherService.TriggerFetchAsync();
|
|
||||||
|
|
||||||
// Check what was cached
|
|
||||||
var results = new Dictionary<string, object>();
|
|
||||||
foreach (var playlist in _spotifySettings.Playlists)
|
|
||||||
{
|
|
||||||
var cacheKey = $"spotify:missing:{playlist.Name}";
|
|
||||||
var tracks = await _cache.GetAsync<List<allstarr.Models.Spotify.MissingTrack>>(cacheKey);
|
|
||||||
|
|
||||||
if (tracks != null && tracks.Count > 0)
|
|
||||||
{
|
|
||||||
results[playlist.Name] = new {
|
|
||||||
status = "success",
|
|
||||||
tracks = tracks.Count,
|
|
||||||
localTracksPosition = playlist.LocalTracksPosition.ToString()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
results[playlist.Name] = new {
|
|
||||||
status = "not_found",
|
|
||||||
message = "No missing tracks found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manually trigger track matching for all Spotify playlists.
|
|
||||||
/// GET /spotify/match?api_key=YOUR_KEY
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("spotify/match", Order = 1)]
|
|
||||||
[ServiceFilter(typeof(ApiKeyAuthFilter))]
|
|
||||||
public async Task<IActionResult> TriggerSpotifyMatch([FromServices] IEnumerable<IHostedService> hostedServices)
|
|
||||||
{
|
|
||||||
if (!_spotifySettings.Enabled)
|
|
||||||
{
|
|
||||||
return BadRequest(new { error = "Spotify Import is not enabled" });
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("Manual Spotify track matching triggered");
|
|
||||||
|
|
||||||
// Find the SpotifyTrackMatchingService
|
|
||||||
var matchingService = hostedServices
|
|
||||||
.OfType<allstarr.Services.Spotify.SpotifyTrackMatchingService>()
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (matchingService == null)
|
|
||||||
{
|
|
||||||
return StatusCode(500, new { error = "SpotifyTrackMatchingService not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger matching asynchronously
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await matchingService.TriggerMatchingAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error during manual track matching");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
status = "started",
|
|
||||||
message = "Track matching started in background. Check logs for progress.",
|
|
||||||
playlists = _spotifySettings.Playlists.Select(p => new { p.Name, p.Id, localTracksPosition = p.LocalTracksPosition.ToString() })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<allstarr.Models.Spotify.MissingTrack> ParseMissingTracksJson(string json)
|
|
||||||
{
|
|
||||||
var tracks = new List<allstarr.Models.Spotify.MissingTrack>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var doc = JsonDocument.Parse(json);
|
|
||||||
|
|
||||||
foreach (var item in doc.RootElement.EnumerateArray())
|
|
||||||
{
|
|
||||||
var track = new allstarr.Models.Spotify.MissingTrack
|
|
||||||
{
|
|
||||||
SpotifyId = item.GetProperty("Id").GetString() ?? "",
|
|
||||||
Title = item.GetProperty("Name").GetString() ?? "",
|
|
||||||
Album = item.GetProperty("AlbumName").GetString() ?? "",
|
|
||||||
Artists = item.GetProperty("ArtistNames")
|
|
||||||
.EnumerateArray()
|
|
||||||
.Select(a => a.GetString() ?? "")
|
|
||||||
.Where(a => !string.IsNullOrEmpty(a))
|
|
||||||
.ToList()
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(track.Title))
|
|
||||||
{
|
|
||||||
tracks.Add(track);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to parse missing tracks JSON");
|
|
||||||
}
|
|
||||||
|
|
||||||
return tracks;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Spotify Debug
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear Spotify playlist cache to force re-matching.
|
|
||||||
/// GET /spotify/clear-cache?api_key=YOUR_KEY
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("spotify/clear-cache")]
|
|
||||||
[ServiceFilter(typeof(ApiKeyAuthFilter))]
|
|
||||||
public async Task<IActionResult> ClearSpotifyCache()
|
|
||||||
{
|
|
||||||
if (!_spotifySettings.Enabled)
|
|
||||||
{
|
|
||||||
return BadRequest(new { error = "Spotify Import is not enabled" });
|
|
||||||
}
|
|
||||||
|
|
||||||
var cleared = new List<string>();
|
|
||||||
|
|
||||||
foreach (var playlist in _spotifySettings.Playlists)
|
|
||||||
{
|
|
||||||
var matchedKey = $"spotify:matched:{playlist.Name}";
|
|
||||||
await _cache.DeleteAsync(matchedKey);
|
|
||||||
cleared.Add(playlist.Name);
|
|
||||||
_logger.LogInformation("Cleared cache for {Playlist}", playlist.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(new { status = "success", cleared = cleared });
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Debug & Monitoring
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets endpoint usage statistics from the log file.
|
|
||||||
/// GET /debug/endpoint-usage?api_key=YOUR_KEY
|
|
||||||
/// Optional query params: top=50 (default 100), since=2024-01-01
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("debug/endpoint-usage")]
|
|
||||||
[ServiceFilter(typeof(ApiKeyAuthFilter))]
|
|
||||||
public async Task<IActionResult> GetEndpointUsage(
|
|
||||||
[FromQuery] int top = 100,
|
|
||||||
[FromQuery] string? since = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var logFile = "/app/cache/endpoint-usage/endpoints.csv";
|
|
||||||
|
|
||||||
if (!System.IO.File.Exists(logFile))
|
|
||||||
{
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
message = "No endpoint usage data collected yet",
|
|
||||||
endpoints = Array.Empty<object>()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var lines = await System.IO.File.ReadAllLinesAsync(logFile);
|
|
||||||
|
|
||||||
// Parse CSV and filter by date if provided
|
|
||||||
DateTime? sinceDate = null;
|
|
||||||
if (!string.IsNullOrEmpty(since) && DateTime.TryParse(since, out var parsedDate))
|
|
||||||
{
|
|
||||||
sinceDate = parsedDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
var entries = lines
|
|
||||||
.Select(line => line.Split(','))
|
|
||||||
.Where(parts => parts.Length >= 3)
|
|
||||||
.Where(parts => !sinceDate.HasValue ||
|
|
||||||
(DateTime.TryParse(parts[0], out var entryDate) && entryDate >= sinceDate.Value))
|
|
||||||
.Select(parts => new
|
|
||||||
{
|
|
||||||
Timestamp = parts[0],
|
|
||||||
Method = parts.Length > 1 ? parts[1] : "",
|
|
||||||
Path = parts.Length > 2 ? parts[2] : "",
|
|
||||||
Query = parts.Length > 3 ? parts[3] : ""
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// Group by path and count
|
|
||||||
var pathCounts = entries
|
|
||||||
.GroupBy(e => new { e.Method, e.Path })
|
|
||||||
.Select(g => new
|
|
||||||
{
|
|
||||||
Method = g.Key.Method,
|
|
||||||
Path = g.Key.Path,
|
|
||||||
Count = g.Count(),
|
|
||||||
FirstSeen = g.Min(e => e.Timestamp),
|
|
||||||
LastSeen = g.Max(e => e.Timestamp)
|
|
||||||
})
|
|
||||||
.OrderByDescending(x => x.Count)
|
|
||||||
.Take(top)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return Ok(new
|
|
||||||
{
|
|
||||||
totalRequests = entries.Count,
|
|
||||||
uniqueEndpoints = pathCounts.Count,
|
|
||||||
topEndpoints = pathCounts,
|
|
||||||
logFile = logFile,
|
|
||||||
logSize = new FileInfo(logFile).Length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to get endpoint usage");
|
|
||||||
return StatusCode(500, new { error = ex.Message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears the endpoint usage log file.
|
|
||||||
/// DELETE /debug/endpoint-usage?api_key=YOUR_KEY
|
|
||||||
/// </summary>
|
|
||||||
[HttpDelete("debug/endpoint-usage")]
|
|
||||||
[ServiceFilter(typeof(ApiKeyAuthFilter))]
|
|
||||||
public IActionResult ClearEndpointUsage()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var logFile = "/app/cache/endpoint-usage/endpoints.csv";
|
|
||||||
|
|
||||||
if (System.IO.File.Exists(logFile))
|
|
||||||
{
|
|
||||||
System.IO.File.Delete(logFile);
|
|
||||||
return Ok(new { status = "success", message = "Endpoint usage log cleared" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(new { status = "success", message = "No log file to clear" });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to clear endpoint usage log");
|
|
||||||
return StatusCode(500, new { error = ex.Message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates artist match score ensuring ALL artists are present.
|
/// Calculates artist match score ensuring ALL artists are present.
|
||||||
/// Penalizes if artist counts don't match or if any artist is missing.
|
/// Penalizes if artist counts don't match or if any artist is missing.
|
||||||
|
|||||||
Reference in New Issue
Block a user