(feature): allow to save contacts to cache on init, then set and retrieve them later

This commit is contained in:
Maksym Sadovnychyy 2024-06-08 22:56:51 +02:00
parent d046bcecd9
commit 9a11bbca10
20 changed files with 1249 additions and 991 deletions

3
.gitignore vendored
View File

@ -260,3 +260,6 @@ paket-files/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
**/*docker_compose

View File

@ -1,8 +1,10 @@
using DomainResults.Common;
using Microsoft.Extensions.Options;
using DomainResults.Common;
using MaksIT.LetsEncryptServer.Services;
using MaksIT.Models.LetsEncryptServer.Requests;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Models.LetsEncryptServer.CertsFlow.Requests;
namespace MaksIT.LetsEncryptServer.BackgroundServices {
public class AutoRenewal : BackgroundService {

View File

@ -0,0 +1,43 @@

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using DomainResults.Mvc;
using MaksIT.LetsEncryptServer.Services;
using MaksIT.Models.LetsEncryptServer.Cache.Requests;
namespace MaksIT.LetsEncryptServer.Controllers;
[ApiController]
[Route("[controller]")]
public class CacheController {
private readonly Configuration _appSettings;
private readonly ICacheService _cacheService;
public CacheController(
IOptions<Configuration> appSettings,
ICacheService cacheService
) {
_appSettings = appSettings.Value;
_cacheService = cacheService;
}
[HttpGet("[action]/{accountId}")]
public async Task<IActionResult> GetContacts(Guid accountId) {
var result = await _cacheService.GetContactsAsync(accountId);
return result.ToActionResult();
}
[HttpPost("[action]/{accountId}")]
public async Task<IActionResult> SetContacts(Guid accountId, [FromBody] SetContactsRequest requestData) {
var result = await _cacheService.SetContactsAsync(accountId, requestData);
return result.ToActionResult();
}
}

View File

@ -4,7 +4,7 @@ using Microsoft.Extensions.Options;
using DomainResults.Mvc;
using MaksIT.LetsEncryptServer.Services;
using MaksIT.Models.LetsEncryptServer.Requests;
using Models.LetsEncryptServer.CertsFlow.Requests;
namespace MaksIT.LetsEncryptServer.Controllers;

View File

@ -3,6 +3,7 @@
using DomainResults.Common;
using MaksIT.Core.Extensions;
using MaksIT.LetsEncrypt.Entities;
using MaksIT.Models.LetsEncryptServer.Cache.Requests;
namespace MaksIT.LetsEncryptServer.Services;
@ -11,6 +12,8 @@ public interface ICacheService {
Task<IDomainResult> SaveToCacheAsync(Guid accountId, RegistrationCache cache);
Task<IDomainResult> DeleteFromCacheAsync(Guid accountId);
Task<(Guid[]?, IDomainResult)> ListCachedAccountsAsync();
Task<(string[]?, IDomainResult)> GetContactsAsync(Guid accountId);
Task<IDomainResult> SetContactsAsync(Guid accountId, SetContactsRequest requestData);
}
public class CacheService : ICacheService, IDisposable {
@ -141,6 +144,25 @@ public class CacheService : ICacheService, IDisposable {
}
}
public async Task<(string[]?, IDomainResult)> GetContactsAsync(Guid accountId) {
var (cache, loadResult) = await LoadFromCacheAsync(accountId);
if (!loadResult.IsSuccess || cache == null)
return (null, loadResult);
return IDomainResult.Success(cache.Contacts);
}
public async Task<IDomainResult> SetContactsAsync(Guid accountId, SetContactsRequest requestData) {
var (cache, loadResult) = await LoadFromCacheAsync(accountId);
if (!loadResult.IsSuccess || cache == null)
return loadResult;
cache.Contacts = requestData.Contacts;
return await SaveToCacheAsync(accountId, cache);
}
public void Dispose() {
_cacheLock?.Dispose();
}

View File

@ -6,7 +6,7 @@ using DomainResults.Common;
using MaksIT.LetsEncrypt.Entities;
using MaksIT.LetsEncrypt.Services;
using MaksIT.Models.LetsEncryptServer.Requests;
using Models.LetsEncryptServer.CertsFlow.Requests;
namespace MaksIT.LetsEncryptServer.Services;

View File

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
namespace MaksIT.Models.LetsEncryptServer.Cache.Requests {
public class SetContactsRequest : IValidatableObject {
public required string[] Contacts { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
if (Contacts == null || Contacts.Length == 0)
yield return new ValidationResult("Contacts is required", new[] { nameof(Contacts) });
}
}
}

View File

@ -0,0 +1,7 @@
namespace Models.LetsEncryptServer.CertsFlow.Requests
{
public class GetCertificatesRequest
{
public string[] Hostnames { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace Models.LetsEncryptServer.CertsFlow.Requests
{
public class GetOrderRequest
{
public string[] Hostnames { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Models.LetsEncryptServer.CertsFlow.Requests
{
public class InitRequest
{
public string[] Contacts { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Models.LetsEncryptServer.CertsFlow.Requests
{
public class NewOrderRequest
{
public string[] Hostnames { get; set; }
public string ChallengeType { get; set; }
}
}

View File

@ -1,5 +0,0 @@
namespace MaksIT.Models.LetsEncryptServer.Requests {
public class GetCertificatesRequest {
public string[] Hostnames { get; set; }
}
}

View File

@ -1,5 +0,0 @@
namespace MaksIT.Models.LetsEncryptServer.Requests {
public class GetOrderRequest {
public string[] Hostnames { get; set; }
}
}

View File

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MaksIT.Models.LetsEncryptServer.Requests {
public class InitRequest {
public string[] Contacts { get; set; }
}
}

View File

@ -1,7 +0,0 @@
namespace MaksIT.Models.LetsEncryptServer.Requests {
public class NewOrderRequest {
public string[] Hostnames { get; set; }
public string ChallengeType { get; set; }
}
}

View File

@ -8,7 +8,8 @@
<ItemGroup>
<Folder Include="Agent\Responses\" />
<Folder Include="LetsEncryptServer\Responses\" />
<Folder Include="LetsEncryptServer\Cache\Responses\" />
<Folder Include="LetsEncryptServer\CertsFlow\Responses\" />
</ItemGroup>
</Project>

View File

@ -5,6 +5,115 @@
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "33635244"
},
"item": [
{
"name": "Cache",
"item": [
{
"name": "get cache contacts",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"url": {
"raw": "http://localhost:8080/Cache/GetContacts/{{accountId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"Cache",
"GetContacts",
"{{accountId}}"
]
}
},
"response": []
},
{
"name": "set cache contacts",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"contacts\": [\r\n \"maksym.sadovnychyy@gmail.com\"\r\n ]\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/Cache/SetContacts/{{accountId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"Cache",
"SetContacts",
"{{accountId}}"
]
}
},
"response": []
},
{
"name": "host with upcoming ssl expire",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"disabled": true
},
{
"key": "Accept",
"value": "application/json",
"disabled": true
}
],
"url": {
"raw": "http://localhost:8080/CertsFlow/HostsWithUpcomingSslExpiry/{{sessionId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"CertsFlow",
"HostsWithUpcomingSslExpiry",
"{{sessionId}}"
]
}
},
"response": []
}
]
},
{
"name": "Certs Manual Flow",
"item": [
{
"name": "letsencrypt production",
@ -474,38 +583,8 @@
}
},
"response": []
},
{
"name": "host with upcoming ssl expire",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"disabled": true
},
{
"key": "Accept",
"value": "application/json",
"disabled": true
}
],
"url": {
"raw": "http://localhost:8080/CertsFlow/HostsWithUpcomingSslExpiry/{{sessionId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"CertsFlow",
"HostsWithUpcomingSslExpiry",
"{{sessionId}}"
]
}
},
"response": []
}
]
}

View File

@ -5,6 +5,115 @@
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "33635244"
},
"item": [
{
"name": "Cache",
"item": [
{
"name": "get cache contacts",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"url": {
"raw": "http://localhost:8080/Cache/GetContacts/{{accountId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"Cache",
"GetContacts",
"{{accountId}}"
]
}
},
"response": []
},
{
"name": "set cache contacts",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"contacts\": [\r\n \"maksym.sadovnychyy@gmail.com\"\r\n ]\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/Cache/SetContacts/{{accountId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"Cache",
"SetContacts",
"{{accountId}}"
]
}
},
"response": []
},
{
"name": "host with upcoming ssl expire Copy",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"disabled": true
},
{
"key": "Accept",
"value": "application/json",
"disabled": true
}
],
"url": {
"raw": "http://localhost:8080/CertsFlow/HostsWithUpcomingSslExpiry/{{sessionId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"CertsFlow",
"HostsWithUpcomingSslExpiry",
"{{sessionId}}"
]
}
},
"response": []
}
]
},
{
"name": "Certs Manual Flow",
"item": [
{
"name": "letsencrypt staging",
@ -476,38 +585,8 @@
}
},
"response": []
},
{
"name": "host with upcoming ssl expire Copy",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"disabled": true
},
{
"key": "Accept",
"value": "application/json",
"disabled": true
}
],
"url": {
"raw": "http://localhost:8080/CertsFlow/HostsWithUpcomingSslExpiry/{{sessionId}}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"CertsFlow",
"HostsWithUpcomingSslExpiry",
"{{sessionId}}"
]
}
},
"response": []
}
]
}

View File

@ -33,7 +33,10 @@ if [[ "$1" != "$NO_NEW_KEY_FLAG" ]]; then
jq --arg newApiKey "$NEW_API_KEY" '.Configuration.ApiKey = $newApiKey' $APPSETTINGS_FILE > tmp.$$.json && mv tmp.$$.json $APPSETTINGS_FILE
fi
cd
# Build and publish the .NET application
cd "$(dirname "$(realpath "$0")")/Agent"
sudo dotnet build --configuration Release
sudo dotnet publish -c Release -o $INSTALL_DIR

View File

@ -5,5 +5,8 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_HTTP_PORTS=8080
volumes:
- ./docker_compose/LetsEncryptServer/acme:/app/bin/Debug/net8.0/acme
- ./docker_compose/LetsEncryptServer/cache:/app/bin/Debug/net8.0/cache
ports:
- "8080:8080"