import { Injectable, NotFoundException, Inject } from '@nestjs/common'; import { eq, and } from 'drizzle-orm'; import { DRIZZLE } from '../../../database/database.module'; import * as schema from '../../../database/schema'; import { CreateProjectDto } from '../dto/create-project.dto'; import { UpdateProjectDto } from '../dto/update-project.dto'; import { WebSocketsService } from '../../websockets/websockets.service'; @Injectable() export class ProjectsService { constructor( @Inject(DRIZZLE) private readonly db: any, private readonly websocketsService: WebSocketsService, ) {} /** * Create a new project */ async create(createProjectDto: CreateProjectDto) { const [project] = await this.db .insert(schema.projects) .values(createProjectDto) .returning(); // Emit project created event this.websocketsService.emitProjectUpdated(project.id, { action: 'created', project, }); return project; } /** * Find all projects */ async findAll() { return this.db.select().from(schema.projects); } /** * Find projects by owner ID */ async findByOwnerId(ownerId: string) { return this.db .select() .from(schema.projects) .where(eq(schema.projects.ownerId, ownerId)); } /** * Find a project by ID */ async findById(id: string) { const [project] = await this.db .select() .from(schema.projects) .where(eq(schema.projects.id, id)); if (!project) { throw new NotFoundException(`Project with ID ${id} not found`); } return project; } /** * Update a project */ async update(id: string, updateProjectDto: UpdateProjectDto) { const [project] = await this.db .update(schema.projects) .set({ ...updateProjectDto, updatedAt: new Date(), }) .where(eq(schema.projects.id, id)) .returning(); if (!project) { throw new NotFoundException(`Project with ID ${id} not found`); } // Emit project updated event this.websocketsService.emitProjectUpdated(project.id, { action: 'updated', project, }); return project; } /** * Delete a project */ async remove(id: string) { const [project] = await this.db .delete(schema.projects) .where(eq(schema.projects.id, id)) .returning(); if (!project) { throw new NotFoundException(`Project with ID ${id} not found`); } // Emit project deleted event this.websocketsService.emitProjectUpdated(project.id, { action: 'deleted', project, }); return project; } /** * Check if a user has access to a project */ async checkUserAccess(projectId: string, userId: string) { // Check if the user is the owner of the project const [project] = await this.db .select() .from(schema.projects) .where( and( eq(schema.projects.id, projectId), eq(schema.projects.ownerId, userId) ) ); if (project) { return true; } // Check if the user is a collaborator on the project const [collaboration] = await this.db .select() .from(schema.projectCollaborators) .where( and( eq(schema.projectCollaborators.projectId, projectId), eq(schema.projectCollaborators.userId, userId) ) ); return !!collaboration; } /** * Add a collaborator to a project */ async addCollaborator(projectId: string, userId: string) { // Check if the project exists const project = await this.findById(projectId); // Check if the user exists const [user] = await this.db .select() .from(schema.users) .where(eq(schema.users.id, userId)); if (!user) { throw new NotFoundException(`User with ID ${userId} not found`); } // Check if the user is already a collaborator on the project const [existingCollaboration] = await this.db .select() .from(schema.projectCollaborators) .where( and( eq(schema.projectCollaborators.projectId, projectId), eq(schema.projectCollaborators.userId, userId) ) ); if (existingCollaboration) { return existingCollaboration; } // Add the user as a collaborator on the project const [collaboration] = await this.db .insert(schema.projectCollaborators) .values({ projectId, userId, }) .returning(); // Emit collaborator added event this.websocketsService.emitCollaboratorAdded(projectId, { project, user, collaboration, }); // Emit notification to the user this.websocketsService.emitNotification(userId, { type: 'project_invitation', message: `You have been added as a collaborator to the project "${project.name}"`, projectId, projectName: project.name, }); return collaboration; } /** * Remove a collaborator from a project */ async removeCollaborator(projectId: string, userId: string) { const [collaboration] = await this.db .delete(schema.projectCollaborators) .where( and( eq(schema.projectCollaborators.projectId, projectId), eq(schema.projectCollaborators.userId, userId) ) ) .returning(); if (!collaboration) { throw new NotFoundException(`User with ID ${userId} is not a collaborator on project with ID ${projectId}`); } return collaboration; } /** * Get all collaborators for a project */ async getCollaborators(projectId: string) { // Check if the project exists await this.findById(projectId); // Get all collaborators for the project return this.db .select({ user: schema.users, }) .from(schema.projectCollaborators) .innerJoin(schema.users, eq(schema.projectCollaborators.userId, schema.users.id)) .where(eq(schema.projectCollaborators.projectId, projectId)); } }