using Microsoft.Data.Sqlite; namespace MxGateway.Server.Security.Authentication; public sealed class SqliteApiKeyAuditStore(AuthSqliteConnectionFactory connectionFactory) : IApiKeyAuditStore { public async Task AppendAsync(ApiKeyAuditEntry entry, CancellationToken cancellationToken) { await using SqliteConnection connection = connectionFactory.CreateConnection(); await connection.OpenAsync(cancellationToken).ConfigureAwait(false); await using SqliteCommand command = connection.CreateCommand(); command.CommandText = """ INSERT INTO api_key_audit (key_id, event_type, remote_address, created_utc, details) VALUES ($key_id, $event_type, $remote_address, $created_utc, $details); """; command.Parameters.AddWithValue("$key_id", (object?)entry.KeyId ?? DBNull.Value); command.Parameters.AddWithValue("$event_type", entry.EventType); command.Parameters.AddWithValue("$remote_address", (object?)entry.RemoteAddress ?? DBNull.Value); command.Parameters.AddWithValue("$created_utc", DateTimeOffset.UtcNow.ToString("O")); command.Parameters.AddWithValue("$details", (object?)entry.Details ?? DBNull.Value); await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } public async Task> ListRecentAsync(int count, CancellationToken cancellationToken) { if (count <= 0) { return []; } await using SqliteConnection connection = connectionFactory.CreateConnection(); await connection.OpenAsync(cancellationToken).ConfigureAwait(false); await using SqliteCommand command = connection.CreateCommand(); command.CommandText = """ SELECT audit_id, key_id, event_type, remote_address, created_utc, details FROM api_key_audit ORDER BY audit_id DESC LIMIT $count; """; command.Parameters.AddWithValue("$count", count); List records = []; await using SqliteDataReader reader = await command.ExecuteReaderAsync(cancellationToken) .ConfigureAwait(false); while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { records.Add(new ApiKeyAuditRecord( AuditId: reader.GetInt64(0), KeyId: reader.IsDBNull(1) ? null : reader.GetString(1), EventType: reader.GetString(2), RemoteAddress: reader.IsDBNull(3) ? null : reader.GetString(3), CreatedUtc: DateTimeOffset.Parse( reader.GetString(4), System.Globalization.CultureInfo.InvariantCulture), Details: reader.IsDBNull(5) ? null : reader.GetString(5))); } return records; } }