Update NuGet paths and add web client directory docs

Added README section describing the web client directory (`wwwroot`) and its configuration options. No code logic changes; only environment and documentation updates.
This commit is contained in:
2026-02-24 12:21:55 -05:00
parent e1775c5124
commit 135b6a81b9
139 changed files with 6383 additions and 6037 deletions
@@ -321,23 +321,161 @@ public sealed class PostgresDatabaseProvider : IJellyfinDatabaseProvider
}
}
/// <summary>
/// Ensures all required database tables exist by applying pending migrations.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task EnsureTablesExistAsync(CancellationToken cancellationToken = default)
{
try
{
logger.LogInformation("Checking PostgreSQL database for missing tables...");
var context = await DbContextFactory!.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using (context.ConfigureAwait(false))
{
// Get pending migrations
var pendingMigrations = await context.Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false);
var pendingMigrationsList = pendingMigrations.ToList();
if (pendingMigrationsList.Count > 0)
{
logger.LogInformation("Found {Count} pending migrations. Applying migrations...", pendingMigrationsList.Count);
// Apply all pending migrations
await context.Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
logger.LogInformation("Successfully applied {Count} migrations", pendingMigrationsList.Count);
}
else
{
logger.LogInformation("All database tables are up to date. No migrations needed.");
}
// Verify all expected tables exist in each schema
var connection = context.Database.GetDbConnection();
var wasOpened = false;
try
{
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
wasOpened = true;
}
foreach (var schema in Schemas.All)
{
// Count tables in the schema
var countQuery = @"
SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = @schema AND table_type = 'BASE TABLE'";
await using (var command = connection.CreateCommand())
{
command.CommandText = countQuery;
var parameter = command.CreateParameter();
parameter.ParameterName = "@schema";
parameter.Value = schema;
command.Parameters.Add(parameter);
var tableCount = (long?)await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
if (tableCount > 0)
{
logger.LogDebug("Schema '{Schema}' contains {TableCount} tables", schema, tableCount);
}
else
{
logger.LogWarning("Schema '{Schema}' exists but contains no tables. This may indicate an incomplete migration.", schema);
}
}
}
logger.LogInformation("Database table verification complete");
}
finally
{
if (wasOpened)
{
await connection.CloseAsync().ConfigureAwait(false);
}
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to ensure PostgreSQL tables exist. Error: {Message}", ex.Message);
throw;
}
}
/// <inheritdoc/>
public async Task RunScheduledOptimisation(CancellationToken cancellationToken)
{
var context = await DbContextFactory!.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using (context.ConfigureAwait(false))
{
// Run PostgreSQL optimization commands on all schemas
// Note: VACUUM cannot be parameterized, but schema names are from const strings, not user input
foreach (var schema in Schemas.All)
{
#pragma warning disable EF1002 // Schema names are internal constants, not user input
await context.Database.ExecuteSqlRawAsync($"VACUUM ANALYZE \"{schema}\"", cancellationToken).ConfigureAwait(false);
#pragma warning restore EF1002
logger.LogDebug("Optimized PostgreSQL schema '{Schema}'", schema);
}
// Get the database connection
var connection = context.Database.GetDbConnection();
var wasOpened = false;
logger.LogInformation("PostgreSQL database optimized successfully!");
try
{
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
wasOpened = true;
}
// Run PostgreSQL optimization commands on all tables in each schema
foreach (var schema in Schemas.All)
{
// Get all tables in the schema
var tablesQuery = @"
SELECT table_name
FROM information_schema.tables
WHERE table_schema = @schema AND table_type = 'BASE TABLE'";
var tables = new List<string>();
await using (var command = connection.CreateCommand())
{
command.CommandText = tablesQuery;
var parameter = command.CreateParameter();
parameter.ParameterName = "@schema";
parameter.Value = schema;
command.Parameters.Add(parameter);
await using var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
tables.Add(reader.GetString(0));
}
}
// Vacuum each table in the schema
foreach (var table in tables)
{
#pragma warning disable EF1002 // Schema and table names are from database metadata, not user input
await context.Database.ExecuteSqlRawAsync($"VACUUM ANALYZE \"{schema}\".\"{table}\"", cancellationToken).ConfigureAwait(false);
#pragma warning restore EF1002
logger.LogDebug("Optimized PostgreSQL table '{Schema}.{Table}'", schema, table);
}
logger.LogDebug("Completed optimization for PostgreSQL schema '{Schema}' ({TableCount} tables)", schema, tables.Count);
}
logger.LogInformation("PostgreSQL database optimized successfully!");
}
finally
{
if (wasOpened)
{
await connection.CloseAsync().ConfigureAwait(false);
}
}
}
}