refactor: Improve type safety in download and file routes by updating type annotations
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextResponse, NextRequest } from 'next/server';
|
||||
import pool from '@/lib/db';
|
||||
import { RowDataPacket } from 'mysql2/promise';
|
||||
|
||||
@@ -6,15 +6,20 @@ interface FilePart extends RowDataPacket {
|
||||
discord_message_id: string | null;
|
||||
}
|
||||
|
||||
interface RouteContext {
|
||||
params: {
|
||||
file_id: string;
|
||||
part_index: string;
|
||||
};
|
||||
}
|
||||
|
||||
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; } }) {
|
||||
export async function GET(request: NextRequest, context: { params: Record<string, string | string[]> }) {
|
||||
try {
|
||||
const params = await context.params;
|
||||
const file_id = params.file_id as string;
|
||||
const part_index = params.part_index as string;
|
||||
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.
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextResponse, NextRequest } from 'next/server';
|
||||
import pool from '@/lib/db';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { RowDataPacket } from 'mysql2/promise';
|
||||
|
||||
export async function GET(request: Request, context: any) {
|
||||
interface FileData extends RowDataPacket {
|
||||
filename: string;
|
||||
size: number;
|
||||
num_parts: number;
|
||||
expires_at: Date | null;
|
||||
token_hash: string;
|
||||
}
|
||||
|
||||
interface FilePartMetadata extends RowDataPacket {
|
||||
part_index: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest, { params }: { params: { file_id: string; } }) {
|
||||
try {
|
||||
const params = await context.params;
|
||||
const file_id = params.file_id as string;
|
||||
const file_id = params.file_id;
|
||||
const { searchParams } = new URL(request.url);
|
||||
const token = searchParams.get('token');
|
||||
|
||||
@@ -16,8 +29,8 @@ export async function GET(request: Request, context: any) {
|
||||
const connection = await pool.getConnection();
|
||||
try {
|
||||
// 1. Fetch file metadata and token hash
|
||||
const [fileRows]: any[] = await connection.query('SELECT * FROM files WHERE id = ? AND deleted = 0', [file_id]);
|
||||
const file = fileRows[0];
|
||||
const [fileRows]: [RowDataPacket[], unknown] = await connection.query('SELECT * FROM files WHERE id = ? AND deleted = 0', [file_id]);
|
||||
const file = fileRows[0] as FileData;
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: 'File not found' }, { status: 404 });
|
||||
@@ -35,7 +48,7 @@ export async function GET(request: Request, context: any) {
|
||||
}
|
||||
|
||||
// 4. Fetch file parts
|
||||
const [partsRows]: any[] = await connection.query(
|
||||
const [partsRows]: [RowDataPacket[], unknown] = await connection.query(
|
||||
'SELECT part_index, size FROM file_parts WHERE file_id = ? ORDER BY part_index ASC',
|
||||
[file_id]
|
||||
);
|
||||
@@ -45,7 +58,7 @@ export async function GET(request: Request, context: any) {
|
||||
filename: file.filename,
|
||||
size: file.size,
|
||||
num_parts: file.num_parts,
|
||||
parts: partsRows,
|
||||
parts: partsRows[0] as FilePartMetadata[],
|
||||
};
|
||||
|
||||
return NextResponse.json(response);
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { useParams, useSearchParams } from 'next/navigation';
|
||||
|
||||
interface MetadataPart {
|
||||
part_index: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
// Helper to convert Base64 string back to ArrayBuffer
|
||||
function base64ToBuffer(base64: string) {
|
||||
const binary_string = window.atob(base64.replace(/-/g, '+').replace(/_/g, '/'));
|
||||
@@ -106,7 +111,7 @@ export default function DownloadPage() {
|
||||
setDownloadState('downloading');
|
||||
const encryptedParts = new Array(metadata.num_parts);
|
||||
|
||||
const downloadPromises = metadata.parts.map(async (part: any) => {
|
||||
const downloadPromises = metadata.parts.map(async (part: MetadataPart) => {
|
||||
const response = await fetchWithRetry(`/api/download-part/${file_id}/${part.part_index}`);
|
||||
const buffer = await response.arrayBuffer();
|
||||
encryptedParts[part.part_index] = { index: part.part_index, data: buffer };
|
||||
@@ -127,7 +132,7 @@ export default function DownloadPage() {
|
||||
['decrypt']
|
||||
);
|
||||
|
||||
const decryptPromises = encryptedParts.map(async (part) => {
|
||||
const decryptPromises = encryptedParts.map(async (part: { index: number; data: ArrayBuffer }) => {
|
||||
const iv = part.data.slice(0, 12);
|
||||
const data = part.data.slice(12);
|
||||
const decrypted = await window.crypto.subtle.decrypt({ name: 'AES-GCM', iv }, fileKey, data);
|
||||
|
||||
Reference in New Issue
Block a user