Skip to content

Commit be35b60

Browse files
Fix recursive delete and add batch import endpoint
1 parent 08bfcfb commit be35b60

3 files changed

Lines changed: 94 additions & 5 deletions

File tree

app/api/files/batch/route.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { createServerClient } from '@/lib/supabase-server'
3+
4+
export const dynamic = 'force-dynamic'
5+
export const maxDuration = 300 // 5 minutes for large imports
6+
7+
interface BatchFileInput {
8+
path: string
9+
content: string
10+
isDirectory?: boolean
11+
}
12+
13+
export async function POST(request: NextRequest) {
14+
try {
15+
const body = await request.json()
16+
const { files } = body as { files: BatchFileInput[] }
17+
18+
if (!files || !Array.isArray(files)) {
19+
return NextResponse.json({ error: 'Files array is required' }, { status: 400 })
20+
}
21+
22+
if (files.length === 0) {
23+
return NextResponse.json({ error: 'Files array cannot be empty' }, { status: 400 })
24+
}
25+
26+
// Limit batch size to prevent abuse
27+
if (files.length > 1000) {
28+
return NextResponse.json({ error: 'Maximum 1000 files per batch' }, { status: 400 })
29+
}
30+
31+
const supabase = createServerClient()
32+
33+
// Get authenticated user from session
34+
const { data: { user }, error: authError } = await supabase.auth.getUser()
35+
36+
if (authError || !user) {
37+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
38+
}
39+
40+
// Prepare batch insert data
41+
const insertData = files.map(file => {
42+
const pathParts = file.path.split('/')
43+
const name = pathParts[pathParts.length - 1]
44+
const parentPath = pathParts.length > 1 ? pathParts.slice(0, -1).join('/') : null
45+
const sizeBytes = Buffer.byteLength(file.content || '', 'utf8')
46+
47+
return {
48+
user_id: user.id,
49+
path: file.path,
50+
name,
51+
content: file.content || '',
52+
is_directory: file.isDirectory || false,
53+
parent_path: parentPath,
54+
size_bytes: sizeBytes,
55+
}
56+
})
57+
58+
// Insert all files in a single batch operation
59+
const { data: insertedFiles, error: insertError } = await supabase
60+
.from('workspace_files')
61+
.insert(insertData)
62+
.select()
63+
64+
if (insertError) {
65+
console.error('Error batch inserting files:', insertError)
66+
67+
// Check if it's a duplicate key error
68+
if (insertError.code === '23505') {
69+
return NextResponse.json({
70+
error: 'Some files already exist. Delete them first or use update endpoint.',
71+
details: insertError.message
72+
}, { status: 409 })
73+
}
74+
75+
return NextResponse.json({
76+
error: 'Failed to import files',
77+
details: insertError.message
78+
}, { status: 500 })
79+
}
80+
81+
return NextResponse.json({
82+
success: true,
83+
imported: insertedFiles?.length || 0,
84+
files: insertedFiles
85+
})
86+
} catch (error) {
87+
console.error('Error in POST /api/files/batch:', error)
88+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
89+
}
90+
}

app/api/files/content/route.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,12 @@ export async function POST(request: NextRequest) {
6969
// Calculate content size
7070
const sizeBytes = Buffer.byteLength(content, 'utf8')
7171

72-
// Update the file content
72+
// Update the file content (updated_at handled by database trigger)
7373
const { data: file, error } = await supabase
7474
.from('workspace_files')
7575
.update({
7676
content,
77-
size_bytes: sizeBytes,
78-
updated_at: new Date().toISOString()
77+
size_bytes: sizeBytes
7978
})
8079
.eq('user_id', user.id)
8180
.eq('path', path)

app/api/files/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,12 @@ export async function DELETE(request: NextRequest) {
140140
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
141141
}
142142

143-
// Delete the file and all its children (for directories)
143+
// Use LIKE to match all nested paths that start with this path
144144
const { error } = await supabase
145145
.from('workspace_files')
146146
.delete()
147147
.eq('user_id', user.id)
148-
.or(`path.eq.${path},parent_path.eq.${path}`)
148+
.or(`path.eq.${path},path.like.${path}/%`)
149149

150150
if (error) {
151151
console.error('Error deleting workspace file:', error)

0 commit comments

Comments
 (0)