From 9642291658d8720609139c9dbf6429a24ffbf912 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 3 Mar 2025 20:47:11 -0500 Subject: [PATCH] Project create limit --- backend/src/project/project-limits.ts | 26 ++++++++++ backend/src/project/project.resolver.ts | 9 ++++ backend/src/project/project.service.ts | 65 ++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 backend/src/project/project-limits.ts diff --git a/backend/src/project/project-limits.ts b/backend/src/project/project-limits.ts new file mode 100644 index 00000000..018c1b88 --- /dev/null +++ b/backend/src/project/project-limits.ts @@ -0,0 +1,26 @@ +import { ForbiddenException, HttpStatus } from '@nestjs/common'; +import { GraphQLError } from 'graphql'; + +export const PROJECT_DAILY_LIMIT = 3; // Maximum number of projects a user can create per day + +export enum ProjectErrorCode { + DAILY_LIMIT_EXCEEDED = 'DAILY_LIMIT_EXCEEDED', +} + +export class ProjectRateLimitException extends ForbiddenException { + constructor(limit: number) { + super( + `Daily project creation limit of ${limit} reached. Please try again tomorrow.`, + ); + } + + getGraphQLError(): GraphQLError { + return new GraphQLError(this.message, { + extensions: { + code: ProjectErrorCode.DAILY_LIMIT_EXCEEDED, + limit: PROJECT_DAILY_LIMIT, + status: HttpStatus.TOO_MANY_REQUESTS, + }, + }); + } +} diff --git a/backend/src/project/project.resolver.ts b/backend/src/project/project.resolver.ts index 944d8174..69686ee5 100644 --- a/backend/src/project/project.resolver.ts +++ b/backend/src/project/project.resolver.ts @@ -7,6 +7,7 @@ import { ResolveField, Parent, ID, + Int, } from '@nestjs/graphql'; import { ProjectService } from './project.service'; import { Project } from './project.model'; @@ -147,4 +148,12 @@ export class ProjectsResolver { ): Promise { return this.projectService.fetchPublicProjects(input); } + + // In ProjectsResolver: + @Query(() => Int) + async getRemainingProjectLimit( + @GetUserIdFromToken() userId: string, + ): Promise { + return this.projectService.getRemainingProjectLimit(userId); + } } diff --git a/backend/src/project/project.service.ts b/backend/src/project/project.service.ts index c30fa75e..61d92f51 100644 --- a/backend/src/project/project.service.ts +++ b/backend/src/project/project.service.ts @@ -7,7 +7,7 @@ import { ForbiddenException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { In, Not, Repository } from 'typeorm'; +import { Between, In, Not, Repository } from 'typeorm'; import { Project } from './project.model'; import { ProjectPackages } from './project-packages.model'; import { @@ -26,6 +26,11 @@ import { BuilderContext } from 'src/build-system/context'; import { ChatService } from 'src/chat/chat.service'; import { Chat } from 'src/chat/chat.model'; import { v4 as uuidv4 } from 'uuid'; +import { + PROJECT_DAILY_LIMIT, + ProjectRateLimitException, +} from './project-limits'; + @Injectable() export class ProjectService { private readonly model: OpenAIModelProvider = @@ -119,12 +124,41 @@ export class ProjectService { } } + /** + * Checks if a user has exceeded their daily project creation limit + * @param userId The user ID to check + * @returns A boolean indicating whether the user can create more projects today + */ + async canCreateProject(userId: string): Promise { + const today = new Date(); + today.setHours(0, 0, 0, 0); // Start of today + + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); // Start of tomorrow + + // Count projects created by user today + const todayProjectCount = await this.projectsRepository.count({ + where: { + userId: userId, + createdAt: Between(today, tomorrow), + }, + }); + + return todayProjectCount < PROJECT_DAILY_LIMIT; + } + async createProject( input: CreateProjectInput, userId: string, ): Promise { try { - // First, handle project name generation if needed (this is the only sync operation we need) + //First check if user have reach the create project limit + const canCreate = await this.canCreateProject(userId); + if (!canCreate) { + throw new ProjectRateLimitException(PROJECT_DAILY_LIMIT); + } + + // handle project name generation if needed (this is the only sync operation we need) let projectName = input.projectName; if (!projectName || projectName === '') { this.logger.debug( @@ -164,6 +198,10 @@ export class ProjectService { // Return chat immediately so user can start interacting return defaultChat; } catch (error) { + if (error instanceof ProjectRateLimitException) { + throw error.getGraphQLError(); // Throw as a GraphQL error for the client + } + this.logger.error( `Error in createProject: ${error.message}`, error.stack, @@ -602,4 +640,27 @@ export class ProjectService { return []; } + + /** + * Gets the number of projects a user can still create today + * @param userId The user ID to check + * @returns The number of remaining projects the user can create today + */ + async getRemainingProjectLimit(userId: string): Promise { + const today = new Date(); + today.setHours(0, 0, 0, 0); // Start of today + + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); // Start of tomorrow + + // Count projects created by this user today + const todayProjectCount = await this.projectsRepository.count({ + where: { + userId: userId, + createdAt: Between(today, tomorrow), + }, + }); + + return Math.max(0, PROJECT_DAILY_LIMIT - todayProjectCount); + } }