Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('FrontendCodeHandler', () => {
name: 'Spotify-like Music Web',
description: 'Users can play music',
databaseType: 'SQLite',
model: 'o3-mini-high',
model: 'o4-mini',
nodes: [
{
handler: FrontendCodeHandler,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/build-system/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class BuilderContext {
this.globalContext.set('projectSize', 'small');
break;
case 'gpt-4o':
case 'o3-mini-high':
case 'o4-mini':
this.globalContext.set('projectSize', 'medium');
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export class FrontendQueueProcessor {
let fixResponse = await chatSyncWithClocker(
this.context,
{
model: 'o3-mini-high',
model: 'o4-mini',
messages: [
{ role: 'system', content: fixPrompt },
{
Expand Down Expand Up @@ -270,7 +270,7 @@ export class FrontendQueueProcessor {
fixResponse = await chatSyncWithClocker(
this.context,
{
model: 'o3-mini-high',
model: 'o4-mini',
messages: [
{ role: 'system', content: fixPrompt },
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
`Layer #${layerIndex + 1}, generating code for file: ${file}`,
);

const currentFullFilePath = normalizePath(

Check warning on line 135 in backend/src/build-system/handlers/frontend-code-generate/index.ts

View workflow job for this annotation

GitHub Actions / autofix

'currentFullFilePath' is assigned a value but never used. Allowed unused vars must match /^_/u
path.resolve(frontendPath, file),
);

Expand Down Expand Up @@ -366,8 +366,8 @@
context,
{
model: isSPAFlag
? 'claude-3.7-sonnet' // Use Claude for SPAs
: 'o3-mini-high', // Use default or fallback for non-SPAs
? 'gpt-4o-mini' // Use Claude for SPAs
: 'o4-mini', // Use default or fallback for non-SPAs
messages,
},
'generate frontend code',
Expand Down
2 changes: 1 addition & 1 deletion backend/src/build-system/handlers/ux/uiux-layout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class UIUXLayoutHandler implements BuildHandler<string> {
context,
{
// model: context.defaultModel || 'gpt-4o-mini',
model: 'claude-3.7-sonnet',
model: 'gpt-4o-mini',
messages,
},
'generateUIUXLayout',
Expand Down
3 changes: 3 additions & 0 deletions backend/src/project/dto/project.input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ export class FetchPublicProjectsInputs {

@Field()
size: number;

@Field()
currentUserId: string;
}

@InputType()
Expand Down
6 changes: 5 additions & 1 deletion backend/src/project/project.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,14 @@ export class ProjectsResolver {
const { buffer, mimetype } = await validateAndBufferFile(file);

// Call the service with the extracted buffer and mimetype
return this.projectService.updateProjectPhotoUrl(
const project1 = await this.projectService.updateProjectPhotoUrl(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove all test code in pr like this

userId,
projectId,
buffer,
mimetype,
);
this.logger.debug('project1', project1.photoUrl);
return project1;
}

@Mutation(() => Project)
Expand Down Expand Up @@ -152,8 +154,10 @@ export class ProjectsResolver {
*/
@Query(() => [Project])
async fetchPublicProjects(
@GetUserIdFromToken() userId: string,
@Args('input') input: FetchPublicProjectsInputs,
): Promise<Project[]> {
input.currentUserId = userId;
return this.projectService.fetchPublicProjects(input);
}

Expand Down
9 changes: 4 additions & 5 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 { Between, In, IsNull, Not, Repository } from 'typeorm';
import { Between, In, Not, Repository } from 'typeorm';
import { Project } from './project.model';
import { ProjectPackages } from './project-packages.model';
import {
Expand Down Expand Up @@ -644,9 +644,8 @@ export class ProjectService {
const limit = input.size > 50 ? 50 : input.size;

const whereCondition = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

add back photoUrl: Not(IsNull()) and is public check

isPublic: true,
isDeleted: false,
photoUrl: Not(IsNull()),
userId: Not(input.currentUserId), // Exclude current user's projects
};

if (input.strategy === 'latest') {
Expand Down Expand Up @@ -808,7 +807,7 @@ export class ProjectService {
this.logger.log(
'check if the github project exist: ' + project.isSyncedWithGitHub,
);
// 2) Check users GitHub installation
// 2) Check user's GitHub installation
if (!user.githubInstallationId) {
throw new Error('GitHub App not installed for this user');
}
Expand All @@ -819,7 +818,7 @@ export class ProjectService {
);
const userOAuthToken = user.githubAccessToken;

// 4) Create the repo if the project doesnt have it yet
// 4) Create the repo if the project doesn't have it yet
if (!project.githubRepoName || !project.githubOwner) {
// Use project.projectName or generate a safe name

Expand Down
2 changes: 1 addition & 1 deletion backend/src/user/user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class User extends SystemBaseModel {
googleId: string;

@Field()
@Column()
@Column({ unique: true })
username: string;

@Column({ nullable: true }) // Made nullable for OAuth users
Expand Down
66 changes: 66 additions & 0 deletions backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
BadRequestException,
ConflictException,
Injectable,
NotFoundException,
} from '@nestjs/common';
Expand Down Expand Up @@ -107,4 +108,69 @@ export class UserService {

return true;
}

/**
* Checks if a username already exists in the database
* @param username Username to check
* @param excludeUserId Optional user ID to exclude from the check (for updates)
* @returns Boolean indicating if the username exists
*/
async isUsernameExists(
username: string,
excludeUserId?: string,
): Promise<boolean> {
const query = this.userRepository
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks like unuse code please remove if is really unuse

.createQueryBuilder('user')
.where('LOWER(user.username) = LOWER(:username)', {
username: username.toLowerCase(),
});

if (excludeUserId) {
query.andWhere('user.id != :userId', { userId: excludeUserId });
}

const count = await query.getCount();
return count > 0;
}

/**
* Updates a user's username with uniqueness validation
* @param userId User ID
* @param newUsername New username to set
* @returns Updated user object
*/
async updateUsername(userId: string, newUsername: string): Promise<User> {
if (!newUsername || newUsername.trim().length < 3) {
throw new BadRequestException(
'Username must be at least 3 characters long',
);
}

// Check if the username is already taken
const exists = await this.isUsernameExists(newUsername, userId);
if (exists) {
throw new ConflictException(
`Username '${newUsername}' is already taken. Please choose another one.`,
);
}

const user = await this.userRepository.findOneBy({ id: userId });
if (!user) {
throw new NotFoundException('User not found');
}

user.username = newUsername;

try {
return await this.userRepository.save(user);
} catch (error) {
// Check for unique constraint error (just in case of race condition)
if (error.code === '23505' || error.message.includes('duplicate')) {
throw new ConflictException(
`Username '${newUsername}' is already taken. Please choose another one.`,
);
}
throw error;
}
}
}
12 changes: 7 additions & 5 deletions docker/project-base-image/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ WORKDIR /app
# Pre-install common frontend dependencies to speed up project startup
RUN npm install -g npm@latest vite@latest

# Create a non-root user to run the app
RUN groupadd -r appuser && useradd -r -g appuser -m appuser
RUN chown -R appuser:appuser /app
#TODO: Uncomment this when we have a non-root usr (Allen)
# #Create a non-root user to run the app
# RUN groupadd -r appuser && useradd -r -g appuser -m appuser
# RUN chown -R appuser:appuser /app
# RUN chmod -R u+w /app

# Switch to non-root user for security
USER appuser
# # Switch to non-root user for security
# USER appuser

EXPOSE 5173

Expand Down
21 changes: 18 additions & 3 deletions frontend/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SignUpModal } from '@/components/sign-up-modal';
import { useRouter } from 'next/navigation';
import { logger } from '../log/logger';
import { AuroraText } from '@/components/magicui/aurora-text';

export default function HomePage() {
// States for AuthChoiceModal
const [showAuthChoice, setShowAuthChoice] = useState(false);
Expand All @@ -22,9 +23,10 @@ export default function HomePage() {

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

const handleSubmit = async () => {
const handleSubmit = async (): Promise<string> => {
if (!promptFormRef.current) return;

const { message, isPublic, model } = promptFormRef.current.getPromptData();
Expand All @@ -34,12 +36,25 @@ export default function HomePage() {
const chatId = await createProjectFromPrompt(message, isPublic, model);

promptFormRef.current.clearMessage();
router.push(`/chat?id=${chatId}`);
if (chatId) {
setRecentlyCompletedProjectId(chatId);
return chatId;
}
} catch (error) {
logger.error('Error creating project:', error);
}
};
// useEffect(() => {
// if (!chatId) return;

// const interval = setInterval(() => {
// pollChatProject(chatId).catch((error) => {
// logger.error('Polling error in HomePage:', error);
// });
// }, 6000);

// return () => clearInterval(interval);
// }, [chatId, pollChatProject]);
return (
<div className="min-h-screen pt-16 pb-24 px-6 flex flex-col items-center justify-center relative overflow-hidden">
<div className="fixed inset-0 -z-20">
Expand Down
Loading
Loading