2022-06-13 17:49:28 +00:00
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using Newtonsoft.Json;
|
2022-06-13 15:22:00 +00:00
|
|
|
|
using VoidCat.Model;
|
2022-09-08 09:41:31 +00:00
|
|
|
|
using VoidCat.Model.User;
|
2022-06-13 13:35:26 +00:00
|
|
|
|
using VoidCat.Services.Abstractions;
|
|
|
|
|
using VoidCat.Services.Files;
|
2022-09-07 14:52:40 +00:00
|
|
|
|
using VoidCat.Services.Payment;
|
2022-06-13 14:20:12 +00:00
|
|
|
|
using VoidCat.Services.Users;
|
2022-06-13 13:35:26 +00:00
|
|
|
|
|
|
|
|
|
namespace VoidCat.Services.Migrations;
|
|
|
|
|
|
2022-06-13 14:20:12 +00:00
|
|
|
|
/// <inheritdoc />
|
2022-06-13 13:35:26 +00:00
|
|
|
|
public class MigrateToPostgres : IMigration
|
|
|
|
|
{
|
|
|
|
|
private readonly ILogger<MigrateToPostgres> _logger;
|
|
|
|
|
private readonly VoidSettings _settings;
|
|
|
|
|
private readonly IFileMetadataStore _fileMetadata;
|
|
|
|
|
private readonly ICache _cache;
|
2022-09-07 14:52:40 +00:00
|
|
|
|
private readonly IPaymentStore _paymentStore;
|
2022-06-13 14:20:12 +00:00
|
|
|
|
private readonly IUserStore _userStore;
|
2022-06-13 15:11:58 +00:00
|
|
|
|
private readonly IUserUploadsStore _userUploads;
|
2022-07-25 17:59:32 +00:00
|
|
|
|
private readonly FileStoreFactory _fileStore;
|
2022-06-13 13:35:26 +00:00
|
|
|
|
|
|
|
|
|
public MigrateToPostgres(VoidSettings settings, ILogger<MigrateToPostgres> logger, IFileMetadataStore fileMetadata,
|
2022-09-07 14:52:40 +00:00
|
|
|
|
ICache cache, IPaymentStore paymentStore, IUserStore userStore, IUserUploadsStore userUploads,
|
2022-07-25 17:59:32 +00:00
|
|
|
|
FileStoreFactory fileStore)
|
2022-06-13 13:35:26 +00:00
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_settings = settings;
|
|
|
|
|
_fileMetadata = fileMetadata;
|
|
|
|
|
_cache = cache;
|
2022-09-07 14:52:40 +00:00
|
|
|
|
_paymentStore = paymentStore;
|
2022-06-13 14:20:12 +00:00
|
|
|
|
_userStore = userStore;
|
2022-06-13 15:11:58 +00:00
|
|
|
|
_userUploads = userUploads;
|
2022-06-13 17:49:28 +00:00
|
|
|
|
_fileStore = fileStore;
|
2022-06-13 13:35:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 22:03:53 +00:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public int Order => 0;
|
|
|
|
|
|
2022-06-13 14:20:12 +00:00
|
|
|
|
/// <inheritdoc />
|
2022-06-13 13:35:26 +00:00
|
|
|
|
public async ValueTask<IMigration.MigrationResult> Migrate(string[] args)
|
|
|
|
|
{
|
|
|
|
|
if (args.Contains("--migrate-local-metadata-to-postgres"))
|
|
|
|
|
{
|
|
|
|
|
await MigrateFiles();
|
|
|
|
|
return IMigration.MigrationResult.ExitCompleted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.Contains("--migrate-cache-paywall-to-postgres"))
|
|
|
|
|
{
|
|
|
|
|
await MigratePaywall();
|
|
|
|
|
return IMigration.MigrationResult.ExitCompleted;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-13 14:20:12 +00:00
|
|
|
|
if (args.Contains("--migrate-cache-users-to-postgres"))
|
|
|
|
|
{
|
|
|
|
|
await MigrateUsers();
|
|
|
|
|
return IMigration.MigrationResult.ExitCompleted;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-13 13:35:26 +00:00
|
|
|
|
return IMigration.MigrationResult.Skipped;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task MigrateFiles()
|
|
|
|
|
{
|
|
|
|
|
var localDiskMetaStore =
|
|
|
|
|
new LocalDiskFileMetadataStore(_settings);
|
|
|
|
|
|
2022-06-13 15:11:58 +00:00
|
|
|
|
var files = await localDiskMetaStore.ListFiles<UploaderSecretVoidFileMeta>(new(0, int.MaxValue));
|
2022-06-13 13:35:26 +00:00
|
|
|
|
await foreach (var file in files.Results)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2022-06-13 17:49:28 +00:00
|
|
|
|
if (string.IsNullOrEmpty(file.Digest))
|
|
|
|
|
{
|
|
|
|
|
var fs = await _fileStore.Open(new(file.Id, Enumerable.Empty<RangeRequest>()),
|
|
|
|
|
CancellationToken.None);
|
2022-07-25 17:59:32 +00:00
|
|
|
|
|
2022-06-13 17:49:28 +00:00
|
|
|
|
var hash = await SHA256.Create().ComputeHashAsync(fs);
|
|
|
|
|
file.Digest = hash.ToHex();
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-13 14:28:52 +00:00
|
|
|
|
file.MimeType ??= "application/octet-stream";
|
2022-06-13 13:35:26 +00:00
|
|
|
|
await _fileMetadata.Set(file.Id, file);
|
2022-06-13 15:11:58 +00:00
|
|
|
|
if (file.Uploader.HasValue)
|
|
|
|
|
{
|
|
|
|
|
await _userUploads.AddFile(file.Uploader.Value, file.Id);
|
|
|
|
|
}
|
2022-06-13 17:49:28 +00:00
|
|
|
|
|
2022-06-13 13:35:26 +00:00
|
|
|
|
await localDiskMetaStore.Delete(file.Id);
|
|
|
|
|
_logger.LogInformation("Migrated file metadata for {File}", file.Id);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Failed to migrate file metadata for {File}", file.Id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task MigratePaywall()
|
|
|
|
|
{
|
2022-09-07 14:52:40 +00:00
|
|
|
|
var cachePaywallStore = new CachePaymentStore(_cache);
|
2022-06-13 14:20:12 +00:00
|
|
|
|
|
2022-09-11 19:07:38 +00:00
|
|
|
|
var files = await _fileMetadata.ListFiles<FileMeta>(new(0, int.MaxValue));
|
2022-06-13 13:35:26 +00:00
|
|
|
|
await foreach (var file in files.Results)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var old = await cachePaywallStore.Get(file.Id);
|
|
|
|
|
if (old != default)
|
|
|
|
|
{
|
2022-09-07 14:52:40 +00:00
|
|
|
|
await _paymentStore.Add(file.Id, old);
|
2022-06-13 13:35:26 +00:00
|
|
|
|
_logger.LogInformation("Migrated paywall config for {File}", file.Id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Failed to migrate paywall config for {File}", file.Id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-13 14:20:12 +00:00
|
|
|
|
|
|
|
|
|
private async Task MigrateUsers()
|
|
|
|
|
{
|
|
|
|
|
var cacheUsers = new CacheUserStore(_cache);
|
|
|
|
|
|
|
|
|
|
var users = await cacheUsers.ListUsers(new(0, int.MaxValue));
|
|
|
|
|
await foreach (var user in users.Results)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2022-06-13 15:00:02 +00:00
|
|
|
|
var privateUser = await cacheUsers.Get<PrivateUser>(user.Id);
|
|
|
|
|
privateUser!.Password ??= privateUser.PasswordHash;
|
2022-06-13 17:49:28 +00:00
|
|
|
|
|
2022-09-08 09:41:31 +00:00
|
|
|
|
await _userStore.Set(privateUser!.Id, new InternalUser()
|
2022-06-13 15:00:02 +00:00
|
|
|
|
{
|
|
|
|
|
Id = privateUser.Id,
|
|
|
|
|
Avatar = privateUser.Avatar,
|
|
|
|
|
Created = privateUser.Created,
|
|
|
|
|
DisplayName = privateUser.DisplayName,
|
|
|
|
|
Email = privateUser.Email,
|
|
|
|
|
Flags = privateUser.Flags,
|
|
|
|
|
LastLogin = privateUser.LastLogin,
|
|
|
|
|
Password = privateUser.Password!,
|
|
|
|
|
Roles = privateUser.Roles
|
|
|
|
|
});
|
2022-07-25 17:59:32 +00:00
|
|
|
|
|
2022-06-13 14:20:12 +00:00
|
|
|
|
_logger.LogInformation("Migrated user {USer}", user.Id);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Failed to migrate user {User}", user.Id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-13 15:00:02 +00:00
|
|
|
|
|
2022-09-08 09:41:31 +00:00
|
|
|
|
private class PrivateUser : Model.User.PrivateUser
|
2022-06-13 15:00:02 +00:00
|
|
|
|
{
|
|
|
|
|
public string? PasswordHash { get; set; }
|
|
|
|
|
public string? Password { get; set; }
|
|
|
|
|
}
|
2022-06-13 15:11:58 +00:00
|
|
|
|
|
2022-09-11 19:07:38 +00:00
|
|
|
|
private record UploaderSecretVoidFileMeta : SecretFileMeta
|
2022-06-13 15:11:58 +00:00
|
|
|
|
{
|
2022-06-13 15:22:00 +00:00
|
|
|
|
[JsonConverter(typeof(Base58GuidConverter))]
|
2022-06-13 15:11:58 +00:00
|
|
|
|
public Guid? Uploader { get; set; }
|
|
|
|
|
}
|
2022-09-07 14:52:40 +00:00
|
|
|
|
}
|