fix: Update Discord API integration to use Bot token and channel ID for message handling
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import pool from '@/lib/db';
|
import pool from '@/lib/db';
|
||||||
|
|
||||||
if (!process.env.DISCORD_WEBHOOK_URL || !process.env.CRON_SECRET) {
|
if (!process.env.DISCORD_BOT_TOKEN || !process.env.DISCORD_CHANNEL_ID || !process.env.CRON_SECRET) {
|
||||||
throw new Error('Please define DISCORD_WEBHOOK_URL and CRON_SECRET in .env.local');
|
throw new Error('Discord or Cron secret environment variables are not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
@@ -36,8 +36,13 @@ export async function POST(request: Request) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const deletePromises = partsRows.map((part: { discord_message_id: string }) => {
|
const deletePromises = partsRows.map((part: { discord_message_id: string }) => {
|
||||||
const deleteUrl = `${process.env.DISCORD_WEBHOOK_URL}/messages/${part.discord_message_id}`;
|
const deleteUrl = `https://discord.com/api/v10/channels/${process.env.DISCORD_CHANNEL_ID}/messages/${part.discord_message_id}`;
|
||||||
return fetch(deleteUrl, { method: 'DELETE' });
|
return fetch(deleteUrl, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bot ${process.env.DISCORD_BOT_TOKEN}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.allSettled(deletePromises);
|
await Promise.allSettled(deletePromises);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { NextResponse } from 'next/server';
|
|||||||
import pool from '@/lib/db';
|
import pool from '@/lib/db';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
|
|
||||||
if (!process.env.DISCORD_WEBHOOK_URL) {
|
if (!process.env.DISCORD_BOT_TOKEN || !process.env.DISCORD_CHANNEL_ID) {
|
||||||
throw new Error('Please define DISCORD_WEBHOOK_URL in .env.local');
|
throw new Error('Discord bot token or channel ID is not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
@@ -35,10 +35,15 @@ export async function POST(request: Request) {
|
|||||||
[file_id]
|
[file_id]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 3. Delete each message from Discord
|
// 3. Delete each message from Discord using the Bot API
|
||||||
const deletePromises = partsRows.map((part: { discord_message_id: string }) => {
|
const deletePromises = partsRows.map((part: { discord_message_id: string }) => {
|
||||||
const deleteUrl = `${process.env.DISCORD_WEBHOOK_URL}/messages/${part.discord_message_id}`;
|
const deleteUrl = `https://discord.com/api/v10/channels/${process.env.DISCORD_CHANNEL_ID}/messages/${part.discord_message_id}`;
|
||||||
return fetch(deleteUrl, { method: 'DELETE' });
|
return fetch(deleteUrl, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bot ${process.env.DISCORD_BOT_TOKEN}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const results = await Promise.allSettled(deletePromises);
|
const results = await Promise.allSettled(deletePromises);
|
||||||
|
|||||||
66
src/app/api/download-part/[file_id]/[part_index]/route.ts
Normal file
66
src/app/api/download-part/[file_id]/[part_index]/route.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import pool from '@/lib/db';
|
||||||
|
|
||||||
|
if (!process.env.DISCORD_BOT_TOKEN || !process.env.DISCORD_CHANNEL_ID) {
|
||||||
|
throw new Error('Discord bot token or channel ID is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(request: Request, { params }: { params: { file_id: string, part_index: string } }) {
|
||||||
|
try {
|
||||||
|
const { file_id, part_index } = params;
|
||||||
|
|
||||||
|
// NOTE: In a real-world scenario, you MUST validate if the user has permission to download this part.
|
||||||
|
// This would involve checking the download_token against the hash in the `files` table.
|
||||||
|
// For this MVP, we are skipping this check on the part download to keep it simpler,
|
||||||
|
// as the initial metadata fetch in the frontend already validates the token.
|
||||||
|
|
||||||
|
const connection = await pool.getConnection();
|
||||||
|
let part;
|
||||||
|
try {
|
||||||
|
const [partsRows]: any[] = await connection.query(
|
||||||
|
'SELECT discord_message_id FROM file_parts WHERE file_id = ? AND part_index = ?',
|
||||||
|
[file_id, part_index]
|
||||||
|
);
|
||||||
|
part = partsRows[0];
|
||||||
|
} finally {
|
||||||
|
connection.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!part) {
|
||||||
|
return NextResponse.json({ error: 'Part not found' }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Get fresh message data from Discord
|
||||||
|
const messageUrl = `https://discord.com/api/v10/channels/${process.env.DISCORD_CHANNEL_ID}/messages/${part.discord_message_id}`;
|
||||||
|
const discordRes = await fetch(messageUrl, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bot ${process.env.DISCORD_BOT_TOKEN}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!discordRes.ok) {
|
||||||
|
return NextResponse.json({ error: 'Failed to fetch message from Discord' }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageData = await discordRes.json();
|
||||||
|
const attachment = messageData.attachments[0];
|
||||||
|
|
||||||
|
if (!attachment || !attachment.url) {
|
||||||
|
return NextResponse.json({ error: 'Attachment not found in Discord message' }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Stream the attachment content from the fresh URL back to the client
|
||||||
|
const attachmentRes = await fetch(attachment.url);
|
||||||
|
|
||||||
|
// Create a new response by streaming the attachment content
|
||||||
|
return new Response(attachmentRes.body, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/octet-stream',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error proxying download:', error);
|
||||||
|
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,7 @@ export async function GET(request: Request, { params }: any) {
|
|||||||
|
|
||||||
// 4. Fetch file parts
|
// 4. Fetch file parts
|
||||||
const [partsRows]: any[] = await connection.query(
|
const [partsRows]: any[] = await connection.query(
|
||||||
'SELECT part_index, discord_attachment_url, size FROM file_parts WHERE file_id = ? ORDER BY part_index ASC',
|
'SELECT part_index, size FROM file_parts WHERE file_id = ? ORDER BY part_index ASC',
|
||||||
[file_id]
|
[file_id]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import pool from '@/lib/db';
|
import pool from '@/lib/db';
|
||||||
|
|
||||||
if (!process.env.DISCORD_WEBHOOK_URL) {
|
if (!process.env.DISCORD_BOT_TOKEN || !process.env.DISCORD_CHANNEL_ID) {
|
||||||
throw new Error('Please define DISCORD_WEBHOOK_URL in .env.local');
|
throw new Error('Please define DISCORD_BOT_TOKEN and DISCORD_CHANNEL_ID in .env.local');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DISCORD_API_URL = `https://discord.com/api/v10/channels/${process.env.DISCORD_CHANNEL_ID}/messages`;
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
@@ -16,14 +18,15 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({ error: 'Missing required form fields' }, { status: 400 });
|
return NextResponse.json({ error: 'Missing required form fields' }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward the chunk to Discord
|
// Forward the chunk to Discord via the Bot API
|
||||||
const discordFormData = new FormData();
|
const discordFormData = new FormData();
|
||||||
discordFormData.append('file', chunk, `chunk-${partIndex}.bin`);
|
discordFormData.append('file', chunk, `chunk-${partIndex}.bin`);
|
||||||
// You can add content to the message if you want
|
|
||||||
// discordFormData.append('content', `File chunk for ${fileId}, part ${partIndex}`);
|
|
||||||
|
|
||||||
const discordRes = await fetch(process.env.DISCORD_WEBHOOK_URL, {
|
const discordRes = await fetch(DISCORD_API_URL, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bot ${process.env.DISCORD_BOT_TOKEN}`,
|
||||||
|
},
|
||||||
body: discordFormData,
|
body: discordFormData,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ export async function POST(request: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const discordMessageId = discordData.id;
|
const discordMessageId = discordData.id;
|
||||||
const discordAttachmentUrl = attachment.url;
|
const discordAttachmentUrl = attachment.url; // This URL is temporary
|
||||||
const partSize = attachment.size;
|
const partSize = attachment.size;
|
||||||
|
|
||||||
// Save part metadata to the database
|
// Save part metadata to the database
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default function DownloadPage() {
|
|||||||
setDownloadState('downloading');
|
setDownloadState('downloading');
|
||||||
let downloadedCount = 0;
|
let downloadedCount = 0;
|
||||||
const downloadPromises = metadata.parts.map((part: any) =>
|
const downloadPromises = metadata.parts.map((part: any) =>
|
||||||
fetch(part.discord_attachment_url).then(res => res.arrayBuffer()).then(buffer => {
|
fetch(`/api/download-part/${file_id}/${part.part_index}`).then(res => res.arrayBuffer()).then(buffer => {
|
||||||
downloadedCount++;
|
downloadedCount++;
|
||||||
setProgress(Math.round((downloadedCount / metadata.num_parts) * 50));
|
setProgress(Math.round((downloadedCount / metadata.num_parts) * 50));
|
||||||
return { index: part.part_index, data: buffer };
|
return { index: part.part_index, data: buffer };
|
||||||
|
|||||||
Reference in New Issue
Block a user