import graphene from graphene_django import DjangoObjectType from django.contrib.auth import get_user_model from ..models import Team as TeamModel, TeamMember as TeamMemberModel, TeamInvitation as TeamInvitationModel, UserProfile from .team_schema import SelectedLocation UserModel = get_user_model() def _get_or_create_user_with_profile(logto_id: str): user, _ = UserModel.objects.get_or_create( username=logto_id, defaults={'email': ''} ) profile, _ = UserProfile.objects.get_or_create( logto_id=logto_id, defaults={'user': user} ) if profile.user_id != user.id: profile.user = user profile.save(update_fields=['user']) # Attach profile to user for resolvers user.profile = profile return user class Team(DjangoObjectType): id = graphene.String() logtoOrgId = graphene.String() teamType = graphene.String() selectedLocation = graphene.Field(SelectedLocation) class Meta: model = TeamModel fields = ('uuid', 'name', 'logto_org_id', 'team_type', 'created_at') def resolve_id(self, info): return self.uuid def resolve_logtoOrgId(self, info): return self.logto_org_id def resolve_teamType(self, info): return self.team_type def resolve_selectedLocation(self, info): loc_type = getattr(self, 'selected_location_type', None) loc_uuid = getattr(self, 'selected_location_uuid', None) if loc_type and loc_uuid: return SelectedLocation( type=loc_type, uuid=loc_uuid, name=getattr(self, 'selected_location_name', None), latitude=getattr(self, 'selected_location_latitude', None), longitude=getattr(self, 'selected_location_longitude', None) ) return None class User(DjangoObjectType): id = graphene.String() firstName = graphene.String() lastName = graphene.String() phone = graphene.String() avatarId = graphene.String() activeTeamId = graphene.String() activeTeam = graphene.Field(Team) teams = graphene.List(Team) class Meta: model = UserModel fields = ('username', 'first_name', 'last_name', 'email') def resolve_id(self, info): if hasattr(self, 'profile') and self.profile: return self.profile.logto_id return self.username def resolve_firstName(self, info): return self.first_name def resolve_lastName(self, info): return self.last_name def resolve_phone(self, info): return getattr(self.profile, 'phone', None) def resolve_avatarId(self, info): return getattr(self.profile, 'avatar_id', None) def resolve_activeTeamId(self, info): return self.profile.active_team.uuid if getattr(self, 'profile', None) and self.profile.active_team else None def resolve_activeTeam(self, info): return self.profile.active_team if getattr(self, 'profile', None) else None def resolve_teams(self, info): # Возвращаем Team объекты через TeamMember отношения from ..models import TeamMember as TeamMemberModel team_members = TeamMemberModel.objects.filter(user=self) return [member.team for member in team_members] class TeamMember(DjangoObjectType): user = graphene.Field(User) role = graphene.String() joinedAt = graphene.String() class Meta: from ..models import TeamMember as TeamMemberModel model = TeamMemberModel fields = ('uuid', 'role') def resolve_joinedAt(self, info): return self.joined_at.isoformat() if self.joined_at else None class TeamInvitation(DjangoObjectType): email = graphene.String() role = graphene.String() status = graphene.String() invitedBy = graphene.String() expiresAt = graphene.String() createdAt = graphene.String() class Meta: model = TeamInvitationModel fields = ('uuid', 'email', 'role', 'status') def resolve_invitedBy(self, info): return self.invited_by def resolve_expiresAt(self, info): return self.expires_at.isoformat() if self.expires_at else None def resolve_createdAt(self, info): return self.created_at.isoformat() if self.created_at else None class TeamWithMembers(DjangoObjectType): id = graphene.String() members = graphene.List(TeamMember) invitations = graphene.List(TeamInvitation) class Meta: model = TeamModel fields = ('uuid', 'name', 'created_at') def resolve_id(self, info): return self.uuid def resolve_members(self, info): return self.members.all() def resolve_invitations(self, info): return self.invitations.filter(status='PENDING') class UserQuery(graphene.ObjectType): me = graphene.Field(User) get_team = graphene.Field(TeamWithMembers, team_id=graphene.String(required=True)) def resolve_me(self, info): # Получаем user_id из ID Token user_id = getattr(info.context, 'user_id', None) if not user_id: return None try: return _get_or_create_user_with_profile(user_id) except Exception: return None def resolve_get_team(self, info, team_id): try: return TeamModel.objects.get(uuid=team_id) except TeamModel.DoesNotExist: return None class CreateTeamInput(graphene.InputObjectType): name = graphene.String(required=True) teamType = graphene.String() # BUYER или SELLER class UpdateUserInput(graphene.InputObjectType): firstName = graphene.String() lastName = graphene.String() phone = graphene.String() avatarId = graphene.String() class CreateTeamMutation(graphene.Mutation): class Arguments: input = CreateTeamInput(required=True) team = graphene.Field(Team) def mutate(self, info, input): # Получаем user_id из контекста (ID Token) user_id = getattr(info.context, 'user_id', None) if not user_id: raise Exception("User not authenticated") try: owner = _get_or_create_user_with_profile(user_id) team = TeamModel.objects.create( name=input.name, owner=owner, team_type=input.teamType or 'BUYER' ) # Добавляем owner как участника команды с ролью OWNER TeamMemberModel.objects.create( team=team, user=owner, role='OWNER' ) # Устанавливаем как активную команду, если у пользователя её нет if hasattr(owner, 'profile') and not owner.profile.active_team: owner.profile.active_team = team owner.profile.save(update_fields=['active_team']) return CreateTeamMutation(team=team) except Exception as e: raise Exception(f"Failed to create team: {str(e)}") class UpdateUserMutation(graphene.Mutation): class Arguments: userId = graphene.String(required=True) input = UpdateUserInput(required=True) user = graphene.Field(User) def mutate(self, info, userId, input): # Проверяем права - пользователь может редактировать только себя context_user_id = getattr(info.context, 'user_id', None) if context_user_id != userId: return UpdateUserMutation(user=None) try: user = _get_or_create_user_with_profile(userId) if input.firstName is not None: user.first_name = input.firstName if input.lastName is not None: user.last_name = input.lastName user.save() if hasattr(user, 'profile'): if input.phone is not None: user.profile.phone = input.phone if input.avatarId is not None: user.profile.avatar_id = input.avatarId user.profile.save() return UpdateUserMutation(user=user) except Exception: return UpdateUserMutation(user=None) class SwitchTeamMutation(graphene.Mutation): class Arguments: teamId = graphene.String(required=True) user = graphene.Field(User) def mutate(self, info, teamId): user_id = getattr(info.context, 'user_id', None) if not user_id: raise Exception("User not authenticated") try: team = TeamModel.objects.get(uuid=teamId) user = _get_or_create_user_with_profile(user_id) if hasattr(user, 'profile'): user.profile.active_team = team user.profile.save(update_fields=['active_team']) return SwitchTeamMutation(user=user) except TeamModel.DoesNotExist: raise Exception("Team not found") class UserMutation(graphene.ObjectType): create_team = CreateTeamMutation.Field() update_user = UpdateUserMutation.Field() switch_team = SwitchTeamMutation.Field() user_schema = graphene.Schema(query=UserQuery, mutation=UserMutation)