Skip to content

Commit 44811c4

Browse files
Add API routes for workspace file management
1 parent ac138fb commit 44811c4

2 files changed

Lines changed: 262 additions & 0 deletions

File tree

app/api/files/content/route.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { createClient } from '@supabase/supabase-js'
3+
4+
export const dynamic = 'force-dynamic'
5+
6+
export async function GET(request: NextRequest) {
7+
try {
8+
const sessionID = request.nextUrl.searchParams.get('sessionID')
9+
const path = request.nextUrl.searchParams.get('path')
10+
11+
if (!sessionID || !path) {
12+
return NextResponse.json({ error: 'Session ID and path are required' }, { status: 400 })
13+
}
14+
15+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
16+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY
17+
18+
if (!supabaseUrl || !supabaseKey) {
19+
return NextResponse.json({ error: 'Supabase configuration missing' }, { status: 500 })
20+
}
21+
22+
const supabase = createClient(supabaseUrl, supabaseKey)
23+
24+
// Fetch the file content
25+
const { data: file, error } = await supabase
26+
.from('workspace_files')
27+
.select('content, path, name, is_directory')
28+
.eq('user_id', sessionID)
29+
.eq('path', path)
30+
.single()
31+
32+
if (error) {
33+
console.error('Error fetching file content:', error)
34+
return NextResponse.json({ error: 'File not found' }, { status: 404 })
35+
}
36+
37+
if (file.is_directory) {
38+
return NextResponse.json({ error: 'Cannot read content of a directory' }, { status: 400 })
39+
}
40+
41+
return NextResponse.json({
42+
content: file.content,
43+
path: file.path,
44+
name: file.name
45+
})
46+
} catch (error) {
47+
console.error('Error in GET /api/files/content:', error)
48+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
49+
}
50+
}
51+
52+
export async function POST(request: NextRequest) {
53+
try {
54+
const body = await request.json()
55+
const { sessionID, path, content } = body
56+
57+
if (!sessionID || !path || content === undefined) {
58+
return NextResponse.json({ error: 'Session ID, path, and content are required' }, { status: 400 })
59+
}
60+
61+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
62+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY
63+
64+
if (!supabaseUrl || !supabaseKey) {
65+
return NextResponse.json({ error: 'Supabase configuration missing' }, { status: 500 })
66+
}
67+
68+
const supabase = createClient(supabaseUrl, supabaseKey)
69+
70+
// Calculate content size
71+
const sizeBytes = Buffer.byteLength(content, 'utf8')
72+
73+
// Update the file content
74+
const { data: file, error } = await supabase
75+
.from('workspace_files')
76+
.update({
77+
content,
78+
size_bytes: sizeBytes,
79+
updated_at: new Date().toISOString()
80+
})
81+
.eq('user_id', sessionID)
82+
.eq('path', path)
83+
.select()
84+
.single()
85+
86+
if (error) {
87+
console.error('Error updating file content:', error)
88+
return NextResponse.json({ error: 'Failed to update file' }, { status: 500 })
89+
}
90+
91+
return NextResponse.json({ success: true, file })
92+
} catch (error) {
93+
console.error('Error in POST /api/files/content:', error)
94+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
95+
}
96+
}

