Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Nickvision.Desktop.Tests/DatabaseServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ public void Case001_Init()
{
_databaseService = new DatabaseService(new MockLogger<DatabaseService>(), new AppInfo("org.nickvision.desktop.test.database", "Nickvision.Desktop.Test.Database", "Test Database"), new SecretService(new MockLogger<SecretService>()));
Assert.IsNotNull(_databaseService);
Assert.IsFalse(_databaseService.IsEncrypted);
}

[TestMethod]
public void Case002_EnsureTableAndTransaction()
{
Assert.IsNotNull(_databaseService);
Assert.IsTrue(_databaseService.EnsureTableExists("test_table", "id TEXT PRIMARY KEY, name TEXT, age INTEGER"));
Assert.IsTrue(_databaseService.IsEncrypted);
Assert.IsTrue(_databaseService.TableExists("test_table"));
Assert.IsFalse(_databaseService.TableExists("missing_table"));
using var transaction = _databaseService.CreateTransaction();
Expand Down
2 changes: 1 addition & 1 deletion Nickvision.Desktop.WinUI/Nickvision.Desktop.WinUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260416003" />
<PackageReference Include="Nickvision.Desktop" Version="2026.4.7" />
</ItemGroup>

Expand Down
51 changes: 45 additions & 6 deletions Nickvision.Desktop/Application/DatabaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ public class DatabaseService : IAsyncDisposable, IDisposable, IDatabaseService
private readonly AppInfo _appInfo;
private SqliteConnection? _connection;

public bool IsEncrypted { get; private set; }

public DatabaseService(ILogger<DatabaseService> logger, AppInfo appInfo, ISecretService secretService)
{
_logger = logger;
_secretService = secretService;
_appInfo = appInfo;
_connection = null;
IsEncrypted = false;
}

~DatabaseService()
Expand Down Expand Up @@ -471,26 +474,44 @@ private void EnsureDatabase()
var secret = string.Empty;
if (!_appInfo.IsPortable && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()))
{
secret = ((Task.Run(() => _secretService.GetAsync(_appInfo.Id)).GetAwaiter().GetResult()) ?? (Task.Run(() => _secretService.CreateAsync(_appInfo.Id)).GetAwaiter().GetResult()))?.Value;
try
{
secret = ((Task.Run(() => _secretService.GetAsync(_appInfo.Id)).GetAwaiter().GetResult()) ?? (Task.Run(() => _secretService.CreateAsync(_appInfo.Id)).GetAwaiter().GetResult()))?.Value;
}
catch (Exception e)
{
_logger.LogWarning($"Secret service unavailable: {e.Message}. The database will not be encrypted.");
}
}
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
_connection = new SqliteConnection(new SqliteConnectionStringBuilder($"Data Source='{path}'")
{
Mode = SqliteOpenMode.ReadWriteCreate,
Password = secret,
Password = secret ?? string.Empty,
Pooling = false
}.ToString());
try
{
_connection.Open();
IsEncrypted = !string.IsNullOrEmpty(secret);
_logger.LogDebug($"Opened application database ({path}).");
}
catch (SqliteException e)
{
_logger.LogError($"Failed to open application database: {e}");
_connection.Dispose();
_connection = null;
throw;
if (string.IsNullOrEmpty(secret))
{
_logger.LogWarning("The database may be encrypted but the secret service is unavailable. Falling back to an in-memory database.");
_connection = new SqliteConnection("Data Source=:memory:");
_connection.Open();
IsEncrypted = false;
}
else
{
throw;
}
}
}

Expand All @@ -505,26 +526,44 @@ private async Task EnsureDatabaseAsync()
var secret = string.Empty;
if (!_appInfo.IsPortable && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()))
{
secret = ((await _secretService.GetAsync(_appInfo.Id)) ?? (await _secretService.CreateAsync(_appInfo.Id)))?.Value;
try
{
secret = ((await _secretService.GetAsync(_appInfo.Id)) ?? (await _secretService.CreateAsync(_appInfo.Id)))?.Value;
}
catch (Exception e)
{
_logger.LogWarning($"Secret service unavailable: {e.Message}. The database will not be encrypted.");
}
}
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
_connection = new SqliteConnection(new SqliteConnectionStringBuilder($"Data Source='{path}'")
{
Mode = SqliteOpenMode.ReadWriteCreate,
Password = secret,
Password = secret ?? string.Empty,
Pooling = false
}.ToString());
try
{
await _connection.OpenAsync();
IsEncrypted = !string.IsNullOrEmpty(secret);
_logger.LogDebug($"Opened application database ({path}).");
}
catch (SqliteException e)
{
_logger.LogError($"Failed to open application database: {e}");
await _connection.DisposeAsync();
_connection = null;
throw;
if (string.IsNullOrEmpty(secret))
{
_logger.LogWarning("The database may be encrypted but the secret service is unavailable. Falling back to an in-memory database.");
_connection = new SqliteConnection("Data Source=:memory:");
await _connection.OpenAsync();
IsEncrypted = false;
}
else
{
throw;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions Nickvision.Desktop/Application/IDatabaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Nickvision.Desktop.Application;

public interface IDatabaseService
{
bool IsEncrypted { get; }
bool ClearTable(string tableName);
Task<bool> ClearTableAsync(string tableName);
int CountInTable(string tableName);
Expand Down
21 changes: 15 additions & 6 deletions Nickvision.Desktop/FreeDesktop/SecretServiceProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,24 @@ private SecretServiceProxy(DBusConnection connection, string sessionPath)
{
return null;
}
var connection = new DBusConnection(sessionAddress);
await connection.ConnectAsync();
var sessionPath = await OpenSessionAsync(connection);
if (string.IsNullOrEmpty(sessionPath) || sessionPath == "/")
DBusConnection? connection = null;
try
{
connection = new DBusConnection(sessionAddress);
await connection.ConnectAsync();
var sessionPath = await OpenSessionAsync(connection);
if (string.IsNullOrEmpty(sessionPath) || sessionPath == "/")
{
connection.Dispose();
return null;
}
return new SecretServiceProxy(connection, sessionPath);
}
catch (Exception)
{
connection.Dispose();
connection?.Dispose();
return null;
}
return new SecretServiceProxy(connection, sessionPath);
}

internal async Task<string?> GetDefaultCollectionPathAsync()
Expand Down
12 changes: 6 additions & 6 deletions Nickvision.Desktop/Nickvision.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<PackageId>Nickvision.Desktop</PackageId>
<Version>2026.4.7</Version>
<Version>2026.4.8</Version>
<Company>Nickvision</Company>
<Authors>Nickvision</Authors>
<Description>A cross-platform base for Nickvision desktop applications.</Description>
Expand All @@ -26,16 +26,16 @@
<ItemGroup>
<PackageReference Include="GetText.NET" Version="10.0.1" />
<PackageReference Include="Markdig" Version="1.1.3" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.6" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.7" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.275">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="NReco.Logging.File" Version="1.3.1" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.11" />
<PackageReference Include="System.Drawing.Common" Version="10.0.6" />
<PackageReference Include="System.Drawing.Common" Version="10.0.7" />
<PackageReference Include="Tmds.DBus.Protocol" Version="0.92.0" />
</ItemGroup>

Expand Down
Loading