(feature): dkim cert upload and managing through gridfs data provider

This commit is contained in:
Maksym Sadovnychyy 2023-01-22 22:45:17 +01:00
parent 44a4b7a5e9
commit cac3d2502e
68 changed files with 774 additions and 497 deletions

47
db/DML/sites.json Normal file
View File

@ -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}}"
]
}
}
]

View File

@ -1,152 +0,0 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Simple Transactional Email</title>
<style>
@media only screen and (max-width: 620px) {
table.body h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table.body p,
table.body ul,
table.body ol,
table.body td,
table.body span,
table.body a {
font-size: 16px !important;
}
table.body .wrapper,
table.body .article {
padding: 10px !important;
}
table.body .content {
padding: 0 !important;
}
table.body .container {
padding: 0 !important;
width: 100% !important;
}
table.body .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table.body .btn table {
width: 100% !important;
}
table.body .btn a {
width: 100% !important;
}
table.body .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;" width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;" width="580" valign="top">
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<table role="presentation" class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;" width="100%">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
{{content}}
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td class="content-block" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" valign="top" align="center">
<span class="apple-link" style="color: #999999; font-size: 12px; text-align: center;">{{address}}</span>
<br> {{usubscribeText}} <a href="https://maks-it.com" style="text-decoration: underline; color: #999999; font-size: 12px; text-align: center;">{{unsubscribe}}</a>.
</td>
</tr>
<tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" valign="top" align="center">
Powered by <a href="https://maks-it.com" style="color: #999999; font-size: 12px; text-align: center; text-decoration: none;">MAKS-IT.com</a>.
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,145 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{subject}}</title>
<style>
@media only screen and (max-width: 620px) {
table.body h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table.body p,
table.body ul,
table.body ol,
table.body td,
table.body span,
table.body a {
font-size: 16px !important;
}
table.body .wrapper,
table.body .article {
padding: 10px !important;
}
table.body .content {
padding: 0 !important;
}
table.body .container {
padding: 0 !important;
width: 100% !important;
}
table.body .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table.body .btn table {
width: 100% !important;
}
table.body .btn a {
width: 100% !important;
}
table.body .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;" width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;" width="580" valign="top">
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<table role="presentation" class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;" width="100%">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
{{paragraphs}}
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" valign="top" align="center">
Powered by <a href="{{poweredBy.target}}" style="color: #999999; font-size: 12px; text-align: center; text-decoration: none;">{{poweredBy.anchorText}}</a>.
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body>
</html>

View File

@ -1,5 +1,4 @@

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using DomainResults.Common;
@ -27,8 +26,20 @@ namespace DataProviders.Buckets {
/// <param name="fileId"></param>
/// <returns></returns>
(BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId);
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="userId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
IDomainResult DeleteOne(Guid siteId, Guid fileId);
}
/// <summary>
///
/// </summary>
public class DkimBucketDataProvider : BucketDataProviderBase, IDkimBucketDataProvider {
/// <summary>
@ -60,5 +71,15 @@ namespace DataProviders.Buckets {
return (list.First(), result);
}
public IDomainResult DeleteOne(Guid siteId, Guid fileId) =>
DeleteOneAsync(siteId, fileId).Result;
public Task<IDomainResult> DeleteOneAsync(Guid siteId, Guid fileId) =>
DeleteAsync(Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
));
}
}

View File

@ -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 {
/// <param name="fileId"></param>
/// <returns></returns>
(BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId);
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
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
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
public (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId) {
var filter = Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
@ -54,5 +68,25 @@ namespace DataProviders.Buckets {
return (list.First(), result);
}
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
public IDomainResult DeleteOne(Guid siteId, Guid fileId) =>
DeleteOneAsync(siteId, fileId).Result;
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
public Task<IDomainResult> DeleteOneAsync(Guid siteId, Guid fileId) =>
DeleteAsync(Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
));
}
}

View File

