Чот много сделано.
This commit is contained in:
parent
bad2805994
commit
9a54341fcc
13
Common.WebApi/Common.WebApi.csproj
Normal file
13
Common.WebApi/Common.WebApi.csproj
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MongoDB.Driver" Version="2.29.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
89
Common.WebApi/Middlewares/ExceptionMiddleware.cs
Normal file
89
Common.WebApi/Middlewares/ExceptionMiddleware.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Common.WebApi.Middlewares;
|
||||||
|
|
||||||
|
public class ExceptionMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IHostEnvironment _host;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly JsonSerializerOptions _serializerOptions;
|
||||||
|
|
||||||
|
public ExceptionMiddleware(RequestDelegate next, IHostEnvironment host, ILogger<ExceptionMiddleware> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_next = next;
|
||||||
|
_host = host;
|
||||||
|
_serializerOptions = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
Converters = {
|
||||||
|
new JsonStringEnumConverter()
|
||||||
|
},
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//if (httpContext.Request.Method == "POST")
|
||||||
|
//{
|
||||||
|
// using var streamreader = new System.IO.StreamReader(httpContext.Request.Body);
|
||||||
|
// var body = await streamreader.ReadToEndAsync();
|
||||||
|
//}
|
||||||
|
await _next(httpContext);
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
var title = exc.Message;
|
||||||
|
var detail = "Unexpected error occured";
|
||||||
|
if (!_host.IsProduction()) detail = GetExceptionDetail(exc, false, true);
|
||||||
|
if (exc is MongoException dbUpdateException)
|
||||||
|
{
|
||||||
|
title = "Database error";
|
||||||
|
if (!_host.IsProduction()) detail = GetExceptionDetail(dbUpdateException, true, true);
|
||||||
|
}
|
||||||
|
else if (exc.Source?.Contains("Mongo") == true)
|
||||||
|
{
|
||||||
|
title = "Database error";
|
||||||
|
if (!_host.IsProduction()) detail = GetExceptionDetail(exc, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var problemDetail = new ProblemDetails
|
||||||
|
{
|
||||||
|
Title = title,
|
||||||
|
Detail = detail,
|
||||||
|
Instance = httpContext.Request.Path,
|
||||||
|
Status = StatusCodes.Status400BadRequest
|
||||||
|
};
|
||||||
|
|
||||||
|
_logger.LogError("Необработанная ошибка: {Message}", exc.Message);
|
||||||
|
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
httpContext.Response.ContentType = "application/problem+json";
|
||||||
|
await httpContext.Response.WriteAsync(JsonSerializer.Serialize(problemDetail, _serializerOptions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetExceptionDetail(Exception exc, bool includeMessage, bool includeInner)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
if (!string.IsNullOrWhiteSpace(exc.Message) && includeMessage) sb.Append(exc.Message);
|
||||||
|
if (!string.IsNullOrWhiteSpace(exc.InnerException?.Message) && includeInner)
|
||||||
|
{
|
||||||
|
if (sb.Length > 0) sb.AppendLine();
|
||||||
|
sb.AppendLine("Inner:");
|
||||||
|
sb.Append(exc.InnerException.Message);
|
||||||
|
}
|
||||||
|
if (sb.Length > 0) sb.AppendLine();
|
||||||
|
sb.Append(exc.StackTrace);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Modules.Account.Api/Class1.cs
Normal file
7
Modules.Account.Api/Class1.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Modules.Account.Api
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Modules.Account.Api/Modules.Account.Api.csproj
Normal file
9
Modules.Account.Api/Modules.Account.Api.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
25
Modules.Account.Application/Commands/CreateAccountCommand.cs
Normal file
25
Modules.Account.Application/Commands/CreateAccountCommand.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Account.Application.Gateways;
|
||||||
|
using Modules.Account.Domain.Gateways;
|
||||||
|
|
||||||
|
namespace Modules.Account.Application.Commands;
|
||||||
|
|
||||||
|
public class CreateAccountCommand : IRequest<Guid>
|
||||||
|
{
|
||||||
|
public string Email { get; set; } = default!;
|
||||||
|
public string Password { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateAccountCommandHandler(IAccountGateway gateway, IPasswordHasher passwordHasher) : IRequestHandler<CreateAccountCommand, Guid>
|
||||||
|
{
|
||||||
|
public async Task<Guid> Handle(CreateAccountCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (await gateway.IsExists(request.Email)) throw new Exception("Account with the same eamil already exists");
|
||||||
|
var newAccount = Domain.Entities.Account.Create(request.Email, request.Password, passwordHasher);
|
||||||
|
return await gateway.Create(new Models.Account
|
||||||
|
{
|
||||||
|
Email = newAccount.Email.Value,
|
||||||
|
HashedPassword = newAccount.HashedPassword,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Modules.Account.Application/Gateways/IAccountGateway.cs
Normal file
13
Modules.Account.Application/Gateways/IAccountGateway.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Modules.Account.Application.Gateways;
|
||||||
|
|
||||||
|
public interface IAccountGateway
|
||||||
|
{
|
||||||
|
public Task<Models.Account?> TryGetByEmail(string email);
|
||||||
|
public Task<Models.Account> GetByEmail(string email);
|
||||||
|
public Task<Guid> Create(Models.Account account);
|
||||||
|
public Task<bool> Update(Guid id, Models.Account account);
|
||||||
|
public Task<bool> IsExists(string email);
|
||||||
|
public Task<bool> Delete(Guid id);
|
||||||
|
}
|
||||||
8
Modules.Account.Application/Models/Account.cs
Normal file
8
Modules.Account.Application/Models/Account.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Modules.Account.Application.Models;
|
||||||
|
|
||||||
|
public class Account
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Email { get; set; } = default!;
|
||||||
|
public string HashedPassword { get; set; } = default!;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MediatR" Version="12.4.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Modules.Account.Database\Modules.Account.Database.csproj" />
|
||||||
|
<ProjectReference Include="..\Modules.Account.Domain\Modules.Account.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
20
Modules.Account.Application/PasswordHasher.cs
Normal file
20
Modules.Account.Application/PasswordHasher.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Modules.Account.Domain.Gateways;
|
||||||
|
|
||||||
|
namespace Modules.Account.Application;
|
||||||
|
|
||||||
|
public class PasswordHasher : IPasswordHasher
|
||||||
|
{
|
||||||
|
private readonly PasswordHasher<Domain.Entities.Account> _hasher;
|
||||||
|
|
||||||
|
public PasswordHasher()
|
||||||
|
{
|
||||||
|
_hasher = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string HashPassword(Domain.Entities.Account account, string password) =>
|
||||||
|
_hasher.HashPassword(account, password);
|
||||||
|
|
||||||
|
public bool VerifyPassword(Domain.Entities.Account account, string password) =>
|
||||||
|
_hasher.VerifyHashedPassword(account, password, account.HashedPassword) != PasswordVerificationResult.Failed;
|
||||||
|
}
|
||||||
24
Modules.Account.Application/Queries/GetAccountQuery.cs
Normal file
24
Modules.Account.Application/Queries/GetAccountQuery.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Account.Application.Gateways;
|
||||||
|
using Modules.Account.Domain.Gateways;
|
||||||
|
|
||||||
|
namespace Modules.Account.Application.Queries;
|
||||||
|
|
||||||
|
public class GetAccountQuery : IRequest<Models.Account>
|
||||||
|
{
|
||||||
|
public string Email { get; set; } = default!;
|
||||||
|
public string Password { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetAccountQueryHandler(IAccountGateway accountGateway, IPasswordHasher passwordHasher) : IRequestHandler<GetAccountQuery, Models.Account>
|
||||||
|
{
|
||||||
|
const string error = "Invalid email or password";
|
||||||
|
public async Task<Models.Account> Handle(GetAccountQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var account = await accountGateway.TryGetByEmail(request.Email);
|
||||||
|
if (account == null) throw new Exception(error);
|
||||||
|
if (passwordHasher.VerifyPassword(new Domain.Entities.Account(account.Id, account.Email, account.HashedPassword), request.Password))
|
||||||
|
throw new Exception(error);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Modules.Account.Database/Class1.cs
Normal file
7
Modules.Account.Database/Class1.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Modules.Account.Database
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Modules.Account.Database/Modules.Account.Database.csproj
Normal file
9
Modules.Account.Database/Modules.Account.Database.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
45
Modules.Account.Domain/Entities/Account.cs
Normal file
45
Modules.Account.Domain/Entities/Account.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Modules.Account.Domain.Gateways;
|
||||||
|
|
||||||
|
namespace Modules.Account.Domain.Entities;
|
||||||
|
|
||||||
|
public class Account
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Email Email { get; private set; }
|
||||||
|
public string HashedPassword { get; private set; }
|
||||||
|
|
||||||
|
public Account(Guid id, string email, string hashedPassword)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Email = new Email(email);
|
||||||
|
HashedPassword = hashedPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Account(Email email)
|
||||||
|
{
|
||||||
|
Email = email;
|
||||||
|
HashedPassword = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Account Create(string email, string password, IPasswordHasher passwordHasher)
|
||||||
|
{
|
||||||
|
var account = new Account(new Email(email));
|
||||||
|
var a = passwordHasher.HashPassword(account, password);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPassword(string email)
|
||||||
|
{
|
||||||
|
//var newPasswordHash = passwordHasher.HashPassword(password);
|
||||||
|
//if (newPasswordHash != HashedPassword) throw new Exception("Password must not be aqual to previous");
|
||||||
|
if (string.IsNullOrWhiteSpace(email)) throw new Exception("Email is empty");
|
||||||
|
Email = new Email(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPassword(string password, IPasswordHasher passwordHasher)
|
||||||
|
{
|
||||||
|
var newPasswordHash = passwordHasher.HashPassword(this, password);
|
||||||
|
if (newPasswordHash != HashedPassword) throw new Exception("Password must not be aqual to previous");
|
||||||
|
HashedPassword = newPasswordHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Modules.Account.Domain/Entities/Email.cs
Normal file
11
Modules.Account.Domain/Entities/Email.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Modules.Account.Domain.Entities;
|
||||||
|
|
||||||
|
public class Email
|
||||||
|
{
|
||||||
|
public string Value { get; private set; }
|
||||||
|
|
||||||
|
public Email(string value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Modules.Account.Domain/Gateways/IPasswordHasher.cs
Normal file
7
Modules.Account.Domain/Gateways/IPasswordHasher.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Modules.Account.Domain.Gateways;
|
||||||
|
|
||||||
|
public interface IPasswordHasher
|
||||||
|
{
|
||||||
|
public string HashPassword(Entities.Account account, string password);
|
||||||
|
public bool VerifyPassword(Entities.Account account, string password);
|
||||||
|
}
|
||||||
9
Modules.Account.Domain/Modules.Account.Domain.csproj
Normal file
9
Modules.Account.Domain/Modules.Account.Domain.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@ -12,8 +12,8 @@ public class EditDescriptionCommand : IRequest<Unit>
|
|||||||
public Guid LanguageId { get; set; }
|
public Guid LanguageId { get; set; }
|
||||||
public string Value { get; set; } = default!;
|
public string Value { get; set; } = default!;
|
||||||
public bool IsOriginal { get; set; }
|
public bool IsOriginal { get; set; }
|
||||||
public string NewValue { get; set; } = default!;
|
public Guid? NewLanguageId { get; set; }
|
||||||
|
public string? NewValue { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EditDescriptionCommandHandler(IAnimeTitleGateway titleGateway, ILanguageGateway languageGateway) : IRequestHandler<EditDescriptionCommand, Unit>
|
public class EditDescriptionCommandHandler(IAnimeTitleGateway titleGateway, ILanguageGateway languageGateway) : IRequestHandler<EditDescriptionCommand, Unit>
|
||||||
@ -27,7 +27,7 @@ public class EditDescriptionCommandHandler(IAnimeTitleGateway titleGateway, ILan
|
|||||||
var episode = season.Episodes.FirstOrDefault(q => q.Id == request.EpisodeId) ??
|
var episode = season.Episodes.FirstOrDefault(q => q.Id == request.EpisodeId) ??
|
||||||
throw new Exception("Episode not found");
|
throw new Exception("Episode not found");
|
||||||
var description = new Domain.Entities.MediaContent.CommonProperties.Description(request.LanguageId, request.IsOriginal, request.Value);
|
var description = new Domain.Entities.MediaContent.CommonProperties.Description(request.LanguageId, request.IsOriginal, request.Value);
|
||||||
episode.CommonProperties.SetDescriptionValue(description, request.NewValue);
|
episode.CommonProperties.SetDescriptionValue(description, request.NewLanguageId, request.NewValue);
|
||||||
await titleGateway.Update(title);
|
await titleGateway.Update(title);
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,20 +13,24 @@ public class EditNameCommand : IRequest<Unit>
|
|||||||
public CommonModels.NameType NameType { get; set; }
|
public CommonModels.NameType NameType { get; set; }
|
||||||
public Guid LanguageId { get; set; }
|
public Guid LanguageId { get; set; }
|
||||||
public string Value { get; set; } = default!;
|
public string Value { get; set; } = default!;
|
||||||
public string NewValue { get; set; } = default!;
|
public string? NewValue { get; set; }
|
||||||
|
public Guid? NewLanguageId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EditNameCommandHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<EditNameCommand, Unit>
|
public class EditNameCommandHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<EditNameCommand, Unit>
|
||||||
{
|
{
|
||||||
public async Task<Unit> Handle(EditNameCommand request, CancellationToken cancellationToken)
|
public async Task<Unit> Handle(EditNameCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
if (!request.NewLanguageId.HasValue && string.IsNullOrWhiteSpace(request.NewValue))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException($"{nameof(EditNameCommand.NewLanguageId)}, {nameof(EditNameCommand.NewValue)}");
|
||||||
|
}
|
||||||
var title = await titleGateway.GetById(request.TitleId);
|
var title = await titleGateway.GetById(request.TitleId);
|
||||||
var season = title.Items.OfType<AnimeSeason>().FirstOrDefault(q => q.Id == request.SeasonId) ??
|
var season = title.Items.OfType<AnimeSeason>().FirstOrDefault(q => q.Id == request.SeasonId) ??
|
||||||
throw new Exception("Season not found");
|
throw new Exception("Season not found");
|
||||||
var episode = season.Episodes.FirstOrDefault(q => q.Id == request.EpisodeId) ??
|
var episode = season.Episodes.FirstOrDefault(q => q.Id == request.EpisodeId) ??
|
||||||
throw new Exception("Episode not found");
|
throw new Exception("Episode not found");
|
||||||
episode.CommonProperties.SetNameValue(new NameItem((NameType)request.NameType, request.LanguageId, request.Value), request.NewValue);
|
episode.CommonProperties.SetNameValue(new NameItem((NameType)request.NameType, request.LanguageId, request.Value), request.NewLanguageId, request.NewValue);
|
||||||
await titleGateway.Update(title);
|
await titleGateway.Update(title);
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,8 @@ public class EditDescriptionCommand : IRequest<Unit>
|
|||||||
public Guid LanguageId { get; set; }
|
public Guid LanguageId { get; set; }
|
||||||
public string Value { get; set; } = default!;
|
public string Value { get; set; } = default!;
|
||||||
public bool IsOriginal { get; set; }
|
public bool IsOriginal { get; set; }
|
||||||
public string NewValue { get; set; } = default!;
|
public string? NewValue { get; set; }
|
||||||
|
public Guid? NewLanguageId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EditDescriptionCommandHandler(IAnimeTitleGateway titleGateway, ILanguageGateway languageGateway) : IRequestHandler<EditDescriptionCommand, Unit>
|
public class EditDescriptionCommandHandler(IAnimeTitleGateway titleGateway, ILanguageGateway languageGateway) : IRequestHandler<EditDescriptionCommand, Unit>
|
||||||
@ -24,7 +24,7 @@ public class EditDescriptionCommandHandler(IAnimeTitleGateway titleGateway, ILan
|
|||||||
var description = new Domain.Entities.MediaContent.CommonProperties.Description(request.LanguageId, request.IsOriginal, request.Value);
|
var description = new Domain.Entities.MediaContent.CommonProperties.Description(request.LanguageId, request.IsOriginal, request.Value);
|
||||||
var season = title.Items.OfType<AnimeSeason>().FirstOrDefault(q => q.Id == request.SeasonId) ??
|
var season = title.Items.OfType<AnimeSeason>().FirstOrDefault(q => q.Id == request.SeasonId) ??
|
||||||
throw new Exception("Season not found");
|
throw new Exception("Season not found");
|
||||||
season.CommonProperties.SetDescriptionValue(description, request.NewValue);
|
season.CommonProperties.SetDescriptionValue(description, request.NewLanguageId, request.NewValue);
|
||||||
await titleGateway.Update(title);
|
await titleGateway.Update(title);
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,18 +12,22 @@ public class EditNameCommand : IRequest<Unit>
|
|||||||
public CommonModels.NameType NameType { get; set; }
|
public CommonModels.NameType NameType { get; set; }
|
||||||
public Guid LanguageId { get; set; }
|
public Guid LanguageId { get; set; }
|
||||||
public string Value { get; set; } = default!;
|
public string Value { get; set; } = default!;
|
||||||
public string NewValue { get; set; } = default!;
|
public string? NewValue { get; set; }
|
||||||
|
public Guid? NewLanguageId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EditNameCommandHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<EditNameCommand, Unit>
|
public class EditNameCommandHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<EditNameCommand, Unit>
|
||||||
{
|
{
|
||||||
public async Task<Unit> Handle(EditNameCommand request, CancellationToken cancellationToken)
|
public async Task<Unit> Handle(EditNameCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
if (!request.NewLanguageId.HasValue && string.IsNullOrWhiteSpace(request.NewValue))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException($"{nameof(EditNameCommand.NewLanguageId)}, {nameof(EditNameCommand.NewValue)}");
|
||||||
|
}
|
||||||
var title = await titleGateway.GetById(request.TitleId);
|
var title = await titleGateway.GetById(request.TitleId);
|
||||||
var season = title.Items.OfType<AnimeSeason>().FirstOrDefault(q => q.Id == request.SeasonId) ??
|
var season = title.Items.OfType<AnimeSeason>().FirstOrDefault(q => q.Id == request.SeasonId) ??
|
||||||
throw new Exception("Season not found");
|
throw new Exception("Season not found");
|
||||||
season.CommonProperties.SetNameValue(new NameItem((NameType)request.NameType, request.LanguageId, request.Value), request.NewValue);
|
season.CommonProperties.SetNameValue(new NameItem((NameType)request.NameType, request.LanguageId, request.Value), request.NewLanguageId, request.NewValue);
|
||||||
await titleGateway.Update(title);
|
await titleGateway.Update(title);
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Modules.Library.Application.Gateways;
|
using Modules.Library.Application.Gateways;
|
||||||
|
using Modules.Library.Application.Models;
|
||||||
|
|
||||||
namespace Modules.Library.Application.Commands.Anime.Title;
|
namespace Modules.Library.Application.Commands.Anime.Title;
|
||||||
|
|
||||||
@ -7,6 +8,7 @@ public class CreateAnimeTitleCommand : IRequest<Guid>
|
|||||||
{
|
{
|
||||||
public string NameOriginal { get; set; } = default!;
|
public string NameOriginal { get; set; } = default!;
|
||||||
public Guid NameOriginalLanguageId { get; set; }
|
public Guid NameOriginalLanguageId { get; set; }
|
||||||
|
public MediaInfo? Preview { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateAnimtTitleCommandHandler(IAnimeTitleGateway titleGateway, ILanguageGateway languageGateway) : IRequestHandler<CreateAnimeTitleCommand, Guid>
|
public class CreateAnimtTitleCommandHandler(IAnimeTitleGateway titleGateway, ILanguageGateway languageGateway) : IRequestHandler<CreateAnimeTitleCommand, Guid>
|
||||||
@ -15,6 +17,7 @@ public class CreateAnimtTitleCommandHandler(IAnimeTitleGateway titleGateway, ILa
|
|||||||
{
|
{
|
||||||
var language = await languageGateway.GetLanguageById(request.NameOriginalLanguageId);
|
var language = await languageGateway.GetLanguageById(request.NameOriginalLanguageId);
|
||||||
var animeTitle = new Domain.Entities.MediaContent.Items.Anime.AnimeTitle(language.Id, request.NameOriginal);
|
var animeTitle = new Domain.Entities.MediaContent.Items.Anime.AnimeTitle(language.Id, request.NameOriginal);
|
||||||
|
if (request.Preview != null) animeTitle.CommonProperties.SetPreview(request.Preview.Url, (Domain.Entities.MediaInfoType)request.Preview.Type);
|
||||||
return await titleGateway.Create(animeTitle);
|
return await titleGateway.Create(animeTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9,7 +9,8 @@ public class EditDescriptionCommand : IRequest<Unit>
|
|||||||
public Guid LanguageId { get; set; }
|
public Guid LanguageId { get; set; }
|
||||||
public string Value { get; set; } = default!;
|
public string Value { get; set; } = default!;
|
||||||
public bool IsOriginal { get; set; }
|
public bool IsOriginal { get; set; }
|
||||||
public string NewValue { get; set; } = default!;
|
public Guid? NewLanguageId { get; set; }
|
||||||
|
public string? NewValue { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ public class EditDescriptionCommandHandler(IAnimeTitleGateway titleGateway, ILan
|
|||||||
var title = await titleGateway.GetById(request.TitleId);
|
var title = await titleGateway.GetById(request.TitleId);
|
||||||
if (!await languageGateway.IsLanguageExists(request.LanguageId)) throw new Exception();
|
if (!await languageGateway.IsLanguageExists(request.LanguageId)) throw new Exception();
|
||||||
var description = new Domain.Entities.MediaContent.CommonProperties.Description(request.LanguageId, request.IsOriginal, request.Value);
|
var description = new Domain.Entities.MediaContent.CommonProperties.Description(request.LanguageId, request.IsOriginal, request.Value);
|
||||||
title.CommonProperties.SetDescriptionValue(description, request.NewValue);
|
title.CommonProperties.SetDescriptionValue(description, request.NewLanguageId, request.NewValue);
|
||||||
await titleGateway.Update(title);
|
await titleGateway.Update(title);
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,16 +10,20 @@ public class EditNameCommand : IRequest<Unit>
|
|||||||
public CommonModels.NameType NameType { get; set; }
|
public CommonModels.NameType NameType { get; set; }
|
||||||
public Guid LanguageId { get; set; }
|
public Guid LanguageId { get; set; }
|
||||||
public string Value { get; set; } = default!;
|
public string Value { get; set; } = default!;
|
||||||
public string NewValue { get; set; } = default!;
|
public Guid? NewLanguageId { get; set; }
|
||||||
|
public string? NewValue { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EditNameCommandHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<EditNameCommand, Unit>
|
public class EditNameCommandHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<EditNameCommand, Unit>
|
||||||
{
|
{
|
||||||
public async Task<Unit> Handle(EditNameCommand request, CancellationToken cancellationToken)
|
public async Task<Unit> Handle(EditNameCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
if (!request.NewLanguageId.HasValue && string.IsNullOrWhiteSpace(request.NewValue))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException($"{nameof(EditNameCommand.NewLanguageId)}, {nameof(EditNameCommand.NewValue)}");
|
||||||
|
}
|
||||||
var title = await titleGateway.GetById(request.TitleId);
|
var title = await titleGateway.GetById(request.TitleId);
|
||||||
title.CommonProperties.SetNameValue(new NameItem((NameType)request.NameType, request.LanguageId, request.Value), request.NewValue);
|
title.CommonProperties.SetNameValue(new NameItem((NameType)request.NameType, request.LanguageId, request.Value), request.NewLanguageId, request.NewValue);
|
||||||
await titleGateway.Update(title);
|
await titleGateway.Update(title);
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Library.Application.Gateways;
|
||||||
|
using Modules.Rating.Api.Commands;
|
||||||
|
|
||||||
|
namespace Modules.Library.Application.Commands.Anime.Title;
|
||||||
|
|
||||||
|
public class RateTitleCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public Guid TitleId { get; set; }
|
||||||
|
public ushort RatePercentage { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RateTitleCommandHandler(IAnimeTitleGateway titleGateway, IMediator mediator) : IRequestHandler<RateTitleCommand, Unit>
|
||||||
|
{
|
||||||
|
public async Task<Unit> Handle(RateTitleCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var subjectId = new Guid("8393230f-78e3-473b-a5dc-3221917e0aeb"); //user
|
||||||
|
var title = await titleGateway.GetById(request.TitleId);
|
||||||
|
if (title != null && !title.Deleted)
|
||||||
|
{
|
||||||
|
await mediator.Send(new RateObjectCommand { ObjectId = title.Id, SubjectId = subjectId, RatePercentage = request.RatePercentage });
|
||||||
|
}
|
||||||
|
return Unit.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Library.Application.Gateways;
|
||||||
|
using Modules.Rating.Api.Commands;
|
||||||
|
|
||||||
|
namespace Modules.Library.Application.Commands.Anime.Title;
|
||||||
|
|
||||||
|
public class UnrateTitleCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public Guid TitleId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UnrateTitleCommandHandler(IAnimeTitleGateway titleGateway, IMediator mediator) : IRequestHandler<UnrateTitleCommand, Unit>
|
||||||
|
{
|
||||||
|
public async Task<Unit> Handle(UnrateTitleCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var subjectId = new Guid("8393230f-78e3-473b-a5dc-3221917e0aeb"); //user
|
||||||
|
var title = await titleGateway.GetById(request.TitleId);
|
||||||
|
if (title != null && !title.Deleted)
|
||||||
|
{
|
||||||
|
await mediator.Send(new UnrateObjectCommand { ObjectId = title.Id, SubjectId = subjectId });
|
||||||
|
}
|
||||||
|
return Unit.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,4 +3,5 @@ public class MediaInfo
|
|||||||
{
|
{
|
||||||
public MediaInfoType Type { get; set; }
|
public MediaInfoType Type { get; set; }
|
||||||
public string Url { get; set; } = default!;
|
public string Url { get; set; } = default!;
|
||||||
|
public string ContentType { get; set; } = default!;
|
||||||
}
|
}
|
||||||
@ -8,11 +8,12 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MediatR" Version="12.4.1" />
|
<PackageReference Include="MediatR" Version="12.4.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Modules.Library.Domain\Modules.Library.Domain.csproj" />
|
<ProjectReference Include="..\Modules.Library.Domain\Modules.Library.Domain.csproj" />
|
||||||
|
<ProjectReference Include="..\Modules.Rating.Api\Modules.Rating.Api.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -1,17 +1,26 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Modules.Library.Application.Gateways;
|
using Modules.Library.Application.Gateways;
|
||||||
|
using Modules.Rating.Api.Querirs;
|
||||||
|
|
||||||
namespace Modules.Library.Application.Queries.Anime.AnimeTitle;
|
namespace Modules.Library.Application.Queries.Anime.AnimeTitle;
|
||||||
|
|
||||||
public class AnimeTitleListQuery : IRequest<List<Models.Anime.Title>>
|
public class AnimeTitleListQuery : IRequest<List<Models.Anime.Title>>
|
||||||
{
|
{
|
||||||
|
public Guid? UserId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AnimeTitleListQueryHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<AnimeTitleListQuery, List<Models.Anime.Title>>
|
public class AnimeTitleListQueryHandler(IAnimeTitleGateway titleGateway, IMediator mediator) : IRequestHandler<AnimeTitleListQuery, List<Models.Anime.Title>>
|
||||||
{
|
{
|
||||||
public async Task<List<Models.Anime.Title>> Handle(AnimeTitleListQuery request, CancellationToken cancellationToken)
|
public async Task<List<Models.Anime.Title>> Handle(AnimeTitleListQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await titleGateway.GetList();
|
var titles = await titleGateway.GetList();
|
||||||
|
var rates = await mediator.Send(new ObjectRatingListQuery { ObjectIds = titles.Select(q => q.Id), SubjectId = request.UserId, });
|
||||||
|
rates.ForEach(q =>
|
||||||
|
{
|
||||||
|
var title = titles.First(x => x.Id == q.ObjectId);
|
||||||
|
title.Rate = q.ObjectRatePercentage;
|
||||||
|
title.MyRate = q.SubjectRatePercentage;
|
||||||
|
});
|
||||||
|
return titles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Modules.Library.Application.Gateways;
|
using Modules.Library.Application.Gateways;
|
||||||
|
using Modules.Rating.Api.Querirs;
|
||||||
|
|
||||||
namespace Modules.Library.Application.Queries.Anime.AnimeTitle;
|
namespace Modules.Library.Application.Queries.Anime.AnimeTitle;
|
||||||
|
|
||||||
@ -8,10 +9,15 @@ public class AnimeTitleQuery : IRequest<Models.Anime.Title>
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AnimeTitleQueryHandler(IAnimeTitleGateway titleGateway) : IRequestHandler<AnimeTitleQuery, Models.Anime.Title>
|
public class AnimeTitleQueryHandler(IAnimeTitleGateway titleGateway, IMediator mediator) : IRequestHandler<AnimeTitleQuery, Models.Anime.Title>
|
||||||
{
|
{
|
||||||
public async Task<Models.Anime.Title> Handle(AnimeTitleQuery request, CancellationToken cancellationToken)
|
public async Task<Models.Anime.Title> Handle(AnimeTitleQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await titleGateway.GetDetail(request.Id);
|
var title = await titleGateway.GetDetail(request.Id);
|
||||||
|
//var rate = await mediator.Send(new ObjectRatingQuery { ObjectId = request.Id, SubjectId = null, });
|
||||||
|
var rate = await mediator.Send(new ObjectRatingQuery { ObjectId = request.Id, SubjectId = new Guid("8393230f-78e3-473b-a5dc-3221917e0aeb"), });
|
||||||
|
title.Rate = rate?.ObjectRatePercentage;
|
||||||
|
title.MyRate = rate?.SubjectRatePercentage;
|
||||||
|
return title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Modules.Rating.Api;
|
||||||
|
|
||||||
namespace Modules.Library.Application;
|
namespace Modules.Library.Application;
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ public class MediaInfo : Entity
|
|||||||
{
|
{
|
||||||
public MediaInfoType Type { get; set; } = MediaInfoType.OtherFile;
|
public MediaInfoType Type { get; set; } = MediaInfoType.OtherFile;
|
||||||
public string Url { get; set; } = default!;
|
public string Url { get; set; } = default!;
|
||||||
|
//public string ContentType { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MediaInfoType
|
public enum MediaInfoType
|
||||||
|
|||||||
@ -14,8 +14,7 @@ internal class CommonPropertiesConverter(LanguageRepository languageRepository,
|
|||||||
commonPropertiesLanguageIds.AddRange(commonProperties.Descriptions.Select(q => q.LanguageId));
|
commonPropertiesLanguageIds.AddRange(commonProperties.Descriptions.Select(q => q.LanguageId));
|
||||||
|
|
||||||
var languages = await languageRepository.GetWhere(q => commonPropertiesLanguageIds.Distinct().Contains(q.Id));
|
var languages = await languageRepository.GetWhere(q => commonPropertiesLanguageIds.Distinct().Contains(q.Id));
|
||||||
var genres = await genreRepository.GetWhere(q => commonProperties.Genres.Select(q => q.Id).Distinct().Contains(q.Id));
|
var genres = await genreRepository.GetWhere(q => commonProperties.Genres.Select(x => x.GenreId).Distinct().Contains(q.Id));
|
||||||
|
|
||||||
|
|
||||||
return new Application.Models.CommonProperties
|
return new Application.Models.CommonProperties
|
||||||
{
|
{
|
||||||
@ -29,6 +28,9 @@ internal class CommonPropertiesConverter(LanguageRepository languageRepository,
|
|||||||
{
|
{
|
||||||
Url = commonProperties.Preview.Url,
|
Url = commonProperties.Preview.Url,
|
||||||
Type = (Application.Models.MediaInfoType)commonProperties.Preview.Type,
|
Type = (Application.Models.MediaInfoType)commonProperties.Preview.Type,
|
||||||
|
//ContentType = commonProperties.Preview.ContentType,
|
||||||
|
ContentType = "image/png",
|
||||||
|
|
||||||
},
|
},
|
||||||
Descriptions = commonProperties.Descriptions.Select(q => new Application.Models.Description
|
Descriptions = commonProperties.Descriptions.Select(q => new Application.Models.Description
|
||||||
{
|
{
|
||||||
@ -90,6 +92,7 @@ internal class CommonPropertiesConverter(LanguageRepository languageRepository,
|
|||||||
Descriptions = commonProperties.Descriptions.Select(q => new DescriptionItem
|
Descriptions = commonProperties.Descriptions.Select(q => new DescriptionItem
|
||||||
{
|
{
|
||||||
IsOriginal = q.IsOriginal,
|
IsOriginal = q.IsOriginal,
|
||||||
|
LanguageId = q.LanguageId,
|
||||||
Value = q.Value,
|
Value = q.Value,
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
Genres = commonProperties.Genres.Select(q => new GenreProportionItem
|
Genres = commonProperties.Genres.Select(q => new GenreProportionItem
|
||||||
|
|||||||
@ -62,5 +62,7 @@ public class GenreGateway(GenreRepository repository) : IGenreGateway
|
|||||||
public Task<bool> IsGenreExists(Guid id) => repository.AnyWhere(q => q.Id == id && !q.Deleted);
|
public Task<bool> IsGenreExists(Guid id) => repository.AnyWhere(q => q.Id == id && !q.Deleted);
|
||||||
|
|
||||||
public Task<bool> IsGenreExists(string name, Guid? selfId) =>
|
public Task<bool> IsGenreExists(string name, Guid? selfId) =>
|
||||||
repository.AnyWhere(q => q.Id != selfId && q.Name == name.Trim() && !q.Deleted);
|
repository.AnyWhere(q => q.Id != selfId
|
||||||
|
&& q.Name.ToLower() == name.Trim().ToLower()
|
||||||
|
&& !q.Deleted);
|
||||||
}
|
}
|
||||||
@ -7,8 +7,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
|
<PackageReference Include="MongoDB.Driver" Version="2.30.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -100,13 +100,17 @@ public class CommonProperties : ValueObject
|
|||||||
_names.Remove(name);
|
_names.Remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetNameValue(NameItem nameItem, string value)
|
public void SetNameValue(NameItem nameItem, Guid? languageId, string? value)
|
||||||
{
|
{
|
||||||
var name = GetName(nameItem);
|
var name = GetName(nameItem);
|
||||||
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value));
|
var newValue = value?.Trim();
|
||||||
if (nameItem.Type != NameType.Original && _names.Any(q => q.LanguageId == nameItem.LanguageId && q.Value == value))
|
if (string.IsNullOrWhiteSpace(newValue)) throw new ArgumentNullException(nameof(value));
|
||||||
throw new Exception("Name item with in same language with same value is already exists");
|
if (nameItem.Type != NameType.Original && _names.Any(q => q.LanguageId == (languageId ?? nameItem.LanguageId) && q.Value == (newValue ?? nameItem.Value)))
|
||||||
nameItem.SetValue(value);
|
throw new Exception("Name item with same language and same value is already exists");
|
||||||
|
else if (nameItem.Type == NameType.Original && languageId.HasValue && _names.Any(q => q.LanguageId == languageId && q.Type == NameType.OriginalInAnotherLanguage))
|
||||||
|
throw new Exception("Could not change original name language to one of \"original in another language\" item's one");
|
||||||
|
if (languageId.HasValue) name.SetLanguage(languageId.Value);
|
||||||
|
if (!string.IsNullOrWhiteSpace(newValue)) name.SetValue(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NameItem GetName(NameItem name) => _names.FirstOrDefault(q => q == name) ??
|
private NameItem GetName(NameItem name) => _names.FirstOrDefault(q => q == name) ??
|
||||||
@ -144,13 +148,15 @@ public class CommonProperties : ValueObject
|
|||||||
//description.SetDeleted();
|
//description.SetDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDescriptionValue(Description descriptionItem, string value)
|
public void SetDescriptionValue(Description descriptionItem, Guid? languageId, string? value)
|
||||||
{
|
{
|
||||||
var description = GetDescription(descriptionItem);
|
var description = GetDescription(descriptionItem);
|
||||||
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value));
|
var newValue = value?.Trim();
|
||||||
if (!descriptionItem.IsOriginal && _descriptions.Any(q => q.LanguageId == descriptionItem.LanguageId && q.Value == value))
|
if (string.IsNullOrWhiteSpace(newValue)) throw new ArgumentNullException(nameof(value));
|
||||||
|
if (!descriptionItem.IsOriginal && _descriptions.Any(q => q.LanguageId == (languageId ?? descriptionItem.LanguageId) && q.Value == (newValue ?? descriptionItem.Value)))
|
||||||
throw new Exception("Descriptoin item with with same value is already exists");
|
throw new Exception("Descriptoin item with with same value is already exists");
|
||||||
descriptionItem.SetValue(value);
|
if (languageId.HasValue) descriptionItem.SetLanguage(languageId.Value);
|
||||||
|
if (!string.IsNullOrWhiteSpace(newValue)) descriptionItem.SetValue(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Description GetDescription(Description description)
|
private Description GetDescription(Description description)
|
||||||
@ -240,14 +246,15 @@ public class CommonProperties : ValueObject
|
|||||||
|
|
||||||
public void SetEstimatedReleaseDate(DateTimeOffset? value)
|
public void SetEstimatedReleaseDate(DateTimeOffset? value)
|
||||||
{
|
{
|
||||||
if (value == default) throw new ArgumentNullException(nameof(value));
|
//if (value == default) throw new ArgumentNullException(nameof(value));
|
||||||
|
if (value.HasValue && value.Value == default) throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
if (AnnouncementDate.HasValue && value <= AnnouncementDate.Value)
|
if (AnnouncementDate.HasValue && value <= AnnouncementDate.Value)
|
||||||
throw new Exception("Estimated release date can not be less or equal to announcement date");
|
throw new Exception("Estimated release date can not be less or equal to announcement date");
|
||||||
EstimatedReleaseDate = value;
|
EstimatedReleaseDate = value;
|
||||||
}
|
}
|
||||||
public void SetReleaseDate(DateTimeOffset? value)
|
public void SetReleaseDate(DateTimeOffset? value)
|
||||||
{
|
{
|
||||||
if (value == default) throw new ArgumentNullException(nameof(value));
|
if (value.HasValue && value.Value == default) throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
if (AnnouncementDate.HasValue && value <= AnnouncementDate.Value)
|
if (AnnouncementDate.HasValue && value <= AnnouncementDate.Value)
|
||||||
throw new Exception("Release date can not be less or equal to announcement date");
|
throw new Exception("Release date can not be less or equal to announcement date");
|
||||||
ReleaseDate = value;
|
ReleaseDate = value;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ public class Description : ValueObject
|
|||||||
public string Value { get; private set; } = string.Empty;
|
public string Value { get; private set; } = string.Empty;
|
||||||
public bool IsOriginal { get; init; }
|
public bool IsOriginal { get; init; }
|
||||||
[Required]
|
[Required]
|
||||||
public Guid LanguageId { get; init; } = default!;
|
public Guid LanguageId { get; private set; } = default!;
|
||||||
|
|
||||||
private Description() { }
|
private Description() { }
|
||||||
|
|
||||||
@ -28,6 +28,11 @@ public class Description : ValueObject
|
|||||||
Value = value;
|
Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetLanguage(Guid languageId)
|
||||||
|
{
|
||||||
|
LanguageId = languageId;
|
||||||
|
}
|
||||||
|
|
||||||
protected override IEnumerable<object?> GetEqualityComponents()
|
protected override IEnumerable<object?> GetEqualityComponents()
|
||||||
{
|
{
|
||||||
yield return Value;
|
yield return Value;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ public class NameItem : ValueObject
|
|||||||
public string Value { get; private set; } = string.Empty;
|
public string Value { get; private set; } = string.Empty;
|
||||||
public NameType Type { get; private init; }
|
public NameType Type { get; private init; }
|
||||||
[Required]
|
[Required]
|
||||||
public Guid LanguageId { get; private init; } = default!;
|
public Guid LanguageId { get; private set; } = default!;
|
||||||
|
|
||||||
private NameItem() { }
|
private NameItem() { }
|
||||||
|
|
||||||
@ -37,6 +37,11 @@ public class NameItem : ValueObject
|
|||||||
Value = value;
|
Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetLanguage(Guid languageId)
|
||||||
|
{
|
||||||
|
LanguageId = languageId;
|
||||||
|
}
|
||||||
|
|
||||||
protected override IEnumerable<object?> GetEqualityComponents()
|
protected override IEnumerable<object?> GetEqualityComponents()
|
||||||
{
|
{
|
||||||
yield return Type;
|
yield return Type;
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Modules.Library.WebApi.Models;
|
using Modules.Library.WebApi.Models;
|
||||||
using Modules.Library.WebApi.Models.Anime;
|
using Modules.Library.WebApi.Models.Views;
|
||||||
|
using Modules.Library.WebApi.Models.Views.Anime;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Automapper;
|
namespace Modules.Library.WebApi.Automapper;
|
||||||
|
|
||||||
@ -32,6 +34,8 @@ public class AnimeTitleMapprigProfile : Profile
|
|||||||
|
|
||||||
CreateMap<Application.Models.Anime.Title, Title>()
|
CreateMap<Application.Models.Anime.Title, Title>()
|
||||||
.ForMember(q => q.ExpirationTimeTicks, opt => opt.MapFrom(q => q.ExpirationTime.Ticks))
|
.ForMember(q => q.ExpirationTimeTicks, opt => opt.MapFrom(q => q.ExpirationTime.Ticks))
|
||||||
.ForMember(q => q.EpisodesInsideSeasonsCount, opt => opt.MapFrom(q => q.Seasons.SelectMany(q => q.Episodes).Count()));
|
.ForMember(q => q.EpisodesInsideSeasonsCount, opt => opt.MapFrom(q => q.Seasons
|
||||||
|
.SelectMany(q => q.Episodes).Count()));
|
||||||
|
//.SelectMany(q => q.Episodes.DefaultIfEmpty()).Count()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Modules.Library.WebApi.Models;
|
using Modules.Library.WebApi.Models;
|
||||||
using Modules.Library.WebApi.Models.Dictionary;
|
using Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Automapper;
|
namespace Modules.Library.WebApi.Automapper;
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
using Amazon.Runtime.Internal.Endpoints.StandardLibrary;
|
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Modules.Library.WebApi.Models;
|
using Modules.Library.WebApi.Models;
|
||||||
using Modules.Library.WebApi.Models.Anime;
|
using Modules.Library.WebApi.Models.Anime;
|
||||||
|
using Modules.Library.WebApi.Models.Anime.CommonProperties;
|
||||||
|
using Modules.Library.WebApi.Models.Views.Anime;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Controllers;
|
namespace Modules.Library.WebApi.Controllers;
|
||||||
|
|
||||||
@ -23,20 +25,41 @@ public class TitleController : ControllerBase
|
|||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("List")]
|
[HttpGet("List")]
|
||||||
public async Task<List<Title>> List() =>
|
public async Task<List<Title>> List() =>
|
||||||
_mapper.Map<List<Title>>(await _mediator.Send(new Application.Queries.Anime.AnimeTitle.AnimeTitleListQuery()));
|
_mapper.Map<List<Title>>(await _mediator.Send(new Application.Queries.Anime.AnimeTitle.AnimeTitleListQuery { UserId = new Guid("8393230f-78e3-473b-a5dc-3221917e0aeb") }));
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<Title> ById(Guid TitleId) =>
|
public async Task<Title> ById(Guid TitleId) =>
|
||||||
_mapper.Map<Title>(await _mediator.Send(new Application.Queries.Anime.AnimeTitle.AnimeTitleQuery { Id = TitleId }));
|
_mapper.Map<Title>(await _mediator.Send(new Application.Queries.Anime.AnimeTitle.AnimeTitleQuery { Id = TitleId }));
|
||||||
|
|
||||||
|
[HttpPost("Rate")]
|
||||||
|
public async Task Rate(RateEdit model) =>
|
||||||
|
await _mediator.Send(new Application.Commands.Anime.Title.RateTitleCommand
|
||||||
|
{
|
||||||
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
|
RatePercentage = model.RatePercentage ?? throw new ArgumentNullException(nameof(model.RatePercentage)),
|
||||||
|
});
|
||||||
|
|
||||||
|
[HttpPost("Unrate")]
|
||||||
|
public async Task Unrate(RateEdit model) =>
|
||||||
|
await _mediator.Send(new Application.Commands.Anime.Title.UnrateTitleCommand
|
||||||
|
{
|
||||||
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
|
});
|
||||||
|
|
||||||
[HttpPost("Create")]
|
[HttpPost("Create")]
|
||||||
public async Task<Guid> CreateTitle([FromQuery] Guid nameOriginalLanguageId, [FromQuery] string nameOriginal) =>
|
public async Task<Guid> CreateTitle(TitleCreate model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.CreateAnimeTitleCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.CreateAnimeTitleCommand
|
||||||
{
|
{
|
||||||
NameOriginalLanguageId = nameOriginalLanguageId,
|
NameOriginalLanguageId = model.OriginalName.LanguageId,
|
||||||
NameOriginal = nameOriginal,
|
NameOriginal = model.OriginalName.Value,
|
||||||
|
Preview = model.Preview == null ? null : new Application.Models.MediaInfo
|
||||||
|
{
|
||||||
|
Url = model.Preview.Url,
|
||||||
|
Type = (Application.Models.MediaInfoType)model.Preview.Type,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("AddSeason")]
|
[HttpPost("AddSeason")]
|
||||||
@ -69,86 +92,85 @@ public class TitleController : ControllerBase
|
|||||||
|
|
||||||
|
|
||||||
[HttpPost("AddName")]
|
[HttpPost("AddName")]
|
||||||
public async Task AddName([FromQuery] Guid titleId, [FromQuery] Guid languageId, [FromQuery] string name,
|
public async Task AddName(NameEdit model) =>
|
||||||
[FromQuery] NameType type) =>
|
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Name.AddNameCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Name.AddNameCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
LanguageId = languageId,
|
LanguageId = model.LanguageId,
|
||||||
NameType = (Application.Commands.CommonModels.NameType)type,
|
NameType = (Application.Commands.CommonModels.NameType)model.Type,
|
||||||
Value = name
|
Value = model.Value
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("EditName")]
|
[HttpPost("EditName")]
|
||||||
public async Task EditName([FromQuery] Guid titleId, [FromQuery] Guid languageId, [FromQuery] string name,
|
public async Task EditName(NameEdit model) =>
|
||||||
[FromQuery] NameType type) =>
|
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Name.EditNameCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Name.EditNameCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
LanguageId = languageId,
|
LanguageId = model.LanguageId,
|
||||||
NameType = (Application.Commands.CommonModels.NameType)type,
|
NameType = (Application.Commands.CommonModels.NameType)model.Type,
|
||||||
Value = name
|
Value = model.Value,
|
||||||
|
NewLanguageId = model.NewLanguageId,
|
||||||
|
NewValue = model.NewValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("DeleteName")]
|
[HttpPost("DeleteName")]
|
||||||
public async Task DeleteName([FromQuery] Guid titleId, [FromQuery] Guid languageId, [FromQuery] string name,
|
public async Task DeleteName(NameEdit model) =>
|
||||||
[FromQuery] NameType type) =>
|
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Name.DeleteNameCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Name.DeleteNameCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
LanguageId = languageId,
|
LanguageId = model.LanguageId,
|
||||||
NameType = (Application.Commands.CommonModels.NameType)type,
|
NameType = (Application.Commands.CommonModels.NameType)model.Type,
|
||||||
Value = name,
|
Value = model.Value,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
[HttpPost("SetPreview")]
|
[HttpPost("SetPreview")]
|
||||||
public async Task SetPreview([FromQuery] Guid titleId, [FromQuery] MediaInfoType type, [FromQuery] string url) =>
|
public async Task SetPreview(MediaInfoEdit model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Preview.SetPreviewCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Preview.SetPreviewCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
Type = (Application.Commands.CommonModels.MediaInfoType)type,
|
Type = (Application.Commands.CommonModels.MediaInfoType)model.Type,
|
||||||
Url = url,
|
Url = model.Url,
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("DeletePreview")]
|
[HttpPost("ClearPreview")]
|
||||||
public async Task DeletePreview([FromQuery] Guid titleId) =>
|
public async Task ClearPreview(MediaInfoEdit model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Preview.DeletePreviewCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Preview.DeletePreviewCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
[HttpPost("AddDescription")]
|
[HttpPost("AddDescription")]
|
||||||
public async Task AddDescription([FromQuery] Guid titleId, [FromQuery] bool isOriginal, [FromQuery] Guid languageId, [FromQuery] string value) =>
|
public async Task AddDescription(DescriptionEdit model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Description.AddDescriptionCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Description.AddDescriptionCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
IsOriginal = isOriginal,
|
IsOriginal = model.IsOriginal,
|
||||||
LanguageId = languageId,
|
LanguageId = model.LanguageId,
|
||||||
Value = value,
|
Value = model.Value,
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("EditDescription")]
|
[HttpPost("EditDescription")]
|
||||||
public async Task EditDescription([FromQuery] Guid titleId, bool isOriginal, [FromQuery] Guid languageId, [FromQuery] string value,
|
public async Task EditDescription(DescriptionEdit model) =>
|
||||||
[FromQuery] string newValue) =>
|
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Description.EditDescriptionCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Description.EditDescriptionCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
LanguageId = languageId,
|
LanguageId = model.LanguageId,
|
||||||
IsOriginal = isOriginal,
|
IsOriginal = model.IsOriginal,
|
||||||
Value = value,
|
Value = model.Value,
|
||||||
NewValue = newValue,
|
NewLanguageId = model.NewLanguageId,
|
||||||
|
NewValue = model.NewValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("DeleteDescription")]
|
[HttpPost("DeleteDescription")]
|
||||||
public async Task DeleteDescription([FromQuery] Guid titleId, bool isOriginal, [FromQuery] Guid languageId, [FromQuery] string value) =>
|
public async Task DeleteDescription(DescriptionEdit model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Description.DeleteDescriptionCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.Description.DeleteDescriptionCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
LanguageId = languageId,
|
LanguageId = model.LanguageId,
|
||||||
IsOriginal = isOriginal,
|
IsOriginal = model.IsOriginal,
|
||||||
Value = value,
|
Value = model.Value,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -229,26 +251,26 @@ public class TitleController : ControllerBase
|
|||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("SetAnnouncementDate")]
|
[HttpPost("SetAnnouncementDate")]
|
||||||
public async Task SetAnnouncementDate([FromQuery] Guid titleId, [FromQuery] DateTimeOffset? value) =>
|
public async Task SetAnnouncementDate(DateEdit model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.SetAnnouncementDateCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.SetAnnouncementDateCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
Value = value,
|
Value = model.Value.HasValue ? new DateTimeOffset(model.Value.Value.DateTime, TimeSpan.Zero) : null
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("SetEstimatedReleaseDate")]
|
[HttpPost("SetEstimatedReleaseDate")]
|
||||||
public async Task SetEstimatedReleaseDate([FromQuery] Guid titleId, [FromQuery] DateTimeOffset? value) =>
|
public async Task SetEstimatedReleaseDate(DateEdit model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.SetEstimatedReleaseDateCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.SetEstimatedReleaseDateCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
Value = value,
|
Value = model.Value.HasValue ? new DateTimeOffset(model.Value.Value.DateTime, TimeSpan.Zero) : null
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("SetReleaseDate")]
|
[HttpPost("SetReleaseDate")]
|
||||||
public async Task SetReleaseDate([FromQuery] Guid titleId, [FromQuery] DateTimeOffset? value) =>
|
public async Task SetReleaseDate(DateEdit model) =>
|
||||||
await _mediator.Send(new Application.Commands.Anime.Title.Properties.SetReleaseDateCommand
|
await _mediator.Send(new Application.Commands.Anime.Title.Properties.SetReleaseDateCommand
|
||||||
{
|
{
|
||||||
TitleId = titleId,
|
TitleId = model.AnimeTitleId ?? throw new ArgumentNullException(nameof(model.AnimeTitleId)),
|
||||||
Value = value,
|
Value = model.Value.HasValue ? new DateTimeOffset(model.Value.Value.DateTime, TimeSpan.Zero) : null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Modules.Library.WebApi.Models.Dictionary;
|
using Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Controllers;
|
namespace Modules.Library.WebApi.Controllers;
|
||||||
|
|
||||||
@ -9,6 +11,11 @@ namespace Modules.Library.WebApi.Controllers;
|
|||||||
[ApiExplorerSettings(GroupName = "GenreV1")]
|
[ApiExplorerSettings(GroupName = "GenreV1")]
|
||||||
[Route("Dictionaries/Genre")]
|
[Route("Dictionaries/Genre")]
|
||||||
//[Route("[controller]")]
|
//[Route("[controller]")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[ProducesResponseType(400, StatusCode = 400, Type = typeof(ProblemDetails))]
|
||||||
|
[ProducesResponseType(401, StatusCode = 401, Type = typeof(UnauthorizedResult))]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
//[Authorize]
|
||||||
public class GenreController : ControllerBase
|
public class GenreController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
@ -27,14 +34,17 @@ public class GenreController : ControllerBase
|
|||||||
_mapper.Map<List<Genre>>(await _mediator.Send(new Application.Queries.Dictionaries.Genre.GenreListQuery()));
|
_mapper.Map<List<Genre>>(await _mediator.Send(new Application.Queries.Dictionaries.Genre.GenreListQuery()));
|
||||||
|
|
||||||
[HttpPost("CreateGenre")]
|
[HttpPost("CreateGenre")]
|
||||||
public async Task<Guid> CreateGenre([FromQuery]string genreName) =>
|
[ProducesResponseType(typeof(Guid), (int)HttpStatusCode.OK)]
|
||||||
await _mediator.Send(new Application.Commands.Dictionaries.Genre.CreateGenreCommand { Name = genreName });
|
public async Task<IActionResult> CreateGenre([FromQuery] string genreName) =>
|
||||||
|
Ok(await _mediator.Send(new Application.Commands.Dictionaries.Genre.CreateGenreCommand { Name = genreName }));
|
||||||
|
|
||||||
[HttpPost("EditGenre")]
|
[HttpPost("EditGenre")]
|
||||||
public async Task EditGenre([FromQuery] Guid id, [FromQuery]string genreName) =>
|
[ProducesResponseType((int)HttpStatusCode.OK)]
|
||||||
await _mediator.Send(new Application.Commands.Dictionaries.Genre.SetGenreNameCommand { Id = id, Name = genreName });
|
public async Task<IActionResult> EditGenre([FromQuery] Guid id, [FromQuery]string genreName) =>
|
||||||
|
Ok(await _mediator.Send(new Application.Commands.Dictionaries.Genre.SetGenreNameCommand { Id = id, Name = genreName }));
|
||||||
|
|
||||||
[HttpPost("DeleteGenre")]
|
[HttpPost("DeleteGenre")]
|
||||||
public async Task DeleteGenre([FromQuery] Guid id) =>
|
[ProducesResponseType((int)HttpStatusCode.OK)]
|
||||||
await _mediator.Send(new Application.Commands.Dictionaries.Genre.DeleteGenreCommand { Id = id });
|
public async Task<IActionResult> DeleteGenre([FromQuery] Guid id) =>
|
||||||
|
Ok(await _mediator.Send(new Application.Commands.Dictionaries.Genre.DeleteGenreCommand { Id = id }));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Modules.Library.WebApi.Models.Dictionary;
|
using Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Controllers;
|
namespace Modules.Library.WebApi.Controllers;
|
||||||
|
|
||||||
@ -27,19 +27,19 @@ public class LanguageController : ControllerBase
|
|||||||
_mapper.Map<List<Language>>(await _mediator.Send(new Application.Queries.Dictionaries.Language.LanguageListQuery()));
|
_mapper.Map<List<Language>>(await _mediator.Send(new Application.Queries.Dictionaries.Language.LanguageListQuery()));
|
||||||
|
|
||||||
[HttpPost("Create")]
|
[HttpPost("Create")]
|
||||||
public async Task<Guid> CreateLanguage([FromQuery] string codeIso2, [FromQuery] string name) =>
|
public async Task<Guid> CreateLanguage([FromQuery] string codeIso3, [FromQuery] string name) =>
|
||||||
await _mediator.Send(new Application.Commands.Dictionaries.Language.CreateLanguageCommand
|
await _mediator.Send(new Application.Commands.Dictionaries.Language.CreateLanguageCommand
|
||||||
{
|
{
|
||||||
Code = codeIso2,
|
Code = codeIso3,
|
||||||
Name = name,
|
Name = name,
|
||||||
});
|
});
|
||||||
|
|
||||||
[HttpPost("Edit")]
|
[HttpPost("Edit")]
|
||||||
public async Task EditLanguage([FromQuery] Guid id, [FromQuery] string? codeIso2, [FromQuery]string? name) =>
|
public async Task EditLanguage([FromQuery] Guid id, [FromQuery] string? codeIso3, [FromQuery]string? name) =>
|
||||||
await _mediator.Send(new Application.Commands.Dictionaries.Language.SetLanguageNameCommand
|
await _mediator.Send(new Application.Commands.Dictionaries.Language.SetLanguageNameCommand
|
||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
Code = codeIso2,
|
Code = codeIso3,
|
||||||
Name = name,
|
Name = name,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Modules.Library.WebApi.Models.Anime.CommonProperties;
|
||||||
|
|
||||||
|
public class DateEdit
|
||||||
|
{
|
||||||
|
public Guid? AnimeTitleId { get; set; }
|
||||||
|
public Guid? AnimeSeasonId { get; set; }
|
||||||
|
public Guid? AnimeEpisodeId { get; set; }
|
||||||
|
public DateTimeOffset? Value { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
namespace Modules.Library.WebApi.Models.Anime.CommonProperties;
|
||||||
|
|
||||||
|
public class DescriptionEdit
|
||||||
|
{
|
||||||
|
public Guid? AnimeTitleId { get; set; }
|
||||||
|
public Guid? AnimeSeasonId { get; set; }
|
||||||
|
public Guid? AnimeEpisodeId { get; set; }
|
||||||
|
public Guid LanguageId { get; set; } = default!;
|
||||||
|
public string Value { get; set; } = default!;
|
||||||
|
public bool IsOriginal { get; set; }
|
||||||
|
public string? NewValue { get; set; }
|
||||||
|
public Guid? NewLanguageId { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
namespace Modules.Library.WebApi.Models.Anime.CommonProperties;
|
||||||
|
|
||||||
|
public class MediaInfoEdit
|
||||||
|
{
|
||||||
|
public Guid? AnimeTitleId { get; set; }
|
||||||
|
public Guid? AnimeSeasonId { get; set; }
|
||||||
|
public Guid? AnimeEpisodeId { get; set; }
|
||||||
|
public MediaInfoType Type { get; set; }
|
||||||
|
public string Url { get; set; } = default!;
|
||||||
|
public string ContentType { get; set; } = default!;
|
||||||
|
public MediaInfoType? NewType { get; set; }
|
||||||
|
public string? NewUrl { get; set; }
|
||||||
|
public string? NewContentType { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
namespace Modules.Library.WebApi.Models.Anime.CommonProperties;
|
||||||
|
|
||||||
|
public class NameEdit
|
||||||
|
{
|
||||||
|
public Guid? AnimeTitleId { get; set; }
|
||||||
|
public Guid? AnimeSeasonId { get; set; }
|
||||||
|
public Guid? AnimeEpisodeId { get; set; }
|
||||||
|
public Guid LanguageId { get; set; } = default!;
|
||||||
|
public string Value { get; set; } = default!;
|
||||||
|
public NameType Type { get; set; } = NameType.Original;
|
||||||
|
public string? NewValue { get; set; }
|
||||||
|
public Guid? NewLanguageId { get; set; }
|
||||||
|
}
|
||||||
8
Modules.Library.WebApi/Models/Anime/Language.cs
Normal file
8
Modules.Library.WebApi/Models/Anime/Language.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Modules.Library.WebApi.Models.Anime;
|
||||||
|
|
||||||
|
public class Name
|
||||||
|
{
|
||||||
|
public Guid LanguageId { get; set; }
|
||||||
|
public string Value { get; set; } = default!;
|
||||||
|
public NameType Type { get; set; } = NameType.Original;
|
||||||
|
}
|
||||||
9
Modules.Library.WebApi/Models/Anime/RateEdit.cs
Normal file
9
Modules.Library.WebApi/Models/Anime/RateEdit.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Modules.Library.WebApi.Models.Anime;
|
||||||
|
|
||||||
|
public class RateEdit
|
||||||
|
{
|
||||||
|
public Guid? AnimeTitleId { get; set; }
|
||||||
|
public Guid? AnimeSeasonId { get; set; }
|
||||||
|
public Guid? AnimeEpisodeId { get; set; }
|
||||||
|
public ushort? RatePercentage { get; set; }
|
||||||
|
}
|
||||||
9
Modules.Library.WebApi/Models/Anime/TitleCreate.cs
Normal file
9
Modules.Library.WebApi/Models/Anime/TitleCreate.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Modules.Library.WebApi.Models.Anime;
|
||||||
|
|
||||||
|
public class TitleCreate
|
||||||
|
{
|
||||||
|
public Name OriginalName { get; set; } = default!;
|
||||||
|
|
||||||
|
public MediaInfo? Preview { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
@ -3,4 +3,5 @@ public class MediaInfo
|
|||||||
{
|
{
|
||||||
public MediaInfoType Type { get; set; }
|
public MediaInfoType Type { get; set; }
|
||||||
public string Url { get; set; } = default!;
|
public string Url { get; set; } = default!;
|
||||||
|
public string ContentType { get; set; } = default!;
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Modules.Library.WebApi.Models.Anime;
|
namespace Modules.Library.WebApi.Models.Views.Anime;
|
||||||
|
|
||||||
public abstract class AnimeItem
|
public abstract class AnimeItem
|
||||||
{
|
{
|
||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Modules.Library.WebApi.Models.Anime;
|
namespace Modules.Library.WebApi.Models.Views.Anime;
|
||||||
|
|
||||||
public class Episode : AnimeItem
|
public class Episode : AnimeItem
|
||||||
{
|
{
|
||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Modules.Library.WebApi.Models.Anime;
|
namespace Modules.Library.WebApi.Models.Views.Anime;
|
||||||
|
|
||||||
public class Season : AnimeItem
|
public class Season : AnimeItem
|
||||||
{
|
{
|
||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Modules.Library.WebApi.Models.Anime;
|
namespace Modules.Library.WebApi.Models.Views.Anime;
|
||||||
|
|
||||||
public class Title
|
public class Title
|
||||||
{
|
{
|
||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Modules.Library.WebApi.Models;
|
namespace Modules.Library.WebApi.Models.Views;
|
||||||
|
|
||||||
public class CommonProperties
|
public class CommonProperties
|
||||||
{
|
{
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Modules.Library.WebApi.Models.Dictionary;
|
using Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Models;
|
namespace Modules.Library.WebApi.Models.Views;
|
||||||
public class Description
|
public class Description
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Models.Dictionary;
|
namespace Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
|
|
||||||
public class Genre
|
public class Genre
|
||||||
{
|
{
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Models.Dictionary;
|
namespace Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
|
|
||||||
public class Language
|
public class Language
|
||||||
{
|
{
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using Modules.Library.WebApi.Models.Dictionary;
|
using Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Models;
|
namespace Modules.Library.WebApi.Models.Views;
|
||||||
|
|
||||||
public class GenreProportion
|
public class GenreProportion
|
||||||
{
|
{
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Modules.Library.WebApi.Models.Dictionary;
|
using Modules.Library.WebApi.Models.Views.Dictionary;
|
||||||
|
|
||||||
namespace Modules.Library.WebApi.Models;
|
namespace Modules.Library.WebApi.Models.Views;
|
||||||
|
|
||||||
public class NameItem
|
public class NameItem
|
||||||
{
|
{
|
||||||
@ -12,6 +12,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common.WebApi\Common.WebApi.csproj" />
|
||||||
<ProjectReference Include="..\Modules.Library.Application\Modules.Library.Application.csproj" />
|
<ProjectReference Include="..\Modules.Library.Application\Modules.Library.Application.csproj" />
|
||||||
<ProjectReference Include="..\Modules.Library.Database\Modules.Library.Database.csproj" />
|
<ProjectReference Include="..\Modules.Library.Database\Modules.Library.Database.csproj" />
|
||||||
<ProjectReference Include="..\MyBookmark.ServiceDefaults\MyBookmark.ServiceDefaults.csproj" />
|
<ProjectReference Include="..\MyBookmark.ServiceDefaults\MyBookmark.ServiceDefaults.csproj" />
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
using Common.WebApi.Middlewares;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Modules.Library.Application;
|
using Modules.Library.Application;
|
||||||
using Modules.Library.Database;
|
using Modules.Library.Database;
|
||||||
using Modules.Library.WebApi.Models.Anime;
|
using Modules.Library.WebApi.Models.Views.Anime;
|
||||||
|
using Modules.Rating.Api;
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||||
|
|
||||||
@ -11,6 +13,7 @@ builder.AddServiceDefaults();
|
|||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddDatabase("mongodb://localhost:27017", "MyBookmarkDb");
|
builder.Services.AddDatabase("mongodb://localhost:27017", "MyBookmarkDb");
|
||||||
|
builder.Services.AddRates("mongodb://localhost:27017", "MyBookmarkDb");
|
||||||
|
|
||||||
builder.Services.AddApplicationServices();
|
builder.Services.AddApplicationServices();
|
||||||
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
|
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
|
||||||
@ -134,8 +137,12 @@ builder.Services.Configure<SwaggerGenOptions>(options =>
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.UseMiddleware<ExceptionMiddleware>();
|
||||||
|
|
||||||
app.MapDefaultEndpoints();
|
app.MapDefaultEndpoints();
|
||||||
|
|
||||||
|
app.UseCors(q => q.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod()); //??
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
|
|||||||
35
Modules.Rating.Api/Commands/RateObjectCommand.cs
Normal file
35
Modules.Rating.Api/Commands/RateObjectCommand.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Rating.Api.Database.Entities;
|
||||||
|
using Modules.Rating.Api.Repositories;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api.Commands;
|
||||||
|
|
||||||
|
public class RateObjectCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public Guid ObjectId { get; set; }
|
||||||
|
public Guid SubjectId { get; set; }
|
||||||
|
public ushort RatePercentage { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RateObjectCommandHandler(RateRepository repository) : IRequestHandler<RateObjectCommand, Unit>
|
||||||
|
{
|
||||||
|
public async Task<Unit> Handle(RateObjectCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var key = new RateKey
|
||||||
|
{
|
||||||
|
ObjectId = request.ObjectId,
|
||||||
|
SubjectId = request.SubjectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!await repository.IsRateExists(key))
|
||||||
|
{
|
||||||
|
await repository.AddAsync(new Rate { Key = key, RatePercentage = request.RatePercentage, });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await repository.UpdateAsync(new Rate { Key = key, RatePercentage = request.RatePercentage, });
|
||||||
|
}
|
||||||
|
return Unit.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Modules.Rating.Api/Commands/UnrateObjectCommand.cs
Normal file
31
Modules.Rating.Api/Commands/UnrateObjectCommand.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Rating.Api.Database.Entities;
|
||||||
|
using Modules.Rating.Api.Repositories;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api.Commands;
|
||||||
|
|
||||||
|
public class UnrateObjectCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public Guid ObjectId { get; set; }
|
||||||
|
public Guid SubjectId { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UnrateObjectCommandHandler(RateRepository repository) : IRequestHandler<UnrateObjectCommand, Unit>
|
||||||
|
{
|
||||||
|
public async Task<Unit> Handle(UnrateObjectCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
//var key = new RateKey
|
||||||
|
//{
|
||||||
|
// ObjectId = request.ObjectId,
|
||||||
|
// SubjectId = request.SubjectId,
|
||||||
|
//};
|
||||||
|
//if (await repository.IsRateExists(key)) await repository.DeleteAsync(key);
|
||||||
|
await repository.DeleteAsync(new RateKey
|
||||||
|
{
|
||||||
|
ObjectId = request.ObjectId,
|
||||||
|
SubjectId = request.SubjectId,
|
||||||
|
});
|
||||||
|
return Unit.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Modules.Rating.Api/Database/Entities/Rate.cs
Normal file
10
Modules.Rating.Api/Database/Entities/Rate.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api.Database.Entities;
|
||||||
|
|
||||||
|
[BsonIgnoreExtraElements]
|
||||||
|
public class Rate
|
||||||
|
{
|
||||||
|
public RateKey Key { get; set; } = default!;
|
||||||
|
public ushort RatePercentage { get; set; }
|
||||||
|
}
|
||||||
7
Modules.Rating.Api/Database/Entities/RateKey.cs
Normal file
7
Modules.Rating.Api/Database/Entities/RateKey.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Modules.Rating.Api.Database.Entities;
|
||||||
|
|
||||||
|
public class RateKey
|
||||||
|
{
|
||||||
|
public Guid ObjectId { get; set; } = default!;
|
||||||
|
public Guid SubjectId { get; set; } = default!;
|
||||||
|
}
|
||||||
35
Modules.Rating.Api/Database/MongoDbContext.cs
Normal file
35
Modules.Rating.Api/Database/MongoDbContext.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Modules.Rating.Api.Database.Entities;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api.Database;
|
||||||
|
|
||||||
|
public class MongoDbContext(IMongoDatabase database)
|
||||||
|
{
|
||||||
|
private bool _initialized;
|
||||||
|
public IMongoCollection<TDocument> GetCollection<TDocument>(string collectionName)
|
||||||
|
{
|
||||||
|
if (!_initialized) throw new Exception(string.Concat(nameof(MongoDbContext), " has not initialized yet"));
|
||||||
|
return database.GetCollection<TDocument>(collectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMongoCollection<Rate> Rates => GetCollection<Rate>(nameof(Rate));
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
BsonClassMap.RegisterClassMap<AnimeTitle>(q =>
|
||||||
|
{
|
||||||
|
q.AutoMap();
|
||||||
|
q.MapIdMember(c => c.Id).SetIdGenerator(CombGuidGenerator.Instance);
|
||||||
|
//q.MapCreator(q => AnimeTitleBuilder.FromAnimeTitle(q).Build());
|
||||||
|
//q.MapIdMember(c => c.Id).SetIdGenerator(StringObjectIdGenerator.Instance);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//private IntSequence _paymentProviderIdSequence = new("PaymentProviderId");
|
||||||
|
|
||||||
|
//public Task<int> GetNextPaymentProviderId() => _paymentProviderIdSequence.GetNextSequenceValue(context);
|
||||||
|
}
|
||||||
9
Modules.Rating.Api/Models/Rate.cs
Normal file
9
Modules.Rating.Api/Models/Rate.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Modules.Rating.Api.Models;
|
||||||
|
|
||||||
|
public class Rate
|
||||||
|
{
|
||||||
|
public Guid ObjectId { get; set; }
|
||||||
|
//public Guid SubjectId { get; set; }
|
||||||
|
public ushort? ObjectRatePercentage { get; set; }
|
||||||
|
public ushort? SubjectRatePercentage { get; set; }
|
||||||
|
}
|
||||||
15
Modules.Rating.Api/Modules.Rating.Api.csproj
Normal file
15
Modules.Rating.Api/Modules.Rating.Api.csproj
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MediatR" Version="12.4.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
|
<PackageReference Include="MongoDB.Driver" Version="2.30.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
27
Modules.Rating.Api/Querirs/ObjectRatingListQuery.cs
Normal file
27
Modules.Rating.Api/Querirs/ObjectRatingListQuery.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Rating.Api.Database.Entities;
|
||||||
|
using Modules.Rating.Api.Models;
|
||||||
|
using Modules.Rating.Api.Repositories;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api.Querirs;
|
||||||
|
|
||||||
|
public class ObjectRatingListQuery : IRequest<List<Models.Rate>>
|
||||||
|
{
|
||||||
|
public Guid? SubjectId { get; set; }
|
||||||
|
public IEnumerable<Guid> ObjectIds { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectRatingListQueryHandler(RateRepository repository) : IRequestHandler<ObjectRatingListQuery, List<Models.Rate>>
|
||||||
|
{
|
||||||
|
public async Task<List<Models.Rate>> Handle(ObjectRatingListQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (request.ObjectIds.Count() == 0) return [];
|
||||||
|
var rates = await repository.GetRates(request.ObjectIds, request.SubjectId);
|
||||||
|
return rates.Select(q => new Models.Rate
|
||||||
|
{
|
||||||
|
ObjectId = q.ObjectId,
|
||||||
|
ObjectRatePercentage = (ushort)Math.Round((decimal)q.Rate, 0),
|
||||||
|
SubjectRatePercentage = q.SubjectRate.HasValue ? Convert.ToUInt16(q.SubjectRate) : null,
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Modules.Rating.Api/Querirs/ObjectRatingQuery.cs
Normal file
37
Modules.Rating.Api/Querirs/ObjectRatingQuery.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Modules.Rating.Api.Database.Entities;
|
||||||
|
using Modules.Rating.Api.Repositories;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api.Querirs;
|
||||||
|
|
||||||
|
public class ObjectRatingQuery : IRequest<Models.Rate?>
|
||||||
|
{
|
||||||
|
public Guid ObjectId { get; set; } = default!;
|
||||||
|
public Guid? SubjectId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectRatingQueryHandler(RateRepository repository) : IRequestHandler<ObjectRatingQuery, Models.Rate?>
|
||||||
|
{
|
||||||
|
public async Task<Models.Rate?> Handle(ObjectRatingQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var rate = await repository.GetAverageObjectRate(request.ObjectId);
|
||||||
|
|
||||||
|
var subjectRate = request.SubjectId.HasValue
|
||||||
|
? await repository.GetFirstOrDefaultWhere(q => q.Key == new RateKey
|
||||||
|
{
|
||||||
|
ObjectId = request.ObjectId,
|
||||||
|
SubjectId = request.SubjectId.HasValue ? request.SubjectId.Value : q.Key.SubjectId,
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return rate.HasValue
|
||||||
|
? new Models.Rate
|
||||||
|
{
|
||||||
|
ObjectId = request.ObjectId,
|
||||||
|
//SubjectId = request.SubjectId,
|
||||||
|
ObjectRatePercentage = (ushort)Math.Round((decimal)rate, 0),
|
||||||
|
SubjectRatePercentage = subjectRate?.RatePercentage,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
145
Modules.Rating.Api/Repositories/RateRepository.cs
Normal file
145
Modules.Rating.Api/Repositories/RateRepository.cs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
using Modules.Rating.Api.Database;
|
||||||
|
using Modules.Rating.Api.Database.Entities;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using MongoDB.Driver.Linq;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api.Repositories;
|
||||||
|
|
||||||
|
public class RateRepository(MongoDbContext context)
|
||||||
|
{
|
||||||
|
private readonly IMongoCollection<Rate> _collection = context.GetCollection<Rate>(nameof(Rate));
|
||||||
|
|
||||||
|
public async Task AddAsync(Rate entity)
|
||||||
|
{
|
||||||
|
if (await IsRateExists(entity.Key)) throw new Exception("Object is already rated by subject");
|
||||||
|
await _collection.InsertOneAsync(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UpdateAsync(Rate entity)
|
||||||
|
{
|
||||||
|
if (!await IsRateExists(entity.Key)) throw new Exception("Rate not found");
|
||||||
|
var document = await _collection.FindOneAndReplaceAsync(q => q.Key == entity.Key, entity);
|
||||||
|
return document != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteAsync(RateKey key)
|
||||||
|
{
|
||||||
|
if (!await IsRateExists(key)) throw new Exception("Rate not found");
|
||||||
|
var document = await _collection.FindOneAndDeleteAsync(q => q.Key == key);
|
||||||
|
return document != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Rate> GetFirstOrDefaultWhere(Expression<Func<Rate, bool>> predicate) =>
|
||||||
|
await _collection.Find(predicate).SingleOrDefaultAsync();
|
||||||
|
|
||||||
|
public async Task<List<Rate>> GetWhere(Expression<Func<Rate, bool>> predicate) =>
|
||||||
|
await _collection.Find(predicate).ToListAsync();
|
||||||
|
|
||||||
|
//internal async Task<List<RateItem>> GetRates(IEnumerable<Guid> objectIds, Guid? subjectId)
|
||||||
|
//{
|
||||||
|
// //var query = _collection.AsQueryable();
|
||||||
|
// //return await query
|
||||||
|
// // .Where(q => objectIds.Contains(q.Key.ObjectId))
|
||||||
|
// // .GroupBy(q => q.Key.ObjectId, (o, r) => new { ObjectId = o, Rate = r.Average(q => q.RatePercentage) })
|
||||||
|
// // .GroupJoin(query.Where(q => q.Key.SubjectId == subjectId),
|
||||||
|
// // q => q.ObjectId,
|
||||||
|
// // q => q.Key.ObjectId,
|
||||||
|
// // (g, r) => new { g.ObjectId, g.Rate, SubjecrRates = r })
|
||||||
|
// // .SelectMany(q => q.SubjecrRates.DefaultIfEmpty(), (r, s) =>
|
||||||
|
// // new RateItem { ObjectId = r.ObjectId, Rate = r.Rate, SubjectRate = s != null ? s.RatePercentage : null })
|
||||||
|
// // .ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
// var builder = Builders<BsonDocument>.SetFields
|
||||||
|
|
||||||
|
// await _collection
|
||||||
|
// .Aggregate()
|
||||||
|
// .Match(q => objectIds.Contains(q.Key.ObjectId))
|
||||||
|
// .Group(q => q.Key.ObjectId, q => new { ObjectId = q.Key, Rate = q.Average(q => q.RatePercentage) })
|
||||||
|
// .Lookup<Rate, RateItem>(nameof(Rate), q => q.ObjectId, q)
|
||||||
|
// .FirstOrDefaultAsync().ConfigureAwait(false);
|
||||||
|
//}
|
||||||
|
|
||||||
|
internal async Task<List<RateItem>> GetRates(IEnumerable<Guid> objectIds, Guid? subjectId)
|
||||||
|
{
|
||||||
|
var matchStage = new BsonDocument("$match", new BsonDocument("Key.ObjectId", new BsonDocument("$in", new BsonArray(objectIds))));
|
||||||
|
|
||||||
|
var groupStage = new BsonDocument("$group", new BsonDocument
|
||||||
|
{
|
||||||
|
{ "_id", "$Key.ObjectId" },
|
||||||
|
{ "AverageRate", new BsonDocument("$avg", "$RatePercentage") }
|
||||||
|
});
|
||||||
|
|
||||||
|
var lookupStage = new BsonDocument("$lookup", new BsonDocument
|
||||||
|
{
|
||||||
|
{ "from", _collection.CollectionNamespace.CollectionName },
|
||||||
|
{ "let", new BsonDocument("objectId", "$_id") },
|
||||||
|
{ "pipeline", new BsonArray
|
||||||
|
{
|
||||||
|
new BsonDocument("$match", new BsonDocument("$expr", new BsonDocument("$and", new BsonArray
|
||||||
|
{
|
||||||
|
new BsonDocument("$eq", new BsonArray { "$Key.ObjectId", "$$objectId" }),
|
||||||
|
//new BsonDocument("$eq", new BsonArray { "$Key.SubjectId", subjectId.ToString() ?? "" })
|
||||||
|
new BsonDocument("$eq", new BsonArray { "$Key.SubjectId", subjectId.ToString() ?? "" })
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "as", "SubjectRates" }
|
||||||
|
});
|
||||||
|
|
||||||
|
var projectStage = new BsonDocument("$project", new BsonDocument
|
||||||
|
{
|
||||||
|
{ "ObjectId", "$_id" },
|
||||||
|
{ "Rate", "$AverageRate" },
|
||||||
|
{ "SubjectRate", new BsonDocument("$arrayElemAt", new BsonArray { "$SubjectRates.RatePercentage", 0 }) }
|
||||||
|
});
|
||||||
|
|
||||||
|
var pipeline = new[] { matchStage, groupStage, lookupStage, projectStage };
|
||||||
|
|
||||||
|
var result = await _collection.AggregateAsync<BsonDocument>(pipeline).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var rateItems = new List<RateItem>();
|
||||||
|
|
||||||
|
//await result.ForEachAsync(doc =>
|
||||||
|
//{
|
||||||
|
// rateItems.Add(new RateItem
|
||||||
|
// {
|
||||||
|
// ObjectId = doc["ObjectId"].AsGuid,
|
||||||
|
// Rate = doc["Rate"].AsDouble,
|
||||||
|
// SubjectRate = doc["SubjectRate"].IsBsonNull ? (ushort?)null : doc["SubjectRate"].AsNullableInt32
|
||||||
|
// });
|
||||||
|
//}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await result.ForEachAsync(doc =>
|
||||||
|
{
|
||||||
|
var subjectRate = doc.GetValue("SubjectRate", BsonNull.Value);
|
||||||
|
rateItems.Add(new RateItem
|
||||||
|
{
|
||||||
|
ObjectId = doc.GetValue("ObjectId").AsGuid,
|
||||||
|
Rate = doc.GetValue("Rate").AsDouble,
|
||||||
|
SubjectRate = subjectRate.IsBsonNull ? null : subjectRate.AsNullableInt32
|
||||||
|
});
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return rateItems;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class RateItem
|
||||||
|
{
|
||||||
|
internal Guid ObjectId { get; set; } = default!;
|
||||||
|
internal double Rate { get; set; }
|
||||||
|
internal int? SubjectRate { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<double?> GetAverageObjectRate(Guid objectId) =>
|
||||||
|
await _collection
|
||||||
|
.Aggregate()
|
||||||
|
.Match(q => q.Key.ObjectId == objectId)
|
||||||
|
.Group(q => q.Key.ObjectId, q => q.Average(q => q.RatePercentage))
|
||||||
|
.FirstOrDefaultAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
public async Task<bool> IsRateExists(RateKey key) => await _collection.Find(q => q.Key == key).AnyAsync();
|
||||||
|
}
|
||||||
59
Modules.Rating.Api/ServiceCollectionExtensions.cs
Normal file
59
Modules.Rating.Api/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Modules.Rating.Api.Database;
|
||||||
|
using Modules.Rating.Api.Repositories;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Api
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddRates(this IServiceCollection services, string connectionString)
|
||||||
|
{
|
||||||
|
AddMongoDb(services, connectionString);
|
||||||
|
services.AddScoped(q =>
|
||||||
|
{
|
||||||
|
var context = new MongoDbContext(q.GetRequiredService<IMongoDatabase>());
|
||||||
|
context.Initialize();
|
||||||
|
return context;
|
||||||
|
});
|
||||||
|
AddRepositories(services);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddRates(this IServiceCollection services, string connectionString, string? databaseName)
|
||||||
|
{
|
||||||
|
AddMongoDb(services, connectionString, databaseName);
|
||||||
|
//services.AddScoped<MongoDbContext>();
|
||||||
|
services.AddScoped(q =>
|
||||||
|
{
|
||||||
|
var context = new MongoDbContext(q.GetRequiredService<IMongoDatabase>());
|
||||||
|
context.Initialize();
|
||||||
|
return context;
|
||||||
|
});
|
||||||
|
AddRepositories(services);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//AddGateways(services);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddRepositories(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<RateRepository>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddMongoDb(this IServiceCollection services, string? connectionString)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullException(nameof(connectionString));
|
||||||
|
services.AddSingleton(new MongoClient(connectionString));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddMongoDb(IServiceCollection services, string? connectionString, string? databaseName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullException(nameof(connectionString));
|
||||||
|
if (string.IsNullOrWhiteSpace(databaseName)) throw new ArgumentNullException(nameof(databaseName));
|
||||||
|
services.AddSingleton(new MongoClient(connectionString).GetDatabase(databaseName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Modules.Rating.Application/Gateways/RatingGateway.cs
Normal file
6
Modules.Rating.Application/Gateways/RatingGateway.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Modules.Rating.Application.Gateways;
|
||||||
|
|
||||||
|
public interface IRatingGateway
|
||||||
|
{
|
||||||
|
public
|
||||||
|
}
|
||||||
18
Modules.Rating.Application/Modules.Rating.Application.csproj
Normal file
18
Modules.Rating.Application/Modules.Rating.Application.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Commands\" />
|
||||||
|
<Folder Include="Querirs\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MediatR" Version="12.4.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
12
Modules.Rating.Application/Querirs/ObjectRatingQuery.cs
Normal file
12
Modules.Rating.Application/Querirs/ObjectRatingQuery.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modules.Rating.Application.Querirs
|
||||||
|
{
|
||||||
|
internal class ObjectRatingQuery
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
namespace Modules.Rating.Application;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
5
Modules.Rating.Application/Services/RatingService.cs
Normal file
5
Modules.Rating.Application/Services/RatingService.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace Modules.Rating.Application.Services;
|
||||||
|
|
||||||
|
public class RatingService
|
||||||
|
{
|
||||||
|
}
|
||||||
7
Modules.Rating.Database/Class1.cs
Normal file
7
Modules.Rating.Database/Class1.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Modules.Rating.Database
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Modules.Rating.Database/Modules.Rating.Database.csproj
Normal file
9
Modules.Rating.Database/Modules.Rating.Database.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
9
Modules.Rating.Domain/Modules.Rating.Domain.csproj
Normal file
9
Modules.Rating.Domain/Modules.Rating.Domain.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
8
Modules.Rating.Domain/Rate.cs
Normal file
8
Modules.Rating.Domain/Rate.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Modules.Rating.Domain;
|
||||||
|
|
||||||
|
public class Rate
|
||||||
|
{
|
||||||
|
public Guid ObjectId { get; set; }
|
||||||
|
public Guid SubjectId { get; set; }
|
||||||
|
public ushort RatePercentage { get; set; }
|
||||||
|
}
|
||||||
9
Modules.User.Api/Modules.User.Api.csproj
Normal file
9
Modules.User.Api/Modules.User.Api.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
6
Modules.User.Api/UserMiddleware.cs
Normal file
6
Modules.User.Api/UserMiddleware.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Modules.User.Api;
|
||||||
|
|
||||||
|
public class UserMiddleware
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
15
Modules.User.Api/UserService.cs
Normal file
15
Modules.User.Api/UserService.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
namespace Modules.User.Api;
|
||||||
|
|
||||||
|
public class UserService
|
||||||
|
{
|
||||||
|
private string? _user;
|
||||||
|
|
||||||
|
public async Task<string?> GetUser()
|
||||||
|
{
|
||||||
|
if (_user == null)
|
||||||
|
{
|
||||||
|
await Task.Delay(5000);
|
||||||
|
}
|
||||||
|
return _user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Views", "Views", "{A1136847
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Search", "Search", "{5BC05B7D-F17B-4776-91CA-A7552FD294EA}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Search", "Search", "{5BC05B7D-F17B-4776-91CA-A7552FD294EA}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rates", "Rates", "{C3D36A31-0F02-4E15-A57D-895E714FE8A7}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rating", "Rating", "{C3D36A31-0F02-4E15-A57D-895E714FE8A7}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UserNotes", "UserNotes", "{1A6524E9-DA15-4044-B29F-BFB337F490EE}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UserNotes", "UserNotes", "{1A6524E9-DA15-4044-B29F-BFB337F490EE}"
|
||||||
EndProject
|
EndProject
|
||||||
@ -55,6 +55,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{30DDC74A-7529-
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyBookmark.UI.RazorPages", "MyBookmark.UI.RazorPages\MyBookmark.UI.RazorPages.csproj", "{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyBookmark.UI.RazorPages", "MyBookmark.UI.RazorPages\MyBookmark.UI.RazorPages.csproj", "{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.WebApi", "Common.WebApi\Common.WebApi.csproj", "{332B1C89-06C2-4ED9-845C-DE41F53D15E8}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules.Rating.Api", "Modules.Rating.Api\Modules.Rating.Api.csproj", "{16F6D8C7-78EE-4800-82E2-951737221958}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{86794C28-E42B-4899-B6FD-0AFA51E204B8}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{C72E692E-B96D-4512-BB2E-CA0C9C9E2B28}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules.Account.Api", "Modules.Account.Api\Modules.Account.Api.csproj", "{1EFD5236-7F81-4AB8-8838-08C3DA7A5306}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules.Account.Domain", "Modules.Account.Domain\Modules.Account.Domain.csproj", "{5EFDE6FF-BE77-458C-ACD1-0D33346F2245}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules.Account.Database", "Modules.Account.Database\Modules.Account.Database.csproj", "{BFEBE295-1923-48C5-A922-D9FD997E150C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules.Account.Application", "Modules.Account.Application\Modules.Account.Application.csproj", "{A18E14C1-422E-44F2-A140-397B1F918F45}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modules.User.Api", "Modules.User.Api\Modules.User.Api.csproj", "{4EE10B8F-EF6C-40A2-A9EB-4FC2D4CF8107}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -107,6 +125,34 @@ Global
|
|||||||
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}.Release|Any CPU.Build.0 = Release|Any CPU
|
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{332B1C89-06C2-4ED9-845C-DE41F53D15E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{332B1C89-06C2-4ED9-845C-DE41F53D15E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{332B1C89-06C2-4ED9-845C-DE41F53D15E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{332B1C89-06C2-4ED9-845C-DE41F53D15E8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{16F6D8C7-78EE-4800-82E2-951737221958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{16F6D8C7-78EE-4800-82E2-951737221958}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{16F6D8C7-78EE-4800-82E2-951737221958}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{16F6D8C7-78EE-4800-82E2-951737221958}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1EFD5236-7F81-4AB8-8838-08C3DA7A5306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1EFD5236-7F81-4AB8-8838-08C3DA7A5306}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1EFD5236-7F81-4AB8-8838-08C3DA7A5306}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1EFD5236-7F81-4AB8-8838-08C3DA7A5306}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5EFDE6FF-BE77-458C-ACD1-0D33346F2245}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5EFDE6FF-BE77-458C-ACD1-0D33346F2245}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5EFDE6FF-BE77-458C-ACD1-0D33346F2245}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5EFDE6FF-BE77-458C-ACD1-0D33346F2245}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BFEBE295-1923-48C5-A922-D9FD997E150C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BFEBE295-1923-48C5-A922-D9FD997E150C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BFEBE295-1923-48C5-A922-D9FD997E150C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BFEBE295-1923-48C5-A922-D9FD997E150C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A18E14C1-422E-44F2-A140-397B1F918F45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A18E14C1-422E-44F2-A140-397B1F918F45}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A18E14C1-422E-44F2-A140-397B1F918F45}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A18E14C1-422E-44F2-A140-397B1F918F45}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4EE10B8F-EF6C-40A2-A9EB-4FC2D4CF8107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4EE10B8F-EF6C-40A2-A9EB-4FC2D4CF8107}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4EE10B8F-EF6C-40A2-A9EB-4FC2D4CF8107}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4EE10B8F-EF6C-40A2-A9EB-4FC2D4CF8107}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -131,6 +177,15 @@ Global
|
|||||||
{43731C43-2BA9-4F2F-8A0F-2F157F05D5A7} = {D034BC31-002F-4F3D-B2C1-9E81B990C51A}
|
{43731C43-2BA9-4F2F-8A0F-2F157F05D5A7} = {D034BC31-002F-4F3D-B2C1-9E81B990C51A}
|
||||||
{6DC1B759-12F6-415D-BCAC-60E9E15B1074} = {036DC6C2-18A3-49DB-92D3-6DFCDCE3D5F8}
|
{6DC1B759-12F6-415D-BCAC-60E9E15B1074} = {036DC6C2-18A3-49DB-92D3-6DFCDCE3D5F8}
|
||||||
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C} = {30DDC74A-7529-4462-94CB-4AB77792BCCD}
|
{37A4BCD2-73B9-4CB3-B12A-58B1298FE02C} = {30DDC74A-7529-4462-94CB-4AB77792BCCD}
|
||||||
|
{332B1C89-06C2-4ED9-845C-DE41F53D15E8} = {BACBF195-FD74-4F57-AF8B-D8752C1EBBF0}
|
||||||
|
{16F6D8C7-78EE-4800-82E2-951737221958} = {C3D36A31-0F02-4E15-A57D-895E714FE8A7}
|
||||||
|
{86794C28-E42B-4899-B6FD-0AFA51E204B8} = {9B8DB13A-9595-419D-83F3-7C537B903D9F}
|
||||||
|
{C72E692E-B96D-4512-BB2E-CA0C9C9E2B28} = {9B8DB13A-9595-419D-83F3-7C537B903D9F}
|
||||||
|
{1EFD5236-7F81-4AB8-8838-08C3DA7A5306} = {9B8DB13A-9595-419D-83F3-7C537B903D9F}
|
||||||
|
{5EFDE6FF-BE77-458C-ACD1-0D33346F2245} = {86794C28-E42B-4899-B6FD-0AFA51E204B8}
|
||||||
|
{BFEBE295-1923-48C5-A922-D9FD997E150C} = {C72E692E-B96D-4512-BB2E-CA0C9C9E2B28}
|
||||||
|
{A18E14C1-422E-44F2-A140-397B1F918F45} = {86794C28-E42B-4899-B6FD-0AFA51E204B8}
|
||||||
|
{4EE10B8F-EF6C-40A2-A9EB-4FC2D4CF8107} = {A006C232-0F12-4B56-9EA0-D8771ABE49AA}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {4D16F124-695A-4782-BEFB-AAAFA0953732}
|
SolutionGuid = {4D16F124-695A-4782-BEFB-AAAFA0953732}
|
||||||
|
|||||||
@ -5,8 +5,21 @@
|
|||||||
<!--<link rel="icon" type="image/svg+xml" href="/vite.svg" />-->
|
<!--<link rel="icon" type="image/svg+xml" href="/vite.svg" />-->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>My bookmark</title>
|
<title>My bookmark</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="plugins/themify-icons/themify-icons.css">
|
||||||
|
|
||||||
|
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> -->
|
||||||
|
<!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> -->
|
||||||
|
<link href="./node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet">
|
||||||
|
<link href="./node_modules/bootstrap/dist/css/bootstrap.js" rel="stylesheet">
|
||||||
|
<!-- <link href="./src/assets/css/custom.css" rel="stylesheet"> -->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<!-- <body style="height: 100%; overflow-x: hidden; overflow-y: hidden;"> -->
|
||||||
|
<!-- <body style="height: 100vh; overflow: hidden;"> -->
|
||||||
|
<!-- <body class="container-xxl" style="height: 100vh;"> -->
|
||||||
|
<!-- <body class="overflow-hidden"> -->
|
||||||
|
<!-- <body class="overflow-hidden" data-bs-theme="dark"> -->
|
||||||
|
<body class="overflow-hidden">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
3413
mybookmark.ui/package-lock.json
generated
3413
mybookmark.ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,8 +10,15 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
|
"jotai": "^2.10.0",
|
||||||
|
"npm": "^10.8.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1",
|
||||||
|
"react-icons": "^5.3.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
|
"react-router-dom": "^6.26.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@eslint/js": "^9.9.0",
|
||||||
|
|||||||
32
mybookmark.ui/scss/custom.scss
Normal file
32
mybookmark.ui/scss/custom.scss
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// @import "../node_modules/bootstrap/scss/functions";
|
||||||
|
|
||||||
|
// // // 2. Include any default variable overrides here
|
||||||
|
|
||||||
|
@import "./themes/Cosmo/variables";
|
||||||
|
|
||||||
|
@import "../node_modules/bootstrap/scss/bootstrap";
|
||||||
|
// @import "../node_modules/bootstrap/scss/variables";
|
||||||
|
// @import "../node_modules/bootstrap/scss/variables-dark";
|
||||||
|
|
||||||
|
// @import "../node_modules/bootstrap/scss/mixins";
|
||||||
|
|
||||||
|
@import "./themes/Cosmo/bootswatch";
|
||||||
|
// @import "../node_modules/bootstrap/scss/maps";
|
||||||
|
// @import "../node_modules/bootstrap/scss/root";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// @import "variables";
|
||||||
|
// Then have Bootstrap do it's magic with these new values
|
||||||
|
|
||||||
|
// $tertiary-bg-rgb: rgba(102, 102, 153, 1);
|
||||||
|
// $tertiary-bg-rgb: rgb(102 102 153 / 1);
|
||||||
|
|
||||||
|
// $body-tertiary-color: rgba($body-color, .5) !default;
|
||||||
|
// $body-tertiary-bg: $gray-100 !default;
|
||||||
|
|
||||||
|
// $body-tertiary-color: rgba(102, 102, 0, 1) !default;
|
||||||
|
// $body-tertiary-bg: rgba(0, 102, 153, 1) !default;
|
||||||
|
|
||||||
|
// $white: #0000 !default;
|
||||||
35
mybookmark.ui/scss/themes/Cosmo/_bootswatch.scss
Normal file
35
mybookmark.ui/scss/themes/Cosmo/_bootswatch.scss
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Cosmo 5.3.3
|
||||||
|
// Bootswatch
|
||||||
|
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
|
||||||
|
$web-font-path: "https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;700&display=swap" !default;
|
||||||
|
@if $web-font-path {
|
||||||
|
@import url("#{$web-font-path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typography
|
||||||
|
|
||||||
|
body {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicators
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
&.bg-light {
|
||||||
|
color: $dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress bars
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
@include box-shadow(none);
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
font-size: 8px;
|
||||||
|
line-height: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
69
mybookmark.ui/scss/themes/Cosmo/_variables.scss
Normal file
69
mybookmark.ui/scss/themes/Cosmo/_variables.scss
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Cosmo 5.3.3
|
||||||
|
// Bootswatch
|
||||||
|
|
||||||
|
$theme: "cosmo" !default;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Color system
|
||||||
|
//
|
||||||
|
|
||||||
|
$white: #fff !default;
|
||||||
|
$gray-100: #f8f9fa !default;
|
||||||
|
$gray-200: #e9ecef !default;
|
||||||
|
$gray-300: #dee2e6 !default;
|
||||||
|
$gray-400: #ced4da !default;
|
||||||
|
$gray-500: #adb5bd !default;
|
||||||
|
$gray-600: #868e96 !default;
|
||||||
|
$gray-700: #495057 !default;
|
||||||
|
$gray-800: #373a3c !default;
|
||||||
|
$gray-900: #212529 !default;
|
||||||
|
$black: #000 !default;
|
||||||
|
|
||||||
|
$blue: #2780e3 !default;
|
||||||
|
$indigo: #6610f2 !default;
|
||||||
|
$purple: #613d7c !default;
|
||||||
|
$pink: #e83e8c !default;
|
||||||
|
$red: #ff0039 !default;
|
||||||
|
$orange: #f0ad4e !default;
|
||||||
|
$yellow: #ff7518 !default;
|
||||||
|
$green: #3fb618 !default;
|
||||||
|
$teal: #20c997 !default;
|
||||||
|
$cyan: #9954bb !default;
|
||||||
|
|
||||||
|
$primary: $blue !default;
|
||||||
|
$secondary: $gray-800 !default;
|
||||||
|
$success: $green !default;
|
||||||
|
$info: $cyan !default;
|
||||||
|
$warning: $yellow !default;
|
||||||
|
$danger: $red !default;
|
||||||
|
$light: $gray-100 !default;
|
||||||
|
$dark: $gray-800 !default;
|
||||||
|
|
||||||
|
$min-contrast-ratio: 2.6 !default;
|
||||||
|
|
||||||
|
// Options
|
||||||
|
|
||||||
|
$enable-rounded: false !default;
|
||||||
|
|
||||||
|
// Body
|
||||||
|
|
||||||
|
$body-color: $gray-800 !default;
|
||||||
|
|
||||||
|
// Fonts
|
||||||
|
|
||||||
|
// stylelint-disable-next-line value-keyword-case
|
||||||
|
$font-family-sans-serif: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
|
||||||
|
$headings-font-weight: 400 !default;
|
||||||
|
|
||||||
|
// Navbar
|
||||||
|
|
||||||
|
$navbar-dark-hover-color: rgba($white, 1) !default;
|
||||||
|
$navbar-light-hover-color: rgba($black, .9) !default;
|
||||||
|
|
||||||
|
// Alerts
|
||||||
|
|
||||||
|
$alert-border-width: 0 !default;
|
||||||
|
|
||||||
|
// Progress bars
|
||||||
|
|
||||||
|
$progress-height: .5rem !default;
|
||||||
@ -1,8 +1,12 @@
|
|||||||
|
/*var*/
|
||||||
|
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
max-width: 1280px;
|
/* max-width: 1280px; */
|
||||||
margin: 0 auto;
|
/* margin: 0 auto; */
|
||||||
padding: 2rem;
|
/* padding: 2rem; */
|
||||||
text-align: center;
|
/* text-align: center; */
|
||||||
|
--navbar-height: 3.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@ -40,3 +44,64 @@
|
|||||||
.read-the-docs {
|
.read-the-docs {
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* .menu{
|
||||||
|
position: sticky;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-right: thick double red;
|
||||||
|
box-shadow: .5rem, 0, 0, DarkSlateGray;
|
||||||
|
height: 100%;
|
||||||
|
position: sticky important!;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.fixed-navbar{
|
||||||
|
position: sticky;
|
||||||
|
height: var(--navbar-height);
|
||||||
|
z-index: 1200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixed-sidebar{
|
||||||
|
position: sticky;
|
||||||
|
top: var(--navbar-height);
|
||||||
|
/* left: 0; */
|
||||||
|
/* width: 300px; */
|
||||||
|
height: 100%;
|
||||||
|
/* background: #042331; */
|
||||||
|
|
||||||
|
/* overflow-x: hidden; */
|
||||||
|
/* overflow-y: scroll; */
|
||||||
|
/* border-right: thick double red; */
|
||||||
|
/* box-shadow: .5rem, 0, 0, DarkSlateGray; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
/* position: sticky; */
|
||||||
|
/* padding-top: var(--navbar-height); */
|
||||||
|
/* height: calc(100% - var(--navbar-height)); */
|
||||||
|
height: calc(100vh - var(--navbar-height));
|
||||||
|
/* min-height: calc(100% - var(--navbar-height)); */
|
||||||
|
min-height: calc(100vh - var(--navbar-height));
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container:before {
|
||||||
|
content: attr(data-text);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
line-height:1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rateStar {
|
||||||
|
/* background-image: url(./assets/thumbs_up_0i8ids3f6gah.svg); */
|
||||||
|
}
|
||||||
@ -1,34 +1,61 @@
|
|||||||
import { useState } from 'react'
|
// import { useState } from 'react'
|
||||||
import reactLogo from './assets/react.svg'
|
// import reactLogo from './assets/react.svg'
|
||||||
import viteLogo from '/vite.svg'
|
// import viteLogo from '/vite.svg'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
||||||
function App() {
|
// import 'bootstrap/dist/css/bootstrap.min.css'
|
||||||
const [count, setCount] = useState(0)
|
// import './assets/css/custom.css'
|
||||||
|
// import './assets/scss/custom.scss'
|
||||||
|
// import 'bootstrap/dist/css/bootstrap.css';
|
||||||
|
// import 'bootstrap/dist/js/bootstrap.js';
|
||||||
|
// import './assets/css/custom.css'
|
||||||
|
|
||||||
|
import HomePage from './pages/HomePage';
|
||||||
|
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||||
|
import Layout from './common/Layout';
|
||||||
|
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
||||||
|
import Dictionaries from './pages/library/edit/Dictionaries';
|
||||||
|
import AdminLayout from './common/AdminLayout';
|
||||||
|
import CreateAnimeTitle from './pages/library/edit/CreateAnimeTitle';
|
||||||
|
import AnimeTitleDetail from './pages/library/anime/AnimeTitleDetail';
|
||||||
|
import AnimeTitleEdit from './pages/library/edit/AnimeTitleEdit';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
//const [count, setCount] = useState(0)
|
||||||
return (
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={
|
||||||
<>
|
<>
|
||||||
<div>
|
<QueryClientProvider client={new QueryClient()}>
|
||||||
<a href="https://vitejs.dev" target="_blank">
|
<Layout />
|
||||||
<img src={viteLogo} className="logo" alt="Vite logo" />
|
</QueryClientProvider>
|
||||||
</a>
|
|
||||||
<a href="https://react.dev" target="_blank">
|
|
||||||
<img src={reactLogo} className="logo react" alt="React logo" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h1>Vite + React</h1>
|
|
||||||
<div className="card">
|
|
||||||
<button onClick={() => setCount((count) => count + 1)}>
|
|
||||||
count is {count}
|
|
||||||
</button>
|
|
||||||
<p>
|
|
||||||
Edit <code>src/App.tsx</code> and save to test HMR
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p className="read-the-docs">
|
|
||||||
Click on the Vite and React logos to learn more
|
|
||||||
</p>
|
|
||||||
</>
|
</>
|
||||||
|
}>
|
||||||
|
<Route index element={<HomePage />} />
|
||||||
|
<Route path="library">
|
||||||
|
<Route path="anime/:id" element={<AnimeTitleDetail />} />
|
||||||
|
</Route>
|
||||||
|
{/* <Route path="about" element={<About />} /> */}
|
||||||
|
{/* <Route path="contact" element={<Contact />} /> */}
|
||||||
|
</Route>
|
||||||
|
<Route path="/admin" element={
|
||||||
|
<>
|
||||||
|
<QueryClientProvider client={new QueryClient()}>
|
||||||
|
<AdminLayout />
|
||||||
|
</QueryClientProvider>
|
||||||
|
</>
|
||||||
|
}>
|
||||||
|
<Route path='library/dictionaries' element={<Dictionaries />} />
|
||||||
|
<Route path='library/anime/title' element={<CreateAnimeTitle />} />
|
||||||
|
<Route path='library/anime/anime/title/:id/edit' element={<AnimeTitleEdit />} />
|
||||||
|
{/* <Route path="about" element={<About />} /> */}
|
||||||
|
{/* <Route path="contact" element={<Contact />} /> */}
|
||||||
|
</Route>
|
||||||
|
{/* <Route path="/connect" element={<Connect />} /> */}
|
||||||
|
{/* <Route path="*" element={<Error />} /> */}
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
392
mybookmark.ui/src/api/AnimeTitleApi.tsx
Normal file
392
mybookmark.ui/src/api/AnimeTitleApi.tsx
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { AnimeTitle, CreateDescriptionModel, CreateMediaInfoModel, CreateNameModel, DeleteDescriptionModel, DeleteMediaInfoModel, DeleteNameModel, EditDateModel, EditDescriptionModel, EditNameModel, RateDeleteModel, RateEditModel, TitleCreateModel } from "./models/Types";
|
||||||
|
import { CatchError } from "./common";
|
||||||
|
|
||||||
|
const controllerUrl = 'https://localhost:7057/MediaContent/Anime/Title/';
|
||||||
|
|
||||||
|
export async function GetAnimeTitleList() {
|
||||||
|
try {
|
||||||
|
// const { data, status } = await axios.get<Array<AnimeTitle>>(`${controllerUrl}List`,
|
||||||
|
const { data } = await axios.get<Array<AnimeTitle>>(`${controllerUrl}List`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GetAnimeTitleDetail(id: string) {
|
||||||
|
try {
|
||||||
|
// const { data, status } = await axios.get<Array<AnimeTitle>>(`${controllerUrl}List`,
|
||||||
|
const { data } = await axios.get<AnimeTitle>(`${controllerUrl}?TitleId=${id}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function RateAnimeTitle(model: RateEditModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}Rate`, model, axiosConfig);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function UnrateAnimeTitle(model: RateDeleteModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}Unrate`, model, axiosConfig);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function CreateAnimeTitle(model: TitleCreateModel) {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
// const { data, status } = await axios.post<string>(`${controllerUrl}Create`,
|
||||||
|
// {
|
||||||
|
// // headers: {
|
||||||
|
// // // Accept: 'text/plain',
|
||||||
|
// // },
|
||||||
|
// // body: model
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}Create`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//- - - - - - - - - - COMMON PROPERTIES - - - - - - - - - -
|
||||||
|
export async function CreateAnimeTitleName(model: CreateNameModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}AddName`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function EditAnimeTitleName(model: EditNameModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}EditName`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DeleteAnimeTitleName(model: DeleteNameModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}DeleteName`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function SetAnimeTitlePreview(model: CreateMediaInfoModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}SetPreview`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ClearAnimeTitlePreview(model: DeleteMediaInfoModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}ClearPreview`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function CreateAnimeTitleDescription(model: CreateDescriptionModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}AddDescription`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function EditAnimeTitleDescription(model: EditDescriptionModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}EditDescription`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DeleteAnimeTitleDescription(model: DeleteDescriptionModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}DeleteDescription`, model, axiosConfig);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function SetAnimeTitleAnnouncementDate(model: EditDateModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}SetAnnouncementDate`, model, axiosConfig);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function SetAnimeTitleEstimatedReleaseDate(model: EditDateModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}SetEstimatedReleaseDate`, model, axiosConfig);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function SetAnimeTitleReleaseDate(model: EditDateModel) {
|
||||||
|
try {
|
||||||
|
const axiosConfig = {
|
||||||
|
headers:{
|
||||||
|
Accept: 'text/plain',
|
||||||
|
ContentType: 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data, status } = await axios.post<string>(`${controllerUrl}SetReleaseDate`, model, axiosConfig);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// export async function EditGenre(id : string, value: string) {
|
||||||
|
// try {
|
||||||
|
// // 👇️ const data: GetUsersResponse
|
||||||
|
// const { data, status } = await axios.post<Array<Genre>>(`${controllerUrl}EditGenre?id=${id}&genreName=${value}`,
|
||||||
|
// {
|
||||||
|
// headers: {
|
||||||
|
// // Accept: 'text/plain',
|
||||||
|
// },
|
||||||
|
// // body: {}
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// // 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
// // return data;
|
||||||
|
// } catch (error) {
|
||||||
|
// if (axios.isAxiosError(error)) {
|
||||||
|
// console.log('error message: ', error.message);
|
||||||
|
// // return [];
|
||||||
|
// //return error.message;
|
||||||
|
// } else {
|
||||||
|
// console.log('unexpected error: ', error);
|
||||||
|
// // return [];
|
||||||
|
// //return 'An unexpected error occurred';
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
108
mybookmark.ui/src/api/GenreApi.tsx
Normal file
108
mybookmark.ui/src/api/GenreApi.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { Genre } from "./models/Types";
|
||||||
|
import { CatchError } from "./common";
|
||||||
|
|
||||||
|
const controllerUrl = 'https://localhost:7057/Dictionaries/Genre/';
|
||||||
|
|
||||||
|
export async function GetGenreList() {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
// const { data, status } = await axios.get<Array<Genre>>(`${controllerUrl}GenreList`,
|
||||||
|
const { data, } = await axios.get<Array<Genre>>(`${controllerUrl}GenreList`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function CreateGenre(value: string) {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
// const { data, status } = await axios.post<Array<Genre>>(`${controllerUrl}CreateGenre?genreName=${value}`,
|
||||||
|
await axios.post(`${controllerUrl}CreateGenre?genreName=${value}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
// Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
// body: {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
// return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function EditGenre(id : string, value: string) {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
const { status } = await axios.post(`${controllerUrl}EditGenre?id=${id}&genreName=${value}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
// Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
// body: {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
console.log('response status is: ', status);
|
||||||
|
|
||||||
|
// return Promise.resolve();
|
||||||
|
// Promise.resolve();
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DeleteGenre(id : string) {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
const { status } = await axios.post(`${controllerUrl}DeleteGenre?id=${id}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
// Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
// body: {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
console.log('response status is: ', status);
|
||||||
|
|
||||||
|
// return Promise.resolve();
|
||||||
|
// Promise.resolve();
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
mybookmark.ui/src/api/LanguageApi.tsx
Normal file
109
mybookmark.ui/src/api/LanguageApi.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { Language } from "./models/Types";
|
||||||
|
import { CatchError } from "./common";
|
||||||
|
|
||||||
|
const controllerUrl = 'https://localhost:7057/Dictionaries/Language/';
|
||||||
|
|
||||||
|
export async function GetLanguageList() {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
// const { data, status } = await axios.get<Array<Language>>(`${controllerUrl}List`,
|
||||||
|
const { data} = await axios.get<Array<Language>>(`${controllerUrl}List`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function CreateLanguage(codeIso3: string, name: string) {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
// const { data, status } = await axios.post<Array<Genre>>(`${controllerUrl}CreateGenre?genreName=${value}`,
|
||||||
|
await axios.post(`${controllerUrl}Create?codeIso3=${codeIso3}&name=${name}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
// Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
// body: {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
// return data;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function EditLanguage(id : string, codeIso3: string, name: string) {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
// const { status } = await axios.post(`${controllerUrl}Edit?id=${id}&codeIso3=${codeIso3}&name=${name}`,
|
||||||
|
await axios.post(`${controllerUrl}Edit?id=${id}&codeIso3=${codeIso3}&name=${name}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
// Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
// body: {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
// console.log('response status is: ', status);
|
||||||
|
|
||||||
|
// return Promise.resolve();
|
||||||
|
// Promise.resolve();
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DeleteLanguage(id : string) {
|
||||||
|
try {
|
||||||
|
// 👇️ const data: GetUsersResponse
|
||||||
|
const { status } = await axios.post(`${controllerUrl}Delete?id=${id}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
// Accept: 'text/plain',
|
||||||
|
},
|
||||||
|
// body: {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(data, null, 4));
|
||||||
|
|
||||||
|
// 👇️ "response status is: 200"
|
||||||
|
console.log('response status is: ', status);
|
||||||
|
|
||||||
|
// return Promise.resolve();
|
||||||
|
// Promise.resolve();
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
CatchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
mybookmark.ui/src/api/common.tsx
Normal file
14
mybookmark.ui/src/api/common.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import axios, { AxiosError } from "axios";
|
||||||
|
import { ProblemDetails } from "./models/Types";
|
||||||
|
|
||||||
|
export function CatchError(error: unknown){
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
console.error('error message: ', error.message);
|
||||||
|
const problemDetails = (error as AxiosError)?.response?.data as ProblemDetails;
|
||||||
|
if (problemDetails?.title) throw new Error(problemDetails.title);
|
||||||
|
}
|
||||||
|
console.error('unexpected error: ', error);
|
||||||
|
// return [];
|
||||||
|
//return 'An unexpected error occurred';
|
||||||
|
throw new Error(`unexpected error: ${(error as Error)?.message ?? JSON.stringify(error, null, 4)}`);
|
||||||
|
}
|
||||||
193
mybookmark.ui/src/api/models/Types.tsx
Normal file
193
mybookmark.ui/src/api/models/Types.tsx
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
export type Entity = {
|
||||||
|
id : string,
|
||||||
|
deleted: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum MediaInfoType
|
||||||
|
{
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Link,
|
||||||
|
OtherFile
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MediaInfo = {
|
||||||
|
type: MediaInfoType,
|
||||||
|
contentType: string,
|
||||||
|
url: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Language = {
|
||||||
|
codeIso3: string,
|
||||||
|
name: string,
|
||||||
|
icon?: MediaInfo,
|
||||||
|
} & Entity
|
||||||
|
|
||||||
|
export type Genre = {
|
||||||
|
name: string,
|
||||||
|
} & Entity
|
||||||
|
|
||||||
|
export enum NameType
|
||||||
|
{
|
||||||
|
Original,
|
||||||
|
OriginalInAnotherLanguage,
|
||||||
|
Translation,
|
||||||
|
Abbreviation,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NameItem = {
|
||||||
|
value: string,
|
||||||
|
type: NameType,
|
||||||
|
language: Language,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Description = {
|
||||||
|
value: string,
|
||||||
|
isOriginal: boolean,
|
||||||
|
language: Language,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GenreProportion = {
|
||||||
|
proportion: number,
|
||||||
|
genre: Genre,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CommonProperties = {
|
||||||
|
names: Array<NameItem>,
|
||||||
|
preview?: MediaInfo,
|
||||||
|
descriptions: Array<Description>,
|
||||||
|
genres: Array<GenreProportion>,
|
||||||
|
relatedContent: Array<MediaInfo>,
|
||||||
|
// announcementDate?: string | Date | null,
|
||||||
|
announcementDate?: string | null,
|
||||||
|
// estimatedReleaseDate?: Date | null,
|
||||||
|
estimatedReleaseDate?: string | null,
|
||||||
|
// releaseDate?: Date | null,
|
||||||
|
releaseDate?: string | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AnimeItem = {
|
||||||
|
completed: boolean,
|
||||||
|
commonProperties: CommonProperties,
|
||||||
|
number?: number,
|
||||||
|
order: number,
|
||||||
|
expirationTime: Date,
|
||||||
|
} & Entity
|
||||||
|
|
||||||
|
export enum AnimeEpisodeType
|
||||||
|
{
|
||||||
|
Regilar,
|
||||||
|
FullLength,
|
||||||
|
Ova,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Episode = {
|
||||||
|
variant: number,
|
||||||
|
type: AnimeEpisodeType,
|
||||||
|
duration: Date,
|
||||||
|
} & AnimeItem
|
||||||
|
|
||||||
|
export type Season = {
|
||||||
|
episodes: Array<Episode>,
|
||||||
|
director?: string,
|
||||||
|
originCountry?: string,
|
||||||
|
} & AnimeItem
|
||||||
|
|
||||||
|
export type AnimeTitle = {
|
||||||
|
commonProperties: CommonProperties,
|
||||||
|
rate?: number,
|
||||||
|
myRate?: number,
|
||||||
|
seasons: Array<Season>,
|
||||||
|
episodes: Array<Episode>,
|
||||||
|
completed: boolean,
|
||||||
|
episodesInsideSeasonsCount: number,
|
||||||
|
expirationTime: Date,
|
||||||
|
} & Entity
|
||||||
|
|
||||||
|
export type ProblemDetails = {
|
||||||
|
type?: string,
|
||||||
|
title?: string,
|
||||||
|
status?: number,
|
||||||
|
detail?: string,
|
||||||
|
instance?: string,
|
||||||
|
extensions: Map<string, object>
|
||||||
|
}
|
||||||
|
|
||||||
|
/* POST Models */
|
||||||
|
|
||||||
|
export type TitleCreateModel = {
|
||||||
|
originalName: CreateNameModel,
|
||||||
|
preview?: MediaInfo | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreateNameModel = {
|
||||||
|
animeTitleId?: string | null,
|
||||||
|
animeSeasonId?: string | null,
|
||||||
|
animeEpisodeId?: string | null,
|
||||||
|
languageId: string,
|
||||||
|
value: string,
|
||||||
|
type: NameType
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EditNameModel = {
|
||||||
|
newLanguageId?: string | null,
|
||||||
|
newValue?: string | null,
|
||||||
|
} & CreateNameModel
|
||||||
|
|
||||||
|
export type DeleteNameModel = { } & CreateNameModel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type CreateDescriptionModel = {
|
||||||
|
animeTitleId?: string | null,
|
||||||
|
animeSeasonId?: string | null,
|
||||||
|
animeEpisodeId?: string | null,
|
||||||
|
languageId: string,
|
||||||
|
value: string,
|
||||||
|
isOriginal: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EditDescriptionModel = {
|
||||||
|
newLanguageId?: string | null,
|
||||||
|
newValue?: string | null,
|
||||||
|
} & CreateDescriptionModel
|
||||||
|
|
||||||
|
export type DeleteDescriptionModel = { } & CreateDescriptionModel
|
||||||
|
|
||||||
|
export type CreateMediaInfoModel = {
|
||||||
|
animeTitleId?: string | null,
|
||||||
|
animeSeasonId?: string | null,
|
||||||
|
animeEpisodeId?: string | null,
|
||||||
|
type: MediaInfoType,
|
||||||
|
url: string,
|
||||||
|
contentType: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EditMediaInfoModel = {
|
||||||
|
type?: MediaInfoType | null,
|
||||||
|
url?: string | null,
|
||||||
|
contentType?: string | null,
|
||||||
|
} & CreateMediaInfoModel
|
||||||
|
|
||||||
|
export type DeleteMediaInfoModel = { } & CreateMediaInfoModel
|
||||||
|
|
||||||
|
|
||||||
|
export type EditDateModel = {
|
||||||
|
animeTitleId?: string | null,
|
||||||
|
animeSeasonId?: string | null,
|
||||||
|
animeEpisodeId?: string | null,
|
||||||
|
value?: Date | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RateEditModel = {
|
||||||
|
animeTitleId?: string | null,
|
||||||
|
animeSeasonId?: string | null,
|
||||||
|
animeEpisodeId?: string | null,
|
||||||
|
ratePercentage: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RateDeleteModel = {
|
||||||
|
animeTitleId?: string | null,
|
||||||
|
animeSeasonId?: string | null,
|
||||||
|
animeEpisodeId?: string | null,
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
//API Urls
|
//API Urls
|
||||||
|
|
||||||
|
export const baseUrl = 'https://localhost:7057';
|
||||||
|
|
||||||
export const apiUrls = {
|
export const apiUrls = {
|
||||||
auth: "/auth/v1/",
|
auth: "/auth/v1/",
|
||||||
product: "/customerapi/v1/Product",
|
product: "/customerapi/v1/Product",
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user