This commit is contained in:
2025-05-21 01:34:02 +03:00
parent 1d64811880
commit d3a760b6ba
25 changed files with 1839 additions and 550 deletions

View File

@@ -1,11 +1,12 @@
from math import ceil
from sqlalchemy import or_
from sqlalchemy import or_, cast, String
from graphql.error import GraphQLError
from auth.decorators import admin_auth_required
from services.db import local_session
from services.schema import query
from auth.orm import Author, Role
from services.schema import query, mutation
from auth.orm import Author, Role, AuthorRole
from services.env import EnvManager, EnvVariable
from utils.logger import root_logger as logger
@@ -40,7 +41,7 @@ async def admin_get_users(_, info, limit=10, offset=0, search=None):
or_(
Author.email.ilike(search_term),
Author.name.ilike(search_term),
Author.id.cast(str).ilike(search_term),
cast(Author.id, String).ilike(search_term),
)
)
@@ -67,9 +68,7 @@ async def admin_get_users(_, info, limit=10, offset=0, search=None):
if hasattr(user, "roles") and user.roles
else [],
"created_at": user.created_at,
"last_seen": user.last_seen,
"muted": user.muted or False,
"is_active": not user.blocked if hasattr(user, "blocked") else True,
"last_seen": user.last_seen
}
for user in users
],
@@ -120,3 +119,179 @@ async def admin_get_roles(_, info):
except Exception as e:
logger.error(f"Ошибка при получении списка ролей: {str(e)}")
raise GraphQLError(f"Не удалось получить список ролей: {str(e)}")
@query.field("getEnvVariables")
@admin_auth_required
async def get_env_variables(_, info):
"""
Получает список переменных окружения, сгруппированных по секциям
Args:
info: Контекст GraphQL запроса
Returns:
Список секций с переменными окружения
"""
try:
# Создаем экземпляр менеджера переменных окружения
env_manager = EnvManager()
# Получаем все переменные
sections = env_manager.get_all_variables()
# Преобразуем к формату GraphQL API
result = [
{
"name": section.name,
"description": section.description,
"variables": [
{
"key": var.key,
"value": var.value,
"description": var.description,
"type": var.type,
"isSecret": var.is_secret,
}
for var in section.variables
]
}
for section in sections
]
return result
except Exception as e:
logger.error(f"Ошибка при получении переменных окружения: {str(e)}")
raise GraphQLError(f"Не удалось получить переменные окружения: {str(e)}")
@mutation.field("updateEnvVariable")
@admin_auth_required
async def update_env_variable(_, info, key, value):
"""
Обновляет значение переменной окружения
Args:
info: Контекст GraphQL запроса
key: Ключ переменной
value: Новое значение
Returns:
Boolean: результат операции
"""
try:
# Создаем экземпляр менеджера переменных окружения
env_manager = EnvManager()
# Обновляем переменную
result = env_manager.update_variable(key, value)
if result:
logger.info(f"Переменная окружения '{key}' успешно обновлена")
else:
logger.error(f"Не удалось обновить переменную окружения '{key}'")
return result
except Exception as e:
logger.error(f"Ошибка при обновлении переменной окружения: {str(e)}")
return False
@mutation.field("updateEnvVariables")
@admin_auth_required
async def update_env_variables(_, info, variables):
"""
Массовое обновление переменных окружения
Args:
info: Контекст GraphQL запроса
variables: Список переменных для обновления
Returns:
Boolean: результат операции
"""
try:
# Создаем экземпляр менеджера переменных окружения
env_manager = EnvManager()
# Преобразуем входные данные в формат для менеджера
env_variables = [
EnvVariable(
key=var.get("key", ""),
value=var.get("value", ""),
type=var.get("type", "string")
)
for var in variables
]
# Обновляем переменные
result = env_manager.update_variables(env_variables)
if result:
logger.info(f"Переменные окружения успешно обновлены ({len(variables)} шт.)")
else:
logger.error(f"Не удалось обновить переменные окружения")
return result
except Exception as e:
logger.error(f"Ошибка при массовом обновлении переменных окружения: {str(e)}")
return False
@mutation.field("adminUpdateUser")
@admin_auth_required
async def admin_update_user(_, info, user):
"""
Обновляет роли пользователя
Args:
info: Контекст GraphQL запроса
user: Данные для обновления пользователя (содержит id и roles)
Returns:
Boolean: результат операции
"""
try:
user_id = user.get("id")
roles = user.get("roles", [])
if not roles:
logger.warning(f"Пользователю {user_id} не назначено ни одной роли. Доступ в систему будет заблокирован.")
with local_session() as session:
# Получаем пользователя из базы данных
author = session.query(Author).filter(Author.id == user_id).first()
if not author:
logger.error(f"Пользователь с ID {user_id} не найден")
return False
# Получаем текущие роли пользователя
current_roles = {role.id for role in author.roles} if author.roles else set()
# Обновляем роли только если они изменились
if set(roles) != current_roles:
# Получаем все существующие роли, которые указаны для обновления
role_objects = session.query(Role).filter(Role.id.in_(roles)).all()
# Очищаем текущие роли и добавляем новые
author.roles = role_objects
# Сохраняем изменения в базе данных
session.commit()
# Проверяем, добавлена ли пользователю роль reader
has_reader = 'reader' in roles
if not has_reader:
logger.warning(f"Пользователю {author.email or author.id} не назначена роль 'reader'. Доступ в систему будет ограничен.")
logger.info(f"Роли пользователя {author.email or author.id} обновлены: {', '.join(roles)}")
else:
logger.info(f"Роли пользователя {author.email or author.id} не изменились")
return True
except Exception as e:
import traceback
logger.error(f"Ошибка при обновлении ролей пользователя: {str(e)}")
logger.error(traceback.format_exc())
return False

