62 lines
2.8 KiB
C#
62 lines
2.8 KiB
C#
using MongoDB.Entities;
|
|||
|
|
|
||
|
|
namespace PrivaPub.Infrastructure.Backup
|
||
|
|
{
|
||
|
|
// What a restore did (or why it was abandoned), kept for the administrator's page and for the followers' grace: for
|
||
|
|
// FollowersGrace after a restore the followers' digests (FEP-8fcf) are neither sent nor acted on to undo a follow, since
|
||
|
|
// what the restore lost of either side is not the other's fault.
|
||
|
|
public class RestoreRecord : Entity
|
||
|
|
{
|
||
|
|
public static readonly TimeSpan FollowersGrace = TimeSpan.FromDays(14);
|
||
|
|
|
||
|
|
public string Backup { get; set; }
|
||
|
|
public DateTime BackupCreatedAt { get; set; }
|
||
|
|
public string PreRestore { get; set; }
|
||
|
|
public string RequestedBy { get; set; }
|
||
|
|
public DateTime RequestedAt { get; set; }
|
||
|
|
public DateTime RestoredAt { get; set; } = DateTime.UtcNow;
|
||
|
|
public int Attempts { get; set; }
|
||
|
|
public bool Abandoned { get; set; }//nothing was changed: the server booted as it was
|
||
|
|
public string Error { get; set; }
|
||
|
|
public RestoreReport Report { get; set; } = new();
|
||
|
|
|
||
|
|
static (DateTime Until, DateTime Read) _grace;
|
||
|
|
|
||
|
|
/// <summary>Whether a restore ended less than FollowersGrace ago (read at most once a minute).</summary>
|
||
|
|
public static async Task<bool> InFollowersGrace(CancellationToken token)
|
||
|
|
{
|
||
|
|
var now = DateTime.UtcNow;
|
||
|
|
var cached = _grace;
|
||
|
|
if (now - cached.Read > TimeSpan.FromMinutes(1))
|
||
|
|
{
|
||
|
|
var last = await DB.Default.Find<RestoreRecord>().Match(r => !r.Abandoned).Sort(r => r.RestoredAt, Order.Descending).ExecuteFirstAsync(token);
|
||
|
|
cached = (last == default ? DateTime.MinValue : last.RestoredAt + FollowersGrace, now);
|
||
|
|
_grace = cached;
|
||
|
|
}
|
||
|
|
return now < cached.Until;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Forgets what was read, so a restore made in this process counts at once.</summary>
|
||
|
|
public static void Forget() => _grace = default;
|
||
|
|
}
|
||
|
|
|
||
|
|
public class RestoreReport
|
||
|
|
{
|
||
|
|
public int Collections { get; set; }
|
||
|
|
public long Documents { get; set; }
|
||
|
|
public int CollectionsDropped { get; set; }//live collections the backup had no documents in
|
||
|
|
public int MediaRestored { get; set; }//files the live directory lacked, brought back
|
||
|
|
public int MediaMissing { get; set; }
|
||
|
|
public int RootsDeleted { get; set; }//deleted since the backup, deleted again
|
||
|
|
public int PersonasDeleted { get; set; }
|
||
|
|
public int GroupsDeleted { get; set; }
|
||
|
|
public int PostsDeleted { get; set; }
|
||
|
|
public List<string> RootsTombstoned { get; set; } = [];//made after the backup: deleted, their names kept
|
||
|
|
public List<string> PersonasTombstoned { get; set; } = [];
|
||
|
|
public List<string> GroupsTombstoned { get; set; } = [];
|
||
|
|
public int PostsGone { get; set; }//local posts made after the backup: 410 from now on
|
||
|
|
public int MediaTrashed { get; set; }
|
||
|
|
public int ProtectiveKept { get; set; }//blocks, mutes, domain blocks, reserved names, reports, filters, deletions kept from since
|
||
|
|
public int SessionsEnded { get; set; }
|
||
|
|
}
|
||
|
|
}
|