Skip to content

Commit

Permalink
Project create limit (#148)
Browse files Browse the repository at this point in the history
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## 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.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
ZHallen122 authored Mar 4, 2025
1 parent 7136304 commit b616285
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 2 deletions.
26 changes: 26 additions & 0 deletions backend/src/project/project-limits.ts
Original file line number Diff line number Diff line change
@@ -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,
},
});
}
}
9 changes: 9 additions & 0 deletions backend/src/project/project.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ResolveField,
Parent,
ID,
Int,
} from '@nestjs/graphql';
import { ProjectService } from './project.service';
import { Project } from './project.model';
Expand Down Expand Up @@ -147,4 +148,12 @@ export class ProjectsResolver {
): Promise<Project[]> {
return this.projectService.fetchPublicProjects(input);
}

// In ProjectsResolver:
@Query(() => Int)
async getRemainingProjectLimit(
@GetUserIdFromToken() userId: string,
): Promise<number> {
return this.projectService.getRemainingProjectLimit(userId);
}
}
65 changes: 63 additions & 2 deletions backend/src/project/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 =
Expand Down Expand Up @@ -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<boolean> {
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<Chat> {
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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<number> {
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);
}
}

0 comments on commit b616285

Please sign in to comment.