Remove startup migration and runtime frontend rewrites
This commit is contained in:
@@ -9,7 +9,6 @@ using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text.Json;
|
||||
using System.Net.Http.Json;
|
||||
@@ -28,34 +27,10 @@ public class HelperTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateIndexMetaBase_rewrites_content_value()
|
||||
public void Program_does_not_include_runtime_index_rewrite_hook()
|
||||
{
|
||||
var webRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(webRoot);
|
||||
var index = Path.Combine(webRoot, "index.html");
|
||||
File.WriteAllText(index, "<meta name=\"app-base\" content=\"\">");
|
||||
|
||||
var env = new FakeEnv { WebRootPath = webRoot };
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).First(m => m.Name.Contains("UpdateIndexMetaBase"));
|
||||
method.Invoke(null, [env, "/pick"]);
|
||||
|
||||
var text = File.ReadAllText(index);
|
||||
Assert.Contains("content=\"/pick\"", text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateIndexMetaBase_no_marker_no_change()
|
||||
{
|
||||
var webRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(webRoot);
|
||||
var index = Path.Combine(webRoot, "index.html");
|
||||
File.WriteAllText(index, "<html></html>");
|
||||
|
||||
var env = new FakeEnv { WebRootPath = webRoot };
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).First(m => m.Name.Contains("UpdateIndexMetaBase"));
|
||||
method.Invoke(null, [env, "/pick"]);
|
||||
|
||||
Assert.Equal("<html></html>", File.ReadAllText(index));
|
||||
var hasRewriteMethod = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).Any(m => m.Name.Contains("UpdateIndexMetaBase", StringComparison.Ordinal));
|
||||
Assert.False(hasRewriteMethod);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -349,16 +324,6 @@ public class HelperTests
|
||||
Assert.DoesNotContain("data-name=\"${v.name}\"", adminJs, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private class FakeEnv : IWebHostEnvironment
|
||||
{
|
||||
public string ApplicationName { get; set; } = "";
|
||||
public IFileProvider WebRootFileProvider { get; set; } = null!;
|
||||
public string WebRootPath { get; set; } = "";
|
||||
public string EnvironmentName { get; set; } = "";
|
||||
public string ContentRootPath { get; set; } = "";
|
||||
public IFileProvider ContentRootFileProvider { get; set; } = null!;
|
||||
}
|
||||
|
||||
private static ForwardedHeadersOptions BuildForwardedHeadersOptionsForTest(IConfiguration config)
|
||||
{
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).First(m => m.Name.Contains("BuildForwardedHeadersOptions"));
|
||||
|
||||
@@ -26,7 +26,7 @@ internal class TestWebApplicationFactory : WebApplicationFactory<Program>
|
||||
services.Remove(descriptor);
|
||||
}
|
||||
|
||||
_connection = new SqliteConnection("Data Source=:memory:;Cache=Shared");
|
||||
_connection = new SqliteConnection($"Data Source=file:tests-{Guid.NewGuid():N}?mode=memory&cache=shared");
|
||||
_connection.Open();
|
||||
|
||||
services.AddDbContext<AppDbContext>(options => { options.UseSqlite(_connection); });
|
||||
@@ -44,7 +44,6 @@ internal class TestWebApplicationFactory : WebApplicationFactory<Program>
|
||||
|
||||
using var scope = host.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
db.Database.EnsureCreated();
|
||||
db.Database.Migrate();
|
||||
|
||||
return host;
|
||||
|
||||
1
IIS.md
1
IIS.md
@@ -8,6 +8,7 @@
|
||||
|
||||
## Publish
|
||||
- From repo root: `dotnet publish -c Release -o publish`
|
||||
- Before first start (and after every new migration): run `dotnet ef database update` from repo root against the target environment.
|
||||
- Copy `publish/` contents to site directory (keep `App_Data` writable by the app pool user).
|
||||
- Set environment variables in web.config or IIS config:
|
||||
- `ASPNETCORE_ENVIRONMENT=Production`
|
||||
|
||||
46
Program.cs
46
Program.cs
@@ -146,7 +146,6 @@ var basePath = builder.Configuration["BasePath"];
|
||||
if (!string.IsNullOrWhiteSpace(basePath))
|
||||
{
|
||||
app.UsePathBase(basePath);
|
||||
UpdateIndexMetaBase(app.Environment, basePath);
|
||||
}
|
||||
|
||||
app.UseGlobalExceptionLogging();
|
||||
@@ -154,13 +153,6 @@ app.UseAuthentication();
|
||||
app.UseMiddleware<EnsurePlayerExistsMiddleware>();
|
||||
app.UseAuthorization();
|
||||
|
||||
// Ensure database and migrations are applied on startup
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
db.Database.Migrate();
|
||||
}
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
@@ -274,42 +266,4 @@ static Task WriteUnauthorizedChallengeAsync(HttpContext context)
|
||||
return context.Response.WriteAsJsonAsync(problem);
|
||||
}
|
||||
|
||||
static void UpdateIndexMetaBase(IWebHostEnvironment env, string basePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var indexPath = Path.Combine(env.WebRootPath, "index.html");
|
||||
if (!File.Exists(indexPath))
|
||||
return;
|
||||
|
||||
var text = File.ReadAllText(indexPath);
|
||||
var marker = "name=\"app-base\"";
|
||||
var contentKey = "content=\"";
|
||||
var markerIndex = text.IndexOf(marker, StringComparison.OrdinalIgnoreCase);
|
||||
if (markerIndex < 0)
|
||||
return;
|
||||
|
||||
var contentIndex = text.IndexOf(contentKey, markerIndex, StringComparison.OrdinalIgnoreCase);
|
||||
if (contentIndex < 0)
|
||||
return;
|
||||
|
||||
var valueStart = contentIndex + contentKey.Length;
|
||||
var valueEnd = text.IndexOf('"', valueStart);
|
||||
if (valueEnd < 0)
|
||||
return;
|
||||
|
||||
var current = text[valueStart..valueEnd];
|
||||
var normalized = basePath.EndsWith('/') ? basePath.TrimEnd('/') : basePath;
|
||||
if (current == normalized)
|
||||
return;
|
||||
|
||||
var updated = text[..valueStart] + normalized + text[valueEnd..];
|
||||
File.WriteAllText(indexPath, updated);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If we can't rewrite, continue; frontend can still be set manually.
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Program;
|
||||
|
||||
@@ -6,11 +6,13 @@ Pick'n'Play is a .NET 10 ASP.NET Core Minimal API app with a static HTML/CSS/JS
|
||||
|
||||
1. Restore and build:
|
||||
`dotnet build GameList.sln`
|
||||
2. Run tests:
|
||||
2. Apply DB migrations explicitly:
|
||||
`dotnet ef database update`
|
||||
3. Run tests:
|
||||
`dotnet test GameList.Tests/GameList.Tests.csproj`
|
||||
3. Run locally:
|
||||
4. Run locally:
|
||||
`dotnet run --project GameList.csproj`
|
||||
4. Open:
|
||||
5. Open:
|
||||
`http://localhost:5000` (or the URL shown by `dotnet run`)
|
||||
|
||||
## Frontend Tooling
|
||||
@@ -28,6 +30,7 @@ Pick'n'Play is a .NET 10 ASP.NET Core Minimal API app with a static HTML/CSS/JS
|
||||
- Core invariants are DB-enforced: single owner account and non-joker suggestion cap.
|
||||
- Gameplay phases: `Suggest`, `Vote`, `Results`.
|
||||
- Storage: SQLite database under `App_Data/gamelist.db`.
|
||||
- Migrations are deployment-time operations (`dotnet ef database update`); app startup does not auto-migrate.
|
||||
- Security defaults: rate-limited auth/admin routes, baseline browser security headers, production HTTPS+HSTS enforcement.
|
||||
|
||||
## Module Ownership
|
||||
|
||||
2
TESTS.md
2
TESTS.md
@@ -87,7 +87,7 @@ stateDiagram-v2
|
||||
- EndpointHelpers.IsValidImageUrl/IsValidHttpUrl: accepts empty, http/https; rejects others/invalid ext.
|
||||
- IsReachableImageAsync: with mocked Http responses covers head success, get fallback, redirect rejection, size guard, and private/reserved host range detection (IPv4/IPv6).
|
||||
- BuildLinkRoots/LinkedIdsFor/FindRootId: cover disjoint groups, chains, cycles guard (visited set), non-existent ids.
|
||||
- UpdateIndexMetaBase (Program.cs): rewrites app-base meta when BasePath set; no change when matching/marker missing; safe exceptions swallowed.
|
||||
- Program startup avoids runtime frontend file rewrites; BasePath remains purely configuration/deploy managed.
|
||||
- Global exception handler returns 500 with JSON body and logs error.
|
||||
- /health returns {status:"ok"}.
|
||||
- Security middleware tests validate response headers and rate-limiting behavior on auth/admin routes.
|
||||
|
||||
Reference in New Issue
Block a user