From c0dd8f5e58f7196657681a514b7071ca7d469c89 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 24 Jun 2026 11:09:17 +0000 Subject: [PATCH] Fix critical auth and build failure regressions Co-authored-by: Cole Collins --- app/api/build-app/route.ts | 42 ++++++++++++++---- app/api/code-completion/route.ts | 29 ++++++++---- .../[id]/milestones/[milestoneId]/route.ts | 9 ++-- lib/queries.ts | 44 +++++++++++++++---- 4 files changed, 95 insertions(+), 29 deletions(-) diff --git a/app/api/build-app/route.ts b/app/api/build-app/route.ts index 45c0852..b8ab81a 100644 --- a/app/api/build-app/route.ts +++ b/app/api/build-app/route.ts @@ -88,7 +88,7 @@ async function createGitHubRepo( body: JSON.stringify({ name: repoName, description, - private: false, + private: true, auto_init: false, }), }) @@ -128,7 +128,7 @@ async function pushFileToGitHub( if (!res.ok) { const err = (await res.json()) as { message?: string } - console.warn(`[build-app] Failed to push ${path}: ${err.message}`) + throw new Error(err.message ?? `Failed to push ${path}`) } } @@ -190,7 +190,7 @@ async function pushFileToGitLab( if (!res.ok) { const err = (await res.json()) as { message?: string } - console.warn(`[build-app] Failed to push ${path} to GitLab: ${err.message}`) + throw new Error(err.message ?? `Failed to push ${path} to GitLab`) } } @@ -285,14 +285,40 @@ export async function POST(request: NextRequest) { content = await generateSingleFile(blueprint, path, purpose, user.id) } catch (e) { console.warn(`[build-app] Failed to generate ${path}:`, e) - content = `# Error generating ${path}\n# ${e instanceof Error ? e.message : String(e)}\n` + send({ + step: 'error', + message: `Failed to generate ${path}: ${e instanceof Error ? e.message : String(e)}`, + repoUrl, + }) + return + } + + if (!content.trim()) { + send({ + step: 'error', + message: `Failed to generate ${path}: empty content returned.`, + repoUrl, + }) + return } // Push to platform - if (platform === 'github') { - await pushFileToGitHub(accessToken, user.github_username, cleanRepoName, path, content) - } else if (gitlabProjectId !== null) { - await pushFileToGitLab(accessToken, gitlabProjectId, gitlabBranch, path, content) + try { + if (platform === 'github') { + await pushFileToGitHub(accessToken, user.github_username, cleanRepoName, path, content) + } else if (gitlabProjectId !== null) { + await pushFileToGitLab(accessToken, gitlabProjectId, gitlabBranch, path, content) + } else { + throw new Error('GitLab project was not initialized.') + } + } catch (e) { + console.warn(`[build-app] Failed to push ${path}:`, e) + send({ + step: 'error', + message: `Failed to push ${path}: ${e instanceof Error ? e.message : String(e)}`, + repoUrl, + }) + return } pushed++ diff --git a/app/api/code-completion/route.ts b/app/api/code-completion/route.ts index f7ede13..910dfbd 100644 --- a/app/api/code-completion/route.ts +++ b/app/api/code-completion/route.ts @@ -4,6 +4,7 @@ import { CodeSnippet, batchGenerateCompletions, } from '@/lib/code-completion' +import { getCurrentUser } from '@/lib/auth' import { getDb } from '@/lib/db' const LANG_EXTENSIONS: Record = { @@ -24,18 +25,20 @@ const LANG_EXTENSIONS: Record = { shell: ['.sh', '.bash'], } -async function fetchSnippetsFromDb(language: string): Promise { +async function fetchSnippetsFromDb(language: string, userId: string): Promise { try { const sql = getDb() const extensions = LANG_EXTENSIONS[language.toLowerCase()] ?? [] if (extensions.length === 0) return [] const rows = await sql` - SELECT id, path, name, extension, ai_summary, reusability_score, exports, imports - FROM repo_files - WHERE extension = ANY(${extensions}::text[]) - AND ai_summary IS NOT NULL - ORDER BY reusability_score DESC + SELECT rf.id, rf.path, rf.name, rf.extension, rf.ai_summary, rf.reusability_score, rf.exports, rf.imports + FROM repo_files rf + INNER JOIN repositories r ON r.id = rf.repository_id + WHERE r.user_id = ${userId} + AND rf.extension = ANY(${extensions}::text[]) + AND rf.ai_summary IS NOT NULL + ORDER BY rf.reusability_score DESC LIMIT 20 ` @@ -63,6 +66,11 @@ async function fetchSnippetsFromDb(language: string): Promise { export async function POST(request: NextRequest) { try { + const user = await getCurrentUser() + if (!user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const { incompleteCode, codebaseSnippets = [], language = 'python' } = body @@ -75,7 +83,7 @@ export async function POST(request: NextRequest) { const snippets: CodeSnippet[] = codebaseSnippets.length > 0 ? codebaseSnippets - : await fetchSnippetsFromDb(language) + : await fetchSnippetsFromDb(language, user.id) const result = await generateRelevanceGuidedCompletion( incompleteCode, @@ -120,6 +128,11 @@ export async function POST(request: NextRequest) { */ export async function PUT(request: NextRequest) { try { + const user = await getCurrentUser() + if (!user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const { codeSnippets = [], codebaseSnippets = [], language = 'python' } = body @@ -132,7 +145,7 @@ export async function PUT(request: NextRequest) { const snippets: CodeSnippet[] = codebaseSnippets.length > 0 ? codebaseSnippets - : await fetchSnippetsFromDb(language) + : await fetchSnippetsFromDb(language, user.id) const results = await batchGenerateCompletions( codeSnippets, diff --git a/app/api/projects/[id]/milestones/[milestoneId]/route.ts b/app/api/projects/[id]/milestones/[milestoneId]/route.ts index 0b04cb1..faf92ff 100644 --- a/app/api/projects/[id]/milestones/[milestoneId]/route.ts +++ b/app/api/projects/[id]/milestones/[milestoneId]/route.ts @@ -9,10 +9,10 @@ export async function PATCH( const user = await getCurrentUser() if (!user?.id) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - const { milestoneId } = await params try { + const { id, milestoneId } = await params const { completed } = await request.json() - const milestone = await toggleMilestone(milestoneId, Boolean(completed)) + const milestone = await toggleMilestone(milestoneId, id, user.id, Boolean(completed)) if (!milestone) return NextResponse.json({ error: 'Not found' }, { status: 404 }) return NextResponse.json({ milestone }) } catch (err) { @@ -28,9 +28,10 @@ export async function DELETE( const user = await getCurrentUser() if (!user?.id) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - const { milestoneId } = await params try { - await deleteMilestone(milestoneId) + const { id, milestoneId } = await params + const deleted = await deleteMilestone(milestoneId, id, user.id) + if (!deleted) return NextResponse.json({ error: 'Not found' }, { status: 404 }) return NextResponse.json({ ok: true }) } catch (err) { console.error('[milestones/id] DELETE error:', err) diff --git a/lib/queries.ts b/lib/queries.ts index 7dd365e..23b8fea 100644 --- a/lib/queries.ts +++ b/lib/queries.ts @@ -1044,27 +1044,53 @@ export async function createMilestone(data: { return result[0] as ProjectMilestone } -export async function toggleMilestone(id: string, completed: boolean): Promise { +export async function toggleMilestone( + id: string, + projectId: string, + userId: string, + completed: boolean, +): Promise { const sql = getDb() const result = completed ? await sql` - UPDATE project_milestones + UPDATE project_milestones pm SET completed = true, completed_at = NOW() - WHERE id = ${id} - RETURNING * + FROM projects p + WHERE pm.id = ${id} + AND pm.project_id = ${projectId} + AND p.id = pm.project_id + AND p.user_id = ${userId} + RETURNING pm.* ` : await sql` - UPDATE project_milestones + UPDATE project_milestones pm SET completed = false, completed_at = NULL - WHERE id = ${id} - RETURNING * + FROM projects p + WHERE pm.id = ${id} + AND pm.project_id = ${projectId} + AND p.id = pm.project_id + AND p.user_id = ${userId} + RETURNING pm.* ` return (result[0] as ProjectMilestone) || null } -export async function deleteMilestone(id: string): Promise { +export async function deleteMilestone( + id: string, + projectId: string, + userId: string, +): Promise { const sql = getDb() - await sql`DELETE FROM project_milestones WHERE id = ${id}` + const result = await sql` + DELETE FROM project_milestones pm + USING projects p + WHERE pm.id = ${id} + AND pm.project_id = ${projectId} + AND p.id = pm.project_id + AND p.user_id = ${userId} + RETURNING pm.id + ` + return result.length > 0 } export async function seedDefaultMilestones(projectId: string): Promise {