This commit is contained in:
parent
f160ab4d26
commit
90260534eb
|
@ -5,12 +5,12 @@
|
|||
import time
|
||||
from typing import Any, Dict
|
||||
|
||||
from sqlalchemy.orm import exc
|
||||
from starlette.authentication import UnauthenticatedUser
|
||||
from starlette.datastructures import Headers
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse, Response
|
||||
from starlette.types import ASGIApp, Receive, Scope, Send
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from auth.credentials import AuthCredentials
|
||||
from auth.orm import Author
|
||||
|
@ -18,6 +18,8 @@ from auth.sessions import SessionManager
|
|||
from services.db import local_session
|
||||
from settings import (
|
||||
ADMIN_EMAILS as ADMIN_EMAILS_LIST,
|
||||
)
|
||||
from settings import (
|
||||
SESSION_COOKIE_HTTPONLY,
|
||||
SESSION_COOKIE_MAX_AGE,
|
||||
SESSION_COOKIE_NAME,
|
||||
|
@ -33,7 +35,9 @@ ADMIN_EMAILS = ADMIN_EMAILS_LIST.split(",")
|
|||
class AuthenticatedUser:
|
||||
"""Аутентифицированный пользователь"""
|
||||
|
||||
def __init__(self, user_id: str, username: str = "", roles: list = None, permissions: dict = None, token: str = None):
|
||||
def __init__(
|
||||
self, user_id: str, username: str = "", roles: list = None, permissions: dict = None, token: str = None
|
||||
):
|
||||
self.user_id = user_id
|
||||
self.username = username
|
||||
self.roles = roles or []
|
||||
|
@ -177,11 +181,11 @@ class AuthMiddleware:
|
|||
|
||||
# Аутентифицируем пользователя
|
||||
auth, user = await self.authenticate_user(token)
|
||||
|
||||
|
||||
# Добавляем в scope данные авторизации и пользователя
|
||||
scope["auth"] = auth
|
||||
scope["user"] = user
|
||||
|
||||
|
||||
if token:
|
||||
# Обновляем заголовки в scope для совместимости
|
||||
new_headers = []
|
||||
|
@ -190,7 +194,7 @@ class AuthMiddleware:
|
|||
new_headers.append((name, value))
|
||||
new_headers.append((SESSION_TOKEN_HEADER.encode("latin1"), token.encode("latin1")))
|
||||
scope["headers"] = new_headers
|
||||
|
||||
|
||||
logger.debug(f"[middleware] Пользователь аутентифицирован: {user.is_authenticated}")
|
||||
else:
|
||||
logger.debug(f"[middleware] Токен не найден, пользователь неаутентифицирован")
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import time
|
||||
import orjson
|
||||
from secrets import token_urlsafe
|
||||
|
||||
import orjson
|
||||
from authlib.integrations.starlette_client import OAuth
|
||||
from authlib.oauth2.rfc7636 import create_s256_code_challenge
|
||||
from starlette.responses import JSONResponse, RedirectResponse
|
||||
|
||||
from auth.orm import Author
|
||||
from auth.tokenstorage import TokenStorage
|
||||
from resolvers.auth import generate_unique_slug
|
||||
from services.db import local_session
|
||||
from services.redis import redis
|
||||
from settings import FRONTEND_URL, OAUTH_CLIENTS
|
||||
from utils.logger import root_logger as logger
|
||||
from resolvers.auth import generate_unique_slug
|
||||
|
||||
oauth = OAuth()
|
||||
|
||||
|
@ -100,7 +100,7 @@ async def oauth_login(request):
|
|||
# Получаем параметры из query string
|
||||
state = request.query_params.get("state")
|
||||
redirect_uri = request.query_params.get("redirect_uri", FRONTEND_URL)
|
||||
|
||||
|
||||
if not state:
|
||||
return JSONResponse({"error": "State parameter is required"}, status_code=400)
|
||||
|
||||
|
@ -113,7 +113,7 @@ async def oauth_login(request):
|
|||
"code_verifier": code_verifier,
|
||||
"provider": provider,
|
||||
"redirect_uri": redirect_uri,
|
||||
"created_at": int(time.time())
|
||||
"created_at": int(time.time()),
|
||||
}
|
||||
await store_oauth_state(state, oauth_data)
|
||||
|
||||
|
@ -172,7 +172,7 @@ async def oauth_callback(request):
|
|||
if not author:
|
||||
# Генерируем slug из имени или email
|
||||
slug = generate_unique_slug(profile["name"] or profile["email"].split("@")[0])
|
||||
|
||||
|
||||
author = Author(
|
||||
email=profile["email"],
|
||||
name=profile["name"],
|
||||
|
@ -223,6 +223,7 @@ async def store_oauth_state(state: str, data: dict) -> None:
|
|||
key = f"oauth_state:{state}"
|
||||
await redis.execute("SETEX", key, OAUTH_STATE_TTL, orjson.dumps(data))
|
||||
|
||||
|
||||
async def get_oauth_state(state: str) -> dict:
|
||||
"""Получает и удаляет OAuth состояние из Redis (one-time use)"""
|
||||
key = f"oauth_state:{state}"
|
||||
|
|
28
cache/cache.py
vendored
28
cache/cache.py
vendored
|
@ -109,37 +109,41 @@ async def update_follower_stat(follower_id, entity_type, count):
|
|||
# Get author from cache
|
||||
async def get_cached_author(author_id: int, get_with_stat):
|
||||
logger.debug(f"[get_cached_author] Начало выполнения для author_id: {author_id}")
|
||||
|
||||
|
||||
author_key = f"author:id:{author_id}"
|
||||
logger.debug(f"[get_cached_author] Проверка кэша по ключу: {author_key}")
|
||||
|
||||
|
||||
result = await redis.execute("GET", author_key)
|
||||
if result:
|
||||
logger.debug(f"[get_cached_author] Найдены данные в кэше, размер: {len(result)} байт")
|
||||
cached_data = orjson.loads(result)
|
||||
logger.debug(f"[get_cached_author] Кэшированные данные имеют ключи: {list(cached_data.keys()) if cached_data else 'None'}")
|
||||
logger.debug(
|
||||
f"[get_cached_author] Кэшированные данные имеют ключи: {list(cached_data.keys()) if cached_data else 'None'}"
|
||||
)
|
||||
return cached_data
|
||||
|
||||
|
||||
logger.debug(f"[get_cached_author] Данные не найдены в кэше, загрузка из БД")
|
||||
|
||||
|
||||
# Load from database if not found in cache
|
||||
q = select(Author).where(Author.id == author_id)
|
||||
authors = get_with_stat(q)
|
||||
logger.debug(f"[get_cached_author] Результат запроса из БД: {len(authors) if authors else 0} записей")
|
||||
|
||||
|
||||
if authors:
|
||||
author = authors[0]
|
||||
logger.debug(f"[get_cached_author] Получен автор из БД: {type(author)}, id: {getattr(author, 'id', 'N/A')}")
|
||||
|
||||
|
||||
# Используем безопасный вызов dict() для Author
|
||||
author_dict = author.dict() if hasattr(author, 'dict') else author.__dict__
|
||||
logger.debug(f"[get_cached_author] Сериализованные данные автора: {list(author_dict.keys()) if author_dict else 'None'}")
|
||||
|
||||
author_dict = author.dict() if hasattr(author, "dict") else author.__dict__
|
||||
logger.debug(
|
||||
f"[get_cached_author] Сериализованные данные автора: {list(author_dict.keys()) if author_dict else 'None'}"
|
||||
)
|
||||
|
||||
await cache_author(author_dict)
|
||||
logger.debug(f"[get_cached_author] Автор кэширован")
|
||||
|
||||
|
||||
return author_dict
|
||||
|
||||
|
||||
logger.warning(f"[get_cached_author] Автор с ID {author_id} не найден в БД")
|
||||
return None
|
||||
|
||||
|
|
2
main.py
2
main.py
|
@ -15,6 +15,7 @@ from starlette.staticfiles import StaticFiles
|
|||
|
||||
from auth.handler import EnhancedGraphQLHTTPHandler
|
||||
from auth.middleware import AuthMiddleware, auth_middleware
|
||||
from auth.oauth import oauth_callback, oauth_login
|
||||
from cache.precache import precache_data
|
||||
from cache.revalidator import revalidation_manager
|
||||
from services.exception import ExceptionHandlerMiddleware
|
||||
|
@ -24,7 +25,6 @@ from services.search import check_search_service, initialize_search_index_backgr
|
|||
from services.viewed import ViewedStorage
|
||||
from settings import DEV_SERVER_PID_FILE_NAME
|
||||
from utils.logger import root_logger as logger
|
||||
from auth.oauth import oauth_login, oauth_callback
|
||||
|
||||
DEVMODE = os.getenv("DOKKU_APP_TYPE", "false").lower() == "false"
|
||||
DIST_DIR = join(os.path.dirname(__file__), "dist") # Директория для собранных файлов
|
||||
|
|
|
@ -290,26 +290,26 @@ def get_with_stat(q):
|
|||
stat["followers"] = cols[2] # Статистика по подписчикам
|
||||
if is_author:
|
||||
# Дополнительная проверка типа entity.id
|
||||
if not hasattr(entity, 'id'):
|
||||
if not hasattr(entity, "id"):
|
||||
logger.error(f"Entity does not have id attribute: {entity}")
|
||||
continue
|
||||
entity_id = entity.id
|
||||
if not isinstance(entity_id, int):
|
||||
logger.error(f"Entity id is not integer: {entity_id} (type: {type(entity_id)})")
|
||||
continue
|
||||
|
||||
|
||||
stat["authors"] = get_author_authors_stat(entity_id) # Статистика по подпискам на авторов
|
||||
stat["comments"] = get_author_comments_stat(entity_id) # Статистика по комментариям
|
||||
else:
|
||||
# Дополнительная проверка типа entity.id для тем
|
||||
if not hasattr(entity, 'id'):
|
||||
if not hasattr(entity, "id"):
|
||||
logger.error(f"Entity does not have id attribute: {entity}")
|
||||
continue
|
||||
entity_id = entity.id
|
||||
if not isinstance(entity_id, int):
|
||||
logger.error(f"Entity id is not integer: {entity_id} (type: {type(entity_id)})")
|
||||
continue
|
||||
|
||||
|
||||
stat["authors"] = get_topic_authors_stat(entity_id) # Статистика по авторам темы
|
||||
entity.stat = stat
|
||||
records.append(entity)
|
||||
|
|
Loading…
Reference in New Issue
Block a user