import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { getObjectContent } from '@/lib/s3'; // Error types for better error handling enum StorageError { UNAUTHORIZED = 'UNAUTHORIZED', FORBIDDEN = 'FORBIDDEN', NOT_FOUND = 'NOT_FOUND', VALIDATION_ERROR = 'VALIDATION_ERROR', S3_ERROR = 'S3_ERROR', INTERNAL_ERROR = 'INTERNAL_ERROR' } // Helper function to create error response function createErrorResponse( error: StorageError, message: string, status: number, details?: any ) { return NextResponse.json( { error: error, message: message, ...(details && { details }) }, { status } ); } // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { console.error('Unauthorized access attempt:', { url: request.url, method: request.method, headers: Object.fromEntries(request.headers) }); return { authorized: false, userId: null }; } return { authorized: true, userId: session.user.id }; } export async function GET(request: Request) { try { const { authorized, userId } = await checkAuth(request); if (!authorized || !userId) { return createErrorResponse( StorageError.UNAUTHORIZED, 'Authentication required', 401 ); } const { searchParams } = new URL(request.url); const path = searchParams.get('path'); const id = searchParams.get('id'); if (!path && !id) { return createErrorResponse( StorageError.VALIDATION_ERROR, 'Path or ID parameter is required', 400, { missing: 'path or id' } ); } // Determine the key to use let key: string; if (id) { // If id is provided directly, use it as the key key = id; // Ensure the user can only access their own files if (!key.startsWith(`user-${userId}/`)) { console.error('Unauthorized file access attempt:', { userId, fileId: id }); return createErrorResponse( StorageError.FORBIDDEN, 'Unauthorized access to file', 403, { fileId: id } ); } } else if (path) { // If a path is provided, ensure it contains the user's ID if (!path.includes(`/files/cube-${userId}/`) && !path.includes(`user-${userId}/`)) { // For backward compatibility, convert NextCloud path to S3 path if (path.startsWith('/files/') || path.includes('/Private/')) { // Extract folder and filename from path const parts = path.split('/').filter(Boolean); const file = parts[parts.length - 1]; let folder = 'notes'; // Default folder // Try to determine folder from path if (path.includes('/Notes/')) folder = 'notes'; else if (path.includes('/Diary/')) folder = 'diary'; else if (path.includes('/Contacts/')) folder = 'contacts'; else if (path.includes('/Health/')) folder = 'health'; // Use direct user path without pages prefix key = `user-${userId}/${folder}/${file}`; console.log('Converted NextCloud path to S3 key:', { path, key }); } else { console.error('Unauthorized file access attempt:', { userId, filePath: path }); return createErrorResponse( StorageError.FORBIDDEN, 'Unauthorized access to file', 403, { filePath: path } ); } } else { // If it already contains user ID, use the path directly key = path; } } else { return createErrorResponse( StorageError.VALIDATION_ERROR, 'Invalid parameters', 400 ); } // Validate key format (prevent path traversal) if (key.includes('..') || key.includes('//')) { return createErrorResponse( StorageError.VALIDATION_ERROR, 'Invalid file path', 400, { key } ); } console.log('Fetching file content from S3:', { key }); try { // Get the file content const content = await getObjectContent(key); if (!content) { return createErrorResponse( StorageError.NOT_FOUND, 'File not found', 404, { key } ); } return NextResponse.json({ content }); } catch (s3Error) { console.error('S3 error fetching file content:', s3Error); // Check if file doesn't exist if (s3Error instanceof Error && s3Error.message.includes('NoSuchKey')) { return createErrorResponse( StorageError.NOT_FOUND, 'File not found', 404, { key } ); } return createErrorResponse( StorageError.S3_ERROR, 'Failed to fetch file content from storage', 503, { error: s3Error instanceof Error ? s3Error.message : String(s3Error), key } ); } } catch (error) { console.error('Error fetching file content:', error); return createErrorResponse( StorageError.INTERNAL_ERROR, 'An unexpected error occurred', 500, { error: error instanceof Error ? error.message : String(error) } ); } }