From 7136304c63b16e0ac26017eeab8cd3d3815dd6a9 Mon Sep 17 00:00:00 2001 From: Jackson Chen <90215880+Sma1lboy@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:28:09 -0600 Subject: [PATCH 1/2] feat: add build and start scripts for frontend and backend (#143) ## Summary by CodeRabbit - **New Features** - Added dedicated commands for building and starting both the frontend and backend, offering improved operational clarity and separation between environments. - **Chores** - Refined task dependencies and command configurations to enhance performance, streamline development workflows, and simplify maintenance. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- backend/package.json | 2 ++ codefox-common/package.json | 2 ++ frontend/package.json | 3 ++- llm-server/package.json | 6 ++++-- package.json | 6 +++++- turbo.json | 16 ++++++++++++++++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/backend/package.json b/backend/package.json index aaa90edb..a74cf3ff 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,10 +8,12 @@ "packageManager": "pnpm@9.1.0", "scripts": { "build": "nest build", + "build:backend": "pnpm build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "lint": "ts-prune \"{src,apps,libs,test}/**/*.ts\" && eslint \"{src,apps,libs,test}/**/*.ts\" --fix ", "start": "nest start", "start:dev": "NODE_OPTIONS=\"--experimental-specifier-resolution=node\" nest start --watch", + "start:backend": "pnpm start", "dev": "pnpm start:dev", "dev:backend": "pnpm start:dev", "start:debug": "nest start --debug --watch", diff --git a/codefox-common/package.json b/codefox-common/package.json index 8123a03b..d67eef64 100644 --- a/codefox-common/package.json +++ b/codefox-common/package.json @@ -19,6 +19,8 @@ ], "scripts": { "build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types", + "build:frontend": "pnpm run build", + "build:backend": "pnpm run build", "build:cjs": "tsc -p tsconfig.cjs.json", "build:esm": "tsc -p tsconfig.esm.json", "build:types": "tsc -p tsconfig.types.json", diff --git a/frontend/package.json b/frontend/package.json index e7c58a36..09b55b32 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,8 +4,9 @@ "private": true, "scripts": { "build": "next build", + "build:frontend": "next build", "dev": "next dev", - "start:dev": "next dev", + "start:frontend": "next start", "start:dev-watch": "tmuxinator start -p .tmuxinator/build.yml", "dev:watch": "tmuxinator start -p .tmuxinator/dev.yml", "start": "next start", diff --git a/llm-server/package.json b/llm-server/package.json index dc217305..2acccfee 100644 --- a/llm-server/package.json +++ b/llm-server/package.json @@ -5,11 +5,13 @@ "type": "module", "scripts": { "start": "NODE_OPTIONS='--enable-source-maps' nodemon --watch \"src/**/*.ts\" --exec \"tsx\" src/main.ts", + "start:backend": "pnpm start", "dev": "NODE_OPTIONS='--enable-source-maps' nodemon --watch \"src/**/*.ts\" --exec \"tsx\" src/main.ts", "dev:backend": "pnpm dev", - "build": " tsc", + "build": "tsc", + "build:backend": "pnpm build", "serve": "node --enable-source-maps dist/main.js", - "format": "prettier --write \"src/**/*.ts\" ", + "format": "prettier --write \"src/**/*.ts\"", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.js", "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.js --watch", diff --git a/package.json b/package.json index 2ae4201b..982461ca 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "scripts": { "build": "turbo build", "build:common": "pnpm --filter codefox-common run build", + "build:frontend": "turbo build:frontend", + "build:backend": "turbo build:backend", "dev:turbo": "turbo dev", "dev": "tmuxinator start -p .tmuxinator/dev.yml", "lint": "eslint . --ext .js,.ts,.tsx", @@ -14,7 +16,9 @@ "dev:backend": "turbo dev:backend", "test": "turbo test", "fix": "eslint . --ext .js,.ts,.tsx --fix", - "start": "turbo start" + "start": "turbo start", + "start:frontend": "turbo start:frontend", + "start:backend": "turbo start:backend" }, "postinstall": "pnpm --filter codefox-common run build", "keywords": [], diff --git a/turbo.json b/turbo.json index dd42c2db..54bc2361 100644 --- a/turbo.json +++ b/turbo.json @@ -21,6 +21,22 @@ "start": { "dependsOn": ["^build"], "cache": false + }, + "start:frontend": { + "dependsOn": ["^build"], + "cache": false + }, + "start:backend": { + "dependsOn": ["^build"], + "cache": false + }, + "build:frontend": { + "dependsOn": ["^build:frontend"], + "cache": true + }, + "build:backend": { + "dependsOn": ["^build:backend"], + "cache": true } } } From b6162854e06aaea88a630764718285439e8ee943 Mon Sep 17 00:00:00 2001 From: ZHallen122 <106571949+ZHallen122@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:22:31 -0500 Subject: [PATCH 2/2] Project create limit (#148) ## Summary by CodeRabbit - **New Features** - Introduced a daily limit for project creation, capping the number of new projects a user can create per day. - Added a new query endpoint that lets users check how many projects they can still create today. - Enhanced error notifications to clearly inform users when their daily project creation limit has been reached. --- 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); + } }