summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-08-11 16:16:01 +0100
committerdiogo464 <[email protected]>2025-08-11 16:16:01 +0100
commit68afafc281103c32b193d5f116d87f74187bdc63 (patch)
treeeb69225e0e9d0f9edc0475933669a1dee2a3eb31
parent8bba15d57502e97b1b96590bd21e0e8f541eaac5 (diff)
add delete api endpoint with authentication
- Added Drive_remove() function to drive_server.ts using fctdrive remove command - Created /api/delete route with POST method, JSON body input, and authentication - Added development mode AUTH header bypass for testing authenticated endpoints - Updated CLAUDE.md with API testing instructions using AUTH: 1 header - Endpoint validates user authentication and upload permissions before deletion - Returns success response even for non-existent files (CLI ignores them) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
-rw-r--r--CLAUDE.md6
-rw-r--r--frontend/app/api/delete/route.ts48
-rw-r--r--frontend/lib/auth.ts16
-rw-r--r--frontend/lib/drive_server.ts11
4 files changed, 80 insertions, 1 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index f836602..3468c48 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -44,4 +44,8 @@
44- **Mobile**: Must be mobile-friendly 44- **Mobile**: Must be mobile-friendly
45- **Architecture**: Server-side rendering, modular reusable components 45- **Architecture**: Server-side rendering, modular reusable components
46- **Updates**: Manual refresh only (no real-time) 46- **Updates**: Manual refresh only (no real-time)
47- the server is running at `127.0.0.1:3000` and you can use curl to test the page \ No newline at end of file 47- the server is running at `127.0.0.1:3000` and you can use curl to test the page
48
49## API Testing
50- For testing authenticated endpoints in development, use header `AUTH: 1`
51- Example: `curl -H "AUTH: 1" -H "Content-Type: application/json" -X POST localhost:3000/api/delete -d '{"path":"/file.txt"}'` \ No newline at end of file
diff --git a/frontend/app/api/delete/route.ts b/frontend/app/api/delete/route.ts
new file mode 100644
index 0000000..b4a27d4
--- /dev/null
+++ b/frontend/app/api/delete/route.ts
@@ -0,0 +1,48 @@
1import { NextRequest, NextResponse } from 'next/server'
2import { Auth_get_user, Auth_user_can_upload } from '@/lib/auth'
3import { Drive_remove } from '@/lib/drive_server'
4import { revalidatePath } from 'next/cache'
5
6export async function POST(request: NextRequest) {
7 try {
8 // Check user authentication and permissions
9 const user = await Auth_get_user()
10 if (!user.isLoggedIn) {
11 return NextResponse.json({ error: 'User not authenticated' }, { status: 401 })
12 }
13
14 if (!Auth_user_can_upload(user)) {
15 return NextResponse.json({ error: 'User does not have upload permissions' }, { status: 403 })
16 }
17
18 // Parse JSON body
19 const body = await request.json()
20 const path = body.path
21
22 // Validate path
23 if (!path || typeof path !== 'string') {
24 return NextResponse.json({ error: 'Path is required and must be a string' }, { status: 400 })
25 }
26
27 // Remove file/directory using Drive_remove
28 await Drive_remove(path, user.email)
29
30 // Revalidate the parent directory to refresh listings
31 const parentPath = path.split('/').slice(0, -1).join('/') || '/'
32 revalidatePath(`/drive${parentPath}`)
33 revalidatePath('/drive')
34
35 return NextResponse.json({
36 success: true,
37 message: 'Path deleted successfully',
38 deletedPath: path
39 })
40
41 } catch (error) {
42 console.error('Delete error:', error)
43 return NextResponse.json(
44 { error: error instanceof Error ? error.message : 'Internal server error' },
45 { status: 500 }
46 )
47 }
48} \ No newline at end of file
diff --git a/frontend/lib/auth.ts b/frontend/lib/auth.ts
index fe00b11..015fddf 100644
--- a/frontend/lib/auth.ts
+++ b/frontend/lib/auth.ts
@@ -30,6 +30,22 @@ export async function Auth_extract_session_cookie(): Promise<UserSessionCookie |
30} 30}
31 31
32export async function Auth_get_user(): Promise<UserAuth> { 32export async function Auth_get_user(): Promise<UserAuth> {
33 // Development mode bypass for testing with AUTH header
34 if (Env_is_development()) {
35 const { headers } = await import('next/headers');
36 const headersList = await headers();
37 if (headersList.get('AUTH') === '1') {
38 return {
39 isLoggedIn: true,
40 username: 'testuser',
41 name: 'Test User',
42 email: '[email protected]',
43 provider: 'dev',
44 oauth: false
45 };
46 }
47 }
48
33 const cookie = await Auth_extract_session_cookie(); 49 const cookie = await Auth_extract_session_cookie();
34 const endpoint = Auth_tinyauth_endpoint(); 50 const endpoint = Auth_tinyauth_endpoint();
35 51
diff --git a/frontend/lib/drive_server.ts b/frontend/lib/drive_server.ts
index 91c3cb4..eba2d5a 100644
--- a/frontend/lib/drive_server.ts
+++ b/frontend/lib/drive_server.ts
@@ -72,4 +72,15 @@ export async function Drive_blob_path(blob: string): Promise<string> {
72 throw new Error(`fctdrive exited with code ${result.status}: ${result.stderr}`) 72 throw new Error(`fctdrive exited with code ${result.status}: ${result.stderr}`)
73 } 73 }
74 return result.stdout.trim(); 74 return result.stdout.trim();
75}
76
77/// removes the file or directory at the given path
78export async function Drive_remove(path: string, email: string) {
79 const result = spawnSync('fctdrive', ['remove', '--email', email, path], { encoding: 'utf-8' });
80 if (result.error) {
81 throw new Error(`Failed to execute fctdrive: ${result.error.message}`);
82 }
83 if (result.status !== 0) {
84 throw new Error(`fctdrive exited with code ${result.status}: ${result.stderr}`);
85 }
75} \ No newline at end of file 86} \ No newline at end of file