@ -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
{

View File

@ -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<CategoryDocument>?, IDomainResult) GetMany(Guid siteId, List<Guid> categoryIds);
(CategoryDocument?, IDomainResult) GetBySlug(Guid siteId, string slug);
(List<CategoryDocument>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs);
(List<CategoryDocument>?, IDomainResult) GetAll(Guid siteId);
(Guid?, IDomainResult) Update(CategoryDocument obj);
(Category?, IDomainResult) Get(Guid siteId, Guid categoryId);
(List<Category>?, IDomainResult) GetMany(Guid siteId, List<Guid> categoryIds);
(Category?, IDomainResult) GetBySlug(Guid siteId, string slug);
(List<Category>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs);
(List<Category>?, IDomainResult) GetAll(Guid siteId);
(Guid?, IDomainResult) Update(Category obj);
IDomainResult Delete(Guid id);
IDomainResult DeleteAll(Guid siteId);
}
public class CategoryDataProvider : CollectionDataProviderBase<CategoryDocument>, ICategoryDataProvider {
public class CategoryDataProvider : CollectionDataProviderBase<Category>, ICategoryDataProvider {
private const string _databaseName = "reactredux";
private const string _collectionName = "categories";
public CategoryDataProvider(
ILogger<CollectionDataProviderBase<CategoryDocument>> logger,
ILogger<CollectionDataProviderBase<Category>> 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<CategoryL10n> {
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<CategoryDocument>?, IDomainResult) GetMany(Guid siteId, List<Guid> categoryIds) =>
public (List<Category>?, IDomainResult) GetMany(Guid siteId, List<Guid> 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<string> { slug });
if (!result.IsSuccess || list == null)
@ -75,13 +75,13 @@ namespace DataProviders.Collections
return (list.First(), result);
}
public (List<CategoryDocument>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs) =>
public (List<Category>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs) =>
GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)), x => x);
public (List<CategoryDocument>?, IDomainResult) GetAll(Guid siteId) =>
public (List<Category>?, 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) =>

View File

@ -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
{

View File

@ -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<CategoryDocument>(cm => {
if (!BsonClassMap.IsClassMapRegistered(typeof(Category))) {
BsonClassMap.RegisterClassMap<Category>(cm => {
cm.AutoMap();
});
}

View File

@ -5,8 +5,13 @@ namespace DomainObjects;
public class Contact : DomainObjectBase<Contact> {
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) {

View File

@ -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; }
}

View File

@ -1,34 +0,0 @@
using DomainObjects.Abstractions;
namespace DomainObjects.Documents;
public class BlogDocument : PostItemBase<BlogDocument> {
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;
}
}
}

View File

@ -0,0 +1,22 @@
using DomainObjects.Abstractions;
using DomainObjects.Documents.Categories.L10n;
namespace DomainObjects.Documents.Categories
{
public class Category : DomainObjectDocumentBase<Category>
{
public Guid SiteId { get; set; }
public List<CategoryL10n> 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;
}
}
}
}

View File

@ -0,0 +1,24 @@
using DomainObjects.Abstractions;
using DomainObjects.Enumerations;
namespace DomainObjects.Documents.Categories.L10n;
public class CategoryL10n : DomainObjectBase<CategoryL10n>
{
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;
}
}
}

View File

@ -1,18 +0,0 @@
using DomainObjects.Abstractions;
using DomainObjects.L10n;
namespace DomainObjects.Documents {
public class CategoryDocument : DomainObjectDocumentBase<CategoryDocument> {
public Guid SiteId { get; set; }
public List<CategoryL10n> 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;
}
}
}
}

View File

@ -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<MailboxConnectionSetti
public string UserName { get; set; }
public string Password { get; private set; }
public void SetPassword (IAesKey aesKey, string password) =>
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);

View File

@ -0,0 +1,37 @@
using DomainObjects.Abstractions;
namespace DomainObjects.Documents.Posts;
public class BlogDocument : PostItemBase<BlogDocument>
{
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;
}
}
}

View File

@ -0,0 +1,39 @@
using DomainObjects.Abstractions;
namespace DomainObjects.Documents.Posts;
public class ShopDocument : PostItemBase<ShopDocument>
{
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;
}
}
}

View File

@ -1,36 +0,0 @@
using DomainObjects.Abstractions;
namespace DomainObjects.Documents;
public class ShopDocument : PostItemBase<ShopDocument> {
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;
}
}
}

