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:
+148
-10
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user