auth fixes, search connected

This commit is contained in:
2025-05-22 04:34:30 +03:00
parent 32bc1276e0
commit ab39b534fe
23 changed files with 610 additions and 359 deletions

View File

@@ -5,7 +5,6 @@ from resolvers.author import ( # search_authors,
get_author_follows,
get_author_follows_authors,
get_author_follows_topics,
get_author_id,
get_authors_all,
load_authors_by,
load_authors_search,
@@ -18,6 +17,7 @@ from resolvers.draft import (
load_drafts,
publish_draft,
update_draft,
unpublish_draft,
)
from resolvers.editor import (
unpublish_shout,
@@ -91,7 +91,6 @@ __all__ = [
# author
"get_author",
"get_author_id",
"get_author_followers",
"get_author_follows",
"get_author_follows_topics",
@@ -161,7 +160,6 @@ __all__ = [
"update_draft",
"delete_draft",
"publish_draft",
"publish_shout",
"unpublish_shout",
"unpublish_draft",
]

View File

@@ -42,11 +42,11 @@ async def get_current_user(_, info):
info: Контекст GraphQL запроса
Returns:
dict: Объект с токеном и данными автора
dict: Объект с токеном и данными автора с добавленной статистикой
"""
# Получаем данные авторизации из контекста запроса
user_id = info.context.get("user_id")
if not user_id:
author_id = info.context.get("author", {}).get("id")
if not author_id:
logger.error("[getSession] Пользователь не авторизован")
from graphql.error import GraphQLError
raise GraphQLError("Требуется авторизация")
@@ -60,19 +60,50 @@ async def get_current_user(_, info):
# Получаем данные автора
author = info.context.get("author")
# Если автор не найден в контексте, пробуем получить из БД
# Если автор не найден в контексте, пробуем получить из БД с добавлением статистики
if not author:
logger.debug(f"[getSession] Автор не найден в контексте для пользователя {user_id}, получаем из БД")
with local_session() as session:
try:
db_author = session.query(Author).filter(Author.id == user_id).one()
db_author.last_seen = int(time.time())
session.commit()
author = db_author
except Exception as e:
logger.error(f"[getSession] Ошибка при получении автора из БД: {e}")
try:
# Используем функцию get_with_stat для получения автора со статистикой
from sqlalchemy import select
from resolvers.stat import get_with_stat
q = select(Author).where(Author.id == user_id)
authors_with_stat = get_with_stat(q)
if authors_with_stat and len(authors_with_stat) > 0:
author = authors_with_stat[0]
# Обновляем last_seen отдельной транзакцией
with local_session() as session:
author_db = session.query(Author).filter(Author.id == user_id).first()
if author_db:
author_db.last_seen = int(time.time())
session.commit()
else:
logger.error(f"[getSession] Автор с ID {user_id} не найден в БД")
from graphql.error import GraphQLError
raise GraphQLError("Ошибка при получении данных пользователя")
raise GraphQLError("Пользователь не найден")
except Exception as e:
logger.error(f"[getSession] Ошибка при получении автора из БД: {e}", exc_info=True)
from graphql.error import GraphQLError
raise GraphQLError("Ошибка при получении данных пользователя")
else:
# Если автор уже есть в контексте, добавляем статистику
try:
from sqlalchemy import select
from resolvers.stat import get_with_stat
q = select(Author).where(Author.id == user_id)
authors_with_stat = get_with_stat(q)
if authors_with_stat and len(authors_with_stat) > 0:
# Обновляем только статистику
author.stat = authors_with_stat[0].stat
except Exception as e:
logger.warning(f"[getSession] Не удалось добавить статистику к автору: {e}")
# Возвращаем данные сессии
logger.info(f"[getSession] Успешно получена сессия для пользователя {user_id}")

View File

@@ -8,7 +8,6 @@ from cache.cache import (
cache_author,
cached_query,
get_cached_author,
get_cached_author_by_user_id,
get_cached_author_followers,
get_cached_follower_authors,
get_cached_follower_topics,
@@ -205,25 +204,24 @@ async def invalidate_authors_cache(author_id=None):
@mutation.field("update_author")
@login_required
async def update_author(_, info, profile):
user_id = info.context.get("user_id")
author_id = info.context.get("author", {}).get("id")
is_admin = info.context.get("is_admin", False)
if not user_id:
if not author_id:
return {"error": "unauthorized", "author": None}
try:
with local_session() as session:
author = session.query(Author).where(Author.id == user_id).first()
author = session.query(Author).where(Author.id == author_id).first()
if author:
Author.update(author, profile)
session.add(author)
session.commit()
author_query = select(Author).where(Author.id == user_id)
author_query = select(Author).where(Author.id == author_id)
result = get_with_stat(author_query)
if result:
author_with_stat = result[0]
if isinstance(author_with_stat, Author):
# Кэшируем полную версию для админов
author_dict = author_with_stat.dict(is_admin=True)
author_dict = author_with_stat.dict(access=is_admin)
asyncio.create_task(cache_author(author_dict))
# Возвращаем обычную полную версию, т.к. это владелец
@@ -244,16 +242,16 @@ async def get_authors_all(_, info):
list: Список всех авторов
"""
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
authors = await get_all_authors(current_user_id, False)
viewer_id = info.context.get("author", {}).get("id")
is_admin = info.context.get("is_admin", False)
authors = await get_all_authors(viewer_id, is_admin)
return authors
@query.field("get_author")
async def get_author(_, info, slug="", author_id=0):
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
is_admin = info.context.get("is_admin", False) if hasattr(info, "context") else False
is_admin = info.context.get("is_admin", False)
author_dict = None
try:
@@ -272,7 +270,7 @@ async def get_author(_, info, slug="", author_id=0):
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Получаем отфильтрованную версию
author_dict = temp_author.dict(current_user_id, is_admin)
author_dict = temp_author.dict(access=is_admin)
# Добавляем статистику, которая могла быть в кэшированной версии
if "stat" in cached_author:
author_dict["stat"] = cached_author["stat"]
@@ -285,11 +283,11 @@ async def get_author(_, info, slug="", author_id=0):
author_with_stat = result[0]
if isinstance(author_with_stat, Author):
# Кэшируем полные данные для админов
original_dict = author_with_stat.dict(is_admin=True)
original_dict = author_with_stat.dict(access=True)
asyncio.create_task(cache_author(original_dict))
# Возвращаем отфильтрованную версию
author_dict = author_with_stat.dict(current_user_id, is_admin)
author_dict = author_with_stat.dict(access=is_admin)
# Добавляем статистику
if hasattr(author_with_stat, "stat"):
author_dict["stat"] = author_with_stat.stat
@@ -302,42 +300,6 @@ async def get_author(_, info, slug="", author_id=0):
return author_dict
@query.field("get_author_id")
async def get_author_id(_, info, user: str):
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
is_admin = info.context.get("is_admin", False) if hasattr(info, "context") else False
user_id = user.strip()
logger.info(f"getting author id for {user_id}")
author = None
try:
cached_author = await get_cached_author_by_user_id(user_id, get_with_stat)
if cached_author:
# Создаем объект автора для использования метода dict
temp_author = Author()
for key, value in cached_author.items():
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Возвращаем отфильтрованную версию
return temp_author.dict(current_user_id, is_admin)
author_query = select(Author).filter(Author.id == user_id)
result = get_with_stat(author_query)
if result:
author_with_stat = result[0]
if isinstance(author_with_stat, Author):
# Кэшируем полную версию данных
original_dict = author_with_stat.dict(is_admin=True)
asyncio.create_task(cache_author(original_dict))
# Возвращаем отфильтрованную версию
return author_with_stat.dict(current_user_id, is_admin)
except Exception as exc:
logger.error(f"Error getting author: {exc}")
return None
@query.field("load_authors_by")
async def load_authors_by(_, info, by, limit, offset):
"""
@@ -352,11 +314,11 @@ async def load_authors_by(_, info, by, limit, offset):
list: Список авторов с учетом критерия
"""
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
is_admin = info.context.get("is_admin", False) if hasattr(info, "context") else False
viewer_id = info.context.get("author", {}).get("id")
is_admin = info.context.get("is_admin", False)
# Используем оптимизированную функцию для получения авторов
return await get_authors_with_stats(limit, offset, by, current_user_id, is_admin)
return await get_authors_with_stats(limit, offset, by, viewer_id, is_admin)
@query.field("load_authors_search")
@@ -423,8 +385,8 @@ def get_author_id_from(slug="", user=None, author_id=None):
@query.field("get_author_follows")
async def get_author_follows(_, info, slug="", user=None, author_id=0):
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
is_admin = info.context.get("is_admin", False) if hasattr(info, "context") else False
viewer_id = info.context.get("author", {}).get("id")
is_admin = info.context.get("is_admin", False)
logger.debug(f"getting follows for @{slug}")
author_id = get_author_id_from(slug=slug, user=user, author_id=author_id)
@@ -447,7 +409,7 @@ async def get_author_follows(_, info, slug="", user=None, author_id=0):
# temp_author - это объект Author, который мы хотим сериализовать
# current_user_id - ID текущего авторизованного пользователя (может быть None)
# is_admin - булево значение, является ли текущий пользователь админом
has_access = is_admin or (current_user_id is not None and str(current_user_id) == str(temp_author.id))
has_access = is_admin or (viewer_id is not None and str(viewer_id) == str(temp_author.id))
followed_authors.append(temp_author.dict(access=has_access))
# TODO: Get followed communities too
@@ -472,13 +434,13 @@ async def get_author_follows_topics(_, _info, slug="", user=None, author_id=None
@query.field("get_author_follows_authors")
async def get_author_follows_authors(_, info, slug="", user=None, author_id=None):
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
is_admin = info.context.get("is_admin", False) if hasattr(info, "context") else False
viewer_id = info.context.get("author", {}).get("id")
is_admin = info.context.get("is_admin", False)
logger.debug(f"getting followed authors for @{slug}")
author_id = get_author_id_from(slug=slug, user=user, author_id=author_id)
if not author_id:
return []
# Получаем данные из кэша
followed_authors_raw = await get_cached_follower_authors(author_id)
@@ -495,7 +457,7 @@ async def get_author_follows_authors(_, info, slug="", user=None, author_id=None
# temp_author - это объект Author, который мы хотим сериализовать
# current_user_id - ID текущего авторизованного пользователя (может быть None)
# is_admin - булево значение, является ли текущий пользователь админом
has_access = is_admin or (current_user_id is not None and str(current_user_id) == str(temp_author.id))
has_access = is_admin or (viewer_id is not None and str(viewer_id) == str(temp_author.id))
followed_authors.append(temp_author.dict(access=has_access))
return followed_authors
@@ -517,8 +479,8 @@ def create_author(user_id: str, slug: str, name: str = ""):
@query.field("get_author_followers")
async def get_author_followers(_, info, slug: str = "", user: str = "", author_id: int = 0):
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
is_admin = info.context.get("is_admin", False) if hasattr(info, "context") else False
viewer_id = info.context.get("author", {}).get("id")
is_admin = info.context.get("is_admin", False)
logger.debug(f"getting followers for author @{slug} or ID:{author_id}")
author_id = get_author_id_from(slug=slug, user=user, author_id=author_id)
@@ -540,7 +502,7 @@ async def get_author_followers(_, info, slug: str = "", user: str = "", author_i
# temp_author - это объект Author, который мы хотим сериализовать
# current_user_id - ID текущего авторизованного пользователя (может быть None)
# is_admin - булево значение, является ли текущий пользователь админом
has_access = is_admin or (current_user_id is not None and str(current_user_id) == str(temp_author.id))
has_access = is_admin or (viewer_id is not None and str(viewer_id) == str(temp_author.id))
followers.append(temp_author.dict(access=has_access))
return followers

View File

@@ -9,7 +9,6 @@ from services.schema import mutation
@mutation.field("accept_invite")
@login_required
async def accept_invite(_, info, invite_id: int):
info.context["user_id"]
author_dict = info.context["author"]
author_id = author_dict.get("id")
if author_id:
@@ -41,7 +40,6 @@ async def accept_invite(_, info, invite_id: int):
@mutation.field("reject_invite")
@login_required
async def reject_invite(_, info, invite_id: int):
info.context["user_id"]
author_dict = info.context["author"]
author_id = author_dict.get("id")
@@ -64,14 +62,17 @@ async def reject_invite(_, info, invite_id: int):
@mutation.field("create_invite")
@login_required
async def create_invite(_, info, slug: str = "", author_id: int = 0):
user_id = info.context["user_id"]
author_dict = info.context["author"]
author_id = author_dict.get("id")
viewer_id = author_dict.get("id")
roles = info.context.get("roles", [])
is_admin = info.context.get("is_admin", False)
if not viewer_id and not is_admin and "admin" not in roles and "editor" not in roles:
return {"error": "Access denied"}
if author_id:
# Check if the inviter is the owner of the shout
with local_session() as session:
shout = session.query(Shout).filter(Shout.slug == slug).first()
inviter = session.query(Author).filter(Author.id == user_id).first()
inviter = session.query(Author).filter(Author.id == viewer_id).first()
if inviter and shout and shout.authors and inviter.id is shout.created_by:
# Check if an invite already exists
existing_invite = (
@@ -89,7 +90,7 @@ async def create_invite(_, info, slug: str = "", author_id: int = 0):
# Create a new invite
new_invite = Invite(
inviter_id=user_id,
inviter_id=viewer_id,
author_id=author_id,
shout_id=shout.id,
status=InviteStatus.PENDING.value,
@@ -107,9 +108,13 @@ async def create_invite(_, info, slug: str = "", author_id: int = 0):
@mutation.field("remove_author")
@login_required
async def remove_author(_, info, slug: str = "", author_id: int = 0):
user_id = info.context["user_id"]
viewer_id = info.context.get("author", {}).get("id")
is_admin = info.context.get("is_admin", False)
roles = info.context.get("roles", [])
if not viewer_id and not is_admin and "admin" not in roles and "editor" not in roles:
return {"error": "Access denied"}
with local_session() as session:
author = session.query(Author).filter(Author.id == user_id).first()
author = session.query(Author).filter(Author.id == author_id).first()
if author:
shout = session.query(Shout).filter(Shout.slug == slug).first()
# NOTE: owner should be first in a list
@@ -123,8 +128,6 @@ async def remove_author(_, info, slug: str = "", author_id: int = 0):
@mutation.field("remove_invite")
@login_required
async def remove_invite(_, info, invite_id: int):
info.context["user_id"]
author_dict = info.context["author"]
author_id = author_dict.get("id")
if isinstance(author_id, int):

View File

@@ -78,12 +78,11 @@ async def load_drafts(_, info):
Returns:
dict: Список черновиков или сообщение об ошибке
"""
user_id = info.context.get("user_id")
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
if not user_id or not author_id:
return {"error": "User ID and author ID are required"}
if not author_id:
return {"error": "Author ID is required"}
try:
with local_session() as session:
@@ -152,11 +151,10 @@ async def create_draft(_, info, draft_input):
... assert result['draft'].title == 'Test'
... return result
"""
user_id = info.context.get("user_id")
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
if not user_id or not author_id:
if not author_id:
return {"error": "Author ID is required"}
# Проверяем обязательные поля
@@ -227,11 +225,10 @@ async def update_draft(_, info, draft_id: int, draft_input):
Returns:
dict: Обновленный черновик или сообщение об ошибке
"""
user_id = info.context.get("user_id")
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
if not user_id or not author_id:
if not author_id:
return {"error": "Author ID are required"}
try:
@@ -389,11 +386,10 @@ async def publish_draft(_, info, draft_id: int):
Returns:
dict: Результат публикации с shout или сообщением об ошибке
"""
user_id = info.context.get("user_id")
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
if not user_id or not author_id:
if not author_id:
return {"error": "Author ID is required"}
try:
@@ -469,7 +465,7 @@ async def publish_draft(_, info, draft_id: int):
await notify_shout(shout.id)
# Обновляем поисковый индекс
search_service.index_shout(shout)
search_service.perform_index(shout)
logger.info(f"Successfully published shout #{shout.id} from draft #{draft_id}")
logger.debug(f"Shout data: {shout.dict()}")
@@ -479,3 +475,74 @@ async def publish_draft(_, info, draft_id: int):
except Exception as e:
logger.error(f"Failed to publish draft {draft_id}: {e}", exc_info=True)
return {"error": f"Failed to publish draft: {str(e)}"}
@mutation.field("unpublish_draft")
@login_required
async def unpublish_draft(_, info, draft_id: int):
"""
Снимает с публикации черновик, обновляя связанный Shout.
Args:
draft_id (int): ID черновика, публикацию которого нужно снять
Returns:
dict: Результат операции с информацией о черновике или сообщением об ошибке
"""
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
if author_id:
return {"error": "Author ID is required"}
try:
with local_session() as session:
# Загружаем черновик со связанной публикацией
draft = (
session.query(Draft)
.options(
joinedload(Draft.publication),
joinedload(Draft.authors),
joinedload(Draft.topics)
)
.filter(Draft.id == draft_id)
.first()
)
if not draft:
return {"error": "Draft not found"}
# Проверяем, есть ли публикация
if not draft.publication:
return {"error": "This draft is not published yet"}
shout = draft.publication
# Снимаем с публикации
shout.published_at = None
shout.updated_at = int(time.time())
shout.updated_by = author_id
session.commit()
# Инвалидируем кэш
cache_keys = [f"shouts:{shout.id}"]
await invalidate_shouts_cache(cache_keys)
await invalidate_shout_related_cache(shout, author_id)
# Формируем результат
draft_dict = draft.dict()
# Добавляем информацию о публикации
draft_dict["publication"] = {
"id": shout.id,
"slug": shout.slug,
"published_at": None
}
logger.info(f"Successfully unpublished shout #{shout.id} for draft #{draft_id}")
return {"draft": draft_dict}
except Exception as e:
logger.error(f"Failed to unpublish draft {draft_id}: {e}", exc_info=True)
return {"error": f"Failed to unpublish draft: {str(e)}"}

View File

@@ -86,12 +86,11 @@ async def get_my_shout(_, info, shout_id: int):
... assert result['shout'].id == 1
... return result
"""
user_id = info.context.get("user_id", "")
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
roles = info.context.get("roles", [])
shout = None
if not user_id or not author_id:
if not author_id:
return {"error": "unauthorized", "shout": None}
with local_session() as session:
shout = (
@@ -136,7 +135,6 @@ async def get_my_shout(_, info, shout_id: int):
@query.field("get_shouts_drafts")
@login_required
async def get_shouts_drafts(_, info):
# user_id = info.context.get("user_id")
author_dict = info.context.get("author")
if not author_dict:
return {"error": "author profile was not found"}
@@ -160,16 +158,15 @@ async def get_shouts_drafts(_, info):
# @login_required
async def create_shout(_, info, inp):
logger.info(f"Starting create_shout with input: {inp}")
user_id = info.context.get("user_id")
author_dict = info.context.get("author")
logger.debug(f"Context user_id: {user_id}, author: {author_dict}")
logger.debug(f"Context author: {author_dict}")
if not author_dict:
logger.error("Author profile not found in context")
return {"error": "author profile was not found"}
author_id = author_dict.get("id")
if user_id and author_id:
if author_id:
try:
with local_session() as session:
author_id = int(author_id)
@@ -268,7 +265,7 @@ async def create_shout(_, info, inp):
logger.error(f"Unexpected error in create_shout: {e}", exc_info=True)
return {"error": f"Unexpected error: {str(e)}"}
error_msg = "cant create shout" if user_id else "unauthorized"
error_msg = "cant create shout" if author_id else "unauthorized"
logger.error(f"Create shout failed: {error_msg}")
return {"error": error_msg}
@@ -394,26 +391,19 @@ def patch_topics(session, shout, topics_input):
# @mutation.field("update_shout")
# @login_required
async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
logger.info(f"Starting update_shout with id={shout_id}, publish={publish}")
logger.debug(f"Full shout_input: {shout_input}") # DraftInput
user_id = info.context.get("user_id")
roles = info.context.get("roles", [])
author_dict = info.context.get("author")
if not author_dict:
logger.error("Author profile not found")
return {"error": "author profile was not found"}
author_id = author_dict.get("id")
shout_input = shout_input or {}
current_time = int(time.time())
shout_id = shout_id or shout_input.get("id", shout_id)
slug = shout_input.get("slug")
if not user_id:
author_id = info.context.get("author").get("id")
if not author_id:
logger.error("Unauthorized update attempt")
return {"error": "unauthorized"}
logger.info(f"Starting update_shout with id={shout_id}, publish={publish}")
logger.debug(f"Full shout_input: {shout_input}") # DraftInput
roles = info.context.get("roles", [])
current_time = int(time.time())
shout_input = shout_input or {}
shout_id = shout_id or shout_input.get("id", shout_id)
slug = shout_input.get("slug")
try:
with local_session() as session:
if author_id:
@@ -620,13 +610,12 @@ async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
# @mutation.field("delete_shout")
# @login_required
async def delete_shout(_, info, shout_id: int):
user_id = info.context.get("user_id")
roles = info.context.get("roles", [])
author_dict = info.context.get("author")
if not author_dict:
return {"error": "author profile was not found"}
author_id = author_dict.get("id")
if user_id and author_id:
roles = info.context.get("roles", [])
if author_id:
author_id = int(author_id)
with local_session() as session:
shout = session.query(Shout).filter(Shout.id == shout_id).first()
@@ -643,7 +632,6 @@ async def delete_shout(_, info, shout_id: int):
for author in shout.authors:
await cache_by_id(Author, author.id, cache_author)
info.context["author"] = author.dict()
info.context["user_id"] = author.id
unfollow(None, info, "shout", shout.slug)
for topic in shout.topics:
@@ -746,7 +734,7 @@ async def unpublish_shout(_, info, shout_id: int):
return {"error": "Shout not found"}
# Если у публикации есть связанный черновик, загружаем его с relationships
if shout.draft:
if shout.draft is not None:
# Отдельно загружаем черновик с его связями
draft = (
session.query(Draft)

View File

@@ -27,12 +27,14 @@ from utils.logger import root_logger as logger
@login_required
async def follow(_, info, what, slug="", entity_id=0):
logger.debug("Начало выполнения функции 'follow'")
user_id = info.context.get("user_id")
viewer_id = info.context.get("author", {}).get("id")
if not viewer_id:
return {"error": "Access denied"}
follower_dict = info.context.get("author")
logger.debug(f"follower: {follower_dict}")
if not user_id or not follower_dict:
return GraphQLError("unauthorized")
if not viewer_id or not follower_dict:
return GraphQLError("Access denied")
follower_id = follower_dict.get("id")
logger.debug(f"follower_id: {follower_id}")
@@ -107,7 +109,6 @@ async def follow(_, info, what, slug="", entity_id=0):
# Если это авторы, получаем безопасную версию
if what == "AUTHOR":
# Получаем ID текущего пользователя и фильтруем данные
current_user_id = user_id
follows_filtered = []
for author_data in existing_follows:
@@ -117,7 +118,7 @@ async def follow(_, info, what, slug="", entity_id=0):
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
follows_filtered.append(temp_author.dict(current_user_id, False))
follows_filtered.append(temp_author.dict(viewer_id, False))
if not existing_sub:
# Создаем объект автора для entity_dict
@@ -126,7 +127,7 @@ async def follow(_, info, what, slug="", entity_id=0):
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
follows = [*follows_filtered, temp_author.dict(current_user_id, False)]
follows = [*follows_filtered, temp_author.dict(viewer_id, False)]
else:
follows = follows_filtered
else:
@@ -149,13 +150,15 @@ async def follow(_, info, what, slug="", entity_id=0):
@login_required
async def unfollow(_, info, what, slug="", entity_id=0):
logger.debug("Начало выполнения функции 'unfollow'")
user_id = info.context.get("user_id")
viewer_id = info.context.get("author", {}).get("id")
if not viewer_id:
return GraphQLError("Access denied")
follower_dict = info.context.get("author")
logger.debug(f"follower: {follower_dict}")
if not user_id or not follower_dict:
if not viewer_id or not follower_dict:
logger.warning("Неавторизованный доступ при попытке отписаться")
return {"error": "unauthorized"}
return GraphQLError("Unauthorized")
follower_id = follower_dict.get("id")
logger.debug(f"follower_id: {follower_id}")
@@ -219,7 +222,6 @@ async def unfollow(_, info, what, slug="", entity_id=0):
# Если это авторы, получаем безопасную версию
if what == "AUTHOR":
# Получаем ID текущего пользователя и фильтруем данные
current_user_id = user_id
follows_filtered = []
for author_data in existing_follows:
@@ -232,7 +234,7 @@ async def unfollow(_, info, what, slug="", entity_id=0):
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
follows_filtered.append(temp_author.dict(current_user_id, False))
follows_filtered.append(temp_author.dict(viewer_id, False))
follows = follows_filtered
else:

View File

@@ -96,7 +96,6 @@ async def get_my_rates_shouts(_, info, shouts):
@mutation.field("rate_author")
@login_required
async def rate_author(_, info, rated_slug, value):
info.context["user_id"]
rater_id = info.context.get("author", {}).get("id")
with local_session() as session:
rater_id = int(rater_id)

View File

@@ -383,11 +383,11 @@ async def update_reaction(_, info, reaction):
:param reaction: Dictionary with reaction data.
:return: Dictionary with updated reaction data or error.
"""
user_id = info.context.get("user_id")
author_id = info.context.get("author", {}).get("id")
roles = info.context.get("roles")
rid = reaction.get("id")
if not rid or not user_id or not roles:
if not rid or not author_id or not roles:
return {"error": "Invalid input data"}
del reaction["id"]
@@ -437,16 +437,15 @@ async def delete_reaction(_, info, reaction_id: int):
:param reaction_id: Reaction ID to delete.
:return: Dictionary with deleted reaction data or error.
"""
user_id = info.context.get("user_id")
author_id = info.context.get("author", {}).get("id")
roles = info.context.get("roles", [])
if not user_id:
if not author_id:
return {"error": "Unauthorized"}
with local_session() as session:
try:
author = session.query(Author).filter(Author.id == user_id).one()
author = session.query(Author).filter(Author.id == author_id).one()
r = session.query(Reaction).filter(Reaction.id == reaction_id).one()
if r.created_by != author_id and "editor" not in roles:
@@ -463,7 +462,7 @@ async def delete_reaction(_, info, reaction_id: int):
session.commit()
# TODO: add more reaction types here
else:
logger.debug(f"{user_id} user removing his #{reaction_id} reaction")
logger.debug(f"{author_id} user removing his #{reaction_id} reaction")
session.delete(r)
session.commit()
if check_to_unfeature(session, r):

View File

@@ -217,6 +217,7 @@ def get_shouts_with_links(info, q, limit=20, offset=0):
shout_id = int(f"{shout.id}")
shout_dict = shout.dict()
# Обработка поля created_by
if has_field(info, "created_by") and shout_dict.get("created_by"):
main_author_id = shout_dict.get("created_by")
a = session.query(Author).filter(Author.id == main_author_id).first()
@@ -226,6 +227,44 @@ def get_shouts_with_links(info, q, limit=20, offset=0):
"slug": a.slug,
"pic": a.pic,
}
# Обработка поля updated_by
if has_field(info, "updated_by"):
if shout_dict.get("updated_by"):
updated_by_id = shout_dict.get("updated_by")
updated_author = session.query(Author).filter(Author.id == updated_by_id).first()
if updated_author:
shout_dict["updated_by"] = {
"id": updated_author.id,
"name": updated_author.name,
"slug": updated_author.slug,
"pic": updated_author.pic,
}
else:
# Если автор не найден, устанавливаем поле в null
shout_dict["updated_by"] = None
else:
# Если updated_by не указан, устанавливаем поле в null
shout_dict["updated_by"] = None
# Обработка поля deleted_by
if has_field(info, "deleted_by"):
if shout_dict.get("deleted_by"):
deleted_by_id = shout_dict.get("deleted_by")
deleted_author = session.query(Author).filter(Author.id == deleted_by_id).first()
if deleted_author:
shout_dict["deleted_by"] = {
"id": deleted_author.id,
"name": deleted_author.name,
"slug": deleted_author.slug,
"pic": deleted_author.pic,
}
else:
# Если автор не найден, устанавливаем поле в null
shout_dict["deleted_by"] = None
else:
# Если deleted_by не указан, устанавливаем поле в null
shout_dict["deleted_by"] = None
if has_field(info, "stat"):
stat = {}

View File

@@ -315,12 +315,12 @@ async def update_topic(_, _info, topic_input):
@mutation.field("delete_topic")
@login_required
async def delete_topic(_, info, slug: str):
user_id = info.context["user_id"]
viewer_id = info.context.get("author", {}).get("id")
with local_session() as session:
t: Topic = session.query(Topic).filter(Topic.slug == slug).first()
if not t:
return {"error": "invalid topic slug"}
author = session.query(Author).filter(Author.id == user_id).first()
author = session.query(Author).filter(Author.id == viewer_id).first()
if author:
if t.created_by != author.id:
return {"error": "access denied"}