Deployment Configuration and Script

- Front End Cleanup
This commit is contained in:
daniel-c-harvey
2025-09-06 18:40:32 -04:00
parent 0951514778
commit a5b7ab041e
13 changed files with 169 additions and 21 deletions
+22
View File
@@ -2,6 +2,7 @@ using DeepDrftContent;
using DeepDrftContent.FileDatabase.Services; using DeepDrftContent.FileDatabase.Services;
using DeepDrftContent.Middleware; using DeepDrftContent.Middleware;
using DeepDrftContent.Models; using DeepDrftContent.Models;
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -35,13 +36,34 @@ builder.Configuration.AddJsonFile("environment/apikey.json", optional: false, re
var apiKeySettings = builder.Configuration.GetSection(nameof(ApiKeySettings)).Get<ApiKeySettings>(); var apiKeySettings = builder.Configuration.GetSection(nameof(ApiKeySettings)).Get<ApiKeySettings>();
if (apiKeySettings is null) { throw new Exception("API key settings are not configured"); } if (apiKeySettings is null) { throw new Exception("API key settings are not configured"); }
// Configure forwarded headers for reverse proxy support
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
// Trust any proxy (nginx) - in production, specify known proxy networks
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
// Use forwarded headers before other middleware
app.UseForwardedHeaders();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.MapOpenApi(); app.MapOpenApi();
} }
else
{
// Only use HTTPS redirection if not behind a reverse proxy
var forwardedProto = app.Services.GetService<IConfiguration>()?["ForwardedHeaders:DisableHttpsRedirection"];
if (string.IsNullOrEmpty(forwardedProto) || !bool.Parse(forwardedProto))
{
app.UseHttpsRedirection();
}
}
app.UseCors("ContentApiPolicy"); app.UseCors("ContentApiPolicy");
app.UseApiKeyAuthentication(apiKeySettings.ApiKey); app.UseApiKeyAuthentication(apiKeySettings.ApiKey);
+8 -1
View File
@@ -7,6 +7,13 @@
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"CorsSettings": { "CorsSettings": {
"AllowedOrigins": [] "AllowedOrigins": [
"https://localhost:12778",
"https://deepdrft.com",
"https://www.deepdrft.com"
]
},
"ForwardedHeaders": {
"DisableHttpsRedirection": "true"
} }
} }
@@ -25,7 +25,7 @@ public class TrackMediaClient
public async Task<TrackMediaResponse> GetTrackMedia(string trackId) public async Task<TrackMediaResponse> GetTrackMedia(string trackId)
{ {
var response = await _http.GetAsync($"api/track/{trackId}"); var response = await _http.GetAsync($"track/{trackId}");
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var contentLength = response.Content.Headers.ContentLength ?? 0; var contentLength = response.Content.Headers.ContentLength ?? 0;
+6 -8
View File
@@ -13,14 +13,12 @@
SelectedTrack="_selectedTrack" SelectedTrack="_selectedTrack"
SelectedTrackChanged="@PlayTrack"/> SelectedTrackChanged="@PlayTrack"/>
</div> </div>
<div class="tracks-footer"> <div class="tracks-footer py-4">
<div class="py-4"> <MudPagination Count="@ViewModel.Page.TotalPages"
<MudPagination Count="@ViewModel.Page.TotalPages" Selected="@ViewModel.Page.Page"
Selected="@ViewModel.Page.Page" SelectedChanged="@SetPage"
SelectedChanged="@SetPage" BoundaryCount="2"
BoundaryCount="2" MiddleCount="3"/>
MiddleCount="3"/>
</div>
<AudioPlayerBar AudioPlaybackEngine="AudioPlaybackEngine" /> <AudioPlayerBar AudioPlaybackEngine="AudioPlaybackEngine" />
</div> </div>
} }
@@ -24,5 +24,8 @@
.tracks-footer { .tracks-footer {
flex: 0 0 auto; flex: 0 0 auto;
padding: 8px 0; padding: 8px 0;
justify-items: center; display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
} }
@@ -4,5 +4,8 @@
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
},
"ApiUrls": {
"ContentApi": "https://localhost:54493/api"
} }
} }
+1 -1
View File
@@ -6,6 +6,6 @@
} }
}, },
"ApiUrls": { "ApiUrls": {
"ContentApi": "https://localhost:54493" "ContentApi": "https://media.deepdrft.com/api"
} }
} }
+20 -4
View File
@@ -30,10 +30,26 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<Folder Include="wwwroot\js\" /> <Folder Include="wwwroot\js\" />
<Content Update="Interop\webaudio.js"> </ItemGroup>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <ItemGroup>
</Content> <TypeScriptCompile Include="Interop\webaudio.ts">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</TypeScriptCompile>
</ItemGroup>
<!-- Prevent TypeScript compilation issues during publish -->
<PropertyGroup>
<TypeScriptCompileOnSaveEnabled>false</TypeScriptCompileOnSaveEnabled>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
</PropertyGroup>
<!-- Only copy tsconfig.json to root output, not nested publish folders -->
<ItemGroup>
<None Update="tsconfig.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>
+20 -1
View File
@@ -2,6 +2,7 @@ using DeepDrftWeb;
using DeepDrftWeb.Client.Services; using DeepDrftWeb.Client.Services;
using MudBlazor.Services; using MudBlazor.Services;
using DeepDrftWeb.Components; using DeepDrftWeb.Components;
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -27,9 +28,21 @@ builder.Services.AddRazorComponents()
.AddInteractiveServerComponents() .AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents(); .AddInteractiveWebAssemblyComponents();
// Configure forwarded headers for reverse proxy support
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
// Trust any proxy (nginx) - in production, specify known proxy networks
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
// Use forwarded headers before other middleware
app.UseForwardedHeaders();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.UseWebAssemblyDebugging(); app.UseWebAssemblyDebugging();
@@ -39,7 +52,13 @@ else
app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts(); app.UseHsts();
app.UseHttpsRedirection();
// Only use HTTPS redirection if not behind a reverse proxy
var disableHttpsRedirection = app.Configuration.GetValue<bool>("ForwardedHeaders:DisableHttpsRedirection");
if (!disableHttpsRedirection)
{
app.UseHttpsRedirection();
}
} }
app.UseAntiforgery(); app.UseAntiforgery();
+4 -1
View File
@@ -5,5 +5,8 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"DetailedErrors": true "DetailedErrors": true,
"ApiUrls": {
"ContentApi": "https://localhost:54493/api"
}
} }
+4 -1
View File
@@ -10,6 +10,9 @@
"DefaultConnection": "Data Source=../Database/deepdrft.db" "DefaultConnection": "Data Source=../Database/deepdrft.db"
}, },
"ApiUrls": { "ApiUrls": {
"ContentApi": "https://localhost:54493" "ContentApi": "https://localhost:12777/api"
},
"ForwardedHeaders": {
"DisableHttpsRedirection": "true"
} }
} }
+4 -2
View File
@@ -10,9 +10,11 @@
"outDir": "wwwroot/js" "outDir": "wwwroot/js"
}, },
"include": [ "include": [
"Interop/**/*" "**/*.ts"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules",
"bin/**/*",
"obj/**/*"
] ]
} }
+72
View File
@@ -0,0 +1,72 @@
# start SSH agent and add key
eval $(ssh-agent -s)
ssh-add /c/.ssh/deepdrft_ed25519
CONTENT_PROJ="DeepDrftContent"
WEB_PROJ="DeepDrftWeb"
CONTENT_APP="deepdrft-content.tar.gz"
WEB_APP="deepdrft-web.tar.gz"
# Restore and Publish
dotnet publish $CONTENT_PROJ -c Release -f net9.0 -o $CONTENT_PROJ/publish -r linux-x64 -p:Platform="Any CPU" --verbosity normal
dotnet publish $WEB_PROJ -c Release -f net9.0 -o $WEB_PROJ/publish -r linux-x64 -p:Platform="Any CPU" --verbosity normal
# Check if migrations are needed
WEB_MIG="deepdrft-migrations.sql"
REMOTE="deepdrft@dch5.snailbird.net"
WEB_APPROOT="/deepdrft/web"
LATEST_MIGRATION=$(dotnet ef migrations list --project $WEB_PROJ --context DeepDrftContext --no-build | tail -1)
REMOTE_MIGRATION=$(ssh $REMOTE "sqlite3 $WEB_APPROOT/Database/deepdrft.db 'SELECT MigrationId FROM __EFMigrationsHistory ORDER BY MigrationId DESC LIMIT 1;'" 2>/dev/null || echo "")
if [ "$LATEST_MIGRATION" != "$REMOTE_MIGRATION" ]; then
echo "Generating migration script from $REMOTE_MIGRATION to $LATEST_MIGRATION..."
if [ -z "$REMOTE_MIGRATION" ]; then
dotnet ef migrations script --project $WEB_PROJ --context DeepDrftContext --output $WEB_MIG --verbose --no-build
else
dotnet ef migrations script $REMOTE_MIGRATION --project $WEB_PROJ --context DeepDrftContext --output $WEB_MIG --verbose --no-build
fi
APPLY_MIGRATIONS=true
else
echo "Database is up to date."
APPLY_MIGRATIONS=false
fi
# Compress published files
tar -czf $CONTENT_APP -C $CONTENT_PROJ/publish .
tar -czf $WEB_APP -C $WEB_PROJ/publish .
# Deploy
CONTENT_APPROOT="/deepdrft/api/content"
ssh $REMOTE "rm -rf $CONTENT_APPROOT/bin/*"
ssh $REMOTE "rm -rf $WEB_APPROOT/bin/*"
scp $CONTENT_APP $REMOTE:$CONTENT_APPROOT/$CONTENT_APP
if [ "$APPLY_MIGRATIONS" = true ]; then
scp $WEB_MIG $REMOTE:$WEB_APPROOT/$WEB_MIG
fi
scp $WEB_APP $REMOTE:$WEB_APPROOT/$WEB_APP
ssh $REMOTE "tar -xzf $CONTENT_APPROOT/$CONTENT_APP -C $CONTENT_APPROOT/bin && rm $CONTENT_APPROOT/$CONTENT_APP"
ssh $REMOTE "tar -xzf $WEB_APPROOT/$WEB_APP -C $WEB_APPROOT/bin && rm $WEB_APPROOT/$WEB_APP"
# Apply Local Environment
ssh $REMOTE "cp $CONTENT_APPROOT/environment/* $CONTENT_APPROOT/bin/environment"
# Apply database migrations on server
if [ "$APPLY_MIGRATIONS" = true ]; then
ssh $REMOTE "sqlite3 $WEB_APPROOT/Database/deepdrft.db < $WEB_APPROOT/$WEB_MIG && rm $WEB_APPROOT/$WEB_MIG"
fi
# Restart the service
ssh $REMOTE "$CONTENT_APPROOT/restart.sh"
ssh $REMOTE "$WEB_APPROOT/restart.sh"
# Clean up
rm -rf ./$CONTENT_PROJ/publish
rm -f ./$CONTENT_APP
rm -rf ./$WEB_PROJ/publish
rm -f ./$WEB_APP
if [ "$APPLY_MIGRATIONS" = true ]; then
rm -f ./$WEB_MIG
fi
ssh-agent -k