Merge branch 'p9-w1-t1-arch-lift' — BlazorBlocks data lift, project renames, initial Postgres migration
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftCli", "DeepDrftCli\DeepDrftCli.csproj", "{84844B37-FD15-4AFC-850B-DD432AA33B4C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftContent.Services", "DeepDrftContent.Services\DeepDrftContent.Services.csproj", "{169D5D3E-DAEC-46BE-98EE-CC5EBF5E3E8A}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftContent.Data", "DeepDrftContent.Data\DeepDrftContent.Data.csproj", "{169D5D3E-DAEC-46BE-98EE-CC5EBF5E3E8A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftWeb.Services", "DeepDrftWeb.Services\DeepDrftWeb.Services.csproj", "{A3DA341B-589E-4705-AB66-6B22652A9B36}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftData", "DeepDrftData\DeepDrftData.csproj", "{A3DA341B-589E-4705-AB66-6B22652A9B36}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetBlocks", "C:\lib\NetBlocks\NetBlocks.csproj", "{41FC69D0-F60D-41B4-AA41-C2382C83DFE8}"
|
||||
EndProject
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DeepDrftModels\DeepDrftModels.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftWeb.Services\DeepDrftWeb.Services.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftContent.Services\DeepDrftContent.Services.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftData\DeepDrftData.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftContent.Data\DeepDrftContent.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+11
-8
@@ -3,10 +3,11 @@ using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DeepDrftWeb.Services.Data;
|
||||
using DeepDrftWeb.Services.Repositories;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Services.Processors;
|
||||
using DeepDrftData;
|
||||
using DeepDrftData.Data;
|
||||
using DeepDrftData.Repositories;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.Processors;
|
||||
using DeepDrftCli.Services;
|
||||
using DeepDrftCli.Models;
|
||||
using NetBlocks.Utilities.Environment;
|
||||
@@ -49,11 +50,13 @@ builder.Services.AddSingleton<FileDatabase>(provider =>
|
||||
}
|
||||
});
|
||||
|
||||
// Add services
|
||||
// Add services. TrackManager fronts the BlazorBlocks data layer and implements
|
||||
// ITrackService for legacy consumers; same scoped instance backs both registrations.
|
||||
builder.Services.AddScoped<TrackRepository>();
|
||||
builder.Services.AddScoped<DeepDrftWeb.Services.ITrackService, DeepDrftWeb.Services.TrackService>();
|
||||
builder.Services.AddScoped<TrackManager>();
|
||||
builder.Services.AddScoped<ITrackService>(sp => sp.GetRequiredService<TrackManager>());
|
||||
builder.Services.AddScoped<AudioProcessor>();
|
||||
builder.Services.AddScoped<DeepDrftContent.Services.TrackService>();
|
||||
builder.Services.AddScoped<DeepDrftContent.Data.TrackService>();
|
||||
builder.Services.AddScoped<CliService>();
|
||||
builder.Services.AddScoped<GuiService>();
|
||||
|
||||
@@ -72,4 +75,4 @@ else
|
||||
// Run traditional CLI mode
|
||||
var cliService = app.Services.GetRequiredService<CliService>();
|
||||
await cliService.RunAsync(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using DeepDrftContent.Services;
|
||||
using DeepDrftContent.Data;
|
||||
using DeepDrftModels.Entities;
|
||||
using NetBlocks.Models;
|
||||
using DeepDrftCli.Utils;
|
||||
@@ -13,13 +13,13 @@ namespace DeepDrftCli.Services;
|
||||
public class CliService
|
||||
{
|
||||
private readonly ILogger<CliService> _logger;
|
||||
private readonly DeepDrftWeb.Services.ITrackService _webTrackService;
|
||||
private readonly DeepDrftContent.Services.TrackService _contentTrackService;
|
||||
private readonly DeepDrftData.ITrackService _webTrackService;
|
||||
private readonly DeepDrftContent.Data.TrackService _contentTrackService;
|
||||
|
||||
public CliService(
|
||||
ILogger<CliService> logger,
|
||||
DeepDrftWeb.Services.ITrackService webTrackService,
|
||||
DeepDrftContent.Services.TrackService contentTrackService)
|
||||
DeepDrftData.ITrackService webTrackService,
|
||||
DeepDrftContent.Data.TrackService contentTrackService)
|
||||
{
|
||||
_logger = logger;
|
||||
_webTrackService = webTrackService;
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace DeepDrftCli.Services;
|
||||
public class GuiService
|
||||
{
|
||||
private readonly ILogger<GuiService> _logger;
|
||||
private readonly DeepDrftWeb.Services.ITrackService _webTrackService;
|
||||
private readonly DeepDrftContent.Services.TrackService _contentTrackService;
|
||||
private readonly DeepDrftData.ITrackService _webTrackService;
|
||||
private readonly DeepDrftContent.Data.TrackService _contentTrackService;
|
||||
|
||||
// GUI Components
|
||||
private Window? _mainWindow;
|
||||
@@ -24,8 +24,8 @@ public class GuiService
|
||||
|
||||
public GuiService(
|
||||
ILogger<GuiService> logger,
|
||||
DeepDrftWeb.Services.ITrackService webTrackService,
|
||||
DeepDrftContent.Services.TrackService contentTrackService)
|
||||
DeepDrftData.ITrackService webTrackService,
|
||||
DeepDrftContent.Data.TrackService contentTrackService)
|
||||
{
|
||||
_logger = logger;
|
||||
_webTrackService = webTrackService;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DeepDrftModels\DeepDrftModels.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftWeb.Services\DeepDrftWeb.Services.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftData\DeepDrftData.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@rendermode InteractiveServer
|
||||
@using AuthBlocksWeb.HierarchicalAuthorize
|
||||
@using AuthBlocksWeb.Services
|
||||
@using DeepDrftWeb.Services
|
||||
@using DeepDrftData
|
||||
@using System.Net.Http.Headers
|
||||
@using System.Net.Http.Json
|
||||
@attribute [HierarchicalRoleAuthorize("Admin")]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@using System.Net
|
||||
@using System.Net.Http.Headers
|
||||
@using AuthBlocksWeb.HierarchicalAuthorize
|
||||
@using DeepDrftModels.Models
|
||||
@using Models.Common
|
||||
@attribute [HierarchicalRoleAuthorize("Admin")]
|
||||
@inject ITrackService TrackService
|
||||
@inject IHttpClientFactory HttpClientFactory
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
@using Microsoft.JSInterop
|
||||
@using DeepDrftCms
|
||||
@using DeepDrftModels.Entities
|
||||
@using DeepDrftWeb.Services
|
||||
@using DeepDrftData
|
||||
@using Models.Common
|
||||
@using MudBlazor
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Text;
|
||||
|
||||
namespace DeepDrftContent.Services.Audio;
|
||||
namespace DeepDrftContent.Data.Audio;
|
||||
|
||||
/// <summary>
|
||||
/// Service for creating WAV audio streams starting from a byte offset.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace DeepDrftContent.Services.Constants;
|
||||
namespace DeepDrftContent.Data.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Constants for FileDatabase vault names
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using IndexType = DeepDrftContent.Services.FileDatabase.Services.IndexType;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using IndexType = DeepDrftContent.Data.FileDatabase.Services.IndexType;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Abstractions;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for creating index instances
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Abstractions;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for registering media type factories
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace DeepDrftContent.Services.FileDatabase.Models;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Base interface for all index types - minimal contract
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Models;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for index data used in serialization
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Models;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Shared media type registry instance — one allocation for all factory classes in this file.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace DeepDrftContent.Services.FileDatabase.Models;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Parameters for creating a FileBinary
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace DeepDrftContent.Services.FileDatabase.Models;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Enum representing different types of media vaults
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Models;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Base metadata for media entries
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Services;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Main file database class that orchestrates multiple media vaults.
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using IndexType = DeepDrftContent.Services.FileDatabase.Services.IndexType;
|
||||
using DeepDrftContent.Data.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
using IndexType = DeepDrftContent.Data.FileDatabase.Services.IndexType;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Services;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Factory service for creating and managing indexes
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using DeepDrftContent.Data.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Services;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Enum representing different types of indexes
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Services;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Watches index files for external modifications and triggers reloads.
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Services;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract base class for media vaults that store and manage media files
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Services;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Factory for creating media vaults
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Services;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Simple dictionary-based registry for media type factories
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Utils;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Utility class for file I/O operations, matching the TypeScript file utilities
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using System.Collections;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Utils;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// A map implementation that uses structural equality for keys by serializing them to JSON.
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using System.Collections;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DeepDrftContent.Services.FileDatabase.Utils;
|
||||
namespace DeepDrftContent.Data.FileDatabase.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// A set implementation that uses structural equality for values by serializing them to JSON.
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
namespace DeepDrftContent.Services.Processors;
|
||||
namespace DeepDrftContent.Data.Processors;
|
||||
|
||||
/// <summary>
|
||||
/// Service for processing audio files and extracting metadata
|
||||
@@ -1,9 +1,9 @@
|
||||
using DeepDrftContent.Services.Constants;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Services.Processors;
|
||||
using DeepDrftContent.Data.Constants;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.Processors;
|
||||
using DeepDrftModels.Entities;
|
||||
|
||||
namespace DeepDrftContent.Services;
|
||||
namespace DeepDrftContent.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Service for managing tracks in both SQL and FileDatabase
|
||||
@@ -52,7 +52,7 @@ public class TrackService
|
||||
// Ensure tracks vault exists
|
||||
if (!_fileDatabase.HasVault(VaultConstants.Tracks))
|
||||
{
|
||||
await _fileDatabase.CreateVaultAsync(VaultConstants.Tracks, DeepDrftContent.Services.FileDatabase.Models.MediaVaultType.Audio);
|
||||
await _fileDatabase.CreateVaultAsync(VaultConstants.Tracks, DeepDrftContent.Data.FileDatabase.Models.MediaVaultType.Audio);
|
||||
}
|
||||
|
||||
// Store the audio in FileDatabase
|
||||
@@ -87,9 +87,9 @@ public class TrackService
|
||||
/// </summary>
|
||||
/// <param name="trackId">Track ID (EntryKey)</param>
|
||||
/// <returns>Audio binary or null if not found</returns>
|
||||
public async Task<DeepDrftContent.Services.FileDatabase.Models.AudioBinary?> GetAudioBinaryAsync(string trackId)
|
||||
public async Task<DeepDrftContent.Data.FileDatabase.Models.AudioBinary?> GetAudioBinaryAsync(string trackId)
|
||||
{
|
||||
return await _fileDatabase.LoadResourceAsync<DeepDrftContent.Services.FileDatabase.Models.AudioBinary>(VaultConstants.Tracks, trackId);
|
||||
return await _fileDatabase.LoadResourceAsync<DeepDrftContent.Data.FileDatabase.Models.AudioBinary>(VaultConstants.Tracks, trackId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -107,7 +107,7 @@ public class TrackService
|
||||
{
|
||||
if (!_fileDatabase.HasVault(VaultConstants.Tracks))
|
||||
{
|
||||
await _fileDatabase.CreateVaultAsync(VaultConstants.Tracks, DeepDrftContent.Services.FileDatabase.Models.MediaVaultType.Audio);
|
||||
await _fileDatabase.CreateVaultAsync(VaultConstants.Tracks, DeepDrftContent.Data.FileDatabase.Models.MediaVaultType.Audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.Audio;
|
||||
using DeepDrftContent.Services.Constants;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.Audio;
|
||||
using DeepDrftContent.Data.Constants;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
using DeepDrftContent.Middleware;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace DeepDrftContent.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
public class TrackController : ControllerBase
|
||||
{
|
||||
private readonly DeepDrftContent.Services.TrackService _trackService;
|
||||
private readonly DeepDrftContent.Data.TrackService _trackService;
|
||||
private readonly WavOffsetService _wavOffsetService;
|
||||
private readonly ILogger<TrackController> _logger;
|
||||
|
||||
@@ -19,11 +19,11 @@ public class TrackController : ControllerBase
|
||||
// AudioBinaryDto over the wire, not a WAV file path. TrackService.AddTrackFromWavAsync is
|
||||
// file-path-oriented and not applicable here. If a file-upload flow is added in future,
|
||||
// route it through TrackService instead.
|
||||
private readonly DeepDrftContent.Services.FileDatabase.Services.FileDatabase _fileDatabase;
|
||||
private readonly DeepDrftContent.Data.FileDatabase.Services.FileDatabase _fileDatabase;
|
||||
|
||||
public TrackController(
|
||||
DeepDrftContent.Services.TrackService trackService,
|
||||
DeepDrftContent.Services.FileDatabase.Services.FileDatabase fileDatabase,
|
||||
DeepDrftContent.Data.TrackService trackService,
|
||||
DeepDrftContent.Data.FileDatabase.Services.FileDatabase fileDatabase,
|
||||
WavOffsetService wavOffsetService,
|
||||
ILogger<TrackController> logger)
|
||||
{
|
||||
@@ -245,7 +245,7 @@ public class TrackController : ControllerBase
|
||||
// Direct FileDatabase write: this endpoint receives an already-processed AudioBinaryDto,
|
||||
// not a WAV file, so TrackService.AddTrackFromWavAsync does not apply. See constructor comment.
|
||||
var success = await _fileDatabase.RegisterResourceAsync(
|
||||
DeepDrftContent.Services.Constants.VaultConstants.Tracks, trackId, audioBinary);
|
||||
DeepDrftContent.Data.Constants.VaultConstants.Tracks, trackId, audioBinary);
|
||||
return success ? Ok() : BadRequest("Failed to store audio track");
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DeepDrftModels\DeepDrftModels.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftContent.Services\DeepDrftContent.Services.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftContent.Data\DeepDrftContent.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using DeepDrftContent;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
using DeepDrftContent.Middleware;
|
||||
using DeepDrftContent.Models;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using DeepDrftContent.Services;
|
||||
using DeepDrftContent.Services.Audio;
|
||||
using DeepDrftContent.Services.Constants;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Services.Processors;
|
||||
using DeepDrftContent.Data;
|
||||
using DeepDrftContent.Data.Audio;
|
||||
using DeepDrftContent.Data.Constants;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.Processors;
|
||||
using DeepDrftContent.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetBlocks.Utilities.Environment;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using Data.Data.Configurations;
|
||||
using DeepDrftModels.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DeepDrftData.Data.Configurations;
|
||||
|
||||
public class TrackConfiguration : BaseEntityConfiguration<TrackEntity>
|
||||
{
|
||||
public override void Configure(EntityTypeBuilder<TrackEntity> builder)
|
||||
{
|
||||
// Wires up Id PK + audit columns (CreatedAt, UpdatedAt, IsDeleted) and the IsDeleted index.
|
||||
base.Configure(builder);
|
||||
|
||||
builder.ToTable("track");
|
||||
|
||||
// Map the base audit columns to the snake_case naming the rest of the schema uses.
|
||||
builder.Property(e => e.Id).HasColumnName("id");
|
||||
builder.Property(e => e.CreatedAt).HasColumnName("created_at");
|
||||
builder.Property(e => e.UpdatedAt).HasColumnName("updated_at");
|
||||
builder.Property(e => e.IsDeleted).HasColumnName("is_deleted");
|
||||
|
||||
builder.Property(e => e.EntryKey)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("entry_key");
|
||||
|
||||
builder.Property(e => e.TrackName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnName("track_name");
|
||||
|
||||
builder.Property(e => e.Artist)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnName("artist");
|
||||
|
||||
builder.Property(e => e.Album)
|
||||
.HasMaxLength(200)
|
||||
.HasColumnName("album");
|
||||
|
||||
builder.Property(e => e.Genre)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("genre");
|
||||
|
||||
builder.Property(e => e.ReleaseDate)
|
||||
.HasColumnName("release_date");
|
||||
|
||||
builder.Property(e => e.ImagePath)
|
||||
.HasMaxLength(500)
|
||||
.HasColumnName("image_path");
|
||||
|
||||
builder.Property(e => e.CreatedByUserId)
|
||||
.HasColumnName("created_by_user_id");
|
||||
|
||||
// Explicit index on is_deleted so soft-delete global query filters are
|
||||
// not full table scans. base.Configure may or may not add this depending
|
||||
// on the BlazorBlocks.Data version; declaring it here guarantees it.
|
||||
builder.HasIndex(e => e.IsDeleted).HasDatabaseName("IX_track_is_deleted");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftWeb.Services.Data.Configurations;
|
||||
using DeepDrftData.Data.Configurations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DeepDrftWeb.Services.Data;
|
||||
namespace DeepDrftData.Data;
|
||||
|
||||
public class DeepDrftContext : DbContext
|
||||
{
|
||||
@@ -15,7 +15,7 @@ public class DeepDrftContext : DbContext
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
|
||||
modelBuilder.ApplyConfiguration(new TrackConfiguration());
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace DeepDrftWeb.Services.Data;
|
||||
namespace DeepDrftData.Data;
|
||||
|
||||
public class DeepDrftContextFactory : IDesignTimeDbContextFactory<DeepDrftContext>
|
||||
{
|
||||
@@ -19,4 +19,4 @@ public class DeepDrftContextFactory : IDesignTimeDbContextFactory<DeepDrftContex
|
||||
|
||||
return new DeepDrftContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
</PackageReference>
|
||||
<!-- Npgsql 10.0.1 requires Microsoft.EntityFrameworkCore >= 10.0.4; keep in sync -->
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
||||
<PackageReference Include="Cerebellum.BlazorBlocks.Data" Version="10.3.30" />
|
||||
<PackageReference Include="Cerebellum.BlazorBlocks.Data.Postgres" Version="10.3.30" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -1,8 +1,8 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftModels.Models;
|
||||
using Models.Common;
|
||||
using NetBlocks.Models;
|
||||
|
||||
namespace DeepDrftWeb.Services;
|
||||
namespace DeepDrftData;
|
||||
|
||||
public interface ITrackService
|
||||
{
|
||||
+21
-4
@@ -1,6 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DeepDrftWeb.Services.Data;
|
||||
using DeepDrftData.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
@@ -9,11 +9,11 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DeepDrftWeb.Migrations
|
||||
namespace DeepDrftData.Migrations
|
||||
{
|
||||
[DbContext(typeof(DeepDrftContext))]
|
||||
[Migration("20260518035137_AddCreatedByUserId")]
|
||||
partial class AddCreatedByUserId
|
||||
[Migration("20260519021400_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
@@ -45,6 +45,10 @@ namespace DeepDrftWeb.Migrations
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("artist");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<long?>("CreatedByUserId")
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("created_by_user_id");
|
||||
@@ -65,6 +69,12 @@ namespace DeepDrftWeb.Migrations
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("image_path");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("is_deleted");
|
||||
|
||||
b.Property<DateOnly?>("ReleaseDate")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("release_date");
|
||||
@@ -75,8 +85,15 @@ namespace DeepDrftWeb.Migrations
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("track_name");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IsDeleted")
|
||||
.HasDatabaseName("IX_track_is_deleted");
|
||||
|
||||
b.ToTable("track", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
+12
-3
@@ -4,10 +4,10 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DeepDrftWeb.Migrations
|
||||
namespace DeepDrftData.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Initial : Migration
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
@@ -24,12 +24,21 @@ namespace DeepDrftWeb.Migrations
|
||||
album = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
genre = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
release_date = table.Column<DateOnly>(type: "date", nullable: true),
|
||||
image_path = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true)
|
||||
image_path = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||
created_by_user_id = table.Column<long>(type: "bigint", nullable: true),
|
||||
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
is_deleted = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_track", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_track_is_deleted",
|
||||
table: "track",
|
||||
column: "is_deleted");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
+19
-2
@@ -1,6 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DeepDrftWeb.Services.Data;
|
||||
using DeepDrftData.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
@@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DeepDrftWeb.Migrations
|
||||
namespace DeepDrftData.Migrations
|
||||
{
|
||||
[DbContext(typeof(DeepDrftContext))]
|
||||
partial class DeepDrftContextModelSnapshot : ModelSnapshot
|
||||
@@ -42,6 +42,10 @@ namespace DeepDrftWeb.Migrations
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("artist");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<long?>("CreatedByUserId")
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("created_by_user_id");
|
||||
@@ -62,6 +66,12 @@ namespace DeepDrftWeb.Migrations
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("image_path");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("is_deleted");
|
||||
|
||||
b.Property<DateOnly?>("ReleaseDate")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("release_date");
|
||||
@@ -72,8 +82,15 @@ namespace DeepDrftWeb.Migrations
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("track_name");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IsDeleted")
|
||||
.HasDatabaseName("IX_track_is_deleted");
|
||||
|
||||
b.ToTable("track", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
@@ -0,0 +1,31 @@
|
||||
using Data.Data.Repositories;
|
||||
using Data.Errors;
|
||||
using DeepDrftData.Data;
|
||||
using DeepDrftModels.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DeepDrftData.Repositories;
|
||||
|
||||
public class TrackRepository : Repository<DeepDrftContext, TrackEntity>
|
||||
{
|
||||
public TrackRepository(
|
||||
DeepDrftContext context,
|
||||
ILogger<Repository<DeepDrftContext, TrackEntity>> logger,
|
||||
IDbExceptionClassifier? classifier = null)
|
||||
: base(context, logger, classifier: classifier)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UpdateEntity(TrackEntity target, TrackEntity source)
|
||||
{
|
||||
base.UpdateEntity(target, source); // copies CreatedAt, UpdatedAt, IsDeleted
|
||||
target.EntryKey = source.EntryKey;
|
||||
target.TrackName = source.TrackName;
|
||||
target.Artist = source.Artist;
|
||||
target.Album = source.Album;
|
||||
target.Genre = source.Genre;
|
||||
target.ReleaseDate = source.ReleaseDate;
|
||||
target.ImagePath = source.ImagePath;
|
||||
target.CreatedByUserId = source.CreatedByUserId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using DeepDrftModels.DTOs;
|
||||
using DeepDrftModels.Entities;
|
||||
using Models.Converters;
|
||||
|
||||
namespace DeepDrftData;
|
||||
|
||||
/// <summary>
|
||||
/// Static entity ↔ DTO converter consumed by the BlazorBlocks Manager base class.
|
||||
/// The DTO side mirrors the entity field-for-field; the audit columns
|
||||
/// (CreatedAt, UpdatedAt) come from BaseEntity / BaseModel.
|
||||
/// IsDeleted does not round-trip — soft-deleted rows are not exposed via the model.
|
||||
/// </summary>
|
||||
public class TrackConverter : IEntityToModelConverter<TrackEntity, TrackDto>
|
||||
{
|
||||
public static TrackDto Convert(TrackEntity entity) => new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
CreatedAt = entity.CreatedAt,
|
||||
UpdatedAt = entity.UpdatedAt,
|
||||
EntryKey = entity.EntryKey,
|
||||
TrackName = entity.TrackName,
|
||||
Artist = entity.Artist,
|
||||
Album = entity.Album,
|
||||
Genre = entity.Genre,
|
||||
ReleaseDate = entity.ReleaseDate,
|
||||
ImagePath = entity.ImagePath,
|
||||
CreatedByUserId = entity.CreatedByUserId
|
||||
};
|
||||
|
||||
public static TrackEntity Convert(TrackDto model) => new()
|
||||
{
|
||||
Id = model.Id,
|
||||
CreatedAt = model.CreatedAt,
|
||||
UpdatedAt = model.UpdatedAt,
|
||||
EntryKey = model.EntryKey,
|
||||
TrackName = model.TrackName,
|
||||
Artist = model.Artist,
|
||||
Album = model.Album,
|
||||
Genre = model.Genre,
|
||||
ReleaseDate = model.ReleaseDate,
|
||||
ImagePath = model.ImagePath,
|
||||
CreatedByUserId = model.CreatedByUserId
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using Data.Managers;
|
||||
using DeepDrftData.Repositories;
|
||||
using DeepDrftModels.DTOs;
|
||||
using DeepDrftModels.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Models.Common;
|
||||
using NetBlocks.Models;
|
||||
|
||||
namespace DeepDrftData;
|
||||
|
||||
/// <summary>
|
||||
/// SQL-side track orchestrator built on the BlazorBlocks Manager base. Two surfaces coexist:
|
||||
///
|
||||
/// - The DTO-shaped IManager surface (GetById → TrackDto, etc.) inherited from Manager<>.
|
||||
/// - The entity-shaped ITrackService surface retained for backward compatibility with the
|
||||
/// web host, CMS, and CLI — all existing controllers and pages inject ITrackService and
|
||||
/// expect TrackEntity-typed results. The two GetById overloads conflict on signature, so
|
||||
/// ITrackService.GetById is implemented explicitly; the base Manager.Delete satisfies
|
||||
/// both interfaces because the signatures align.
|
||||
/// </summary>
|
||||
public class TrackManager
|
||||
: Manager<TrackEntity, TrackDto, TrackRepository, TrackConverter>, ITrackService
|
||||
{
|
||||
public TrackManager(
|
||||
TrackRepository repository,
|
||||
ILogger<Manager<TrackEntity, TrackDto, TrackRepository, TrackConverter>> logger)
|
||||
: base(repository, logger)
|
||||
{
|
||||
}
|
||||
|
||||
// --- ITrackService implementation (entity-space; calls Repository directly) ---
|
||||
|
||||
// Explicit interface implementation — IManager.GetById returns ResultContainer<TrackDto>
|
||||
// (inherited from Manager<>), so this entity-typed overload cannot coexist as a public
|
||||
// member with the same name. Callers always inject ITrackService, so the explicit impl
|
||||
// resolves correctly at the call site.
|
||||
async Task<ResultContainer<TrackEntity?>> ITrackService.GetById(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entity = await Repository.GetByIdAsync(id);
|
||||
return ResultContainer<TrackEntity?>.CreatePassResult(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<TrackEntity?>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<List<TrackEntity>>> GetAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
var entities = await Repository.GetAllAsync();
|
||||
return ResultContainer<List<TrackEntity>>.CreatePassResult(entities.ToList());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<List<TrackEntity>>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<PagedResult<TrackEntity>>> GetPaged(
|
||||
int pageNumber,
|
||||
int pageSize,
|
||||
string? sortColumn,
|
||||
bool sortDescending,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var parameters = new PagingParameters<TrackEntity>
|
||||
{
|
||||
Page = pageNumber,
|
||||
PageSize = pageSize,
|
||||
IsDescending = sortDescending,
|
||||
OrderBy = sortColumn switch
|
||||
{
|
||||
"TrackName" => e => e.TrackName,
|
||||
"Artist" => e => e.Artist,
|
||||
"Album" => e => (object)(e.Album ?? string.Empty),
|
||||
"Genre" => e => (object)(e.Genre ?? string.Empty),
|
||||
"ReleaseDate" => e => (object)(e.ReleaseDate ?? DateOnly.MaxValue),
|
||||
_ => e => e.Id
|
||||
}
|
||||
};
|
||||
|
||||
var page = await Repository.GetPagedAsync(parameters);
|
||||
return ResultContainer<PagedResult<TrackEntity>>.CreatePassResult(page);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<PagedResult<TrackEntity>>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<TrackEntity>> Create(TrackEntity newTrack)
|
||||
{
|
||||
try
|
||||
{
|
||||
var added = await Repository.AddAsync(newTrack);
|
||||
return ResultContainer<TrackEntity>.CreatePassResult(added);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<TrackEntity>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// Manager<>.Update takes TrackDto and returns Result; this Update keeps the
|
||||
// entity-typed contract callers expect and returns the post-update entity for the
|
||||
// existing CMS edit flow that reads back the persisted values.
|
||||
/// <summary>
|
||||
/// Updates the track's metadata fields and returns the DB-authoritative entity.
|
||||
/// The caller's <paramref name="track"/> object has its <c>UpdatedAt</c> field
|
||||
/// mutated in place by <see cref="TrackRepository.UpdateAsync"/>; do not reuse it.
|
||||
/// </summary>
|
||||
public async Task<ResultContainer<TrackEntity>> Update(TrackEntity track)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Repository.UpdateAsync(track);
|
||||
var updated = await Repository.GetByIdAsync(track.Id);
|
||||
return updated is not null
|
||||
? ResultContainer<TrackEntity>.CreatePassResult(updated)
|
||||
: ResultContainer<TrackEntity>.CreateFailResult("Track not found after update.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<TrackEntity>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete(long) is inherited from Manager<> — its Task<Result> signature already
|
||||
// satisfies ITrackService.Delete, and the base implementation performs the soft delete
|
||||
// via Repository.DeleteAsync. No override needed.
|
||||
}
|
||||
+2
-2
@@ -14,9 +14,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftTests", "DeepDrftTe
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftCli", "DeepDrftCli\DeepDrftCli.csproj", "{E4C0ADD6-1264-47A5-98E1-0843AD14B7F9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftWeb.Services", "DeepDrftWeb.Services\DeepDrftWeb.Services.csproj", "{1D1CE905-DAD0-4E93-9B09-326E8EC05877}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftData", "DeepDrftData\DeepDrftData.csproj", "{1D1CE905-DAD0-4E93-9B09-326E8EC05877}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftContent.Services", "DeepDrftContent.Services\DeepDrftContent.Services.csproj", "{4D025326-7F27-4C42-BE0F-92C1E64E0696}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftContent.Data", "DeepDrftContent.Data\DeepDrftContent.Data.csproj", "{4D025326-7F27-4C42-BE0F-92C1E64E0696}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftCms", "DeepDrftCms\DeepDrftCms.csproj", "{81F1D47F-F892-45FB-9E35-D7775805FFD3}"
|
||||
EndProject
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
namespace DeepDrftModels.DTOs;
|
||||
using Models.Models;
|
||||
|
||||
public class TrackDto
|
||||
namespace DeepDrftModels.DTOs;
|
||||
|
||||
// Inherits Id, CreatedAt, UpdatedAt from BaseModel (Cerebellum.BlazorBlocks.Models).
|
||||
// BlazorBlocks's Manager<> generic constraint requires `new()` on the model type, which
|
||||
// disqualifies `required` properties (the `new()` constraint and required members do not
|
||||
// compose). EntryKey/TrackName/Artist therefore drop `required` here — the TrackEntity
|
||||
// side remains required, and TrackConverter assigns every field on the round-trip so an
|
||||
// empty default is never observable in production code paths.
|
||||
public class TrackDto : BaseModel
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public required string EntryKey { get; set; }
|
||||
public required string TrackName { get; set; }
|
||||
public required string Artist { get; set; }
|
||||
public string EntryKey { get; set; } = string.Empty;
|
||||
public string TrackName { get; set; } = string.Empty;
|
||||
public string Artist { get; set; } = string.Empty;
|
||||
public string? Album { get; set; }
|
||||
public string? Genre { get; set; }
|
||||
public DateOnly? ReleaseDate { get; set; }
|
||||
public string? ImagePath { get; set; }
|
||||
}
|
||||
public long? CreatedByUserId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Cerebellum.NetBlocks" Version="10.3.30" />
|
||||
<PackageReference Include="Cerebellum.BlazorBlocks.Models" Version="10.3.30" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
namespace DeepDrftModels.Entities;
|
||||
using Models.Entities;
|
||||
|
||||
public class TrackEntity
|
||||
namespace DeepDrftModels.Entities;
|
||||
|
||||
// Inherits Id, CreatedAt, UpdatedAt, IsDeleted from BaseEntity (Cerebellum.BlazorBlocks.Models).
|
||||
// BaseEntity ships the audit columns but does not declare IEntity itself, so subclasses
|
||||
// declare it explicitly to satisfy the generic constraints on Repository<>/Manager<>/etc.
|
||||
public class TrackEntity : BaseEntity, IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public required string EntryKey { get; set; }
|
||||
public required string TrackName { get; set; }
|
||||
public required string Artist { get; set; }
|
||||
@@ -11,4 +15,4 @@ public class TrackEntity
|
||||
public DateOnly? ReleaseDate { get; set; }
|
||||
public string? ImagePath { get; set; }
|
||||
public long? CreatedByUserId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
namespace DeepDrftModels.Models;
|
||||
|
||||
public class PagedResult<T>
|
||||
{
|
||||
public IEnumerable<T> Items { get; set; } = new List<T>();
|
||||
public int TotalCount { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages => PageSize > 0 ? (int)Math.Ceiling((double)TotalCount / PageSize) : 0;
|
||||
public bool HasNextPage => Page < TotalPages;
|
||||
public bool HasPreviousPage => Page > 1;
|
||||
|
||||
public PagedResult()
|
||||
{
|
||||
}
|
||||
|
||||
public static PagedResult<T> From<TOther>(PagedResult<TOther> other, IEnumerable<T> items)
|
||||
{
|
||||
return new PagedResult<T>()
|
||||
{
|
||||
Items = items.ToList(),
|
||||
Page = other.Page,
|
||||
PageSize = other.PageSize,
|
||||
TotalCount = other.TotalCount,
|
||||
};
|
||||
}
|
||||
|
||||
public PagedResult(IEnumerable<T> items, int totalCount, int page, int pageSize)
|
||||
{
|
||||
Items = items.ToList();
|
||||
TotalCount = totalCount;
|
||||
Page = page;
|
||||
PageSize = pageSize;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace DeepDrftModels.Models;
|
||||
|
||||
public class PagingParameters
|
||||
{
|
||||
private const int _maxPageSize = 100;
|
||||
private int _pageSize = 20;
|
||||
|
||||
public int Page { get; set; } = 1;
|
||||
|
||||
public int PageSize
|
||||
{
|
||||
get => _pageSize;
|
||||
set => _pageSize = value > _maxPageSize ? _maxPageSize : value;
|
||||
}
|
||||
}
|
||||
|
||||
public class PagingParameters<T> : PagingParameters
|
||||
{
|
||||
public Expression<Func<T, object>>? OrderBy { get; set; }
|
||||
public bool IsDescending { get; set; } = false;
|
||||
|
||||
public int Skip => (Page - 1) * PageSize;
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DeepDrftContent.Services\DeepDrftContent.Services.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftContent.Data\DeepDrftContent.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using DeepDrftContent.Data.FileDatabase.Abstractions;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Services;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Services;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using DeepDrftContent.Services.FileDatabase.Models;
|
||||
using DeepDrftContent.Services.FileDatabase.Utils;
|
||||
using DeepDrftContent.Data.FileDatabase.Models;
|
||||
using DeepDrftContent.Data.FileDatabase.Utils;
|
||||
|
||||
namespace DeepDrftTests;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftModels.Models;
|
||||
using Models.Common;
|
||||
using NetBlocks.Models;
|
||||
using System.Text.Json;
|
||||
using System.Web;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftModels.Models;
|
||||
using DeepDrftWeb.Client.Services;
|
||||
using DeepDrftWeb.Client.ViewModels;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Models.Common;
|
||||
|
||||
namespace DeepDrftWeb.Client.Pages;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftModels.Models;
|
||||
using DeepDrftWeb.Client.Clients;
|
||||
using Models.Common;
|
||||
|
||||
namespace DeepDrftWeb.Client.ViewModels;
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DeepDrftWeb.Services.Data.Configurations;
|
||||
|
||||
public class TrackConfiguration : IEntityTypeConfiguration<TrackEntity>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TrackEntity> builder)
|
||||
{
|
||||
builder.ToTable("track");
|
||||
|
||||
builder.HasKey(x => x.Id);
|
||||
|
||||
builder.Property(x => x.Id)
|
||||
.HasColumnName("id")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(x => x.EntryKey)
|
||||
.HasColumnName("entry_key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(x => x.TrackName)
|
||||
.HasColumnName("track_name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(x => x.Artist)
|
||||
.HasColumnName("artist")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(x => x.Album)
|
||||
.HasColumnName("album")
|
||||
.HasMaxLength(200);
|
||||
|
||||
builder.Property(x => x.Genre)
|
||||
.HasColumnName("genre")
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(x => x.ReleaseDate)
|
||||
.HasColumnName("release_date");
|
||||
|
||||
builder.Property(x => x.ImagePath)
|
||||
.HasColumnName("image_path")
|
||||
.HasMaxLength(500);
|
||||
|
||||
builder.Property(x => x.CreatedByUserId)
|
||||
.HasColumnName("created_by_user_id");
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DeepDrftWeb.Services.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DeepDrftWeb.Migrations
|
||||
{
|
||||
[DbContext(typeof(DeepDrftContext))]
|
||||
[Migration("20260518025102_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("DeepDrftModels.Entities.TrackEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Album")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("album");
|
||||
|
||||
b.Property<string>("Artist")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("artist");
|
||||
|
||||
b.Property<string>("EntryKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("entry_key");
|
||||
|
||||
b.Property<string>("Genre")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("genre");
|
||||
|
||||
b.Property<string>("ImagePath")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("image_path");
|
||||
|
||||
b.Property<DateOnly?>("ReleaseDate")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("release_date");
|
||||
|
||||
b.Property<string>("TrackName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("track_name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("track", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DeepDrftWeb.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddCreatedByUserId : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "created_by_user_id",
|
||||
table: "track",
|
||||
type: "bigint",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "created_by_user_id",
|
||||
table: "track");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftModels.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DeepDrftWeb.Services.Data;
|
||||
|
||||
namespace DeepDrftWeb.Services.Repositories;
|
||||
|
||||
public class TrackRepository
|
||||
{
|
||||
private readonly DeepDrftContext _db;
|
||||
|
||||
public TrackRepository(DeepDrftContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<TrackEntity?> GetById(long id)
|
||||
{
|
||||
return await _db.Tracks.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<List<TrackEntity>> GetAll()
|
||||
{
|
||||
return await _db.Tracks.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<PagedResult<TrackEntity>> GetPage(PagingParameters<TrackEntity> pageParameters, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Two separate queries with no transaction: count and page can be momentarily inconsistent
|
||||
// under concurrent writes. Acceptable — write volume is low and the UI is read-only.
|
||||
// If filtering is added, the count query must be updated to apply the same filter.
|
||||
var count = await _db.Tracks.CountAsync(cancellationToken);
|
||||
|
||||
var orderBy = pageParameters.OrderBy ?? (t => t.Id);
|
||||
var ordered = pageParameters.IsDescending
|
||||
? _db.Tracks.OrderByDescending(orderBy)
|
||||
: _db.Tracks.OrderBy(orderBy);
|
||||
|
||||
var page = await ordered
|
||||
.Skip(pageParameters.Skip)
|
||||
.Take(pageParameters.PageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return new PagedResult<TrackEntity>(page, count, pageParameters.Page, pageParameters.PageSize);
|
||||
}
|
||||
|
||||
public async Task<TrackEntity> Create(TrackEntity newTrack)
|
||||
{
|
||||
var track = _db.Tracks.Add(newTrack);
|
||||
await _db.SaveChangesAsync();
|
||||
return track.Entity;
|
||||
}
|
||||
|
||||
public async Task<TrackEntity> Update(TrackEntity track)
|
||||
{
|
||||
var trackEntity = await GetById(track.Id);
|
||||
|
||||
if (trackEntity == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Track not found: {track.Id}");
|
||||
}
|
||||
|
||||
trackEntity.Album = track.Album;
|
||||
trackEntity.Artist = track.Artist;
|
||||
trackEntity.Genre = track.Genre;
|
||||
trackEntity.ImagePath = track.ImagePath;
|
||||
trackEntity.EntryKey = track.EntryKey;
|
||||
trackEntity.ReleaseDate = track.ReleaseDate;
|
||||
trackEntity.TrackName = track.TrackName;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return trackEntity;
|
||||
}
|
||||
|
||||
public async Task Delete(long id)
|
||||
{
|
||||
var track = await GetById(id);
|
||||
if (track != null)
|
||||
{
|
||||
_db.Tracks.Remove(track);
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftModels.Models;
|
||||
using DeepDrftWeb.Services.Data;
|
||||
using DeepDrftWeb.Services.Repositories;
|
||||
using NetBlocks.Models;
|
||||
|
||||
namespace DeepDrftWeb.Services;
|
||||
|
||||
public class TrackService : ITrackService
|
||||
{
|
||||
private readonly TrackRepository _repository;
|
||||
|
||||
public TrackService(TrackRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<TrackEntity?>> GetById(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var track = await _repository.GetById(id);
|
||||
return ResultContainer<TrackEntity?>.CreatePassResult(track);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<TrackEntity?>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<List<TrackEntity>>> GetAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tracks = await _repository.GetAll();
|
||||
return ResultContainer<List<TrackEntity>>.CreatePassResult(tracks);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<List<TrackEntity>>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<PagedResult<TrackEntity>>> GetPaged(int pageNumber, int pageSize, string? sortColumn, bool sortDescending, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var parameters = new PagingParameters<TrackEntity>()
|
||||
{
|
||||
Page = pageNumber,
|
||||
PageSize = pageSize,
|
||||
IsDescending = sortDescending
|
||||
};
|
||||
|
||||
if (sortColumn != null)
|
||||
{
|
||||
switch (sortColumn)
|
||||
{
|
||||
case "TrackName":
|
||||
parameters.OrderBy = entity => entity.TrackName;
|
||||
break;
|
||||
case "Artist":
|
||||
parameters.OrderBy = entity => entity.Artist;
|
||||
break;
|
||||
case "Album":
|
||||
parameters.OrderBy = entity => entity.Album ?? "";
|
||||
break;
|
||||
case "ReleaseDate":
|
||||
parameters.OrderBy = entity => entity.ReleaseDate ?? DateOnly.MaxValue;
|
||||
break;
|
||||
case "Genre":
|
||||
parameters.OrderBy = entity => entity.Genre ?? "";
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var page = await _repository.GetPage(parameters, cancellationToken);
|
||||
return ResultContainer<PagedResult<TrackEntity>>.CreatePassResult(page);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<PagedResult<TrackEntity>>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<TrackEntity>> Create(TrackEntity newTrack)
|
||||
{
|
||||
try
|
||||
{
|
||||
var track = await _repository.Create(newTrack);
|
||||
return ResultContainer<TrackEntity>.CreatePassResult(track);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<TrackEntity>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ResultContainer<TrackEntity>> Update(TrackEntity track)
|
||||
{
|
||||
try
|
||||
{
|
||||
var updatedTrack = await _repository.Update(track);
|
||||
return ResultContainer<TrackEntity>.CreatePassResult(updatedTrack);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ResultContainer<TrackEntity>.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result> Delete(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _repository.Delete(id);
|
||||
return Result.CreatePassResult();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return Result.CreateFailResult(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using DeepDrftWeb.Services;
|
||||
using DeepDrftData;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DeepDrftData;
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftWeb.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NetBlocks.Models;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Claims;
|
||||
using DeepDrftData;
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftWeb.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace DeepDrftWeb.Controllers;
|
||||
/// <summary>
|
||||
/// CMS upload surface. Proxies a WAV + metadata multipart form to DeepDrftContent's
|
||||
/// POST api/track/upload, then persists the returned unpersisted TrackEntity to SQL via
|
||||
/// ITrackService.Create. DeepDrftWeb intentionally does not reference DeepDrftContent.Services
|
||||
/// ITrackService.Create. DeepDrftWeb intentionally does not reference DeepDrftContent.Data
|
||||
/// (CMS-PLAN §5, Option B) — all vault access is over HTTP.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using DeepDrftModels.Entities;
|
||||
using DeepDrftModels.Models;
|
||||
using DeepDrftWeb.Services;
|
||||
using DeepDrftData;
|
||||
using DeepDrftModels.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Models.Common;
|
||||
using NetBlocks.Models;
|
||||
|
||||
namespace DeepDrftWeb.Controllers;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<ProjectReference Include="..\DeepDrftCms\DeepDrftCms.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftModels\DeepDrftModels.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftWeb.Client\DeepDrftWeb.Client.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftWeb.Services\DeepDrftWeb.Services.csproj" />
|
||||
<ProjectReference Include="..\DeepDrftData\DeepDrftData.csproj" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
+17
-12
@@ -1,6 +1,7 @@
|
||||
using DeepDrftWeb.Services.Data;
|
||||
using DeepDrftWeb.Services.Repositories;
|
||||
using DeepDrftWeb.Services;
|
||||
using DeepDrftData;
|
||||
using DeepDrftData.Data;
|
||||
using DeepDrftData.Repositories;
|
||||
using DeepDrftWeb.Services; // DarkModeService namespace (within this host project)
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DeepDrftWeb;
|
||||
@@ -26,10 +27,14 @@ public static class Startup
|
||||
.AddHttpContextAccessor()
|
||||
.AddScoped<DarkModeService>();
|
||||
|
||||
// Add Track services
|
||||
// Add Track services. TrackManager implements ITrackService for backward compatibility
|
||||
// with controllers and CMS pages that inject the interface; resolving ITrackService
|
||||
// returns the same scoped TrackManager instance so the manager surface (DTO-space)
|
||||
// and the service surface (entity-space) share state.
|
||||
builder.Services
|
||||
.AddScoped<TrackRepository>()
|
||||
.AddScoped<ITrackService, TrackService>();
|
||||
.AddScoped<TrackManager>()
|
||||
.AddScoped<ITrackService>(sp => sp.GetRequiredService<TrackManager>());
|
||||
|
||||
// CMS → DeepDrftContent client. The API key is required up front (no lazy resolution)
|
||||
// so a misconfiguration surfaces at startup instead of on the first delete attempt.
|
||||
@@ -44,13 +49,13 @@ public static class Startup
|
||||
client.DefaultRequestHeaders.Add("ApiKey", contentApiKey);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static string GetKestrelUrl(this WebApplicationBuilder builder)
|
||||
{
|
||||
// Check all the places Kestrel URL can be configured
|
||||
var urls = builder.Configuration["ASPNETCORE_URLS"]
|
||||
var urls = builder.Configuration["ASPNETCORE_URLS"]
|
||||
?? builder.Configuration["urls"];
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(urls))
|
||||
{
|
||||
return urls.Split(';')[0].Trim();
|
||||
@@ -60,15 +65,15 @@ public static class Startup
|
||||
var kestrelSection = builder.Configuration.GetSection("Kestrel:Endpoints");
|
||||
var firstEndpoint = kestrelSection.GetChildren().FirstOrDefault();
|
||||
var endpointUrl = firstEndpoint?["Url"];
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(endpointUrl))
|
||||
{
|
||||
return endpointUrl;
|
||||
}
|
||||
|
||||
// ASP.NET Core defaults
|
||||
return builder.Environment.IsDevelopment()
|
||||
? "https://localhost:5001"
|
||||
return builder.Environment.IsDevelopment()
|
||||
? "https://localhost:5001"
|
||||
: "http://localhost:5000";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<!-- Machine-specific local feed — update path if not at C:\Development\AuthBlocks\nupkgs\ -->
|
||||
<add key="AuthBlocks-local" value="C:\Development\AuthBlocks\nupkgs\" />
|
||||
<add key="BlazorBlocks-local" value="C:\Development\BlazorBlocks\nupkgs\" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftContent.Services", "DeepDrftContent.Services\DeepDrftContent.Services.csproj", "{9C5D844B-46A6-4C70-B5A7-3A028795629C}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftContent.Data", "DeepDrftContent.Data\DeepDrftContent.Data.csproj", "{9C5D844B-46A6-4C70-B5A7-3A028795629C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftContent", "DeepDrftContent\DeepDrftContent.csproj", "{91F52E3A-D36D-47C3-924B-2A3CE3606B03}"
|
||||
EndProject
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftWeb.Services", "DeepDrftWeb.Services\DeepDrftWeb.Services.csproj", "{0B1ABD2E-ACC4-40B2-A80E-CC47CF209C74}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftData", "DeepDrftData\DeepDrftData.csproj", "{0B1ABD2E-ACC4-40B2-A80E-CC47CF209C74}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepDrftModels", "DeepDrftModels\DeepDrftModels.csproj", "{2507F960-A210-40A3-999D-368A6D067350}"
|
||||
EndProject
|
||||
|
||||
Reference in New Issue
Block a user