mirror of
https://github.com/sune-org/D1P.git
synced 2026-01-14 00:28:00 +00:00
refactor
This commit is contained in:
82
src/index.js
82
src/index.js
@@ -1,78 +1,32 @@
|
|||||||
|
// Define shared headers for responses.
|
||||||
// Helper function for CORS headers
|
const headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type' };
|
||||||
const corsHeaders = {
|
|
||||||
'Access-Control-Allow-Origin': '*',
|
|
||||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
||||||
'Access-Control-Allow-Headers': 'Content-Type',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handler for CORS preflight OPTIONS requests
|
|
||||||
function handleOptions(request) {
|
|
||||||
if (
|
|
||||||
request.headers.get('Origin') !== null &&
|
|
||||||
request.headers.get('Access-Control-Request-Method') !== null &&
|
|
||||||
request.headers.get('Access-Control-Request-Headers') !== null
|
|
||||||
) {
|
|
||||||
// Handle CORS pre-flight request.
|
|
||||||
return new Response(null, {
|
|
||||||
headers: {
|
|
||||||
...corsHeaders,
|
|
||||||
'Access-Control-Max-Age': '86400',
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Handle standard OPTIONS request.
|
|
||||||
return new Response(null, { headers: { 'Allow': 'POST, OPTIONS' } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async fetch(request, env, ctx) {
|
async fetch(request, env) {
|
||||||
if (request.method === 'OPTIONS') {
|
// Handle CORS preflight requests.
|
||||||
return handleOptions(request);
|
if (request.method === 'OPTIONS') return new Response(null, { headers: { ...headers, 'Access-Control-Allow-Methods': 'POST', 'Access-Control-Max-Age': '86400' }});
|
||||||
}
|
|
||||||
|
|
||||||
if (request.method !== 'POST') {
|
// Enforce POST and JSON content-type.
|
||||||
return new Response('Method Not Allowed. This proxy only accepts POST requests.', { status: 405, headers: corsHeaders });
|
if (request.method !== 'POST') return new Response('Method Not Allowed', { status: 405, headers });
|
||||||
}
|
if (!request.headers.get('content-type')?.includes('application/json')) return new Response(JSON.stringify({ error: 'Request must be application/json' }), { status: 400, headers });
|
||||||
|
|
||||||
if (request.headers.get('content-type') !== 'application/json') {
|
|
||||||
return new Response(JSON.stringify({ error: 'Request must be application/json' }), { status: 400, headers: corsHeaders });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { query, params = [] } = await request.json();
|
const { query, params = [] } = await request.json();
|
||||||
|
|
||||||
if (!query || typeof query !== 'string') {
|
// Basic query validation and security checks.
|
||||||
return new Response(JSON.stringify({ error: 'Invalid request: "query" property must be a non-empty string.' }), { status: 400, headers: corsHeaders });
|
if (!query || typeof query !== 'string' || query.trim().includes(';')) return new Response(JSON.stringify({ error: 'Invalid or forbidden query provided.' }), { status: 400, headers });
|
||||||
}
|
if (!['select', 'insert', 'explain'].some(verb => query.trim().toLowerCase().startsWith(verb))) return new Response(JSON.stringify({ error: 'Forbidden: Only SELECT, INSERT, and EXPLAIN are permitted.' }), { status: 403, headers });
|
||||||
|
|
||||||
// --- CRITICAL: Sanitize and validate the query ---
|
// Execute the prepared statement against D1.
|
||||||
const sanitizedQuery = query.trim().toLowerCase();
|
const result = await env.D1_SUNE.prepare(query).bind(...params).all();
|
||||||
|
|
||||||
// 1. Disallow multiple statements
|
// Return results with correct content-type.
|
||||||
if (query.trim().includes(';')) {
|
return new Response(JSON.stringify(result, null, 2), { headers: { ...headers, 'Content-Type': 'application/json' } });
|
||||||
return new Response(JSON.stringify({ error: 'Forbidden: Multiple SQL statements are not allowed.' }), { status: 403, headers: corsHeaders });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Only allow approved starting verbs
|
|
||||||
const allowedVerbs = ['select', 'insert', 'update', 'explain'];
|
|
||||||
const isAllowedVerb = allowedVerbs.some(verb => sanitizedQuery.startsWith(verb));
|
|
||||||
|
|
||||||
if (!isAllowedVerb) {
|
|
||||||
return new Response(JSON.stringify({ error: 'Forbidden: Operation not allowed. Only SELECT, INSERT, UPDATE, and EXPLAIN are permitted.' }), { status: 403, headers: corsHeaders });
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Execute the query ---
|
|
||||||
const stmt = env.DB.prepare(query).bind(...params);
|
|
||||||
const result = await stmt.all();
|
|
||||||
|
|
||||||
return new Response(JSON.stringify(result, null, 2), { headers: corsHeaders });
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Catch D1 errors and other exceptions
|
// Catch and format any runtime errors.
|
||||||
const errorMsg = e.cause ? e.cause.message : e.message;
|
const errorMsg = e.cause?.message ?? e.message;
|
||||||
return new Response(JSON.stringify({ error: 'Database Error', details: errorMsg }), { status: 500, headers: corsHeaders });
|
return new Response(JSON.stringify({ error: 'Database Error', details: errorMsg }), { status: 500, headers });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user