import { Injectable, ForbiddenException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './entities/user.entity'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository, ) {} async findByEmail(email: string): Promise { return this.usersRepository.findOne({ where: { email: email.toLowerCase() }, }); } async findById(id: string): Promise { return this.usersRepository.findOne({ where: { id } }); } async findByIdWithOrgs(id: string): Promise { return this.usersRepository.findOne({ where: { id }, relations: ['userOrganizations', 'userOrganizations.organization'], }); } async create(data: Partial): Promise { const user = this.usersRepository.create({ ...data, email: data.email?.toLowerCase(), }); return this.usersRepository.save(user); } async updateLastLogin(id: string): Promise { await this.usersRepository.update(id, { lastLoginAt: new Date() }); } async findAllUsers(): Promise { return this.usersRepository.find({ relations: ['userOrganizations', 'userOrganizations.organization'], order: { createdAt: 'DESC' }, }); } async findAllOrganizations(): Promise { const dataSource = this.usersRepository.manager.connection; return dataSource.query(` SELECT o.*, (SELECT COUNT(*) FROM shared.user_organizations WHERE organization_id = o.id) as member_count, (SELECT MAX(lh.logged_in_at) FROM shared.login_history lh WHERE lh.organization_id = o.id) as last_activity FROM shared.organizations o ORDER BY o.created_at DESC `); } async markIntroSeen(id: string): Promise { await this.usersRepository.update(id, { hasSeenIntro: true }); } async setSuperadmin(userId: string, isSuperadmin: boolean): Promise { // Protect platform owner from having superadmin removed const user = await this.usersRepository.findOne({ where: { id: userId } }); if (user?.isPlatformOwner) { throw new ForbiddenException('Cannot modify platform owner superadmin status'); } await this.usersRepository.update(userId, { isSuperadmin }); } }