more error handling and logging in uploads mechanic
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// src/pages/api/upload/media.ts
|
||||
// src/pages/api/upload/media.ts - Enhanced with detailed logging and error handling
|
||||
import type { APIRoute } from 'astro';
|
||||
import { withAPIAuth } from '../../../utils/auth.js';
|
||||
import { apiResponse, apiError, apiServerError, apiSpecial, handleAPIRequest } from '../../../utils/api.js';
|
||||
@@ -50,6 +50,7 @@ function checkUploadRateLimit(userEmail: string): boolean {
|
||||
}
|
||||
|
||||
if (userLimit.count >= RATE_LIMIT_MAX) {
|
||||
console.warn(`[UPLOAD] Rate limit exceeded for user: ${userEmail} (${userLimit.count}/${RATE_LIMIT_MAX})`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -58,27 +59,37 @@ function checkUploadRateLimit(userEmail: string): boolean {
|
||||
}
|
||||
|
||||
function validateFile(file: File): { valid: boolean; error?: string } {
|
||||
console.log(`[UPLOAD] Validating file: ${file.name}, size: ${file.size}, type: ${file.type}`);
|
||||
|
||||
if (file.size > UPLOAD_CONFIG.maxFileSize) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `File too large. Maximum size is ${Math.round(UPLOAD_CONFIG.maxFileSize / 1024 / 1024)}MB`
|
||||
};
|
||||
const errorMsg = `File too large. Maximum size is ${Math.round(UPLOAD_CONFIG.maxFileSize / 1024 / 1024)}MB`;
|
||||
console.warn(`[UPLOAD] ${errorMsg} - File size: ${file.size}`);
|
||||
return { valid: false, error: errorMsg };
|
||||
}
|
||||
|
||||
if (!UPLOAD_CONFIG.allowedTypes.has(file.type)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `File type ${file.type} not allowed`
|
||||
};
|
||||
const errorMsg = `File type ${file.type} not allowed`;
|
||||
console.warn(`[UPLOAD] ${errorMsg} - Allowed types:`, Array.from(UPLOAD_CONFIG.allowedTypes));
|
||||
return { valid: false, error: errorMsg };
|
||||
}
|
||||
|
||||
console.log(`[UPLOAD] File validation passed for: ${file.name}`);
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
async function uploadToNextcloud(file: File, userEmail: string): Promise<UploadResult> {
|
||||
console.log(`[UPLOAD] Attempting Nextcloud upload for: ${file.name} by ${userEmail}`);
|
||||
|
||||
try {
|
||||
const uploader = new NextcloudUploader();
|
||||
const result = await uploader.uploadFile(file, userEmail);
|
||||
|
||||
console.log(`[UPLOAD] Nextcloud upload successful:`, {
|
||||
filename: result.filename,
|
||||
url: result.url,
|
||||
size: file.size
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
url: result.url,
|
||||
@@ -87,7 +98,7 @@ async function uploadToNextcloud(file: File, userEmail: string): Promise<UploadR
|
||||
storage: 'nextcloud'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Nextcloud upload failed:', error);
|
||||
console.error('[UPLOAD] Nextcloud upload failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Nextcloud upload failed',
|
||||
@@ -97,7 +108,10 @@ async function uploadToNextcloud(file: File, userEmail: string): Promise<UploadR
|
||||
}
|
||||
|
||||
async function uploadToLocal(file: File, userType: string): Promise<UploadResult> {
|
||||
console.log(`[UPLOAD] Attempting local upload for: ${file.name} (${userType})`);
|
||||
|
||||
try {
|
||||
console.log(`[UPLOAD] Creating directory: ${UPLOAD_CONFIG.localUploadPath}`);
|
||||
await fs.mkdir(UPLOAD_CONFIG.localUploadPath, { recursive: true });
|
||||
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
@@ -106,11 +120,20 @@ async function uploadToLocal(file: File, userType: string): Promise<UploadResult
|
||||
const filename = `${timestamp}-${randomString}${extension}`;
|
||||
|
||||
const filepath = path.join(UPLOAD_CONFIG.localUploadPath, filename);
|
||||
console.log(`[UPLOAD] Writing file to: ${filepath}`);
|
||||
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
await fs.writeFile(filepath, buffer);
|
||||
|
||||
const publicUrl = `${UPLOAD_CONFIG.publicBaseUrl}/uploads/${filename}`;
|
||||
|
||||
console.log(`[UPLOAD] Local upload successful:`, {
|
||||
filename,
|
||||
filepath,
|
||||
publicUrl,
|
||||
size: file.size
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
url: publicUrl,
|
||||
@@ -119,7 +142,7 @@ async function uploadToLocal(file: File, userType: string): Promise<UploadResult
|
||||
storage: 'local'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Local upload failed:', error);
|
||||
console.error('[UPLOAD] Local upload failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Local upload failed',
|
||||
@@ -130,12 +153,23 @@ async function uploadToLocal(file: File, userType: string): Promise<UploadResult
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
return await handleAPIRequest(async () => {
|
||||
console.log('[UPLOAD] Processing upload request');
|
||||
|
||||
// Enhanced auth logging
|
||||
const authResult = await withAPIAuth(request, 'contributions');
|
||||
console.log('[UPLOAD] Auth result:', {
|
||||
authenticated: authResult.authenticated,
|
||||
authRequired: authResult.authRequired,
|
||||
userId: authResult.userId
|
||||
});
|
||||
|
||||
if (authResult.authRequired && !authResult.authenticated) {
|
||||
return apiError.unauthorized();
|
||||
console.warn('[UPLOAD] Upload rejected - authentication required but user not authenticated');
|
||||
return apiError.unauthorized('Authentication required for file uploads');
|
||||
}
|
||||
|
||||
const userEmail = authResult.session?.email || 'anon@anon.anon';
|
||||
console.log(`[UPLOAD] Processing upload for user: ${userEmail}`);
|
||||
|
||||
if (!checkUploadRateLimit(userEmail)) {
|
||||
return apiError.rateLimit('Upload rate limit exceeded. Please wait before uploading again.');
|
||||
@@ -143,38 +177,60 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
|
||||
let formData;
|
||||
try {
|
||||
console.log('[UPLOAD] Parsing form data');
|
||||
formData = await request.formData();
|
||||
console.log('[UPLOAD] Form data keys:', Array.from(formData.keys()));
|
||||
} catch (error) {
|
||||
return apiError.badRequest('Invalid form data');
|
||||
console.error('[UPLOAD] Failed to parse form data:', error);
|
||||
return apiError.badRequest('Invalid form data - could not parse request');
|
||||
}
|
||||
|
||||
const file = formData.get('file') as File;
|
||||
const type = formData.get('type') as string;
|
||||
|
||||
if (!file) {
|
||||
console.warn('[UPLOAD] No file provided in request');
|
||||
return apiSpecial.missingRequired(['file']);
|
||||
}
|
||||
|
||||
console.log(`[UPLOAD] Processing file: ${file.name}, type parameter: ${type}`);
|
||||
|
||||
const validation = validateFile(file);
|
||||
if (!validation.valid) {
|
||||
return apiError.badRequest(validation.error!);
|
||||
}
|
||||
|
||||
// Enhanced environment logging
|
||||
const nextcloudConfigured = isNextcloudConfigured();
|
||||
console.log('[UPLOAD] Environment check:', {
|
||||
nextcloudConfigured,
|
||||
localUploadPath: UPLOAD_CONFIG.localUploadPath,
|
||||
publicBaseUrl: UPLOAD_CONFIG.publicBaseUrl,
|
||||
nodeEnv: process.env.NODE_ENV
|
||||
});
|
||||
|
||||
let result: UploadResult;
|
||||
|
||||
if (isNextcloudConfigured()) {
|
||||
if (nextcloudConfigured) {
|
||||
console.log('[UPLOAD] Using Nextcloud as primary storage');
|
||||
result = await uploadToNextcloud(file, userEmail);
|
||||
|
||||
if (!result.success) {
|
||||
console.warn('Nextcloud upload failed, trying local fallback:', result.error);
|
||||
console.warn('[UPLOAD] Nextcloud upload failed, trying local fallback:', result.error);
|
||||
result = await uploadToLocal(file, type);
|
||||
}
|
||||
} else {
|
||||
console.log('[UPLOAD] Using local storage (Nextcloud not configured)');
|
||||
result = await uploadToLocal(file, type);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
console.log(`[MEDIA UPLOAD] ${file.name} (${file.size} bytes) by ${userEmail} -> ${result.storage}: ${result.url}`);
|
||||
console.log(`[UPLOAD] Upload completed successfully:`, {
|
||||
filename: result.filename,
|
||||
storage: result.storage,
|
||||
url: result.url,
|
||||
user: userEmail
|
||||
});
|
||||
|
||||
return apiSpecial.uploadSuccess({
|
||||
url: result.url!,
|
||||
@@ -183,7 +239,12 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
storage: result.storage!
|
||||
});
|
||||
} else {
|
||||
console.error(`[MEDIA UPLOAD FAILED] ${file.name} by ${userEmail}: ${result.error}`);
|
||||
console.error(`[UPLOAD] Upload failed completely:`, {
|
||||
filename: file.name,
|
||||
error: result.error,
|
||||
storage: result.storage,
|
||||
user: userEmail
|
||||
});
|
||||
|
||||
return apiSpecial.uploadFailed(result.error!);
|
||||
}
|
||||
@@ -193,6 +254,8 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
return await handleAPIRequest(async () => {
|
||||
console.log('[UPLOAD] Getting upload status');
|
||||
|
||||
const authResult = await withAPIAuth(request);
|
||||
if (authResult.authRequired && !authResult.authenticated) {
|
||||
return apiError.unauthorized();
|
||||
@@ -204,12 +267,14 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
await fs.access(UPLOAD_CONFIG.localUploadPath);
|
||||
localStorageAvailable = true;
|
||||
console.log('[UPLOAD] Local storage accessible');
|
||||
} catch {
|
||||
try {
|
||||
await fs.mkdir(UPLOAD_CONFIG.localUploadPath, { recursive: true });
|
||||
localStorageAvailable = true;
|
||||
console.log('[UPLOAD] Local storage created');
|
||||
} catch (error) {
|
||||
console.warn('Local upload directory not accessible:', error);
|
||||
console.warn('[UPLOAD] Local upload directory not accessible:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,9 +302,14 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
paths: {
|
||||
uploadEndpoint: '/api/upload/media',
|
||||
localPath: localStorageAvailable ? '/uploads' : null
|
||||
},
|
||||
environment: {
|
||||
nodeEnv: process.env.NODE_ENV,
|
||||
publicBaseUrl: UPLOAD_CONFIG.publicBaseUrl
|
||||
}
|
||||
};
|
||||
|
||||
console.log('[UPLOAD] Status check completed:', status);
|
||||
return apiResponse.success(status);
|
||||
|
||||
}, 'Upload status retrieval failed');
|
||||
|
||||
Reference in New Issue
Block a user