diff --git a/src/Core/LockManager.cs b/src/Core/LockManager.cs new file mode 100644 index 0000000..c857ac7 --- /dev/null +++ b/src/Core/LockManager.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +public class LockManager : IDisposable { + private readonly SemaphoreSlim _semaphore; + + public LockManager(int initialCount, int maxCount) { + _semaphore = new SemaphoreSlim(initialCount, maxCount); + } + + public async Task ExecuteWithLockAsync(Func> action) { + await _semaphore.WaitAsync(); + try { + return await action(); + } + finally { + _semaphore.Release(); + } + } + + public async Task ExecuteWithLockAsync(Func action) { + await _semaphore.WaitAsync(); + try { + await action(); + } + finally { + _semaphore.Release(); + } + } + + public async Task ExecuteWithLockAsync(Func action) { + await _semaphore.WaitAsync(); + try { + return await Task.Run(action); + } + finally { + _semaphore.Release(); + } + } + + public async Task ExecuteWithLockAsync(Action action) { + await _semaphore.WaitAsync(); + try { + await Task.Run(action); + } + finally { + _semaphore.Release(); + } + } + + public void Dispose() { + _semaphore?.Dispose(); + } +} diff --git a/src/LetsEncrypt/Entities/LetsEncrypt/RegistrationCache.cs b/src/LetsEncrypt/Entities/LetsEncrypt/RegistrationCache.cs index 25d801d..1784e11 100644 --- a/src/LetsEncrypt/Entities/LetsEncrypt/RegistrationCache.cs +++ b/src/LetsEncrypt/Entities/LetsEncrypt/RegistrationCache.cs @@ -11,6 +11,18 @@ public class CertificateCache { public byte[]? Private { get; set; } } +public class CachedHostname { + public string Hostname { get; set; } + public DateTime Expires { get; set; } + public bool IsUpcomingExpire { get; set; } + + public CachedHostname(string hostname, DateTime expires, bool isUpcomingExpire) { + Hostname = hostname; + Expires = expires; + IsUpcomingExpire = isUpcomingExpire; + } +} + public class RegistrationCache { #region Custom Properties @@ -53,6 +65,26 @@ public class RegistrationCache { return hostsWithUpcomingSslExpiry.ToArray(); } + + public CachedHostname[] GetHosts() { + if (CachedCerts == null) + return Array.Empty(); + + var hosts = new List(); + + foreach (var result in CachedCerts) { + var (subject, cachedChert) = result; + + if (cachedChert.Cert != null) { + var cert = new X509Certificate2(Encoding.ASCII.GetBytes(cachedChert.Cert)); + + hosts.Add(new CachedHostname(subject, cert.NotAfter, (cert.NotAfter - DateTime.UtcNow).TotalDays < 30)); + } + } + + return hosts.ToArray(); + } + /// /// Returns cached certificate. Certs older than 30 days are not returned /// diff --git a/src/LetsEncryptServer/Controllers/CacheController.cs b/src/LetsEncryptServer/Controllers/CacheController.cs index fe9d764..aa9756e 100644 --- a/src/LetsEncryptServer/Controllers/CacheController.cs +++ b/src/LetsEncryptServer/Controllers/CacheController.cs @@ -9,7 +9,7 @@ using MaksIT.Models.LetsEncryptServer.Cache.Requests; namespace MaksIT.LetsEncryptServer.Controllers; [ApiController] -[Route("api/[controller]")] +[Route("api/cache")] public class CacheController : ControllerBase { private readonly Configuration _appSettings; private readonly ICacheRestService _cacheService; @@ -28,27 +28,39 @@ public class CacheController : ControllerBase { return result.ToActionResult(); } + [HttpPut("account/{accountId:guid}")] + public async Task PutAccount(Guid accountId, [FromBody] PutAccountRequest requestData) { + var result = await _cacheService.PutAccountAsync(accountId, requestData); + return result.ToActionResult(); + } + + [HttpPatch("account/{accountId:guid}")] + public async Task PatchAccount(Guid accountId, [FromBody] PatchAccountRequest requestData) { + var result = await _cacheService.PatchAccountAsync(accountId, requestData); + return result.ToActionResult(); + } + #region Contacts - [HttpGet("{accountId}/contacts")] + [HttpGet("account/{accountId:guid}/contacts")] public async Task GetContacts(Guid accountId) { var result = await _cacheService.GetContactsAsync(accountId); return result.ToActionResult(); } - [HttpPut("{accountId}/contacts")] + [HttpPut("account/{accountId:guid}/contacts")] public async Task PutContacts(Guid accountId, [FromBody] PutContactsRequest requestData) { var result = await _cacheService.PutContactsAsync(accountId, requestData); return result.ToActionResult(); } - [HttpPatch("{accountId}/contacts")] - public async Task PatchContacts(Guid accountId, [FromBody] PatchContactRequest requestData) { + [HttpPatch("account/{accountId:guid}/contacts")] + public async Task PatchContacts(Guid accountId, [FromBody] PatchContactsRequest requestData) { var result = await _cacheService.PatchContactsAsync(accountId, requestData); return result.ToActionResult(); } - [HttpDelete("{accountId}/contacts/{index}")] + [HttpDelete("account/{accountId:guid}/contacts/{index:int}")] public async Task DeleteContact(Guid accountId, int index) { var result = await _cacheService.DeleteContactAsync(accountId, index); return result.ToActionResult(); @@ -57,7 +69,7 @@ public class CacheController : ControllerBase { #region Hostnames - [HttpGet("{accountId}/hostnames")] + [HttpGet("account/{accountId:guid}/hostnames")] public async Task GetHostnames(Guid accountId) { var result = await _cacheService.GetHostnames(accountId); return result.ToActionResult(); diff --git a/src/LetsEncryptServer/Services/CacheService.cs b/src/LetsEncryptServer/Services/CacheService.cs index e48bc13..1a5cb98 100644 --- a/src/LetsEncryptServer/Services/CacheService.cs +++ b/src/LetsEncryptServer/Services/CacheService.cs @@ -1,13 +1,11 @@ -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Text.Json; +using System.Text.Json; using DomainResults.Common; + using MaksIT.Core.Extensions; using MaksIT.LetsEncrypt.Entities; using MaksIT.Models; using MaksIT.Models.LetsEncryptServer.Cache.Requests; -using MaksIT.Models.LetsEncryptServer.Cache.Responses; using Models.LetsEncryptServer.Cache.Responses; namespace MaksIT.LetsEncryptServer.Services; @@ -19,289 +17,247 @@ public interface ICacheService { } public interface ICacheRestService { - Task<(GetAccountsResponse?, IDomainResult)> GetAccountsAsync(); + Task<(GetAccountResponse[]?, IDomainResult)> GetAccountsAsync(); Task<(GetAccountResponse?, IDomainResult)> GetAccountAsync(Guid accountId); - - #region Contacts + Task<(GetAccountResponse?, IDomainResult)> PutAccountAsync(Guid accountId, PutAccountRequest requestData); + Task<(GetAccountResponse?, IDomainResult)> PatchAccountAsync(Guid accountId, PatchAccountRequest requestData); Task<(GetContactsResponse?, IDomainResult)> GetContactsAsync(Guid accountId); Task<(GetAccountResponse?, IDomainResult)> PutContactsAsync(Guid accountId, PutContactsRequest requestData); - Task<(GetAccountResponse?, IDomainResult)> PatchContactsAsync(Guid accountId, PatchContactRequest requestData); + Task<(GetAccountResponse?, IDomainResult)> PatchContactsAsync(Guid accountId, PatchContactsRequest requestData); Task DeleteContactAsync(Guid accountId, int index); - #endregion - - #region Hostnames Task<(GetHostnamesResponse?, IDomainResult)> GetHostnames(Guid accountId); - #endregion } public class CacheService : ICacheService, ICacheRestService, IDisposable { - private readonly ILogger _logger; private readonly string _cacheDirectory; - private readonly SemaphoreSlim _cacheLock = new SemaphoreSlim(1, 1); + private readonly LockManager _lockManager; - public CacheService( - ILogger logger - ) { + public CacheService(ILogger logger) { _logger = logger; _cacheDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache"); + _lockManager = new LockManager(1, 1); if (!Directory.Exists(_cacheDirectory)) { Directory.CreateDirectory(_cacheDirectory); } } + /// + /// Generates the cache file path for the given account ID. + /// private string GetCacheFilePath(Guid accountId) { return Path.Combine(_cacheDirectory, $"{accountId}.json"); } - public async Task<(RegistrationCache?, IDomainResult)> LoadFromCacheAsync(Guid accountId) { - var cacheFilePath = GetCacheFilePath(accountId); + #region Cache Operations - await _cacheLock.WaitAsync(); - - try { - if (!File.Exists(cacheFilePath)) { - var message = $"Cache file not found for account {accountId}"; - _logger.LogWarning(message); - - return IDomainResult.Failed(message); - } - - var json = await File.ReadAllTextAsync(cacheFilePath); - if (string.IsNullOrEmpty(json)) { - var message = $"Cache file is empty for account {accountId}"; - _logger.LogWarning(message); - - return IDomainResult.Failed(message); - } - - var cache = JsonSerializer.Deserialize(json); - return IDomainResult.Success(cache); - } - catch (Exception ex) { - var message = "Error reading cache file for account {accountId}"; - _logger.LogError(ex, message); - - return IDomainResult.Failed(message); - } - finally { - _cacheLock.Release(); - } + public Task<(RegistrationCache?, IDomainResult)> LoadFromCacheAsync(Guid accountId) { + return _lockManager.ExecuteWithLockAsync(() => LoadFromCacheInternalAsync(accountId)); } - public async Task SaveToCacheAsync(Guid accountId, RegistrationCache cache) { + private async Task<(RegistrationCache?, IDomainResult)> LoadFromCacheInternalAsync(Guid accountId) { var cacheFilePath = GetCacheFilePath(accountId); - await _cacheLock.WaitAsync(); - try { - var json = JsonSerializer.Serialize(cache); - await File.WriteAllTextAsync(cacheFilePath, json); + if (!File.Exists(cacheFilePath)) { + var message = $"Cache file not found for account {accountId}"; + _logger.LogWarning(message); + return IDomainResult.Failed(message); + } - _logger.LogInformation($"Cache file saved for account {accountId}"); + var json = await File.ReadAllTextAsync(cacheFilePath); + if (string.IsNullOrEmpty(json)) { + var message = $"Cache file is empty for account {accountId}"; + _logger.LogWarning(message); + return IDomainResult.Failed(message); + } - return DomainResult.Success(); - } - catch (Exception ex) { - var message = "Error writing cache file for account {accountId}"; - _logger.LogError(ex, message); - - return IDomainResult.Failed(message); - } - finally { - _cacheLock.Release(); - } + var cache = JsonSerializer.Deserialize(json); + return IDomainResult.Success(cache); } - public async Task DeleteFromCacheAsync(Guid accountId) { - var cacheFilePath = GetCacheFilePath(accountId); - await _cacheLock.WaitAsync(); - - try { - if (File.Exists(cacheFilePath)) { - File.Delete(cacheFilePath); - _logger.LogInformation($"Cache file deleted for account {accountId}"); - } - else { - _logger.LogWarning($"Cache file not found for account {accountId}"); - } - - return IDomainResult.Success(); - } - catch (Exception ex) { - var message = $"Error deleting cache file for account {accountId}"; - _logger.LogError(ex, message); - - return IDomainResult.Failed(message); - } - finally { - _cacheLock.Release(); - } + public Task SaveToCacheAsync(Guid accountId, RegistrationCache cache) { + return _lockManager.ExecuteWithLockAsync(() => SaveToCacheInternalAsync(accountId, cache)); } + private async Task SaveToCacheInternalAsync(Guid accountId, RegistrationCache cache) { + var cacheFilePath = GetCacheFilePath(accountId); + var json = JsonSerializer.Serialize(cache); + await File.WriteAllTextAsync(cacheFilePath, json); + _logger.LogInformation($"Cache file saved for account {accountId}"); + return DomainResult.Success(); + } + public Task DeleteFromCacheAsync(Guid accountId) { + return _lockManager.ExecuteWithLockAsync(() => DeleteFromCacheInternal(accountId)); + } - #region RestService - public async Task<(GetAccountsResponse?, IDomainResult)> GetAccountsAsync() { - await _cacheLock.WaitAsync(); + private IDomainResult DeleteFromCacheInternal(Guid accountId) { + var cacheFilePath = GetCacheFilePath(accountId); + if (File.Exists(cacheFilePath)) { + File.Delete(cacheFilePath); + _logger.LogInformation($"Cache file deleted for account {accountId}"); + } + else { + _logger.LogWarning($"Cache file not found for account {accountId}"); + } + return DomainResult.Success(); + } + + #endregion - try { + #region Account Operations + + public async Task<(GetAccountResponse[]?, IDomainResult)> GetAccountsAsync() { + return await _lockManager.ExecuteWithLockAsync(async () => { var cacheFiles = Directory.GetFiles(_cacheDirectory); - if (cacheFiles == null) - return IDomainResult.Success(new GetAccountsResponse { - Accounts = Array.Empty() - }); - - var accountIds = cacheFiles.Select(x => Path.GetFileNameWithoutExtension(x).ToGuid()); - + var accountIds = cacheFiles.Select(x => Path.GetFileNameWithoutExtension(x).ToGuid()).ToArray(); var accounts = new List(); - foreach (var accountId in accountIds) { - var (account, getAccountResult) = await GetAccountAsync(accountId); - if(!getAccountResult.IsSuccess || account == null) - return (null, getAccountResult); + foreach (var accountId in accountIds) { + var (account, result) = await GetAccountAsync(accountId); + if (!result.IsSuccess || account == null) { + return (null, result); + } accounts.Add(account); } - - return IDomainResult.Success(new GetAccountsResponse { - Accounts = accounts.ToArray() - }); - } - catch (Exception ex) { - var message = "Error listing cache files"; - _logger.LogError(ex, message); - return IDomainResult.Failed (message); - } - finally { - _cacheLock.Release(); - } + return IDomainResult.Success(accounts.ToArray()); + }); } public async Task<(GetAccountResponse?, IDomainResult)> GetAccountAsync(Guid accountId) { + return await _lockManager.ExecuteWithLockAsync(async () => { + var (cache, result) = await LoadFromCacheAsync(accountId); + if (!result.IsSuccess || cache == null) { + return (null, result); + } - await _cacheLock.WaitAsync(); - - try { - var (registrationCache, gerRegistrationCacheResult) = await LoadFromCacheAsync(accountId); - if (!gerRegistrationCacheResult.IsSuccess || registrationCache == null) - return (null, gerRegistrationCacheResult); - - return IDomainResult.Success(new GetAccountResponse { + var response = new GetAccountResponse { AccountId = accountId, - Description = registrationCache.Description, - Contacts = registrationCache.Contacts, - Hostnames = registrationCache.GetHostsWithUpcomingSslExpiry() - }); - } - catch (Exception ex) { - var message = "Error listing cache files"; - _logger.LogError(ex, message); + Description = cache.Description, + Contacts = cache.Contacts, + Hostnames = GetHostnamesFromCache(cache).ToArray() + }; - return IDomainResult.Failed(message); - } - finally { - _cacheLock.Release(); - } + return IDomainResult.Success(response); + }); } + public async Task<(GetAccountResponse?, IDomainResult)> PutAccountAsync(Guid accountId, PutAccountRequest requestData) { + var (cache, loadResult) = await LoadFromCacheAsync(accountId); + if (!loadResult.IsSuccess || cache == null) { + return (null, loadResult); + } + + cache.Description = requestData.Description; + cache.Contacts = requestData.Contacts; + + var saveResult = await SaveToCacheAsync(accountId, cache); + if (!saveResult.IsSuccess) { + return (null, saveResult); + } + + return CreateGetAccountResponse(accountId, cache); + } + + public async Task<(GetAccountResponse?, IDomainResult)> PatchAccountAsync(Guid accountId, PatchAccountRequest requestData) { + var (cache, loadResult) = await LoadFromCacheAsync(accountId); + if (!loadResult.IsSuccess || cache == null) { + return (null, loadResult); + } + + if (requestData.Description != null) { + switch (requestData.Description.Op) { + case PatchOperation.Replace: + cache.Description = requestData.Description.Value; + break; + } + } + + if (requestData.Contacts != null && requestData.Contacts.Any()) { + var contacts = cache.Contacts?.ToList() ?? new List(); + foreach (var action in requestData.Contacts) { + switch (action.Op) + { + case PatchOperation.Add: + if (action.Value != null) contacts.Add(action.Value); + break; + case PatchOperation.Replace: + if (action.Index != null && action.Index >= 0 && action.Index < contacts.Count) + contacts[action.Index.Value] = action.Value; + break; + case PatchOperation.Remove: + if (action.Index != null && action.Index >= 0 && action.Index < contacts.Count) + contacts.RemoveAt(action.Index.Value); + break; + } + } + cache.Contacts = contacts.ToArray(); + } + + var saveResult = await SaveToCacheAsync(accountId, cache); + if (!saveResult.IsSuccess) { + return (null, saveResult); + } + + return CreateGetAccountResponse(accountId, cache); + } + + #endregion + + #region Contacts Operations - #region Contacts - /// - /// Retrieves the contacts list for the account. - /// - /// The ID of the account. - /// The contacts list and domain result. public async Task<(GetContactsResponse?, IDomainResult)> GetContactsAsync(Guid accountId) { var (cache, loadResult) = await LoadFromCacheAsync(accountId); - if (!loadResult.IsSuccess || cache == null) + if (!loadResult.IsSuccess || cache == null) { return (null, loadResult); + } return IDomainResult.Success(new GetContactsResponse { Contacts = cache.Contacts ?? Array.Empty() }); } - /// - /// Adds new contacts to the account. This method initializes the contacts list if it is null. - /// - /// The ID of the account. - /// The request containing the contacts to add. - /// The updated account response and domain result. - public async Task<(GetAccountResponse?, IDomainResult)> PostContactAsync(Guid accountId, PostContactsRequest requestData) { - var (cache, loadResult) = await LoadFromCacheAsync(accountId); - if (!loadResult.IsSuccess || cache == null) - return (null, loadResult); - - var contacts = cache.Contacts?.ToList() ?? new List(); - - if (requestData.Contacts != null) { - contacts.AddRange(requestData.Contacts); - } - - cache.Contacts = contacts.ToArray(); - var saveResult = await SaveToCacheAsync(accountId, cache); - if (!saveResult.IsSuccess) - return (null, saveResult); - - return (new GetAccountResponse { - AccountId = accountId, - Description = cache.Description, - Contacts = cache.Contacts, - Hostnames = cache.GetHostsWithUpcomingSslExpiry() - }, IDomainResult.Success()); - } - - /// - /// Replaces the entire contacts list for the account. - /// - /// The ID of the account. - /// The request containing the new contacts list. - /// The updated account response and domain result. public async Task<(GetAccountResponse?, IDomainResult)> PutContactsAsync(Guid accountId, PutContactsRequest requestData) { var (cache, loadResult) = await LoadFromCacheAsync(accountId); - if (!loadResult.IsSuccess || cache == null) + if (!loadResult.IsSuccess || cache == null) { return (null, loadResult); + } cache.Contacts = requestData.Contacts; var saveResult = await SaveToCacheAsync(accountId, cache); - if (!saveResult.IsSuccess) + if (!saveResult.IsSuccess) { return (null, saveResult); + } - return (new GetAccountResponse { - AccountId = accountId, - Description = cache.Description, - Contacts = cache.Contacts, - Hostnames = cache.GetHostsWithUpcomingSslExpiry() - }, IDomainResult.Success()); + return CreateGetAccountResponse(accountId, cache); } - /// - /// Partially updates the contacts list for the account. Supports add, replace, and remove operations. - /// - /// The ID of the account. - /// The request containing the patch operations for contacts. - /// The updated account response and domain result. - public async Task<(GetAccountResponse?, IDomainResult)> PatchContactsAsync(Guid accountId, PatchContactRequest requestData) { + public async Task<(GetAccountResponse?, IDomainResult)> PatchContactsAsync(Guid accountId, PatchContactsRequest requestData) { var (cache, loadResult) = await LoadFromCacheAsync(accountId); - if (!loadResult.IsSuccess || cache == null) + if (!loadResult.IsSuccess || cache == null) { return (null, loadResult); + } var contacts = cache.Contacts?.ToList() ?? new List(); foreach (var contact in requestData.Contacts) { switch (contact.Op) { case PatchOperation.Add: - if (contact.Value != null) + if (contact.Value != null) { contacts.Add(contact.Value); + } break; case PatchOperation.Replace: - if (contact.Index.HasValue && contact.Index.Value >= 0 && contact.Index.Value < contacts.Count && contact.Value != null) + if (contact.Index.HasValue && contact.Index.Value >= 0 && contact.Index.Value < contacts.Count && contact.Value != null) { contacts[contact.Index.Value] = contact.Value; + } break; case PatchOperation.Remove: - if (contact.Index.HasValue && contact.Index.Value >= 0 && contact.Index.Value < contacts.Count) + if (contact.Index.HasValue && contact.Index.Value >= 0 && contact.Index.Value < contacts.Count) { contacts.RemoveAt(contact.Index.Value); + } break; default: return (null, IDomainResult.Failed("Invalid patch operation.")); @@ -310,76 +266,79 @@ public class CacheService : ICacheService, ICacheRestService, IDisposable { cache.Contacts = contacts.ToArray(); var saveResult = await SaveToCacheAsync(accountId, cache); - if (!saveResult.IsSuccess) + if (!saveResult.IsSuccess) { return (null, saveResult); + } - return (new GetAccountResponse { - AccountId = accountId, - Description = cache.Description, - Contacts = cache.Contacts, - Hostnames = cache.GetHostsWithUpcomingSslExpiry() - }, IDomainResult.Success()); + return CreateGetAccountResponse(accountId, cache); } - /// - /// Deletes a contact from the account by index. - /// - /// The ID of the account. - /// The index of the contact to remove. - /// The domain result indicating success or failure. public async Task DeleteContactAsync(Guid accountId, int index) { var (cache, loadResult) = await LoadFromCacheAsync(accountId); - if (!loadResult.IsSuccess || cache == null) + if (!loadResult.IsSuccess || cache == null) { return loadResult; + } var contacts = cache.Contacts?.ToList() ?? new List(); - if (index >= 0 && index < contacts.Count) + if (index >= 0 && index < contacts.Count) { contacts.RemoveAt(index); + } cache.Contacts = contacts.ToArray(); var saveResult = await SaveToCacheAsync(accountId, cache); - if (!saveResult.IsSuccess) + if (!saveResult.IsSuccess) { return saveResult; + } return IDomainResult.Success(); } #endregion - #region Hostnames + #region Hostnames Operations + public async Task<(GetHostnamesResponse?, IDomainResult)> GetHostnames(Guid accountId) { var (cache, loadResult) = await LoadFromCacheAsync(accountId); - if (!loadResult.IsSuccess || cache?.CachedCerts == null) + if (!loadResult.IsSuccess || cache?.CachedCerts == null) { return (null, loadResult); - - var hoststWithUpcomingSslExpire = cache.GetHostsWithUpcomingSslExpiry(); - - - var response = new GetHostnamesResponse { - Hostnames = new List() - }; - - foreach (var result in cache.CachedCerts) { - var (subject, cachedChert) = result; - - var cert = new X509Certificate2(Encoding.ASCII.GetBytes(cachedChert.Cert)); - - response.Hostnames.Add(new HostnameResponse { - Hostname = subject, - Expires = cert.NotBefore, - IsUpcomingExpire = hoststWithUpcomingSslExpire.Contains(subject) - }); } - return IDomainResult.Success(response); + var hostnames = GetHostnamesFromCache(cache); + + return IDomainResult.Success(new GetHostnamesResponse { + Hostnames = hostnames + }); + } + + private List GetHostnamesFromCache(RegistrationCache cache) { + var hosts = cache.GetHosts().Select(x => new HostnameResponse { + Hostname = x.Hostname, + Expires = x.Expires, + IsUpcomingExpire = x.IsUpcomingExpire + }).ToList(); + + return hosts; } - #endregion #endregion + #region Helper Methods + + private (GetAccountResponse?, IDomainResult) CreateGetAccountResponse(Guid accountId, RegistrationCache cache) { + var hostnames = GetHostnamesFromCache(cache) ?? new List(); + + return (new GetAccountResponse { + AccountId = accountId, + Description = cache.Description, + Contacts = cache.Contacts, + Hostnames = hostnames.ToArray() + }, IDomainResult.Success()); + } public void Dispose() { - _cacheLock?.Dispose(); + _lockManager?.Dispose(); } + + #endregion } diff --git a/src/Models/LetsEncryptServer/Cache/Requests/PatchAccountRequest.cs b/src/Models/LetsEncryptServer/Cache/Requests/PatchAccountRequest.cs new file mode 100644 index 0000000..22d2bc7 --- /dev/null +++ b/src/Models/LetsEncryptServer/Cache/Requests/PatchAccountRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MaksIT.Models.LetsEncryptServer.Cache.Requests { + public class PatchAccountRequest { + + public PatchAction? Description { get; set; } + + public List>? Contacts { get; set; } + } +} diff --git a/src/Models/LetsEncryptServer/Cache/Requests/PatchContactRequest.cs b/src/Models/LetsEncryptServer/Cache/Requests/PatchContactsRequest.cs similarity index 86% rename from src/Models/LetsEncryptServer/Cache/Requests/PatchContactRequest.cs rename to src/Models/LetsEncryptServer/Cache/Requests/PatchContactsRequest.cs index c1bd057..92f2883 100644 --- a/src/Models/LetsEncryptServer/Cache/Requests/PatchContactRequest.cs +++ b/src/Models/LetsEncryptServer/Cache/Requests/PatchContactsRequest.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace MaksIT.Models.LetsEncryptServer.Cache.Requests { - public class PatchContactRequest { + public class PatchContactsRequest { public List> Contacts { get; set; } } } diff --git a/src/Models/LetsEncryptServer/Cache/Requests/PutAccountRequest.cs b/src/Models/LetsEncryptServer/Cache/Requests/PutAccountRequest.cs new file mode 100644 index 0000000..0e21aad --- /dev/null +++ b/src/Models/LetsEncryptServer/Cache/Requests/PutAccountRequest.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MaksIT.Models.LetsEncryptServer.Cache.Requests { + public class PutAccountRequest { + public string Description { get; set; } + public string[] Contacts { get; set; } + } +} diff --git a/src/Models/LetsEncryptServer/Cache/Responses/GetAccountResponse.cs b/src/Models/LetsEncryptServer/Cache/Responses/GetAccountResponse.cs index 6e75356..1c868d4 100644 --- a/src/Models/LetsEncryptServer/Cache/Responses/GetAccountResponse.cs +++ b/src/Models/LetsEncryptServer/Cache/Responses/GetAccountResponse.cs @@ -12,6 +12,6 @@ namespace Models.LetsEncryptServer.Cache.Responses { public string []? Contacts { get; set; } - public string[]? Hostnames { get; set; } + public HostnameResponse[]? Hostnames { get; set; } } } diff --git a/src/Models/LetsEncryptServer/Cache/Responses/GetAccountsResponse.cs b/src/Models/LetsEncryptServer/Cache/Responses/GetAccountsResponse.cs deleted file mode 100644 index a56ba94..0000000 --- a/src/Models/LetsEncryptServer/Cache/Responses/GetAccountsResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Models.LetsEncryptServer.Cache.Responses; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MaksIT.Models.LetsEncryptServer.Cache.Responses { - public class GetAccountsResponse { - public GetAccountResponse[] Accounts { get; set; } - } -} diff --git a/src/Models/LetsEncryptServer/Cache/Responses/GetHostnamesResponse.cs b/src/Models/LetsEncryptServer/Cache/Responses/GetHostnamesResponse.cs index 787e2c1..09d4f4d 100644 --- a/src/Models/LetsEncryptServer/Cache/Responses/GetHostnamesResponse.cs +++ b/src/Models/LetsEncryptServer/Cache/Responses/GetHostnamesResponse.cs @@ -6,13 +6,6 @@ using System.Threading.Tasks; namespace Models.LetsEncryptServer.Cache.Responses { - public class HostnameResponse { - public string Hostname { get; set; } - public DateTime Expires { get; set; } - public bool IsUpcomingExpire { get; set; } - } - - public class GetHostnamesResponse { public List Hostnames { get; set; } } diff --git a/src/Models/LetsEncryptServer/Cache/Responses/HostnameResponse.cs b/src/Models/LetsEncryptServer/Cache/Responses/HostnameResponse.cs new file mode 100644 index 0000000..69576a7 --- /dev/null +++ b/src/Models/LetsEncryptServer/Cache/Responses/HostnameResponse.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Models.LetsEncryptServer.Cache.Responses { + public class HostnameResponse { + public string Hostname { get; set; } + public DateTime Expires { get; set; } + public bool IsUpcomingExpire { get; set; } + } + +}