- Implemented `WebSocketsGateway` for handling Socket.IO connections, events, and rooms. - Added `WebSocketsService` to act as a facade for emitting WebSocket events (projects, groups, notifications). - Registered `WebSocketsModule` and integrated it into `AppModule`. - Enabled real-time updates in `ProjectsService` and `GroupsService` with relevant WebSocket events (create, update, delete, collaborator/group actions).
245 lines
5.9 KiB
TypeScript
245 lines
5.9 KiB
TypeScript
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));
|
|
}
|
|
}
|