added new zip download system
This commit is contained in:
@@ -2,12 +2,46 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||||||
import { downloadFilteredEntries } from '@/app/actions/download-filtered-entries';
|
import { downloadFilteredEntries } from '@/app/actions/download-filtered-entries';
|
||||||
import JSZip from 'jszip';
|
import JSZip from 'jszip';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { readFile, existsSync } from 'fs';
|
import { readFile, existsSync, writeFileSync, mkdirSync, readdirSync, unlinkSync, statSync } from 'fs';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { requireAdmin } from '@/lib/auth';
|
import { requireAdmin } from '@/lib/auth';
|
||||||
|
|
||||||
const readFileAsync = promisify(readFile);
|
const readFileAsync = promisify(readFile);
|
||||||
|
|
||||||
|
// Clean up old ZIP files older than 24 hours
|
||||||
|
async function cleanupOldZips(downloadsDir: string, maxAgeHours: number = 24) {
|
||||||
|
try {
|
||||||
|
if (!existsSync(downloadsDir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = readdirSync(downloadsDir);
|
||||||
|
const now = Date.now();
|
||||||
|
const maxAgeMs = maxAgeHours * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (!file.startsWith('dataset-') || !file.endsWith('.zip')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = join(downloadsDir, file);
|
||||||
|
try {
|
||||||
|
const stats = statSync(filePath);
|
||||||
|
const fileAge = now - stats.mtimeMs;
|
||||||
|
|
||||||
|
if (fileAge > maxAgeMs) {
|
||||||
|
unlinkSync(filePath);
|
||||||
|
console.log(`Cleaned up old ZIP file: ${file} (age: ${(fileAge / (60 * 60 * 1000)).toFixed(1)} hours)`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error cleaning up file ${file}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error during cleanup:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface FilterParams {
|
interface FilterParams {
|
||||||
speakerId?: string;
|
speakerId?: string;
|
||||||
modelName?: string;
|
modelName?: string;
|
||||||
@@ -84,16 +118,37 @@ export async function POST(request: NextRequest) {
|
|||||||
// Generate ZIP blob
|
// Generate ZIP blob
|
||||||
const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' });
|
const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' });
|
||||||
|
|
||||||
// Create response with ZIP file
|
// Save ZIP to public folder
|
||||||
const fileName = `dataset-${datasetId}-export-${new Date().toISOString().split('T')[0]}.zip`;
|
const downloadsDir = join(process.cwd(), 'public', 'downloads');
|
||||||
|
|
||||||
|
// Ensure downloads directory exists
|
||||||
|
try {
|
||||||
|
mkdirSync(downloadsDir, { recursive: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating downloads directory:', error);
|
||||||
|
}
|
||||||
|
|
||||||
return new NextResponse(new Uint8Array(zipBuffer), {
|
// Create filename with timestamp for uniqueness
|
||||||
status: 200,
|
const timestamp = Date.now();
|
||||||
headers: {
|
const fileName = `dataset-${datasetId}-export-${new Date().toISOString().split('T')[0]}-${timestamp}.zip`;
|
||||||
'Content-Type': 'application/zip',
|
const filePath = join(downloadsDir, fileName);
|
||||||
'Content-Disposition': `attachment; filename="${fileName}"`,
|
|
||||||
'Content-Length': zipBuffer.length.toString(),
|
// Write ZIP file to disk
|
||||||
},
|
writeFileSync(filePath, zipBuffer);
|
||||||
|
console.log(`ZIP file saved to: ${filePath}`);
|
||||||
|
|
||||||
|
// Clean up old ZIP files in the background
|
||||||
|
cleanupOldZips(downloadsDir).catch(error => {
|
||||||
|
console.error('Cleanup failed:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return download URL
|
||||||
|
const downloadUrl = `/downloads/${fileName}`;
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
downloadUrl,
|
||||||
|
fileName,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating ZIP:', error);
|
console.error('Error creating ZIP:', error);
|
||||||
|
|||||||
@@ -24,21 +24,17 @@ export async function downloadFilteredEntriesAsZip(
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
throw new Error(errorData.error || 'Failed to download ZIP');
|
throw new Error(errorData.error || 'Failed to prepare ZIP');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the ZIP blob from response
|
const data = await response.json();
|
||||||
const blob = await response.blob();
|
|
||||||
|
if (!data.success || !data.downloadUrl) {
|
||||||
|
throw new Error('Invalid response from server');
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger download
|
// Redirect to the generated ZIP file
|
||||||
const url = URL.createObjectURL(blob);
|
window.location.href = data.downloadUrl;
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = `dataset-${datasetId}-export-${new Date().toISOString().split('T')[0]}.zip`;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error downloading ZIP:', error);
|
console.error('Error downloading ZIP:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
Reference in New Issue
Block a user