diff --git a/docker-compose.yml b/docker-compose.yml index aa300bd..eee1ca1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: - ./data/db:/var/lib/postgresql/data networks: - proxy - ports: # !сменить внешний порт перед использованием! + ports: # !только локальный тест! - 5432:5432 env_file: - ./.env @@ -31,23 +31,6 @@ services: timeout: 5s retries: 5 - replication-setup: - image: postgres:17-alpine - container_name: replication-setup - restart: "no" - networks: - - proxy - env_file: - - ./.env - volumes: - - ./setup-replication.sh:/setup-replication.sh - entrypoint: ["/bin/sh", "/setup-replication.sh"] - depends_on: - api: - condition: service_started - db: - condition: service_healthy - llm: image: ollama/ollama:latest container_name: llm diff --git a/example-docker.env b/example-docker.env index 5ad23bc..7ff5b73 100644 --- a/example-docker.env +++ b/example-docker.env @@ -4,9 +4,6 @@ POSTGRES_PORT=5432 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=lib -REMOTE_HOST= -REMOTE_PORT= -NODE_ID= # Ollama OLLAMA_URL="http://llm:11434" diff --git a/init-replication.sql b/init-replication.sql deleted file mode 100644 index b2b3ee8..0000000 --- a/init-replication.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE PUBLICATION all_tables_pub FOR ALL TABLES; - -ALTER SYSTEM SET password_encryption = 'scram-sha-256'; -SELECT pg_reload_conf(); diff --git a/library_service/main.py b/library_service/main.py index 9ba4581..0c4f8d4 100644 --- a/library_service/main.py +++ b/library_service/main.py @@ -73,15 +73,20 @@ app = get_app(lifespan) @app.exception_handler(status.HTTP_404_NOT_FOUND) async def custom_not_found_handler(request: Request, exc: HTTPException): - path = request.url.path + if exc.detail == "Not Found": + path = request.url.path - if path.startswith("/api"): - return JSONResponse( - status_code=status.HTTP_404_NOT_FOUND, - content={"detail": "API endpoint not found", "path": path}, - ) + if path.startswith("/api/"): + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={"detail": "API endpoint not found", "path": path}, + ) + return await unknown(request, app) - return await unknown(request, app) + return JSONResponse( + status_code=exc.status_code, + content={"detail": exc.detail}, + ) @app.middleware("http") diff --git a/library_service/routers/auth.py b/library_service/routers/auth.py index 5b68ae7..6becbea 100644 --- a/library_service/routers/auth.py +++ b/library_service/routers/auth.py @@ -287,7 +287,8 @@ def enable_2fa( if not current_user.totp_secret: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Secret key not generated" + status_code=status.HTTP_400_BAD_REQUEST, + detail="Secret key not generated", ) if not verify_totp_code(secret, data.code): @@ -299,7 +300,8 @@ def enable_2fa( decrypted = cipher.decrypt(base64.b64decode(current_user.totp_secret.encode())) if secret != decrypted.decode(): raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Incorret secret" + status_code=status.HTTP_400_BAD_REQUEST, + detail="Incorret secret", ) current_user.is_2fa_enabled = True diff --git a/library_service/routers/authors.py b/library_service/routers/authors.py index f3b1670..bb4dbd7 100644 --- a/library_service/routers/authors.py +++ b/library_service/routers/authors.py @@ -67,7 +67,8 @@ def get_author( author = session.get(Author, author_id) if not author: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Author not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Author not found", ) books = session.exec( @@ -98,7 +99,8 @@ def update_author( db_author = session.get(Author, author_id) if not db_author: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Author not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Author not found", ) update_data = author.model_dump(exclude_unset=True) @@ -125,7 +127,8 @@ def delete_author( author = session.get(Author, author_id) if not author: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Author not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Author not found", ) author_read = AuthorRead(**author.model_dump()) diff --git a/library_service/routers/books.py b/library_service/routers/books.py index 2bfbe4a..528676a 100644 --- a/library_service/routers/books.py +++ b/library_service/routers/books.py @@ -1,23 +1,20 @@ """Модуль работы с книгами""" -from library_service.services import transcode_image -import shutil -from uuid import uuid4 - -from pydantic import Field from typing_extensions import Annotated - -from sqlalchemy.orm import selectinload, defer - -from sqlalchemy import text, case, distinct +from uuid import uuid4 +import shutil from datetime import datetime, timezone from typing import List from fastapi import APIRouter, Depends, HTTPException, Path, Query, status, UploadFile, File from ollama import Client +from pydantic import Field +from sqlalchemy import text, case, distinct +from sqlalchemy.orm import selectinload, defer from sqlmodel import Session, select, col, func from library_service.auth import RequireStaff +from library_service.services import transcode_image from library_service.settings import get_session, OLLAMA_URL, BOOKS_PREVIEW_DIR from library_service.models.enums import BookStatus from library_service.models.db import ( @@ -139,7 +136,8 @@ def create_book( session.commit() session.refresh(db_book) - book_data = db_book.model_dump(exclude={"embedding", "preview_id"}) + book_dict = {k: v for k, v in db_book.__dict__.items() if not k.startswith('_')} + book_data = {k: v for k, v in book_dict.items() if k not in {"embedding", "preview_id"}} book_data["preview_urls"] = { "png": f"/static/books/{db_book.preview_id}.png", "jpeg": f"/static/books/{db_book.preview_id}.jpg", @@ -160,12 +158,17 @@ def read_books(session: Session = Depends(get_session)): books_data = [] for book in books: - book_data = book.model_dump(exclude={"embedding", "preview_id"}) + book = book[0] + book_dict = dict(book) + + book_data = {k: v for k, v in book_dict.items() if k not in {"embedding", "preview_id"}} + book_data["preview_urls"] = { "png": f"/static/books/{book.preview_id}.png", "jpeg": f"/static/books/{book.preview_id}.jpg", "webp": f"/static/books/{book.preview_id}.webp", } if book.preview_id else {} + books_data.append(book_data) return BookList( @@ -188,7 +191,8 @@ def get_book( book = session.get(Book, book_id) if not book: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Book not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", ) authors = session.scalars( @@ -203,7 +207,8 @@ def get_book( genre_reads = [GenreRead(**genre.model_dump()) for genre in genres] - book_data = book.model_dump(exclude={"embedding", "preview_id"}) + book_dict = {k: v for k, v in book.__dict__.items() if not k.startswith('_')} + book_data = {k: v for k, v in book_dict.items() if k not in {"embedding", "preview_id"}} book_data["preview_urls"] = { "png": f"/static/books/{book.preview_id}.png", "jpeg": f"/static/books/{book.preview_id}.jpg", @@ -231,7 +236,8 @@ def update_book( db_book = session.get(Book, book_id) if not db_book: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Book not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", ) if book_update.status is not None: @@ -292,7 +298,8 @@ def delete_book( book = session.get(Book, book_id) if not book: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Book not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", ) book_read = BookRead( id=(book.id or 0), @@ -313,10 +320,16 @@ async def upload_book_preview( session: Session = Depends(get_session) ): if not (file.content_type or "").startswith("image/"): - raise HTTPException(status.HTTP_415_UNSUPPORTED_MEDIA_TYPE, "Image required") + raise HTTPException( + status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE, + detail="Image required", + ) if (file.size or 0) > 32 * 1024 * 1024: - raise HTTPException(status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, "File larger than 10 MB") + raise HTTPException( + status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, + detail="File larger than 10 MB", + ) file_uuid= uuid4() tmp_path = BOOKS_PREVIEW_DIR / f"{file_uuid}.upload" @@ -327,7 +340,10 @@ async def upload_book_preview( book = session.get(Book, book_id) if not book: tmp_path.unlink() - raise HTTPException(status.HTTP_404_NOT_FOUND, "Book not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", + ) transcode_image(tmp_path) tmp_path.unlink() diff --git a/library_service/routers/cap.py b/library_service/routers/cap.py index bb83f51..bdfb145 100644 --- a/library_service/routers/cap.py +++ b/library_service/routers/cap.py @@ -27,11 +27,13 @@ async def challenge(request: Request, ip: str = Depends(get_ip)): """Возвращает задачу capjs""" if challenges_by_ip[ip] >= MAX_CHALLENGES_PER_IP: raise HTTPException( - status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail="Too many challenges" + status_code=status.HTTP_429_TOO_MANY_REQUESTS, + detail="Too many challenges", ) if len(active_challenges) >= MAX_TOTAL_CHALLENGES: raise HTTPException( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Server busy" + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Server busy", ) token = secrets.token_hex(25) @@ -60,17 +62,22 @@ async def redeem(request: Request, payload: dict, ip: str = Depends(get_ip)): if token not in active_challenges: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Invalid challenge" + status_code=status.HTTP_404_NOT_FOUND, + detail="Invalid challenge", ) ch = active_challenges.pop(token) challenges_by_ip[ch["ip"]] -= 1 if now_ms() > ch["expires"]: - raise HTTPException(status_code=status.HTTP_410_GONE, detail="Expired") + raise HTTPException( + status_code=status.HTTP_410_GONE, + detail="Expired" + ) if len(solutions) < ch["c"]: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Bad solutions" + status_code=status.HTTP_400_BAD_REQUEST, + detail="Bad solutions", ) def verify(i: int) -> bool: @@ -84,7 +91,8 @@ async def redeem(request: Request, payload: dict, ip: str = Depends(get_ip)): ) if not all(results): raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid solution" + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid solution", ) r_token = ch["redeem_token"] diff --git a/library_service/routers/genres.py b/library_service/routers/genres.py index be0a134..2e06999 100644 --- a/library_service/routers/genres.py +++ b/library_service/routers/genres.py @@ -66,7 +66,8 @@ def get_genre( genre = session.get(Genre, genre_id) if not genre: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Genre not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Genre not found", ) books = session.exec( @@ -97,7 +98,8 @@ def update_genre( db_genre = session.get(Genre, genre_id) if not db_genre: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Genre not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Genre not found", ) update_data = genre.model_dump(exclude_unset=True) @@ -124,7 +126,8 @@ def delete_genre( genre = session.get(Genre, genre_id) if not genre: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Genre not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Genre not found", ) genre_read = GenreRead(**genre.model_dump()) diff --git a/library_service/routers/loans.py b/library_service/routers/loans.py index 48e3df5..1ac1455 100644 --- a/library_service/routers/loans.py +++ b/library_service/routers/loans.py @@ -41,7 +41,8 @@ def create_loan( book = session.get(Book, loan.book_id) if not book: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Book not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", ) if book.status != BookStatus.ACTIVE: @@ -53,7 +54,8 @@ def create_loan( target_user = session.get(User, loan.user_id) if not target_user: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="User not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found", ) db_loan = BookUserLink( @@ -253,14 +255,16 @@ def get_loan( if not loan: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Loan not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Loan not found", ) is_staff = is_user_staff(current_user) if not is_staff and loan.user_id != current_user.id: raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Access denied to this loan" + status_code=status.HTTP_403_FORBIDDEN, + detail="Access denied to this loan", ) return LoanRead(**loan.model_dump()) @@ -282,7 +286,8 @@ def update_loan( db_loan = session.get(BookUserLink, loan_id) if not db_loan: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Loan not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Loan not found", ) is_staff = is_user_staff(current_user) @@ -296,7 +301,8 @@ def update_loan( book = session.get(Book, db_loan.book_id) if not book: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Book not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", ) if loan_update.user_id is not None: @@ -308,7 +314,8 @@ def update_loan( new_user = session.get(User, loan_update.user_id) if not new_user: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="User not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found", ) db_loan.user_id = loan_update.user_id @@ -347,18 +354,21 @@ def confirm_loan( loan = session.get(BookUserLink, loan_id) if not loan: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Loan not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Loan not found", ) if loan.returned_at: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Loan is already returned" + status_code=status.HTTP_400_BAD_REQUEST, + detail="Loan is already returned", ) book = session.get(Book, loan.book_id) if not book: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Book not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", ) if book.status not in [BookStatus.RESERVED, BookStatus.ACTIVE]: @@ -392,12 +402,14 @@ def return_loan( loan = session.get(BookUserLink, loan_id) if not loan: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Loan not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Loan not found", ) if loan.returned_at: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Loan is already returned" + status_code=status.HTTP_400_BAD_REQUEST, + detail="Loan is already returned", ) loan.returned_at = datetime.now(timezone.utc) @@ -429,7 +441,8 @@ def delete_loan( loan = session.get(BookUserLink, loan_id) if not loan: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Loan not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Loan not found", ) is_staff = is_user_staff(current_user) @@ -499,7 +512,8 @@ def issue_book_directly( book = session.get(Book, loan.book_id) if not book: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Book not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Book not found", ) if book.status != BookStatus.ACTIVE: @@ -511,7 +525,8 @@ def issue_book_directly( target_user = session.get(User, loan.user_id) if not target_user: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="User not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found", ) db_loan = BookUserLink( diff --git a/library_service/routers/misc.py b/library_service/routers/misc.py index 7fde233..0eac8a5 100644 --- a/library_service/routers/misc.py +++ b/library_service/routers/misc.py @@ -56,8 +56,14 @@ async def create_genre(request: Request, app=Depends(lambda: get_app())): @router.get("/genre/{genre_id}/edit", include_in_schema=False) -async def edit_genre(request: Request, genre_id: int, app=Depends(lambda: get_app())): +async def edit_genre(request: Request, genre_id: str, app=Depends(lambda: get_app())): """Рендерит страницу редактирования жанра""" + + try: + assert int(genre_id) > 0 + except: + return await unknown(request, app) + return templates.TemplateResponse(request, "edit_genre.html", get_info(app) | {"request": request, "title": "LiB - Редактировать жанр", "id": genre_id}) @@ -74,15 +80,32 @@ async def create_author(request: Request, app=Depends(lambda: get_app())): @router.get("/author/{author_id}/edit", include_in_schema=False) -async def edit_author(request: Request, author_id: int, app=Depends(lambda: get_app())): +async def edit_author(request: Request, author_id: str, app=Depends(lambda: get_app()), session=Depends(get_session)): """Рендерит страницу редактирования автора""" - return templates.TemplateResponse(request, "edit_author.html", get_info(app) | {"request": request, "title": "LiB - Редактировать автора", "id": author_id}) + + try: + author = session.get(Author, int(author_id)) + assert author is not None + except: + return await unknown(request, app) + + return templates.TemplateResponse(request, "edit_author.html", get_info(app) | {"request": request, "title": f"LiB - Редактировать автора \"{author.name}\"", "id": author_id}) @router.get("/author/{author_id}", include_in_schema=False) -async def author(request: Request, author_id: int, app=Depends(lambda: get_app())): +async def author(request: Request, author_id: str, app=Depends(lambda: get_app()), session=Depends(get_session)): """Рендерит страницу просмотра автора""" - return templates.TemplateResponse(request, "author.html", get_info(app) | {"request": request, "title": "LiB - Автор", "id": author_id}) + + if author_id == "": + return RedirectResponse("/authors") + + try: + author = session.get(Author, int(author_id)) + assert author is not None + except: + return await unknown(request, app) + + return templates.TemplateResponse(request, "author.html", get_info(app) | {"request": request, "title": f"LiB - Автор \"{author.name}\"", "id": author_id}) @router.get("/books", include_in_schema=False) @@ -98,16 +121,32 @@ async def create_book(request: Request, app=Depends(lambda: get_app())): @router.get("/book/{book_id}/edit", include_in_schema=False) -async def edit_book(request: Request, book_id: int, app=Depends(lambda: get_app())): +async def edit_book(request: Request, book_id: str, app=Depends(lambda: get_app()), session=Depends(get_session)): """Рендерит страницу редактирования книги""" - return templates.TemplateResponse(request, "edit_book.html", get_info(app) | {"request": request, "title": "LiB - Редактировать книгу", "id": book_id}) + + try: + book = session.get(Book, int(book_id)) + assert book is not None + except: + return await unknown(request, app) + + return templates.TemplateResponse(request, "edit_book.html", get_info(app) | {"request": request, "title": f"LiB - Редактировать книгу \"{book.title}\"", "id": book_id}) @router.get("/book/{book_id}", include_in_schema=False) -async def book(request: Request, book_id: int, app=Depends(lambda: get_app()), session=Depends(get_session)): +async def book(request: Request, book_id: str, app=Depends(lambda: get_app()), session=Depends(get_session)): """Рендерит страницу просмотра книги""" - book = session.get(Book, book_id) - return templates.TemplateResponse(request, "book.html", get_info(app) | {"request": request, "title": "LiB - Книга", "id": book_id, "img": book.preview_id}) + + if book_id == "": + return RedirectResponse("/books") + + try: + book = session.get(Book, int(book_id)) + assert book is not None + except: + return await unknown(request, app) + + return templates.TemplateResponse(request, "book.html", get_info(app) | {"request": request, "title": f"LiB - Книга \"{book.title}\"", "id": book_id, "img": book.preview_id}) @router.get("/auth", include_in_schema=False) diff --git a/library_service/routers/relationships.py b/library_service/routers/relationships.py index 4fda0fa..8892eb4 100644 --- a/library_service/routers/relationships.py +++ b/library_service/routers/relationships.py @@ -19,7 +19,8 @@ def check_entity_exists(session, model, entity_id, entity_name): entity = session.get(model, entity_id) if not entity: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=f"{entity_name} not found" + status_code=status.HTTP_404_NOT_FOUND, + detail=f"{entity_name} not found", ) return entity @@ -33,7 +34,10 @@ def add_relationship(session, link_model, id1, field1, id2, field2, detail): ).first() if existing_link: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=detail) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=detail, + ) link = link_model(**{field1: id1, field2: id2}) session.add(link) @@ -52,7 +56,8 @@ def remove_relationship(session, link_model, id1, field1, id2, field2): if not link: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Relationship not found" + status_code=status.HTTP_404_NOT_FOUND, + detail="Relationship not found", ) session.delete(link) diff --git a/library_service/services/captcha.py b/library_service/services/captcha.py index 3797364..67249db 100644 --- a/library_service/services/captcha.py +++ b/library_service/services/captcha.py @@ -72,6 +72,7 @@ async def require_captcha(request: Request): token = request.cookies.get("capjs_token") if not token or token not in redeem_tokens or redeem_tokens[token] < now_ms(): raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail={"error": "captcha_required"} + status_code=status.HTTP_403_FORBIDDEN, + detail={"error": "captcha_required"}, ) del redeem_tokens[token] diff --git a/library_service/static/page/author.js b/library_service/static/page/author.js index 157bcee..8be15f7 100644 --- a/library_service/static/page/author.js +++ b/library_service/static/page/author.js @@ -9,7 +9,6 @@ $(document).ready(() => { Api.get(`/api/authors/${authorId}`) .then((author) => { - document.title = `LiB - ${author.name}`; renderAuthor(author); renderBooks(author.books); if (window.canManage()) { diff --git a/library_service/static/page/book.js b/library_service/static/page/book.js index 8536564..363df31 100644 --- a/library_service/static/page/book.js +++ b/library_service/static/page/book.js @@ -366,7 +366,6 @@ $(document).ready(() => { Api.get(`/api/books/${bookId}`) .then((book) => { currentBook = book; - document.title = `LiB - ${book.title}`; renderBook(book); if (window.canManage()) { $("#edit-book-btn") diff --git a/library_service/static/page/edit_author.js b/library_service/static/page/edit_author.js index f984b9d..3c32ca8 100644 --- a/library_service/static/page/edit_author.js +++ b/library_service/static/page/edit_author.js @@ -32,7 +32,6 @@ $(document).ready(() => { originalAuthor = author; authorBooks = booksData.books || booksData || []; - document.title = `Редактирование: ${author.name} | LiB`; populateForm(author); renderAuthorBooks(authorBooks); diff --git a/library_service/static/page/edit_book.js b/library_service/static/page/edit_book.js index 29063d5..5b2e54f 100644 --- a/library_service/static/page/edit_book.js +++ b/library_service/static/page/edit_book.js @@ -49,7 +49,6 @@ $(document).ready(() => { currentGenres.set(g.id, g.name), ); - document.title = `Редактирование: ${book.title} | LiB`; populateForm(book); initAuthorsDropdown(); initGenresDropdown(); diff --git a/library_service/static/page/edit_genre.js b/library_service/static/page/edit_genre.js index 3d7e33f..6974771 100644 --- a/library_service/static/page/edit_genre.js +++ b/library_service/static/page/edit_genre.js @@ -35,7 +35,6 @@ $(document).ready(() => { originalGenre = genre; genreBooks = booksData.books || booksData || []; - document.title = `Редактирование: ${genre.name} | LiB`; populateForm(genre); renderGenreBooks(genreBooks); diff --git a/library_service/static/page/profile.js b/library_service/static/page/profile.js index 333dbcc..9288d0c 100644 --- a/library_service/static/page/profile.js +++ b/library_service/static/page/profile.js @@ -17,7 +17,7 @@ $(document).ready(() => { Api.get("/api/auth/recovery-codes/status").catch(() => null), ]) .then(async ([user, rolesData, recoveryStatus]) => { - document.title = `LiB - ${user.full_name || user.username}`; + document.title = `LiB - Профиль ${user.full_name || user.username}`; currentUsername = user.username; await renderProfileHeader(user); diff --git a/library_service/templates/api.html b/library_service/templates/api.html index bb62380..41252f5 100644 --- a/library_service/templates/api.html +++ b/library_service/templates/api.html @@ -2,10 +2,15 @@ - Loading... + + + + + + Loading...