diff --git a/functions/upload.js b/functions/upload.js index e2157b1..ffb44b5 100644 --- a/functions/upload.js +++ b/functions/upload.js @@ -1,39 +1,76 @@ -const BASE = 'https://temp.imgur.gg'; +// functions/upload.js +// Handles cover image uploads via imgur.gg API + +const API_BASE = 'https://temp.imgur.gg/api/upload'; export async function onRequest(context) { const { request } = context; + // Handle CORS preflight + if (request.method === 'OPTIONS') { + return new Response(null, { + status: 204, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'POST', + 'Access-Control-Allow-Headers': 'Content-Type', + }, + }); + } + if (request.method !== 'POST') { - return new Response('Method not allowed', { status: 405 }); + return new Response(JSON.stringify({ error: 'Method not allowed' }), { + status: 405, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }); } - const formData = await request.formData(); - const file = formData.get('file'); + try { + // Parse the multipart form data + const formData = await request.formData(); + const file = formData.get('file'); - if (!file) { - return new Response('No file', { status: 400 }); - } + if (!file) { + return new Response(JSON.stringify({ error: 'No file provided' }), { + status: 400, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }); + } - const buffer = await file.arrayBuffer(); - // 1. GET site to grab cookie - const getRes = await fetch(BASE, { - method: 'GET', - }); + // Validate file type + if (!file.type.startsWith('image/')) { + return new Response(JSON.stringify({ error: 'File must be an image' }), { + status: 400, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }); + } - const setCookie = getRes.headers.get('set-cookie') || ''; - const cookie = setCookie.split(';')[0]; // _s=xxxxx + // Validate file size (max 10MB) + const maxSize = 10 * 1024 * 1024; // 10MB + if (file.size > maxSize) { + return new Response(JSON.stringify({ error: 'File size exceeds 10MB limit' }), { + status: 400, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + }); + } - // 2. Request metadata WITH cookie + // Get file bytes + const fileBytes = await file.arrayBuffer(); - const metadataRes = await fetch(`${BASE}/api/upload`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Cookie: cookie, - Origin: BASE, - Referer: BASE + '/', - }, - body: JSON.stringify({ + // Step 1: Request upload metadata + const metadataPayload = { files: [ { fileName: file.name, @@ -41,42 +78,74 @@ export async function onRequest(context) { fileSize: file.size, }, ], - }), - }); + }; - if (!metadataRes.ok) { - const text = await metadataRes.text(); - return new Response('Metadata failed: ' + text, { status: 500 }); - } + const metadataResponse = await fetch(API_BASE, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(metadataPayload), + }); - const metadata = await metadataRes.json(); - const uploadInfo = metadata.files[0]; - - // 3. Upload to signed URL - - const uploadRes = await fetch(uploadInfo.uploadUrl, { - method: 'PUT', - headers: { - 'Content-Type': file.type, - }, - body: buffer, - }); - - if (!uploadRes.ok) { - return new Response('Upload failed', { status: 500 }); - } - - // 4. Return public URL - - const publicUrl = `https://i.imgur.gg/${uploadInfo.fileId}-${uploadInfo.fileName}`; - - return new Response( - JSON.stringify({ - success: true, - url: publicUrl, - }), - { - headers: { 'Content-Type': 'application/json' }, + if (!metadataResponse.ok) { + throw new Error(`Metadata request failed: ${metadataResponse.status}`); } - ); + + const metadata = await metadataResponse.json(); + + if (!metadata.success || !metadata.files || !metadata.files[0]) { + throw new Error('Failed to get upload URL from imgur.gg'); + } + + const fileInfo = metadata.files[0]; + const uploadUrl = fileInfo.uploadUrl; + + // Step 2: Upload the file + const uploadResponse = await fetch(uploadUrl, { + method: 'PUT', + body: fileBytes, + headers: { + 'Content-Type': file.type, + }, + }); + + if (!uploadResponse.ok) { + throw new Error(`File upload failed: ${uploadResponse.status}`); + } + + // Step 3: Return the public URL + const publicUrl = `https://i.imgur.gg/${fileInfo.fileId}-${fileInfo.fileName}`; + + return new Response( + JSON.stringify({ + success: true, + url: publicUrl, + fileId: fileInfo.fileId, + fileName: fileInfo.fileName, + }), + { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + } + ); + } catch (error) { + console.error('Upload error:', error); + return new Response( + JSON.stringify({ + error: 'Upload failed', + message: error.message, + }), + { + status: 500, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + } + ); + } }