Add rate limiting to location detection and standardize error messages

Introduces rate limiting to the `detect-location` function and refactors error responses in `upload-image` to provide consistent, descriptive messages.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: b3d7d4df-59a9-4a9c-971d-175b92dadbfa
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7cdf4e95-3f41-4180-b8e3-8ef56d032c0e/b3d7d4df-59a9-4a9c-971d-175b92dadbfa/1VcvPNb
This commit is contained in:
pac7
2025-10-08 17:18:00 +00:00
parent 3e2ce53fa7
commit 61285b0261
3 changed files with 150 additions and 26 deletions

View File

@@ -30,7 +30,10 @@ serve(async (req) => {
const authHeader = req.headers.get('Authorization')
if (!authHeader) {
return new Response(
JSON.stringify({ error: 'Authentication required for delete operations' }),
JSON.stringify({
error: 'Authentication required',
message: 'Authentication required for delete operations'
}),
{
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -49,7 +52,10 @@ serve(async (req) => {
if (authError || !user) {
console.error('Auth verification failed:', authError)
return new Response(
JSON.stringify({ error: 'Invalid authentication' }),
JSON.stringify({
error: 'Invalid authentication',
message: 'Authentication token is invalid or expired'
}),
{
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -67,7 +73,10 @@ serve(async (req) => {
if (profileError || !profile) {
console.error('Failed to fetch user profile:', profileError)
return new Response(
JSON.stringify({ error: 'User profile not found' }),
JSON.stringify({
error: 'User profile not found',
message: 'Unable to verify user profile'
}),
{
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -77,7 +86,10 @@ serve(async (req) => {
if (profile.banned) {
return new Response(
JSON.stringify({ error: 'Account suspended. Contact support for assistance.' }),
JSON.stringify({
error: 'Account suspended',
message: 'Account suspended. Contact support for assistance.'
}),
{
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -91,7 +103,10 @@ serve(async (req) => {
requestBody = await req.json();
} catch (error) {
return new Response(
JSON.stringify({ error: 'Invalid JSON in request body' }),
JSON.stringify({
error: 'Invalid JSON',
message: 'Request body must be valid JSON'
}),
{
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -103,7 +118,10 @@ serve(async (req) => {
if (!imageId || typeof imageId !== 'string' || imageId.trim() === '') {
return new Response(
JSON.stringify({ error: 'imageId is required and must be a non-empty string' }),
JSON.stringify({
error: 'Invalid imageId',
message: 'imageId is required and must be a non-empty string'
}),
{
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -125,7 +143,10 @@ serve(async (req) => {
} catch (fetchError) {
console.error('Network error deleting image:', fetchError)
return new Response(
JSON.stringify({ error: 'Network error: Unable to reach Cloudflare Images API' }),
JSON.stringify({
error: 'Network error',
message: 'Unable to reach Cloudflare Images API'
}),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -139,7 +160,10 @@ serve(async (req) => {
} catch (parseError) {
console.error('Failed to parse Cloudflare delete response:', parseError)
return new Response(
JSON.stringify({ error: 'Invalid response from Cloudflare Images API' }),
JSON.stringify({
error: 'Invalid response',
message: 'Unable to parse response from Cloudflare Images API'
}),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -151,7 +175,8 @@ serve(async (req) => {
console.error('Cloudflare delete error:', deleteResult)
return new Response(
JSON.stringify({
error: 'Failed to delete image',
error: 'Failed to delete image',
message: deleteResult.errors?.[0]?.message || deleteResult.error || 'Unknown error occurred',
details: deleteResult.errors || deleteResult.error
}),
{
@@ -174,7 +199,10 @@ serve(async (req) => {
const authHeader = req.headers.get('Authorization')
if (!authHeader) {
return new Response(
JSON.stringify({ error: 'Authentication required for upload operations' }),
JSON.stringify({
error: 'Authentication required',
message: 'Authentication required for upload operations'
}),
{
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -193,7 +221,10 @@ serve(async (req) => {
if (authError || !user) {
console.error('Auth verification failed:', authError)
return new Response(
JSON.stringify({ error: 'Invalid authentication' }),
JSON.stringify({
error: 'Invalid authentication',
message: 'Authentication token is invalid or expired'
}),
{
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -211,7 +242,10 @@ serve(async (req) => {
if (profileError || !profile) {
console.error('Failed to fetch user profile:', profileError)
return new Response(
JSON.stringify({ error: 'User profile not found' }),
JSON.stringify({
error: 'User profile not found',
message: 'Unable to verify user profile'
}),
{
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -221,7 +255,10 @@ serve(async (req) => {
if (profile.banned) {
return new Response(
JSON.stringify({ error: 'Account suspended. Contact support for assistance.' }),
JSON.stringify({
error: 'Account suspended',
message: 'Account suspended. Contact support for assistance.'
}),
{
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -240,7 +277,10 @@ serve(async (req) => {
// Validate request body structure
if (requestBody && typeof requestBody !== 'object') {
return new Response(
JSON.stringify({ error: 'Request body must be a valid JSON object' }),
JSON.stringify({
error: 'Invalid request body',
message: 'Request body must be a valid JSON object'
}),
{
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -274,7 +314,10 @@ serve(async (req) => {
} catch (fetchError) {
console.error('Network error getting upload URL:', fetchError)
return new Response(
JSON.stringify({ error: 'Network error: Unable to reach Cloudflare Images API' }),
JSON.stringify({
error: 'Network error',
message: 'Unable to reach Cloudflare Images API'
}),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -288,7 +331,10 @@ serve(async (req) => {
} catch (parseError) {
console.error('Failed to parse Cloudflare upload response:', parseError)
return new Response(
JSON.stringify({ error: 'Invalid response from Cloudflare Images API' }),
JSON.stringify({
error: 'Invalid response',
message: 'Unable to parse response from Cloudflare Images API'
}),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -300,7 +346,8 @@ serve(async (req) => {
console.error('Cloudflare direct upload error:', directUploadResult)
return new Response(
JSON.stringify({
error: 'Failed to get upload URL',
error: 'Failed to get upload URL',
message: directUploadResult.errors?.[0]?.message || directUploadResult.error || 'Unable to create upload URL',
details: directUploadResult.errors || directUploadResult.error
}),
{
@@ -328,7 +375,10 @@ serve(async (req) => {
const authHeader = req.headers.get('Authorization')
if (!authHeader) {
return new Response(
JSON.stringify({ error: 'Authentication required for image status operations' }),
JSON.stringify({
error: 'Authentication required',
message: 'Authentication required for image status operations'
}),
{
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -347,7 +397,10 @@ serve(async (req) => {
if (authError || !user) {
console.error('Auth verification failed:', authError)
return new Response(
JSON.stringify({ error: 'Invalid authentication' }),
JSON.stringify({
error: 'Invalid authentication',
message: 'Authentication token is invalid or expired'
}),
{
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -361,7 +414,10 @@ serve(async (req) => {
if (!imageId || imageId.trim() === '') {
return new Response(
JSON.stringify({ error: 'id query parameter is required and must be non-empty' }),
JSON.stringify({
error: 'Missing id parameter',
message: 'id query parameter is required and must be non-empty'
}),
{
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -382,7 +438,10 @@ serve(async (req) => {
} catch (fetchError) {
console.error('Network error fetching image status:', fetchError)
return new Response(
JSON.stringify({ error: 'Network error: Unable to reach Cloudflare Images API' }),
JSON.stringify({
error: 'Network error',
message: 'Unable to reach Cloudflare Images API'
}),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -396,7 +455,10 @@ serve(async (req) => {
} catch (parseError) {
console.error('Failed to parse Cloudflare image status response:', parseError)
return new Response(
JSON.stringify({ error: 'Invalid response from Cloudflare Images API' }),
JSON.stringify({
error: 'Invalid response',
message: 'Unable to parse response from Cloudflare Images API'
}),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -408,7 +470,8 @@ serve(async (req) => {
console.error('Cloudflare image status error:', imageResult)
return new Response(
JSON.stringify({
error: 'Failed to get image status',
error: 'Failed to get image status',
message: imageResult.errors?.[0]?.message || imageResult.error || 'Unable to retrieve image information',
details: imageResult.errors || imageResult.error
}),
{
@@ -447,7 +510,10 @@ serve(async (req) => {
}
return new Response(
JSON.stringify({ error: 'Method not allowed' }),
JSON.stringify({
error: 'Method not allowed',
message: 'HTTP method not supported for this endpoint'
}),
{
status: 405,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
@@ -458,8 +524,8 @@ serve(async (req) => {
console.error('Upload error:', error)
return new Response(
JSON.stringify({
error: 'Internal server error',
message: error instanceof Error ? error.message : 'Unknown error'
error: 'Internal server error',
message: error instanceof Error ? error.message : 'An unexpected error occurred'
}),
{
status: 500,