Files
core/resolvers/community.py

248 lines
11 KiB
Python
Raw Normal View History

from typing import Any
from graphql import GraphQLResolveInfo
2025-07-31 18:55:59 +03:00
from sqlalchemy import distinct, func
2025-05-16 09:23:48 +03:00
from auth.orm import Author
2025-07-31 18:55:59 +03:00
from auth.permissions import ContextualPermissionCheck
from orm.community import Community, CommunityAuthor, CommunityFollower
from orm.shout import Shout, ShoutAuthor
2023-12-17 23:30:20 +03:00
from services.db import local_session
2025-07-02 22:30:21 +03:00
from services.rbac import require_any_permission, require_permission
2025-06-30 21:25:26 +03:00
from services.schema import mutation, query, type_community
2025-07-31 18:55:59 +03:00
from utils.logger import root_logger as logger
2024-01-25 22:41:27 +03:00
2024-04-17 18:32:23 +03:00
@query.field("get_communities_all")
async def get_communities_all(_: None, _info: GraphQLResolveInfo) -> list[Community]:
2025-06-30 21:25:26 +03:00
with local_session() as session:
2025-07-31 18:55:59 +03:00
return session.query(Community).all()
2023-10-23 17:47:11 +03:00
2024-04-17 18:32:23 +03:00
@query.field("get_community")
async def get_community(_: None, _info: GraphQLResolveInfo, slug: str) -> Community | None:
2024-10-21 10:52:23 +03:00
q = local_session().query(Community).where(Community.slug == slug)
return q.first()
2023-10-23 17:47:11 +03:00
2024-10-21 10:52:23 +03:00
@query.field("get_communities_by_author")
async def get_communities_by_author(
_: None, _info: GraphQLResolveInfo, slug: str = "", user: str = "", author_id: int = 0
) -> list[Community]:
2024-10-21 10:52:23 +03:00
with local_session() as session:
q = session.query(Community).join(CommunityFollower)
if slug:
2025-07-31 18:55:59 +03:00
author = session.query(Author).where(Author.slug == slug).first()
if author:
author_id = author.id
q = q.where(CommunityFollower.follower == author_id)
2024-10-21 10:52:23 +03:00
if user:
2025-07-31 18:55:59 +03:00
author = session.query(Author).where(Author.id == user).first()
if author:
author_id = author.id
q = q.where(CommunityFollower.follower == author_id)
2024-10-21 10:52:23 +03:00
if author_id:
2025-07-31 18:55:59 +03:00
q = q.where(CommunityFollower.follower == author_id)
2024-10-21 10:52:23 +03:00
return q.all()
return []
2024-10-21 16:42:30 +03:00
@mutation.field("join_community")
2025-07-02 22:30:21 +03:00
@require_permission("community:read")
async def join_community(_: None, info: GraphQLResolveInfo, slug: str) -> dict[str, Any]:
2024-10-21 16:42:30 +03:00
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
2025-07-31 18:55:59 +03:00
if not author_id:
return {"ok": False, "error": "Unauthorized"}
2024-10-21 16:42:30 +03:00
with local_session() as session:
community = session.query(Community).where(Community.slug == slug).first()
if not community:
return {"ok": False, "error": "Community not found"}
2025-07-31 18:55:59 +03:00
session.add(CommunityFollower(community=community.id, follower=int(author_id)))
2024-10-21 16:42:30 +03:00
session.commit()
return {"ok": True}
@mutation.field("leave_community")
async def leave_community(_: None, info: GraphQLResolveInfo, slug: str) -> dict[str, Any]:
2024-10-21 16:42:30 +03:00
author_dict = info.context.get("author", {})
author_id = author_dict.get("id")
with local_session() as session:
session.query(CommunityFollower).where(
2025-07-31 18:55:59 +03:00
CommunityFollower.follower == author_id, CommunityFollower.community == slug
2024-10-21 16:42:30 +03:00
).delete()
session.commit()
return {"ok": True}
@mutation.field("create_community")
2025-07-02 22:30:21 +03:00
@require_permission("community:create")
2025-06-30 21:25:26 +03:00
async def create_community(_: None, info: GraphQLResolveInfo, community_input: dict[str, Any]) -> dict[str, Any]:
# Получаем author_id из контекста через декоратор авторизации
request = info.context.get("request")
author_id = None
if hasattr(request, "auth") and request.auth and hasattr(request.auth, "author_id"):
author_id = request.auth.author_id
elif hasattr(request, "scope") and "auth" in request.scope:
auth_info = request.scope.get("auth", {})
if isinstance(auth_info, dict):
author_id = auth_info.get("author_id")
elif hasattr(auth_info, "author_id"):
author_id = auth_info.author_id
if not author_id:
return {"error": "Не удалось определить автора"}
try:
with local_session() as session:
# Исключаем created_by из входных данных - он всегда из токена
filtered_input = {k: v for k, v in community_input.items() if k != "created_by"}
# Создаем новое сообщество с обязательным created_by из токена
new_community = Community(created_by=author_id, **filtered_input)
session.add(new_community)
2025-07-02 22:30:21 +03:00
session.flush() # Получаем ID сообщества
# Инициализируем права ролей для нового сообщества
await new_community.initialize_role_permissions()
2025-06-30 21:25:26 +03:00
session.commit()
return {"error": None}
except Exception as e:
return {"error": f"Ошибка создания сообщества: {e!s}"}
2024-10-21 16:42:30 +03:00
@mutation.field("update_community")
2025-07-02 22:30:21 +03:00
@require_any_permission(["community:update_own", "community:update_any"])
2025-06-30 21:25:26 +03:00
async def update_community(_: None, info: GraphQLResolveInfo, community_input: dict[str, Any]) -> dict[str, Any]:
# Получаем author_id из контекста через декоратор авторизации
request = info.context.get("request")
author_id = None
if hasattr(request, "auth") and request.auth and hasattr(request.auth, "author_id"):
author_id = request.auth.author_id
elif hasattr(request, "scope") and "auth" in request.scope:
auth_info = request.scope.get("auth", {})
if isinstance(auth_info, dict):
author_id = auth_info.get("author_id")
elif hasattr(auth_info, "author_id"):
author_id = auth_info.author_id
if not author_id:
return {"error": "Не удалось определить автора"}
slug = community_input.get("slug")
if not slug:
return {"error": "Не указан slug сообщества"}
try:
2024-10-21 16:42:30 +03:00
with local_session() as session:
2025-06-30 21:25:26 +03:00
# Находим сообщество для обновления
2025-07-31 18:55:59 +03:00
community = session.query(Community).where(Community.slug == slug).first()
2025-06-30 21:25:26 +03:00
if not community:
return {"error": "Сообщество не найдено"}
# Проверяем права на редактирование (создатель или админ/редактор)
with local_session() as auth_session:
2025-07-31 18:55:59 +03:00
# Получаем роли пользователя в сообществе
community_author = (
auth_session.query(CommunityAuthor)
.where(CommunityAuthor.author_id == author_id, CommunityAuthor.community_id == community.id)
.first()
)
user_roles = community_author.role_list if community_author else []
2025-06-30 21:25:26 +03:00
# Разрешаем редактирование если пользователь - создатель или имеет роль admin/editor
if community.created_by != author_id and "admin" not in user_roles and "editor" not in user_roles:
return {"error": "Недостаточно прав для редактирования этого сообщества"}
# Обновляем поля сообщества
for key, value in community_input.items():
# Исключаем изменение created_by - создатель не может быть изменен
if hasattr(community, key) and key not in ["slug", "created_by"]:
setattr(community, key, value)
session.commit()
return {"error": None}
except Exception as e:
return {"error": f"Ошибка обновления сообщества: {e!s}"}
2024-10-21 16:42:30 +03:00
@mutation.field("delete_community")
2025-07-02 22:30:21 +03:00
@require_any_permission(["community:delete_own", "community:delete_any"])
2025-07-31 18:55:59 +03:00
async def delete_community(root, info, slug: str) -> dict[str, Any]:
2025-06-30 21:25:26 +03:00
try:
2025-07-31 18:55:59 +03:00
# Используем local_session как контекстный менеджер
2025-06-30 21:25:26 +03:00
with local_session() as session:
2025-07-31 18:55:59 +03:00
# Находим сообщество по slug
community = session.query(Community).where(Community.slug == slug).first()
2025-06-30 21:25:26 +03:00
if not community:
2025-07-31 18:55:59 +03:00
return {"error": "Сообщество не найдено", "success": False}
2025-06-30 21:25:26 +03:00
2025-07-31 18:55:59 +03:00
# Проверяем права на удаление
user_id = info.context.get("user_id", 0)
permission_check = ContextualPermissionCheck()
2025-06-30 21:25:26 +03:00
2025-07-31 18:55:59 +03:00
# Проверяем права на удаление сообщества
if not await permission_check.can_delete_community(user_id, community, session):
return {"error": "Недостаточно прав", "success": False}
2025-06-30 21:25:26 +03:00
# Удаляем сообщество
session.delete(community)
2024-10-21 16:42:30 +03:00
session.commit()
2025-06-30 21:25:26 +03:00
2025-07-31 18:55:59 +03:00
return {"success": True, "error": None}
2025-06-30 21:25:26 +03:00
2025-07-31 18:55:59 +03:00
except Exception as e:
# Логируем ошибку
logger.error(f"Ошибка удаления сообщества: {e}")
return {"error": str(e), "success": False}
2025-06-30 21:25:26 +03:00
@type_community.field("stat")
2025-07-31 18:55:59 +03:00
def resolve_community_stat(community: Community | dict[str, Any], *_: Any) -> dict[str, int]:
2025-06-30 21:25:26 +03:00
"""
Резолвер поля stat для Community.
Возвращает статистику сообщества: количество публикаций, подписчиков и авторов.
"""
2025-07-31 18:55:59 +03:00
community_id = community.get("id") if isinstance(community, dict) else community.id
2025-06-30 21:25:26 +03:00
try:
with local_session() as session:
# Количество опубликованных публикаций в сообществе
shouts_count = (
session.query(func.count(Shout.id))
2025-07-31 18:55:59 +03:00
.where(Shout.community == community_id, Shout.published_at.is_not(None), Shout.deleted_at.is_(None))
2025-06-30 21:25:26 +03:00
.scalar()
or 0
)
# Количество подписчиков сообщества
followers_count = (
session.query(func.count(CommunityFollower.follower))
2025-07-31 18:55:59 +03:00
.where(CommunityFollower.community == community_id)
2025-06-30 21:25:26 +03:00
.scalar()
or 0
)
# Количество уникальных авторов, опубликовавших в сообществе
authors_count = (
session.query(func.count(distinct(ShoutAuthor.author)))
.join(Shout, ShoutAuthor.shout == Shout.id)
2025-07-31 18:55:59 +03:00
.where(Shout.community == community_id, Shout.published_at.is_not(None), Shout.deleted_at.is_(None))
2025-06-30 21:25:26 +03:00
.scalar()
or 0
)
return {"shouts": int(shouts_count), "followers": int(followers_count), "authors": int(authors_count)}
except Exception as e:
2025-07-31 18:55:59 +03:00
logger.error(f"Ошибка при получении статистики сообщества {community_id}: {e}")
2025-06-30 21:25:26 +03:00
# Возвращаем нулевую статистику при ошибке
return {"shouts": 0, "followers": 0, "authors": 0}