Files
SocialPub/PrivaPub/Domain/Portability/PersonaArchives.cs
T

79 lines
3.3 KiB
C#
Raw Normal View History

using MongoDB.Entities;
using PrivaPub.ClientModels.User;
namespace PrivaPub.Domain.Portability
{
// A persona's archive, made and restored in jobs (owner decision 2026-10-07): where each stands, one row per persona
// (its ID is the persona's). The files live in <backups>/.personas/<avatarId>/: the export, kept a week, and the upload
// being imported.
public class PersonaArchive : Entity
{
public string ExportState { get; set; } = "none";
public DateTime? ExportAskedAt { get; set; }
public DateTime? ExportReadyAt { get; set; }
public string ExportFile { get; set; }
public long ExportBytes { get; set; }
public string ExportError { get; set; }
public string ImportState { get; set; } = "none";
public DateTime? ImportAskedAt { get; set; }
public DateTime? ImportEndedAt { get; set; }
public string ImportFile { get; set; }
public List<string> ImportParts { get; set; } = [];
public long ImportDone { get; set; }
public long ImportTotal { get; set; }
public Dictionary<string, int> ImportCounts { get; set; } = [];
public string ImportError { get; set; }
public string ImportFrom { get; set; }
public bool ImportSameActor { get; set; }
public bool StopAsked { get; set; }
}
public static class PersonaArchives
{
public static readonly TimeSpan Kept = TimeSpan.FromDays(7);
/// <summary>Every part an import can take; the last three only when asked (they act, or tell others).</summary>
public static readonly string[] Parts = ["posts", "profile", "follows", "blocks", "mutes", "domainblocks", "lists", "bookmarks", "filters", "tags",
"notificationpolicy", "pins", "located", "scheduled", "likes"];
public static string Folder(string backupsRoot, string avatarId) => Path.Combine(backupsRoot, ".personas", avatarId);
public static async Task<PersonaArchive> Of(string avatarId, CancellationToken token) =>
await DB.Default.Find<PersonaArchive>().MatchID(avatarId).ExecuteFirstAsync(token) ?? new PersonaArchive { ID = avatarId };
public static ViewPersonaArchive View(PersonaArchive archive) => new()
{
ExportState = archive.ExportState,
ExportAskedAt = archive.ExportAskedAt,
ExportReadyAt = archive.ExportReadyAt,
ExportExpiresAt = archive.ExportReadyAt + Kept,
ExportBytes = archive.ExportBytes,
ExportError = archive.ExportError,
ImportState = archive.ImportState,
ImportAskedAt = archive.ImportAskedAt,
ImportEndedAt = archive.ImportEndedAt,
ImportParts = archive.ImportParts,
ImportDone = archive.ImportDone,
ImportTotal = archive.ImportTotal,
ImportCounts = archive.ImportCounts,
ImportError = archive.ImportError,
ImportFrom = archive.ImportFrom,
ImportSameActor = archive.ImportSameActor
};
/// <summary>Exports older than a week go, as do imports' files left behind.</summary>
public static async Task Forget(string backupsRoot, CancellationToken token)
{
var folder = Path.Combine(backupsRoot, ".personas");
if (!Directory.Exists(folder))
return;
var before = DateTime.UtcNow - Kept;
foreach (var file in Directory.EnumerateFiles(folder, "*", SearchOption.AllDirectories).Where(f => File.GetLastWriteTimeUtc(f) < before))
File.Delete(file);
await DB.Default.Update<PersonaArchive>().Match(a => a.ExportState == "ready" && a.ExportReadyAt < before)
.Modify(a => a.ExportState, "none").Modify(a => a.ExportFile, null).ExecuteAsync(token);
}
}
}