using MongoDB.Driver; using MongoDB.Entities; namespace PrivaPub.Models { // The server's first-time setup (owner decision 2026-10-16): while no administrator exists, the client's setup page makes // the first login, which becomes the administrator; this document then closes setup for good, whoever is demoted or // deleted later (the CLI stays the way back). The first request to insert it wins, so two setups at once make one // administrator. A backup never holds it and a restore never drops it (ServerBackup.Excluded): a restore tombstones an // administrator made after the backup, and setup would otherwise be open to whoever came first. public class ServerSetup : Entity { public const string Name = "setup"; public string AdminId { get; set; }//null while the claiming request is still making its login public DateTime SealedAt { get; set; } public static async Task IsPending(CancellationToken token) => !await DB.Default.Find().Match(s => s.ID == Name).ExecuteAnyAsync(token); /// Takes the setup for one request: false when it is done, or another request took it first. public static async Task Claim(CancellationToken token) { try { await DB.Default.InsertAsync(new ServerSetup { ID = Name, SealedAt = DateTime.UtcNow }, token); return true; } catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey) { return false; } } /// Names the administrator the claiming request made. public static Task Claimed(string adminId, CancellationToken token) => DB.Default.Update().Match(s => s.ID == Name && s.AdminId == null).Modify(s => s.AdminId, adminId).ExecuteAsync(token); /// Gives a claim back when its login couldn't be made; a named administrator is never given back. public static Task Release() => DB.Default.DeleteAsync(s => s.ID == Name && s.AdminId == null);//a delete takes no token outside a transaction /// Closes setup for an administrator made elsewhere (the CLI, a server that had one already). public static async Task Seal(string adminId, CancellationToken token) { try { await DB.Default.Update().Match(s => s.ID == Name) .Modify(b => b.SetOnInsert(s => s.AdminId, adminId)) .Modify(b => b.SetOnInsert(s => s.SealedAt, DateTime.UtcNow)) .Option(o => o.IsUpsert = true) .ExecuteAsync(token); } catch (MongoCommandException ex) when (ex.Code == 11000) { //sealed meanwhile } catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey) { //sealed meanwhile } } } }