View File

@@ -40,6 +40,7 @@ async def get_current_user(_, info):
author.last_seen = int(time.time())
session.commit()
# Здесь можно не применять фильтрацию, так как пользователь получает свои данные
return {"token": token, "author": author}
@@ -76,6 +77,7 @@ async def confirm_email(_, info, token):
session.add(user)
session.commit()
logger.info(f"[auth] confirmEmail: Email для пользователя {user_id} успешно подтвержден.")
# Здесь можно не применять фильтрацию, так как пользователь получает свои данные
return {"success": True, "token": session_token, "author": user, "error": None}
except InvalidToken as e:
logger.warning(f"[auth] confirmEmail: Невалидный токен - {e.message}")
@@ -166,6 +168,7 @@ async def register_by_email(_, _info, email: str, password: str = "", name: str
logger.info(
f"[auth] registerUser: Пользователь {email} зарегистрирован, ссылка для подтверждения отправлена."
)
# При регистрации возвращаем данные самому пользователю, поэтому не фильтруем
return {
"success": True,
"token": None,
@@ -237,21 +240,52 @@ async def login(_, info, email: str, password: str):
logger.info(
f"[auth] login: Найден автор {email}, id={author.id}, имя={author.name}, пароль есть: {bool(author.password)}"
)
# Проверяем наличие роли reader
has_reader_role = False
if hasattr(author, "roles") and author.roles:
for role in author.roles:
if role.id == "reader":
has_reader_role = True
break
# Если у пользователя нет роли reader и он не админ, запрещаем вход
if not has_reader_role:
# Проверяем, есть ли роль admin или super
is_admin = author.email in ADMIN_EMAILS.split(",")
if not is_admin:
logger.warning(f"[auth] login: У пользователя {email} нет роли 'reader', в доступе отказано")
return {
"success": False,
"token": None,
"author": None,
"error": "У вас нет необходимых прав для входа. Обратитесь к администратору.",
}
# Проверяем пароль
# Проверяем пароль - важно использовать непосредственно объект author, а не его dict
logger.info(f"[auth] login: НАЧАЛО ПРОВЕРКИ ПАРОЛЯ для {email}")
verify_result = Identity.password(author, password)
logger.info(
f"[auth] login: РЕЗУЛЬТАТ ПРОВЕРКИ ПАРОЛЯ: {verify_result if isinstance(verify_result, dict) else 'успешно'}"
)
try:
verify_result = Identity.password(author, password)
logger.info(
f"[auth] login: РЕЗУЛЬТАТ ПРОВЕРКИ ПАРОЛЯ: {verify_result if isinstance(verify_result, dict) else 'успешно'}"
)
if isinstance(verify_result, dict) and verify_result.get("error"):
logger.warning(f"[auth] login: Неверный пароль для {email}: {verify_result.get('error')}")
if isinstance(verify_result, dict) and verify_result.get("error"):
logger.warning(f"[auth] login: Неверный пароль для {email}: {verify_result.get('error')}")
return {
"success": False,
"token": None,
"author": None,
"error": verify_result.get("error", "Ошибка авторизации"),
}
except Exception as e:
logger.error(f"[auth] login: Ошибка при проверке пароля: {str(e)}")
return {
"success": False,
"token": None,
"author": None,
"error": verify_result.get("error", "Ошибка авторизации"),
"error": str(e),
}
# Получаем правильный объект автора - результат verify_result
@@ -346,9 +380,12 @@ async def login(_, info, email: str, password: str):
if not cookie_set:
logger.warning(f"[auth] login: Не удалось установить cookie никаким способом")
# Возвращаем успешный результат
# Возвращаем успешный результат с данными для клиента
# Для ответа клиенту используем dict() с параметром access=True,
# чтобы получить полный доступ к данным для самого пользователя
logger.info(f"[auth] login: Успешный вход для {email}")
result = {"success": True, "token": token, "author": valid_author, "error": None}
author_dict = valid_author.dict(access=True)
result = {"success": True, "token": token, "author": author_dict, "error": None}
logger.info(
f"[auth] login: Возвращаемый результат: {{success: {result['success']}, token_length: {len(token) if token else 0}}}"
)

View File

@@ -1,6 +1,6 @@
import asyncio
import time
from typing import Optional
from typing import Optional, List, Dict, Any
from sqlalchemy import select, text
@@ -26,11 +26,15 @@ DEFAULT_COMMUNITIES = [1]
# Вспомогательная функция для получения всех авторов без статистики
async def get_all_authors():
async def get_all_authors(current_user_id=None):
"""
Получает всех авторов без статистики.
Используется для случаев, когда нужен полный список авторов без дополнительной информации.
Args:
current_user_id: ID текущего пользователя для проверки прав доступа
is_admin: Флаг, указывающий, является ли пользователь администратором
Returns:
list: Список всех авторов без статистики
"""
@@ -45,15 +49,15 @@ async def get_all_authors():
authors_query = select(Author).where(Author.deleted_at.is_(None))
authors = session.execute(authors_query).scalars().all()
# Преобразуем авторов в словари
return [author.dict() for author in authors]
# Преобразуем авторов в словари с учетом прав доступа
return [author.dict(current_user_id, False) for author in authors]
# Используем универсальную функцию для кеширования запросов
return await cached_query(cache_key, fetch_all_authors)
# Вспомогательная функция для получения авторов со статистикой с пагинацией
async def get_authors_with_stats(limit=50, offset=0, by: Optional[str] = None):
async def get_authors_with_stats(limit=50, offset=0, by: Optional[str] = None, current_user_id: Optional[int] = None):
"""
Получает авторов со статистикой с пагинацией.
@@ -61,7 +65,7 @@ async def get_authors_with_stats(limit=50, offset=0, by: Optional[str] = None):
limit: Максимальное количество возвращаемых авторов
offset: Смещение для пагинации
by: Опциональный параметр сортировки (new/active)
current_user_id: ID текущего пользователя
Returns:
list: Список авторов с их статистикой
"""
@@ -133,15 +137,18 @@ async def get_authors_with_stats(limit=50, offset=0, by: Optional[str] = None):
# Формируем результат с добавлением статистики
result = []
for author in authors:
# Получаем словарь с учетом прав доступа
author_dict = author.dict()
author_dict["stat"] = {
"shouts": shouts_stats.get(author.id, 0),
"followers": followers_stats.get(author.id, 0),
}
result.append(author_dict)
# Кешируем каждого автора отдельно для использования в других функциях
await cache_author(author_dict)
# Важно: кэшируем полный словарь для админов
await cache_author(author.dict())
return result
@@ -172,8 +179,8 @@ async def invalidate_authors_cache(author_id=None):
# Получаем user_id автора, если есть
with local_session() as session:
author = session.query(Author).filter(Author.id == author_id).first()
if author and author.user:
specific_keys.append(f"author:user:{author.user.strip()}")
if author and Author.id:
specific_keys.append(f"author:user:{Author.id.strip()}")
# Удаляем конкретные ключи
for key in specific_keys:
@@ -198,24 +205,28 @@ async def invalidate_authors_cache(author_id=None):
@login_required
async def update_author(_, info, profile):
user_id = info.context.get("user_id")
is_admin = info.context.get("is_admin", False)
if not user_id:
return {"error": "unauthorized", "author": None}
try:
with local_session() as session:
author = session.query(Author).where(Author.user == user_id).first()
author = session.query(Author).where(Author.id == user_id).first()
if author:
Author.update(author, profile)
session.add(author)
session.commit()
author_query = select(Author).where(Author.user == user_id)
author_query = select(Author).where(Author.id == user_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()
# await cache_author(author_dict)
# Кэшируем полную версию для админов
author_dict = author_with_stat.dict(is_admin=True)
asyncio.create_task(cache_author(author_dict))
return {"error": None, "author": author}
# Возвращаем обычную полную версию, т.к. это владелец
return {"error": None, "author": author}
except Exception as exc:
import traceback
@@ -224,24 +235,46 @@ async def update_author(_, info, profile):
@query.field("get_authors_all")
async def get_authors_all(_, _info):
async def get_authors_all(_, info):
"""
Получает список всех авторов без статистики.
Returns:
list: Список всех авторов
"""
return await get_all_authors()
# Получаем ID текущего пользователя и флаг админа из контекста
current_user_id = info.context.get("user_id") if hasattr(info, "context") else None
authors = await get_all_authors(current_user_id, False)
return authors
@query.field("get_author")
async def get_author(_, _info, slug="", author_id=0):
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
author_dict = None
try:
author_id = get_author_id_from(slug=slug, user="", author_id=author_id)
if not author_id:
raise ValueError("cant find")
author_dict = await get_cached_author(int(author_id), get_with_stat)
# Получаем данные автора из кэша (полные данные)
cached_author = await get_cached_author(int(author_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)
# Получаем отфильтрованную версию
author_dict = temp_author.dict(current_user_id, is_admin)
# Добавляем статистику, которая могла быть в кэшированной версии
if "stat" in cached_author:
author_dict["stat"] = cached_author["stat"]
if not author_dict or not author_dict.get("stat"):
# update stat from db
@@ -250,9 +283,15 @@ async def get_author(_, _info, slug="", author_id=0):
if result:
author_with_stat = result[0]
if isinstance(author_with_stat, Author):
author_dict = author_with_stat.dict()
# await cache_author(author_dict)
asyncio.create_task(cache_author(author_dict))
# Кэшируем полные данные для админов
original_dict = author_with_stat.dict(is_admin=True)
asyncio.create_task(cache_author(original_dict))
# Возвращаем отфильтрованную версию
author_dict = author_with_stat.dict(current_user_id, is_admin)
# Добавляем статистику
if hasattr(author_with_stat, "stat"):
author_dict["stat"] = author_with_stat.stat
except ValueError:
pass
except Exception as exc:
@@ -263,31 +302,43 @@ async def get_author(_, _info, slug="", author_id=0):
@query.field("get_author_id")
async def get_author_id(_, _info, user: str):
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:
author = await get_cached_author_by_user_id(user_id, get_with_stat)
if author:
return author
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.user == user_id)
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):
author_dict = author_with_stat.dict()
# await cache_author(author_dict)
asyncio.create_task(cache_author(author_dict))
return author_with_stat
# Кэшируем полную версию данных
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):
async def load_authors_by(_, info, by, limit, offset):
"""
Загружает авторов по заданному критерию с пагинацией.
@@ -299,8 +350,12 @@ async def load_authors_by(_, _info, by, limit, offset):
Returns:
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
# Используем оптимизированную функцию для получения авторов
return await get_authors_with_stats(limit, offset, by)
return await get_authors_with_stats(limit, offset, by, current_user_id, is_admin)
def get_author_id_from(slug="", user=None, author_id=None):
@@ -316,7 +371,7 @@ def get_author_id_from(slug="", user=None, author_id=None):
author_id = author.id
return author_id
if user:
author = session.query(Author).filter(Author.user == user).first()
author = session.query(Author).filter(Author.id == user).first()
if author:
author_id = author.id
except Exception as exc:
@@ -325,14 +380,30 @@ 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):
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
logger.debug(f"getting follows for @{slug}")
author_id = get_author_id_from(slug=slug, user=user, author_id=author_id)
if not author_id:
return {}
followed_authors = await get_cached_follower_authors(author_id)
# Получаем данные из кэша
followed_authors_raw = await get_cached_follower_authors(author_id)
followed_topics = await get_cached_follower_topics(author_id)
# Фильтруем чувствительные данные авторов
followed_authors = []
for author_data in followed_authors_raw:
# Создаем объект автора для использования метода dict
temp_author = Author()
for key, value in author_data.items():
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
followed_authors.append(temp_author.dict(current_user_id, is_admin))
# TODO: Get followed communities too
return {
@@ -354,18 +425,36 @@ 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):
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
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 = await get_cached_follower_authors(author_id)
# Получаем данные из кэша
followed_authors_raw = await get_cached_follower_authors(author_id)
# Фильтруем чувствительные данные авторов
followed_authors = []
for author_data in followed_authors_raw:
# Создаем объект автора для использования метода dict
temp_author = Author()
for key, value in author_data.items():
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
followed_authors.append(temp_author.dict(current_user_id, is_admin))
return followed_authors
def create_author(user_id: str, slug: str, name: str = ""):
author = Author()
author.user = user_id # Связь с user_id из системы авторизации
Author.id = user_id # Связь с user_id из системы авторизации
author.slug = slug # Идентификатор из системы авторизации
author.created_at = author.updated_at = int(time.time())
author.name = name or slug # если не указано
@@ -377,10 +466,28 @@ 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):
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
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)
if not author_id:
return []
followers = await get_cached_author_followers(author_id)
# Получаем данные из кэша
followers_raw = await get_cached_author_followers(author_id)
# Фильтруем чувствительные данные авторов
followers = []
for follower_data in followers_raw:
# Создаем объект автора для использования метода dict
temp_author = Author()
for key, value in follower_data.items():
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
followers.append(temp_author.dict(current_user_id, is_admin))
return followers

View File

@@ -71,7 +71,7 @@ async def create_invite(_, info, slug: str = "", author_id: int = 0):
# 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.user == user_id).first()
inviter = session.query(Author).filter(Author.id == user_id).first()
if inviter and shout and shout.authors and inviter.id is shout.created_by:
# Check if an invite already exists
existing_invite = (
@@ -109,7 +109,7 @@ async def create_invite(_, info, slug: str = "", author_id: int = 0):
async def remove_author(_, info, slug: str = "", author_id: int = 0):
user_id = info.context["user_id"]
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
author = session.query(Author).filter(Author.id == user_id).first()
if author:
shout = session.query(Shout).filter(Shout.slug == slug).first()
# NOTE: owner should be first in a list

View File

@@ -23,7 +23,7 @@ async def get_communities_by_author(_, _info, slug="", user="", author_id=0):
author_id = session.query(Author).where(Author.slug == slug).first().id
q = q.where(CommunityFollower.author == author_id)
if user:
author_id = session.query(Author).where(Author.user == user).first().id
author_id = session.query(Author).where(Author.id == user).first().id
q = q.where(CommunityFollower.author == author_id)
if author_id:
q = q.where(CommunityFollower.author == author_id)

View File

@@ -643,7 +643,7 @@ 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.user
info.context["user_id"] = author.id
unfollow(None, info, "shout", shout.slug)
for topic in shout.topics:

View File

@@ -63,7 +63,14 @@ async def follow(_, info, what, slug="", entity_id=0):
return {"error": f"{what.lower()} not found"}
if not entity_id and entity:
entity_id = entity.id
entity_dict = entity.dict()
# Если это автор, учитываем фильтрацию данных
if what == "AUTHOR":
# Полная версия для кэширования
entity_dict = entity.dict(is_admin=True)
else:
entity_dict = entity.dict()
logger.debug(f"entity_id: {entity_id}, entity_dict: {entity_dict}")
if entity_id:
@@ -96,7 +103,35 @@ async def follow(_, info, what, slug="", entity_id=0):
if get_cached_follows_method:
logger.debug("Получение подписок из кэша")
existing_follows = await get_cached_follows_method(follower_id)
follows = [*existing_follows, entity_dict] if not existing_sub else existing_follows
# Если это авторы, получаем безопасную версию
if what == "AUTHOR":
# Получаем ID текущего пользователя и фильтруем данные
current_user_id = user_id
follows_filtered = []
for author_data in existing_follows:
# Создаем объект автора для использования метода dict
temp_author = Author()
for key, value in author_data.items():
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
follows_filtered.append(temp_author.dict(current_user_id, False))
if not existing_sub:
# Создаем объект автора для entity_dict
temp_author = Author()
for key, value in entity_dict.items():
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
follows = [*follows_filtered, temp_author.dict(current_user_id, False)]
else:
follows = follows_filtered
else:
follows = [*existing_follows, entity_dict] if not existing_sub else existing_follows
logger.debug("Обновлен список подписок")
if what == "AUTHOR" and not existing_sub:
@@ -171,11 +206,38 @@ async def unfollow(_, info, what, slug="", entity_id=0):
if cache_method:
logger.debug("Обновление кэша после отписки")
await cache_method(entity.dict())
# Если это автор, кэшируем полную версию
if what == "AUTHOR":
await cache_method(entity.dict(is_admin=True))
else:
await cache_method(entity.dict())
if get_cached_follows_method:
logger.debug("Получение подписок из кэша")
existing_follows = await get_cached_follows_method(follower_id)
follows = filter(lambda x: x["id"] != entity_id, existing_follows)
# Если это авторы, получаем безопасную версию
if what == "AUTHOR":
# Получаем ID текущего пользователя и фильтруем данные
current_user_id = user_id
follows_filtered = []
for author_data in existing_follows:
if author_data["id"] == entity_id:
continue
# Создаем объект автора для использования метода dict
temp_author = Author()
for key, value in author_data.items():
if hasattr(temp_author, key):
setattr(temp_author, key, value)
# Добавляем отфильтрованную версию
follows_filtered.append(temp_author.dict(current_user_id, False))
follows = follows_filtered
else:
follows = [item for item in existing_follows if item["id"] != entity_id]
logger.debug("Обновлен список подписок")
if what == "AUTHOR":

View File

@@ -215,7 +215,7 @@ async def set_featured(session, shout_id):
session.commit()
author = session.query(Author).filter(Author.id == s.created_by).first()
if author:
await add_user_role(str(author.user))
await add_user_role(str(author.id))
session.add(s)
session.commit()
@@ -446,7 +446,7 @@ async def delete_reaction(_, info, reaction_id: int):
with local_session() as session:
try:
author = session.query(Author).filter(Author.user == user_id).one()
author = session.query(Author).filter(Author.id == user_id).one()
r = session.query(Reaction).filter(Reaction.id == reaction_id).one()
if r.created_by != author_id and "editor" not in roles:

View File

@@ -255,7 +255,7 @@ async def get_topics_by_author(_, _info, author_id=0, slug="", user=""):
elif slug:
topics_by_author_query = topics_by_author_query.join(Author).where(Author.slug == slug)
elif user:
topics_by_author_query = topics_by_author_query.join(Author).where(Author.user == user)
topics_by_author_query = topics_by_author_query.join(Author).where(Author.id == user)
return get_with_stat(topics_by_author_query)
@@ -320,7 +320,7 @@ async def delete_topic(_, info, slug: str):
t: Topic = session.query(Topic).filter(Topic.slug == slug).first()
if not t:
return {"error": "invalid topic slug"}
author = session.query(Author).filter(Author.user == user_id).first()
author = session.query(Author).filter(Author.id == user_id).first()
if author:
if t.created_by != author.id:
return {"error": "access denied"}