Skip to content

Commit

Permalink
Merge branch 'main' into feat-adding-s3
Browse files Browse the repository at this point in the history
  • Loading branch information
NarwhalChen authored Mar 5, 2025
2 parents da2b890 + b616285 commit 8deecfc
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 6 deletions.
2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
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 @@ -159,4 +160,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);
}
}
64 changes: 62 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 @@ -27,6 +27,10 @@ import { ChatService } from 'src/chat/chat.service';
import { Chat } from 'src/chat/chat.model';
import { v4 as uuidv4 } from 'uuid';
import { UploadService } from 'src/upload/upload.service';
import {
PROJECT_DAILY_LIMIT,
ProjectRateLimitException,
} from './project-limits';
@Injectable()
export class ProjectService {
private readonly model: OpenAIModelProvider =
Expand Down Expand Up @@ -122,12 +126,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 @@ -167,6 +200,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 @@ -623,4 +660,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);
}
}
2 changes: 2 additions & 0 deletions codefox-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 4 additions & 2 deletions llm-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
"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",
"format": "prettier --write .",
"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": [],
Expand Down
16 changes: 16 additions & 0 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}

0 comments on commit 8deecfc

Please sign in to comment.