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
|
// app/api/vendors/attachments/download-temp/route.ts
import { NextRequest, NextResponse } from 'next/server';
import path from 'path';
import fs from 'fs';
import { promises as fsPromises } from 'fs';
import { cleanupTempFiles } from '@/lib/vendors/service';
export async function GET(request: NextRequest) {
try {
// 파일명 파라미터 추출
const searchParams = request.nextUrl.searchParams;
const fileName = searchParams.get('file');
if (!fileName) {
return NextResponse.json(
{ success: false, error: 'File name is required' },
{ status: 400 }
);
}
// 보안: 파일명에 경로 문자가 포함되어 있는지 확인 (경로 탐색 공격 방지)
if (fileName.includes('/') || fileName.includes('\\')) {
return NextResponse.json(
{ success: false, error: 'Invalid file name' },
{ status: 400 }
);
}
// 임시 디렉토리의 파일 경로 생성
const tempDir = path.join(process.cwd(), 'tmp');
const filePath = path.join(tempDir, fileName);
// 파일 존재 확인
try {
await fsPromises.access(filePath, fs.constants.F_OK);
} catch {
return NextResponse.json(
{ success: false, error: 'File not found' },
{ status: 404 }
);
}
// 파일 읽기
const fileBuffer = await fsPromises.readFile(filePath);
// 파일명에서 UUID 부분 제거하여 표시용 이름 생성
const uuidPattern = /-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.zip$/i;
const displayName = fileName.replace(uuidPattern, '.zip');
// 파일 응답 반환
const response = new NextResponse(fileBuffer, {
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${encodeURIComponent(displayName)}"`,
},
});
// 비동기적으로 파일 정리 요청 (별도 API 호출)
// Note: Next.js 환경에 따라 작동하지 않을 수 있음
try {
fetch(`${request.nextUrl.origin}/api/vendors/cleanup-temp-files?file=${encodeURIComponent(fileName)}`, {
method: 'POST',
}).catch(e => console.error('임시 파일 정리 요청 실패:', e));
} catch (e) {
console.error('파일 정리 요청 오류:', e);
}
return response;
} catch (error) {
console.error('임시 파일 다운로드 오류:', error);
return NextResponse.json(
{ success: false, error: 'Failed to download file' },
{ status: 500 }
);
}
}
// 임시 파일 정리 API 엔드포인트
// app/api/vendors/cleanup-temp-files/route.ts
export async function POST(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams;
const fileName = searchParams.get('file');
if (!fileName) {
return NextResponse.json({ success: false, error: 'File name is required' }, { status: 400 });
}
// 보안 검증
if (fileName.includes('/') || fileName.includes('\\')) {
return NextResponse.json({ success: false, error: 'Invalid file name' }, { status: 400 });
}
// 서버 액션 호출하여 파일 정리
await cleanupTempFiles(fileName);
return NextResponse.json({ success: true });
} catch (error) {
console.error('임시 파일 정리 API 오류:', error);
return NextResponse.json({ success: false, error: '임시 파일 정리 중 오류가 발생했습니다.' }, { status: 500 });
}
}
|