View File

@ -4,6 +4,8 @@ namespace DomainObjects.Documents.Sites;
public class PassworRecoverySettings : DomainObjectBase<PassworRecoverySettings> {
public MailboxConnectionSettings SmtpSettings { get; set; }
/// <summary>
/// DomainKeys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in email (email spoofing),<br />
/// a technique often used in phishing and email spam.
@ -12,14 +14,16 @@ public class PassworRecoverySettings : DomainObjectBase<PassworRecoverySettings>
public Guid TemplateId { get; set; }
public string Name { get; set; }
public Contact Email { get; set; }
public string Subject { get; set; }
public List<string> Paragraphs { get; set; }
public override int GetHashCode() {
throw new NotImplementedException();
}

View File

@ -1,20 +1,19 @@
using DomainObjects.Abstractions;
using DomainObjects.Enumerations;
namespace DomainObjects.Documents.Sites {
public class Site : DomainObjectDocumentBase<Site> {
public Locales Locale { get; set; }
public string Name { get; set; }
public List<string> 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; }

View File

@ -1,21 +0,0 @@
using DomainObjects.Abstractions;
using DomainObjects.Enumerations;
namespace DomainObjects.L10n;
public class CategoryL10n : DomainObjectBase<CategoryL10n> {
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;
}
}
}

View File

@ -50,6 +50,11 @@ namespace WeatherForecast {
/// </summary>
public AesKey? JwtTokenEncryption { get; set; }
/// <summary>
///
/// </summary>
public AesKey? MailPasswordEncryption { get; set; }
/// <summary>
///
/// </summary>

View File

@ -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;

View File

@ -34,7 +34,7 @@ public class DkimController : ControllerBase {
}
/// <summary>
/// Allows to upload private dkim certificate
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="file"></param>
@ -52,4 +52,17 @@ public class DkimController : ControllerBase {
return result.ToActionResult();
}
/// <summary>
/// Delete private dkim cert
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{fileId}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid fileId) {
var result = _dkimService.Delete(siteId, fileId);
return result.ToActionResult();
}
}

View File

@ -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;

View File

@ -53,4 +53,18 @@ public class TemplateController : ControllerBase {
return result.ToActionResult();
}
/// <summary>
/// Delete template
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{fileId}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid fileId) {
var result = _templateService.Delete(siteId, fileId);
return result.ToActionResult();
}
}

View File

@ -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());
}
/// <summary>
///
/// </summary>
/// <param name="requestData"></param>
/// <returns></returns>
[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();
}
}

View File

