refactor(manager): replace internal CMS HTTP layer with direct CmsTrackService calls
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
@page "/cms/tracks/{Id:int}"
|
||||
@using System.Net.Http.Headers
|
||||
@using System.Net.Http.Json
|
||||
@using DeepDrftManager.Services
|
||||
@attribute [HierarchicalRoleAuthorize([SystemRoleConstants.Admin])]
|
||||
@inject ITrackService TrackService
|
||||
@inject IHttpClientFactory HttpClientFactory
|
||||
@inject IAuthSession AuthSession
|
||||
@inject ICmsTrackService CmsTrackService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
@inject NavigationManager Nav
|
||||
@@ -126,27 +124,32 @@
|
||||
_busy = true;
|
||||
try
|
||||
{
|
||||
var http = HttpClientFactory.CreateClient("DeepDrft.API");
|
||||
await AttachBearerAsync(http);
|
||||
|
||||
var payload = new
|
||||
// Re-fetch under the current scope so we mutate the DB-authoritative entity, not
|
||||
// the copy loaded at OnInitialized. Metadata-only update — EntryKey is immutable.
|
||||
var lookup = await TrackService.GetById(Id);
|
||||
if (!lookup.Success || lookup.Value is null)
|
||||
{
|
||||
TrackName = _form.TrackName,
|
||||
Artist = _form.Artist,
|
||||
Album = string.IsNullOrWhiteSpace(_form.Album) ? null : _form.Album,
|
||||
Genre = string.IsNullOrWhiteSpace(_form.Genre) ? null : _form.Genre,
|
||||
ReleaseDate = _form.ReleaseDate is { } d ? DateOnly.FromDateTime(d) : (DateOnly?)null
|
||||
};
|
||||
Snackbar.Add("Save failed — track could not be loaded.", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var response = await http.PutAsJsonAsync($"api/cms/track/{Id}", payload);
|
||||
if (response.IsSuccessStatusCode)
|
||||
var track = lookup.Value;
|
||||
track.TrackName = _form.TrackName;
|
||||
track.Artist = _form.Artist;
|
||||
track.Album = string.IsNullOrWhiteSpace(_form.Album) ? null : _form.Album;
|
||||
track.Genre = string.IsNullOrWhiteSpace(_form.Genre) ? null : _form.Genre;
|
||||
track.ReleaseDate = _form.ReleaseDate is { } d ? DateOnly.FromDateTime(d) : null;
|
||||
|
||||
var updated = await TrackService.Update(track);
|
||||
if (updated.Success)
|
||||
{
|
||||
Snackbar.Add("Track updated.", Severity.Success);
|
||||
await LoadAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"Save failed: {(int)response.StatusCode} {response.ReasonPhrase}", Severity.Error);
|
||||
var error = updated.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
Snackbar.Add($"Save failed: {error}", Severity.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -160,7 +163,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE api/cms/track/{Id} is handled by CmsDeleteController (T3 branch).
|
||||
private async Task ConfirmDelete()
|
||||
{
|
||||
if (_track is null) return;
|
||||
@@ -176,18 +178,16 @@
|
||||
_busy = true;
|
||||
try
|
||||
{
|
||||
var http = HttpClientFactory.CreateClient("DeepDrft.API");
|
||||
await AttachBearerAsync(http);
|
||||
|
||||
var response = await http.DeleteAsync($"api/cms/track/{Id}");
|
||||
if (response.IsSuccessStatusCode)
|
||||
var result = await CmsTrackService.DeleteTrackAsync(Id);
|
||||
if (result.Success)
|
||||
{
|
||||
Snackbar.Add("Track deleted.", Severity.Success);
|
||||
Nav.NavigateTo("/cms/tracks");
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"Delete failed: {(int)response.StatusCode} {response.ReasonPhrase}", Severity.Error);
|
||||
var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
Snackbar.Add($"Delete failed: {error}", Severity.Error);
|
||||
_busy = false;
|
||||
}
|
||||
}
|
||||
@@ -199,15 +199,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AttachBearerAsync(HttpClient http)
|
||||
{
|
||||
var token = await AuthSession.GetValidTokenAsync();
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TrackEditForm
|
||||
{
|
||||
public string TrackName { get; set; } = string.Empty;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
@page "/cms/tracks"
|
||||
@using System.Net
|
||||
@using System.Net.Http.Headers
|
||||
@using DeepDrftManager.Services
|
||||
@attribute [HierarchicalRoleAuthorize([SystemRoleConstants.Admin])]
|
||||
@inject ITrackService TrackService
|
||||
@inject IHttpClientFactory HttpClientFactory
|
||||
@inject IAuthSession AuthSession
|
||||
@inject ICmsTrackService CmsTrackService
|
||||
@inject IDialogService DialogService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject ILogger<TrackList> Logger
|
||||
@@ -113,20 +112,16 @@
|
||||
|
||||
try
|
||||
{
|
||||
var client = HttpClientFactory.CreateClient("DeepDrft.API");
|
||||
var token = await AuthSession.GetValidTokenAsync();
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
var response = await client.DeleteAsync($"api/cms/track/{track.Id}");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
var result = await CmsTrackService.DeleteTrackAsync(track.Id);
|
||||
if (result.Success)
|
||||
{
|
||||
Snackbar.Add($"Deleted '{track.TrackName}'.", Severity.Success);
|
||||
if (_table is not null) await _table.ReloadServerData();
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"Delete failed ({(int)response.StatusCode} {response.ReasonPhrase}).", Severity.Error);
|
||||
var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
Snackbar.Add($"Delete failed: {error}", Severity.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
@page "/cms/tracks/new"
|
||||
@using System.Net.Http.Headers
|
||||
@using System.Security.Claims
|
||||
@using DeepDrftManager.Services
|
||||
@attribute [HierarchicalRoleAuthorize([SystemRoleConstants.Admin])]
|
||||
|
||||
@inject IHttpClientFactory HttpClientFactory
|
||||
@inject ICmsTrackService CmsTrackService
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject NavigationManager Navigation
|
||||
@inject ISnackbar Snackbar
|
||||
@inject ILogger<TrackNew> Logger
|
||||
@inject IAuthSession AuthSession
|
||||
|
||||
<PageTitle>Add Track — DeepDrft CMS</PageTitle>
|
||||
|
||||
@@ -61,8 +62,8 @@
|
||||
</MudContainer>
|
||||
|
||||
@code {
|
||||
// 1 GB ceiling matches the proxy controller's RequestSizeLimit; the actual streaming
|
||||
// path means the limit caps the request, not in-memory buffering.
|
||||
// 1 GB ceiling matches DeepDrftContent's per-request limit on api/track/upload; the
|
||||
// streaming path means the limit caps the request, not in-memory buffering.
|
||||
private const long MaxUploadBytes = 1_073_741_824L;
|
||||
|
||||
private IBrowserFile? _selectedFile;
|
||||
@@ -115,41 +116,46 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
var userIdValue = authState.User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (!long.TryParse(userIdValue, out var createdByUserId))
|
||||
{
|
||||
// The page is gated by [HierarchicalRoleAuthorize(Admin)], so a missing or
|
||||
// unparseable id here is a configuration bug, not normal client state.
|
||||
Logger.LogError("Authenticated user has no parseable NameIdentifier claim: {Value}", userIdValue);
|
||||
_errorMessage = "Your session is missing a valid identifier. Please sign in again.";
|
||||
return;
|
||||
}
|
||||
|
||||
_isUploading = true;
|
||||
try
|
||||
{
|
||||
using var multipart = new MultipartFormDataContent();
|
||||
|
||||
// OpenReadStream streams chunks from the browser via the SignalR circuit;
|
||||
// wrapping in StreamContent avoids materialising the whole file in memory
|
||||
// before the proxy controller receives it.
|
||||
// OpenReadStream streams chunks from the browser via the SignalR circuit; the
|
||||
// service wraps it in StreamContent so the whole file is never materialised in
|
||||
// memory before DeepDrftContent receives it.
|
||||
await using var fileStream = _selectedFile.OpenReadStream(MaxUploadBytes);
|
||||
var fileContent = new StreamContent(fileStream);
|
||||
fileContent.Headers.ContentType = new MediaTypeHeaderValue(
|
||||
string.IsNullOrWhiteSpace(_selectedFile.ContentType) ? "audio/wav" : _selectedFile.ContentType);
|
||||
multipart.Add(fileContent, "wav", _selectedFile.Name);
|
||||
multipart.Add(new StringContent(_trackName), "trackName");
|
||||
multipart.Add(new StringContent(_artist), "artist");
|
||||
if (!string.IsNullOrWhiteSpace(_album)) multipart.Add(new StringContent(_album), "album");
|
||||
if (!string.IsNullOrWhiteSpace(_genre)) multipart.Add(new StringContent(_genre), "genre");
|
||||
if (!string.IsNullOrWhiteSpace(_releaseDate)) multipart.Add(new StringContent(_releaseDate), "releaseDate");
|
||||
|
||||
var client = HttpClientFactory.CreateClient("DeepDrft.API");
|
||||
await AttachBearerAsync(client);
|
||||
using var response = await client.PostAsync("api/cms/track", multipart);
|
||||
var result = await CmsTrackService.UploadTrackAsync(
|
||||
fileStream,
|
||||
_selectedFile.Name,
|
||||
_selectedFile.ContentType,
|
||||
_trackName,
|
||||
_artist,
|
||||
string.IsNullOrWhiteSpace(_album) ? null : _album,
|
||||
string.IsNullOrWhiteSpace(_genre) ? null : _genre,
|
||||
string.IsNullOrWhiteSpace(_releaseDate) ? null : _releaseDate,
|
||||
createdByUserId);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
if (result.Success)
|
||||
{
|
||||
Snackbar.Add($"Uploaded '{_trackName}'.", Severity.Success);
|
||||
Navigation.NavigateTo("/cms/tracks");
|
||||
return;
|
||||
}
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
_errorMessage = string.IsNullOrWhiteSpace(body)
|
||||
? $"Upload failed ({(int)response.StatusCode})."
|
||||
: $"Upload failed ({(int)response.StatusCode}): {body}";
|
||||
Logger.LogWarning("CMS upload rejected: {Status} {Body}", (int)response.StatusCode, body);
|
||||
var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
_errorMessage = $"Upload failed: {error}";
|
||||
Logger.LogWarning("CMS upload rejected: {Error}", error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -167,15 +173,6 @@
|
||||
Navigation.NavigateTo("/cms/tracks");
|
||||
}
|
||||
|
||||
private async Task AttachBearerAsync(HttpClient http)
|
||||
{
|
||||
var token = await AuthSession.GetValidTokenAsync();
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatBytes(long bytes)
|
||||
{
|
||||
const long KB = 1024;
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Headers
|
||||
@using Microsoft.AspNetCore.Components
|
||||
@inject IHttpClientFactory HttpClientFactory
|
||||
@inject IAuthSession AuthSession
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudText Typo="Typo.body1">
|
||||
Are you sure you want to delete '@TrackName'? This cannot be undone.
|
||||
</MudText>
|
||||
@if (!string.IsNullOrEmpty(_errorMessage))
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mt-3" Dense="true">@_errorMessage</MudAlert>
|
||||
}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel" Disabled="_isDeleting">Cancel</MudButton>
|
||||
<MudButton Color="Color.Error"
|
||||
Variant="Variant.Filled"
|
||||
OnClick="ConfirmAsync"
|
||||
Disabled="_isDeleting">
|
||||
@if (_isDeleting)
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="me-2" />
|
||||
<span>Deleting...</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Delete</span>
|
||||
}
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = default!;
|
||||
|
||||
[Parameter] public long TrackId { get; set; }
|
||||
[Parameter] public string TrackName { get; set; } = "";
|
||||
[Parameter] public EventCallback OnDeleted { get; set; }
|
||||
|
||||
private bool _isDeleting;
|
||||
private string? _errorMessage;
|
||||
|
||||
private async Task ConfirmAsync()
|
||||
{
|
||||
_isDeleting = true;
|
||||
_errorMessage = null;
|
||||
|
||||
try
|
||||
{
|
||||
var client = HttpClientFactory.CreateClient("DeepDrft.API");
|
||||
var token = await AuthSession.GetValidTokenAsync();
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
var response = await client.DeleteAsync($"api/cms/track/{TrackId}");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
if (OnDeleted.HasDelegate)
|
||||
{
|
||||
await OnDeleted.InvokeAsync();
|
||||
}
|
||||
MudDialog.Close(DialogResult.Ok(true));
|
||||
return;
|
||||
}
|
||||
|
||||
_errorMessage = response.StatusCode switch
|
||||
{
|
||||
System.Net.HttpStatusCode.NotFound => "Track not found. It may have already been deleted.",
|
||||
System.Net.HttpStatusCode.Unauthorized => "You are not authorized to delete this track.",
|
||||
System.Net.HttpStatusCode.Forbidden => "You are not authorized to delete this track.",
|
||||
_ => $"Delete failed ({(int)response.StatusCode})."
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_errorMessage = $"Delete failed: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDeleting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Cancel() => MudDialog.Cancel();
|
||||
}
|
||||
Reference in New Issue
Block a user