feat: Implement file upload and management system
- Created database schema for files and file parts. - Added API endpoint to create a new file and generate an upload token. - Implemented API endpoint to complete file uploads and generate a download token. - Developed cron job for cleaning up expired files and deleting associated Discord messages. - Added API endpoint for soft deletion of files and their parts. - Implemented API endpoint to fetch file metadata and parts for download. - Created API endpoint to upload file parts to Discord. - Developed a client-side download page to handle file decryption and assembly. - Established database connection pooling for efficient database operations.
This commit is contained in:
59
src/app/api/cron/cleanup/route.ts
Normal file
59
src/app/api/cron/cleanup/route.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import pool from '@/lib/db';
|
||||
|
||||
if (!process.env.DISCORD_WEBHOOK_URL || !process.env.CRON_SECRET) {
|
||||
throw new Error('Please define DISCORD_WEBHOOK_URL and CRON_SECRET in .env.local');
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
// 1. Authenticate the cron job request
|
||||
const authHeader = request.headers.get('authorization');
|
||||
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const connection = await pool.getConnection();
|
||||
try {
|
||||
// 2. Find expired files
|
||||
const [expiredFiles]: any[] = await connection.query(
|
||||
'SELECT id FROM files WHERE expires_at <= NOW() AND deleted = 0'
|
||||
);
|
||||
|
||||
if (expiredFiles.length === 0) {
|
||||
return NextResponse.json({ success: true, message: 'No expired files to clean up.' });
|
||||
}
|
||||
|
||||
let totalCleaned = 0;
|
||||
|
||||
// 3. For each expired file, delete parts and mark as deleted
|
||||
for (const file of expiredFiles) {
|
||||
const file_id = file.id;
|
||||
|
||||
const [partsRows]: any[] = await connection.query(
|
||||
'SELECT discord_message_id FROM file_parts WHERE file_id = ?',
|
||||
[file_id]
|
||||
);
|
||||
|
||||
const deletePromises = partsRows.map(part => {
|
||||
const deleteUrl = `${process.env.DISCORD_WEBHOOK_URL}/messages/${part.discord_message_id}`;
|
||||
return fetch(deleteUrl, { method: 'DELETE' });
|
||||
});
|
||||
|
||||
await Promise.allSettled(deletePromises);
|
||||
|
||||
await connection.query('UPDATE files SET deleted = 1 WHERE id = ?', [file_id]);
|
||||
totalCleaned++;
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true, message: `Cleaned up ${totalCleaned} expired files.` });
|
||||
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during cron cleanup:', error);
|
||||
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user