diff --git a/src/BukiVedi.App/Controllers/AuthController.cs b/src/BukiVedi.App/Controllers/AuthController.cs index 67e7a74..404e591 100644 --- a/src/BukiVedi.App/Controllers/AuthController.cs +++ b/src/BukiVedi.App/Controllers/AuthController.cs @@ -2,7 +2,10 @@ using BukiVedi.App.Responces; using BukiVedi.Shared.Models; using BukiVedi.Shared.Services; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; namespace BukiVedi.App.Controllers { @@ -27,10 +30,21 @@ namespace BukiVedi.App.Controllers return Ok(new AutoCodeResponse { Success = false, ReasonPhrase = "Аккаунт не найден" }); } var token = UserTokenGenerator.GenerateUserToken(acc); + + var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, + ClaimTypes.Name, ClaimTypes.Role); + identity.AddClaim(new Claim("Subject", acc.Login)); + identity.AddClaim(new Claim("Token", token)); + await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(identity), + new AuthenticationProperties + { + IsPersistent = true + }); + return Ok(new AutoCodeResponse { - Success = true, - Token = token + Success = true }); } } diff --git a/src/BukiVedi.App/Controllers/BooksController.cs b/src/BukiVedi.App/Controllers/BooksController.cs index a0f0d4d..2334cd2 100644 --- a/src/BukiVedi.App/Controllers/BooksController.cs +++ b/src/BukiVedi.App/Controllers/BooksController.cs @@ -1,12 +1,15 @@ using BukiVedi.App.Requests; using BukiVedi.App.Responces; using BukiVedi.App.Services.Mappers; +using BukiVedi.Shared.Entities; using BukiVedi.Shared.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using MongoDB.Driver; namespace BukiVedi.App.Controllers { + [Authorize("authorized")] [ApiController] public class BooksController : BaseController @@ -18,7 +21,6 @@ namespace BukiVedi.App.Controllers _library = library; } - //[Authorize] [HttpPost("/api/books/search")] public async Task>> Search([FromBody] QueryRequest request) { @@ -26,11 +28,113 @@ namespace BukiVedi.App.Controllers return Ok(books.Select(b => BookEntityMapper.Map(b))); } - [Authorize] - [HttpPost("/api/books/aisearch")] - public async Task>> AiSearch([FromBody] QueryRequest request) + + [HttpPost("/api/books/search/author/{id}")] + public async Task>> SearchByAuthor([FromRoute] string id) { - var books = (await _library.SearchBooks(request.Query)).ToArray(); + var books = (await _library.SearchBooksByAuthor(id)).ToArray(); + return Ok(books.Select(b => BookEntityMapper.Map(b))); + } + + + [HttpPost("/api/books/{id}/favorite")] + public async Task>> AddToFavorite([FromRoute] string id) + { + if (await Tables.Books.ExistById(id)) + { + var account_id = OperationContext.OperationInitiator.Id; + if (!string.IsNullOrEmpty(account_id)) + { + var exists_fiter = Builders.Filter.And + ( + Builders.Filter.Eq(f => f.UserId, account_id), + Builders.Filter.Eq(f => f.BookId, id) + ); + if (await Tables.FavoriteBooks.Exists(exists_fiter) == false) + { + await Tables.FavoriteBooks.Write(new FavoriteBook { BookId = id, UserId = account_id }); + } + } + } + return Ok(); + } + + [HttpPost("/api/books/{id}/block")] + public async Task>> BlockBook([FromRoute] string id) + { + if (await Tables.Books.ExistById(id)) + { + var account_id = OperationContext.OperationInitiator.Id; + if (!string.IsNullOrEmpty(account_id)) + { + var exists_fiter = Builders.Filter.And + ( + Builders.Filter.Eq(f => f.UserId, account_id), + Builders.Filter.Eq(f => f.BookId, id) + ); + if (await Tables.DisgustingBooks.Exists(exists_fiter) == false) + { + await Tables.DisgustingBooks.Write(new DisgustingBook { BookId = id, UserId = account_id }); + } + } + } + return Ok(); + } + + [HttpPost("/api/books/{id}/author/block")] + public async Task>> BlockBookAuthor([FromRoute] string id) + { + var book = await Tables.Books.GetById(id); + if (book != null) + { + var account_id = OperationContext.OperationInitiator.Id; + var authors = book.AuthorIds; + if (!string.IsNullOrEmpty(account_id) && authors?.Count > 0) + { + foreach (var author in authors) + { + var exists_fiter = Builders.Filter.And + ( + Builders.Filter.Eq(f => f.UserId, account_id), + Builders.Filter.Eq(f => f.AuthorId, author) + ); + if (await Tables.DisgustingAuthors.Exists(exists_fiter) == false) + { + await Tables.DisgustingAuthors.Write(new DisgustingAuthor { AuthorId = author, UserId = account_id }); + } + } + } + } + return Ok(); + } + + [HttpPost("/api/books/{id}/read")] + public async Task>> AddBookToReadingQueue([FromRoute] string id) + { + if (await Tables.Books.ExistById(id)) + { + var account_id = OperationContext.OperationInitiator.Id; + if (!string.IsNullOrEmpty(account_id)) + { + var exists_fiter = Builders.Filter.And + ( + Builders.Filter.Eq(f => f.UserId, account_id), + Builders.Filter.Eq(f => f.BookId, id) + ); + if (await Tables.ReadQueue.Exists(exists_fiter) == false) + { + await Tables.ReadQueue.Write(new ReadQueueItem { BookId = id, UserId = account_id, Timestamp = Timestamp.UtcNow }); + } + } + } + return Ok(); + } + + + [HttpPost("/api/books/author/{id}")] + public async Task>> AiSearch([FromRoute] string id) + { + var books = (await _library.SearchBooksByAuthor(id)).ToArray(); return Ok(books.Select(b => BookEntityMapper.Map(b))); } diff --git a/src/BukiVedi.App/Middlewares/AuthExtractMiddleware.cs b/src/BukiVedi.App/Middlewares/AuthExtractMiddleware.cs index 83160ab..bdff4f8 100644 --- a/src/BukiVedi.App/Middlewares/AuthExtractMiddleware.cs +++ b/src/BukiVedi.App/Middlewares/AuthExtractMiddleware.cs @@ -1,10 +1,8 @@ using BukiVedi.Shared; using BukiVedi.Shared.Models; using BukiVedi.Shared.Services; -using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc.Controllers; using System.Net; -using System.Security.Claims; using ZeroLevel; using ZeroLevel.Services.Serialization; @@ -21,7 +19,6 @@ namespace BukiVedi.App.Middlewares public class BukiVediAuthMiddleware { - private const string TOKEN_HEADER = "X-Token"; private readonly IAuthProvider _authProvider; private readonly RequestDelegate _next; @@ -34,8 +31,6 @@ namespace BukiVedi.App.Middlewares public async Task Invoke(HttpContext context) { await ReadDataFromContext(context); - context.Response.Headers.Add("server_time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz")); - context.Response.Headers.Add("server_time_ts", Timestamp.UtcNow.ToString()); try { await _next(context); @@ -74,28 +69,15 @@ namespace BukiVedi.App.Middlewares private async Task ReadDataFromContext(HttpContext context) { var op_context = new OperationContext(_authProvider, Timestamp.UtcNow); - string token = context.Request?.Headers[TOKEN_HEADER]!; - if (string.IsNullOrWhiteSpace(token)) - { - token = context.Request?.Cookies[TOKEN_HEADER]!; - } + string token = context.User?.Claims?.FirstOrDefault(c => c.Type.Equals("Token"))?.Value!; if (string.IsNullOrWhiteSpace(token) == false) { // TRY AUTHORIZE var authData = ReadAccountInfoFromToken(token); if (authData != null) { - var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, - ClaimTypes.Name, ClaimTypes.Role); - identity.AddClaim(new Claim("Subject", authData.Login)); var account = await _authProvider.GetAccountByLogin(authData.Login); - await op_context.SetAccount(account); - - if (account != null) - { - context.User.AddIdentity(identity); - } context.Items["op_context"] = op_context; return; } diff --git a/src/BukiVedi.App/Program.cs b/src/BukiVedi.App/Program.cs index 4900493..557ac95 100644 --- a/src/BukiVedi.App/Program.cs +++ b/src/BukiVedi.App/Program.cs @@ -3,7 +3,10 @@ using BukiVedi.Shared; using BukiVedi.Shared.Entities; using BukiVedi.Shared.Models; using BukiVedi.Shared.Services; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; using System.Net; using System.Text; using ZeroLevel; @@ -38,9 +41,15 @@ namespace BukiVedi.App Log.Error(ex, "Fault services initialization"); return; } - var app = builder.Build(); + app.UseCookiePolicy(new CookiePolicyOptions + { + MinimumSameSitePolicy = SameSiteMode.Strict, + }); + + app.UseAuthentication(); + app.UseAuthorization(); app.UseBukiVediAuthMiddleware(); @@ -55,8 +64,7 @@ namespace BukiVedi.App var path = ctx.Context.Request.Path; if (path.HasValue && path.Value.Contains("index.html", StringComparison.OrdinalIgnoreCase)) { - var context = ctx.Context.Items["op_context"] as OperationContext; - if (context == null || context.OperationInitiator == null) + if (ctx.Context.User == null) { ctx.Context.Response.ContentLength = 0; ctx.Context.Response.Body = Stream.Null; @@ -86,8 +94,20 @@ namespace BukiVedi.App } services.AddSingleton(authProvider); services.AddSingleton(new Library()); - services.AddAuthentication(); - services.AddAuthorization(); + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); + + services.AddAuthorization(options => + { + options.AddPolicy("authorized", policy => + { + policy.RequireAuthenticatedUser(); + policy.AuthenticationSchemes = new List() + { + CookieAuthenticationDefaults.AuthenticationScheme + }; + }); + }); + services.AddControllers(); } } diff --git a/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.App.pdb b/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.App.pdb index 5a48303..d9070a0 100644 Binary files a/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.App.pdb and b/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.App.pdb differ diff --git a/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.Shared.pdb b/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.Shared.pdb index 0d5880c..4520f05 100644 Binary files a/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.Shared.pdb and b/src/BukiVedi.App/bin/Debug/net8.0/BukiVedi.Shared.pdb differ diff --git a/src/BukiVedi.App/bin/Debug/net8.0/web/index.html b/src/BukiVedi.App/bin/Debug/net8.0/web/index.html index cc3555a..6b20881 100644 --- a/src/BukiVedi.App/bin/Debug/net8.0/web/index.html +++ b/src/BukiVedi.App/bin/Debug/net8.0/web/index.html @@ -31,6 +31,7 @@ } #search { + display: block; margin-top: 20px; margin-left: auto; margin-right: auto; @@ -43,9 +44,12 @@ } #searchButton { + display: block; + margin-top: 10px; margin-left: auto; margin-right: auto; - max-width: 160px; + max-width: 200px; + width: 200px; min-width: 80px; height: 33px; } @@ -106,6 +110,7 @@ margin-left: 30px; padding-bottom: 5px; color: #06001aff; + width: 100%; } .card .unassigned { @@ -115,6 +120,46 @@ .card .due { color: #ec7373; } + + + .menu { + visibility: hidden; + z-index: 1000; + position: relative; + height: 0; + background-color: #171717; + } + + .open-menu { + text-decoration: none; + font-size: 18px; + font-weight: bolder; + } + + .menu.opened { + visibility: visible; + color: azure; + width: 180px; + } + + .menu-item { + text-decoration: none; + background-color: #272727; + height: 28px; + width: 100%; + text-align: left; + padding-top: 12px; + padding-left: 16px; + cursor: pointer; + } + + .menu-item:hover { + background-color: #373737; + } + + ul { + list-style-type: none; + } @@ -127,47 +172,134 @@