1. Search by author
2. List of all authors in web
3. Favorite
4. Write block authors and books in db
5. Write to reading queue
6. Book context menu
main
Ogoun 6 months ago
parent 4e697c91a8
commit c8a5574633

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

@ -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<ActionResult<IEnumerable<BookResponse>>> 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<ActionResult<IEnumerable<BookResponse>>> AiSearch([FromBody] QueryRequest request)
[HttpPost("/api/books/search/author/{id}")]
public async Task<ActionResult<IEnumerable<BookResponse>>> 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<ActionResult<IEnumerable<BookResponse>>> 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<FavoriteBook>.Filter.And
(
Builders<FavoriteBook>.Filter.Eq(f => f.UserId, account_id),
Builders<FavoriteBook>.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<ActionResult<IEnumerable<BookResponse>>> 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<DisgustingBook>.Filter.And
(
Builders<DisgustingBook>.Filter.Eq(f => f.UserId, account_id),
Builders<DisgustingBook>.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<ActionResult<IEnumerable<BookResponse>>> 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<DisgustingAuthor>.Filter.And
(
Builders<DisgustingAuthor>.Filter.Eq(f => f.UserId, account_id),
Builders<DisgustingAuthor>.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<ActionResult<IEnumerable<BookResponse>>> 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<ReadQueueItem>.Filter.And
(
Builders<ReadQueueItem>.Filter.Eq(f => f.UserId, account_id),
Builders<ReadQueueItem>.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<ActionResult<IEnumerable<BookResponse>>> AiSearch([FromRoute] string id)
{
var books = (await _library.SearchBooksByAuthor(id)).ToArray();
return Ok(books.Select(b => BookEntityMapper.Map(b)));
}

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

@ -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<IAuthProvider>(authProvider);
services.AddSingleton<ILibrary>(new Library());
services.AddAuthentication();
services.AddAuthorization();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
services.AddAuthorization(options =>
{
options.AddPolicy("authorized", policy =>
{
policy.RequireAuthenticatedUser();
policy.AuthenticationSchemes = new List<string>()
{
CookieAuthenticationDefaults.AuthenticationScheme
};
});
});
services.AddControllers();
}
}

@ -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;
}
</style>
</head>
<body>
@ -127,47 +172,134 @@
</div>
<script>
const MenuActions =
{
AddToFavorite: '0',
ToReadingQueue: '1',
BookBlock: '2',
AuthorBlock: '3',
Tag: '4',
Note: '5',
Share: '6'
};
function Tag(id) { }
function Note(id) { }
function Share(id) { }
function eraseCookie(name) {
document.cookie = name + '=; Max-Age=-99999999;';
}
function search(searchText) {
BukiVedi.Search(searchText, books => {
$("#books").empty();
for (var idx in books) {
let data = books[idx];
function bindMenuButton(menuId, buttonId) {
var openMenuBtn = document.getElementById(buttonId);
openMenuBtn.addEventListener('click', function (e) {
e.preventDefault();
var menu = document.getElementById(menuId);
menu.classList.toggle('opened');
}, false);
//window.addEventListener('click', function (e) { if (menu.classList.contains('opened') && !document.getElementById('menu').contains(e.target) && !document.getElementById('menuBtn').contains(e.target)) { menu.classList.toggle('opened'); } })
}
function proceedMenuAction(bookId, menuId, actionType) {
var menu = document.getElementById(menuId);
menu.classList.toggle('opened');
switch (actionType) {
case MenuActions.AddToFavorite:
BukiVedi.AddToFavorite(bookId);
break;
case MenuActions.ToReadingQueue:
BukiVedi.ToReadingQueue(bookId);
break;
case MenuActions.BookBlock:
BukiVedi.BookBlock(bookId);
break;
case MenuActions.AuthorBlock:
BukiVedi.AuthorBlock(bookId);
break;
case MenuActions.Tag:
Tag(bookId);
break;
case MenuActions.Note:
Note(bookId);
break;
case MenuActions.Share:
Share(bookId);
break;
}
}
const book = $('<div>').addClass('card');
function appendBooksToContent(books) {
$("#books").empty();
// Кнопка скачать
const download = $('<a>').on('click', () => BukiVedi.Download(data.id));
$('<div>').addClass('card-circle').appendTo(download);
download.appendTo(book);
for (var idx in books) {
const data = books[idx];
let id = data.id;
//автор
let author = '--';
var authorLinks = [];
if (data.authors.length > 0) {
author = data.authors[0].name;
for (idx in data.authors) {
author = data.authors[idx].name;
author_id = data.authors[idx].id;
authorLinks.push("<a onclick=\"BukiVedi.SearchByAuthor('" + author_id + "', appendBooksToContent)\">" + author + "</a>");
authorLinks.push("<strong>;</strong>");
}
}
const author_genre = $('<p>').addClass('author').text(author);
$('<string>').text(' · ').appendTo(author_genre);
// Жанр
let genre = '--';
if (data.genres.length > 0) {
genre = data.genres[0].name;
}
$('<span>').addClass('due').text(genre).appendTo(author_genre);
author_genre.appendTo(book);
if (genre == null) genre = '--';
let menuId = "menu_" + id;
let btnId = "menuBtn_" + id;
// Название
$('<p>').text(data.title).appendTo(book);
$('<p>').addClass('desc').text(data.description).appendTo(book);
$('#books').append(
$("<div class='card'>"
+ "<a onclick=\"BukiVedi.Download('" + id + "')\">"
+ "<div class='card-circle'></div>"
+ "</a>"
+ "<div style='display: float;'>"
+ "<a id='"
+ btnId
+ "' href='#' class='open-menu'>···</a>"
+ "<nav id='"
+ menuId
+ "' class='menu'><ul>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.AddToFavorite + "')\" class='menu-item'>В избранное</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.ToReadingQueue + "')\"class='menu-item'>В очередь на чтение</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.BookBlock + "')\"class='menu-item'>Игнорировать</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.AuthorBlock + "')\"class='menu-item'>Игнорировать автора</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.Tag + "')\"class='menu-item'>Теги</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.Note + "')\"class='menu-item'>Заметка</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.Share + "')\"class='menu-item'>Поделиться</li>"
+ "</ul></nav>"
+ "</div>"
+ "<p class='author'>"
+ authorLinks.join("")
+ "<string> · </string><span class='due'>"
+ genre
+ "</span>"
+ "</p>"
+ "<p>"
+ data.title
+ "</p>"
+ "<p class='desc'>"
+ data.description
+ "</p>"
+ "</div>")
);
$('#books').append(book);
bindMenuButton(menuId, btnId);
}
});
}
function search(searchText) {
BukiVedi.Search(searchText, books => appendBooksToContent(books));
}
$(() => {

@ -1,4 +1,19 @@
class BukiVedi {
static SearchByAuthor(id, success) {
$.ajax({
contentType: "application/json; charset=utf-8",
type: "POST",
url: "../api/books/search/author/" + id,
success: function (data, textStatus, jqXHR) {
success(data);
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static Search(query, success) {
const info = { "query": query };
$.ajax({
@ -20,6 +35,59 @@
window.location.href = "../api/books/download/" + id;
}
static AddToFavorite(id, success) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/favorite",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static ToReadingQueue(id) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/read",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static BookBlock(id) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/block",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static AuthorBlock(id) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/author/block",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static Logout() {
localStorage.removeItem(this._token_name);
window.location.replace("web/login.html");

@ -107,8 +107,6 @@
const pwd = document.getElementById('password').value;
BukiVediAuth.SignIn(name, pwd, (data) => {
if (data.success == true) {
localStorage.setItem("token", data.token);
document.cookie = "X-Token=" + data.token;
window.location.replace("index.html");
}
else {

@ -34,17 +34,121 @@
}
]
},
{
"ContainingType": "BukiVedi.App.Controllers.BooksController",
"Method": "BlockBookAuthor",
"RelativePath": "api/books/{id}/author/block",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.String",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "System.Collections.Generic.IEnumerable\u00601[[BukiVedi.App.Responces.BookResponse, BukiVedi.App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "BukiVedi.App.Controllers.BooksController",
"Method": "BlockBook",
"RelativePath": "api/books/{id}/block",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.String",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "System.Collections.Generic.IEnumerable\u00601[[BukiVedi.App.Responces.BookResponse, BukiVedi.App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "BukiVedi.App.Controllers.BooksController",
"Method": "AddToFavorite",
"RelativePath": "api/books/{id}/favorite",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.String",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "System.Collections.Generic.IEnumerable\u00601[[BukiVedi.App.Responces.BookResponse, BukiVedi.App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "BukiVedi.App.Controllers.BooksController",
"Method": "AddBookToReadingQueue",
"RelativePath": "api/books/{id}/read",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.String",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "System.Collections.Generic.IEnumerable\u00601[[BukiVedi.App.Responces.BookResponse, BukiVedi.App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
},
{
"ContainingType": "BukiVedi.App.Controllers.BooksController",
"Method": "AiSearch",
"RelativePath": "api/books/aisearch",
"RelativePath": "api/books/author/{id}",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "request",
"Type": "BukiVedi.App.Requests.QueryRequest",
"Name": "id",
"Type": "System.String",
"IsRequired": true
}
],
@ -101,5 +205,31 @@
"StatusCode": 200
}
]
},
{
"ContainingType": "BukiVedi.App.Controllers.BooksController",
"Method": "SearchByAuthor",
"RelativePath": "api/books/search/author/{id}",
"HttpMethod": "POST",
"IsController": true,
"Order": 0,
"Parameters": [
{
"Name": "id",
"Type": "System.String",
"IsRequired": true
}
],
"ReturnTypes": [
{
"Type": "System.Collections.Generic.IEnumerable\u00601[[BukiVedi.App.Responces.BookResponse, BukiVedi.App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]",
"MediaTypes": [
"text/plain",
"application/json",
"text/json"
],
"StatusCode": 200
}
]
}
]

@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("BukiVedi.App")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+7eed3bd890d6fea441a83f8139430004eb71fe5e")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4e697c91a83d95731e2cea4131399178264fd6a2")]
[assembly: System.Reflection.AssemblyProductAttribute("BukiVedi.App")]
[assembly: System.Reflection.AssemblyTitleAttribute("BukiVedi.App")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

@ -1 +1 @@
827c2c1ccbda85cec2cca48419c134275d2524c227339cc52e803bf1942355b5
9d9d30a119e571814faa5f6aa186f82345b545bc19591da9baa14505e2ac9e3d

@ -110,6 +110,7 @@
margin-left: 30px;
padding-bottom: 5px;
color: #06001aff;
width: 100%;
}
.card .unassigned {
@ -119,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;
}
</style>
</head>
<body>
@ -131,47 +172,134 @@
</div>
<script>
const MenuActions =
{
AddToFavorite: '0',
ToReadingQueue: '1',
BookBlock: '2',
AuthorBlock: '3',
Tag: '4',
Note: '5',
Share: '6'
};
function Tag(id) { }
function Note(id) { }
function Share(id) { }
function eraseCookie(name) {
document.cookie = name + '=; Max-Age=-99999999;';
}
function search(searchText) {
BukiVedi.Search(searchText, books => {
$("#books").empty();
for (var idx in books) {
let data = books[idx];
function bindMenuButton(menuId, buttonId) {
var openMenuBtn = document.getElementById(buttonId);
openMenuBtn.addEventListener('click', function (e) {
e.preventDefault();
var menu = document.getElementById(menuId);
menu.classList.toggle('opened');
}, false);
//window.addEventListener('click', function (e) { if (menu.classList.contains('opened') && !document.getElementById('menu').contains(e.target) && !document.getElementById('menuBtn').contains(e.target)) { menu.classList.toggle('opened'); } })
}
const book = $('<div>').addClass('card');
function proceedMenuAction(bookId, menuId, actionType) {
var menu = document.getElementById(menuId);
menu.classList.toggle('opened');
switch (actionType) {
case MenuActions.AddToFavorite:
BukiVedi.AddToFavorite(bookId);
break;
case MenuActions.ToReadingQueue:
BukiVedi.ToReadingQueue(bookId);
break;
case MenuActions.BookBlock:
BukiVedi.BookBlock(bookId);
break;
case MenuActions.AuthorBlock:
BukiVedi.AuthorBlock(bookId);
break;
case MenuActions.Tag:
Tag(bookId);
break;
case MenuActions.Note:
Note(bookId);
break;
case MenuActions.Share:
Share(bookId);
break;
}
}
// Кнопка скачать
const download = $('<a>').on('click', () => BukiVedi.Download(data.id));
$('<div>').addClass('card-circle').appendTo(download);
download.appendTo(book);
function appendBooksToContent(books) {
$("#books").empty();
for (var idx in books) {
const data = books[idx];
let id = data.id;
//автор
let author = '--';
var authorLinks = [];
if (data.authors.length > 0) {
author = data.authors[0].name;
for (idx in data.authors) {
author = data.authors[idx].name;
author_id = data.authors[idx].id;
authorLinks.push("<a onclick=\"BukiVedi.SearchByAuthor('" + author_id + "', appendBooksToContent)\">" + author + "</a>");
authorLinks.push("<strong>;</strong>");
}
}
const author_genre = $('<p>').addClass('author').text(author);
$('<string>').text(' · ').appendTo(author_genre);
// Жанр
let genre = '--';
if (data.genres.length > 0) {
genre = data.genres[0].name;
}
$('<span>').addClass('due').text(genre).appendTo(author_genre);
author_genre.appendTo(book);
if (genre == null) genre = '--';
// Название
$('<p>').text(data.title).appendTo(book);
$('<p>').addClass('desc').text(data.description).appendTo(book);
let menuId = "menu_" + id;
let btnId = "menuBtn_" + id;
$('#books').append(book);
$('#books').append(
$("<div class='card'>"
+ "<a onclick=\"BukiVedi.Download('" + id + "')\">"
+ "<div class='card-circle'></div>"
+ "</a>"
+ "<div style='display: float;'>"
+ "<a id='"
+ btnId
+ "' href='#' class='open-menu'>···</a>"
+ "<nav id='"
+ menuId
+ "' class='menu'><ul>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.AddToFavorite + "')\" class='menu-item'>В избранное</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.ToReadingQueue + "')\"class='menu-item'>В очередь на чтение</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.BookBlock + "')\"class='menu-item'>Игнорировать</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.AuthorBlock + "')\"class='menu-item'>Игнорировать автора</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.Tag + "')\"class='menu-item'>Теги</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.Note + "')\"class='menu-item'>Заметка</li>"
+ "<li onclick=\"proceedMenuAction('" + id + "', '" + menuId + "', '" + MenuActions.Share + "')\"class='menu-item'>Поделиться</li>"
+ "</ul></nav>"
+ "</div>"
+ "<p class='author'>"
+ authorLinks.join("")
+ "<string> · </string><span class='due'>"
+ genre
+ "</span>"
+ "</p>"
+ "<p>"
+ data.title
+ "</p>"
+ "<p class='desc'>"
+ data.description
+ "</p>"
+ "</div>")
);
bindMenuButton(menuId, btnId);
}
});
}
function search(searchText) {
BukiVedi.Search(searchText, books => appendBooksToContent(books));
}
$(() => {

@ -1,4 +1,19 @@
class BukiVedi {
static SearchByAuthor(id, success) {
$.ajax({
contentType: "application/json; charset=utf-8",
type: "POST",
url: "../api/books/search/author/" + id,
success: function (data, textStatus, jqXHR) {
success(data);
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static Search(query, success) {
const info = { "query": query };
$.ajax({
@ -20,6 +35,59 @@
window.location.href = "../api/books/download/" + id;
}
static AddToFavorite(id, success) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/favorite",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static ToReadingQueue(id) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/read",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static BookBlock(id) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/block",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static AuthorBlock(id) {
$.ajax({
type: "POST",
url: "../api/books/" + id + "/author/block",
success: function (data, textStatus, jqXHR) {
if (success)
success();
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR.statusText);
}
});
}
static Logout() {
localStorage.removeItem(this._token_name);
window.location.replace("web/login.html");

@ -107,8 +107,6 @@
const pwd = document.getElementById('password').value;
BukiVediAuth.SignIn(name, pwd, (data) => {
if (data.success == true) {
localStorage.setItem("token", data.token);
document.cookie = "X-Token=" + data.token;
window.location.replace("index.html");
}
else {

@ -21,7 +21,7 @@
/// <summary>
/// Авторы
/// </summary>
public string[] AuthorIds { get; set; }
public List<string> AuthorIds { get; set; }
/// <summary>
/// Формат
/// </summary>

@ -11,7 +11,7 @@ namespace BukiVedi.Shared.Services
{
public interface ILibrary
{
Task SearchBooksByAuthor(string name);
Task<IEnumerable<BookEntity>> SearchBooksByAuthor(string author_id);
Task<IEnumerable<BookEntity>> SearchBooks(string title);
Task DownloadToStream(Stream stream, string id);
}
@ -58,31 +58,37 @@ namespace BukiVedi.Shared.Services
}
}
public async Task SearchBooksByAuthor(string name)
public async Task<IEnumerable<BookEntity>> SearchBooksByAuthor(string author_id)
{
if (string.IsNullOrWhiteSpace(name) == false && name.Select(ch => char.IsLetter(ch)).Count() > 5)
if (string.IsNullOrWhiteSpace(author_id) == false)
{
var authorFilter = Builders<Author>.Filter.Eq<string>(a => a.Name, name);
var authors = await Tables.Authors.Get(authorFilter);
if (authors.Any())
var filter = Builders<Book>.Filter.AnyEq(b => b.AuthorIds, author_id);
var result = await Tables.Books.Get(filter);
if (result != null && result.Any())
{
var authorsIds = authors.Select(a => a.Id).ToArray();
var result = await Tables.Books.Get(Builders<Book>.Filter.ElemMatch(b => b.AuthorIds, a => authorsIds.Any(at => at.Equals(a, StringComparison.OrdinalIgnoreCase))));
return result.Select(b => new BookEntity
{
Authors = GetAuthors(b),
Title = b.Title,
Year = b.Year,
Description = b.Description,
Format = b.Format,
Genre = new Genre(),
Id = b.Id,
});
}
}
return Enumerable.Empty<BookEntity>();
}
public async Task ReadAllBooks(Func<BookEntity, bool> bookHandler)
{
foreach (var b in (await Tables.Books.GetAll()))
{
var authors = new List<Author>(b.AuthorIds?.Length ?? 0);
var authors = new List<Author>(b.AuthorIds?.Count ?? 0);
Genre genre;
if (b.AuthorIds?.Length > 0)
if (b.AuthorIds?.Count > 0)
{
foreach (var aid in b.AuthorIds)
{
@ -261,7 +267,7 @@ namespace BukiVedi.Shared.Services
{
foreach (var book in (await Tables.Books.GetAll()))
{
var authorLine = (book.AuthorIds?.Length > 0) ? string.Join(' ', book.AuthorIds.Select(a => authors[a])) : string.Empty;
var authorLine = (book.AuthorIds?.Count > 0) ? string.Join(' ', book.AuthorIds.Select(a => authors[a])) : string.Empty;
var genre = string.IsNullOrWhiteSpace(book.GenreId) ? string.Empty : genres[book.GenreId];
var indexItem = new BookDocument
{
@ -340,7 +346,7 @@ namespace BukiVedi.Shared.Services
{
ArchiveIndex = zip_book.ArchiveIndex,
ArchivePath = archive_path,
AuthorIds = author_ids.ToArray(),
AuthorIds = author_ids.ToList(),
Year = zip_book.Year,
Description = zip_book.Description,
Title = zip_book.Title,

@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("BukiVedi.Shared")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0854d0d5e375585dfb4831af0aa4e5089fb0e8ae")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4e697c91a83d95731e2cea4131399178264fd6a2")]
[assembly: System.Reflection.AssemblyProductAttribute("BukiVedi.Shared")]
[assembly: System.Reflection.AssemblyTitleAttribute("BukiVedi.Shared")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

@ -1 +1 @@
c26bb1f765467d5f9ebfe36a8f5628a1bf2b1c03f059be7613e04f8f77a7a9fe
86c42d4be25574172e2d579dfa02e41a379e51b0c675314acf9d44ccc18e6cee

@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("TitleReader")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0854d0d5e375585dfb4831af0aa4e5089fb0e8ae")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4e697c91a83d95731e2cea4131399178264fd6a2")]
[assembly: System.Reflection.AssemblyProductAttribute("TitleReader")]
[assembly: System.Reflection.AssemblyTitleAttribute("TitleReader")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

@ -1 +1 @@
22a66b48f25fdf876eb7692c83b659900d8e9c1b260c873fe5d6180b00a753b8
c5ac3a47b85a3d5934c2eb5b570db310225ebb22491771b55abcb0f32d99cb90

Loading…
Cancel
Save

Powered by TurnKey Linux.