more of J's code organization.
This commit is contained in:
parent
8171744438
commit
c01548b764
1
journal-master/journal/.gitignore
vendored
1
journal-master/journal/.gitignore
vendored
@ -37,4 +37,3 @@ desktop.ini
|
|||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
journal-master\journal\tls_registry_backup_before_fix.txt
|
|
||||||
@ -7,44 +7,31 @@ using Journal.Core.Services;
|
|||||||
|
|
||||||
namespace Journal.Core;
|
namespace Journal.Core;
|
||||||
|
|
||||||
public class Entry
|
public class Entry(
|
||||||
|
IFragmentService fragments,
|
||||||
|
IEntrySearchService entrySearch,
|
||||||
|
IVaultStorageService vaultStorage,
|
||||||
|
IJournalDatabaseService database,
|
||||||
|
IJournalConfigService config,
|
||||||
|
IAiService ai,
|
||||||
|
ISpeechBridgeService speech,
|
||||||
|
IEntryFileService entryFiles,
|
||||||
|
CommandLogger logger)
|
||||||
{
|
{
|
||||||
private readonly IFragmentService _fragments;
|
private readonly IFragmentService _fragments = fragments;
|
||||||
private readonly IEntrySearchService _entrySearch;
|
private readonly IEntrySearchService _entrySearch = entrySearch;
|
||||||
private readonly IVaultStorageService _vaultStorage;
|
private readonly IVaultStorageService _vaultStorage = vaultStorage;
|
||||||
private readonly IJournalDatabaseService _database;
|
private readonly IJournalDatabaseService _database = database;
|
||||||
private readonly IJournalConfigService _config;
|
private readonly IJournalConfigService _config = config;
|
||||||
private readonly IAiService _ai;
|
private readonly IAiService _ai = ai;
|
||||||
private readonly ISpeechBridgeService _speech;
|
private readonly ISpeechBridgeService _speech = speech;
|
||||||
private readonly IEntryFileService _entryFiles;
|
private readonly IEntryFileService _entryFiles = entryFiles;
|
||||||
private readonly CommandLogger _logger;
|
private readonly CommandLogger _logger = logger;
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
{
|
{
|
||||||
PropertyNameCaseInsensitive = true
|
PropertyNameCaseInsensitive = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public Entry(
|
|
||||||
IFragmentService fragments,
|
|
||||||
IEntrySearchService entrySearch,
|
|
||||||
IVaultStorageService vaultStorage,
|
|
||||||
IJournalDatabaseService database,
|
|
||||||
IJournalConfigService config,
|
|
||||||
IAiService ai,
|
|
||||||
ISpeechBridgeService speech,
|
|
||||||
IEntryFileService entryFiles,
|
|
||||||
CommandLogger logger)
|
|
||||||
{
|
|
||||||
_fragments = fragments;
|
|
||||||
_entrySearch = entrySearch;
|
|
||||||
_vaultStorage = vaultStorage;
|
|
||||||
_database = database;
|
|
||||||
_config = config;
|
|
||||||
_ai = ai;
|
|
||||||
_speech = speech;
|
|
||||||
_entryFiles = entryFiles;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RunAsync()
|
public async Task RunAsync()
|
||||||
{
|
{
|
||||||
string? line;
|
string? line;
|
||||||
@ -77,7 +64,7 @@ public class Entry
|
|||||||
var correlationId = string.IsNullOrWhiteSpace(cmd.CorrelationId)
|
var correlationId = string.IsNullOrWhiteSpace(cmd.CorrelationId)
|
||||||
? Guid.NewGuid().ToString("N")
|
? Guid.NewGuid().ToString("N")
|
||||||
: cmd.CorrelationId.Trim();
|
: cmd.CorrelationId.Trim();
|
||||||
_logger.LogStart(action, correlationId, cmd.Payload);
|
CommandLogger.LogStart(action, correlationId, cmd.Payload);
|
||||||
object? result;
|
object? result;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -260,47 +247,47 @@ public class Entry
|
|||||||
result = _database.HydrateWorkspace(dbHydratePayload.Password, dbHydratePayload.DataDirectory);
|
result = _database.HydrateWorkspace(dbHydratePayload.Password, dbHydratePayload.DataDirectory);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_logger.LogFailure(action, correlationId, "unknown_action");
|
CommandLogger.LogFailure(action, correlationId, "unknown_action");
|
||||||
return Error($"Unknown action: {action}");
|
return Error($"Unknown action: {action}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
_logger.LogFailure(action, correlationId, "invalid_payload_json");
|
CommandLogger.LogFailure(action, correlationId, "invalid_payload_json");
|
||||||
return Error("Missing or invalid payload");
|
return Error("Missing or invalid payload");
|
||||||
}
|
}
|
||||||
catch (ValidationException ex)
|
catch (ValidationException ex)
|
||||||
{
|
{
|
||||||
_logger.LogFailure(action, correlationId, "validation", ex.Message);
|
CommandLogger.LogFailure(action, correlationId, "validation", ex.Message);
|
||||||
return Error(ex.Message);
|
return Error(ex.Message);
|
||||||
}
|
}
|
||||||
catch (ArgumentException ex)
|
catch (ArgumentException ex)
|
||||||
{
|
{
|
||||||
_logger.LogFailure(action, correlationId, "argument", ex.Message);
|
CommandLogger.LogFailure(action, correlationId, "argument", ex.Message);
|
||||||
return Error(ex.Message);
|
return Error(ex.Message);
|
||||||
}
|
}
|
||||||
catch (TimeoutException ex)
|
catch (TimeoutException ex)
|
||||||
{
|
{
|
||||||
_logger.LogFailure(action, correlationId, "timeout", ex.Message);
|
CommandLogger.LogFailure(action, correlationId, "timeout", ex.Message);
|
||||||
return Error(ex.Message);
|
return Error(ex.Message);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException ex)
|
catch (InvalidOperationException ex)
|
||||||
{
|
{
|
||||||
_logger.LogFailure(action, correlationId, "invalid_operation", ex.Message);
|
CommandLogger.LogFailure(action, correlationId, "invalid_operation", ex.Message);
|
||||||
return Error(ex.Message);
|
return Error(ex.Message);
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException ex)
|
catch (FileNotFoundException ex)
|
||||||
{
|
{
|
||||||
_logger.LogFailure(action, correlationId, "not_found", ex.Message);
|
CommandLogger.LogFailure(action, correlationId, "not_found", ex.Message);
|
||||||
return Error(ex.Message);
|
return Error(ex.Message);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
_logger.LogFailure(action, correlationId, "internal_error");
|
CommandLogger.LogFailure(action, correlationId, "internal_error");
|
||||||
return Error("Internal error");
|
return Error("Internal error");
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogSuccess(action, correlationId);
|
CommandLogger.LogSuccess(action, correlationId);
|
||||||
return JsonSerializer.Serialize(new { ok = true, data = result });
|
return JsonSerializer.Serialize(new { ok = true, data = result });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,9 +7,7 @@ public sealed class DiskEntryFileRepository : IEntryFileRepository
|
|||||||
if (!Directory.Exists(dataDirectory))
|
if (!Directory.Exists(dataDirectory))
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
return Directory.GetFiles(dataDirectory, "*.md")
|
return [.. Directory.GetFiles(dataDirectory, "*.md").OrderBy(Path.GetFileName, StringComparer.Ordinal)];
|
||||||
.OrderBy(Path.GetFileName, StringComparer.Ordinal)
|
|
||||||
.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ReadFile(string filePath) => File.ReadAllText(filePath);
|
public string ReadFile(string filePath) => File.ReadAllText(filePath);
|
||||||
|
|||||||
@ -183,7 +183,7 @@ public class FileFragmentRepository : IFragmentRepository
|
|||||||
return [];
|
return [];
|
||||||
|
|
||||||
var docs = JsonSerializer.Deserialize<List<FragmentDocument>>(json, _jsonOptions) ?? [];
|
var docs = JsonSerializer.Deserialize<List<FragmentDocument>>(json, _jsonOptions) ?? [];
|
||||||
return docs.Select(d => new Fragment(d.Id, d.Type, d.Description, d.Time, d.Tags)).ToList();
|
return [.. docs.Select(d => new Fragment(d.Id, d.Type, d.Description, d.Time, d.Tags))];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveStoreLocked()
|
private void SaveStoreLocked()
|
||||||
|
|||||||
@ -5,18 +5,18 @@ namespace Journal.Core.Services;
|
|||||||
|
|
||||||
public sealed class CommandLogger
|
public sealed class CommandLogger
|
||||||
{
|
{
|
||||||
public void LogStart(string action, string correlationId, JsonElement? payload)
|
public static void LogStart(string action, string correlationId, JsonElement? payload)
|
||||||
{
|
{
|
||||||
var redactedPayload = LogRedactor.RedactPayload(payload);
|
var redactedPayload = LogRedactor.RedactPayload(payload);
|
||||||
EmitLog("information", action, correlationId, "start", redactedPayload);
|
EmitLog("information", action, correlationId, "start", redactedPayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LogSuccess(string action, string correlationId)
|
public static void LogSuccess(string action, string correlationId)
|
||||||
{
|
{
|
||||||
EmitLog("information", action, correlationId, "success");
|
EmitLog("information", action, correlationId, "success");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LogFailure(string action, string correlationId, string errorType, string? message = null)
|
public static void LogFailure(string action, string correlationId, string errorType, string? message = null)
|
||||||
{
|
{
|
||||||
var details = string.IsNullOrWhiteSpace(message)
|
var details = string.IsNullOrWhiteSpace(message)
|
||||||
? ""
|
? ""
|
||||||
|
|||||||
@ -2,18 +2,11 @@ using Journal.Core.Dtos;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public sealed class DisabledAiService : IAiService
|
public sealed class DisabledAiService(string provider, string message = "AI provider disabled.", bool healthy = true) : IAiService
|
||||||
{
|
{
|
||||||
private readonly string _provider;
|
private readonly string _provider = string.IsNullOrWhiteSpace(provider) ? "none" : provider.Trim();
|
||||||
private readonly string _message;
|
private readonly string _message = string.IsNullOrWhiteSpace(message) ? "AI provider disabled." : message.Trim();
|
||||||
private readonly bool _healthy;
|
private readonly bool _healthy = healthy;
|
||||||
|
|
||||||
public DisabledAiService(string provider, string message = "AI provider disabled.", bool healthy = true)
|
|
||||||
{
|
|
||||||
_provider = string.IsNullOrWhiteSpace(provider) ? "none" : provider.Trim();
|
|
||||||
_message = string.IsNullOrWhiteSpace(message) ? "AI provider disabled." : message.Trim();
|
|
||||||
_healthy = healthy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<AiHealthDto> HealthAsync(CancellationToken cancellationToken = default) =>
|
public Task<AiHealthDto> HealthAsync(CancellationToken cancellationToken = default) =>
|
||||||
Task.FromResult(new AiHealthDto(_provider, Enabled: false, Healthy: _healthy, Message: _message));
|
Task.FromResult(new AiHealthDto(_provider, Enabled: false, Healthy: _healthy, Message: _message));
|
||||||
|
|||||||
@ -2,16 +2,10 @@ using Journal.Core.Dtos;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public sealed class DisabledSpeechBridgeService : ISpeechBridgeService
|
public sealed class DisabledSpeechBridgeService(string provider = "none", string message = "Speech bridge is disabled.") : ISpeechBridgeService
|
||||||
{
|
{
|
||||||
private readonly string _provider;
|
private readonly string _provider = string.IsNullOrWhiteSpace(provider) ? "none" : provider.Trim();
|
||||||
private readonly string _message;
|
private readonly string _message = string.IsNullOrWhiteSpace(message) ? "Speech bridge is disabled." : message.Trim();
|
||||||
|
|
||||||
public DisabledSpeechBridgeService(string provider = "none", string message = "Speech bridge is disabled.")
|
|
||||||
{
|
|
||||||
_provider = string.IsNullOrWhiteSpace(provider) ? "none" : provider.Trim();
|
|
||||||
_message = string.IsNullOrWhiteSpace(message) ? "Speech bridge is disabled." : message.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<SpeechDevicesResultDto> ListDevicesAsync(CancellationToken cancellationToken = default)
|
public Task<SpeechDevicesResultDto> ListDevicesAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,20 +3,16 @@ using Journal.Core.Repositories;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public sealed class EntryFileService : IEntryFileService
|
public sealed class EntryFileService(IEntryFileRepository repo) : IEntryFileService
|
||||||
{
|
{
|
||||||
private readonly IEntryFileRepository _repo;
|
private readonly IEntryFileRepository _repo = repo ?? throw new ArgumentNullException(nameof(repo));
|
||||||
|
|
||||||
public EntryFileService(IEntryFileRepository repo) =>
|
|
||||||
_repo = repo ?? throw new ArgumentNullException(nameof(repo));
|
|
||||||
|
|
||||||
public IReadOnlyList<EntryListItem> ListEntries(string dataDirectory)
|
public IReadOnlyList<EntryListItem> ListEntries(string dataDirectory)
|
||||||
{
|
{
|
||||||
return _repo.ListMarkdownFiles(dataDirectory)
|
return [.. _repo.ListMarkdownFiles(dataDirectory)
|
||||||
.Select(path => new EntryListItem(
|
.Select(path => new EntryListItem(
|
||||||
FileName: _repo.GetFileName(path),
|
FileName: _repo.GetFileName(path),
|
||||||
FilePath: _repo.GetFullPath(path)))
|
FilePath: _repo.GetFullPath(path)))];
|
||||||
.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntryLoadResult LoadEntry(string filePath)
|
public EntryLoadResult LoadEntry(string filePath)
|
||||||
|
|||||||
@ -5,11 +5,9 @@ using Journal.Core.Repositories;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public class FragmentService : IFragmentService
|
public class FragmentService(IFragmentRepository repo) : IFragmentService
|
||||||
{
|
{
|
||||||
private readonly IFragmentRepository _repo;
|
private readonly IFragmentRepository _repo = repo ?? throw new ArgumentNullException(nameof(repo));
|
||||||
|
|
||||||
public FragmentService(IFragmentRepository repo) => _repo = repo ?? throw new ArgumentNullException(nameof(repo));
|
|
||||||
|
|
||||||
private static FragmentDto Map(Fragment f) => new(
|
private static FragmentDto Map(Fragment f) => new(
|
||||||
f.Id,
|
f.Id,
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using Microsoft.Data.Sqlite;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public sealed class JournalDatabaseService : IJournalDatabaseService
|
public sealed class JournalDatabaseService(IJournalConfigService config) : IJournalDatabaseService
|
||||||
{
|
{
|
||||||
public const int KeySize = 32;
|
public const int KeySize = 32;
|
||||||
public const int Iterations = 600_000;
|
public const int Iterations = 600_000;
|
||||||
@ -15,12 +15,7 @@ public sealed class JournalDatabaseService : IJournalDatabaseService
|
|||||||
private static readonly IReadOnlyList<string> RequiredSchemaTables =
|
private static readonly IReadOnlyList<string> RequiredSchemaTables =
|
||||||
["entries", "sections", "fragments", "tags", "fragment_tags"];
|
["entries", "sections", "fragments", "tags", "fragment_tags"];
|
||||||
|
|
||||||
private readonly IJournalConfigService _config;
|
private readonly IJournalConfigService _config = config;
|
||||||
|
|
||||||
public JournalDatabaseService(IJournalConfigService config)
|
|
||||||
{
|
|
||||||
_config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetDatabasePath(string? dataDirectory = null)
|
public string GetDatabasePath(string? dataDirectory = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,19 +4,14 @@ using Journal.Core.Models;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public sealed class PythonSidecarClient
|
public sealed class PythonSidecarClient(JournalConfig config)
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
{
|
{
|
||||||
PropertyNameCaseInsensitive = true
|
PropertyNameCaseInsensitive = true
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly JournalConfig _config;
|
private readonly JournalConfig _config = config;
|
||||||
|
|
||||||
public PythonSidecarClient(JournalConfig config)
|
|
||||||
{
|
|
||||||
_config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<JsonElement?> SendAsync(string action, object payload, CancellationToken cancellationToken)
|
public async Task<JsonElement?> SendAsync(string action, object payload, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,18 +3,11 @@ using Journal.Core.Dtos;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public sealed class SidecarCli
|
public sealed class SidecarCli(IVaultStorageService vaultStorage, IEntrySearchService entrySearch, IJournalConfigService config)
|
||||||
{
|
{
|
||||||
private readonly IVaultStorageService _vaultStorage;
|
private readonly IVaultStorageService _vaultStorage = vaultStorage;
|
||||||
private readonly IEntrySearchService _entrySearch;
|
private readonly IEntrySearchService _entrySearch = entrySearch;
|
||||||
private readonly IJournalConfigService _config;
|
private readonly IJournalConfigService _config = config;
|
||||||
|
|
||||||
public SidecarCli(IVaultStorageService vaultStorage, IEntrySearchService entrySearch, IJournalConfigService config)
|
|
||||||
{
|
|
||||||
_vaultStorage = vaultStorage;
|
|
||||||
_entrySearch = entrySearch;
|
|
||||||
_config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> RunAsync(string[] args, Entry entry)
|
public async Task<int> RunAsync(string[] args, Entry entry)
|
||||||
{
|
{
|
||||||
@ -34,9 +27,9 @@ public sealed class SidecarCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(args[0], "vault", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(args[0], "vault", StringComparison.OrdinalIgnoreCase))
|
||||||
return RunVaultCommand(args.Skip(1).ToArray());
|
return RunVaultCommand([.. args.Skip(1)]);
|
||||||
if (string.Equals(args[0], "search", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(args[0], "search", StringComparison.OrdinalIgnoreCase))
|
||||||
return RunSearchCommand(args.Skip(1).ToArray());
|
return RunSearchCommand([.. args.Skip(1)]);
|
||||||
|
|
||||||
Console.Error.WriteLine($"Unknown command: {args[0]}");
|
Console.Error.WriteLine($"Unknown command: {args[0]}");
|
||||||
PrintUsage();
|
PrintUsage();
|
||||||
@ -60,7 +53,7 @@ public sealed class SidecarCli
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryParseVaultOptions(args.Skip(1).ToArray(), out var options, out var parseError))
|
if (!TryParseVaultOptions([.. args.Skip(1)], out var options, out var parseError))
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine(parseError);
|
Console.Error.WriteLine(parseError);
|
||||||
PrintVaultUsage();
|
PrintVaultUsage();
|
||||||
|
|||||||
@ -6,14 +6,12 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace Journal.Core.Services;
|
namespace Journal.Core.Services;
|
||||||
|
|
||||||
public class VaultStorageService : IVaultStorageService
|
public class VaultStorageService(IVaultCryptoService crypto) : IVaultStorageService
|
||||||
{
|
{
|
||||||
private readonly IVaultCryptoService _crypto;
|
private readonly IVaultCryptoService _crypto = crypto;
|
||||||
private readonly Dictionary<string, string> _monthFingerprintCache = new(StringComparer.Ordinal);
|
private readonly Dictionary<string, string> _monthFingerprintCache = new(StringComparer.Ordinal);
|
||||||
private readonly object _vaultIoLock = new();
|
private readonly object _vaultIoLock = new();
|
||||||
|
|
||||||
public VaultStorageService(IVaultCryptoService crypto) => _crypto = crypto;
|
|
||||||
|
|
||||||
public string GetMonthlyVaultFileName(DateTime date) => date.ToString("yyyy-MM") + ".vault";
|
public string GetMonthlyVaultFileName(DateTime date) => date.ToString("yyyy-MM") + ".vault";
|
||||||
|
|
||||||
public bool LoadAllVaults(string password, string vaultDirectory, string dataDirectory)
|
public bool LoadAllVaults(string password, string vaultDirectory, string dataDirectory)
|
||||||
|
|||||||
@ -2,4 +2,5 @@
|
|||||||
<Project Path="Journal.Api/Journal.Api.csproj" />
|
<Project Path="Journal.Api/Journal.Api.csproj" />
|
||||||
<Project Path="Journal.Core/Journal.Core.csproj" />
|
<Project Path="Journal.Core/Journal.Core.csproj" />
|
||||||
<Project Path="Journal.Sidecar/Journal.Sidecar.csproj" />
|
<Project Path="Journal.Sidecar/Journal.Sidecar.csproj" />
|
||||||
|
<Project Path="Journal.SmokeTests/Journal.SmokeTests.csproj" />
|
||||||
</Solution>
|
</Solution>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user