- Move services/nextjs to nextjs/ - Move deploy/docker/Dockerfile.prod to Dockerfile - Add GitHub Actions workflows (ci.yml, build.yml) - Remove deploy/, services/, scripts/ folders
178 lines
4.9 KiB
TypeScript
178 lines
4.9 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { uploadFileServer, deleteFile } from '@/shared/lib/s3';
|
|
import { cookies } from 'next/headers';
|
|
import { verifyToken } from '@/shared/lib/auth';
|
|
|
|
// File size limits (in bytes)
|
|
const MAX_FILE_SIZE = {
|
|
image: 10 * 1024 * 1024, // 10MB
|
|
audio: 50 * 1024 * 1024, // 50MB
|
|
document: 20 * 1024 * 1024, // 20MB
|
|
};
|
|
|
|
// Allowed file types
|
|
const ALLOWED_TYPES = {
|
|
image: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'],
|
|
audio: ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/mp4', 'audio/webm'],
|
|
document: [
|
|
'application/pdf',
|
|
'application/msword',
|
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
'application/vnd.ms-excel',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
'text/plain',
|
|
'text/csv',
|
|
],
|
|
};
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
// Verify authentication - check cookie
|
|
const cookieStore = await cookies();
|
|
const cookieToken = cookieStore.get('auth-token')?.value;
|
|
|
|
const token = cookieToken;
|
|
|
|
if (!token) {
|
|
console.error('[Upload] No token found in Authorization header or cookie');
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const decoded = verifyToken(token);
|
|
if (!decoded) {
|
|
console.error('[Upload] Token verification failed');
|
|
return NextResponse.json(
|
|
{ error: 'Invalid token' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
console.log('[Upload] Token verified successfully for user:', decoded.userId);
|
|
|
|
// Parse form data
|
|
const formData = await request.formData();
|
|
const file = formData.get('file') as File;
|
|
const fileType = formData.get('type') as 'images' | 'audio' | 'documents';
|
|
|
|
if (!file) {
|
|
return NextResponse.json(
|
|
{ error: 'No file provided' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Determine folder based on file type
|
|
let folder: 'images' | 'audio' | 'documents' = 'documents';
|
|
let maxSize = MAX_FILE_SIZE.document;
|
|
let allowedTypes = ALLOWED_TYPES.document;
|
|
|
|
if (fileType === 'images') {
|
|
folder = 'images';
|
|
maxSize = MAX_FILE_SIZE.image;
|
|
allowedTypes = ALLOWED_TYPES.image;
|
|
} else if (fileType === 'audio') {
|
|
folder = 'audio';
|
|
maxSize = MAX_FILE_SIZE.audio;
|
|
allowedTypes = ALLOWED_TYPES.audio;
|
|
} else if (file.type.startsWith('image/')) {
|
|
folder = 'images';
|
|
maxSize = MAX_FILE_SIZE.image;
|
|
allowedTypes = ALLOWED_TYPES.image;
|
|
} else if (file.type.startsWith('audio/')) {
|
|
folder = 'audio';
|
|
maxSize = MAX_FILE_SIZE.audio;
|
|
allowedTypes = ALLOWED_TYPES.audio;
|
|
}
|
|
|
|
// Validate file size
|
|
if (file.size > maxSize) {
|
|
return NextResponse.json(
|
|
{ error: `File size exceeds maximum limit of ${maxSize / 1024 / 1024}MB` },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validate file type
|
|
if (!allowedTypes.includes(file.type)) {
|
|
return NextResponse.json(
|
|
{ error: `File type ${file.type} is not allowed` },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Upload file to S3
|
|
const { url, path } = await uploadFileServer(file, file.name, file.type, folder);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
url,
|
|
path,
|
|
fileName: file.name,
|
|
fileSize: file.size,
|
|
fileType: file.type,
|
|
});
|
|
} catch (error) {
|
|
console.error('Upload error:', error);
|
|
return NextResponse.json(
|
|
{ error: error instanceof Error ? error.message : 'Failed to upload file' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// DELETE /api/upload - Delete a file from storage
|
|
export async function DELETE(request: NextRequest) {
|
|
try {
|
|
// Verify authentication
|
|
const authHeader = request.headers.get('Authorization');
|
|
const cookieStore = await cookies();
|
|
const cookieToken = cookieStore.get('auth-token')?.value;
|
|
|
|
const token = authHeader?.startsWith('Bearer ')
|
|
? authHeader.substring(7)
|
|
: cookieToken;
|
|
|
|
if (!token) {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const decoded = verifyToken(token);
|
|
if (!decoded) {
|
|
return NextResponse.json(
|
|
{ error: 'Invalid token' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const { searchParams } = new URL(request.url);
|
|
const filePath = searchParams.get('path');
|
|
|
|
if (!filePath) {
|
|
return NextResponse.json(
|
|
{ error: 'File path is required' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Delete file from S3
|
|
await deleteFile(filePath);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: 'File deleted successfully',
|
|
});
|
|
} catch (error) {
|
|
console.error('Delete file error:', error);
|
|
return NextResponse.json(
|
|
{ error: error instanceof Error ? error.message : 'Failed to delete file' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|