using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; namespace AxCopilot.Services; public static class AuditLogService { private static readonly string AuditDir; private static readonly object _lock; private static readonly JsonSerializerOptions _jsonOpts; static AuditLogService() { _lock = new object(); _jsonOpts = new JsonSerializerOptions { WriteIndented = false, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; AuditDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "audit"); try { Directory.CreateDirectory(AuditDir); } catch { } } public static void Log(AuditEntry entry) { try { string path = $"{DateTime.Now:yyyy-MM-dd}.json"; string path2 = Path.Combine(AuditDir, path); string text = JsonSerializer.Serialize(entry, _jsonOpts); lock (_lock) { File.AppendAllText(path2, text + "\n", Encoding.UTF8); } } catch { } } public static void LogToolCall(string conversationId, string tab, string toolName, string parameters, string result, string? filePath, bool success) { Log(new AuditEntry { ConversationId = conversationId, Tab = tab, Action = "ToolCall", ToolName = toolName, Parameters = Truncate(parameters, 500), Result = Truncate(result, 500), FilePath = filePath, Success = success }); } public static void LogFileAccess(string conversationId, string tab, string action, string filePath, bool success) { Log(new AuditEntry { ConversationId = conversationId, Tab = tab, Action = action, FilePath = filePath, Success = success }); } public static List LoadToday() { string path = $"{DateTime.Now:yyyy-MM-dd}.json"; return LoadFile(Path.Combine(AuditDir, path)); } public static List LoadDate(DateTime date) { string path = $"{date:yyyy-MM-dd}.json"; return LoadFile(Path.Combine(AuditDir, path)); } private static List LoadFile(string filePath) { List list = new List(); if (!File.Exists(filePath)) { return list; } try { string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text)) { AuditEntry auditEntry = JsonSerializer.Deserialize(text, _jsonOpts); if (auditEntry != null) { list.Add(auditEntry); } } } } catch { } return list; } public static void PurgeOldLogs(int retentionDays = 30) { try { DateTime dateTime = DateTime.Now.AddDays(-retentionDays); string[] files = Directory.GetFiles(AuditDir, "*.json"); foreach (string path in files) { if (File.GetCreationTime(path) < dateTime) { File.Delete(path); } } } catch { } } public static string GetAuditFolder() { return AuditDir; } private static string Truncate(string? s, int maxLen) { return string.IsNullOrEmpty(s) ? "" : ((s.Length <= maxLen) ? s : (s.Substring(0, maxLen) + "…")); } }