Compare commits

...

7 Commits

5 changed files with 33 additions and 20 deletions

View File

@@ -50,20 +50,18 @@ export async function POST(request: Request) {
); );
// 3. Delete each message from Discord sequentially to avoid rate limits // 3. Delete each message from Discord sequentially to avoid rate limits
for (const part of partsRows[0] as FilePart[]) { for (const part of partsRows as FilePart[]) {
if (!part.discord_message_id) continue; if (!part.discord_message_id) continue;
try { try {
const res: Response = await rateLimiter.request( await rateLimiter.request(
`/channels/${process.env.DISCORD_CHANNEL_ID}/messages/${part.discord_message_id}`, `/channels/${process.env.DISCORD_CHANNEL_ID}/messages/${part.discord_message_id}`,
{ {
method: 'DELETE', method: 'DELETE',
} }
); );
// No need to check res.ok or res.status here, as rateLimiter.request throws on !response.ok
if (!res.ok && res.status !== 404) { // Don't warn on 404 (already deleted) // and returns null for 204.
console.warn(`Failed to delete message ${part.discord_message_id}. Status: ${res.status}`);
}
} catch (e) { } catch (e) {
console.error(`Error deleting message ${part.discord_message_id}:`, e); console.error(`Error deleting message ${part.discord_message_id}:`, e);
} }

View File

@@ -64,7 +64,7 @@ export async function GET(request: NextRequest, context: RouteContext) {
filename: file.filename, filename: file.filename,
size: file.size, size: file.size,
num_parts: file.num_parts, num_parts: file.num_parts,
parts: partsRows[0] as FilePartMetadata[], parts: partsRows as FilePartMetadata[],
}; };
return NextResponse.json(response); return NextResponse.json(response);

View File

@@ -10,6 +10,15 @@ if (!process.env.DISCORD_BOT_TOKEN || !process.env.DISCORD_CHANNEL_ID) {
const rateLimiter = new DiscordRateLimiter(DISCORD_API_BASE_URL, process.env.DISCORD_BOT_TOKEN); const rateLimiter = new DiscordRateLimiter(DISCORD_API_BASE_URL, process.env.DISCORD_BOT_TOKEN);
interface DiscordMessageResponse {
id: string;
attachments: Array<{
id: string;
url: string;
size: number;
}>;
}
export async function POST(request: Request) { export async function POST(request: Request) {
try { try {
const formData = await request.formData(); const formData = await request.formData();
@@ -25,21 +34,13 @@ export async function POST(request: Request) {
const discordFormData = new FormData(); const discordFormData = new FormData();
discordFormData.append('file', chunk, `chunk-${partIndex}.bin`); discordFormData.append('file', chunk, `chunk-${partIndex}.bin`);
const discordRes: Response = await rateLimiter.request( const discordData = await rateLimiter.request<DiscordMessageResponse>(
`/channels/${process.env.DISCORD_CHANNEL_ID}/messages`, `/channels/${process.env.DISCORD_CHANNEL_ID}/messages`,
{ {
method: 'POST', method: 'POST',
body: discordFormData, body: discordFormData,
} }
); );
if (!discordRes.ok) {
const errorBody = await discordRes.json();
console.error('Discord API Error:', errorBody);
return NextResponse.json({ error: 'Failed to upload to Discord' }, { status: 500 });
}
const discordData = await discordRes.json();
const attachment = discordData.attachments[0]; const attachment = discordData.attachments[0];
if (!attachment) { if (!attachment) {

View File

@@ -100,17 +100,28 @@ export default function DownloadPage() {
try { try {
const metaRes = await fetch(`/api/file/${file_id}?token=${token}`); const metaRes = await fetch(`/api/file/${file_id}?token=${token}`);
if (!metaRes.ok) { let metadata;
const err = await metaRes.json(); try {
throw new Error(err.error || 'Failed to fetch file metadata.'); metadata = await metaRes.json();
} catch (jsonError) {
console.error('Failed to parse metadata response as JSON:', jsonError);
throw new Error('Failed to parse file metadata.');
} }
const metadata = await metaRes.json();
if (!metaRes.ok) {
throw new Error(metadata.error || 'Failed to fetch file metadata.');
}
setFilename(metadata.filename); setFilename(metadata.filename);
setTotalBytes(metadata.size); setTotalBytes(metadata.size);
setDownloadState('downloading'); setDownloadState('downloading');
const encryptedParts = new Array(metadata.num_parts); const encryptedParts = new Array(metadata.num_parts);
if (!Array.isArray(metadata.parts)) {
throw new Error('Invalid metadata: parts is not an array.');
}
const downloadPromises = metadata.parts.map(async (part: MetadataPart) => { const downloadPromises = metadata.parts.map(async (part: MetadataPart) => {
const response = await fetchWithRetry(`/api/download-part/${file_id}/${part.part_index}`); const response = await fetchWithRetry(`/api/download-part/${file_id}/${part.part_index}`);
const buffer = await response.arrayBuffer(); const buffer = await response.arrayBuffer();

View File

@@ -45,6 +45,9 @@ export class DiscordRateLimiter {
throw new Error(`Discord API error: ${response.status} - ${errorData.message || JSON.stringify(errorData)}`); throw new Error(`Discord API error: ${response.status} - ${errorData.message || JSON.stringify(errorData)}`);
} }
if (response.status === 204) {
return null as T; // Return null for 204 No Content
}
return response.json() as Promise<T>; return response.json() as Promise<T>;
} }
} }