From cac3d2502e30425fbb2e547acf6d18c116d8e5a8 Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Sun, 22 Jan 2023 22:45:17 +0100 Subject: [PATCH] (feature): dkim cert upload and managing through gridfs data provider --- db/DML/sites.json | 47 ++++++ .../dkims/{maks-it.com => maks-it.com.key} | 0 postman/templates/email-inlined.html | 152 ------------------ .../templates/password-recovery-template.html | 145 +++++++++++++++++ .../Buckets/DkimBucketDataProvider.cs | 25 ++- .../Buckets/TemplateBucketDataProvider.cs | 48 +++++- .../Collections/BlogCatalogDataProvider.cs | 3 +- .../Collections/CategoryDataProvider.cs | 36 ++--- .../Collections/ShopCatalogDataProvider.cs | 2 +- src/DataProviders/Mappings.cs | 7 +- src/DomainObjects/Contact.cs | 5 + src/DomainObjects/Documents/Address.cs | 1 + src/DomainObjects/Documents/BlogDocument.cs | 34 ---- .../Documents/Categories/Category.cs | 22 +++ .../Documents/Categories/L10n/CategoryL10n.cs | 24 +++ .../Documents/CategoryDocument.cs | 18 --- .../Documents/MailboxConnectionSettings.cs | 12 +- .../Documents/Posts/BlogDocument.cs | 37 +++++ .../Documents/Posts/ShopDocument.cs | 39 +++++ src/DomainObjects/Documents/ShopDocument.cs | 36 ----- .../Sites/PassworRecoverySettings.cs | 8 +- src/DomainObjects/Documents/Sites/Site.cs | 9 +- src/DomainObjects/L10n/CategoryL10n.cs | 21 --- src/WeatherForecast/Configuration.cs | 5 + .../Controllers/BlogItemController.cs | 2 +- .../Controllers/DkimController.cs | 15 +- .../Controllers/ShopItemController.cs | 2 +- .../Controllers/TemplateController.cs | 14 ++ .../Controllers/UtilsController.cs | 15 ++ .../Responsens/PostItemResponseModelBase.cs | 20 +-- .../BlogItem/Requests/BlogItemRequestModel.cs | 15 +- .../Responses/BlogItemResponseModel.cs | 21 +-- .../Requests/CategoryItemRequestModel.cs | 15 +- .../Requests/CategoryL10nModel.cs | 13 +- .../Responses/CategoryItemResponseModel.cs | 20 +-- .../Responses/CategoryL10nModel.cs | 13 +- .../Requests/InitializeSystemRequestModel.cs | 17 +- .../Shop/Requests/ShopItemRequestModel.cs | 14 +- .../Responses/ShopCartItemResponseModel.cs | 12 +- .../Shop/Responses/ShopItemResponseModel.cs | 18 ++- .../Requests/EncryptStringAesRequestModel.cs | 45 ++++++ .../Policies/BlogAuthorizationHandler.cs | 3 +- .../Policies/ShopAuthorizationHandler.cs | 5 +- .../Services/AccountService.cs | 31 ++-- .../Services/BlogItemService.cs | 14 +- src/WeatherForecast/Services/DkimService.cs | 25 ++- src/WeatherForecast/Services/ImageService.cs | 1 + .../Services/InitializationService.cs | 74 +++++---- .../Services/ShopItemService.cs | 13 +- .../Services/TemplateService.cs | 89 ++++++---- src/WeatherForecast/appsettings.json | 5 + src/docker-compose.dcproj | 2 +- .../mongo/data/db/WiredTiger.turtle | 2 +- .../mongo/data/db/WiredTiger.wt | Bin 122880 -> 139264 bytes .../mongo/data/db/_mdb_catalog.wt | Bin 36864 -> 36864 bytes .../db/collection-0--6471522924120802764.wt | Bin 0 -> 36864 bytes .../db/collection-3--6471522924120802764.wt | Bin 0 -> 20480 bytes .../db/collection-4--4715807334585891142.wt | Bin 24576 -> 36864 bytes .../db/collection-4-597769541568262742.wt | Bin 20480 -> 36864 bytes .../data/db/diagnostic.data/metrics.interim | Bin 18845 -> 20841 bytes .../data/db/index-1--6471522924120802764.wt | Bin 0 -> 20480 bytes .../data/db/index-2--6471522924120802764.wt | Bin 0 -> 20480 bytes .../data/db/index-4--6471522924120802764.wt | Bin 0 -> 20480 bytes .../data/db/index-5--4715807334585891142.wt | Bin 24576 -> 36864 bytes .../data/db/index-5--6471522924120802764.wt | Bin 0 -> 20480 bytes .../data/db/index-6--4715807334585891142.wt | Bin 12288 -> 36864 bytes .../data/db/journal/WiredTigerLog.0000000038 | Bin 104857600 -> 104857600 bytes .../mongo/data/db/sizeStorer.wt | Bin 36864 -> 36864 bytes 68 files changed, 774 insertions(+), 497 deletions(-) create mode 100644 db/DML/sites.json rename postman/dkims/{maks-it.com => maks-it.com.key} (100%) delete mode 100644 postman/templates/email-inlined.html create mode 100644 postman/templates/password-recovery-template.html delete mode 100644 src/DomainObjects/Documents/BlogDocument.cs create mode 100644 src/DomainObjects/Documents/Categories/Category.cs create mode 100644 src/DomainObjects/Documents/Categories/L10n/CategoryL10n.cs delete mode 100644 src/DomainObjects/Documents/CategoryDocument.cs create mode 100644 src/DomainObjects/Documents/Posts/BlogDocument.cs create mode 100644 src/DomainObjects/Documents/Posts/ShopDocument.cs delete mode 100644 src/DomainObjects/Documents/ShopDocument.cs delete mode 100644 src/DomainObjects/L10n/CategoryL10n.cs create mode 100644 src/WeatherForecast/Models/Utils/Requests/EncryptStringAesRequestModel.cs create mode 100644 src/docker-compose/mongo/data/db/collection-0--6471522924120802764.wt create mode 100644 src/docker-compose/mongo/data/db/collection-3--6471522924120802764.wt create mode 100644 src/docker-compose/mongo/data/db/index-1--6471522924120802764.wt create mode 100644 src/docker-compose/mongo/data/db/index-2--6471522924120802764.wt create mode 100644 src/docker-compose/mongo/data/db/index-4--6471522924120802764.wt create mode 100644 src/docker-compose/mongo/data/db/index-5--6471522924120802764.wt diff --git a/db/DML/sites.json b/db/DML/sites.json new file mode 100644 index 0000000..8dad6f5 --- /dev/null +++ b/db/DML/sites.json @@ -0,0 +1,47 @@ +[ + { + "_id": "404c8232-9048-4519-bfba-6e78dc7005ca", + "locale": 0, + "name": "Contoso", + "hosts": [ + "localhost:7174", + "maks-it.com" + ], + "address": { + "street": "123 456th St", + "city": "New York", + "region": "NY" + "postCode": "10001", + "country": "US" + }, + "poweredBy": { + "target": "https://maks-it.com", + "anchorText": "MAKS-IT.com" + }, + "passwordRecoverySettings": { + "smtpSettings": { + "server": "smtp.ionos.it", + "port": 587 + "useSsl": true, + "userName": "commercial@maks-it.com", + "password": "nECbzrWqwzM5Lv4zCxV91g==" + }, + "dkimSettings": { + "hostName": "maks-it.com", + "selector": "default", + "dkimId": "0c5527ca-c10f-45a1-b1b2-4b153e89e209" + }, + "templateId": "", + "email": { + "type": 0, + "name": "support", + "value": "support@maks-it.com" + }, + "subject": "Password recovery service", + "paragraphs": [ + "Your recovery link: {{recoveryLink}}" + ] + + } + } +] diff --git a/postman/dkims/maks-it.com b/postman/dkims/maks-it.com.key similarity index 100% rename from postman/dkims/maks-it.com rename to postman/dkims/maks-it.com.key diff --git a/postman/templates/email-inlined.html b/postman/templates/email-inlined.html deleted file mode 100644 index dc611bb..0000000 --- a/postman/templates/email-inlined.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - Simple Transactional Email - - - - - - - - - - - - - diff --git a/postman/templates/password-recovery-template.html b/postman/templates/password-recovery-template.html new file mode 100644 index 0000000..8dbd6eb --- /dev/null +++ b/postman/templates/password-recovery-template.html @@ -0,0 +1,145 @@ + + + + + + {{subject}} + + + + + + + + + + + + + diff --git a/src/DataProviders/Buckets/DkimBucketDataProvider.cs b/src/DataProviders/Buckets/DkimBucketDataProvider.cs index f677cc2..47a8015 100644 --- a/src/DataProviders/Buckets/DkimBucketDataProvider.cs +++ b/src/DataProviders/Buckets/DkimBucketDataProvider.cs @@ -1,5 +1,4 @@ - -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using DomainResults.Common; @@ -27,8 +26,20 @@ namespace DataProviders.Buckets { /// /// (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId); + + /// + /// + /// + /// + /// + /// + /// + IDomainResult DeleteOne(Guid siteId, Guid fileId); } + /// + /// + /// public class DkimBucketDataProvider : BucketDataProviderBase, IDkimBucketDataProvider { /// @@ -60,5 +71,15 @@ namespace DataProviders.Buckets { return (list.First(), result); } + + + public IDomainResult DeleteOne(Guid siteId, Guid fileId) => + DeleteOneAsync(siteId, fileId).Result; + + public Task DeleteOneAsync(Guid siteId, Guid fileId) => + DeleteAsync(Builders.Filter.And( + Builders.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"), + Builders.Filter.Eq(x => x.Filename, $"{fileId}") + )); } } diff --git a/src/DataProviders/Buckets/TemplateBucketDataProvider.cs b/src/DataProviders/Buckets/TemplateBucketDataProvider.cs index 372ae13..a5096d9 100644 --- a/src/DataProviders/Buckets/TemplateBucketDataProvider.cs +++ b/src/DataProviders/Buckets/TemplateBucketDataProvider.cs @@ -1,13 +1,11 @@ -using DataProviders.Buckets.Abstractions; +using Microsoft.Extensions.Logging; + using DomainResults.Common; -using Microsoft.Extensions.Logging; + using MongoDB.Driver; using MongoDB.Driver.GridFS; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; + +using DataProviders.Buckets.Abstractions; namespace DataProviders.Buckets { @@ -32,6 +30,14 @@ namespace DataProviders.Buckets { /// /// (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId); + + /// + /// + /// + /// + /// + /// + IDomainResult DeleteOne(Guid siteId, Guid fileId); } public class TemplateBucketDataProvider : BucketDataProviderBase, ITemplateBucketDataProvider { @@ -40,6 +46,14 @@ namespace DataProviders.Buckets { IMongoClient client ) : base(logger, client, "reactredux", "templates") { } + // TODO: Manage TempleteTypes Enumeration and Metadata in Model and DataProvider + + /// + /// + /// + /// + /// + /// public (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId) { var filter = Builders.Filter.And( Builders.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"), @@ -54,5 +68,25 @@ namespace DataProviders.Buckets { return (list.First(), result); } + /// + /// + /// + /// + /// + /// + public IDomainResult DeleteOne(Guid siteId, Guid fileId) => + DeleteOneAsync(siteId, fileId).Result; + + /// + /// + /// + /// + /// + /// + public Task DeleteOneAsync(Guid siteId, Guid fileId) => + DeleteAsync(Builders.Filter.And( + Builders.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"), + Builders.Filter.Eq(x => x.Filename, $"{fileId}") + )); } } diff --git a/src/DataProviders/Collections/BlogCatalogDataProvider.cs b/src/DataProviders/Collections/BlogCatalogDataProvider.cs index 80bbe6b..fd20855 100644 --- a/src/DataProviders/Collections/BlogCatalogDataProvider.cs +++ b/src/DataProviders/Collections/BlogCatalogDataProvider.cs @@ -4,9 +4,8 @@ using DomainResults.Common; using MongoDB.Bson.Serialization; using MongoDB.Driver; - -using DomainObjects.Documents; using DataProviders.Collections.Abstractions; +using DomainObjects.Documents.Posts; namespace DataProviders.Collections { diff --git a/src/DataProviders/Collections/CategoryDataProvider.cs b/src/DataProviders/Collections/CategoryDataProvider.cs index a9b2249..0d7774d 100644 --- a/src/DataProviders/Collections/CategoryDataProvider.cs +++ b/src/DataProviders/Collections/CategoryDataProvider.cs @@ -4,33 +4,33 @@ using MongoDB.Driver; using MongoDB.Bson.Serialization; using DomainResults.Common; -using DomainObjects.Documents; -using DomainObjects.L10n; using DomainObjects.Enumerations; using DataProviders.Collections.Abstractions; +using DomainObjects.Documents.Categories; +using DomainObjects.Documents.Categories.L10n; namespace DataProviders.Collections { public interface ICategoryDataProvider { - (Guid?, IDomainResult) Insert(CategoryDocument obj); + (Guid?, IDomainResult) Insert(Category obj); (Guid?, IDomainResult) CreateDefault(Guid siteId); - (CategoryDocument?, IDomainResult) Get(Guid siteId, Guid categoryId); - (List?, IDomainResult) GetMany(Guid siteId, List categoryIds); - (CategoryDocument?, IDomainResult) GetBySlug(Guid siteId, string slug); - (List?, IDomainResult) GetBySlugs(Guid siteId, List slugs); - (List?, IDomainResult) GetAll(Guid siteId); - (Guid?, IDomainResult) Update(CategoryDocument obj); + (Category?, IDomainResult) Get(Guid siteId, Guid categoryId); + (List?, IDomainResult) GetMany(Guid siteId, List categoryIds); + (Category?, IDomainResult) GetBySlug(Guid siteId, string slug); + (List?, IDomainResult) GetBySlugs(Guid siteId, List slugs); + (List?, IDomainResult) GetAll(Guid siteId); + (Guid?, IDomainResult) Update(Category obj); IDomainResult Delete(Guid id); IDomainResult DeleteAll(Guid siteId); } - public class CategoryDataProvider : CollectionDataProviderBase, ICategoryDataProvider { + public class CategoryDataProvider : CollectionDataProviderBase, ICategoryDataProvider { private const string _databaseName = "reactredux"; private const string _collectionName = "categories"; public CategoryDataProvider( - ILogger> logger, + ILogger> logger, IMongoClient client, IIdGenerator idGenerator, ISessionService sessionService) : base(logger, client, idGenerator, sessionService, _databaseName, _collectionName) { @@ -42,7 +42,7 @@ namespace DataProviders.Collections if (result.IsSuccess && list != null) return (list.First().Id, result); - return Insert(new CategoryDocument { + return Insert(new Category { SiteId = siteId, L10n = new List { new CategoryL10n { @@ -54,7 +54,7 @@ namespace DataProviders.Collections }); } - public (CategoryDocument?, IDomainResult) Get(Guid siteId, Guid categoryId) { + public (Category?, IDomainResult) Get(Guid siteId, Guid categoryId) { var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Id == categoryId, x => x); if (!result.IsSuccess || list == null) @@ -63,10 +63,10 @@ namespace DataProviders.Collections return (list.First(), result); } - public (List?, IDomainResult) GetMany(Guid siteId, List categoryIds) => + public (List?, IDomainResult) GetMany(Guid siteId, List categoryIds) => GetWithPredicate(x => x.SiteId == siteId && categoryIds.Contains(x.Id), x => x); - public (CategoryDocument?, IDomainResult) GetBySlug(Guid siteId, string slug) { + public (Category?, IDomainResult) GetBySlug(Guid siteId, string slug) { var (list, result) = GetBySlugs(siteId, new List { slug }); if (!result.IsSuccess || list == null) @@ -75,13 +75,13 @@ namespace DataProviders.Collections return (list.First(), result); } - public (List?, IDomainResult) GetBySlugs(Guid siteId, List slugs) => + public (List?, IDomainResult) GetBySlugs(Guid siteId, List slugs) => GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)), x => x); - public (List?, IDomainResult) GetAll(Guid siteId) => + public (List?, IDomainResult) GetAll(Guid siteId) => GetWithPredicate(x => x.SiteId == siteId, x => x); - public (Guid?, IDomainResult) Update(CategoryDocument obj) => + public (Guid?, IDomainResult) Update(Category obj) => UpdateWithPredicate(obj, x => x.Id == obj.Id); public IDomainResult Delete(Guid id) => diff --git a/src/DataProviders/Collections/ShopCatalogDataProvider.cs b/src/DataProviders/Collections/ShopCatalogDataProvider.cs index a0fb020..e59014f 100644 --- a/src/DataProviders/Collections/ShopCatalogDataProvider.cs +++ b/src/DataProviders/Collections/ShopCatalogDataProvider.cs @@ -4,8 +4,8 @@ using DomainResults.Common; using MongoDB.Bson.Serialization; using MongoDB.Driver; -using DomainObjects.Documents; using DataProviders.Collections.Abstractions; +using DomainObjects.Documents.Posts; namespace DataProviders.Collections { diff --git a/src/DataProviders/Mappings.cs b/src/DataProviders/Mappings.cs index 44f55b0..8be2a38 100644 --- a/src/DataProviders/Mappings.cs +++ b/src/DataProviders/Mappings.cs @@ -12,6 +12,9 @@ using DomainObjects.Pages; using DomainObjects.Documents; using DomainObjects.Documents.Users; using DomainObjects.Documents.Sites; +using DomainObjects.Documents.Categories; +using DomainObjects.Documents.Categories.L10n; +using DomainObjects.Documents.Posts; namespace DataProviders { @@ -70,8 +73,8 @@ namespace DataProviders }); } - if (!BsonClassMap.IsClassMapRegistered(typeof(CategoryDocument))) { - BsonClassMap.RegisterClassMap(cm => { + if (!BsonClassMap.IsClassMapRegistered(typeof(Category))) { + BsonClassMap.RegisterClassMap(cm => { cm.AutoMap(); }); } diff --git a/src/DomainObjects/Contact.cs b/src/DomainObjects/Contact.cs index 029daff..2d40d2b 100644 --- a/src/DomainObjects/Contact.cs +++ b/src/DomainObjects/Contact.cs @@ -5,8 +5,13 @@ namespace DomainObjects; public class Contact : DomainObjectBase { public ContactTypes Type { get; set; } + + public string? Name { get; set; } + public string Value { get; set; } + public bool? Confirmed { get; set; } + public bool? Primary { get; set; } public Contact(ContactTypes type, string value) { diff --git a/src/DomainObjects/Documents/Address.cs b/src/DomainObjects/Documents/Address.cs index 7f05b4a..1fc384d 100644 --- a/src/DomainObjects/Documents/Address.cs +++ b/src/DomainObjects/Documents/Address.cs @@ -4,6 +4,7 @@ public class Address { public string Street { get; set; } public string City { get; set; } + public string Region { get; set; } public string PostCode { get; set; } public string Country { get; set; } } diff --git a/src/DomainObjects/Documents/BlogDocument.cs b/src/DomainObjects/Documents/BlogDocument.cs deleted file mode 100644 index 9dd4211..0000000 --- a/src/DomainObjects/Documents/BlogDocument.cs +++ /dev/null @@ -1,34 +0,0 @@ -using DomainObjects.Abstractions; - -namespace DomainObjects.Documents; - -public class BlogDocument : PostItemBase { - - public uint? ReadTime { get; set; } - - public uint? Likes { get; set; } - - public override int GetHashCode() { - unchecked { - int hash = 17; - hash = hash * 23 + Id.GetHashCode(); - hash = hash * 23 + SiteId.GetHashCode(); - hash = hash * 23 + L10n.GetHashCode(); - if (MediaAttachments != null) - hash = hash * 23 + MediaAttachments.Sum(x => x.GetHashCode()); - hash = hash * 23 + Author.GetHashCode(); - hash = hash * 23 + Created.GetHashCode(); - if(Tags != null) - hash = hash * 23 + Tags.Sum(x => x.GetHashCode()); - - if(Categories != null) - hash = hash * 23 + Categories.Sum(x => x.GetHashCode()); - - hash = hash * 23 + ReadTime.GetHashCode(); - hash = hash * 23 + Likes.GetHashCode(); - - return hash; - } - } -} - diff --git a/src/DomainObjects/Documents/Categories/Category.cs b/src/DomainObjects/Documents/Categories/Category.cs new file mode 100644 index 0000000..5665e96 --- /dev/null +++ b/src/DomainObjects/Documents/Categories/Category.cs @@ -0,0 +1,22 @@ +using DomainObjects.Abstractions; +using DomainObjects.Documents.Categories.L10n; + +namespace DomainObjects.Documents.Categories +{ + public class Category : DomainObjectDocumentBase + { + public Guid SiteId { get; set; } + public List L10n { get; set; } + + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = hash * 23 + Id.GetHashCode(); + hash += L10n.Sum(x => x.GetHashCode() * 23); + return hash; + } + } + } +} diff --git a/src/DomainObjects/Documents/Categories/L10n/CategoryL10n.cs b/src/DomainObjects/Documents/Categories/L10n/CategoryL10n.cs new file mode 100644 index 0000000..fc5d50d --- /dev/null +++ b/src/DomainObjects/Documents/Categories/L10n/CategoryL10n.cs @@ -0,0 +1,24 @@ +using DomainObjects.Abstractions; +using DomainObjects.Enumerations; + +namespace DomainObjects.Documents.Categories.L10n; + +public class CategoryL10n : DomainObjectBase +{ + public Locales Locale { get; set; } + public string Slug { get; set; } + public string Text { get; set; } + + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = hash * 23 + Locale.GetHashCode(); + hash = hash * 23 + Slug.GetHashCode(); + hash = hash * 23 + Text.GetHashCode(); + return hash; + } + } +} + diff --git a/src/DomainObjects/Documents/CategoryDocument.cs b/src/DomainObjects/Documents/CategoryDocument.cs deleted file mode 100644 index f4cfb8e..0000000 --- a/src/DomainObjects/Documents/CategoryDocument.cs +++ /dev/null @@ -1,18 +0,0 @@ -using DomainObjects.Abstractions; -using DomainObjects.L10n; - -namespace DomainObjects.Documents { - public class CategoryDocument : DomainObjectDocumentBase { - public Guid SiteId { get; set; } - public List L10n { get; set; } - - public override int GetHashCode() { - unchecked { - int hash = 17; - hash = hash * 23 + Id.GetHashCode(); - hash += L10n.Sum(x => x.GetHashCode() * 23); - return hash; - } - } - } -} diff --git a/src/DomainObjects/Documents/MailboxConnectionSettings.cs b/src/DomainObjects/Documents/MailboxConnectionSettings.cs index c9845dc..d641702 100644 --- a/src/DomainObjects/Documents/MailboxConnectionSettings.cs +++ b/src/DomainObjects/Documents/MailboxConnectionSettings.cs @@ -1,10 +1,5 @@ using CryptoProvider; using DomainObjects.Abstractions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace DomainObjects.Documents; @@ -19,10 +14,17 @@ public class MailboxConnectionSettings : DomainObjectBase + SetPassword(aesKey.IV, aesKey.Key, password); + public void SetPassword(string iv, string key, string password) { Password = AesService.EncryptString(iv, key, password); } + public string GetPassword(IAesKey aesKey) => + GetPassword(aesKey.IV, aesKey.Key); + public string GetPassword(string iv, string key) => AesService.DecryptString(iv, key, Password); diff --git a/src/DomainObjects/Documents/Posts/BlogDocument.cs b/src/DomainObjects/Documents/Posts/BlogDocument.cs new file mode 100644 index 0000000..b85df91 --- /dev/null +++ b/src/DomainObjects/Documents/Posts/BlogDocument.cs @@ -0,0 +1,37 @@ +using DomainObjects.Abstractions; + +namespace DomainObjects.Documents.Posts; + +public class BlogDocument : PostItemBase +{ + + public uint? ReadTime { get; set; } + + public uint? Likes { get; set; } + + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = hash * 23 + Id.GetHashCode(); + hash = hash * 23 + SiteId.GetHashCode(); + hash = hash * 23 + L10n.GetHashCode(); + if (MediaAttachments != null) + hash = hash * 23 + MediaAttachments.Sum(x => x.GetHashCode()); + hash = hash * 23 + Author.GetHashCode(); + hash = hash * 23 + Created.GetHashCode(); + if (Tags != null) + hash = hash * 23 + Tags.Sum(x => x.GetHashCode()); + + if (Categories != null) + hash = hash * 23 + Categories.Sum(x => x.GetHashCode()); + + hash = hash * 23 + ReadTime.GetHashCode(); + hash = hash * 23 + Likes.GetHashCode(); + + return hash; + } + } +} + diff --git a/src/DomainObjects/Documents/Posts/ShopDocument.cs b/src/DomainObjects/Documents/Posts/ShopDocument.cs new file mode 100644 index 0000000..5ed4240 --- /dev/null +++ b/src/DomainObjects/Documents/Posts/ShopDocument.cs @@ -0,0 +1,39 @@ +using DomainObjects.Abstractions; + +namespace DomainObjects.Documents.Posts; + +public class ShopDocument : PostItemBase +{ + public string BrandName { get; set; } + public string Sku { get; set; } + public decimal? Rating { get; set; } + public decimal Price { get; set; } + public decimal? NewPrice { get; set; } + public uint Quantity { get; set; } + + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = hash * 23 + Id.GetHashCode(); + hash = hash * 23 + SiteId.GetHashCode(); + hash = hash * 23 + L10n.GetHashCode(); + if (MediaAttachments != null) + hash = hash * 23 + MediaAttachments.Sum(x => x.GetHashCode()); + hash = hash * 23 + Author.GetHashCode(); + hash = hash * 23 + Created.GetHashCode(); + hash = hash * 23 + Tags.GetHashCode(); + hash = hash * 23 + Categories.Sum(x => x.GetHashCode()); + + hash = hash * 23 + BrandName.GetHashCode(); + hash = hash * 23 + Sku.GetHashCode(); + hash = hash * 23 + Rating.GetHashCode(); + hash = hash * 23 + Price.GetHashCode(); + hash = hash * 23 + NewPrice.GetHashCode(); + hash = hash * 23 + Quantity.GetHashCode(); + + return hash; + } + } +} diff --git a/src/DomainObjects/Documents/ShopDocument.cs b/src/DomainObjects/Documents/ShopDocument.cs deleted file mode 100644 index 57548ed..0000000 --- a/src/DomainObjects/Documents/ShopDocument.cs +++ /dev/null @@ -1,36 +0,0 @@ -using DomainObjects.Abstractions; - -namespace DomainObjects.Documents; - -public class ShopDocument : PostItemBase { - public string BrandName { get; set; } - public string Sku { get; set; } - public decimal? Rating { get; set; } - public decimal Price { get; set; } - public decimal? NewPrice { get; set; } - public uint Quantity { get; set; } - - public override int GetHashCode() { - unchecked { - int hash = 17; - hash = hash * 23 + Id.GetHashCode(); - hash = hash * 23 + SiteId.GetHashCode(); - hash = hash * 23 + L10n.GetHashCode(); - if (MediaAttachments != null) - hash = hash * 23 + MediaAttachments.Sum(x => x.GetHashCode()); - hash = hash * 23 + Author.GetHashCode(); - hash = hash * 23 + Created.GetHashCode(); - hash = hash * 23 + Tags.GetHashCode(); - hash = hash * 23 + Categories.Sum(x => x.GetHashCode()); - - hash = hash * 23 + BrandName.GetHashCode(); - hash = hash * 23 + Sku.GetHashCode(); - hash = hash * 23 + Rating.GetHashCode(); - hash = hash * 23 + Price.GetHashCode(); - hash = hash * 23 + NewPrice.GetHashCode(); - hash = hash * 23 + Quantity.GetHashCode(); - - return hash; - } - } -} diff --git a/src/DomainObjects/Documents/Sites/PassworRecoverySettings.cs b/src/DomainObjects/Documents/Sites/PassworRecoverySettings.cs index 793ef88..0a20521 100644 --- a/src/DomainObjects/Documents/Sites/PassworRecoverySettings.cs +++ b/src/DomainObjects/Documents/Sites/PassworRecoverySettings.cs @@ -4,6 +4,8 @@ namespace DomainObjects.Documents.Sites; public class PassworRecoverySettings : DomainObjectBase { + public MailboxConnectionSettings SmtpSettings { get; set; } + /// /// DomainKeys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in email (email spoofing),
/// a technique often used in phishing and email spam. @@ -12,14 +14,16 @@ public class PassworRecoverySettings : DomainObjectBase public Guid TemplateId { get; set; } - public string Name { get; set; } - public Contact Email { get; set; } + public string Subject { get; set; } public List Paragraphs { get; set; } + + + public override int GetHashCode() { throw new NotImplementedException(); } diff --git a/src/DomainObjects/Documents/Sites/Site.cs b/src/DomainObjects/Documents/Sites/Site.cs index 81ff66f..9333f33 100644 --- a/src/DomainObjects/Documents/Sites/Site.cs +++ b/src/DomainObjects/Documents/Sites/Site.cs @@ -1,20 +1,19 @@ using DomainObjects.Abstractions; +using DomainObjects.Enumerations; namespace DomainObjects.Documents.Sites { public class Site : DomainObjectDocumentBase { + public Locales Locale { get; set; } + public string Name { get; set; } public List Hosts { get; set; } public Address Address { get; set; } - public string PoweredBy { get; set; } - - public MailboxConnectionSettings SmtpSettings { get; set; } - - public MailboxConnectionSettings ImapSettings { get; set; } + public Link PoweredBy { get; set; } public PassworRecoverySettings PassworRecoverySettings { get; set; } diff --git a/src/DomainObjects/L10n/CategoryL10n.cs b/src/DomainObjects/L10n/CategoryL10n.cs deleted file mode 100644 index 908332b..0000000 --- a/src/DomainObjects/L10n/CategoryL10n.cs +++ /dev/null @@ -1,21 +0,0 @@ -using DomainObjects.Abstractions; -using DomainObjects.Enumerations; - -namespace DomainObjects.L10n; - -public class CategoryL10n : DomainObjectBase { - public Locales Locale { get; set; } - public string Slug { get; set; } - public string Text { get; set; } - - public override int GetHashCode() { - unchecked { - int hash = 17; - hash = hash * 23 + Locale.GetHashCode(); - hash = hash * 23 + Slug.GetHashCode(); - hash = hash * 23 + Text.GetHashCode(); - return hash; - } - } -} - diff --git a/src/WeatherForecast/Configuration.cs b/src/WeatherForecast/Configuration.cs index 5a2f5e7..4148e9c 100644 --- a/src/WeatherForecast/Configuration.cs +++ b/src/WeatherForecast/Configuration.cs @@ -50,6 +50,11 @@ namespace WeatherForecast { ///
public AesKey? JwtTokenEncryption { get; set; } + /// + /// + /// + public AesKey? MailPasswordEncryption { get; set; } + /// /// /// diff --git a/src/WeatherForecast/Controllers/BlogItemController.cs b/src/WeatherForecast/Controllers/BlogItemController.cs index 398d9f7..34f6543 100644 --- a/src/WeatherForecast/Controllers/BlogItemController.cs +++ b/src/WeatherForecast/Controllers/BlogItemController.cs @@ -10,7 +10,7 @@ using Core.Enumerations; using DataProviders.Collections; using Extensions; using DomainResults.Common; -using DomainObjects.Documents; +using DomainObjects.Documents.Posts; namespace WeatherForecast.Controllers; diff --git a/src/WeatherForecast/Controllers/DkimController.cs b/src/WeatherForecast/Controllers/DkimController.cs index 1bf65f1..4debdfa 100644 --- a/src/WeatherForecast/Controllers/DkimController.cs +++ b/src/WeatherForecast/Controllers/DkimController.cs @@ -34,7 +34,7 @@ public class DkimController : ControllerBase { } /// - /// Allows to upload private dkim certificate + /// /// /// /// @@ -52,4 +52,17 @@ public class DkimController : ControllerBase { return result.ToActionResult(); } + + /// + /// Delete private dkim cert + /// + /// + /// + /// + [HttpDelete("{siteId}/{fileId}")] + public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid fileId) { + + var result = _dkimService.Delete(siteId, fileId); + return result.ToActionResult(); + } } diff --git a/src/WeatherForecast/Controllers/ShopItemController.cs b/src/WeatherForecast/Controllers/ShopItemController.cs index 2258155..6fd3dad 100644 --- a/src/WeatherForecast/Controllers/ShopItemController.cs +++ b/src/WeatherForecast/Controllers/ShopItemController.cs @@ -8,9 +8,9 @@ using WeatherForecast.Models.Requests; using WeatherForecast.Policies; using Core.Enumerations; using DataProviders.Collections; -using DomainObjects.Documents; using DomainResults.Common; using Extensions; +using DomainObjects.Documents.Posts; namespace WeatherForecast.Controllers; diff --git a/src/WeatherForecast/Controllers/TemplateController.cs b/src/WeatherForecast/Controllers/TemplateController.cs index b206568..624ede1 100644 --- a/src/WeatherForecast/Controllers/TemplateController.cs +++ b/src/WeatherForecast/Controllers/TemplateController.cs @@ -53,4 +53,18 @@ public class TemplateController : ControllerBase { return result.ToActionResult(); } + + + /// + /// Delete template + /// + /// + /// + /// + [HttpDelete("{siteId}/{fileId}")] + public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid fileId) { + + var result = _templateService.Delete(siteId, fileId); + return result.ToActionResult(); + } } diff --git a/src/WeatherForecast/Controllers/UtilsController.cs b/src/WeatherForecast/Controllers/UtilsController.cs index d445f30..ffa8864 100644 --- a/src/WeatherForecast/Controllers/UtilsController.cs +++ b/src/WeatherForecast/Controllers/UtilsController.cs @@ -6,6 +6,7 @@ using DomainResults.Mvc; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using WeatherForecast.Models.Initialization.Requests; +using WeatherForecast.Models.Utils.Requests; using WeatherForecast.Services; namespace WeatherForecast.Controllers; @@ -27,5 +28,19 @@ public class UtilsController : ControllerBase { return Ok(AesService.GenerateKey()); } + /// + /// + /// + /// + /// + [HttpPost("[action]")] + public IActionResult EncryptStringAes([FromBody] EncryptStringAesRequestModel requestData) { + var result = AesService.EncryptString(requestData.IV, requestData.Key, requestData.InputString); + if(result != null) + return Ok(result); + + return BadRequest(); + } + } diff --git a/src/WeatherForecast/Models/Abstractions/PostItem/Responsens/PostItemResponseModelBase.cs b/src/WeatherForecast/Models/Abstractions/PostItem/Responsens/PostItemResponseModelBase.cs index 89cd2c2..e27b507 100644 --- a/src/WeatherForecast/Models/Abstractions/PostItem/Responsens/PostItemResponseModelBase.cs +++ b/src/WeatherForecast/Models/Abstractions/PostItem/Responsens/PostItemResponseModelBase.cs @@ -1,19 +1,19 @@ using DomainObjects.Abstractions; -using DomainObjects.Documents; using DomainObjects.Enumerations; using Core.Abstractions.Models; using WeatherForecast.Models.Category.Responses; +using DomainObjects.Documents.Categories; +namespace WeatherForecast.Models.Abstractions.PostItem.Responses +{ -namespace WeatherForecast.Models.Abstractions.PostItem.Responses { - - /// - /// - /// - /// - public abstract class PostItemResponseModelBase : ResponseModelBase { + /// + /// + /// + /// + public abstract class PostItemResponseModelBase : ResponseModelBase { /// /// @@ -122,7 +122,7 @@ namespace WeatherForecast.Models.Abstractions.PostItem.Responses { /// /// /// - public PostItemResponseModelBase(PostItemBase postItem, List categories) : this(postItem) { + public PostItemResponseModelBase(PostItemBase postItem, List categories) : this(postItem) { L10n = postItem.L10n.Select(x => new PostItemL10nModel(x)).ToList(); Categories = categories.Select(x => new CategoryItemResponseModel(x)).ToList(); MediaAttachemnts = postItem.MediaAttachments?.Select(x => new MediaAttachmentResponseModel(x)).ToList(); @@ -134,7 +134,7 @@ namespace WeatherForecast.Models.Abstractions.PostItem.Responses { /// /// /// - public PostItemResponseModelBase(PostItemBase postItem, List categories, Locales locale) : this(postItem) { + public PostItemResponseModelBase(PostItemBase postItem, List categories, Locales locale) : this(postItem) { var postItemL10n = postItem.L10n.Single(x => x.Locale == locale); diff --git a/src/WeatherForecast/Models/BlogItem/Requests/BlogItemRequestModel.cs b/src/WeatherForecast/Models/BlogItem/Requests/BlogItemRequestModel.cs index a32155a..0979dc4 100644 --- a/src/WeatherForecast/Models/BlogItem/Requests/BlogItemRequestModel.cs +++ b/src/WeatherForecast/Models/BlogItem/Requests/BlogItemRequestModel.cs @@ -1,15 +1,16 @@ -using DomainObjects.Documents; -using Core.Enumerations; +using Core.Enumerations; using Extensions; using System.ComponentModel.DataAnnotations; using WeatherForecast.Models.Abstractions.PostItem.Requests; +using DomainObjects.Documents.Posts; -namespace WeatherForecast.Models.Blog.Requests { +namespace WeatherForecast.Models.Blog.Requests +{ - /// - /// - /// - public class BlogItemRequestModel : PostItemRequestModelBase { + /// + /// + /// + public class BlogItemRequestModel : PostItemRequestModelBase { /// /// diff --git a/src/WeatherForecast/Models/BlogItem/Responses/BlogItemResponseModel.cs b/src/WeatherForecast/Models/BlogItem/Responses/BlogItemResponseModel.cs index 5a53022..f7425f6 100644 --- a/src/WeatherForecast/Models/BlogItem/Responses/BlogItemResponseModel.cs +++ b/src/WeatherForecast/Models/BlogItem/Responses/BlogItemResponseModel.cs @@ -1,14 +1,15 @@ -using DomainObjects.Documents; - -using WeatherForecast.Models.Abstractions.PostItem.Responses; +using WeatherForecast.Models.Abstractions.PostItem.Responses; using DomainObjects.Enumerations; +using DomainObjects.Documents.Categories; +using DomainObjects.Documents.Posts; -namespace WeatherForecast.Models.Blog.Responses { +namespace WeatherForecast.Models.Blog.Responses +{ - /// - /// - /// - public class BlogItemResponseModel : PostItemResponseModelBase { + /// + /// + /// + public class BlogItemResponseModel : PostItemResponseModelBase { /// /// @@ -25,7 +26,7 @@ namespace WeatherForecast.Models.Blog.Responses { /// /// /// - public BlogItemResponseModel(BlogDocument blogItem, List categories) : base(blogItem, categories) { + public BlogItemResponseModel(BlogDocument blogItem, List categories) : base(blogItem, categories) { ReadTime = blogItem.ReadTime; Likes = blogItem.Likes; } @@ -36,7 +37,7 @@ namespace WeatherForecast.Models.Blog.Responses { /// /// /// - public BlogItemResponseModel(BlogDocument blogItem, List categories, Locales locale) : base(blogItem, categories, locale) { + public BlogItemResponseModel(BlogDocument blogItem, List categories, Locales locale) : base(blogItem, categories, locale) { ReadTime = blogItem.ReadTime; Likes = blogItem.Likes; } diff --git a/src/WeatherForecast/Models/CategoryItem/Requests/CategoryItemRequestModel.cs b/src/WeatherForecast/Models/CategoryItem/Requests/CategoryItemRequestModel.cs index 62d1997..cb26bcc 100644 --- a/src/WeatherForecast/Models/CategoryItem/Requests/CategoryItemRequestModel.cs +++ b/src/WeatherForecast/Models/CategoryItem/Requests/CategoryItemRequestModel.cs @@ -1,15 +1,16 @@ using Core.Abstractions.Models; -using DomainObjects.Documents; using Core.Enumerations; using Extensions; using System.ComponentModel.DataAnnotations; +using DomainObjects.Documents.Categories; -namespace WeatherForecast.Models.Category.Requests { +namespace WeatherForecast.Models.Category.Requests +{ - /// - /// - /// - public class CategoryItemRequestModel : RequestModelBase { + /// + /// + /// + public class CategoryItemRequestModel : RequestModelBase { /// /// @@ -20,7 +21,7 @@ namespace WeatherForecast.Models.Category.Requests { /// /// /// - public CategoryDocument ToDomainObject(Guid siteId) => new CategoryDocument() { + public DomainObjects.Documents.Categories.Category ToDomainObject(Guid siteId) => new DomainObjects.Documents.Categories.Category() { SiteId = siteId, L10n = L10n.Select(x => x.ToDomainObject()).ToList() diff --git a/src/WeatherForecast/Models/CategoryItem/Requests/CategoryL10nModel.cs b/src/WeatherForecast/Models/CategoryItem/Requests/CategoryL10nModel.cs index cfc692c..a1bd5f5 100644 --- a/src/WeatherForecast/Models/CategoryItem/Requests/CategoryL10nModel.cs +++ b/src/WeatherForecast/Models/CategoryItem/Requests/CategoryL10nModel.cs @@ -2,16 +2,17 @@ using System.Diagnostics.CodeAnalysis; using Core.Abstractions; using Core.Abstractions.Models; -using DomainObjects.L10n; using Core.Enumerations; using DomainObjects.Enumerations; +using DomainObjects.Documents.Categories.L10n; -namespace WeatherForecast.Models.Category.Requests { +namespace WeatherForecast.Models.Category.Requests +{ - /// - /// - /// - public class CategoryL10nModel : RequestModelBase { + /// + /// + /// + public class CategoryL10nModel : RequestModelBase { /// /// diff --git a/src/WeatherForecast/Models/CategoryItem/Responses/CategoryItemResponseModel.cs b/src/WeatherForecast/Models/CategoryItem/Responses/CategoryItemResponseModel.cs index a4b059d..91b3b2b 100644 --- a/src/WeatherForecast/Models/CategoryItem/Responses/CategoryItemResponseModel.cs +++ b/src/WeatherForecast/Models/CategoryItem/Responses/CategoryItemResponseModel.cs @@ -1,14 +1,14 @@ -using DomainObjects.Documents; - -using Core.Abstractions.Models; +using Core.Abstractions.Models; using DomainObjects.Enumerations; +using DomainObjects.Documents.Categories; -namespace WeatherForecast.Models.Category.Responses { +namespace WeatherForecast.Models.Category.Responses +{ - /// - /// - /// - public class CategoryItemResponseModel : ResponseModelBase { + /// + /// + /// + public class CategoryItemResponseModel : ResponseModelBase { /// /// @@ -39,7 +39,7 @@ namespace WeatherForecast.Models.Category.Responses { /// /// /// - public CategoryItemResponseModel(CategoryDocument category) { + public CategoryItemResponseModel(DomainObjects.Documents.Categories.Category category) { Id = category.Id; SiteId = category.SiteId; @@ -51,7 +51,7 @@ namespace WeatherForecast.Models.Category.Responses { /// /// /// - public CategoryItemResponseModel(CategoryDocument category, Locales locale) { + public CategoryItemResponseModel(DomainObjects.Documents.Categories.Category category, Locales locale) { Id = category.Id; SiteId = category.SiteId; diff --git a/src/WeatherForecast/Models/CategoryItem/Responses/CategoryL10nModel.cs b/src/WeatherForecast/Models/CategoryItem/Responses/CategoryL10nModel.cs index 33fc86c..544e1dd 100644 --- a/src/WeatherForecast/Models/CategoryItem/Responses/CategoryL10nModel.cs +++ b/src/WeatherForecast/Models/CategoryItem/Responses/CategoryL10nModel.cs @@ -1,12 +1,13 @@ using Core.Abstractions.Models; -using DomainObjects.L10n; +using DomainObjects.Documents.Categories.L10n; -namespace WeatherForecast.Models.Category.Responses { +namespace WeatherForecast.Models.Category.Responses +{ - /// - /// - /// - public class CategoryL10nModel : ResponseModelBase { + /// + /// + /// + public class CategoryL10nModel : ResponseModelBase { /// /// diff --git a/src/WeatherForecast/Models/Initialization/Requests/InitializeSystemRequestModel.cs b/src/WeatherForecast/Models/Initialization/Requests/InitializeSystemRequestModel.cs index d61a056..c597e59 100644 --- a/src/WeatherForecast/Models/Initialization/Requests/InitializeSystemRequestModel.cs +++ b/src/WeatherForecast/Models/Initialization/Requests/InitializeSystemRequestModel.cs @@ -41,15 +41,8 @@ namespace WeatherForecast.Models.Initialization.Requests { /// public string RePassword { get; set; } - /// - /// - /// - public string DkimBase64 { get; set; } - /// - /// - /// - public string ServiceEmlTemplateBase64 { get; set; } + /// /// @@ -88,11 +81,11 @@ namespace WeatherForecast.Models.Initialization.Requests { if (string.Compare(Password, RePassword) != 0) yield return new ValidationResult($"{nameof(Password)} and {nameof(RePassword)} ${Errors.NotMatched}"); - if (string.IsNullOrWhiteSpace(DkimBase64)) - yield return new ValidationResult($"{nameof(DkimBase64)} {Errors.NullOrWhiteSpace}"); + //if (string.IsNullOrWhiteSpace(DkimBase64)) + // yield return new ValidationResult($"{nameof(DkimBase64)} {Errors.NullOrWhiteSpace}"); - if (string.IsNullOrWhiteSpace(ServiceEmlTemplateBase64)) - yield return new ValidationResult($"{nameof(ServiceEmlTemplateBase64)} {Errors.NullOrWhiteSpace}"); + //if (string.IsNullOrWhiteSpace(ServiceEmlTemplateBase64)) + // yield return new ValidationResult($"{nameof(ServiceEmlTemplateBase64)} {Errors.NullOrWhiteSpace}"); } } } diff --git a/src/WeatherForecast/Models/Shop/Requests/ShopItemRequestModel.cs b/src/WeatherForecast/Models/Shop/Requests/ShopItemRequestModel.cs index feb5ee0..fb23fbe 100644 --- a/src/WeatherForecast/Models/Shop/Requests/ShopItemRequestModel.cs +++ b/src/WeatherForecast/Models/Shop/Requests/ShopItemRequestModel.cs @@ -1,16 +1,16 @@ using System.ComponentModel.DataAnnotations; - -using DomainObjects.Documents; using Core.Enumerations; using Extensions; using WeatherForecast.Models.Abstractions.PostItem.Requests; +using DomainObjects.Documents.Posts; -namespace WeatherForecast.Models.Requests { +namespace WeatherForecast.Models.Requests +{ - /// - /// - /// - public class ShopItemRequestModel : PostItemRequestModelBase { + /// + /// + /// + public class ShopItemRequestModel : PostItemRequestModelBase { /// /// diff --git a/src/WeatherForecast/Models/Shop/Responses/ShopCartItemResponseModel.cs b/src/WeatherForecast/Models/Shop/Responses/ShopCartItemResponseModel.cs index 3bcc125..3bf79cd 100644 --- a/src/WeatherForecast/Models/Shop/Responses/ShopCartItemResponseModel.cs +++ b/src/WeatherForecast/Models/Shop/Responses/ShopCartItemResponseModel.cs @@ -1,14 +1,16 @@ using Core.Abstractions.Models; using DomainObjects.Documents; +using DomainObjects.Documents.Posts; using DomainObjects.Enumerations; using WeatherForecast.Models.Abstractions.PostItem.Responses; -namespace WeatherForecast.Models.Responses { +namespace WeatherForecast.Models.Responses +{ - /// - /// - /// - public class ShopCartItemResponseModel : ResponseModelBase { + /// + /// + /// + public class ShopCartItemResponseModel : ResponseModelBase { /// /// diff --git a/src/WeatherForecast/Models/Shop/Responses/ShopItemResponseModel.cs b/src/WeatherForecast/Models/Shop/Responses/ShopItemResponseModel.cs index ad9c45a..deb9ec5 100644 --- a/src/WeatherForecast/Models/Shop/Responses/ShopItemResponseModel.cs +++ b/src/WeatherForecast/Models/Shop/Responses/ShopItemResponseModel.cs @@ -1,13 +1,15 @@ -using DomainObjects.Documents; +using DomainObjects.Documents.Categories; +using DomainObjects.Documents.Posts; using DomainObjects.Enumerations; using WeatherForecast.Models.Abstractions.PostItem.Responses; -namespace WeatherForecast.Models.Shop.Responses { +namespace WeatherForecast.Models.Shop.Responses +{ - /// - /// - /// - public class ShopItemResponseModel : PostItemResponseModelBase { + /// + /// + /// + public class ShopItemResponseModel : PostItemResponseModelBase { /// /// @@ -39,7 +41,7 @@ namespace WeatherForecast.Models.Shop.Responses { /// /// /// - public ShopItemResponseModel(ShopDocument shopCatalogItem, List categories) : base(shopCatalogItem, categories) { + public ShopItemResponseModel(ShopDocument shopCatalogItem, List categories) : base(shopCatalogItem, categories) { Sku = shopCatalogItem.Sku; Rating = shopCatalogItem.Rating; Price = shopCatalogItem.Price; @@ -53,7 +55,7 @@ namespace WeatherForecast.Models.Shop.Responses { /// /// /// - public ShopItemResponseModel(ShopDocument shopCatalogItem, List categories, Locales locale) : base(shopCatalogItem, categories, locale) { + public ShopItemResponseModel(ShopDocument shopCatalogItem, List categories, Locales locale) : base(shopCatalogItem, categories, locale) { Sku = shopCatalogItem.Sku; Rating = shopCatalogItem.Rating; Price = shopCatalogItem.Price; diff --git a/src/WeatherForecast/Models/Utils/Requests/EncryptStringAesRequestModel.cs b/src/WeatherForecast/Models/Utils/Requests/EncryptStringAesRequestModel.cs new file mode 100644 index 0000000..ea94937 --- /dev/null +++ b/src/WeatherForecast/Models/Utils/Requests/EncryptStringAesRequestModel.cs @@ -0,0 +1,45 @@ +using Core.Abstractions.Models; +using Core.Enumerations; +using Extensions; +using System.ComponentModel.DataAnnotations; + +namespace WeatherForecast.Models.Utils.Requests { + + /// + /// + /// + public class EncryptStringAesRequestModel : RequestModelBase { + + /// + /// String to encrypt + /// + public string InputString { get; set; } = string.Empty; + + /// + /// + /// + public string IV { get; set; } = string.Empty; + + /// + /// + /// + public string Key { get; set; } = string.Empty; + + /// + /// + /// + /// + /// + /// + public override IEnumerable Validate(ValidationContext validationContext) { + if (string.IsNullOrEmpty(InputString)) + yield return new ValidationResult($"{nameof(InputString)} {Errors.NullOrEmpty}"); + + if (string.IsNullOrEmpty(IV)) + yield return new ValidationResult($"{nameof(IV)} {Errors.NullOrEmpty}"); + + if (string.IsNullOrEmpty(Key)) + yield return new ValidationResult($"{nameof(Key)} {Errors.NullOrEmpty}"); + } + } +} diff --git a/src/WeatherForecast/Policies/BlogAuthorizationHandler.cs b/src/WeatherForecast/Policies/BlogAuthorizationHandler.cs index 6d4c3ff..403fcef 100644 --- a/src/WeatherForecast/Policies/BlogAuthorizationHandler.cs +++ b/src/WeatherForecast/Policies/BlogAuthorizationHandler.cs @@ -1,6 +1,4 @@ using Microsoft.AspNetCore.Authorization; - -using DomainObjects.Documents; using Core.Enumerations; using DataProviders.Collections; @@ -8,6 +6,7 @@ using DataProviders.Collections; using WeatherForecast.Policies.Abstractions; using Microsoft.Extensions.Options; using DomainObjects.Documents.Users; +using DomainObjects.Documents.Posts; namespace WeatherForecast.Policies; diff --git a/src/WeatherForecast/Policies/ShopAuthorizationHandler.cs b/src/WeatherForecast/Policies/ShopAuthorizationHandler.cs index 0615a66..0ef8716 100644 --- a/src/WeatherForecast/Policies/ShopAuthorizationHandler.cs +++ b/src/WeatherForecast/Policies/ShopAuthorizationHandler.cs @@ -1,12 +1,13 @@ using Core.Enumerations; using DataProviders.Collections; -using DomainObjects.Documents; +using DomainObjects.Documents.Posts; using DomainObjects.Documents.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Options; using WeatherForecast.Policies.Abstractions; -namespace WeatherForecast.Policies { +namespace WeatherForecast.Policies +{ /// /// diff --git a/src/WeatherForecast/Services/AccountService.cs b/src/WeatherForecast/Services/AccountService.cs index b1e55f0..e2620d1 100644 --- a/src/WeatherForecast/Services/AccountService.cs +++ b/src/WeatherForecast/Services/AccountService.cs @@ -55,7 +55,9 @@ namespace WeatherForecast.Services { /// public class AccountService : ServiceBase, IAccountService { - private readonly IAesKey _aesKey; + private readonly IAesKey _jwtTokenEncryption; + private readonly IAesKey _mailPasswordEncryption; + private readonly IJwtConfig _jwtConfig; private readonly IUserDataProvider _userDataProvider; @@ -84,9 +86,16 @@ namespace WeatherForecast.Services { if (options.Value.JwtTokenEncryption == null) throw new ArgumentNullException(); - _aesKey = options.Value.JwtTokenEncryption; + _jwtTokenEncryption = options.Value.JwtTokenEncryption; - if(options.Value.JwtConfig == null) + + if (options.Value.MailPasswordEncryption == null) + throw new ArgumentNullException(); + + _mailPasswordEncryption = options.Value.MailPasswordEncryption; + + + if (options.Value.JwtConfig == null) throw new ArgumentNullException(); _jwtConfig = options.Value.JwtConfig; @@ -104,12 +113,6 @@ namespace WeatherForecast.Services { /// public (string?, IDomainResult) Authenticate(AuthenticationRequestModel requestData) { - if (_aesKey?.IV == null || _aesKey?.Key == null) - return IDomainResult.Failed("IV or Key are not set"); - - if (_jwtConfig?.Secret == null || _jwtConfig?.Expires == null) - return IDomainResult.Failed("Secret or Expires are not set"); - // Retrieve user from database by userName var (user, getUserResult) = _userDataProvider.GetByUsername(requestData.Username); if (!getUserResult.IsSuccess || user == null) @@ -119,7 +122,7 @@ namespace WeatherForecast.Services { if (!user.Authentication.ValidatePassword(requestData.Password)) return IDomainResult.Unauthorized(); - var token = user.AddToken(_aesKey.IV, _aesKey.Key, _jwtConfig.Secret, _jwtConfig.Expires); + var token = user.AddToken(_jwtTokenEncryption.IV, _jwtTokenEncryption.Key, _jwtConfig.Secret, _jwtConfig.Expires); var (_, usdateUserResult) = _userDataProvider.Update(user); if (!usdateUserResult.IsSuccess) @@ -166,7 +169,7 @@ namespace WeatherForecast.Services { _userDataProvider.Update(user); var emailBuilder = new EmailMessageBuilder(); - emailBuilder.AddFrom(site.PassworRecoverySettings.Name, site.PassworRecoverySettings.Email.Value); + emailBuilder.AddFrom(site.PassworRecoverySettings.Email.Name ?? site.PassworRecoverySettings.Email.Value, site.PassworRecoverySettings.Email.Value); emailBuilder.AddTo(user.Username, email.Value); emailBuilder.AddSubject(site.PassworRecoverySettings.Subject); emailBuilder.AddHtmlBody(htmlBody); @@ -174,9 +177,9 @@ namespace WeatherForecast.Services { using var smtpService = new SMTPService(); - // TODO: change email password and manage configs inside database - smtpService.Connect(site.SmtpSettings.Server, site.SmtpSettings.Port, site.SmtpSettings.UseSsl); - //smtpService.Authenticate(site.PassworRecoverySettings.SmtpSettings.UserName, site.PassworRecoverySettings.SmtpSettings.GetPassword()); + var smtpSettings = site.PassworRecoverySettings.SmtpSettings; + smtpService.Connect(smtpSettings.Server, smtpSettings.Port, smtpSettings.UseSsl); + smtpService.Authenticate(smtpSettings.UserName, smtpSettings.GetPassword(_mailPasswordEncryption)); smtpService.Send(emailBuilder.Build()); diff --git a/src/WeatherForecast/Services/BlogItemService.cs b/src/WeatherForecast/Services/BlogItemService.cs index 6c2b7ce..8674162 100644 --- a/src/WeatherForecast/Services/BlogItemService.cs +++ b/src/WeatherForecast/Services/BlogItemService.cs @@ -2,18 +2,18 @@ using DataProviders.Collections; -using DomainObjects.Documents; - using WeatherForecast.Services.Abstractions; using WeatherForecast.Models.Blog.Responses; using WeatherForecast.Models.Blog.Requests; +using DomainObjects.Documents.Posts; -namespace WeatherForecast.Services { +namespace WeatherForecast.Services +{ - /// - /// - /// - public interface IBlogItemService { + /// + /// + /// + public interface IBlogItemService { /// /// diff --git a/src/WeatherForecast/Services/DkimService.cs b/src/WeatherForecast/Services/DkimService.cs index d74a1d8..3cc9db5 100644 --- a/src/WeatherForecast/Services/DkimService.cs +++ b/src/WeatherForecast/Services/DkimService.cs @@ -16,6 +16,14 @@ namespace WeatherForecast.Services { /// /// (Guid?, IDomainResult) Post(BucketFile file); + + /// + /// + /// + /// + /// + /// + IDomainResult Delete(Guid siteId, Guid fileId); } /// @@ -38,7 +46,7 @@ namespace WeatherForecast.Services { } /// - /// + /// Upload dkim private cert for website /// /// /// @@ -50,5 +58,20 @@ namespace WeatherForecast.Services { return IDomainResult.Success(fileId); } + + /// + /// + /// + /// + /// + /// + /// + public IDomainResult Delete(Guid siteId, Guid fileId) { + var result = _dkimBucketDataProvider.DeleteOne(siteId, fileId); + if (!result.IsSuccess) + return IDomainResult.Failed(); + + return IDomainResult.Success(); + } } } diff --git a/src/WeatherForecast/Services/ImageService.cs b/src/WeatherForecast/Services/ImageService.cs index 0c0e43c..e953358 100644 --- a/src/WeatherForecast/Services/ImageService.cs +++ b/src/WeatherForecast/Services/ImageService.cs @@ -42,6 +42,7 @@ namespace WeatherForecast.Services { /// /// /// + /// public ImageService( ILogger logger, IImageBucketDataProvider imageBucketDataProvider, diff --git a/src/WeatherForecast/Services/InitializationService.cs b/src/WeatherForecast/Services/InitializationService.cs index b03d552..328244a 100644 --- a/src/WeatherForecast/Services/InitializationService.cs +++ b/src/WeatherForecast/Services/InitializationService.cs @@ -57,7 +57,15 @@ public class InitializationService : ServiceBase, IInitia IDkimBucketDataProvider dkimBucketDataProvider, ITemplateBucketDataProvider serviceEmlTemplateBucketDataProvider ) : base(logger) { + + if (options.Value.JwtTokenEncryption == null) + throw new ArgumentNullException(nameof(options.Value.JwtTokenEncryption)); + _aesKey = options.Value.JwtTokenEncryption; + + if (options.Value.JwtConfig == null) + throw new ArgumentNullException(nameof(options.Value.JwtConfig)); + _jwtConfig = options.Value.JwtConfig; _siteDataProvider = siteDataProvider; _userDataProvider = userDataProvider; @@ -76,46 +84,46 @@ public class InitializationService : ServiceBase, IInitia var userId = "fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60".ToGuid(); var siteId = "404c8232-9048-4519-bfba-6e78dc7005ca".ToGuid(); - #region Upload dkim for service email - var (_, dkimUpdloadResult) = _dkimBucketDataProvider.Upload(new BucketFile( - siteId, - requestData.Host, - Convert.FromBase64String(requestData.DkimBase64), - "application/x-pem-file" - )); + //#region Upload dkim for service email + //var (_, dkimUpdloadResult) = _dkimBucketDataProvider.Upload(new BucketFile( + // siteId, + // requestData.Host, + // Convert.FromBase64String(requestData.DkimBase64), + // "application/x-pem-file" + //)); - if (!dkimUpdloadResult.IsSuccess) - return IDomainResult.Failed(); - #endregion + //if (!dkimUpdloadResult.IsSuccess) + // return IDomainResult.Failed(); + //#endregion - #region Upload basic service email templates - var (templateId, _) = _serviceEmlTemplateBucketDataProvider.Upload(new BucketFile( - siteId, - "template.html", - Convert.FromBase64String(requestData.ServiceEmlTemplateBase64), - "text/html" - )); + //#region Upload basic service email templates + //var (templateId, _) = _serviceEmlTemplateBucketDataProvider.Upload(new BucketFile( + // siteId, + // "template.html", + // Convert.FromBase64String(requestData.ServiceEmlTemplateBase64), + // "text/html" + //)); - if (!dkimUpdloadResult.IsSuccess || templateId == null) - return IDomainResult.Failed(); - #endregion + //if (!dkimUpdloadResult.IsSuccess || templateId == null) + // return IDomainResult.Failed(); + //#endregion - var (_, siteInsetResult) = _siteDataProvider.Insert(new Site(requestData.SiteName, new List { requestData.Host }) { - Id = siteId, - PassworRecoverySettings = new PassworRecoverySettings { - Name = "Password recovery support", - Email = new Contact(ContactTypes.Email, $"support@{requestData.Host}"), - TemplateId = templateId.Value - } - }); + //var (_, siteInsetResult) = _siteDataProvider.Insert(new Site(requestData.SiteName, new List { requestData.Host }) { + // Id = siteId, + // PassworRecoverySettings = new PassworRecoverySettings { + // Name = "Password recovery support", + // Email = new Contact(ContactTypes.Email, $"support@{requestData.Host}"), + // TemplateId = templateId.Value + // } + //}); - var user = new User(new List { new SiteRole(siteId, Roles.Admin) }, requestData.Username, requestData.Email, requestData.Password) { - Id = userId - }; + //var user = new User(new List { new SiteRole(siteId, Roles.Admin) }, requestData.Username, requestData.Email, requestData.Password) { + // Id = userId + //}; - user.Contacts.ForEach(x => x.Confirmed = true); + //user.Contacts.ForEach(x => x.Confirmed = true); - var (_, userInsertResult) = _userDataProvider.Insert(user); + //var (_, userInsertResult) = _userDataProvider.Insert(user); return IDomainResult.Success(); } diff --git a/src/WeatherForecast/Services/ShopItemService.cs b/src/WeatherForecast/Services/ShopItemService.cs index ff47e73..59d3d6b 100644 --- a/src/WeatherForecast/Services/ShopItemService.cs +++ b/src/WeatherForecast/Services/ShopItemService.cs @@ -4,16 +4,17 @@ using DataProviders.Collections; using WeatherForecast.Models.Requests; using WeatherForecast.Services.Abstractions; -using DomainObjects.Documents; using WeatherForecast.Models.Shop.Responses; using DomainObjects.Enumerations; +using DomainObjects.Documents.Posts; -namespace WeatherForecast.Services { +namespace WeatherForecast.Services +{ - /// - /// - /// - public interface IShopItemService { + /// + /// + /// + public interface IShopItemService { /// /// diff --git a/src/WeatherForecast/Services/TemplateService.cs b/src/WeatherForecast/Services/TemplateService.cs index 34622b2..b07c7e1 100644 --- a/src/WeatherForecast/Services/TemplateService.cs +++ b/src/WeatherForecast/Services/TemplateService.cs @@ -2,53 +2,74 @@ using DataProviders.Buckets; using DomainResults.Common; -namespace WeatherForecast.Services { +namespace WeatherForecast.Services; + +/// +/// +/// +public interface ITemplateService { /// /// /// - public interface ITemplateService { + /// + /// + (Guid?, IDomainResult) Post(BucketFile file); - /// - /// - /// - /// - /// - (Guid?, IDomainResult) Post(BucketFile file); + /// + /// + /// + /// + /// + /// + IDomainResult Delete(Guid siteId, Guid fileId); +} + +/// +/// +/// +public class TemplateService : ServiceBase, ITemplateService { + + + private readonly ITemplateBucketDataProvider _templateBucketDataProvider; + + /// + /// + /// + /// + /// + public TemplateService( + ILogger logger, + ITemplateBucketDataProvider templateBucketDataProvider + ) : base(logger) { + _templateBucketDataProvider = templateBucketDataProvider; } /// /// /// - public class TemplateService : ServiceBase, ITemplateService { + /// + /// + public (Guid?, IDomainResult) Post(BucketFile file) { + var (fileId, uploadFileResult) = _templateBucketDataProvider.Upload(file); + if (!uploadFileResult.IsSuccess || fileId == null) + return IDomainResult.Failed(); - private readonly ITemplateBucketDataProvider _templateBucketDataProvider; + return IDomainResult.Success(fileId); + } - /// - /// - /// - /// - /// - public TemplateService( - ILogger logger, - ITemplateBucketDataProvider templateBucketDataProvider - ) : base(logger) { - _templateBucketDataProvider = templateBucketDataProvider; - } + /// + /// + /// + /// + /// + /// + public IDomainResult Delete(Guid siteId, Guid fileId) { + var result = _templateBucketDataProvider.DeleteOne(siteId, fileId); + if (!result.IsSuccess) + return IDomainResult.Failed(); - /// - /// - /// - /// - /// - public (Guid?, IDomainResult) Post(BucketFile file) { - - var (fileId, uploadFileResult) = _templateBucketDataProvider.Upload(file); - if (!uploadFileResult.IsSuccess || fileId == null) - return IDomainResult.Failed(); - - return IDomainResult.Success(fileId); - } + return IDomainResult.Success(); } } diff --git a/src/WeatherForecast/appsettings.json b/src/WeatherForecast/appsettings.json index a12d0e4..9005ad9 100644 --- a/src/WeatherForecast/appsettings.json +++ b/src/WeatherForecast/appsettings.json @@ -25,6 +25,11 @@ "Key": "KBsLNL2/pju3uX6Wtjkf2zUS1TbJ0YiD84zusIyPVUM=" }, + "MailPasswordEncryption": { + "IV": "1DCr+7GMT1skaTTTNSO3lA==", + "Key": "36m7ZAWCZdQC05xOyUvqBmz3sgzGShSCDTBmwvNEWNM=" + }, + "Database": { "ConnectionString": "mongodb://root:example@mongo:27017" diff --git a/src/docker-compose.dcproj b/src/docker-compose.dcproj index be68195..7f80434 100644 --- a/src/docker-compose.dcproj +++ b/src/docker-compose.dcproj @@ -5,7 +5,7 @@ Linux 7fc6f0ba-2dcb-4b53-a3b3-61ceef42b9d0 LaunchBrowser - {Scheme}://localhost:{ServicePort} + {Scheme}://localhost:{ServicePort}/swagger reverseproxy diff --git a/src/docker-compose/mongo/data/db/WiredTiger.turtle b/src/docker-compose/mongo/data/db/WiredTiger.turtle index af66381..30117f5 100644 --- a/src/docker-compose/mongo/data/db/WiredTiger.turtle +++ b/src/docker-compose/mongo/data/db/WiredTiger.turtle @@ -3,4 +3,4 @@ WiredTiger 10.0.2: (December 21, 2021) WiredTiger version major=10,minor=0,patch=2 file:WiredTiger.wt -access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.98221=(addr="018081e4af72785d8181e4aac961f88281e479ff3f5b808080e301bfc0e2dfc0",order=98221,time=1674344889,size=69632,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=202,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=295056,run_write_gen=294763)),checkpoint_backup_info=,checkpoint_lsn=(38,77312) +access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.99539=(addr="018081e4760268df8181e4e314ef2b8281e4f0cb9f65808080e301ffc0e3010fc0",order=99539,time=1674424019,size=81920,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=3110,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=299042,run_write_gen=294763)),checkpoint_backup_info=,checkpoint_lsn=(38,1188480) diff --git a/src/docker-compose/mongo/data/db/WiredTiger.wt b/src/docker-compose/mongo/data/db/WiredTiger.wt index 36bcd8cdc7fc9ec57a93285f2e9173c68f5c4bbc..3d595e19ad869ccd91d5db83034778df648ef9e9 100644 GIT binary patch delta 4455 zcmeHKU2GiH6`niUwbxG4-A(K`ZqwM_q@kuZo_pucow+nFe?bXJ2uY|Q1Y&1qXGW}W zyx_Hn%L7@fB0nT1h!G5^st6*essPE`$+Acw*i;Z6{199fs;Y#<-fcu%1hfkMi%@%K z#!kj&Hmy|kExS8f&E7fZeD|L3obzqIT%Rw;#$yRR^<;!|Ujyu_^Dn?9IIisw|1e

L(-Sap)~(26bbkTVz(} z4?K6UdTK@PjY9Hxyyphytc6|7sTbgZcyq-KF#Y8>^QX(F%hPS;>7(Te*jJwJFX!`x zmvK8Br3h%Rds!+ zHzhzy6lh?PD!PCbjmqwwi503-O`(c=NA$EHn+Pg``!+-Ynr7Pj7T9;HD?6kcL%q~> z+clioWAs8IV~j*9(XL$P5u;au6bW5}#$F>g+@G`lInWakp`cg=a#w^3BDGI$0!`Mq z)UyymMV2Az+*SU{dI+ZmnNNBdDhtox$V5Oy(M<)qcZLYDVIs{3_n#rsAVkczAAo56 zc|chpLTKG2-OYeuo?l3ZUHUM;LRgZ=Ae0*82+H^BYepztQQ-s&vzx3!En<)^=Gw~I_|pFxv-T%cg<04 z^BPOc++N-XTC9cJjyq@W99!V|!L|L=SaM-JayRzPrEv4M?d|sAwLr6uZC|sj#xt&; zw{{+mEy%w!W=$2Zx86Ut--+u+#CdE|1+8Pe#Dc%sL`){K1XVNb!IeZ!B{(hGqj8X`$9V2( zHF&2*7)I;m4c9oImY0HhhDP{_`uubDefrnN)YE}!6kfMpC>|qg3hs3h zf7r;^QC-tT)p}u|Ifk*M>qu0>11+3Q-54WFybpZa%I4~vpZx7i{i>BS^wJ;YxCdwH zmFGA|Xx{D8i{Y}9{h-aIS8)rtB{2tScxRYFif54C#>c2BOYB$#?@%;FLXv6<;aKZ@ ztfiv~@M0AFy0{J814>6)K!GPXm}ie)s$@VyP08mMkpx|(#PIo*mUKZAp*h1ZJZHp+ zSKwD^@LS+V(avB-!aJAS74q*)vYxfEbYv|^u+ow)Wv3qL22muRf`W++O43^RmM>M- zn3cUZRch=2Up9gd{GH#^4SvBhKl3XKr;%h*EQame5KT;ouDCmIU7|>}3rqG`-#AlP zo!rw_p>K654T0Wln-F~8dH>iqKq;{SI6RSs`4|Y39h$2^LVB=>1X)*f>SK_Fxfo_D zFT#pjvdf#mI=8l2*&w@NGg!kIB$Qp11zaAvF*ZUFlE2B9V{uaxxGBe$ z2-eg;b1z7QX$E^+hJ{(!2NqiwwtA)?m+E$bC}0MQYfNg-ro?O(MZBlNODf=5+RU^F)(>v%uo*8^``AvG>5T|Mx` z!RqejM9x*w-;tQJ#q45EJr(`;e;m5t25?n0Q_vOA=4t2O#jS*p=uw|9@0ne#fGiWt zOfSbTiELLkp9_=Ca)6k=7R9PAQ>@R^qR5lYrles_V%&A#SCY;09L_23K3_MNW2-u9 z%!P~Aic@ZmuFeJbe`9#zU7ZX6ueor3W-grn+FXFb{J8*yt8*dzPg(d}c)a1?2P_I@ delta 2698 zcmeH}e{2(F7{~AJ)~*YZwZM?!CUuB{>9%{{yWS6y%9de;Qy~Al@14ISsIFCOAi5yzh<7 z^?T)0F9w3`(t3Yad%NTh2fI3}AmSX_;`qS9%$B;2kD+lm*eOc;swjM5AU+(=+&$N; zB<=5(R6mcsEyn|UHSWM2m^z`~A^p?J=vD2imR@Cihqd&$7L6wDUzRAg1hCZ{X?^he z%z5b&{lr!c|K0>1E$z{wv73c$TW^9-H0+8XF15&?e!emlOn~PKBOF4cd#xafJ?=^p zax4cW+9xr79*R82=@v~>6p~yl$Lkh}e4@w&1dh}{5=BB^z)wm@#QRETSC1(5xOv^F zGaT%Y+>l{uil#ZlJ43W9%#V)CiB56Y0AGMe zB;UB)Y(_i|N;x(PR#Y4X(OBkm&pvM~mN_vc{2VnlJ*(+&wWNK@TD~ii<;082xEUw9 zs1R4NmLQ7V_pYl?*FI^I2VX9g2P4bWq5594Zm1hJ-?FN;&DAEkw%Mk%c){c9sTNph zSg-o9RpG5DFswZRZ0#4qR?DEHFM9EcdMCKX46dm+Mq7cpr~9m_jLpfAkrNjBd6p7= zvT$gb72;}1IA?NOXa!*9E8`~tBk!Nsp-gQ8%Vkf(q80pQEdv`_w1 z;QQlm$1{fx?!B)A59@H6%J9Hm4d9hVYU3=(81@=1jbX2uB^fh+wZuJNChLE>V4A%u zg7L=!DACI@1ApT}@IyW)X$Dp<#z!Ja#OUW?g>;LP`~e9f=@EEAJ`XRJ;T{5i0pvLy zHH(27sf>?;`eN3_lXNcqe^;Z1DjyNv!?bD8KTnSiYTde{ARfz1wvSxvk7YJe)h+Yj z|HAszSJUu+zl9*mXW(xlh~I|lw&=BaUmk8Km9-b_swc7A29C==Y<^bV{oa_lXb`u@ z=p}2GQ4*4wXulzA~onmj0<)p=QA13ZiTB(p$P za0AY1$bXd;CAJ7GmDyH%s?rX&nNxS304o-O$!rAvwcw1&!=OC1%tc@*2Qf73A~|s; z0y3{I6oc|2K%7w*6E06pq~1)ih~`pWCwQwAe5Q_&n?MTI1J%UPx#+ue6^cH{^9&<$ zjBe4m;3Vs#vr8B2V+4wFiE`q{vL$rhA_Ng)>Gw#H4)J2$txLTxr`59CBNikm`O3^cALl zZvq>vsSVpfkx9Y(#VOp;9~5N&;&y(2dKUNc!^KI*Ca`*N0ZQCs2M_&E360;K{wtL9 TPgBzWCrZfql#u@_CAHuW=)y;7 diff --git a/src/docker-compose/mongo/data/db/_mdb_catalog.wt b/src/docker-compose/mongo/data/db/_mdb_catalog.wt index b67b58026a16495aeba567b6ec55f075ea9814de..5e6be18d1b1b5e8d9289fa00bc1537f9d6c0c9a1 100644 GIT binary patch delta 541 zcmZozz|^pSX#+=qQ)WF20|;=+FfhmgX?9iyMg{=}hAsL5K8!#h5 zRWNteR9BlG9fRp_^Xs)Ncv8*(onS{s+fJ}Eq?K+|D-+}~)YUcPD;75~GO{!>F*Gu; zFfcMVGg%qVx1Pi96uYDqgHf0jgKI;U7L#wA+fQ-7b_NLs)=3Rv2VZ6Rb}+2A-q-em$}HU>x#JrMgD2o53!3x@WV#wR`j{3RO& z@9=LHC=d`(TIW7L451V@u5uX2>y9Mj;;GMU{? zgV)%?FxAA+!XPy@G0l9kUYo>ZAGf2EH@a%E8R?jqn3+$0=&B}ZVXKjtl2T-=WMF7v z3esR|m}Ze?mS`|ps!d1GLZ>7%H`UhA%-qDpz{14La&oNO3>`}ob2B5IqSCzh@}kU= m)cEw&JX<4>pn<05EXLNxCxJh{Eof_e;^fG_WwWCDeGUNV5UR%j delta 265 zcmZozz|^pSX#+=qliF`)1`x0iVPFsg((J4Zj0^${4EZ864=@7xtc?u^zLWr2%(*EH zuYm$gdBqH(3=E-cKsFHkP~Yq&5X`nwD4SD99cq>dP@D;ffl9z;sW|<52R4hrf}y>o z@yUT1U-UN$-r?UYP#_>6{`aG<14#DKSE+{c2Oh~EXlQ)QeCx$#TZdDul4iCVi76>X zwn_$u7N!=4sV1qZX^BQada^>Bj)0j?NoH=Ut)ZE@fvE|QoE+meSHoDRs5CFWyeP9I oH9kEx&(_Gyz{1i(Q*#z$YvYr5zZ9ReH9m22WZ$w`(fuh008kW5&lh; zN<90$^+DW!C3u4?%Abnzz#t71=0DE_SAO~7(vO$r0FiGghp2i((+rba&9>KQ>AG)N zU1oMVddp+F*=jeKvfK@*dlqr!X{xSymSxb*x~FU1x@j@J?&_{lH(lLo@OFnAT1UAj z;)3~1FlB?9Ea!pXFZe}sQ!~A`(J<;A&1_S1y;FC)E~__rtL=L&O|!ge#FyoWWW5rx zeqIm6s+UBoeZHwIdr2&KEKY8wTsc4$Sek}`$3&3CN7BBRx1S^-4iEqV5C8!X009sH z0T2KI5C8!XSWp6K75|&0pN#)0m@nf0wQGk?6!E{(R^Azowr^N{Z9z?dS|9)dAOHd& z00JNY0w4eaAOHd&00RG#fI;>4(U)TWBK|-1)A55c{@=R!*zoH3#&|fS0uB%W0T2KI z5C8!X009sH0T2KI5CDO@nm~uNe~7-5`2XFnzBxaM|F@nPUmb598Pj<<`gM<^Zzbc> z*e`V+d)a0xx53*r+9)6Rwko&n+bgOYCSKo}Y|3}4irlxmEX?`bZTk%KJf7!H%7oxq z?5qc|u;V1=DhsKjVxzs2Q*wcAzIaTf-S^X;?OHdy;t_oh-pT4B$=JZq~)gS zF>jqaBny1XAE=Bck=IWXX`?#3c3kH52dNXp-DHRWAucCTI|OHHhNe zR{g+}USQd#t*N*6O3BK0MaH?hKInEM7CYoPds?`_!U4Bc@}#M$K`%}+P8t#JD9JXR z(wxE(r60)#@)rvoA{99i+o0j6GV1uBL?Ro+WMX3CR9HfwdC(0uoXL!kQ?n%Y_Zvqhz3=`q_oTr@d zlMB4{AoL0Et{W-O=5C~(usfP60-kZ-N!)Xs1m)0`ET^eM2HG_?5bKUe`kdyMJMj9P z=3{wwN-b%E74w(uq+VzO*o4j_Z)aSl!$_kjm#19vYB`i6Sk~jBT&&uPT9U6^IZ!X~ z%uRA`pE)Zpixpdo*g2ZZx=KdM^NKpR?wmm`)s>Hfv*X`p3&yUUCcXTVL62w8O_$Va zgADWi%s#B?ZTfK2G7R0dsAR~JZkbKj*PCr^_Tfge>uDZ+535P$^UQ7#Kzfep&6a5z zjfT}xi%H+sJBAwbi?rk&dZuLLpvmJTC)XsQKf5EOq^c`6;#qfpdgw_84vlQ&_%>V!7{nz%%_+RG#f2`5fU6lVn zB)#us{{Q3D|IYmXjrxYAb?Wl>3Fx&0-NXR`AOHd&00JNY0w4eaAOHd&00Ij}Ai72V z|MxdfA1&g4ng4%jw7u`OjcmbGhdLkt0w4eaAOHd&00JNY0w4eaAOHflNuWXXAEYnE z{6+r%AJ42ElkvaI|EI10_yK_1ph>vnUy+uC-iqmiOP* zxM9p?{u{e8|BcQ2JR+CF;v+Iw%0wvVmt{j|#bKZ4BvMf~pzoj?EtKmY_l L00cnb&J*|x1SmPZ literal 0 HcmV?d00001 diff --git a/src/docker-compose/mongo/data/db/collection-3--6471522924120802764.wt b/src/docker-compose/mongo/data/db/collection-3--6471522924120802764.wt new file mode 100644 index 0000000000000000000000000000000000000000..f5f29ca79d6fd1822809643c3ed104fd61877e99 GIT binary patch literal 20480 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 literal 0 HcmV?d00001 diff --git a/src/docker-compose/mongo/data/db/collection-4--4715807334585891142.wt b/src/docker-compose/mongo/data/db/collection-4--4715807334585891142.wt index e298ba455d59de3f5c4499802d192d08cc6f82bd..f2d83d42de6b1448fd8171c64ffc71de5fe41ed2 100644 GIT binary patch literal 36864 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