app/api/files/route.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { createClient } from '@supabase/supabase-js'
3+
4+
export const dynamic = 'force-dynamic'
5+
6+
// Helper function to build file tree from flat list
7+
function buildFileTree(files: any[]): any[] {
8+
const tree: any[] = []
9+
const pathMap = new Map<string, any>()
10+
11+
// Sort files by path to ensure parents are processed before children
12+
const sortedFiles = files.sort((a, b) => a.path.localeCompare(b.path))
13+
14+
for (const file of sortedFiles) {
15+
const node = {
16+
name: file.name,
17+
path: file.path,
18+
isDirectory: file.is_directory,
19+
children: file.is_directory ? [] : undefined,
20+
}
21+
22+
pathMap.set(file.path, node)
23+
24+
if (file.parent_path) {
25+
const parent = pathMap.get(file.parent_path)
26+
if (parent && parent.children) {
27+
parent.children.push(node)
28+
} else {
29+
tree.push(node)
30+
}
31+
} else {
32+
tree.push(node)
33+
}
34+
}
35+
36+
return tree
37+
}
38+
39+
export async function GET(request: NextRequest) {
40+
try {
41+
const sessionID = request.nextUrl.searchParams.get('sessionID')
42+
43+
if (!sessionID) {
44+
return NextResponse.json({ error: 'Session ID is required' }, { status: 400 })
45+
}
46+
47+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
48+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY
49+
50+
if (!supabaseUrl || !supabaseKey) {
51+
return NextResponse.json({ error: 'Supabase configuration missing' }, { status: 500 })
52+
}
53+
54+
const supabase = createClient(supabaseUrl, supabaseKey)
55+
56+
// Fetch all workspace files for the user
57+
const { data: files, error } = await supabase
58+
.from('workspace_files')
59+
.select('*')
60+
.eq('user_id', sessionID)
61+
.order('path', { ascending: true })
62+
63+
if (error) {
64+
console.error('Error fetching workspace files:', error)
65+
return NextResponse.json({ error: 'Failed to fetch files' }, { status: 500 })
66+
}
67+
68+
// Build file tree structure
69+
const fileTree = buildFileTree(files || [])
70+
71+
return NextResponse.json(fileTree)
72+
} catch (error) {
73+
console.error('Error in GET /api/files:', error)
74+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
75+
}
76+
}
77+
78+
export async function POST(request: NextRequest) {
79+
try {
80+
const body = await request.json()
81+
const { sessionID, path, isDirectory, content = '' } = body
82+
83+
if (!sessionID || !path) {
84+
return NextResponse.json({ error: 'Session ID and path are required' }, { status: 400 })
85+
}
86+
87+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
88+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY
89+
90+
if (!supabaseUrl || !supabaseKey) {
91+
return NextResponse.json({ error: 'Supabase configuration missing' }, { status: 500 })
92+
}
93+
94+
const supabase = createClient(supabaseUrl, supabaseKey)
95+
96+
// Extract file name and parent path
97+
const pathParts = path.split('/')
98+
const name = pathParts[pathParts.length - 1]
99+
const parentPath = pathParts.length > 1 ? pathParts.slice(0, -1).join('/') : null
100+
101+
// Calculate content size
102+
const sizeBytes = Buffer.byteLength(content, 'utf8')
103+
104+
// Insert the new file
105+
const { data: file, error } = await supabase
106+
.from('workspace_files')
107+
.insert({
108+
user_id: sessionID,
109+
path,
110+
name,
111+
content,
112+
is_directory: isDirectory,
113+
parent_path: parentPath,
114+
size_bytes: sizeBytes,
115+
})
116+
.select()
117+
.single()
118+
119+
if (error) {
120+
console.error('Error creating workspace file:', error)
121+
return NextResponse.json({ error: 'Failed to create file' }, { status: 500 })
122+
}
123+
124+
return NextResponse.json({ success: true, file })
125+
} catch (error) {
126+
console.error('Error in POST /api/files:', error)
127+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
128+
}
129+
}
130+
131+
export async function DELETE(request: NextRequest) {
132+
try {
133+
const body = await request.json()
134+
const { sessionID, path } = body
135+
136+
if (!sessionID || !path) {
137+
return NextResponse.json({ error: 'Session ID and path are required' }, { status: 400 })
138+
}
139+
140+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
141+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY
142+
143+
if (!supabaseUrl || !supabaseKey) {
144+
return NextResponse.json({ error: 'Supabase configuration missing' }, { status: 500 })
145+
}
146+
147+
const supabase = createClient(supabaseUrl, supabaseKey)
148+
149+
// Delete the file and all its children (for directories)
150+
const { error } = await supabase
151+
.from('workspace_files')
152+
.delete()
153+
.eq('user_id', sessionID)
154+
.or(`path.eq.${path},parent_path.eq.${path}`)
155+
156+
if (error) {
157+
console.error('Error deleting workspace file:', error)
158+
return NextResponse.json({ error: 'Failed to delete file' }, { status: 500 })
159+
}
160+
161+
return NextResponse.json({ success: true })
162+
} catch (error) {
163+
console.error('Error in DELETE /api/files:', error)
164+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
165+
}
166+
}

0 commit comments

Comments
 (0)