From 3cb78a90d8cd56bac4c277803a35ccc815fc9694 Mon Sep 17 00:00:00 2001 From: Omar Najjar Date: Wed, 2 Apr 2025 20:37:49 +1100 Subject: [PATCH] x --- index.html | 137 +++++++++++++++++++++++++++++--------------------- worker.js | 102 ++++++++++++++++++++++++++++--------- wrangler.toml | 6 ++- 3 files changed, 162 insertions(+), 83 deletions(-) diff --git a/index.html b/index.html index 3f7599f..99559ea 100644 --- a/index.html +++ b/index.html @@ -417,34 +417,27 @@ function smartCopyShare(proposalId, proposalText) { // Create or get user in DB async function createUser(user) { - try { - console.log("Creating user:", user); // Log the user data - - const response = await fetch(`${API_BASE_URL}/users`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - id: user.id, - name: user.name - }) - }); - - if (!response.ok) { - const errorData = await response.json(); - console.error("User creation API error:", errorData); - throw new Error('Failed to create user'); - } - - const data = await response.json(); - console.log("User creation successful:", data); - return data; - } catch (error) { - console.error('Error creating user:', error); - throw error; - } - } + try { + console.log("Creating user:", user); + + const data = await fetchWithErrorHandling(`${API_BASE_URL}/users`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + id: user.id, + name: user.name + }) + }); + + console.log("User creation successful:", data); + return data; + } catch (error) { + console.error('Error creating user:', error); + throw error; + } +} // Animation for successful post function animatePostSuccess() { @@ -543,28 +536,58 @@ function smartCopyShare(proposalId, proposalText) { }, 500); }, 3000); } + + async function fetchWithErrorHandling(url, options = {}) { + try { + // Add mode: 'cors' explicitly to ensure CORS is respected + const response = await fetch(url, { + ...options, + mode: 'cors', + credentials: 'same-origin' + }); + + if (!response.ok) { + // Try to parse error message from response + let errorData; + try { + errorData = await response.json(); + } catch (e) { + errorData = { error: `HTTP error: ${response.status} ${response.statusText}` }; + } + + throw new Error( + errorData.error || + errorData.message || + `Request failed with status ${response.status}` + ); + } + + return await response.json(); + } catch (error) { + console.error(`Fetch error for ${url}:`, error); + throw error; + } +} // Fetch proposals from API async function fetchProposals() { - try { - // Show loading indicator - showLoadingIndicator(); - - const sortSelect = document.getElementById('sort-select'); - const sortBy = sortSelect ? sortSelect.value : 'newest'; - const response = await fetch(`${API_BASE_URL}/proposals?sortBy=${sortBy}&userId=${currentUser.id}`); - - if (!response.ok) { - throw new Error('Failed to fetch proposals'); - } - - const proposals = await response.json(); - renderProposals(proposals); - } catch (error) { - console.error('Error fetching proposals:', error); - showErrorMessage('Oops! Failed to load petitions. Parliament might be censoring us already!'); - } - } + try { + // Show loading indicator + showLoadingIndicator(); + + const sortSelect = document.getElementById('sort-select'); + const sortBy = sortSelect ? sortSelect.value : 'newest'; + + const data = await fetchWithErrorHandling( + `${API_BASE_URL}/proposals?sortBy=${sortBy}&userId=${currentUser.id}` + ); + + renderProposals(data); + } catch (error) { + console.error('Error fetching proposals:', error); + showErrorMessage('Oops! Failed to load petitions. Try refreshing the page.'); + } +} // Handle voting async function handleVote(proposalId, voteType) { @@ -760,7 +783,7 @@ async function submitVoteToServer(proposalId, voteType, isPetition = false, peti voteData.petitionDetails = petitionDetails; } - const response = await fetch(`${API_BASE_URL}/votes`, { + const response = await fetchWithErrorHandling(`${API_BASE_URL}/votes`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -1117,7 +1140,7 @@ uploadIcon.style.marginRight = '8px'; const trending = Math.random() > 0.8; // Randomly mark some as trending // Create the proposal first - const createResponse = await fetch(`${API_BASE_URL}/proposals`, { + const createResponse = await fetchWithErrorHandling(`${API_BASE_URL}/proposals`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -1466,7 +1489,7 @@ uploadIcon.style.marginRight = '8px'; showToastMessage('Loading petition...', '#11cc77'); // Fetch the specific proposal - const response = await fetch(`${API_BASE_URL}/proposals?userId=${currentUser.id}`); + const response = await fetchWithErrorHandling(`${API_BASE_URL}/proposals?userId=${currentUser.id}`); if (!response.ok) { throw new Error('Failed to fetch proposal'); @@ -1761,7 +1784,7 @@ async function loadModalComments(proposalId) { try { // Include the current user ID to get their votes - const response = await fetch(`${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${currentUser.id}`); + const response = await fetchWithErrorHandling(`${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${currentUser.id}`); if (!response.ok) { throw new Error('Failed to fetch comments'); @@ -1880,7 +1903,7 @@ async function handleCommentVote(commentId, voteType) { // Submit vote to server try { - const response = await fetch(`${API_BASE_URL}/comment-votes`, { + const response = await fetchWithErrorHandling(`${API_BASE_URL}/comment-votes`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -1938,7 +1961,7 @@ async function loadComments(proposalId) { try { // Include the current user ID to get their votes - const response = await fetch(`${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${currentUser.id}`); + const response = await fetchWithErrorHandling(`${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${currentUser.id}`); if (!response.ok) { throw new Error('Failed to fetch comments'); @@ -2028,7 +2051,7 @@ async function submitComment(proposalId, form) { submitButton.disabled = true; try { - const response = await fetch(`${API_BASE_URL}/comments`, { + const response = await fetchWithErrorHandling(`${API_BASE_URL}/comments`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -2342,7 +2365,7 @@ function updateCommentCount(proposalId) { const commentsToggle = document.getElementById(`comments-toggle-${proposalId}`); if (!commentsToggle) return; - fetch(`${API_BASE_URL}/comments?proposalId=${proposalId}`) + fetchWithErrorHandling(`${API_BASE_URL}/comments?proposalId=${proposalId}`) .then(response => response.json()) .then(comments => { const commentCount = comments.length; @@ -2360,7 +2383,7 @@ function loadComments(proposalId) { commentsList.innerHTML = '
Loading comments...
'; - fetch(`${API_BASE_URL}/comments?proposalId=${proposalId}`) + fetchWithErrorHandling(`${API_BASE_URL}/comments?proposalId=${proposalId}`) .then(response => response.json()) .then(comments => { if (comments.length === 0) { @@ -2389,7 +2412,7 @@ function loadComments(proposalId) { // Update submitComment function to handle promises function submitComment(proposalId, commentText) { - return fetch(`${API_BASE_URL}/comments`, { + return fetchWithErrorHandling(`${API_BASE_URL}/comments`, { method: 'POST', headers: { 'Content-Type': 'application/json' diff --git a/worker.js b/worker.js index 938561a..3dd155b 100644 --- a/worker.js +++ b/worker.js @@ -1,3 +1,38 @@ +// Define CORS headers - These need to be applied to *all* responses +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + 'Access-Control-Max-Age': '86400', // 24 hours +}; + +// Handle OPTIONS request for CORS preflight +function handleOptions() { + return new Response(null, { + headers: corsHeaders + }); +} + +// Helper to create standardized responses with CORS headers +function createResponse(body, status = 200, cacheDuration = 60*60*24) { + const headers = { + 'Content-Type': 'application/json', + ...corsHeaders // Make sure CORS headers are added to every response + }; + + if (cacheDuration > 0) { + headers['Cache-Control'] = `public, max-age=${cacheDuration}`; + headers['CDN-Cache-Control'] = `public, max-age=${cacheDuration}`; + } else { + headers['Cache-Control'] = 'no-store'; + } + + return new Response(JSON.stringify(body), { + status, + headers + }); +} + export default { async fetch(request, env, ctx) { try { @@ -13,7 +48,10 @@ export default { // Check if the hostname is valid if (!validHostnames.includes(url.hostname)) { console.error(`Unexpected hostname: ${url.hostname}`); - return new Response('Invalid hostname', { status: 400 }); + return new Response('Invalid hostname', { + status: 400, + headers: corsHeaders // Add CORS headers even to error responses + }); } const path = url.pathname; @@ -22,17 +60,11 @@ export default { console.log(`Processing request: ${url.toString()}`); console.log(`User-Agent: ${request.headers.get('User-Agent')}`); - // Handle CORS preflight requests first + // Handle CORS preflight requests first - this is critical if (request.method === 'OPTIONS') { return handleOptions(); } - // // Check if this is an API request (starts with /api/) - // if (!path.startsWith('/api/')) { - // // For non-API routes, pass through to Cloudflare Pages - // return fetch(request); - // } - // For GET requests, try to serve from cache first if (request.method === "GET") { // Create a cache key from the request URL @@ -69,7 +101,10 @@ export default { if (!response.ok) { console.error('Failed to fetch styles.css', response.status, response.statusText); - return new Response('CSS File Not Found', { status: 404 }); + return new Response('CSS File Not Found', { + status: 404, + headers: corsHeaders // Add CORS headers + }); } // Get the CSS content @@ -96,7 +131,10 @@ export default { return cssResponse; } catch (error) { console.error('Error fetching styles.css:', error); - return new Response('Internal Server Error', { status: 500 }); + return new Response('Internal Server Error', { + status: 500, + headers: corsHeaders // Add CORS headers + }); } } @@ -252,22 +290,36 @@ export default { } } - return response; - - } catch (error) { - console.error('Critical error in request handling:', error); - return new Response(JSON.stringify({ - error: 'Internal Server Error', - details: error.message - }), { - status: 500, - headers: { - 'Content-Type': 'application/json', - ...corsHeaders // Add CORS headers to error responses too - } - }); - } + // If no other handler catches the request, return a 404 + return createResponse({ + error: 'Not found', + path, + method: request.method, + availableRoutes: [ + { path: '/api/health', method: 'GET', description: 'Check system health' }, + { path: '/api/proposals', method: 'GET', description: 'Get proposals' }, + { path: '/api/proposals/:id', method: 'GET', description: 'Get a single proposal by ID' }, + { path: '/api/proposals/:id', method: 'PUT', description: 'Update proposal' }, + { path: '/api/votes', method: 'POST', description: 'Create/update vote' }, + { path: '/api/users', method: 'POST', description: 'Create/get user' }, + { path: '/api/petition-stats', method: 'GET', description: 'Get petition statistics' } + ] + }, 404); + + } catch (error) { + console.error('Critical error in request handling:', error); + return new Response(JSON.stringify({ + error: 'Internal Server Error', + details: error.message + }), { + status: 500, + headers: { + 'Content-Type': 'application/json', + ...corsHeaders // Add CORS headers to error responses too + } + }); } +} }; diff --git a/wrangler.toml b/wrangler.toml index b8b5d2e..77c93d3 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -17,7 +17,11 @@ bucket_name = "memes" [vars] ENVIRONMENT = "production" -# Routes (if using workers.dev) +# Routes - Ensure ALL routes are properly configured [[routes]] pattern = "theradicalparty.com/api/*" zone_name = "theradicalparty.com" + +[[routes]] +pattern = "radical.omar-c29.workers.dev/*" +zone_name = "radical.omar-c29.workers.dev"