feat: migrate runtime to postgresql

This commit is contained in:
2026-04-29 18:47:12 +08:00
parent 49d893f29b
commit 8132466c5d
17 changed files with 2347 additions and 306 deletions
+45 -22
View File
@@ -21,12 +21,12 @@ using LiveRecorder.Infrastructure.Platforms.Douyin.Signing;
using LiveRecorder.Infrastructure.Platforms.Huya;
using LiveRecorder.Infrastructure.Services;
using LiveRecorder.WebApi.Middleware;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
var resetRecordingData = args.Contains("--reset-recording-data", StringComparer.OrdinalIgnoreCase);
var migrateSqlitePath = GetOptionValue(args, "--migrate-sqlite");
var corsOrigins = builder.Configuration.GetSection("Cors:Origins").Get<string[]>() ?? ["http://localhost:5173"];
builder.Services.AddControllers();
@@ -111,16 +111,12 @@ builder.Services.AddHttpClient("bilibili", client =>
}
});
var sqliteConnectionStringBuilder = new SqliteConnectionStringBuilder(
builder.Configuration.GetConnectionString("DefaultConnection"))
{
Cache = SqliteCacheMode.Shared,
Mode = SqliteOpenMode.ReadWriteCreate,
DefaultTimeout = 30
};
var defaultConnection = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("ConnectionStrings:DefaultConnection is required.");
builder.Services.AddDbContext<LiveRecorderDbContext>(options =>
options.UseSqlite(sqliteConnectionStringBuilder.ToString()));
options.UseNpgsql(defaultConnection, npgsql =>
npgsql.MigrationsAssembly(typeof(LiveRecorderDbContext).Assembly.FullName)));
builder.Services.AddScoped<IUnitOfWork>(provider => provider.GetRequiredService<LiveRecorderDbContext>());
builder.Services.AddScoped<IAppSettingRepository, AppSettingRepository>();
@@ -150,6 +146,7 @@ builder.Services.AddScoped<StoppedOrphanRecordSessionCleanupService>();
builder.Services.AddScoped<PlatformHttpClientFactory>();
builder.Services.AddScoped<RecordUploadService>();
builder.Services.AddScoped<DatabaseInitializer>();
builder.Services.AddScoped<SqliteToPostgresMigrationService>();
builder.Services.AddSingleton<BilibiliWbiSigner>();
builder.Services.AddScoped<BilibiliHttpClient>();
@@ -183,11 +180,25 @@ app.UseMiddleware<ApiTokenAuthenticationMiddleware>();
app.MapGet("/", () => Results.Redirect("/swagger"));
app.MapControllers();
if (!resetRecordingData)
using (var scope = app.Services.CreateScope())
{
var initializer = scope.ServiceProvider.GetRequiredService<DatabaseInitializer>();
await initializer.InitializeAsync(seedDefaults: migrateSqlitePath is null, cancellationToken: default);
}
if (!string.IsNullOrWhiteSpace(migrateSqlitePath))
{
using var scope = app.Services.CreateScope();
var initializer = scope.ServiceProvider.GetRequiredService<DatabaseInitializer>();
await initializer.InitializeAsync();
var migrator = scope.ServiceProvider.GetRequiredService<SqliteToPostgresMigrationService>();
var report = await migrator.MigrateAsync(migrateSqlitePath);
Console.WriteLine($"SQLite migration completed. Source={report.SourcePath}");
foreach (var tableName in report.SourceCounts.Keys.OrderBy(static item => item, StringComparer.OrdinalIgnoreCase))
{
Console.WriteLine($"{tableName}: {report.SourceCounts[tableName]} -> {report.TargetCounts[tableName]}");
}
return;
}
if (resetRecordingData)
@@ -196,16 +207,15 @@ if (resetRecordingData)
var dbContext = scope.ServiceProvider.GetRequiredService<LiveRecorderDbContext>();
var before = await ReadRecordingDataCountsAsync(dbContext);
await using (var transaction = await dbContext.Database.BeginTransactionAsync())
{
await dbContext.Database.ExecuteSqlRawAsync(
"DELETE FROM SystemLogEntries WHERE LiveRoomId IS NOT NULL OR RecordSessionId IS NOT NULL OR RecordTaskId IS NOT NULL;");
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM RecordResults;");
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM RecordTasks;");
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM RecordSessions;");
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM LiveRooms;");
await transaction.CommitAsync();
}
await using var transaction = await dbContext.Database.BeginTransactionAsync();
await dbContext.SystemLogEntries
.Where(item => item.LiveRoomId != null || item.RecordSessionId != null || item.RecordTaskId != null)
.ExecuteDeleteAsync();
await dbContext.RecordResults.ExecuteDeleteAsync();
await dbContext.RecordTasks.ExecuteDeleteAsync();
await dbContext.RecordSessions.ExecuteDeleteAsync();
await dbContext.LiveRooms.ExecuteDeleteAsync();
await transaction.CommitAsync();
var after = await ReadRecordingDataCountsAsync(dbContext);
Console.WriteLine("Recording data reset complete.");
@@ -249,3 +259,16 @@ static SocketsHttpHandler CreateDouyinHttpHandler(bool useProxy)
}
};
}
static string? GetOptionValue(string[] args, string optionName)
{
for (var index = 0; index < args.Length - 1; index++)
{
if (string.Equals(args[index], optionName, StringComparison.OrdinalIgnoreCase))
{
return args[index + 1];
}
}
return null;
}