diff --git a/index.html b/index.html
index 6d81f1d..c22c173 100644
--- a/index.html
+++ b/index.html
@@ -1311,17 +1311,35 @@ uploadIcon.style.marginRight = '8px';
console.log("Create response headers:", Object.fromEntries(createResponse.headers.entries()));
if (!createResponse.ok) {
- const errorText = await createResponse.text();
- console.error("Error response:", errorText);
- throw new Error(`Failed to create proposal: ${createResponse.status} ${errorText}`);
+ let errorData;
+ try {
+ errorData = await createResponse.json();
+ console.error("Error response JSON:", errorData);
+ } catch (parseError) {
+ const errorText = await createResponse.text();
+ console.error("Error response text:", errorText);
+ errorData = { error: errorText || 'Unknown error' };
+ }
+ throw new Error(`Failed to create proposal: ${errorData.error || createResponse.statusText}`);
}
- const proposalResult = await createResponse.json();
- console.log("Proposal created successfully:", proposalResult);
+ let proposalResult;
+ try {
+ proposalResult = await createResponse.json();
+ console.log("Proposal created successfully:", proposalResult);
+ } catch (jsonError) {
+ console.error("Error parsing success response:", jsonError);
+ throw new Error("Server returned invalid response format");
+ }
// Get the proposal ID from the result
const proposalId = proposalResult.id;
+ if (!proposalId) {
+ console.error("Missing proposal ID in response:", proposalResult);
+ throw new Error("Server returned incomplete response");
+ }
+
// If there's a meme, upload it
if (selectedMeme) {
// Show progress bar
diff --git a/worker.js b/worker.js
index a30d8e5..d312f90 100644
--- a/worker.js
+++ b/worker.js
@@ -1162,85 +1162,101 @@ async function getProposalById(request, env) {
async function generateAndStoreShareImage(proposal, env) {
try {
+ // Check if we have the necessary bindings
+ if (!env.MEMES_BUCKET) {
+ console.error('Missing MEMES_BUCKET binding');
+ return null;
+ }
+
// Get base text for the image
const text = proposal.text || "Unknown petition";
const authorName = proposal.author_name || "Anonymous";
- // Create Cloudinary URL for transformation
- const cloudinaryUrl = "https://res.cloudinary.com/dh8apmjya/image/fetch";
+ // Instead of using Cloudinary, let's use our SVG generation which we know works
+ const imageId = `share_${proposal.id}`;
+ const fallbackImageUrl = "https://theradicalparty.com/memes/petition-template.png";
- // Encode text for URL parameters
- const encodedText = encodeURIComponent(text.substring(0, 80))
- .replace(/'/g, '%27')
- .replace(/"/g, '%22');
+ // Create a simple text-based SVG directly
+ const svgMarkup = ``;
- // Create transformation parameters
- const params = `b_black,w_1200,h_630,c_fill/l_text:Arial_32_bold:${encodedText},co_white,w_1000,c_fit,g_center`;
+ // Convert SVG to a blob
+ const svgBlob = new Blob([svgMarkup], { type: 'image/svg+xml' });
- // Template image URL
- const templateUrl = encodeURIComponent("https://theradicalparty.com/memes/petition-template.png");
-
- // Create a unique identifier for this image
- const imageId = `share_${proposal.id}_${Date.now()}`;
-
- // Final Cloudinary URL
- const cloudinaryImageUrl = `${cloudinaryUrl}/${params}/${templateUrl}`;
-
- console.log(`Generating share image: ${cloudinaryImageUrl}`);
-
- // Fetch the image from Cloudinary
- const imageResponse = await fetch(cloudinaryImageUrl);
-
- if (!imageResponse.ok) {
- throw new Error(`Failed to fetch image: ${imageResponse.status}`);
+ // Upload the SVG to R2
+ try {
+ await env.MEMES_BUCKET.put(imageId, svgBlob, {
+ httpMetadata: {
+ contentType: 'image/svg+xml',
+ },
+ });
+
+ // Generate the public URL for the stored image
+ const shareImageUrl = `https://theradicalparty.com/memes/${imageId}`;
+ console.log(`Created share image at: ${shareImageUrl}`);
+ return shareImageUrl;
+ } catch (uploadError) {
+ console.error('Error uploading share image:', uploadError);
+ return fallbackImageUrl;
}
-
- // Get the image as a blob
- const imageBlob = await imageResponse.blob();
-
- // Upload the image to your R2 bucket
- await env.MEMES_BUCKET.put(imageId, imageBlob, {
- httpMetadata: {
- contentType: 'image/png',
- },
- });
-
- // Generate the public URL for the stored image
- const shareImageUrl = `https://theradicalparty.com/memes/${imageId}`;
-
- console.log(`Stored share image at: ${shareImageUrl}`);
-
- // Update the proposal in the database with the share image URL
- await env.DB.prepare(`
- UPDATE proposals
- SET share_image_url = ?
- WHERE id = ?
- `).bind(shareImageUrl, proposal.id).run();
-
- return shareImageUrl;
} catch (error) {
- console.error('Error generating share image:', error);
+ console.error('Error in share image generation:', error);
// Return a fallback image URL in case of failure
- return "https://theradicalparty.com/memes/mickeymeta.PNG";
+ return "https://theradicalparty.com/memes/petition-template.png";
}
}
// Create a new proposal
async function createProposal(request, env) {
try {
- const data = await request.json();
+ // Log request details for debugging
+ console.log(`Received proposal creation request at ${Date.now()}`);
+
+ // Check if DB binding exists
+ if (!env.DB) {
+ console.error('Database binding is missing');
+ return createResponse({
+ error: 'Database configuration error',
+ details: 'Database binding is not available'
+ }, 500);
+ }
+
+ // Parse request data
+ let data;
+ try {
+ data = await request.json();
+ console.log(`Parsed request data: ${JSON.stringify(data)}`);
+ } catch (parseError) {
+ console.error('Failed to parse request JSON:', parseError);
+ return createResponse({
+ error: 'Invalid request format',
+ details: 'Could not parse JSON body'
+ }, 400);
+ }
+
const { text, authorId, trending = false } = data;
- console.log(`Creating proposal: ${JSON.stringify(data)}`);
-
+ // Validate required fields
if (!text || !authorId) {
- return createResponse({ error: 'Missing required fields', received: { text, authorId } }, 400);
+ console.error('Missing required fields:', { text: !!text, authorId: !!authorId });
+ return createResponse({
+ error: 'Missing required fields',
+ received: { text: text ? 'present' : 'missing', authorId: authorId ? 'present' : 'missing' }
+ }, 400);
}
// Check if user exists first
try {
+ console.log(`Checking if user exists: ${authorId}`);
const userCheck = await env.DB.prepare(`SELECT id, name FROM users WHERE id = ?`).bind(authorId).first();
+
if (!userCheck) {
+ console.error(`User not found: ${authorId}`);
return createResponse({
error: 'Author does not exist',
authorId,
@@ -1248,38 +1264,67 @@ async function createProposal(request, env) {
}, 400);
}
+ console.log(`User found: ${userCheck.id}, ${userCheck.name}`);
+
// Create a temporary proposal object with author name for image generation
+ const id = 'proposal_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
+ const timestamp = Date.now();
+
+ console.log(`Generated proposal ID: ${id}`);
+
+ // Create temporary proposal object
const tempProposal = {
- id: 'proposal_' + Date.now(),
+ id: id,
text: text,
author_name: userCheck.name
};
- const id = tempProposal.id;
- const timestamp = Date.now();
-
// Insert the proposal first
+ console.log('Inserting proposal into database');
const query = `
INSERT INTO proposals (id, author_id, text, timestamp, trending)
VALUES (?, ?, ?, ?, ?)
`;
- await env.DB.prepare(query).bind(id, authorId, text, timestamp, trending ? 1 : 0).run();
+ try {
+ await env.DB.prepare(query)
+ .bind(id, authorId, text, timestamp, trending ? 1 : 0)
+ .run();
+
+ console.log('Proposal inserted successfully');
+ } catch (insertError) {
+ console.error('Database insertion error:', insertError);
+ return createResponse({
+ error: 'Failed to insert proposal into database',
+ details: logError(insertError, { action: 'insert_proposal' })
+ }, 500);
+ }
- // Generate and store the share image
- const shareImageUrl = await generateAndStoreShareImage(tempProposal, env);
+ // Generate and store the share image (but don't block on it)
+ console.log('Generating share image');
+ let shareImageUrl = null;
- // Update the proposal with the share image URL
- if (shareImageUrl) {
- await env.DB.prepare(`
- UPDATE proposals
- SET share_image_url = ?
- WHERE id = ?
- `).bind(shareImageUrl, id).run();
+ try {
+ shareImageUrl = await generateAndStoreShareImage(tempProposal, env);
+ console.log(`Generated share image: ${shareImageUrl}`);
+
+ // Update the proposal with the share image URL if we got one
+ if (shareImageUrl) {
+ console.log('Updating proposal with share image URL');
+ await env.DB.prepare(`
+ UPDATE proposals
+ SET share_image_url = ?
+ WHERE id = ?
+ `).bind(shareImageUrl, id).run();
+ }
+ } catch (imageError) {
+ // Log but don't fail the whole request if image generation fails
+ console.error('Image generation error (non-fatal):', imageError);
}
console.log(`Successfully created proposal with ID: ${id}`);
+ // Return success response with created proposal data
return createResponse({
id,
authorId,
@@ -1288,16 +1333,15 @@ async function createProposal(request, env) {
trending,
shareImageUrl
});
- } catch (dbError) {
+ } catch (userCheckError) {
+ console.error('Error checking user existence:', userCheckError);
return createResponse({
- error: 'Failed to create proposal in database',
- details: logError(dbError, {
- action: 'insert_proposal',
- proposal: { id, authorId, timestamp, trending }
- })
+ error: 'Failed to verify user',
+ details: logError(userCheckError, { action: 'check_user', authorId })
}, 500);
}
} catch (error) {
+ console.error('Unhandled error in proposal creation:', error);
return createResponse({
error: 'Failed to process proposal creation',
details: logError(error, { action: 'create_proposal_outer' })