mirror of
https://github.com/wowlikon/LiB.git
synced 2026-02-03 20:31:09 +00:00
Compare commits
4 Commits
dfa4d14afc
...
4368ee0d3c
| Author | SHA1 | Date | |
|---|---|---|---|
| 4368ee0d3c | |||
| 4f9c472a54 | |||
| a6811a3e86 | |||
| 19d322c9d9 |
@@ -1,44 +0,0 @@
|
||||
# Postgres
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DB=lib
|
||||
|
||||
# Ollama
|
||||
OLLAMA_URL="http://localhost:11434"
|
||||
OLLAMA_MAX_LOADED_MODELS=1
|
||||
OLLAMA_NUM_THREADS=4
|
||||
OLLAMA_KEEP_ALIVE=5m
|
||||
|
||||
# Default admin account
|
||||
# DEFAULT_ADMIN_USERNAME="admin"
|
||||
# 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
|
||||
|
||||
# Hash
|
||||
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
|
||||
|
||||
# TOTP_2FA
|
||||
TOTP_ISSUER=LiB
|
||||
TOTP_VALID_WINDOW=1
|
||||
@@ -7,7 +7,7 @@ from .role import RoleBase, RoleCreate, RoleList, RoleRead, RoleUpdate
|
||||
from .user import UserBase, UserCreate, UserList, UserRead, UserUpdate, UserLogin
|
||||
from .loan import LoanBase, LoanCreate, LoanList, LoanRead, LoanUpdate
|
||||
from .recovery import RecoveryCodesResponse, RecoveryCodesStatus, RecoveryCodeUse
|
||||
from .token import Token, TokenData, PartialToken
|
||||
from .token import TokenData
|
||||
from .misc import (
|
||||
AuthorWithBooks,
|
||||
GenreWithBooks,
|
||||
@@ -62,9 +62,7 @@ __all__ = [
|
||||
"RoleUpdate",
|
||||
"RoleRead",
|
||||
"RoleList",
|
||||
"Token",
|
||||
"TokenData",
|
||||
"PartialToken",
|
||||
"TOTPSetupResponse",
|
||||
"TOTPVerifyRequest",
|
||||
"TOTPDisableRequest",
|
||||
|
||||
@@ -11,8 +11,8 @@ class AuthorBase(SQLModel):
|
||||
|
||||
name: str = Field(description="Псевдоним")
|
||||
|
||||
model_config = ConfigDict( # pyright: ignore
|
||||
json_schema_extra={"example": {"name": "author_name"}}
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={"example": {"name": "John Doe"}}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
@@ -12,6 +13,16 @@ class RoleBase(SQLModel):
|
||||
description: str | None = Field(None, description="Описание")
|
||||
payroll: int = Field(0, description="Оплата")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"name": "admin",
|
||||
"description": "system administrator",
|
||||
"payroll": 500,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class RoleCreate(RoleBase):
|
||||
"""Модель роли для создания"""
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
"""Модуль DTO-моделей токенов"""
|
||||
"""Модуль DTO-модели токена"""
|
||||
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class Token(SQLModel):
|
||||
"""Модель токена"""
|
||||
|
||||
access_token: str = Field(description="Токен доступа")
|
||||
token_type: str = Field("bearer", description="Тип токена")
|
||||
refresh_token: str | None = Field(None, description="Токен обновления")
|
||||
|
||||
|
||||
class PartialToken(SQLModel):
|
||||
"""Частичный токен — для подтверждения 2FA"""
|
||||
|
||||
partial_token: str = Field(description="Частичный токен")
|
||||
token_type: str = Field("partial", description="Тип токена")
|
||||
requires_2fa: bool = Field(True, description="Требуется TOTP-код")
|
||||
|
||||
|
||||
class TokenData(SQLModel):
|
||||
"""Модель содержимого токена"""
|
||||
|
||||
|
||||
@@ -12,15 +12,12 @@ from sqlmodel import Session, select
|
||||
from library_service.services import require_captcha
|
||||
from library_service.models.db import Role, User
|
||||
from library_service.models.dto import (
|
||||
Token,
|
||||
UserCreate,
|
||||
UserRead,
|
||||
UserUpdate,
|
||||
UserList,
|
||||
RoleRead,
|
||||
RoleList,
|
||||
Token,
|
||||
PartialToken,
|
||||
LoginResponse,
|
||||
RecoveryCodeUse,
|
||||
RegisterResponse,
|
||||
@@ -147,11 +144,14 @@ def login(
|
||||
)
|
||||
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
new_access_token = create_access_token(
|
||||
data=token_data, expires_delta=access_token_expires
|
||||
)
|
||||
new_refresh_token = create_refresh_token(data=token_data)
|
||||
|
||||
return LoginResponse(
|
||||
access_token=create_access_token(
|
||||
data=token_data, expires_delta=access_token_expires
|
||||
),
|
||||
refresh_token=create_refresh_token(data=token_data),
|
||||
access_token=new_access_token,
|
||||
refresh_token=new_refresh_token,
|
||||
token_type="bearer",
|
||||
requires_2fa=False,
|
||||
)
|
||||
@@ -159,7 +159,7 @@ def login(
|
||||
|
||||
@router.post(
|
||||
"/refresh",
|
||||
response_model=Token,
|
||||
response_model=LoginResponse,
|
||||
summary="Обновление токена",
|
||||
description="Получение новой пары токенов, используя действующий Refresh токен",
|
||||
)
|
||||
@@ -190,19 +190,18 @@ def refresh_token(
|
||||
detail="User is inactive",
|
||||
)
|
||||
|
||||
token_data = {"sub": user.username, "user_id": user.id}
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
new_access_token = create_access_token(
|
||||
data={"sub": user.username, "user_id": user.id},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
new_refresh_token = create_refresh_token(
|
||||
data={"sub": user.username, "user_id": user.id}
|
||||
data=token_data, expires_delta=access_token_expires
|
||||
)
|
||||
new_refresh_token = create_refresh_token(data=token_data)
|
||||
|
||||
return Token(
|
||||
return LoginResponse(
|
||||
access_token=new_access_token,
|
||||
refresh_token=new_refresh_token,
|
||||
token_type="bearer",
|
||||
requires_2fa=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -343,7 +342,7 @@ def disable_2fa(
|
||||
|
||||
@router.post(
|
||||
"/2fa/verify",
|
||||
response_model=Token,
|
||||
response_model=LoginResponse,
|
||||
summary="Верификация 2FA",
|
||||
description="Завершает аутентификацию с помощью TOTP кода или резервного кода",
|
||||
)
|
||||
@@ -374,12 +373,16 @@ def verify_2fa(
|
||||
|
||||
token_data = {"sub": user.username, "user_id": user.id}
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
new_access_token = create_access_token(
|
||||
data=token_data, expires_delta=access_token_expires
|
||||
)
|
||||
new_refresh_token = create_refresh_token(data=token_data)
|
||||
|
||||
return Token(
|
||||
access_token=create_access_token(
|
||||
data=token_data, expires_delta=access_token_expires
|
||||
),
|
||||
refresh_token=create_refresh_token(data=token_data),
|
||||
return LoginResponse(
|
||||
access_token=new_access_token,
|
||||
refresh_token=new_refresh_token,
|
||||
token_type="bearer",
|
||||
requires_2fa=False,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ $(document).ready(() => {
|
||||
let currentSort = "name_asc";
|
||||
|
||||
loadAuthors();
|
||||
const USER_CAN_MANAGE =
|
||||
typeof window.canManage === "function" && window.canManage();
|
||||
if (USER_CAN_MANAGE) {
|
||||
$("#add-author-btn").removeClass("hidden");
|
||||
}
|
||||
|
||||
function loadAuthors() {
|
||||
showLoadingState();
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
<h2 class="text-2xl font-bold text-gray-800">Авторы</h2>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 w-full md:w-auto">
|
||||
<a href="/author/create" id="add-author-btn" class="hidden flex justify-center items-center px-4 py-2 bg-green-600 text-white font-medium rounded-lg hover:bg-green-700 transition whitespace-nowrap">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||
</svg>
|
||||
Добавить автора
|
||||
</a>
|
||||
<div class="relative">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "LiB"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
description = "Это простое API для управления авторами, книгами и их жанрами."
|
||||
authors = [{ name = "wowlikon" }]
|
||||
readme = "README.md"
|
||||
|
||||
Reference in New Issue
Block a user