This commit is contained in:
Omar Najjar 2025-04-02 20:37:49 +11:00
parent 9f98c853db
commit 3cb78a90d8
3 changed files with 162 additions and 83 deletions

View file

@ -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 = '<div class="comments-loading">Loading comments...</div>';
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'

102
worker.js
View file

@ -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
}
});
}
}
};

View file

@ -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"