using MoShiro.Backend.Application; namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteAuditStore : IAuditStore { private readonly SqliteDatabase _db; public SqliteAuditStore(SqliteDatabase db) { _db = db; } public ValueTask AddAsync(AuditRecord record, CancellationToken ct) => _db.ExecuteAsync( c => c.Exec( "INSERT INTO audit_log (at, user_id, actor, method, action, target, detail, remote, success) VALUES (@at, @uid, @actor, @method, @action, @target, @detail, @remote, @ok)", ("@at", record.AtMs), ("@uid", record.UserId), ("@actor", record.Actor), ("@method", record.Method), ("@action", record.Action), ("@target", record.Target), ("@detail", record.Detail), ("@remote", record.Remote), ("@ok", record.Success ? 1 : 0)), ct); public ValueTask> ListAsync(AuditQuery query, CancellationToken ct) => _db.ReadAsync>( c => c.Query( "SELECT id, at, user_id, actor, method, action, target, detail, remote, success FROM audit_log " + "WHERE (@before IS NULL OR id < @before) AND (@actor IS NULL OR actor = @actor) AND (@action IS NULL OR action = @action) ORDER BY id DESC LIMIT @limit", r => new AuditRecord(r.GetInt64(0), r.GetInt64(1), r.NullableString(2), r.GetString(3), r.GetString(4), r.GetString(5), r.NullableString(6), r.NullableString(7), r.NullableString(8), r.GetInt64(9) != 0), ("@before", query.BeforeId), ("@actor", query.Actor), ("@action", query.Action), ("@limit", query.Limit)), ct); public ValueTask DeleteOlderThanAsync(long cutoffMs, CancellationToken ct) => _db.WriteAsync(c => c.Exec("DELETE FROM audit_log WHERE at < @cutoff", ("@cutoff", cutoffMs)), ct); }