Spaces:
Running
Running
File size: 4,282 Bytes
8af739b 822cbdd 8af739b 80540c8 8af739b 80540c8 8af739b 80540c8 8af739b 75d362b 8af739b 80540c8 8af739b 80540c8 8af739b 80540c8 8af739b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
import { NextRequest, NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'
// Use /data for Hugging Face persistent storage, fallback to local for development
const DATA_DIR = process.env.NODE_ENV === 'production' && fs.existsSync('/data')
? '/data'
: path.join(process.cwd(), 'data')
const DOCS_DIR = path.join(DATA_DIR, 'documents')
export async function GET(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams
const filePath = searchParams.get('path')
const preview = searchParams.get('preview') === 'true'
if (!filePath) {
return NextResponse.json({ error: 'File path required' }, { status: 400 })
}
// Normalize the path to handle both forward and backward slashes
const normalizedPath = filePath.replace(/\\/g, '/')
const fullPath = path.resolve(path.join(DOCS_DIR, normalizedPath))
const resolvedDocsDir = path.resolve(DOCS_DIR)
// Security check - ensure the resolved path is within the allowed directory
if (!fullPath.startsWith(resolvedDocsDir)) {
return NextResponse.json({ error: 'Invalid path - access denied' }, { status: 400 })
}
if (!fs.existsSync(fullPath)) {
return NextResponse.json({ error: 'File not found' }, { status: 404 })
}
const stats = fs.statSync(fullPath)
if (stats.isDirectory()) {
return NextResponse.json({ error: 'Cannot download directory' }, { status: 400 })
}
const fileBuffer = fs.readFileSync(fullPath)
const fileName = path.basename(normalizedPath)
const ext = path.extname(fileName).toLowerCase()
// Determine content type
let contentType = 'application/octet-stream'
const mimeTypes: Record<string, string> = {
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.xls': 'application/vnd.ms-excel',
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'.ppt': 'application/vnd.ms-powerpoint',
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'.txt': 'text/plain',
'.md': 'text/markdown',
'.json': 'application/json',
'.html': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript',
'.ts': 'text/typescript',
'.py': 'text/x-python',
'.java': 'text/x-java',
'.cpp': 'text/x-c++',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.mp3': 'audio/mpeg',
'.mp4': 'video/mp4',
'.zip': 'application/zip',
'.rar': 'application/x-rar-compressed',
'.tex': 'text/x-tex',
'.latex': 'text/x-latex',
'.dart': 'text/x-dart',
'.flutter': 'text/x-dart',
'.yaml': 'text/yaml',
'.yml': 'text/yaml',
'.xml': 'text/xml',
'.csv': 'text/csv',
'.rtf': 'application/rtf',
'.odt': 'application/vnd.oasis.opendocument.text',
'.ods': 'application/vnd.oasis.opendocument.spreadsheet',
'.odp': 'application/vnd.oasis.opendocument.presentation'
}
if (mimeTypes[ext]) {
contentType = mimeTypes[ext]
}
const headers = new Headers({
'Content-Type': contentType,
'Content-Length': fileBuffer.length.toString(),
})
// If not preview mode, add download header
if (!preview) {
headers.set('Content-Disposition', `attachment; filename="${encodeURIComponent(fileName)}"`)
} else {
// For preview, use inline disposition for supported types
if (['application/pdf', 'text/plain', 'text/markdown', 'application/json'].includes(contentType) ||
contentType.startsWith('image/') || contentType.startsWith('text/')) {
headers.set('Content-Disposition', `inline; filename="${encodeURIComponent(fileName)}"`)
} else {
headers.set('Content-Disposition', `attachment; filename="${encodeURIComponent(fileName)}"`)
}
}
return new NextResponse(fileBuffer, { headers })
} catch (error) {
console.error('Error downloading file:', error)
return NextResponse.json(
{ error: 'Failed to download file' },
{ status: 500 }
)
}
} |