auth fixes, search connected
This commit is contained in:
@@ -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",
|
||||
]
|
||||
|
@@ -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}")
|
||||
|
@@ -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
|
||||
|
@@ -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):
|
||||
|
@@ -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)}"}
|
||||
|
@@ -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)
|
||||
|
@@ -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:
|
||||
|
@@ -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)
|
||||
|
@@ -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):
|
||||
|
@@ -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 = {}
|
||||
|
@@ -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"}
|
||||
|
Reference in New Issue
Block a user