CRUD implemented
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../Logic/Logic.csproj" />
|
<ProjectReference Include="../Logic/Logic.csproj" />
|
||||||
|
<ProjectReference Include="../Repository/Repository.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Logic;
|
using Logic;
|
||||||
|
using Repository;
|
||||||
|
|
||||||
namespace Keychain;
|
namespace Keychain;
|
||||||
|
|
||||||
@@ -25,8 +26,8 @@ class Program
|
|||||||
private static ServiceProvider SetupServices()
|
private static ServiceProvider SetupServices()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
//services.AddTransient<IPasswordStoreService, PasswordStoreService>();
|
|
||||||
services.AddSingleton<IPasswordStoreService, PasswordStoreService>();
|
services.AddSingleton<IPasswordStoreService, PasswordStoreService>();
|
||||||
|
services.AddSingleton<IRepository, JsonRepository>();
|
||||||
return services.BuildServiceProvider();
|
return services.BuildServiceProvider();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using Adw;
|
using Adw;
|
||||||
using Keychain.UI.ViewModels;
|
using Keychain.ViewModels;
|
||||||
|
|
||||||
namespace Keychain.UI;
|
namespace Keychain.UI;
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,9 @@ namespace Keychain.ViewModels;
|
|||||||
|
|
||||||
public class PasswordStoreShortcut : INotifyPropertyChanged
|
public class PasswordStoreShortcut : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private IPasswordService passwordService;
|
private IPasswordStoreService passwordService;
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
private string displayName;
|
|
||||||
private bool displayNameSet = false;
|
|
||||||
private string? iconName;
|
|
||||||
private string path;
|
|
||||||
public bool DisplayNameSet { get => displayNameSet; }
|
|
||||||
|
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
get => displayName;
|
get => displayName;
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ public interface IPasswordStoreService
|
|||||||
{
|
{
|
||||||
IEnumerable<PasswordStore> GetAll();
|
IEnumerable<PasswordStore> GetAll();
|
||||||
PasswordStore Get(uint ID);
|
PasswordStore Get(uint ID);
|
||||||
int Delete(uint ID);
|
void Delete(uint ID);
|
||||||
int Delete(PasswordStore item);
|
void Delete(PasswordStore item);
|
||||||
int Create(string path, string? displayName = null, string? iconName = null);
|
void Create(PasswordStore item);
|
||||||
int Create(PasswordStore item);
|
void Edit(uint ID, PasswordStore newItem);
|
||||||
int Edit(uint ID, PasswordStore newItem);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,38 +7,33 @@ public class PasswordStoreService : IPasswordStoreService
|
|||||||
{
|
{
|
||||||
private readonly IRepository repository;
|
private readonly IRepository repository;
|
||||||
|
|
||||||
public int Create(string path, string? displayName = null, string? iconName = null)
|
public void Create(PasswordStore item)
|
||||||
|
{
|
||||||
|
Create(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(uint ID)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Create(PasswordStore item)
|
public void Delete(PasswordStore item)
|
||||||
{
|
{
|
||||||
return Create(item.Path, item.DisplayName, item.IconName);
|
Delete(item.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Delete(uint ID)
|
public void Edit(uint ID, PasswordStore newItem)
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Delete(PasswordStore item)
|
|
||||||
{
|
|
||||||
return Delete(item.ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Edit(uint ID, PasswordStore newItem)
|
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PasswordStore Get(uint ID)
|
public PasswordStore Get(uint ID)
|
||||||
{
|
{
|
||||||
return repository.ReadAll().Where(item => item.ID.Equals(ID)).First();
|
return repository.Get(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<PasswordStore> GetAll()
|
public IEnumerable<PasswordStore> GetAll()
|
||||||
{
|
{
|
||||||
return (IEnumerable<PasswordStore>)repository.ReadAll();
|
return (IEnumerable<PasswordStore>)repository.GetAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
public class PasswordStore
|
public class PasswordStore
|
||||||
{
|
{
|
||||||
public uint ID;
|
public uint ID { get; set; }
|
||||||
public string Path;
|
public string Path { get; set; }
|
||||||
public string? DisplayName;
|
public string? DisplayName { get; set; }
|
||||||
public string? IconName;
|
public string? IconName { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using Models;
|
||||||
|
|
||||||
namespace Repository;
|
namespace Repository;
|
||||||
|
|
||||||
public interface IRepository
|
public interface IRepository
|
||||||
{
|
{
|
||||||
IEnumerable ReadAll();
|
List<PasswordStore> GetAll();
|
||||||
void WriteAll(IEnumerable items);
|
PasswordStore? Get(uint id);
|
||||||
object Get(uint id);
|
void Edit(uint ID, PasswordStore newItem);
|
||||||
|
void Create(PasswordStore item);
|
||||||
|
void Delete(uint ID);
|
||||||
|
void Delete(PasswordStore item);
|
||||||
}
|
}
|
||||||
172
Repository/JsonRepository.cs
Normal file
172
Repository/JsonRepository.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Models;
|
||||||
|
|
||||||
|
namespace Repository;
|
||||||
|
|
||||||
|
public class JsonRepository : IRepository, IDisposable
|
||||||
|
{
|
||||||
|
private const string _appName = "Keychain";
|
||||||
|
|
||||||
|
private uint _autoIncrementedId;
|
||||||
|
private readonly string _filePath;
|
||||||
|
private List<PasswordStore> _cache;
|
||||||
|
private bool _cacheAhead;
|
||||||
|
|
||||||
|
public JsonRepository(string fileName)
|
||||||
|
{
|
||||||
|
string? xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
|
||||||
|
string dataHome;
|
||||||
|
if (!string.IsNullOrEmpty(xdgDataHome))
|
||||||
|
{
|
||||||
|
dataHome = Path.Combine(xdgDataHome, _appName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dataHome = Path.Combine(
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"),
|
||||||
|
_appName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_filePath = Path.Combine(dataHome, fileName);
|
||||||
|
|
||||||
|
ReadAllFromFile();
|
||||||
|
|
||||||
|
var lastItem = _cache.OrderBy(item => item.ID).LastOrDefault();
|
||||||
|
_autoIncrementedId = lastItem != null ? lastItem.ID : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadAllFromFile()
|
||||||
|
{
|
||||||
|
var items = new List<PasswordStore>();
|
||||||
|
|
||||||
|
if (File.Exists(_filePath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string json = File.ReadAllText(_filePath);
|
||||||
|
items = JsonSerializer.Deserialize<List<PasswordStore>>(json) ?? new List<PasswordStore>();
|
||||||
|
}
|
||||||
|
catch (JsonException e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"JSON error: {e.Message}");
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"File I/O error: {e.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"Unexpected error: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache = items;
|
||||||
|
_cacheAhead = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PasswordStore> GetAll()
|
||||||
|
{
|
||||||
|
return _cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordStore? Get(uint id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _cache.First(item => item.ID.Equals(id));
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// Not found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"Unexpected error: {e.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_cacheAhead)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string json = JsonSerializer.Serialize(_cache);
|
||||||
|
string? directory = Path.GetDirectoryName(_filePath);
|
||||||
|
if (!Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory!);
|
||||||
|
}
|
||||||
|
File.WriteAllText(_filePath, json);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"File I/O error: {e.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"Unexpected error: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadAllFromFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Edit(uint ID, PasswordStore newItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PasswordStore item = _cache.First(item => item.ID.Equals(ID));
|
||||||
|
item.DisplayName = newItem.DisplayName;
|
||||||
|
item.IconName = newItem.IconName;
|
||||||
|
item.Path = newItem.Path;
|
||||||
|
_cacheAhead = true;
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"Edit error: Item with ID {ID} not found.");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"Unexpected error: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Create(PasswordStore item)
|
||||||
|
{
|
||||||
|
item.ID = ++_autoIncrementedId;
|
||||||
|
_cache.Add(item);
|
||||||
|
_cacheAhead = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(PasswordStore item)
|
||||||
|
{
|
||||||
|
Delete(item.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(uint id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var item = _cache.First(item => item.ID.Equals(id));
|
||||||
|
_cache.Remove(item);
|
||||||
|
_cacheAhead = true;
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"Delete error: Item with ID {id} not found.");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
WriteToStdErr($"Unexpected error: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteToStdErr(string message)
|
||||||
|
{
|
||||||
|
using var sw = new StreamWriter(Console.OpenStandardError());
|
||||||
|
sw.WriteLine(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Repository;
|
|
||||||
|
|
||||||
public class Repository : IRepository
|
|
||||||
{
|
|
||||||
private const string _appName = "Keychain";
|
|
||||||
private readonly string _filePath;
|
|
||||||
private List<object>? _cache;
|
|
||||||
private bool _cacheDirty = true;
|
|
||||||
|
|
||||||
public Repository(string fileName)
|
|
||||||
{
|
|
||||||
var xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
|
|
||||||
string dataHome;
|
|
||||||
if (!string.IsNullOrEmpty(xdgDataHome))
|
|
||||||
{
|
|
||||||
dataHome = Path.Combine(xdgDataHome, _appName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dataHome = Path.Combine(
|
|
||||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"),
|
|
||||||
_appName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_filePath = Path.Combine(dataHome, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable ReadAll()
|
|
||||||
{
|
|
||||||
if (!_cacheDirty && _cache != null)
|
|
||||||
return _cache;
|
|
||||||
|
|
||||||
if (!File.Exists(_filePath))
|
|
||||||
{
|
|
||||||
_cache = new List<object>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var json = File.ReadAllText(_filePath);
|
|
||||||
_cache = JsonSerializer.Deserialize<List<object>>(json) ?? new List<object>();
|
|
||||||
}
|
|
||||||
_cacheDirty = false;
|
|
||||||
return _cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Get(uint id)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteAll(IEnumerable items)
|
|
||||||
{
|
|
||||||
var json = JsonSerializer.Serialize(items);
|
|
||||||
var directory = Path.GetDirectoryName(_filePath);
|
|
||||||
if (!Directory.Exists(directory))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directory!);
|
|
||||||
}
|
|
||||||
File.WriteAllText(_filePath, json);
|
|
||||||
|
|
||||||
_cache = (List<object>)items;
|
|
||||||
_cacheDirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,4 +6,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../Models/Models.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user