Eliminate DeepDrftPublic internal track API
This commit is contained in:
@@ -47,8 +47,7 @@ public class TrackController : ControllerBase
|
|||||||
// resolution never treats "page", "upload", or "meta" as a trackId.
|
// resolution never treats "page", "upload", or "meta" as a trackId.
|
||||||
|
|
||||||
// GET api/track/page?page=1&pageSize=20&sortColumn=TrackName&sortDescending=false
|
// GET api/track/page?page=1&pageSize=20&sortColumn=TrackName&sortDescending=false
|
||||||
// CMS metadata listing — paged read straight from SQL.
|
// Public track listing — paged read straight from SQL. Unauthenticated, like GET api/track/{id}.
|
||||||
[ApiKeyAuthorize]
|
|
||||||
[HttpGet("page")]
|
[HttpGet("page")]
|
||||||
public async Task<ActionResult> GetPage(
|
public async Task<ActionResult> GetPage(
|
||||||
[FromQuery] int page = 1,
|
[FromQuery] int page = 1,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class TrackClient
|
|||||||
bool sortDescending = false)
|
bool sortDescending = false)
|
||||||
{
|
{
|
||||||
var queryArgs = new Dictionary<string, string?>(){
|
var queryArgs = new Dictionary<string, string?>(){
|
||||||
["pageNumber"] = pageNumber.ToString(),
|
["page"] = pageNumber.ToString(),
|
||||||
["pageSize"] = pageSize.ToString()
|
["pageSize"] = pageSize.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,13 +36,18 @@ public class TrackClient
|
|||||||
string query = QueryString.Create(queryArgs).ToString();
|
string query = QueryString.Create(queryArgs).ToString();
|
||||||
|
|
||||||
var response = await _http.GetAsync($"api/track/page{query}");
|
var response = await _http.GetAsync($"api/track/page{query}");
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
var dto = JsonSerializer.Deserialize<ApiResultDto<PagedResult<TrackDto>>>(json, new JsonSerializerOptions
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return ApiResult<PagedResult<TrackDto>>.CreateFailResult($"HTTP {(int)response.StatusCode}");
|
||||||
|
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
var paged = JsonSerializer.Deserialize<PagedResult<TrackDto>>(json, new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
PropertyNameCaseInsensitive = true
|
PropertyNameCaseInsensitive = true
|
||||||
});
|
});
|
||||||
|
|
||||||
return dto?.From() ?? ApiResult<PagedResult<TrackDto>>.CreateFailResult("Failed to deserialize response");
|
return paged is not null
|
||||||
|
? ApiResult<PagedResult<TrackDto>>.CreatePassResult(paged)
|
||||||
|
: ApiResult<PagedResult<TrackDto>>.CreateFailResult("Failed to deserialize response");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,10 +7,11 @@ var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
|||||||
Console.WriteLine(builder.HostEnvironment.BaseAddress);
|
Console.WriteLine(builder.HostEnvironment.BaseAddress);
|
||||||
|
|
||||||
var contentApiUrl = builder.Configuration["ApiUrls:ContentApi"] ?? "https://localhost:7001";
|
var contentApiUrl = builder.Configuration["ApiUrls:ContentApi"] ?? "https://localhost:7001";
|
||||||
|
var sqlApiUrl = builder.Configuration["ApiUrls:SqlApi"] ?? "https://localhost:5002";
|
||||||
|
|
||||||
builder.Services.AddMudServices();
|
builder.Services.AddMudServices();
|
||||||
|
|
||||||
Startup.ConfigureApiHttpClient(builder.Services, builder.HostEnvironment.BaseAddress);
|
Startup.ConfigureApiHttpClient(builder.Services, sqlApiUrl);
|
||||||
Startup.ConfigureContentServices(builder.Services, contentApiUrl);
|
Startup.ConfigureContentServices(builder.Services, contentApiUrl);
|
||||||
Startup.ConfigureDomainServices(builder.Services);
|
Startup.ConfigureDomainServices(builder.Services);
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ public static class Startup
|
|||||||
services.AddScoped<DarkModeSettings>();
|
services.AddScoped<DarkModeSettings>();
|
||||||
services.AddScoped<DarkModeCookieService>();
|
services.AddScoped<DarkModeCookieService>();
|
||||||
|
|
||||||
// Track Client. The HTTP-backed ITrackDataService registration here is the WASM
|
// Track Client. The HTTP-backed ITrackDataService is used by both WASM and SSR
|
||||||
// default; the server host overrides it with an in-process implementation after
|
// prerender — both call DeepDrftAPI over the "DeepDrft.API" client.
|
||||||
// this method runs, so SSR prerender skips the loopback hop.
|
|
||||||
services.AddScoped<TrackClient>();
|
services.AddScoped<TrackClient>();
|
||||||
services.AddScoped<ITrackDataService, TrackClientDataService>();
|
services.AddScoped<ITrackDataService, TrackClientDataService>();
|
||||||
services.AddScoped<TracksViewModel>();
|
services.AddScoped<TracksViewModel>();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ApiUrls": {
|
"ApiUrls": {
|
||||||
"ContentApi": "https://media.deepdrft.com/"
|
"ContentApi": "https://media.deepdrft.com/",
|
||||||
|
"SqlApi": "https://api.deepdrft.com/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
using DeepDrftData;
|
|
||||||
using DeepDrftModels.DTOs;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Models.Common;
|
|
||||||
using NetBlocks.Models;
|
|
||||||
|
|
||||||
namespace DeepDrftPublic.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class TrackController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ITrackService _trackService;
|
|
||||||
|
|
||||||
public TrackController(ITrackService trackService)
|
|
||||||
{
|
|
||||||
_trackService = trackService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("page")]
|
|
||||||
public async Task<ActionResult<ApiResultDto<PagedResult<TrackDto>>>> GetPage(
|
|
||||||
[FromQuery] int pageNumber,
|
|
||||||
[FromQuery] int pageSize,
|
|
||||||
[FromQuery] string? sortColumn = null,
|
|
||||||
[FromQuery] bool sortDescending = false)
|
|
||||||
{
|
|
||||||
var result = await _trackService.GetPaged(pageNumber, pageSize, sortColumn, sortDescending);
|
|
||||||
var apiResult = ApiResult<PagedResult<TrackDto>>.From(result);
|
|
||||||
var dto = new ApiResultDto<PagedResult<TrackDto>>(apiResult);
|
|
||||||
|
|
||||||
return result.Success ? Ok(dto) : StatusCode(500, dto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
||||||
<ProjectReference Include="..\DeepDrftModels\DeepDrftModels.csproj" />
|
<ProjectReference Include="..\DeepDrftModels\DeepDrftModels.csproj" />
|
||||||
<ProjectReference Include="..\DeepDrftPublic.Client\DeepDrftPublic.Client.csproj" />
|
<ProjectReference Include="..\DeepDrftPublic.Client\DeepDrftPublic.Client.csproj" />
|
||||||
<ProjectReference Include="..\DeepDrftData\DeepDrftData.csproj" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.7" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.7" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -2,32 +2,21 @@ using DeepDrftPublic;
|
|||||||
using MudBlazor.Services;
|
using MudBlazor.Services;
|
||||||
using DeepDrftPublic.Components;
|
using DeepDrftPublic.Components;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using NetBlocks.Utilities.Environment;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add MudBlazor services
|
// Add MudBlazor services
|
||||||
builder.Services.AddMudServices();
|
builder.Services.AddMudServices();
|
||||||
|
|
||||||
// Required credential files — must exist before the app will start.
|
|
||||||
// In dev: create the files under DeepDrftPublic/environment/ (gitignored).
|
|
||||||
// In prod: systemd CREDENTIALS_DIRECTORY points to encrypted credential blobs.
|
|
||||||
// - environment/connections.json: { "ConnectionStrings": { "DefaultConnection": "..." } }
|
|
||||||
// AuthBlocks and the DeepDrftAPI API key now live on DeepDrftManager;
|
|
||||||
// the public host has no auth surface and no CMS upload proxy.
|
|
||||||
var connectionsPath = CredentialTools.ResolvePathOrThrow("connections", "environment/connections.json");
|
|
||||||
builder.Configuration.AddJsonFile(connectionsPath, optional: false, reloadOnChange: false);
|
|
||||||
|
|
||||||
var contentApiUrl = builder.Configuration["ApiUrls:ContentApi"] ?? throw new Exception("Content API URL is not configured");
|
var contentApiUrl = builder.Configuration["ApiUrls:ContentApi"] ?? throw new Exception("Content API URL is not configured");
|
||||||
|
var sqlApiUrl = builder.Configuration["ApiUrls:SqlApi"] ?? throw new Exception("ApiUrls:SqlApi is not configured");
|
||||||
|
|
||||||
DeepDrftPublic.Client.Startup.ConfigureApiHttpClient(builder.Services, builder.GetKestrelUrl());
|
DeepDrftPublic.Client.Startup.ConfigureApiHttpClient(builder.Services, sqlApiUrl);
|
||||||
DeepDrftPublic.Client.Startup.ConfigureDomainServices(builder.Services);
|
DeepDrftPublic.Client.Startup.ConfigureDomainServices(builder.Services);
|
||||||
DeepDrftPublic.Client.Startup.ConfigureContentServices(builder.Services, contentApiUrl);
|
DeepDrftPublic.Client.Startup.ConfigureContentServices(builder.Services, contentApiUrl);
|
||||||
|
|
||||||
Startup.ConfigureDomainServices(builder);
|
Startup.ConfigureDomainServices(builder);
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorComponents()
|
builder.Services.AddRazorComponents()
|
||||||
.AddInteractiveServerComponents()
|
.AddInteractiveServerComponents()
|
||||||
@@ -110,7 +99,6 @@ if (app.Environment.IsDevelopment())
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.MapControllers();
|
|
||||||
app.MapRazorComponents<App>()
|
app.MapRazorComponents<App>()
|
||||||
.AddInteractiveServerRenderMode()
|
.AddInteractiveServerRenderMode()
|
||||||
.AddInteractiveWebAssemblyRenderMode()
|
.AddInteractiveWebAssemblyRenderMode()
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
using DeepDrftData;
|
|
||||||
using DeepDrftModels.DTOs;
|
|
||||||
using DeepDrftPublic.Client.Services;
|
|
||||||
using Models.Common;
|
|
||||||
using NetBlocks.Models;
|
|
||||||
|
|
||||||
namespace DeepDrftPublic.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Server-side <see cref="ITrackDataService"/> that calls <see cref="ITrackService"/>
|
|
||||||
/// in-process (EF Core / SQL). Replaces the loopback HTTP hop during SSR prerender:
|
|
||||||
/// the WASM interactive pass still uses <see cref="Client.Services.TrackClientDataService"/>
|
|
||||||
/// over HTTP, but on the server we already have the domain service in DI.
|
|
||||||
/// </summary>
|
|
||||||
public class TrackDirectDataService : ITrackDataService
|
|
||||||
{
|
|
||||||
private readonly ITrackService _trackService;
|
|
||||||
|
|
||||||
public TrackDirectDataService(ITrackService trackService)
|
|
||||||
{
|
|
||||||
_trackService = trackService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ApiResult<PagedResult<TrackDto>>> GetPage(
|
|
||||||
int pageNumber,
|
|
||||||
int pageSize,
|
|
||||||
string? sortColumn = null,
|
|
||||||
bool sortDescending = false)
|
|
||||||
{
|
|
||||||
var result = await _trackService.GetPaged(pageNumber, pageSize, sortColumn, sortDescending);
|
|
||||||
return ApiResult<PagedResult<TrackDto>>.From(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
using DeepDrftData;
|
|
||||||
using DeepDrftData.Data;
|
|
||||||
using DeepDrftData.Repositories;
|
|
||||||
using DeepDrftPublic.Client.Services;
|
|
||||||
using DeepDrftPublic.Services; // DarkModeService namespace (within this host project)
|
using DeepDrftPublic.Services; // DarkModeService namespace (within this host project)
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace DeepDrftPublic;
|
namespace DeepDrftPublic;
|
||||||
|
|
||||||
@@ -11,55 +6,10 @@ public static class Startup
|
|||||||
{
|
{
|
||||||
public static void ConfigureDomainServices(WebApplicationBuilder builder)
|
public static void ConfigureDomainServices(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
// Add Entity Framework services
|
|
||||||
builder.Services.AddDbContext<DeepDrftContext>(options =>
|
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
|
||||||
|
|
||||||
// Add Server Prerendering Theming Support
|
// Add Server Prerendering Theming Support
|
||||||
// DarkModeSettings is registered in DeepDrftPublic.Client.Startup.ConfigureDomainServices
|
// DarkModeSettings is registered in DeepDrftPublic.Client.Startup.ConfigureDomainServices
|
||||||
builder.Services
|
builder.Services
|
||||||
.AddHttpContextAccessor()
|
.AddHttpContextAccessor()
|
||||||
.AddScoped<DarkModeService>();
|
.AddScoped<DarkModeService>();
|
||||||
|
|
||||||
// Add Track services. TrackManager implements ITrackService for backward compatibility
|
|
||||||
// with 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<TrackManager>()
|
|
||||||
.AddScoped<ITrackService>(sp => sp.GetRequiredService<TrackManager>());
|
|
||||||
|
|
||||||
// Override the WASM HTTP-backed ITrackDataService (registered earlier by
|
|
||||||
// DeepDrftPublic.Client.Startup.ConfigureDomainServices) with an in-process
|
|
||||||
// adapter for SSR prerender. Last registration wins for single-resolution.
|
|
||||||
builder.Services.AddScoped<ITrackDataService, TrackDirectDataService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetKestrelUrl(this WebApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
// Check all the places Kestrel URL can be configured
|
|
||||||
var urls = builder.Configuration["ASPNETCORE_URLS"]
|
|
||||||
?? builder.Configuration["urls"];
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(urls))
|
|
||||||
{
|
|
||||||
return urls.Split(';')[0].Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check Kestrel endpoints configuration
|
|
||||||
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"
|
|
||||||
: "http://localhost:5000";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ApiUrls": {
|
"ApiUrls": {
|
||||||
"ContentApi": "http://localhost:12777/"
|
"ContentApi": "http://localhost:12777/",
|
||||||
|
"SqlApi": "https://localhost:5002"
|
||||||
},
|
},
|
||||||
"ForwardedHeaders": {
|
"ForwardedHeaders": {
|
||||||
"DisableHttpsRedirection": "true"
|
"DisableHttpsRedirection": "true"
|
||||||
|
|||||||
Reference in New Issue
Block a user