diff --git a/.env b/.env index 98674b8..849493e 100644 --- a/.env +++ b/.env @@ -1,9 +1,9 @@ # Postgres -POSTGRES_HOST="localhost" -POSTGRES_PORT="5432" -POSTGRES_USER="postgres" -POSTGRES_PASSWORD="postgres" -POSTGRES_DB="lib" +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=lib # Ollama OLLAMA_URL="http://localhost:11434" @@ -16,28 +16,29 @@ OLLAMA_KEEP_ALIVE=5m # DEFAULT_ADMIN_EMAIL="admin@example.com" # DEFAULT_ADMIN_PASSWORD="password-is-generated-randomly-on-first-launch" SECRET_KEY="your-secret-key-change-in-production" +DOMAIN=localhost # JWT -ALGORITHM="HS256" -REFRESH_TOKEN_EXPIRE_DAYS="7" -ACCESS_TOKEN_EXPIRE_MINUTES="15" -PARTIAL_TOKEN_EXPIRE_MINUTES="5" +ALGORITHM=HS256 +REFRESH_TOKEN_EXPIRE_DAYS=7 +ACCESS_TOKEN_EXPIRE_MINUTES=15 +PARTIAL_TOKEN_EXPIRE_MINUTES=5 # Hash -ARGON2_TYPE="id" -ARGON2_TIME_COST="3" -ARGON2_MEMORY_COST="65536" -ARGON2_PARALLELISM="4" -ARGON2_SALT_LENGTH="16" -ARGON2_HASH_LENGTH="48" +ARGON2_TYPE=id +ARGON2_TIME_COST=3 +ARGON2_MEMORY_COST=65536 +ARGON2_PARALLELISM=4 +ARGON2_SALT_LENGTH=16 +ARGON2_HASH_LENGTH=48 # Recovery codes -RECOVERY_CODES_COUNT="10" -RECOVERY_CODE_SEGMENTS="4" -RECOVERY_CODE_SEGMENT_BYTES="2" -RECOVERY_MIN_REMAINING_WARNING="3" -RECOVERY_MAX_AGE_DAYS="365" +RECOVERY_CODES_COUNT=10 +RECOVERY_CODE_SEGMENTS=4 +RECOVERY_CODE_SEGMENT_BYTES=2 +RECOVERY_MIN_REMAINING_WARNING=3 +RECOVERY_MAX_AGE_DAYS=365 # TOTP_2FA -TOTP_ISSUER="LiB" -TOTP_VALID_WINDOW="1" +TOTP_ISSUER=LiB +TOTP_VALID_WINDOW=1 diff --git a/example-docker.env b/example-docker.env index 4517192..5ad23bc 100644 --- a/example-docker.env +++ b/example-docker.env @@ -1,9 +1,9 @@ # Postgres -POSTGRES_HOST="db" -POSTGRES_PORT="5432" -POSTGRES_USER="postgres" -POSTGRES_PASSWORD="postgres" -POSTGRES_DB="lib" +POSTGRES_HOST=db +POSTGRES_PORT=5432 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=lib REMOTE_HOST= REMOTE_PORT= NODE_ID= @@ -19,28 +19,29 @@ DEFAULT_ADMIN_USERNAME="admin" DEFAULT_ADMIN_EMAIL="admin@example.com" DEFAULT_ADMIN_PASSWORD="Password12345" SECRET_KEY="your-secret-key-change-in-production" +DOMAIN="mydomain.com" # JWT -ALGORITHM="HS256" -REFRESH_TOKEN_EXPIRE_DAYS="7" -ACCESS_TOKEN_EXPIRE_MINUTES="15" -PARTIAL_TOKEN_EXPIRE_MINUTES="5" +ALGORITHM=HS256 +REFRESH_TOKEN_EXPIRE_DAYS=7 +ACCESS_TOKEN_EXPIRE_MINUTES=15 +PARTIAL_TOKEN_EXPIRE_MINUTES=5 # Hash -ARGON2_TYPE="id" -ARGON2_TIME_COST="3" -ARGON2_MEMORY_COST="65536" -ARGON2_PARALLELISM="4" -ARGON2_SALT_LENGTH="16" -ARGON2_HASH_LENGTH="48" +ARGON2_TYPE=id +ARGON2_TIME_COST=3 +ARGON2_MEMORY_COST=65536 +ARGON2_PARALLELISM=4 +ARGON2_SALT_LENGTH=16 +ARGON2_HASH_LENGTH=48 # Recovery codes -RECOVERY_CODES_COUNT="10" -RECOVERY_CODE_SEGMENTS="4" -RECOVERY_CODE_SEGMENT_BYTES="2" -RECOVERY_MIN_REMAINING_WARNING="3" -RECOVERY_MAX_AGE_DAYS="365" +RECOVERY_CODES_COUNT=10 +RECOVERY_CODE_SEGMENTS=4 +RECOVERY_CODE_SEGMENT_BYTES=2 +RECOVERY_MIN_REMAINING_WARNING=3 +RECOVERY_MAX_AGE_DAYS=365 # TOTP_2FA -TOTP_ISSUER="LiB" -TOTP_VALID_WINDOW="1" +TOTP_ISSUER=LiB +TOTP_VALID_WINDOW=1 diff --git a/example-local.env b/example-local.env index 6d034ee..b611ff8 100644 --- a/example-local.env +++ b/example-local.env @@ -1,9 +1,9 @@ # Postgres -POSTGRES_HOST="localhost" -POSTGRES_PORT="5432" -POSTGRES_USER="postgres" -POSTGRES_PASSWORD="postgres" -POSTGRES_DB="lib" +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=lib # Ollama OLLAMA_URL="http://localhost:11434" @@ -16,28 +16,29 @@ DEFAULT_ADMIN_USERNAME="admin" DEFAULT_ADMIN_EMAIL="admin@example.com" DEFAULT_ADMIN_PASSWORD="Password12345" SECRET_KEY="your-secret-key-change-in-production" +DOMAIN="mydomain.com" # JWT -ALGORITHM="HS256" -REFRESH_TOKEN_EXPIRE_DAYS="7" -ACCESS_TOKEN_EXPIRE_MINUTES="15" -PARTIAL_TOKEN_EXPIRE_MINUTES="5" +ALGORITHM=HS256 +REFRESH_TOKEN_EXPIRE_DAYS=7 +ACCESS_TOKEN_EXPIRE_MINUTES=15 +PARTIAL_TOKEN_EXPIRE_MINUTES=5 # Hash -ARGON2_TYPE="id" -ARGON2_TIME_COST="3" -ARGON2_MEMORY_COST="65536" -ARGON2_PARALLELISM="4" -ARGON2_SALT_LENGTH="16" -ARGON2_HASH_LENGTH="48" +ARGON2_TYPE=id +ARGON2_TIME_COST=3 +ARGON2_MEMORY_COST=65536 +ARGON2_PARALLELISM=4 +ARGON2_SALT_LENGTH=16 +ARGON2_HASH_LENGTH=48 # Recovery codes -RECOVERY_CODES_COUNT="10" -RECOVERY_CODE_SEGMENTS="4" -RECOVERY_CODE_SEGMENT_BYTES="2" -RECOVERY_MIN_REMAINING_WARNING="3" -RECOVERY_MAX_AGE_DAYS="365" +RECOVERY_CODES_COUNT=10 +RECOVERY_CODE_SEGMENTS=4 +RECOVERY_CODE_SEGMENT_BYTES=2 +RECOVERY_MIN_REMAINING_WARNING=3 +RECOVERY_MAX_AGE_DAYS=365 # TOTP_2FA -TOTP_ISSUER="LiB" -TOTP_VALID_WINDOW="1" +TOTP_ISSUER=LiB +TOTP_VALID_WINDOW=1 diff --git a/library_service/main.py b/library_service/main.py index 62e160e..da3ccef 100644 --- a/library_service/main.py +++ b/library_service/main.py @@ -81,7 +81,7 @@ async def custom_not_found_handler(request: Request, exc: HTTPException): content={"detail": "API endpoint not found", "path": path}, ) - return await unknown(request) + return await unknown(request, app) @app.middleware("http") diff --git a/library_service/routers/misc.py b/library_service/routers/misc.py index 9903825..169b432 100644 --- a/library_service/routers/misc.py +++ b/library_service/routers/misc.py @@ -1,4 +1,6 @@ """Модуль прочих эндпоинтов и веб-страниц""" +import os +import sys from datetime import datetime from pathlib import Path @@ -31,115 +33,116 @@ def get_info(app) -> Dict: "description": app.description.rsplit("|", 1)[0], }, "server_time": datetime.now().isoformat(), + "domain": os.getenv("DOMAIN", ""), } @router.get("/", include_in_schema=False) -async def root(request: Request): +async def root(request: Request, app=Depends(lambda: get_app())): """Рендерит главную страницу""" - return templates.TemplateResponse(request, "index.html") + return templates.TemplateResponse(request, "index.html", get_info(app) | {"title": "LiB - Библиотека"}) @router.get("/unknown", include_in_schema=False) -async def unknown(request: Request): +async def unknown(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу 404 ошибки""" - return templates.TemplateResponse(request, "unknown.html") + return templates.TemplateResponse(request, "unknown.html", get_info(app) | {"title": "LiB - Страница не найдена"}) @router.get("/genre/create", include_in_schema=False) -async def create_genre(request: Request): +async def create_genre(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу создания жанра""" - return templates.TemplateResponse(request, "create_genre.html") + return templates.TemplateResponse(request, "create_genre.html", get_info(app) | {"title": "LiB - Создать жанр"}) @router.get("/genre/{genre_id}/edit", include_in_schema=False) -async def edit_genre(request: Request, genre_id: int): +async def edit_genre(request: Request, genre_id: int, app=Depends(lambda: get_app())): """Рендерит страницу редактирования жанра""" - return templates.TemplateResponse(request, "edit_genre.html") + return templates.TemplateResponse(request, "edit_genre.html", get_info(app) | {"id": genre_id} | {"id": genre_id, "title": "LiB - Редактировать жанр"}) @router.get("/authors", include_in_schema=False) -async def authors(request: Request): +async def authors(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу списка авторов""" - return templates.TemplateResponse(request, "authors.html") + return templates.TemplateResponse(request, "authors.html", get_info(app) | {"title": "LiB - Авторы"}) @router.get("/author/create", include_in_schema=False) -async def create_author(request: Request): +async def create_author(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу создания автора""" - return templates.TemplateResponse(request, "create_author.html") + return templates.TemplateResponse(request, "create_author.html", get_info(app) | {"title": "LiB - Создать автора"}) @router.get("/author/{author_id}/edit", include_in_schema=False) -async def edit_author(request: Request, author_id: int): +async def edit_author(request: Request, author_id: int, app=Depends(lambda: get_app())): """Рендерит страницу редактирования автора""" - return templates.TemplateResponse(request, "edit_author.html") + return templates.TemplateResponse(request, "edit_author.html", get_info(app) | {"id": author_id, "title": "LiB - Редактировать автора"}) @router.get("/author/{author_id}", include_in_schema=False) -async def author(request: Request, author_id: int): +async def author(request: Request, author_id: int, app=Depends(lambda: get_app())): """Рендерит страницу просмотра автора""" - return templates.TemplateResponse(request, "author.html") + return templates.TemplateResponse(request, "author.html", get_info(app) | {"id": author_id, "title": "LiB - Автор"}) @router.get("/books", include_in_schema=False) -async def books(request: Request): +async def books(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу списка книг""" - return templates.TemplateResponse(request, "books.html") + return templates.TemplateResponse(request, "books.html", get_info(app) | {"title": "LiB - Книги"}) @router.get("/book/create", include_in_schema=False) -async def create_book(request: Request): +async def create_book(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу создания книги""" - return templates.TemplateResponse(request, "create_book.html") + return templates.TemplateResponse(request, "create_book.html", get_info(app) | {"title": "LiB - Создать книгу"}) @router.get("/book/{book_id}/edit", include_in_schema=False) -async def edit_book(request: Request, book_id: int): +async def edit_book(request: Request, book_id: int, app=Depends(lambda: get_app())): """Рендерит страницу редактирования книги""" - return templates.TemplateResponse(request, "edit_book.html") + return templates.TemplateResponse(request, "edit_book.html", get_info(app) | {"id": book_id, "title": "LiB - Редактировать книгу"}) @router.get("/book/{book_id}", include_in_schema=False) -async def book(request: Request, book_id: int): +async def book(request: Request, book_id: int, app=Depends(lambda: get_app())): """Рендерит страницу просмотра книги""" - return templates.TemplateResponse(request, "book.html") + return templates.TemplateResponse(request, "book.html", get_info(app) | {"id": book_id, "title": "LiB - Книга"}) @router.get("/auth", include_in_schema=False) -async def auth(request: Request): +async def auth(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу авторизации""" - return templates.TemplateResponse(request, "auth.html") + return templates.TemplateResponse(request, "auth.html", get_info(app) | {"title": "LiB - Авторизация"}) @router.get("/2fa", include_in_schema=False) -async def set2fa(request: Request): +async def set2fa(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу установки двухфакторной аутентификации""" - return templates.TemplateResponse(request, "2fa.html") + return templates.TemplateResponse(request, "2fa.html", get_info(app) | {"title": "LiB - Двухфакторная аутентификация"}) @router.get("/profile", include_in_schema=False) -async def profile(request: Request): +async def profile(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу профиля пользователя""" - return templates.TemplateResponse(request, "profile.html") + return templates.TemplateResponse(request, "profile.html", get_info(app) | {"title": "LiB - Профиль"}) @router.get("/users", include_in_schema=False) -async def users(request: Request): +async def users(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу управления пользователями""" - return templates.TemplateResponse(request, "users.html") + return templates.TemplateResponse(request, "users.html", get_info(app) | {"title": "LiB - Пользователи"}) @router.get("/my-books", include_in_schema=False) -async def my_books(request: Request): +async def my_books(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу моих книг пользователя""" - return templates.TemplateResponse(request, "my_books.html") + return templates.TemplateResponse(request, "my_books.html", get_info(app) | {"title": "LiB - Мои книги"}) @router.get("/analytics", include_in_schema=False) -async def analytics(request: Request): +async def analytics(request: Request, app=Depends(lambda: get_app())): """Рендерит страницу аналитики выдач""" - return templates.TemplateResponse(request, "analytics.html") + return templates.TemplateResponse(request, "analytics.html", get_info(app) | {"title": "LiB - Аналитика"}) @router.get("/favicon.ico", include_in_schema=False) diff --git a/library_service/templates/2fa.html b/library_service/templates/2fa.html index 740634d..a7d0884 100644 --- a/library_service/templates/2fa.html +++ b/library_service/templates/2fa.html @@ -1,5 +1,4 @@ -{% extends "base.html" %} {% block title %}LiB - Настройка 2FA{% endblock %} -{% block content %} +{% extends "base.html" %}{% block content %}