// components/file-manager/SharedFileViewer.tsx 'use client'; import { useState, useEffect } from 'react'; import { Download, Eye, EyeOff, FileText, Image, Film, Music, Archive, Code, File, Lock, AlertCircle, Calendar, Clock, User } from 'lucide-react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { useToast } from '@/hooks/use-toast'; import { cn } from '@/lib/utils'; interface SharedFile { id: string; name: string; type: 'file' | 'folder'; size: number; mimeType?: string; category: string; createdAt: string; updatedAt: string; } interface SharedFileViewerProps { token: string; } export function SharedFileViewer({ token }: SharedFileViewerProps) { const [file, setFile] = useState(null); const [accessLevel, setAccessLevel] = useState(''); const [passwordRequired, setPasswordRequired] = useState(false); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showContent, setShowContent] = useState(false); const [downloading, setDownloading] = useState(false); const { toast } = useToast(); useEffect(() => { // 초기 접근 시도 checkAccess(); }, [token]); const checkAccess = async (pwd?: string) => { setLoading(true); setError(null); try { const params = new URLSearchParams(); if (pwd) params.append('password', pwd); const response = await fetch(`/api/shared/${token}?${params}`); const data = await response.json(); if (!response.ok) { if (data.error?.includes('비밀번호')) { setPasswordRequired(true); setError('비밀번호가 필요합니다'); } else if (data.error?.includes('만료')) { setError('이 공유 링크는 만료되었습니다'); } else if (data.error?.includes('최대 다운로드')) { setError('최대 다운로드 횟수를 초과했습니다'); } else { setError(data.error || '파일에 접근할 수 없습니다'); } return; } setFile(data.file); setAccessLevel(data.accessLevel); setShowContent(true); setPasswordRequired(false); } catch (err) { setError('파일을 불러오는 중 오류가 발생했습니다'); } finally { setLoading(false); } }; const handlePasswordSubmit = (e: React.FormEvent) => { e.preventDefault(); checkAccess(password); }; const handleDownload = async () => { if (!file || accessLevel !== 'view_download') return; setDownloading(true); try { const response = await fetch(`/api/shared/${token}/download`, { method: 'POST', headers: password ? { 'X-Share-Password': password } : {}, }); if (!response.ok) { throw new Error('다운로드 실패'); } const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = file.name; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); toast({ title: '다운로드 완료', description: `${file.name} 파일이 다운로드되었습니다.`, }); } catch (error) { toast({ title: '다운로드 실패', description: '파일 다운로드 중 오류가 발생했습니다.', variant: 'destructive', }); } finally { setDownloading(false); } }; const getFileIcon = (mimeType?: string, name?: string) => { if (!mimeType && name) { const ext = name.split('.').pop()?.toLowerCase(); if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext || '')) return Image; if (['mp4', 'avi', 'mov', 'wmv'].includes(ext || '')) return Film; if (['mp3', 'wav', 'flac'].includes(ext || '')) return Music; if (['zip', 'rar', '7z', 'tar'].includes(ext || '')) return Archive; if (['js', 'ts', 'py', 'java', 'cpp'].includes(ext || '')) return Code; if (['pdf', 'doc', 'docx', 'txt'].includes(ext || '')) return FileText; } if (mimeType?.startsWith('image/')) return Image; if (mimeType?.startsWith('video/')) return Film; if (mimeType?.startsWith('audio/')) return Music; if (mimeType?.includes('zip') || mimeType?.includes('compressed')) return Archive; if (mimeType?.includes('pdf')) return FileText; return File; }; const formatFileSize = (bytes: number) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; if (loading) { return (

파일 정보를 불러오는 중...

); } if (error && !passwordRequired) { return (
접근할 수 없습니다
{error}
); } if (passwordRequired && !showContent) { return (
비밀번호 입력 이 파일은 비밀번호로 보호되어 있습니다
setPassword(e.target.value)} placeholder="비밀번호를 입력하세요" autoFocus />
{error && ( {error} )}
); } if (!file) return null; const FileIcon = getFileIcon(file.mimeType, file.name); return (
{/* 헤더 */}
FM

공유된 파일

File Manager Shared

{/* 메인 컨텐츠 */}
{file.name} {file.type === 'folder' ? '폴더' : formatFileSize(file.size)}
{accessLevel === 'view_only' && ( 보기 전용 )} {accessLevel === 'view_download' && ( 다운로드 가능 )}
{/* 파일 정보 */}

파일 유형

{file.mimeType || '알 수 없음'}

생성일

{new Date(file.createdAt).toLocaleDateString()}

카테고리

{file.category}

수정일

{new Date(file.updatedAt).toLocaleDateString()}

{/* 미리보기 영역 (이미지인 경우) */} {file.mimeType?.startsWith('image/') && accessLevel !== 'download_only' && (

미리보기

{file.name}
)} {/* 액션 버튼 */}
{accessLevel === 'view_download' && ( )} {accessLevel === 'view_only' && ( 이 파일은 보기 전용입니다. 다운로드할 수 없습니다. )}
{/* 보안 안내 */} 이 링크는 보안을 위해 제한된 시간 동안만 유효합니다. 필요한 경우 파일을 다운로드하여 보관하세요.
{/* 하단 정보 */}

© 2024 File Manager. All rights reserved.

문제가 있으신가요?{' '} 고객 지원

); }