diff --git a/DeepDrftAPI/Controllers/TrackController.cs b/DeepDrftAPI/Controllers/TrackController.cs index 6d7ed4e..b9414c3 100644 --- a/DeepDrftAPI/Controllers/TrackController.cs +++ b/DeepDrftAPI/Controllers/TrackController.cs @@ -47,8 +47,7 @@ public class TrackController : ControllerBase // resolution never treats "page", "upload", or "meta" as a trackId. // GET api/track/page?page=1&pageSize=20&sortColumn=TrackName&sortDescending=false - // CMS metadata listing — paged read straight from SQL. - [ApiKeyAuthorize] + // Public track listing — paged read straight from SQL. Unauthenticated, like GET api/track/{id}. [HttpGet("page")] public async Task GetPage( [FromQuery] int page = 1, diff --git a/DeepDrftPublic.Client/Clients/TrackClient.cs b/DeepDrftPublic.Client/Clients/TrackClient.cs index 3a75d69..f4681e4 100644 --- a/DeepDrftPublic.Client/Clients/TrackClient.cs +++ b/DeepDrftPublic.Client/Clients/TrackClient.cs @@ -17,32 +17,37 @@ public class TrackClient } public async Task>> GetPage( - int pageNumber, - int pageSize, - string? sortColumn = null, + int pageNumber, + int pageSize, + string? sortColumn = null, bool sortDescending = false) { var queryArgs = new Dictionary(){ - ["pageNumber"] = pageNumber.ToString(), + ["page"] = pageNumber.ToString(), ["pageSize"] = pageSize.ToString() }; - + if (!string.IsNullOrEmpty(sortColumn)) queryArgs["sortColumn"] = sortColumn; - + if (sortDescending) queryArgs["sortDescending"] = "true"; string query = QueryString.Create(queryArgs).ToString(); - + var response = await _http.GetAsync($"api/track/page{query}"); + + if (!response.IsSuccessStatusCode) + return ApiResult>.CreateFailResult($"HTTP {(int)response.StatusCode}"); + var json = await response.Content.ReadAsStringAsync(); - - var dto = JsonSerializer.Deserialize>>(json, new JsonSerializerOptions + var paged = JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - - return dto?.From() ?? ApiResult>.CreateFailResult("Failed to deserialize response"); + + return paged is not null + ? ApiResult>.CreatePassResult(paged) + : ApiResult>.CreateFailResult("Failed to deserialize response"); } } \ No newline at end of file diff --git a/DeepDrftPublic.Client/Program.cs b/DeepDrftPublic.Client/Program.cs index 147012e..300c3bc 100644 --- a/DeepDrftPublic.Client/Program.cs +++ b/DeepDrftPublic.Client/Program.cs @@ -7,10 +7,11 @@ var builder = WebAssemblyHostBuilder.CreateDefault(args); Console.WriteLine(builder.HostEnvironment.BaseAddress); var contentApiUrl = builder.Configuration["ApiUrls:ContentApi"] ?? "https://localhost:7001"; +var sqlApiUrl = builder.Configuration["ApiUrls:SqlApi"] ?? "https://localhost:5002"; builder.Services.AddMudServices(); -Startup.ConfigureApiHttpClient(builder.Services, builder.HostEnvironment.BaseAddress); +Startup.ConfigureApiHttpClient(builder.Services, sqlApiUrl); Startup.ConfigureContentServices(builder.Services, contentApiUrl); Startup.ConfigureDomainServices(builder.Services); diff --git a/DeepDrftPublic.Client/Services/ITrackDataService.cs b/DeepDrftPublic.Client/Services/ITrackDataService.cs index 842ec42..8bf97eb 100644 --- a/DeepDrftPublic.Client/Services/ITrackDataService.cs +++ b/DeepDrftPublic.Client/Services/ITrackDataService.cs @@ -5,13 +5,9 @@ using NetBlocks.Models; namespace DeepDrftPublic.Client.Services; /// -/// Track metadata fetch abstraction with two render-mode-specific implementations: -/// -/// - Server prerender pass: TrackDirectDataService in the DeepDrftPublic host -/// resolves in-process (EF Core / SQL) and -/// avoids a loopback HTTP hop. -/// - WASM interactive pass: TrackClientDataService in this assembly delegates -/// to over HTTP. +/// Track metadata fetch abstraction. Both SSR and WASM passes are served by +/// TrackClientDataService in this assembly, which delegates to +/// over HTTP. /// /// Components inject this single seam so they do not branch on render mode. /// diff --git a/DeepDrftPublic.Client/Startup.cs b/DeepDrftPublic.Client/Startup.cs index 0858ad7..fa8ed89 100644 --- a/DeepDrftPublic.Client/Startup.cs +++ b/DeepDrftPublic.Client/Startup.cs @@ -14,9 +14,8 @@ public static class Startup services.AddScoped(); services.AddScoped(); - // Track Client. The HTTP-backed ITrackDataService registration here is the WASM - // default; the server host overrides it with an in-process implementation after - // this method runs, so SSR prerender skips the loopback hop. + // Track Client. The HTTP-backed ITrackDataService is used by both WASM and SSR + // prerender — both call DeepDrftAPI over the "DeepDrft.API" client. services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/DeepDrftPublic.Client/wwwroot/appsettings.json b/DeepDrftPublic.Client/wwwroot/appsettings.json index e6588d2..0ef5f55 100644 --- a/DeepDrftPublic.Client/wwwroot/appsettings.json +++ b/DeepDrftPublic.Client/wwwroot/appsettings.json @@ -6,6 +6,7 @@ } }, "ApiUrls": { - "ContentApi": "https://media.deepdrft.com/" + "ContentApi": "https://media.deepdrft.com/", + "SqlApi": "https://api.deepdrft.com/" } } diff --git a/DeepDrftPublic/Controllers/TrackController.cs b/DeepDrftPublic/Controllers/TrackController.cs deleted file mode 100644 index f4d1164..0000000 --- a/DeepDrftPublic/Controllers/TrackController.cs +++ /dev/null @@ -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>>> 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>.From(result); - var dto = new ApiResultDto>(apiResult); - - return result.Success ? Ok(dto) : StatusCode(500, dto); - } -} \ No newline at end of file diff --git a/DeepDrftPublic/DeepDrftPublic.csproj b/DeepDrftPublic/DeepDrftPublic.csproj index db3433f..8e8bdcc 100644 --- a/DeepDrftPublic/DeepDrftPublic.csproj +++ b/DeepDrftPublic/DeepDrftPublic.csproj @@ -8,21 +8,8 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/DeepDrftPublic/Program.cs b/DeepDrftPublic/Program.cs index c5294c9..c5d0a88 100644 --- a/DeepDrftPublic/Program.cs +++ b/DeepDrftPublic/Program.cs @@ -2,32 +2,21 @@ using DeepDrftPublic; using MudBlazor.Services; using DeepDrftPublic.Components; using Microsoft.AspNetCore.HttpOverrides; -using NetBlocks.Utilities.Environment; var builder = WebApplication.CreateBuilder(args); // Add MudBlazor services 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 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.ConfigureContentServices(builder.Services, contentApiUrl); Startup.ConfigureDomainServices(builder); -builder.Services.AddControllers(); - // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents() @@ -110,7 +99,6 @@ if (app.Environment.IsDevelopment()) }); } -app.MapControllers(); app.MapRazorComponents() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() diff --git a/DeepDrftPublic/Services/TrackDirectDataService.cs b/DeepDrftPublic/Services/TrackDirectDataService.cs deleted file mode 100644 index 31676b1..0000000 --- a/DeepDrftPublic/Services/TrackDirectDataService.cs +++ /dev/null @@ -1,33 +0,0 @@ -using DeepDrftData; -using DeepDrftModels.DTOs; -using DeepDrftPublic.Client.Services; -using Models.Common; -using NetBlocks.Models; - -namespace DeepDrftPublic.Services; - -/// -/// Server-side that calls -/// in-process (EF Core / SQL). Replaces the loopback HTTP hop during SSR prerender: -/// the WASM interactive pass still uses -/// over HTTP, but on the server we already have the domain service in DI. -/// -public class TrackDirectDataService : ITrackDataService -{ - private readonly ITrackService _trackService; - - public TrackDirectDataService(ITrackService trackService) - { - _trackService = trackService; - } - - public async Task>> GetPage( - int pageNumber, - int pageSize, - string? sortColumn = null, - bool sortDescending = false) - { - var result = await _trackService.GetPaged(pageNumber, pageSize, sortColumn, sortDescending); - return ApiResult>.From(result); - } -} diff --git a/DeepDrftPublic/Startup.cs b/DeepDrftPublic/Startup.cs index 61ca8cf..4ab30e1 100644 --- a/DeepDrftPublic/Startup.cs +++ b/DeepDrftPublic/Startup.cs @@ -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 Microsoft.EntityFrameworkCore; namespace DeepDrftPublic; @@ -11,55 +6,10 @@ public static class Startup { public static void ConfigureDomainServices(WebApplicationBuilder builder) { - // Add Entity Framework services - builder.Services.AddDbContext(options => - options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))); - // Add Server Prerendering Theming Support // DarkModeSettings is registered in DeepDrftPublic.Client.Startup.ConfigureDomainServices builder.Services .AddHttpContextAccessor() .AddScoped(); - - // 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() - .AddScoped() - .AddScoped(sp => sp.GetRequiredService()); - - // 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(); - } - - 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"; } } diff --git a/DeepDrftPublic/appsettings.json b/DeepDrftPublic/appsettings.json index 6f4ca4f..a66335c 100644 --- a/DeepDrftPublic/appsettings.json +++ b/DeepDrftPublic/appsettings.json @@ -7,7 +7,8 @@ }, "AllowedHosts": "*", "ApiUrls": { - "ContentApi": "http://localhost:12777/" + "ContentApi": "http://localhost:12777/", + "SqlApi": "https://localhost:5002/" }, "ForwardedHeaders": { "DisableHttpsRedirection": "true" diff --git a/dch5-publish-cli.sh b/dch5-publish-cli.sh deleted file mode 100644 index ba6c9f9..0000000 --- a/dch5-publish-cli.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/bash - -echo "🚀 Starting CLI deployment process..." - -# start SSH agent and add key -echo "🔑 Starting SSH agent and adding deployment key..." -eval $(ssh-agent -s) -ssh-add /c/.ssh/deepdrft_dch6_ed25519 -echo "✅ SSH agent configured" - -CLI_PROJ="DeepDrftCli" -CLI_APP="deepdrft-cli.tar.gz" - -# Publish CLI with framework-dependent single file -echo "🔨 Publishing CLI project for linux-x64..." -dotnet publish $CLI_PROJ -c Release -f net9.0 -o $CLI_PROJ/publish -r linux-x64 \ - --self-contained false \ - -p:PublishSingleFile=true \ - -p:Platform="Any CPU" \ - --verbosity normal - -if [ $? -eq 0 ]; then - echo "✅ CLI project published successfully" -else - echo "❌ Failed to publish CLI project" - exit 1 -fi - -# Eliminate local environment from package -echo "🧹 Removing local environment from package..." -rm -rf $CLI_PROJ/publish/environment -echo "✅ Local environment removed" - -# Compress published files -echo "📦 Compressing published files..." -tar -czf $CLI_APP -C $CLI_PROJ/publish . -echo "✅ Package created: $CLI_APP" - -# Deploy -REMOTE="deepdrft@dch6.snailbird.net" -CLI_APPROOT="/deepdrft/cli" - -echo "🌐 Deploying to remote server: $REMOTE" -echo "📁 Target directory: $CLI_APPROOT" - -echo "🗑️ Cleaning existing deployment..." -ssh $REMOTE "rm -rf $CLI_APPROOT/bin/*" -echo "✅ Remote directory cleaned" - -echo "📤 Uploading package to remote server..." -scp $CLI_APP $REMOTE:$CLI_APPROOT/$CLI_APP -if [ $? -eq 0 ]; then - echo "✅ Package uploaded successfully" -else - echo "❌ Failed to upload package" - exit 1 -fi - -echo "📦 Extracting and setting up CLI on remote server..." -ssh $REMOTE "tar -xzf $CLI_APPROOT/$CLI_APP -C $CLI_APPROOT/bin && \ - chmod +x $CLI_APPROOT/bin/DeepDrftCli && \ - rm $CLI_APPROOT/$CLI_APP" -if [ $? -eq 0 ]; then - echo "✅ CLI extracted and configured on remote server" -else - echo "❌ Failed to extract CLI on remote server" - exit 1 -fi - -# Apply Local Environment (if exists) -echo "🔧 Checking for local environment configuration..." -if ssh $REMOTE "[ -d $CLI_APPROOT/environment ]"; then - echo "📋 Local environment found, applying configuration..." - - # Ensure environment directory exists in the binary location - ssh $REMOTE "mkdir -p $CLI_APPROOT/bin/environment" - - # Copy environment files with better error handling - if ssh $REMOTE "cp $CLI_APPROOT/environment/* $CLI_APPROOT/bin/environment/ 2>/dev/null"; then - echo "✅ Local environment configuration applied successfully" - else - echo "⚠️ Warning: Some environment files may not have been copied" - fi -else - echo "ℹ️ No local environment configuration found - skipping" -fi - -echo "🔗 Setting up user-accessible command symlink..." -# Create user-accessible symlink without sudo -ssh $REMOTE "mkdir -p ~/bin && ln -sf $CLI_APPROOT/bin/DeepDrftCli ~/bin/deepdrft" -if [ $? -eq 0 ]; then - echo "✅ Symlink created successfully" -else - echo "❌ Failed to create symlink" - exit 1 -fi - -echo "🛣️ Ensuring ~/bin is in PATH..." -# Ensure ~/bin is in PATH (add to .bashrc if not present) -ssh $REMOTE "grep -q '~/bin' ~/.bashrc || echo 'export PATH=\"\$HOME/bin:\$PATH\"' >> ~/.bashrc" -echo "✅ PATH configuration updated" - -echo "🧹 Cleaning up local files..." -# Clean up -rm -rf ./$CLI_PROJ/publish -rm -f ./$CLI_APP -ssh-agent -k -echo "✅ Local cleanup completed" - -echo "" -echo "🎉 CLI deployment complete!" -echo "📝 Note: Run 'source ~/.bashrc' or start a new shell session to activate the deepdrft command in PATH" \ No newline at end of file