@ -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 {
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class PostItemResponseModelBase<T> : ResponseModelBase {
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class PostItemResponseModelBase<T> : ResponseModelBase {
/// <summary>
///
@ -122,7 +122,7 @@ namespace WeatherForecast.Models.Abstractions.PostItem.Responses {
/// </summary>
/// <param name="postItem"></param>
/// <param name="categories"></param>
public PostItemResponseModelBase(PostItemBase<T> postItem, List<CategoryDocument> categories) : this(postItem) {
public PostItemResponseModelBase(PostItemBase<T> postItem, List<DomainObjects.Documents.Categories.Category> 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 {
/// <param name="postItem"></param>
/// <param name="categories"></param>
/// <param name="locale"></param>
public PostItemResponseModelBase(PostItemBase<T> postItem, List<CategoryDocument> categories, Locales locale) : this(postItem) {
public PostItemResponseModelBase(PostItemBase<T> postItem, List<DomainObjects.Documents.Categories.Category> categories, Locales locale) : this(postItem) {
var postItemL10n = postItem.L10n.Single(x => x.Locale == locale);

View File

@ -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
{
/// <summary>
///
/// </summary>
public class BlogItemRequestModel : PostItemRequestModelBase<BlogDocument> {
/// <summary>
///
/// </summary>
public class BlogItemRequestModel : PostItemRequestModelBase<BlogDocument> {
/// <summary>
///

View File

@ -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
{
/// <summary>
///
/// </summary>
public class BlogItemResponseModel : PostItemResponseModelBase<BlogDocument> {
/// <summary>
///
/// </summary>
public class BlogItemResponseModel : PostItemResponseModelBase<BlogDocument> {
/// <summary>
///
@ -25,7 +26,7 @@ namespace WeatherForecast.Models.Blog.Responses {
/// </summary>
/// <param name="blogItem"></param>
/// <param name="categories"></param>
public BlogItemResponseModel(BlogDocument blogItem, List<CategoryDocument> categories) : base(blogItem, categories) {
public BlogItemResponseModel(BlogDocument blogItem, List<DomainObjects.Documents.Categories.Category> categories) : base(blogItem, categories) {
ReadTime = blogItem.ReadTime;
Likes = blogItem.Likes;
}
@ -36,7 +37,7 @@ namespace WeatherForecast.Models.Blog.Responses {
/// <param name="blogItem"></param>
/// <param name="categories"></param>
/// <param name="locale"></param>
public BlogItemResponseModel(BlogDocument blogItem, List<CategoryDocument> categories, Locales locale) : base(blogItem, categories, locale) {
public BlogItemResponseModel(BlogDocument blogItem, List<DomainObjects.Documents.Categories.Category> categories, Locales locale) : base(blogItem, categories, locale) {
ReadTime = blogItem.ReadTime;
Likes = blogItem.Likes;
}

View File

@ -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
{
/// <summary>
///
/// </summary>
public class CategoryItemRequestModel : RequestModelBase<CategoryDocument> {
/// <summary>
///
/// </summary>
public class CategoryItemRequestModel : RequestModelBase<DomainObjects.Documents.Categories.Category> {
/// <summary>
///
@ -20,7 +21,7 @@ namespace WeatherForecast.Models.Category.Requests {
///
/// </summary>
/// <returns></returns>
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()

View File

@ -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
{
/// <summary>
///
/// </summary>
public class CategoryL10nModel : RequestModelBase<CategoryL10n> {
/// <summary>
///
/// </summary>
public class CategoryL10nModel : RequestModelBase<CategoryL10n> {
/// <summary>
///

View File

@ -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
{
/// <summary>
///
/// </summary>
public class CategoryItemResponseModel : ResponseModelBase {
/// <summary>
///
/// </summary>
public class CategoryItemResponseModel : ResponseModelBase {
/// <summary>
///
@ -39,7 +39,7 @@ namespace WeatherForecast.Models.Category.Responses {
///
/// </summary>
/// <param name="category"></param>
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 {
/// </summary>
/// <param name="category"></param>
/// <param name="locale"></param>
public CategoryItemResponseModel(CategoryDocument category, Locales locale) {
public CategoryItemResponseModel(DomainObjects.Documents.Categories.Category category, Locales locale) {
Id = category.Id;
SiteId = category.SiteId;

View File

@ -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
{
/// <summary>
///
/// </summary>
public class CategoryL10nModel : ResponseModelBase {
/// <summary>
///
/// </summary>
public class CategoryL10nModel : ResponseModelBase {
/// <summary>
///

View File

@ -41,15 +41,8 @@ namespace WeatherForecast.Models.Initialization.Requests {
/// </summary>
public string RePassword { get; set; }
/// <summary>
///
/// </summary>
public string DkimBase64 { get; set; }
/// <summary>
///
/// </summary>
public string ServiceEmlTemplateBase64 { get; set; }
/// <summary>
///
@ -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}");
}
}
}

View File

@ -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
{
/// <summary>
///
/// </summary>
public class ShopItemRequestModel : PostItemRequestModelBase<ShopDocument> {
/// <summary>
///
/// </summary>
public class ShopItemRequestModel : PostItemRequestModelBase<ShopDocument> {
/// <summary>
///

View File

@ -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
{
/// <summary>
///
/// </summary>
public class ShopCartItemResponseModel : ResponseModelBase {
/// <summary>
///
/// </summary>
public class ShopCartItemResponseModel : ResponseModelBase {
/// <summary>
///

View File

@ -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
{
/// <summary>
///
/// </summary>
public class ShopItemResponseModel : PostItemResponseModelBase<ShopDocument> {
/// <summary>
///
/// </summary>
public class ShopItemResponseModel : PostItemResponseModelBase<ShopDocument> {
/// <summary>
///
@ -39,7 +41,7 @@ namespace WeatherForecast.Models.Shop.Responses {
/// </summary>
/// <param name="shopCatalogItem"></param>
/// <param name="categories"></param>
public ShopItemResponseModel(ShopDocument shopCatalogItem, List<CategoryDocument> categories) : base(shopCatalogItem, categories) {
public ShopItemResponseModel(ShopDocument shopCatalogItem, List<DomainObjects.Documents.Categories.Category> categories) : base(shopCatalogItem, categories) {
Sku = shopCatalogItem.Sku;
Rating = shopCatalogItem.Rating;
Price = shopCatalogItem.Price;
@ -53,7 +55,7 @@ namespace WeatherForecast.Models.Shop.Responses {
/// <param name="shopCatalogItem"></param>
/// <param name="categories"></param>
/// <param name="locale"></param>
public ShopItemResponseModel(ShopDocument shopCatalogItem, List<CategoryDocument> categories, Locales locale) : base(shopCatalogItem, categories, locale) {
public ShopItemResponseModel(ShopDocument shopCatalogItem, List<DomainObjects.Documents.Categories.Category> categories, Locales locale) : base(shopCatalogItem, categories, locale) {
Sku = shopCatalogItem.Sku;
Rating = shopCatalogItem.Rating;
Price = shopCatalogItem.Price;

View File

@ -0,0 +1,45 @@
using Core.Abstractions.Models;
using Core.Enumerations;
using Extensions;
using System.ComponentModel.DataAnnotations;
namespace WeatherForecast.Models.Utils.Requests {
/// <summary>
///
/// </summary>
public class EncryptStringAesRequestModel : RequestModelBase {
/// <summary>
/// String to encrypt
/// </summary>
public string InputString { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string IV { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
public string Key { get; set; } = string.Empty;
/// <summary>
///
/// </summary>
/// <param name="validationContext"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public override IEnumerable<ValidationResult> 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}");
}
}
}

View File

@ -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;

View File

@ -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
{
/// <summary>
///

View File

@ -55,7 +55,9 @@ namespace WeatherForecast.Services {
/// </summary>
public class AccountService : ServiceBase<AccountService>, 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 {
/// <returns></returns>
public (string?, IDomainResult) Authenticate(AuthenticationRequestModel requestData) {
if (_aesKey?.IV == null || _aesKey?.Key == null)
return IDomainResult.Failed<string?>("IV or Key are not set");
if (_jwtConfig?.Secret == null || _jwtConfig?.Expires == null)
return IDomainResult.Failed<string?>("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<string?>();
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());

View File

@ -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
{
/// <summary>
///
/// </summary>
public interface IBlogItemService {
/// <summary>
///
/// </summary>
public interface IBlogItemService {
/// <summary>
///

View File

@ -16,6 +16,14 @@ namespace WeatherForecast.Services {
/// <param name="file"></param>
/// <returns></returns>
(Guid?, IDomainResult) Post(BucketFile file);
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
IDomainResult Delete(Guid siteId, Guid fileId);
}
/// <summary>
@ -38,7 +46,7 @@ namespace WeatherForecast.Services {
}
/// <summary>
///
/// Upload dkim private cert for website
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
@ -50,5 +58,20 @@ namespace WeatherForecast.Services {
return IDomainResult.Success(fileId);
}
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public IDomainResult Delete(Guid siteId, Guid fileId) {
var result = _dkimBucketDataProvider.DeleteOne(siteId, fileId);
if (!result.IsSuccess)
return IDomainResult.Failed();
return IDomainResult.Success();
}
}
}

View File

@ -42,6 +42,7 @@ namespace WeatherForecast.Services {
/// <param name="logger"></param>
/// <param name="imageBucketDataProvider"></param>
/// <param name="imageProvider"></param>
/// <param name="contentDataProvider"></param>
public ImageService(
ILogger<ImageService> logger,
IImageBucketDataProvider imageBucketDataProvider,

View File

@ -57,7 +57,15 @@ public class InitializationService : ServiceBase<InitializationService>, 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<InitializationService>, 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<string> { 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<string> { 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<SiteRole> { new SiteRole(siteId, Roles.Admin) }, requestData.Username, requestData.Email, requestData.Password) {
Id = userId
};
//var user = new User(new List<SiteRole> { 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();
}

View File

@ -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
{
/// <summary>
///
/// </summary>
public interface IShopItemService {
/// <summary>
///
/// </summary>
public interface IShopItemService {
/// <summary>
///

View File

@ -2,53 +2,74 @@
using DataProviders.Buckets;
using DomainResults.Common;
namespace WeatherForecast.Services {
namespace WeatherForecast.Services;
/// <summary>
///
/// </summary>
public interface ITemplateService {
/// <summary>
///
/// </summary>
public interface ITemplateService {
/// <param name="file"></param>
/// <returns></returns>
(Guid?, IDomainResult) Post(BucketFile file);
/// <summary>
///
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
(Guid?, IDomainResult) Post(BucketFile file);
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
IDomainResult Delete(Guid siteId, Guid fileId);
}
/// <summary>
///
/// </summary>
public class TemplateService : ServiceBase<TemplateService>, ITemplateService {
private readonly ITemplateBucketDataProvider _templateBucketDataProvider;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="templateBucketDataProvider"></param>
public TemplateService(
ILogger<TemplateService> logger,
ITemplateBucketDataProvider templateBucketDataProvider
) : base(logger) {
_templateBucketDataProvider = templateBucketDataProvider;
}
/// <summary>
///
/// </summary>
public class TemplateService : ServiceBase<TemplateService>, ITemplateService {
/// <param name="file"></param>
/// <returns></returns>
public (Guid?, IDomainResult) Post(BucketFile file) {
var (fileId, uploadFileResult) = _templateBucketDataProvider.Upload(file);
if (!uploadFileResult.IsSuccess || fileId == null)
return IDomainResult.Failed<Guid?>();
private readonly ITemplateBucketDataProvider _templateBucketDataProvider;
return IDomainResult.Success(fileId);
}
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="templateBucketDataProvider"></param>
public TemplateService(
ILogger<TemplateService> logger,
ITemplateBucketDataProvider templateBucketDataProvider
) : base(logger) {
_templateBucketDataProvider = templateBucketDataProvider;
}
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
public IDomainResult Delete(Guid siteId, Guid fileId) {
var result = _templateBucketDataProvider.DeleteOne(siteId, fileId);
if (!result.IsSuccess)
return IDomainResult.Failed();
/// <summary>
///
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public (Guid?, IDomainResult) Post(BucketFile file) {
var (fileId, uploadFileResult) = _templateBucketDataProvider.Upload(file);
if (!uploadFileResult.IsSuccess || fileId == null)
return IDomainResult.Failed<Guid?>();
return IDomainResult.Success(fileId);
}
return IDomainResult.Success();
}
}

View File

@ -25,6 +25,11 @@
"Key": "KBsLNL2/pju3uX6Wtjkf2zUS1TbJ0YiD84zusIyPVUM="
},
"MailPasswordEncryption": {
"IV": "1DCr+7GMT1skaTTTNSO3lA==",
"Key": "36m7ZAWCZdQC05xOyUvqBmz3sgzGShSCDTBmwvNEWNM="
},
"Database": {
"ConnectionString": "mongodb://root:example@mongo:27017"

View File

@ -5,7 +5,7 @@
<DockerTargetOS>Linux</DockerTargetOS>
<ProjectGuid>7fc6f0ba-2dcb-4b53-a3b3-61ceef42b9d0</ProjectGuid>
<DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
<DockerServiceUrl>{Scheme}://localhost:{ServicePort}</DockerServiceUrl>
<DockerServiceUrl>{Scheme}://localhost:{ServicePort}/swagger</DockerServiceUrl>
<DockerServiceName>reverseproxy</DockerServiceName>
</PropertyGroup>
<ItemGroup>

View File

@ -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)