Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 41 additions & 22 deletions backend/src/project/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,36 @@ export class ProjectService {
message: input.description,
});

// Create project entity and set properties
const project = new Project();
project.projectName = projectName;
project.projectPath = '';
project.userId = userId;
project.isPublic = input.public || false;
project.uniqueProjectId = uuidv4();

// Set project packages
try {
project.projectPackages = await this.transformInputToProjectPackages(
input.packages,
);
} catch (packageError) {
this.logger.error(`Error processing packages: ${packageError.message}`);
// Continue even if packages processing fails
project.projectPackages = [];
}

// Save project
const savedProject = await this.projectsRepository.save(project);
this.logger.debug(`Project created: ${savedProject.id}`);

// Perform the rest of project creation asynchronously
this.createProjectInBackground(input, projectName, userId, defaultChat);
this.createProjectInBackground(
input,
projectName,
savedProject,
defaultChat,
);

// Return chat immediately so user can start interacting
return defaultChat;
Expand All @@ -226,7 +254,7 @@ export class ProjectService {
private async createProjectInBackground(
input: CreateProjectInput,
projectName: string,
userId: string,
savedProject: Project,
chat: Chat,
): Promise<void> {
try {
Expand All @@ -236,31 +264,22 @@ export class ProjectService {
projectName,
});
const context = new BuilderContext(sequence, sequence.id);
const projectPath = await context.execute();

// Create project entity and set properties
const project = new Project();
project.projectName = projectName;
project.projectPath = projectPath;
project.userId = userId;
project.isPublic = input.public || false;
project.uniqueProjectId = uuidv4();
// Execute the context and update projectPath
const projectPath = await context.execute();

// Set project packages
try {
project.projectPackages = await this.transformInputToProjectPackages(
input.packages,
if (projectPath) {
savedProject.projectPath = projectPath;
await this.projectsRepository.save(savedProject); // Update the project with path
this.logger.debug(
`Updated project path: ${savedProject.id} -> ${projectPath}`,
);
} else {
this.logger.error(
`Failed to retrieve project path for: ${savedProject.id}`,
);
} catch (packageError) {
this.logger.error(`Error processing packages: ${packageError.message}`);
// Continue even if packages processing fails
project.projectPackages = [];
}

// Save project
const savedProject = await this.projectsRepository.save(project);
this.logger.debug(`Project created: ${savedProject.id}`);

// Bind chat to project
const bindSuccess = await this.bindProjectAndChat(savedProject, chat);
if (!bindSuccess) {
Expand Down
36 changes: 31 additions & 5 deletions frontend/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,54 @@ import { AuthChoiceModal } from '@/components/auth-choice-modal';
import { useAuthContext } from '@/providers/AuthProvider';
import { ProjectsSection } from '@/components/root/projects-section';
import { PromptForm, PromptFormRef } from '@/components/root/prompt-form';
import { ProjectContext } from '@/components/chat/code-engine/project-context';
import {
CreateProjectResult,
ProjectContext,
} from '@/components/chat/code-engine/project-context';
import { SignInModal } from '@/components/sign-in-modal';
import { SignUpModal } from '@/components/sign-up-modal';
import { useRouter } from 'next/navigation';
import { logger } from '../log/logger';
import { AuroraText } from '@/components/magicui/aurora-text';
import { RateLimitModal } from '@/components/rate-limit-modal';
export default function HomePage() {
// States for AuthChoiceModal
const [showAuthChoice, setShowAuthChoice] = useState(false);
const router = useRouter();
const [showSignIn, setShowSignIn] = useState(false);
const [showSignUp, setShowSignUp] = useState(false);
const [showRateLimitModal, setShowRateLimitModal] = useState(false);
const [rateLimiNumber, setRateLimiNumber] = useState<number | undefined>(
undefined
);
Comment on lines +26 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix typo in state variable name

There's a typo in the state variable name which should be corrected for consistency.

-  const [rateLimiNumber, setRateLimiNumber] = useState<number | undefined>(
+  const [rateLimitNumber, setRateLimitNumber] = useState<number | undefined>(
   undefined
  );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [showRateLimitModal, setShowRateLimitModal] = useState(false);
const [rateLimiNumber, setRateLimiNumber] = useState<number | undefined>(
undefined
);
const [showRateLimitModal, setShowRateLimitModal] = useState(false);
const [rateLimitNumber, setRateLimitNumber] = useState<number | undefined>(
undefined
);


const promptFormRef = useRef<PromptFormRef>(null);
const { isAuthorized } = useAuthContext();
const { createProjectFromPrompt, isLoading } = useContext(ProjectContext);

function isRateLimitResult(
result: CreateProjectResult
): result is { success: false; rateLimit?: boolean; limitNumber?: number } {
return result.success === false;
}
Comment on lines +35 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve type guard function

The isRateLimitResult type guard is checking the success property, but does not specifically check for rate limit conditions, which can lead to incorrect behavior.

function isRateLimitResult(
  result: CreateProjectResult
): result is { success: false; rateLimit?: boolean; limitNumber?: number } {
-  return result.success === false;
+  return result.success === false && result.rateLimit === true;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function isRateLimitResult(
result: CreateProjectResult
): result is { success: false; rateLimit?: boolean; limitNumber?: number } {
return result.success === false;
}
function isRateLimitResult(
result: CreateProjectResult
): result is { success: false; rateLimit?: boolean; limitNumber?: number } {
- return result.success === false;
+ return result.success === false && result.rateLimit === true;
}


const handleSubmit = async () => {
if (!promptFormRef.current) return;

const { message, isPublic, model } = promptFormRef.current.getPromptData();
if (!message.trim()) return;

try {
const chatId = await createProjectFromPrompt(message, isPublic, model);

promptFormRef.current.clearMessage();
router.push(`/chat?id=${chatId}`);
const result = await createProjectFromPrompt(message, isPublic, model);

if (result.success && result.chatId) {
promptFormRef.current.clearMessage();
router.push(`/chat?id=${result.chatId}`);
} else if (isRateLimitResult(result) && result.rateLimit) {
setShowRateLimitModal(true);
setRateLimiNumber(result.limitNumber);
console.log('Rate limit reached ' + result.limitNumber);
}
Comment on lines +53 to +57
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove console.log and fix state setter name

There's a console.log that should be removed, and the state setter name needs to be fixed.

  } else if (isRateLimitResult(result) && result.rateLimit) {
    setShowRateLimitModal(true);
-   setRateLimiNumber(result.limitNumber);
-   console.log('Rate limit reached ' + result.limitNumber);
+   setRateLimitNumber(result.limitNumber);
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (isRateLimitResult(result) && result.rateLimit) {
setShowRateLimitModal(true);
setRateLimiNumber(result.limitNumber);
console.log('Rate limit reached ' + result.limitNumber);
}
} else if (isRateLimitResult(result) && result.rateLimit) {
setShowRateLimitModal(true);
setRateLimitNumber(result.limitNumber);
}
🧰 Tools
🪛 GitHub Actions: autofix.ci

[warning] 56-56: Unexpected console statement no-console

} catch (error) {
logger.error('Error creating project:', error);
}
Expand Down Expand Up @@ -178,6 +198,12 @@ export default function HomePage() {

<SignInModal isOpen={showSignIn} onClose={() => setShowSignIn(false)} />
<SignUpModal isOpen={showSignUp} onClose={() => setShowSignUp(false)} />

<RateLimitModal
isOpen={showRateLimitModal}
limit={rateLimiNumber ?? 3}
onClose={() => setShowRateLimitModal(false)}
/>
Comment on lines +202 to +206
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix variable name in component props

Fix the variable name in the RateLimitModal component props to match the state variable name change.

<RateLimitModal
  isOpen={showRateLimitModal}
- limit={rateLimiNumber ?? 3}
+ limit={rateLimitNumber ?? 3}
  onClose={() => setShowRateLimitModal(false)}
/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<RateLimitModal
isOpen={showRateLimitModal}
limit={rateLimiNumber ?? 3}
onClose={() => setShowRateLimitModal(false)}
/>
<RateLimitModal
isOpen={showRateLimitModal}
limit={rateLimitNumber ?? 3}
onClose={() => setShowRateLimitModal(false)}
/>

</div>
</div>
);
Expand Down
53 changes: 46 additions & 7 deletions frontend/src/components/chat/code-engine/project-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import { useAuthContext } from '@/providers/AuthProvider';
import { URL_PROTOCOL_PREFIX } from '@/utils/const';
import { logger } from '@/app/log/logger';

export type CreateProjectResult =
| { success: true; chatId: string }
| { success: false; rateLimit?: boolean; limitNumber?: number };

export interface ProjectContextType {
projects: Project[];
setProjects: React.Dispatch<React.SetStateAction<Project[]>>;
Expand All @@ -36,7 +40,7 @@ export interface ProjectContextType {
prompt: string,
isPublic: boolean,
model?: string
) => Promise<boolean>;
) => Promise<CreateProjectResult>;
forkProject: (projectId: string) => Promise<void>;
setProjectPublicStatus: (
projectId: string,
Expand Down Expand Up @@ -104,6 +108,8 @@ export function ProjectProvider({ children }: { children: ReactNode }) {
const [projectLoading, setProjectLoading] = useState<boolean>(true);
const [filePath, setFilePath] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const rateLimitReachedRef = useRef(false);
const rateLimitValueRef = useRef<number | undefined>(undefined);
const editorRef = useRef<any>(null);

interface ChatProjectCacheEntry {
Expand Down Expand Up @@ -405,11 +411,24 @@ export function ProjectProvider({ children }: { children: ReactNode }) {
// Create project mutation
const [createProject] = useMutation(CREATE_PROJECT, {
onCompleted: (data) => {
console.log('createProject have yes');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove console.log statement

There's an unnecessary console.log statement that should be removed.

-      console.log('createProject have yes');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log('createProject have yes');
🧰 Tools
🪛 GitHub Actions: autofix.ci

[warning] 412-412: Unexpected console statement no-console

if (!isMounted.current) return;
},
onError: (error) => {
if (isMounted.current) {
toast.error(`Failed to create project: ${error.message}`);
const graphqlErrors = error?.graphQLErrors || [];

const rateLimitError = graphqlErrors.find(
(err) => err.extensions?.code === 'DAILY_LIMIT_EXCEEDED'
);

if (rateLimitError) {
rateLimitReachedRef.current = true;
const limitFromBackend = rateLimitError.extensions?.limit;
if (typeof limitFromBackend === 'number') {
rateLimitValueRef.current = limitFromBackend;
}
} else {
logger.error(`GraphQL error: ${error.message}`);
}
},
});
Expand Down Expand Up @@ -686,19 +705,21 @@ export function ProjectProvider({ children }: { children: ReactNode }) {
prompt: string,
isPublic: boolean,
model = 'gpt-4o-mini'
): Promise<boolean> => {
): Promise<CreateProjectResult> => {
if (!prompt.trim()) {
if (isMounted.current) {
toast.error('Please enter a project description');
}
return false;
return { success: false };
}

try {
if (isMounted.current) {
setIsLoading(true);
}

rateLimitReachedRef.current = false;

// Default packages based on typical web project needs
const defaultPackages = [
{ name: 'react', version: '^18.2.0' },
Expand All @@ -717,13 +738,31 @@ export function ProjectProvider({ children }: { children: ReactNode }) {
},
});

return result.data.createProject.id;
// Rate limit detected
if (rateLimitReachedRef.current) {
return {
success: false,
rateLimit: true,
limitNumber: rateLimitValueRef.current,
};
}

// Check if result and result.data exist before accessing properties
if (result?.data?.createProject?.id) {
return { success: true, chatId: result.data.createProject.id };
} else {
logger.warn(
'Project creation response missing expected data structure'
);
return { success: false };
}
} catch (error) {
logger.error('Error creating project:', error);

if (isMounted.current) {
toast.error('Failed to create project from prompt');
}
return false;
return { success: false };
} finally {
if (isMounted.current) {
setIsLoading(false);
Expand Down
58 changes: 58 additions & 0 deletions frontend/src/components/rate-limit-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// components/rate-limit-modal.tsx
'use client';

import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { AlertTriangle } from 'lucide-react';

interface RateLimitModalProps {
isOpen: boolean;
onClose: () => void;
limit?: number;
}

export const RateLimitModal = ({
isOpen,
onClose,
limit = 3,
}: RateLimitModalProps) => {
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<div className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-amber-500" />
<DialogTitle>Daily Limit Reached</DialogTitle>
</div>
<DialogDescription>
You've reached your daily project creation limit.

Check failure on line 35 in frontend/src/components/rate-limit-modal.tsx

View workflow job for this annotation

GitHub Actions / autofix

`'` can be escaped with `&apos;`, `&lsquo;`, `&#39;`, `&rsquo;`
</DialogDescription>
</DialogHeader>

<div className="py-4">
<p className="mb-4">
Your current plan allows creating up to {limit} projects per day.
This limit resets at midnight UTC.
</p>
<div className="bg-amber-50 dark:bg-amber-950/30 p-3 rounded-md border border-amber-200 dark:border-amber-800">
<p className="text-sm text-amber-800 dark:text-amber-300">
Try editing or reusing one of your existing projects, or wait
until tomorrow to create a new one.
</p>
</div>
</div>

<DialogFooter>
<Button onClick={onClose}>I understand</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
Comment on lines +21 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix unescaped apostrophe in JSX content

The JSX content contains an unescaped apostrophe that's causing a pipeline failure.

-            You've reached your daily project creation limit.
+            You&apos;ve reached your daily project creation limit.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const RateLimitModal = ({
isOpen,
onClose,
limit = 3,
}: RateLimitModalProps) => {
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<div className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-amber-500" />
<DialogTitle>Daily Limit Reached</DialogTitle>
</div>
<DialogDescription>
You've reached your daily project creation limit.
</DialogDescription>
</DialogHeader>
<div className="py-4">
<p className="mb-4">
Your current plan allows creating up to {limit} projects per day.
This limit resets at midnight UTC.
</p>
<div className="bg-amber-50 dark:bg-amber-950/30 p-3 rounded-md border border-amber-200 dark:border-amber-800">
<p className="text-sm text-amber-800 dark:text-amber-300">
Try editing or reusing one of your existing projects, or wait
until tomorrow to create a new one.
</p>
</div>
</div>
<DialogFooter>
<Button onClick={onClose}>I understand</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
export const RateLimitModal = ({
isOpen,
onClose,
limit = 3,
}: RateLimitModalProps) => {
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<div className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-amber-500" />
<DialogTitle>Daily Limit Reached</DialogTitle>
</div>
<DialogDescription>
You&apos;ve reached your daily project creation limit.
</DialogDescription>
</DialogHeader>
<div className="py-4">
<p className="mb-4">
Your current plan allows creating up to {limit} projects per day.
This limit resets at midnight UTC.
</p>
<div className="bg-amber-50 dark:bg-amber-950/30 p-3 rounded-md border border-amber-200 dark:border-amber-800">
<p className="text-sm text-amber-800 dark:text-amber-300">
Try editing or reusing one of your existing projects, or wait
until tomorrow to create a new one.
</p>
</div>
</div>
<DialogFooter>
<Button onClick={onClose}>I understand</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
🧰 Tools
🪛 GitHub Check: autofix

[failure] 35-35:
' can be escaped with &apos;, &lsquo;, &#39;, &rsquo;

🪛 GitHub Actions: autofix.ci

[error] 35-35: 'can be escaped with', , ', ’` react/no-unescaped-entities

Loading