Code/SboxSuperuser/Engine/Storage/AuditedDocument.cs

Internal helper around one persisted host setting document. Loads the stored JSON, applies changes only after the store commits a write with an audit intent at the expected revision, and reports read failures to a callback. Callers fall back to built-in state until the initial load succeeds.

namespace SboxSuperuser.Engine.Storage;

/// <summary>
/// One small host setting kept in the store, such as message wording or content bans: loads its state, and saves
/// changes together with an audit intent at the expected revision, the way moderation history is kept. Until it
/// loads, or after a failed read, callers keep using their built-in state.
/// </summary>
internal sealed class AuditedDocument
{
	private readonly IAdminStore _store;
	private readonly StoreKey _key;
	private readonly Func<bool> _available;
	private readonly Action<string> _apply;
	private readonly Action<Exception> _report;
	private readonly Func<DateTimeOffset> _clock;
	private readonly SemaphoreSlim _gate = new( 1, 1 );
	private long _revision;

	/// <param name="apply">Takes the stored state's JSON; throws FormatException for anything invalid.</param>
	internal AuditedDocument( IAdminStore store, StoreKey key, Func<bool> available, Action<string> apply, Action<Exception> report,
		Func<DateTimeOffset> clock = null )
	{
		_store = store;
		_key = key;
		_available = available;
		_apply = apply;
		_report = report;
		_clock = clock ?? (() => DateTimeOffset.UtcNow);
		Initialization = ReloadAsync();
	}

	internal Task Initialization { get; }
	internal StoreReadStatus Status { get; private set; } = StoreReadStatus.Unknown;
	internal bool IsUsable => _available() && Status is StoreReadStatus.Found or StoreReadStatus.Missing;

	internal void OnStorageChanged( StoreKey key )
	{
		if ( key.ModuleId != _key.ModuleId || key.DocumentId != _key.DocumentId ) return;
		Status = StoreReadStatus.Unknown;
		_ = ReloadAsync();
	}

	private async Task ReloadAsync()
	{
		await _gate.WaitAsync();
		try
		{
			var stored = await _store.ReadAsync( _key, 1, CancellationToken.None );
			if ( stored.Status == StoreReadStatus.Found )
			{
				using var json = System.Text.Json.JsonDocument.Parse( stored.Document.PayloadJson );
				_apply( json.RootElement.GetProperty( "state" ).GetRawText() );
				_revision = stored.Document.Revision;
			}
			Status = stored.Status;
		}
		catch ( Exception error )
		{
			Status = error is Core.Storage.UnsupportedStoreSchemaException ? StoreReadStatus.UnsupportedSchema : StoreReadStatus.Corrupt;
			_report( error );
		}
		finally
		{
			_gate.Release();
		}
	}

	/// <summary>Saves new state with an audit intent for the command; applies it only once the store confirms.</summary>
	internal Task<CommandResult> SaveAsync( CommandContext context, string command, string state, string success ) =>
		WriteAsync( new[] { new AuditIntent( Guid.NewGuid(), context.RequestId, context.Actor.Kind, context.Actor.SubjectId,
			context.Origin, command, _clock(), targets: null ) }, state, success, context.Cancellation );

	/// <summary>Saves state no one asked for, such as automatic records of who connected, audited as Superuser's own work.</summary>
	internal Task<CommandResult> SaveAutomaticAsync( string state, string command ) =>
		WriteAsync( new[] { new AuditIntent( Guid.NewGuid(), Guid.NewGuid(), ActorKind.InternalService, "superuser", CommandOrigin.InternalService,
			command, _clock(), targets: null ) }, state, "Saved.", CancellationToken.None );

	private async Task<CommandResult> WriteAsync( AuditIntent[] intents, string state, string success, CancellationToken cancellation )
	{
		await Initialization;
		await _gate.WaitAsync( cancellation );
		try
		{
			if ( !IsUsable ) return new CommandResult( CommandStatus.StorageUnavailable, "This setting's storage is unavailable." );
			var result = await _store.WriteAsync( new PolicyTransaction( state, intents ).CreateWriteRequest( _key, _revision ), cancellation );
			if ( result.Status != StoreWriteStatus.Committed )
			{
				if ( result.Status is StoreWriteStatus.Unknown or StoreWriteStatus.Conflict or StoreWriteStatus.Corrupt ) Status = StoreReadStatus.Unknown;
				return new CommandResult( result.Status == StoreWriteStatus.Unknown ? CommandStatus.Unknown : CommandStatus.StorageUnavailable,
					"The change was not confirmed." );
			}
			_apply( state );
			_revision++;
			Status = StoreReadStatus.Found;
			return new CommandResult( CommandStatus.Success, success );
		}
		finally
		{
			_gate.Release();
		}
	}
}