This commit is contained in:
parent
34511a8edf
commit
8116160b4d
|
@ -1,4 +1,5 @@
|
|||
#### [0.4.6]
|
||||
- login_accepted decorator added
|
||||
- `docs` added
|
||||
- optimized and unified `load_shouts_*` resolvers with `LoadShoutsOptions`
|
||||
- `load_shouts_bookmarked` resolver fixed
|
||||
|
|
|
@ -54,12 +54,8 @@ class JWTAuthenticate(AuthenticationBackend):
|
|||
def login_required(func):
|
||||
@wraps(func)
|
||||
async def wrap(parent, info: GraphQLResolveInfo, *args, **kwargs):
|
||||
# debug only
|
||||
# print('[auth.authenticate] login required for %r with info %r' % (func, info))
|
||||
auth: AuthCredentials = info.context["request"].auth
|
||||
# print(auth)
|
||||
if not auth or not auth.logged_in:
|
||||
# raise Unauthorized(auth.error_message or "Please login")
|
||||
return {"error": "Please login first"}
|
||||
return await func(parent, info, *args, **kwargs)
|
||||
|
||||
|
@ -79,3 +75,22 @@ def permission_required(resource, operation, func):
|
|||
return await func(parent, info, *args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
def login_accepted(func):
|
||||
@wraps(func)
|
||||
async def wrap(parent, info: GraphQLResolveInfo, *args, **kwargs):
|
||||
auth: AuthCredentials = info.context["request"].auth
|
||||
|
||||
# Если есть авторизация, добавляем данные автора в контекст
|
||||
if auth and auth.logged_in:
|
||||
# Существующие данные auth остаются
|
||||
pass
|
||||
else:
|
||||
# Очищаем данные автора из контекста если авторизация отсутствует
|
||||
info.context["author"] = None
|
||||
info.context["user_id"] = None
|
||||
|
||||
return await func(parent, info, *args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
|
|
@ -95,7 +95,6 @@ class User(Base):
|
|||
ratings = relationship(UserRating, foreign_keys=UserRating.user)
|
||||
roles = relationship(lambda: Role, secondary=UserRole.__tablename__)
|
||||
|
||||
|
||||
def get_permission(self):
|
||||
scope = {}
|
||||
for role in self.roles:
|
||||
|
|
|
@ -314,7 +314,7 @@ async def create_reaction(_, info, reaction):
|
|||
shout = session.query(Shout).filter(Shout.id == shout_id).first()
|
||||
if not shout:
|
||||
return {"error": "Shout not found"}
|
||||
rdict['shout'] = shout.dict()
|
||||
rdict["shout"] = shout.dict()
|
||||
rdict["created_by"] = author_dict
|
||||
return {"reaction": rdict}
|
||||
except Exception as e:
|
||||
|
|
|
@ -9,6 +9,7 @@ from orm.author import Author
|
|||
from orm.reaction import Reaction, ReactionKind
|
||||
from orm.shout import Shout, ShoutAuthor, ShoutTopic
|
||||
from orm.topic import Topic
|
||||
from services.auth import login_accepted
|
||||
from services.db import json_array_builder, json_builder, local_session
|
||||
from services.schema import query
|
||||
from services.search import search_text
|
||||
|
@ -161,8 +162,28 @@ def query_with_stat(info):
|
|||
.group_by(Reaction.shout)
|
||||
.subquery()
|
||||
)
|
||||
author_id = info.context.get("author", {}).get("id")
|
||||
user_reaction_subquery = None
|
||||
if author_id:
|
||||
user_reaction_subquery = (
|
||||
select(Reaction.shout, Reaction.kind.label("my_rate"))
|
||||
.where(
|
||||
and_(
|
||||
Reaction.created_by == author_id,
|
||||
Reaction.deleted_at.is_(None),
|
||||
Reaction.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]),
|
||||
Reaction.reply_to.is_(None),
|
||||
)
|
||||
)
|
||||
.distinct(Reaction.shout)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
q = q.outerjoin(stats_subquery, stats_subquery.c.shout == Shout.id)
|
||||
if user_reaction_subquery:
|
||||
q = q.outerjoin(user_reaction_subquery, user_reaction_subquery.c.shout == Shout.id)
|
||||
|
||||
# Добавляем поле my_rate в JSON
|
||||
q = q.add_columns(
|
||||
json_builder(
|
||||
"comments_count",
|
||||
|
@ -171,6 +192,8 @@ def query_with_stat(info):
|
|||
stats_subquery.c.rating,
|
||||
"last_reacted_at",
|
||||
stats_subquery.c.last_reacted_at,
|
||||
"my_rate",
|
||||
user_reaction_subquery.c.my_rate if user_reaction_subquery else None,
|
||||
).label("stat")
|
||||
)
|
||||
|
||||
|
@ -337,6 +360,7 @@ def apply_sorting(q, options):
|
|||
|
||||
|
||||
@query.field("load_shouts_by")
|
||||
@login_accepted
|
||||
async def load_shouts_by(_, info, options):
|
||||
"""
|
||||
Загрузка публикаций с фильтрацией, сортировкой и пагинацией.
|
||||
|
@ -346,7 +370,7 @@ async def load_shouts_by(_, info, options):
|
|||
:param options: Опции фильтрации и сортировки.
|
||||
:return: Список публикаций, удовлетворяющих критериям.
|
||||
"""
|
||||
# Базовый запрос: если запрашиваются статистические данные, используем специальный запрос с статистикой
|
||||
# Базовый запрос: используем специальный запрос с статистикой
|
||||
q = query_with_stat(info)
|
||||
q, limit, offset = apply_options(q, options)
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@ type Stat {
|
|||
viewed: Int
|
||||
# followed: Int
|
||||
last_reacted_at: Int
|
||||
my_rate: ReactionKind
|
||||
}
|
||||
|
||||
type CommunityStat {
|
||||
|
|
|
@ -94,3 +94,39 @@ def login_required(f):
|
|||
return await f(*args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
def login_accepted(f):
|
||||
"""
|
||||
Декоратор, который добавляет данные авторизации в контекст, если они доступны,
|
||||
но не блокирует доступ для неавторизованных пользователей.
|
||||
"""
|
||||
|
||||
@wraps(f)
|
||||
async def decorated_function(*args, **kwargs):
|
||||
info = args[1]
|
||||
req = info.context.get("request")
|
||||
|
||||
# Пробуем получить данные авторизации
|
||||
user_id, user_roles = await check_auth(req)
|
||||
|
||||
if user_id and user_roles:
|
||||
# Если пользователь авторизован, добавляем его данные в контекст
|
||||
logger.info(f" got {user_id} roles: {user_roles}")
|
||||
info.context["user_id"] = user_id.strip()
|
||||
info.context["roles"] = user_roles
|
||||
|
||||
# Пробуем получить профиль автора
|
||||
author = await get_cached_author_by_user_id(user_id, get_with_stat)
|
||||
if not author:
|
||||
logger.warning(f"author profile not found for user {user_id}")
|
||||
info.context["author"] = author
|
||||
else:
|
||||
# Для неавторизованных пользователей очищаем контекст
|
||||
info.context["user_id"] = None
|
||||
info.context["roles"] = None
|
||||
info.context["author"] = None
|
||||
|
||||
return await f(*args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
|
Loading…
Reference in New Issue
Block a user