(refactor): dkim data provider file retrieval
This commit is contained in:
parent
9e59ca7f30
commit
44a4b7a5e9
10
postman/New Text Document.txt
Normal file
10
postman/New Text Document.txt
Normal file
@ -0,0 +1,10 @@
|
||||
smtpService.Connect("smtp.ionos.it", 465, true);
|
||||
smtpService.Authenticate("commercial@maks-it.com", "E23{R#<X&#Lyz");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Company Inc, 3 Abbey Road, San Francisco CA 94102
|
||||
Don't like these emails? Unsubscribe.
|
||||
Powered by MAKS-IT.com.
|
||||
152
postman/templates/email-inlined.html
Normal file
152
postman/templates/email-inlined.html
Normal file
@ -0,0 +1,152 @@
|
||||
<!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"> </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"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@ -6,12 +6,12 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace CryptoProvider {
|
||||
public interface IAesKey {
|
||||
public string? IV { get; set; }
|
||||
public string? Key { get; set; }
|
||||
public string IV { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
public class AesKey : IAesKey {
|
||||
public string? IV { get; set; }
|
||||
public string? Key { get; set; }
|
||||
public string IV { get; set; } = string.Empty;
|
||||
public string Key { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@ -1,283 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using DomainObjects.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DataProviders.Abstractions {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class CollectionDataProviderBase<T> : DataProviderBase<CollectionDataProviderBase<T>> where T : DomainObjectDocumentBase<T> {
|
||||
|
||||
private protected readonly IIdGenerator _idGenerator;
|
||||
private protected readonly ISessionService _sessionService;
|
||||
private protected readonly IMongoCollection<T> _collection;
|
||||
|
||||
/// <summary>
|
||||
/// Main constructor
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="idGenerator"></param>
|
||||
/// <param name="dataProviderUtils"></param>
|
||||
public CollectionDataProviderBase(
|
||||
ILogger<CollectionDataProviderBase<T>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService,
|
||||
string databaseName,
|
||||
string collectionName
|
||||
) : base(logger, client, databaseName) {
|
||||
_idGenerator = idGenerator;
|
||||
_sessionService = sessionService;
|
||||
|
||||
if (!_database.ListCollectionNames().ToList().Contains(collectionName))
|
||||
_database.CreateCollection(collectionName);
|
||||
|
||||
_collection = _database.GetCollection<T>(collectionName);
|
||||
}
|
||||
|
||||
#region Insert
|
||||
public (Guid?, IDomainResult) Insert(T obj) =>
|
||||
InsertAsync(obj).Result;
|
||||
|
||||
public (Guid?, IDomainResult) Insert(T obj, Guid sessionId) =>
|
||||
InsertAsync(obj, sessionId).Result;
|
||||
|
||||
public Task<(Guid?, IDomainResult)> InsertAsync(T obj) =>
|
||||
InsertAsync(obj, null);
|
||||
|
||||
public Task<(Guid?, IDomainResult)> InsertAsync(T obj, Guid sessionId) =>
|
||||
InsertAsync(obj, sessionId);
|
||||
|
||||
protected virtual async Task<(Guid?, IDomainResult)> InsertAsync(T obj, Guid? sessionId) {
|
||||
try {
|
||||
if (sessionId != null)
|
||||
await _collection.InsertOneAsync(_sessionService.GetSession(sessionId.Value), obj);
|
||||
else
|
||||
_collection.InsertOne(obj);
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region InsertMany
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList) =>
|
||||
InsertManyAsync(objList).Result;
|
||||
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList, Guid sessionId) =>
|
||||
InsertManyAsync(objList, sessionId).Result;
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList) =>
|
||||
InsertManyAsync(objList, null);
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, Guid sessionId) =>
|
||||
InsertManyAsync(objList, sessionId);
|
||||
|
||||
protected virtual async Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, Guid? sessionId) {
|
||||
try {
|
||||
if (sessionId != null)
|
||||
await _collection.InsertManyAsync(_sessionService.GetSession(sessionId.Value), objList);
|
||||
else
|
||||
_collection.InsertMany(objList);
|
||||
|
||||
return IDomainResult.Success(objList.Select(x => x.Id).ToList());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Count
|
||||
private protected (int?, IDomainResult) CountWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
CountWithPredicate(new List<Expression<Func<T, bool>>> { predicate });
|
||||
|
||||
private protected (int?, IDomainResult) CountWithPredicate(List<Expression<Func<T, bool>>> predicates) {
|
||||
try {
|
||||
var query = GetWithPredicate(predicates);
|
||||
|
||||
var result = query.Count();
|
||||
|
||||
return IDomainResult.Success(result);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<int?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> selector) =>
|
||||
GetWithPredicate(new List<Expression<Func<T, bool>>> { predicate }, selector, null, null);
|
||||
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(Expression<Func<T, bool>> predicates, Expression<Func<T, TResult>> selector, int? skip, int? limit) =>
|
||||
GetWithPredicate(new List<Expression<Func<T, bool>>> { predicates }, selector, skip, limit);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="predicates"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <param name="skip"></param>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(List<Expression<Func<T, bool>>> predicates, Expression<Func<T, TResult>> selector, int? skip, int? limit) {
|
||||
try {
|
||||
var query = GetWithPredicate(predicates).Select(selector);
|
||||
|
||||
if (skip != null)
|
||||
query = query.Skip(skip.Value);
|
||||
|
||||
if (limit != null)
|
||||
query = query.Take(limit.Value);
|
||||
|
||||
var result = query.ToList();
|
||||
|
||||
return result != null && result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.NotFound<List<TResult>?>();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<TResult>?>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicates"></param>
|
||||
/// <returns></returns>
|
||||
private protected IQueryable<T> GetWithPredicate(List<Expression<Func<T, bool>>> predicates) {
|
||||
var query = GetQuery();
|
||||
|
||||
foreach (var predicate in predicates)
|
||||
query = query.Where(predicate);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IQueryable<T> GetQuery() => _collection.AsQueryable();
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate) =>
|
||||
UpdateWithPredicateAsync(obj, predicate).Result;
|
||||
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, sessionId).Result;
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, null);
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, sessionId);
|
||||
|
||||
protected virtual async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
try {
|
||||
|
||||
|
||||
if (sessionId != null)
|
||||
await _collection.ReplaceOneAsync(_sessionService.GetSession(sessionId.Value), predicate, obj);
|
||||
else
|
||||
await _collection.ReplaceOneAsync(predicate, obj);
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteWithPredicateAsync(predicate).Result;
|
||||
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteWithPredicateAsync(predicate, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteWithPredicateAsync(predicate, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteWithPredicateAsync(predicate, sessionId);
|
||||
|
||||
protected virtual async Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
try {
|
||||
if (sessionId != null)
|
||||
await _collection.DeleteOneAsync<T>(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await _collection.DeleteOneAsync<T>(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DeleteMany
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteManyWithPredicateAsync(predicate).Result;
|
||||
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsync(predicate, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteManyWithPredicateAsync(predicate, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsync(predicate, sessionId);
|
||||
|
||||
protected virtual async Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
try {
|
||||
|
||||
if (sessionId != null)
|
||||
await _collection.DeleteManyAsync(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await _collection.DeleteManyAsync(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -6,18 +6,17 @@ using MongoDB.Driver.GridFS;
|
||||
|
||||
using DomainResults.Common;
|
||||
using Extensions;
|
||||
using DataProviders.Buckets;
|
||||
using DataProviders.Abstractions;
|
||||
|
||||
namespace DataProviders.Abstractions
|
||||
{
|
||||
namespace DataProviders.Buckets.Abstractions {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class BucketDataProviderBase : DataProviderBase<BucketDataProviderBase> {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class BucketDataProviderBase : DataProviderBase<BucketDataProviderBase> {
|
||||
|
||||
private readonly GridFSBucket _bucket;
|
||||
protected readonly GridFSBucket _bucket;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -27,8 +26,9 @@ namespace DataProviders.Abstractions
|
||||
public BucketDataProviderBase(
|
||||
ILogger<BucketDataProviderBase> logger,
|
||||
IMongoClient client,
|
||||
string databaseName,
|
||||
string bucketName
|
||||
) : base (logger, client, "reactredux") {
|
||||
) : base(logger, client, databaseName) {
|
||||
_bucket = new GridFSBucket(_database, new GridFSBucketOptions {
|
||||
BucketName = bucketName,
|
||||
ChunkSizeBytes = 1048576, // 1MB
|
||||
@ -55,7 +55,7 @@ namespace DataProviders.Abstractions
|
||||
public (List<Guid>?, IDomainResult) UploadMany(List<BucketFile> files) =>
|
||||
UploadManyAsync(files).Result;
|
||||
|
||||
public async Task<(List<Guid>?, IDomainResult)> UploadManyAsync(List<BucketFile> files) {
|
||||
protected virtual async Task<(List<Guid>?, IDomainResult)> UploadManyAsync(List<BucketFile> files) {
|
||||
var options = new GridFSUploadOptions {
|
||||
ChunkSizeBytes = 64512, // 63KB
|
||||
};
|
||||
@ -63,11 +63,8 @@ namespace DataProviders.Abstractions
|
||||
try {
|
||||
var result = new List<Guid>();
|
||||
foreach (var file in files) {
|
||||
|
||||
options.Metadata = new BsonDocument {
|
||||
{ "siteId", $"{file.SiteId}"},
|
||||
{ "userId", $"{file.UserId}"},
|
||||
{ "published", file.Published.ToString() },
|
||||
{ "fileName", file.Name },
|
||||
{ "contentType", file.ContentType }
|
||||
};
|
||||
@ -91,7 +88,8 @@ namespace DataProviders.Abstractions
|
||||
private protected (List<BucketFile>?, IDomainResult) Download(FilterDefinition<GridFSFileInfo> filter) =>
|
||||
DownloadAsync(filter).Result;
|
||||
|
||||
private protected async Task<(List<BucketFile>?, IDomainResult)> DownloadAsync(FilterDefinition<GridFSFileInfo> filter) {
|
||||
protected virtual async Task<(List<BucketFile>?, IDomainResult)> DownloadAsync(FilterDefinition<GridFSFileInfo> filter) {
|
||||
|
||||
try {
|
||||
var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
|
||||
|
||||
@ -107,19 +105,17 @@ namespace DataProviders.Abstractions
|
||||
return IDomainResult.NotFound<List<BucketFile>?>();
|
||||
|
||||
var id = fileInfo.Filename.ToGuid();
|
||||
var userId = fileInfo.Metadata["userId"].ToString()?.ToNullableGuid();
|
||||
var siteId = fileInfo.Metadata["siteId"].ToString()?.ToNullableGuid();
|
||||
var published = fileInfo.Metadata["published"].ToString()?.ToNullableDateTime();
|
||||
var fileName = fileInfo.Metadata["fileName"].ToString() ?? "";
|
||||
var bytes = await _bucket.DownloadAsBytesByNameAsync($"{fileInfo.Filename}", new GridFSDownloadByNameOptions {
|
||||
Revision = -1
|
||||
});
|
||||
var contentType = fileInfo.Metadata["contentType"].ToString() ?? "";
|
||||
|
||||
if (siteId == null || userId == null || bytes == null)
|
||||
if (siteId == null || bytes == null)
|
||||
return IDomainResult.Failed<List<BucketFile>?>();
|
||||
|
||||
result.Add(new BucketFile(id, siteId.Value, userId.Value, published, fileName, bytes, contentType));
|
||||
result.Add(new BucketFile(id, siteId.Value, fileName, bytes, contentType));
|
||||
}
|
||||
|
||||
return result.Count > 0
|
||||
@ -4,36 +4,42 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataProviders.Buckets
|
||||
{
|
||||
public class BucketFile
|
||||
{
|
||||
namespace DataProviders.Buckets {
|
||||
public class BucketFile {
|
||||
|
||||
public Guid Id { get; private set; }
|
||||
public Guid SiteId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
public Guid Id { get; private set; }
|
||||
public Guid SiteId { get; private set; }
|
||||
public Guid? UserId { get; private set; }
|
||||
|
||||
public DateTime? Published { get; private set; }
|
||||
public DateTime? Published { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
public byte[] Bytes { get; private set; }
|
||||
public string ContentType { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public byte[] Bytes { get; private set; }
|
||||
public string ContentType { get; private set; }
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, DateTime? published, string name, byte[] bytes, string contentType)
|
||||
{
|
||||
Id = id;
|
||||
SiteId = siteId;
|
||||
UserId = userId;
|
||||
|
||||
Published = published;
|
||||
|
||||
Name = name;
|
||||
Bytes = bytes;
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, string name, byte[] bytes, string contentType)
|
||||
: this(id, siteId, userId, null, name, bytes, contentType) { }
|
||||
|
||||
public BucketFile(Guid siteId, string name, byte[] bytes, string contentType) {
|
||||
Id = Guid.NewGuid();
|
||||
SiteId = siteId;
|
||||
Name = name;
|
||||
Bytes = bytes;
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, string name, byte[] bytes, string contentType) {
|
||||
Id = id;
|
||||
SiteId = siteId;
|
||||
Name = name;
|
||||
Bytes = bytes;
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, string name, byte[] bytes, string contentType) : this(id, siteId, name, bytes, contentType) {
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, DateTime? published, string name, byte[] bytes, string contentType) : this(id, siteId, userId, name, bytes, contentType) {
|
||||
Published = published;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
64
src/DataProviders/Buckets/DkimBucketDataProvider.cs
Normal file
64
src/DataProviders/Buckets/DkimBucketDataProvider.cs
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.GridFS;
|
||||
|
||||
using DataProviders.Buckets.Abstractions;
|
||||
|
||||
namespace DataProviders.Buckets {
|
||||
|
||||
public interface IDkimBucketDataProvider {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) Upload(BucketFile file);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
(BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId);
|
||||
}
|
||||
|
||||
public class DkimBucketDataProvider : BucketDataProviderBase, IDkimBucketDataProvider {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="client"></param>
|
||||
public DkimBucketDataProvider(
|
||||
ILogger<BucketDataProviderBase> logger,
|
||||
IMongoClient client
|
||||
) : base(logger, client, "reactredux", "dkims") { }
|
||||
|
||||
/// <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}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
|
||||
);
|
||||
|
||||
var (list, result) = Download(filter);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,17 +3,17 @@
|
||||
using DomainResults.Common;
|
||||
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
using MongoDB.Driver.GridFS;
|
||||
using DataProviders.Buckets.Abstractions;
|
||||
using MongoDB.Bson;
|
||||
using Extensions;
|
||||
|
||||
namespace DataProviders.Buckets
|
||||
{
|
||||
namespace DataProviders.Buckets {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IImageBucketDataProvider {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IImageBucketDataProvider {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -84,9 +84,41 @@ namespace DataProviders.Buckets
|
||||
public ImageBucketDataProvider(
|
||||
ILogger<BucketDataProviderBase> logger,
|
||||
IMongoClient client
|
||||
) : base(logger, client, "images") { }
|
||||
) : base(logger, client, "reactredux", "images") { }
|
||||
|
||||
#region Upload many
|
||||
protected override async Task<(List<Guid>?, IDomainResult)> UploadManyAsync(List<BucketFile> files) {
|
||||
var options = new GridFSUploadOptions {
|
||||
ChunkSizeBytes = 64512, // 63KB
|
||||
};
|
||||
|
||||
try {
|
||||
var result = new List<Guid>();
|
||||
foreach (var file in files) {
|
||||
options.Metadata = new BsonDocument {
|
||||
{ "siteId", $"{file.SiteId}"},
|
||||
{ "userId", $"{file.UserId}"},
|
||||
{ "published", file.Published.ToString() },
|
||||
{ "fileName", file.Name },
|
||||
{ "contentType", file.ContentType }
|
||||
};
|
||||
|
||||
await _bucket.UploadFromBytesAsync($"{file.Id}", file.Bytes, options);
|
||||
result.Add(file.Id);
|
||||
}
|
||||
|
||||
return result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Bucket data provider error");
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Download
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@ -130,7 +162,9 @@ namespace DataProviders.Buckets
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Download many
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@ -152,11 +186,58 @@ namespace DataProviders.Buckets
|
||||
);
|
||||
}
|
||||
|
||||
if (filter == null)
|
||||
return IDomainResult.Failed<List<BucketFile>?>();
|
||||
|
||||
var result = Download(filter);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override async Task<(List<BucketFile>?, IDomainResult)> DownloadAsync(FilterDefinition<GridFSFileInfo> filter) {
|
||||
try {
|
||||
var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
|
||||
|
||||
using var cursor = await _bucket.FindAsync(filter, new GridFSFindOptions {
|
||||
Sort = sort,
|
||||
});
|
||||
|
||||
var result = new List<BucketFile>();
|
||||
|
||||
// fileInfo either has the matching file information or is null
|
||||
foreach (var fileInfo in await cursor.ToListAsync()) {
|
||||
if (fileInfo == null)
|
||||
return IDomainResult.NotFound<List<BucketFile>?>();
|
||||
|
||||
var id = fileInfo.Filename.ToGuid();
|
||||
var userId = fileInfo.Metadata["userId"].ToString()?.ToNullableGuid();
|
||||
var siteId = fileInfo.Metadata["siteId"].ToString()?.ToNullableGuid();
|
||||
var published = fileInfo.Metadata["published"].ToString()?.ToNullableDateTime();
|
||||
var fileName = fileInfo.Metadata["fileName"].ToString() ?? "";
|
||||
var bytes = await _bucket.DownloadAsBytesByNameAsync($"{fileInfo.Filename}", new GridFSDownloadByNameOptions {
|
||||
Revision = -1
|
||||
});
|
||||
var contentType = fileInfo.Metadata["contentType"].ToString() ?? "";
|
||||
|
||||
if (siteId == null || userId == null || bytes == null)
|
||||
return IDomainResult.Failed<List<BucketFile>?>();
|
||||
|
||||
result.Add(new BucketFile(id, siteId.Value, userId.Value, published, fileName, bytes, contentType));
|
||||
}
|
||||
|
||||
return result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.NotFound<List<BucketFile>?>();
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Bucket data provider error");
|
||||
return IDomainResult.Failed<List<BucketFile>?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
public IDomainResult DeleteOne(Guid siteId, Guid userId, Guid fileId) =>
|
||||
DeleteOneAsync(siteId, userId, fileId).Result;
|
||||
|
||||
@ -175,5 +256,6 @@ namespace DataProviders.Buckets
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}")
|
||||
));
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
58
src/DataProviders/Buckets/TemplateBucketDataProvider.cs
Normal file
58
src/DataProviders/Buckets/TemplateBucketDataProvider.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using DataProviders.Buckets.Abstractions;
|
||||
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;
|
||||
|
||||
namespace DataProviders.Buckets {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface ITemplateBucketDataProvider {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) Upload(BucketFile file);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
(BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId);
|
||||
}
|
||||
|
||||
public class TemplateBucketDataProvider : BucketDataProviderBase, ITemplateBucketDataProvider {
|
||||
public TemplateBucketDataProvider(
|
||||
ILogger<BucketDataProviderBase> logger,
|
||||
IMongoClient client
|
||||
) : base(logger, client, "reactredux", "templates") { }
|
||||
|
||||
public (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId) {
|
||||
var filter = Builders<GridFSFileInfo>.Filter.And(
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
|
||||
);
|
||||
|
||||
var (list, result) = Download(filter);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,309 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using DomainObjects.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DataProviders.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections.Abstractions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class CollectionDataProviderBase<T> : DataProviderBase<CollectionDataProviderBase<T>> where T : DomainObjectDocumentBase<T>
|
||||
{
|
||||
|
||||
private protected readonly IIdGenerator _idGenerator;
|
||||
private protected readonly ISessionService _sessionService;
|
||||
private protected readonly IMongoCollection<T> _collection;
|
||||
|
||||
/// <summary>
|
||||
/// Main constructor
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="idGenerator"></param>
|
||||
/// <param name="dataProviderUtils"></param>
|
||||
public CollectionDataProviderBase(
|
||||
ILogger<CollectionDataProviderBase<T>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService,
|
||||
string databaseName,
|
||||
string collectionName
|
||||
) : base(logger, client, databaseName)
|
||||
{
|
||||
_idGenerator = idGenerator;
|
||||
_sessionService = sessionService;
|
||||
|
||||
if (!_database.ListCollectionNames().ToList().Contains(collectionName))
|
||||
_database.CreateCollection(collectionName);
|
||||
|
||||
_collection = _database.GetCollection<T>(collectionName);
|
||||
}
|
||||
|
||||
#region Insert
|
||||
public (Guid?, IDomainResult) Insert(T obj) =>
|
||||
InsertAsync(obj).Result;
|
||||
|
||||
public (Guid?, IDomainResult) Insert(T obj, Guid sessionId) =>
|
||||
InsertAsync(obj, sessionId).Result;
|
||||
|
||||
public Task<(Guid?, IDomainResult)> InsertAsync(T obj) =>
|
||||
InsertAsync(obj, null);
|
||||
|
||||
public Task<(Guid?, IDomainResult)> InsertAsync(T obj, Guid sessionId) =>
|
||||
InsertAsync(obj, sessionId);
|
||||
|
||||
protected virtual async Task<(Guid?, IDomainResult)> InsertAsync(T obj, Guid? sessionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (sessionId != null)
|
||||
await _collection.InsertOneAsync(_sessionService.GetSession(sessionId.Value), obj);
|
||||
else
|
||||
_collection.InsertOne(obj);
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region InsertMany
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList) =>
|
||||
InsertManyAsync(objList).Result;
|
||||
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList, Guid sessionId) =>
|
||||
InsertManyAsync(objList, sessionId).Result;
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList) =>
|
||||
InsertManyAsync(objList, null);
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, Guid sessionId) =>
|
||||
InsertManyAsync(objList, sessionId);
|
||||
|
||||
protected virtual async Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, Guid? sessionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (sessionId != null)
|
||||
await _collection.InsertManyAsync(_sessionService.GetSession(sessionId.Value), objList);
|
||||
else
|
||||
_collection.InsertMany(objList);
|
||||
|
||||
return IDomainResult.Success(objList.Select(x => x.Id).ToList());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Count
|
||||
private protected (int?, IDomainResult) CountWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
CountWithPredicate(new List<Expression<Func<T, bool>>> { predicate });
|
||||
|
||||
private protected (int?, IDomainResult) CountWithPredicate(List<Expression<Func<T, bool>>> predicates)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = GetWithPredicate(predicates);
|
||||
|
||||
var result = query.Count();
|
||||
|
||||
return IDomainResult.Success(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<int?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> selector) =>
|
||||
GetWithPredicate(new List<Expression<Func<T, bool>>> { predicate }, selector, null, null);
|
||||
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(Expression<Func<T, bool>> predicates, Expression<Func<T, TResult>> selector, int? skip, int? limit) =>
|
||||
GetWithPredicate(new List<Expression<Func<T, bool>>> { predicates }, selector, skip, limit);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="predicates"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <param name="skip"></param>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(List<Expression<Func<T, bool>>> predicates, Expression<Func<T, TResult>> selector, int? skip, int? limit)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = GetWithPredicate(predicates).Select(selector);
|
||||
|
||||
if (skip != null)
|
||||
query = query.Skip(skip.Value);
|
||||
|
||||
if (limit != null)
|
||||
query = query.Take(limit.Value);
|
||||
|
||||
var result = query.ToList();
|
||||
|
||||
return result != null && result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.NotFound<List<TResult>?>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<TResult>?>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicates"></param>
|
||||
/// <returns></returns>
|
||||
private protected IQueryable<T> GetWithPredicate(List<Expression<Func<T, bool>>> predicates)
|
||||
{
|
||||
var query = GetQuery();
|
||||
|
||||
foreach (var predicate in predicates)
|
||||
query = query.Where(predicate);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IQueryable<T> GetQuery() => _collection.AsQueryable();
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate) =>
|
||||
UpdateWithPredicateAsync(obj, predicate).Result;
|
||||
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, sessionId).Result;
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, null);
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, sessionId);
|
||||
|
||||
protected virtual async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, Guid? sessionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (sessionId != null)
|
||||
await _collection.ReplaceOneAsync(_sessionService.GetSession(sessionId.Value), predicate, obj);
|
||||
else
|
||||
await _collection.ReplaceOneAsync(predicate, obj);
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteWithPredicateAsync(predicate).Result;
|
||||
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteWithPredicateAsync(predicate, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteWithPredicateAsync(predicate, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteWithPredicateAsync(predicate, sessionId);
|
||||
|
||||
protected virtual async Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (sessionId != null)
|
||||
await _collection.DeleteOneAsync<T>(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await _collection.DeleteOneAsync<T>(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DeleteMany
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteManyWithPredicateAsync(predicate).Result;
|
||||
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsync(predicate, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate) =>
|
||||
DeleteManyWithPredicateAsync(predicate, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsync(predicate, sessionId);
|
||||
|
||||
protected virtual async Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (sessionId != null)
|
||||
await _collection.DeleteManyAsync(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await _collection.DeleteManyAsync(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -5,12 +5,12 @@ using DomainResults.Common;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
|
||||
using DomainObjects.Documents;
|
||||
using DataProviders.Collections.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections {
|
||||
public interface IBlogCatalogDataProvider {
|
||||
namespace DataProviders.Collections
|
||||
{
|
||||
public interface IBlogCatalogDataProvider {
|
||||
(Guid?, IDomainResult) Insert(BlogDocument blogItem);
|
||||
(BlogDocument?, IDomainResult) Get(Guid siteId, Guid blogId);
|
||||
(BlogDocument?, IDomainResult) GetBySlug(Guid siteId, string slug);
|
||||
|
||||
@ -4,15 +4,15 @@ using MongoDB.Driver;
|
||||
using MongoDB.Bson.Serialization;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
using DomainObjects.Documents;
|
||||
using DomainObjects.L10n;
|
||||
using DomainObjects.Enumerations;
|
||||
using DataProviders.Collections.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections {
|
||||
namespace DataProviders.Collections
|
||||
{
|
||||
|
||||
public interface ICategoryDataProvider {
|
||||
public interface ICategoryDataProvider {
|
||||
(Guid?, IDomainResult) Insert(CategoryDocument obj);
|
||||
(Guid?, IDomainResult) CreateDefault(Guid siteId);
|
||||
(CategoryDocument?, IDomainResult) Get(Guid siteId, Guid categoryId);
|
||||
|
||||
@ -4,13 +4,13 @@ using DomainResults.Common;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
using DomainObjects.Documents;
|
||||
using DataProviders.Collections.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections {
|
||||
namespace DataProviders.Collections
|
||||
{
|
||||
|
||||
public interface IContentDataProvider {
|
||||
public interface IContentDataProvider {
|
||||
(List<ContentDocument>?, IDomainResult) Get(Guid siteId);
|
||||
}
|
||||
|
||||
|
||||
@ -5,13 +5,13 @@ using DomainResults.Common;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
|
||||
using DomainObjects.Documents;
|
||||
using DataProviders.Collections.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections {
|
||||
namespace DataProviders.Collections
|
||||
{
|
||||
|
||||
public interface IShopCartDataProvider {
|
||||
public interface IShopCartDataProvider {
|
||||
(Guid?, IDomainResult) Insert(ShopCartDocument obj);
|
||||
(List<ShopCartDocument>?, IDomainResult) GetAll(Guid siteId, Guid userId);
|
||||
(ShopCartDocument?, IDomainResult) Get(Guid siteId, Guid userId, string sku);
|
||||
|
||||
@ -4,12 +4,12 @@ using DomainResults.Common;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
using DomainObjects.Documents;
|
||||
using DataProviders.Collections.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections {
|
||||
public interface IShopCatalogDataProvider {
|
||||
namespace DataProviders.Collections
|
||||
{
|
||||
public interface IShopCatalogDataProvider {
|
||||
(Guid?, IDomainResult) Insert(ShopDocument obj);
|
||||
(ShopDocument?, IDomainResult) Get(Guid siteId, string sku);
|
||||
(ShopDocument?, IDomainResult) GetBySlug(Guid siteId, string slug);
|
||||
|
||||
@ -5,9 +5,9 @@ using MongoDB.Driver;
|
||||
|
||||
using DomainObjects.Documents.Sites;
|
||||
using DomainResults.Common;
|
||||
using DataProviders.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using DataProviders.Collections.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections;
|
||||
|
||||
@ -28,6 +28,7 @@ public class SiteDataProvider : CollectionDataProviderBase<Site>, ISiteDataProvi
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService, _databaseName, _collectionName) { }
|
||||
|
||||
|
||||
public (Site?, IDomainResult) GetByHostName(string hostName) {
|
||||
var (list, result) = GetWithPredicate(x => x.Hosts.Contains(hostName), x => x);
|
||||
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
using DomainObjects.Documents.Users;
|
||||
using DataProviders.Collections.Abstractions;
|
||||
|
||||
namespace DataProviders.Collections;
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ namespace DataProviders.Extensions
|
||||
|
||||
#region Buckets
|
||||
services.AddSingleton<IImageBucketDataProvider, ImageBucketDataProvider>();
|
||||
services.AddSingleton<IDkimBucketDataProvider, DkimBucketDataProvider>();
|
||||
services.AddSingleton<ITemplateBucketDataProvider, TemplateBucketDataProvider>();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,13 +6,12 @@ namespace DomainObjects;
|
||||
public class Contact : DomainObjectBase<Contact> {
|
||||
public ContactTypes Type { get; set; }
|
||||
public string Value { get; set; }
|
||||
public bool Confirmed { get; set; }
|
||||
public bool? Confirmed { get; set; }
|
||||
public bool? Primary { get; set; }
|
||||
|
||||
public Contact(ContactTypes type, string value) {
|
||||
Type = type;
|
||||
Value = value;
|
||||
Confirmed = false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace DomainObjects.Documents.Users;
|
||||
namespace DomainObjects.Documents;
|
||||
|
||||
public class Address
|
||||
{
|
||||
32
src/DomainObjects/Documents/MailboxConnectionSettings.cs
Normal file
32
src/DomainObjects/Documents/MailboxConnectionSettings.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using CryptoProvider;
|
||||
using DomainObjects.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DomainObjects.Documents;
|
||||
|
||||
public class MailboxConnectionSettings : DomainObjectBase<MailboxConnectionSettings> {
|
||||
|
||||
public string Server { get; set; }
|
||||
|
||||
public uint Port { get; set; }
|
||||
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Password { get; private set; }
|
||||
public void SetPassword(string iv, string key, string password) {
|
||||
Password = AesService.EncryptString(iv, key, password);
|
||||
}
|
||||
|
||||
public string GetPassword(string iv, string key) =>
|
||||
AesService.DecryptString(iv, key, Password);
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
16
src/DomainObjects/Documents/Sites/DkimSettings.cs
Normal file
16
src/DomainObjects/Documents/Sites/DkimSettings.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.Sites;
|
||||
public class DkimSettings : DomainObjectBase<DkimSettings> {
|
||||
|
||||
public string HostName { get; set; }
|
||||
|
||||
public string Selector { get; set; }
|
||||
|
||||
public Guid DkimId { get; set; }
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
27
src/DomainObjects/Documents/Sites/PassworRecoverySettings.cs
Normal file
27
src/DomainObjects/Documents/Sites/PassworRecoverySettings.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.Sites;
|
||||
|
||||
public class PassworRecoverySettings : DomainObjectBase<PassworRecoverySettings> {
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public DkimSettings DkimSettings { get; set; }
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,23 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.Sites {
|
||||
|
||||
public class Site : DomainObjectDocumentBase<Site> {
|
||||
|
||||
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 PassworRecoverySettings PassworRecoverySettings { get; set; }
|
||||
|
||||
public Site(string name, List<string> hosts) {
|
||||
Name = name;
|
||||
Hosts = hosts;
|
||||
|
||||
@ -89,6 +89,23 @@ namespace EmailProvider {
|
||||
}.Sign(_mimeMessage, new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.To });
|
||||
}
|
||||
|
||||
|
||||
public void DkimSign(string domain, string selector, byte[] bytes) {
|
||||
|
||||
using var ms = new MemoryStream(bytes);
|
||||
|
||||
new DkimSigner(
|
||||
ms,
|
||||
domain,
|
||||
selector
|
||||
) {
|
||||
HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple,
|
||||
BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple,
|
||||
AgentOrUserIdentifier = $"@{domain}", // your domain name
|
||||
QueryMethod = "dns/txt",
|
||||
}.Sign(_mimeMessage, new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.To });
|
||||
}
|
||||
|
||||
public byte [] Build() {
|
||||
|
||||
using var stream = new MemoryStream();
|
||||
|
||||
@ -3,15 +3,36 @@ using DataProviders;
|
||||
|
||||
namespace WeatherForecast {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IJwtConfig {
|
||||
|
||||
public string? Secret { get; set; }
|
||||
public int? Expires { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Secret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Expires { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class JwtConfig : IJwtConfig {
|
||||
public string? Secret { get; set; }
|
||||
public int? Expires { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Secret { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Expires { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -8,6 +8,8 @@ using WeatherForecast.Policies;
|
||||
using WeatherForecast.Services;
|
||||
using DomainObjects.Documents.Users;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
using DomainResults.Common;
|
||||
|
||||
namespace WeatherForecast.Controllers;
|
||||
|
||||
@ -47,13 +49,16 @@ public class AccountController : ControllerBase {
|
||||
return result.ToActionResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="requestData"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("[action]")]
|
||||
public IActionResult Register([FromBody] RegisterRequestModel requestData) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Password
|
||||
/// <summary>
|
||||
/// Passing the Username in the request body is a more secure alternative to passing it as a GET param
|
||||
@ -63,7 +68,12 @@ public class AccountController : ControllerBase {
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
[HttpPut($"{_password}/[action]")]
|
||||
public IActionResult Recovery([FromBody] PasswordRecoveryRequestModel requestData) {
|
||||
var result = _accountService.PasswordRecovery(requestData);
|
||||
|
||||
var hostName = Request?.Headers["X-Forwarded-Host"].FirstOrDefault();
|
||||
if (hostName == null)
|
||||
return IDomainResult.Failed().ToActionResult();
|
||||
|
||||
var result = _accountService.PasswordRecovery(hostName, requestData);
|
||||
return result.ToActionResult();
|
||||
}
|
||||
|
||||
|
||||
55
src/WeatherForecast/Controllers/DkimController.cs
Normal file
55
src/WeatherForecast/Controllers/DkimController.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using DomainResults.Common;
|
||||
using DomainResults.Mvc;
|
||||
|
||||
using DataProviders.Buckets;
|
||||
using WeatherForecast.Services;
|
||||
|
||||
namespace WeatherForecast.Controllers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[Route("api/[controller]")]
|
||||
public class DkimController : ControllerBase {
|
||||
|
||||
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IDkimService _dkimService;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="authorizationService"></param>
|
||||
/// <param name="dkimService"></param>
|
||||
public DkimController(
|
||||
IAuthorizationService authorizationService,
|
||||
IDkimService dkimService
|
||||
) {
|
||||
_authorizationService = authorizationService;
|
||||
_dkimService = dkimService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows to upload private dkim certificate
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{siteId}")]
|
||||
public IActionResult Post([FromRoute] Guid siteId, IFormFile file) {
|
||||
|
||||
if (!(file.Length > 0))
|
||||
return IDomainResult.Failed().ToActionResult();
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
file.CopyTo(ms);
|
||||
|
||||
var result = _dkimService.Post(new BucketFile(siteId, file.FileName, ms.ToArray(), file.ContentType));
|
||||
|
||||
return result.ToActionResult();
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,8 @@ public class FilesController : ControllerBase {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="filesService"></param>
|
||||
/// <param name="authorizationService"></param>
|
||||
/// <param name="imageBucketDataProvider"></param>
|
||||
public FilesController(
|
||||
IAuthorizationService authorizationService,
|
||||
IImageBucketDataProvider imageBucketDataProvider
|
||||
|
||||
@ -5,18 +5,31 @@ using WeatherForecast.Models.Initialization.Requests;
|
||||
using WeatherForecast.Services;
|
||||
|
||||
namespace WeatherForecast.Controllers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class InitializationController : ControllerBase {
|
||||
|
||||
private readonly IInitializationService _initializationService;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="initializationService"></param>
|
||||
public InitializationController(
|
||||
IInitializationService initializationService
|
||||
) {
|
||||
_initializationService = initializationService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="requestData"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public IActionResult InitializeSystem([FromBody] InitializeSystemRequestModel requestData) {
|
||||
var result = _initializationService.InitializeSystem(requestData);
|
||||
|
||||
56
src/WeatherForecast/Controllers/TemplateController.cs
Normal file
56
src/WeatherForecast/Controllers/TemplateController.cs
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using DomainResults.Common;
|
||||
using DomainResults.Mvc;
|
||||
|
||||
using DataProviders.Buckets;
|
||||
using WeatherForecast.Services;
|
||||
|
||||
namespace WeatherForecast.Controllers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[Route("api/[controller]")]
|
||||
public class TemplateController : ControllerBase {
|
||||
|
||||
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly ITemplateService _templateService;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="authorizationService"></param>
|
||||
/// <param name="templateService"></param>
|
||||
public TemplateController(
|
||||
IAuthorizationService authorizationService,
|
||||
ITemplateService templateService
|
||||
) {
|
||||
_authorizationService = authorizationService;
|
||||
_templateService = templateService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows to upload private dkim certificate
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{siteId}")]
|
||||
public IActionResult Post([FromRoute] Guid siteId, IFormFile file) {
|
||||
|
||||
if (!(file.Length > 0))
|
||||
return IDomainResult.Failed().ToActionResult();
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
file.CopyTo(ms);
|
||||
|
||||
var result = _templateService.Post(new BucketFile(siteId, file.FileName, ms.ToArray(), file.ContentType));
|
||||
|
||||
return result.ToActionResult();
|
||||
}
|
||||
}
|
||||
31
src/WeatherForecast/Controllers/UtilsController.cs
Normal file
31
src/WeatherForecast/Controllers/UtilsController.cs
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
|
||||
using CryptoProvider;
|
||||
using DomainResults.Mvc;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WeatherForecast.Models.Initialization.Requests;
|
||||
using WeatherForecast.Services;
|
||||
|
||||
namespace WeatherForecast.Controllers;
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class UtilsController : ControllerBase {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("[action]")]
|
||||
public IActionResult Aes() {
|
||||
return Ok(AesService.GenerateKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using DomainObjects.Abstractions;
|
||||
using Core.Abstractions.Models;
|
||||
using DomainObjects.Abstractions;
|
||||
using WeatherForecast.Models.Abstractions.PostItem.Responses;
|
||||
|
||||
namespace WeatherForecast.Models.Abstractions {
|
||||
|
||||
@ -12,12 +12,12 @@ namespace WeatherForecast.Models.Account.Requests {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Username { get; set; }
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Password { get; set; }
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@ -1,4 +1,21 @@
|
||||
namespace WeatherForecast.Models.Account.Requests {
|
||||
public class CreateAccountRequestModel {
|
||||
using Core.Abstractions.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WeatherForecast.Models.Account.Requests {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class CreateAccountRequestModel : RequestModelBase {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="validationContext"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,22 @@ using Core.Enumerations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WeatherForecast.Models.Account.Requests {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class PasswordRecoveryRequestModel : RequestModelBase {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="validationContext"></param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||
if (string.IsNullOrWhiteSpace(Username))
|
||||
yield return new ValidationResult($"{nameof(Username)} ${Errors.NullOrEmpty}");
|
||||
|
||||
@ -24,6 +24,11 @@ namespace WeatherForecast.Models.Account.Requests {
|
||||
/// </summary>
|
||||
public string RePassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="validationContext"></param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Token))
|
||||
|
||||
@ -1,4 +1,21 @@
|
||||
namespace WeatherForecast.Models.Account.Requests {
|
||||
public class RegisterRequestModel {
|
||||
using Core.Abstractions.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WeatherForecast.Models.Account.Requests {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class RegisterRequestModel : RequestModelBase {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="validationContext"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using DomainObjects.Pages;
|
||||
using DomainObjects.Pages;
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
using WeatherForecast.Models.Content.Responses.PageSections;
|
||||
|
||||
|
||||
@ -1,11 +1,26 @@
|
||||
using Core.Abstractions.Models;
|
||||
|
||||
namespace WeatherForecast.Models.File.Responses {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FileResponseModel : ResponseModelBase {
|
||||
public string Name { get; set; }
|
||||
|
||||
public byte[] Bytes { get; set; }
|
||||
/// <summary>
|
||||
/// File name
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string ContentType { get; set; }
|
||||
/// <summary>
|
||||
/// Byte contente, when file is returned as stream
|
||||
/// </summary>
|
||||
|
||||
public byte[] Bytes { get; set; } = Array.Empty<byte>();
|
||||
|
||||
/// <summary>
|
||||
/// MIME content type
|
||||
/// </summary>
|
||||
public string ContentType { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,24 +5,66 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace WeatherForecast.Models.Initialization.Requests {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class InitializeSystemRequestModel : RequestModelBase<InitializeSystemRequestModel> {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string SiteName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string RePassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string DkimBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ServiceEmlTemplateBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public InitializeSystemRequestModel ToDomainObject() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="validationContext"></param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||
|
||||
if (string.IsNullOrWhiteSpace(SiteName))
|
||||
@ -45,6 +87,12 @@ 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(ServiceEmlTemplateBase64))
|
||||
yield return new ValidationResult($"{nameof(ServiceEmlTemplateBase64)} {Errors.NullOrWhiteSpace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ public static class AuthorisationHandlerHelper {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="site"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
public static Roles? GetRole(Site site, User user) {
|
||||
@ -109,6 +109,12 @@ public abstract class AuthorizationHandlerBase<TRequirement> : AuthorizationHand
|
||||
protected (Site?, User?) GetUser(AuthorizationHandlerContext context) =>
|
||||
AuthorisationHandlerHelper.GetUser(context, _aesKey, _contextAccessor, _siteDataProvider, _userDataProvider);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="site"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
protected Roles? GetRole(Site site, User user) =>
|
||||
AuthorisationHandlerHelper.GetRole(site, user);
|
||||
}
|
||||
@ -152,6 +158,12 @@ public abstract class AuthorizationHandlerBase<TRequirement, TResource> : Author
|
||||
protected (Site?, User?) GetUser(AuthorizationHandlerContext context) =>
|
||||
AuthorisationHandlerHelper.GetUser(context, _aesKey, _contextAccessor, _siteDataProvider, _userDataProvider);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="site"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
protected Roles? GetRole(Site site, User user) =>
|
||||
AuthorisationHandlerHelper.GetRole(site, user);
|
||||
}
|
||||
|
||||
@ -20,7 +20,9 @@ public class FileAuthorisationHandler : AuthorizationHandlerBase<FileAuthorisati
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
/// <param name="fileSecurityService"></param>
|
||||
public FileAuthorisationHandler(
|
||||
|
||||
@ -57,6 +57,10 @@ public class PasswordChangeAuthorizationHandler : AuthorizationHandlerBase<Passw
|
||||
///
|
||||
/// </summary>
|
||||
public class PasswordChangeRequirement : AuthorizationRequirementBase {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string OldPassword { get; init; }
|
||||
}
|
||||
|
||||
|
||||
@ -15,9 +15,10 @@ namespace WeatherForecast.Policies {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
/// <param name="accountService"></param>
|
||||
public ShopCartAuthorizationHandler(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
|
||||
@ -7,10 +7,8 @@ using WeatherForecast.Models.Account.Requests;
|
||||
using DomainObjects.Documents.Users;
|
||||
using EmailProvider;
|
||||
using DomainObjects.Enumerations;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Extensions;
|
||||
using DomainObjects.Documents.Sites;
|
||||
using DataProviders.Buckets;
|
||||
using System.Text;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
@ -28,9 +26,10 @@ namespace WeatherForecast.Services {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="hostName"></param>
|
||||
/// <param name="requestData"></param>
|
||||
/// <returns></returns>
|
||||
IDomainResult PasswordRecovery(PasswordRecoveryRequestModel requestData);
|
||||
IDomainResult PasswordRecovery(string hostName, PasswordRecoveryRequestModel requestData);
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -56,10 +55,13 @@ namespace WeatherForecast.Services {
|
||||
/// </summary>
|
||||
public class AccountService : ServiceBase<AccountService>, IAccountService {
|
||||
|
||||
private readonly IAesKey? _aesKey;
|
||||
private readonly IJwtConfig? _jwtConfig;
|
||||
private readonly IAesKey _aesKey;
|
||||
private readonly IJwtConfig _jwtConfig;
|
||||
|
||||
private readonly IUserDataProvider _userDataProvider;
|
||||
private readonly ISiteDataProvider _siteDataProvider;
|
||||
private readonly IDkimBucketDataProvider _dkimBucketDataProvider;
|
||||
private readonly ITemplateBucketDataProvider _templateBucketDataProvider;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -67,14 +69,32 @@ namespace WeatherForecast.Services {
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="dkimBucketDataProvider"></param>
|
||||
/// <param name="templateBucketDataProvider"></param>
|
||||
public AccountService(
|
||||
ILogger<AccountService> logger,
|
||||
IOptions<Configuration> options,
|
||||
IUserDataProvider userDataProvider
|
||||
IUserDataProvider userDataProvider,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IDkimBucketDataProvider dkimBucketDataProvider,
|
||||
ITemplateBucketDataProvider templateBucketDataProvider
|
||||
) : base(logger) {
|
||||
|
||||
if (options.Value.JwtTokenEncryption == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
_aesKey = options.Value.JwtTokenEncryption;
|
||||
|
||||
if(options.Value.JwtConfig == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
_jwtConfig = options.Value.JwtConfig;
|
||||
|
||||
_userDataProvider = userDataProvider;
|
||||
_siteDataProvider = siteDataProvider;
|
||||
_dkimBucketDataProvider = dkimBucketDataProvider;
|
||||
_templateBucketDataProvider = templateBucketDataProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -99,7 +119,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.Value);
|
||||
var token = user.AddToken(_aesKey.IV, _aesKey.Key, _jwtConfig.Secret, _jwtConfig.Expires);
|
||||
|
||||
var (_, usdateUserResult) = _userDataProvider.Update(user);
|
||||
if (!usdateUserResult.IsSuccess)
|
||||
@ -108,24 +128,14 @@ namespace WeatherForecast.Services {
|
||||
return IDomainResult.Success(token);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Passing the Username in the request body is a more secure alternative to passing it as a GET param
|
||||
/// </summary>
|
||||
/// <param name="hostName"></param>
|
||||
/// <param name="requestData"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public IDomainResult PasswordRecovery(PasswordRecoveryRequestModel requestData) {
|
||||
public IDomainResult PasswordRecovery(string hostName, PasswordRecoveryRequestModel requestData) {
|
||||
var (user, getUserResult) = _userDataProvider.GetByUsername(requestData.Username);
|
||||
if (!getUserResult.IsSuccess || user == null)
|
||||
return getUserResult;
|
||||
@ -135,27 +145,38 @@ namespace WeatherForecast.Services {
|
||||
if (email == null)
|
||||
return IDomainResult.Failed();
|
||||
|
||||
var template = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "templates", "email-inlined.html"))
|
||||
.Replace("{{content}}", $"Your recovery token: {user.Authentication.PasswordRecovery()}");
|
||||
var (site, getSiteResult) = _siteDataProvider.GetByHostName(hostName);
|
||||
if (!getSiteResult.IsSuccess || site == null)
|
||||
return IDomainResult.Failed();
|
||||
|
||||
var dkimSettings = site.PassworRecoverySettings.DkimSettings;
|
||||
|
||||
var (dkimPrivateKey, downloadDkimPrivateKeyResult) = _dkimBucketDataProvider.Download(site.Id, dkimSettings.DkimId);
|
||||
if(!downloadDkimPrivateKeyResult.IsSuccess || dkimPrivateKey == null)
|
||||
return IDomainResult.Failed();
|
||||
|
||||
var (template, getTemplateResult) = _templateBucketDataProvider.Download(site.Id, site.PassworRecoverySettings.TemplateId);
|
||||
if (!getTemplateResult.IsSuccess || template == null)
|
||||
return IDomainResult.Failed();
|
||||
|
||||
var htmlBody = Encoding.UTF8.GetString(template.Bytes);
|
||||
|
||||
htmlBody = htmlBody.Replace("{{token}}", user.Authentication.PasswordRecovery());
|
||||
|
||||
_userDataProvider.Update(user);
|
||||
|
||||
var emailBuilder = new EmailMessageBuilder();
|
||||
emailBuilder.AddFrom("commercial", "commercial@maks-it.com");
|
||||
emailBuilder.AddFrom(site.PassworRecoverySettings.Name, site.PassworRecoverySettings.Email.Value);
|
||||
emailBuilder.AddTo(user.Username, email.Value);
|
||||
emailBuilder.AddSubject("Password recovery");
|
||||
|
||||
|
||||
emailBuilder.AddHtmlBody(template);
|
||||
|
||||
// TODO: save inside mongo file bucket
|
||||
emailBuilder.DkimSign("maks-it.com", "default", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "dkim", "maks-it.com"));
|
||||
emailBuilder.AddSubject(site.PassworRecoverySettings.Subject);
|
||||
emailBuilder.AddHtmlBody(htmlBody);
|
||||
emailBuilder.DkimSign(dkimSettings.HostName, dkimSettings.Selector, dkimPrivateKey.Bytes);
|
||||
|
||||
using var smtpService = new SMTPService();
|
||||
|
||||
// TODO: change email password and manage configs inside database
|
||||
smtpService.Connect("smtp.ionos.it", 465, true);
|
||||
smtpService.Authenticate("commercial@maks-it.com", "E23{R#<X&#Lyz");
|
||||
smtpService.Connect(site.SmtpSettings.Server, site.SmtpSettings.Port, site.SmtpSettings.UseSsl);
|
||||
//smtpService.Authenticate(site.PassworRecoverySettings.SmtpSettings.UserName, site.PassworRecoverySettings.SmtpSettings.GetPassword());
|
||||
|
||||
smtpService.Send(emailBuilder.Build());
|
||||
|
||||
@ -183,9 +204,10 @@ namespace WeatherForecast.Services {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Only if user is authenticated, do not expose this methods as anonymous
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="oldPassword"></param>
|
||||
/// <param name="newPassword"></param>
|
||||
/// <returns></returns>
|
||||
public (Guid?, IDomainResult) PasswordChange(User user, string oldPassword, string newPassword) {
|
||||
|
||||
54
src/WeatherForecast/Services/DkimService.cs
Normal file
54
src/WeatherForecast/Services/DkimService.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using Core.Abstractions;
|
||||
using DataProviders.Buckets;
|
||||
using DomainResults.Common;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IDkimService {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) Post(BucketFile file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class DkimService : ServiceBase<DkimService>, IDkimService {
|
||||
|
||||
private readonly IDkimBucketDataProvider _dkimBucketDataProvider;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="dkimBucketDataProvider"></param>
|
||||
public DkimService(
|
||||
ILogger<DkimService> logger,
|
||||
IDkimBucketDataProvider dkimBucketDataProvider
|
||||
) : base (logger) {
|
||||
_dkimBucketDataProvider = dkimBucketDataProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
public (Guid?, IDomainResult) Post(BucketFile file) {
|
||||
|
||||
var (fileId, uploadFileResult) = _dkimBucketDataProvider.Upload(file);
|
||||
if (!uploadFileResult.IsSuccess || fileId == null)
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
|
||||
return IDomainResult.Success(fileId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,10 +9,20 @@ using DomainResults.Common;
|
||||
using Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WeatherForecast.Models.Initialization.Requests;
|
||||
using DataProviders.Buckets;
|
||||
|
||||
namespace WeatherForecast.Services;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IInitializationService {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="requestData"></param>
|
||||
/// <returns></returns>
|
||||
IDomainResult InitializeSystem(InitializeSystemRequestModel requestData);
|
||||
}
|
||||
|
||||
@ -22,11 +32,13 @@ public interface IInitializationService {
|
||||
/// </summary>
|
||||
public class InitializationService : ServiceBase<InitializationService>, IInitializationService {
|
||||
|
||||
private readonly IAesKey? _aesKey;
|
||||
private readonly IJwtConfig? _jwtConfig;
|
||||
private readonly IAesKey _aesKey;
|
||||
private readonly IJwtConfig _jwtConfig;
|
||||
|
||||
private readonly ISiteDataProvider _siteDataProvider;
|
||||
private readonly IUserDataProvider _userDataProvider;
|
||||
private readonly IDkimBucketDataProvider _dkimBucketDataProvider;
|
||||
private readonly ITemplateBucketDataProvider _serviceEmlTemplateBucketDataProvider;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -35,16 +47,22 @@ public class InitializationService : ServiceBase<InitializationService>, IInitia
|
||||
/// <param name="options"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
/// <param name="dkimBucketDataProvider"></param>
|
||||
/// <param name="serviceEmlTemplateBucketDataProvider"></param>
|
||||
public InitializationService(
|
||||
ILogger<InitializationService> logger,
|
||||
IOptions<Configuration> options,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
IUserDataProvider userDataProvider,
|
||||
IDkimBucketDataProvider dkimBucketDataProvider,
|
||||
ITemplateBucketDataProvider serviceEmlTemplateBucketDataProvider
|
||||
) : base(logger) {
|
||||
_aesKey = options.Value.JwtTokenEncryption;
|
||||
_jwtConfig = options.Value.JwtConfig;
|
||||
_siteDataProvider = siteDataProvider;
|
||||
_userDataProvider = userDataProvider;
|
||||
_dkimBucketDataProvider = dkimBucketDataProvider;
|
||||
_serviceEmlTemplateBucketDataProvider = serviceEmlTemplateBucketDataProvider;
|
||||
|
||||
}
|
||||
|
||||
@ -58,8 +76,37 @@ 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"
|
||||
));
|
||||
|
||||
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"
|
||||
));
|
||||
|
||||
if (!dkimUpdloadResult.IsSuccess || templateId == null)
|
||||
return IDomainResult.Failed();
|
||||
#endregion
|
||||
|
||||
var (_, siteInsetResult) = _siteDataProvider.Insert(new Site(requestData.SiteName, new List<string> { requestData.Host }) {
|
||||
Id = siteId
|
||||
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) {
|
||||
|
||||
@ -29,9 +29,7 @@ public interface IShopCartItemService {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="sku"></param>
|
||||
/// <param name="cartItem"></param>
|
||||
/// <param name="locale"></param>
|
||||
/// <returns></returns>
|
||||
(ShopCartItemResponseModel?, IDomainResult) Get(ShopCartDocument cartItem, string? locale);
|
||||
@ -39,9 +37,7 @@ public interface IShopCartItemService {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="sku"></param>
|
||||
/// <param name="cartItem"></param>
|
||||
/// <param name="requestData"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) Update(ShopCartDocument cartItem, ShopCartItemRequestModel requestData);
|
||||
|
||||
@ -18,17 +18,14 @@ namespace WeatherForecast.Services {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="sku"></param>
|
||||
/// <param name="requestModel"></param>
|
||||
/// <param name="shopItem"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) Post(ShopDocument shopItem);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="sku"></param>
|
||||
/// <param name="shopItem"></param>
|
||||
/// <returns></returns>
|
||||
(ShopItemResponseModel?, IDomainResult) Get(ShopDocument shopItem);
|
||||
|
||||
@ -101,8 +98,7 @@ namespace WeatherForecast.Services {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="sku"></param>
|
||||
/// <param name="shopItem"></param>
|
||||
/// <returns></returns>
|
||||
public (ShopItemResponseModel?, IDomainResult) Get(ShopDocument shopItem) {
|
||||
var (categories, getCategoryResult) = _categoryDataProvider.GetMany(shopItem.SiteId, shopItem.Categories);
|
||||
|
||||
54
src/WeatherForecast/Services/TemplateService.cs
Normal file
54
src/WeatherForecast/Services/TemplateService.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using Core.Abstractions;
|
||||
using DataProviders.Buckets;
|
||||
using DomainResults.Common;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface ITemplateService {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) Post(BucketFile file);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,6 @@ using ImageProvider.Extensions;
|
||||
using Core.Middlewares;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using WeatherForecast.Policies;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace WeatherForecast {
|
||||
|
||||
@ -58,7 +57,7 @@ namespace WeatherForecast {
|
||||
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull);
|
||||
|
||||
#region configure jwt authentication
|
||||
if (appSettings.JwtConfig?.Secret != null) {
|
||||
if (appSettings?.JwtConfig?.Secret != null) {
|
||||
services.AddAuthentication(options => {
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
@ -93,12 +92,14 @@ namespace WeatherForecast {
|
||||
services.AddScoped<ICategoryItemService, CategoryItemService>();
|
||||
services.AddScoped<ICategoryItemsService, CategoryItemsService>();
|
||||
services.AddScoped<IContentService, ContentService>();
|
||||
services.AddScoped<IDkimService, DkimService>();
|
||||
services.AddScoped<IImageService, ImageService>();
|
||||
services.AddScoped<IInitializationService, InitializationService>();
|
||||
services.AddScoped<IShopCartItemService, ShopCartItemService>();
|
||||
services.AddScoped<IShopCartItemsService, ShopCartItemsService>();
|
||||
services.AddScoped<IShopItemService, ShopItemService>();
|
||||
services.AddScoped<IShopItemsService, ShopItemsService>();
|
||||
services.AddScoped<ITemplateService, TemplateService>();
|
||||
#endregion
|
||||
|
||||
services.RegisterDataproviders(appSettings);
|
||||
|
||||
@ -12,6 +12,13 @@
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="templates\**" />
|
||||
<Content Remove="templates\**" />
|
||||
<EmbeddedResource Remove="templates\**" />
|
||||
<None Remove="templates\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DomainResult" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.10" />
|
||||
@ -35,20 +42,10 @@
|
||||
<ProjectReference Include="..\ImageProvider\ImageProvider.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="templates\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="dkim\maks-it.com">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="templates\email-inlined.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="templates\email.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -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.87032=(addr="018781e4072edf558b81e49885698c8c81e48750482f808080e301bfc0e2dfc0",order=87032,time=1671750241,size=69632,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=442,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=261469,run_write_gen=260980)),checkpoint_backup_info=,checkpoint_lsn=(33,135936)
|
||||
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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user