A Cloudflare Worker that provides various form-related services including Netlify integration and email functionality.
- Netlify OAuth integration
- Form deployment to Netlify
- Email form data via Mailrelay
- AI Gateway proxy functionality with file upload support
- SQLite database storage for JSON data
The worker acts as a secure proxy for various LLM APIs (OpenAI, Google Gemini, etc.) with support for both JSON requests and file uploads.
Set the following environment variables in your Cloudflare Worker:
OPENAI_APIKEY=your_openai_api_key
GEMINI_APIKEY=your_gemini_api_key
The proxy supports the following file types for upload:
- PDF:
application/pdf
- Images:
image/jpeg
,image/jpg
,image/png
,image/gif
,image/webp
,image/svg+xml
- Maximum file size: 10MB
- Multiple files can be uploaded in a single request
Send JSON requests to LLM APIs:
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'system-key': 'openai'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{ role: 'user', content: 'Hello, how are you?' }
]
})
});
const result = await response.json();
console.log(result);
Send files along with all the same parameters that JSON requests support:
const formData = new FormData();
formData.append('file', pdfFile); // or imageFile
formData.append('model', 'gpt-4');
formData.append('messages', JSON.stringify([
{ role: 'user', content: 'Analyze this document and summarize the key points' }
]));
formData.append('temperature', '0.7');
formData.append('max_tokens', '1000');
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'system-key': 'openai'
},
body: formData
});
const result = await response.json();
console.log(result);
Upload multiple files in a single request with all API parameters:
const formData = new FormData();
formData.append('file1', pdfFile);
formData.append('file2', imageFile);
formData.append('model', 'gpt-4');
formData.append('messages', JSON.stringify([
{ role: 'user', content: 'Compare these documents and images' }
]));
formData.append('temperature', '0.5');
formData.append('max_tokens', '2000');
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'system-key': 'openai'
},
body: formData
});
api-url
: The base URL of the target LLM API (e.g.,https://api.openai.com/v1
)api-path
: The API endpoint path (e.g.,chat/completions
,images/generations
)
system-key
: Use predefined API keys (openai
orgemini
)Authorization
: Custom authorization header (if not using system-key)Origin
: Required for CORS validation
// Using system key
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'system-key': 'openai'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }]
})
});
// Using custom authorization
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'Authorization': 'Bearer your-custom-openai-key'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }]
})
});
// Using system key
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'api-url': 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent',
'api-path': '-',
'system-key': 'gemini'
},
body: JSON.stringify({
contents: [{
parts: [{ text: 'Hello, how are you?' }]
}]
})
});
{
"error": "Invalid file type",
"message": "File type text/plain is not supported. Allowed types: PDF, JPEG, PNG, GIF, WebP, SVG",
"allowedTypes": ["application/pdf", "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp", "image/svg+xml"]
}
{
"error": "File too large",
"message": "File size must be less than 10MB"
}
{
"error": "Invalid form data",
"message": "Failed to parse multipart form data"
}
{
"error": "Missing api-url or api-path",
"message": "Missing api-url or api-path null null"
}
The proxy includes CORS headers for the following origins:
https://app.prompttoform.ai/
https://demo.codeflowcanvas.io/
https://ocif-generator.vercel.app/
*.prompttoform.pages.dev
(for PR branches)
const formData = new FormData();
formData.append('file', pdfDocument);
formData.append('model', 'gpt-4');
formData.append('messages', JSON.stringify([
{ role: 'user', content: 'Extract all the key information from this document and format it as a structured summary' }
]));
formData.append('temperature', '0.3');
formData.append('max_tokens', '1500');
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'system-key': 'openai'
},
body: formData
});
const formData = new FormData();
formData.append('file', imageFile);
formData.append('model', 'gpt-4-vision-preview');
formData.append('messages', JSON.stringify([
{ role: 'user', content: 'Describe this image in detail, including any text visible in the image' }
]));
formData.append('max_tokens', '500');
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'system-key': 'openai'
},
body: formData
});
const formData = new FormData();
formData.append('file', codeFile);
formData.append('model', 'gpt-4');
formData.append('messages', JSON.stringify([
{ role: 'user', content: 'Review this code for potential bugs, security issues, and suggest improvements' }
]));
formData.append('temperature', '0.1');
formData.append('max_tokens', '2000');
const response = await fetch('https://your-worker.your-subdomain.workers.dev', {
method: 'POST',
headers: {
'api-url': 'https://api.openai.com/v1',
'api-path': 'chat/completions',
'system-key': 'openai'
},
body: formData
});
The worker includes an endpoint for sending form data via email using Mailrelay.
Set the following environment variables in your Cloudflare Worker:
MAILRELAY_API_KEY=your_mailrelay_api_key
MAILRELAY_DOMAIN=your_mailrelay_domain
Send a POST request to /email/form-data
with the following JSON structure:
{
"to": "[email protected]",
"subject": "New Form Submission",
"formData": {
"name": "John Doe",
"email": "[email protected]",
"message": "Hello world"
},
"from": "[email protected]"
}
to
(required): Email address of the recipientsubject
(optional): Email subject line (defaults to "New Form Submission")formData
(required): Object containing the form data to be sentfrom
(optional): Sender email address (defaults to "[email protected]")
Success response:
{
"success": true,
"message": "Email sent successfully",
"messageId": "message_id_from_mailrelay"
}
Error response:
{
"success": false,
"message": "Error description",
"error": "Detailed error information"
}
const response = await fetch('https://your-worker.your-subdomain.workers.dev/email/form-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: '[email protected]',
subject: 'Contact Form Submission',
formData: {
name: 'Jane Smith',
email: '[email protected]',
phone: '+1234567890',
message: 'I would like to inquire about your services.'
}
})
});
const result = await response.json();
console.log(result);
npm install
npm run dev
npm run deploy
The worker includes a comprehensive API for storing and managing JSON data in a Cloudflare D1 SQLite database.
- Create a D1 database in your Cloudflare dashboard
- Update the
database_id
inwrangler.jsonc
with your actual database ID - Deploy the worker
Store JSON data in the database.
Request:
{
"name": "John Doe",
"email": "[email protected]",
"message": "Hello world",
"customField": "any value"
}
Response:
{
"success": true,
"message": "Data stored successfully",
"data": {
"id": 1,
"data": {
"name": "John Doe",
"email": "[email protected]",
"message": "Hello world",
"customField": "any value"
},
"created_at": "2023-01-01T00:00:00.000Z",
"updated_at": "2023-01-01T00:00:00.000Z"
}
}
Retrieve all stored data with pagination support.
Query Parameters:
limit
(optional): Number of records to return (default: 100)offset
(optional): Number of records to skip (default: 0)
Response:
{
"success": true,
"message": "Data retrieved successfully",
"data": [
{
"id": 2,
"data": {
"name": "Jane Smith",
"email": "[email protected]"
},
"created_at": "2023-01-02T00:00:00.000Z",
"updated_at": "2023-01-02T00:00:00.000Z"
},
{
"id": 1,
"data": {
"name": "John Doe",
"email": "[email protected]"
},
"created_at": "2023-01-01T00:00:00.000Z",
"updated_at": "2023-01-01T00:00:00.000Z"
}
],
"pagination": {
"limit": 100,
"offset": 0,
"count": 2
}
}
Retrieve a specific record by ID.
Response:
{
"success": true,
"message": "Data retrieved successfully",
"data": {
"id": 1,
"data": {
"name": "John Doe",
"email": "[email protected]"
},
"created_at": "2023-01-01T00:00:00.000Z",
"updated_at": "2023-01-01T00:00:00.000Z"
}
}
Update an existing record.
Request:
{
"name": "John Updated",
"email": "[email protected]"
}
Response:
{
"success": true,
"message": "Data updated successfully",
"data": {
"id": 1,
"data": {
"name": "John Updated",
"email": "[email protected]"
},
"updated_at": "2023-01-01T12:00:00.000Z"
}
}
Delete a specific record.
Response:
{
"success": true,
"message": "Data deleted successfully"
}
// Store new data
const response = await fetch('https://your-worker.your-subdomain.workers.dev/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'John Doe',
email: '[email protected]',
message: 'Hello from the form!'
})
});
const result = await response.json();
console.log('Stored data ID:', result.data.id);
// Retrieve all data
const allDataResponse = await fetch('https://your-worker.your-subdomain.workers.dev/api/data?limit=10&offset=0');
const allData = await allDataResponse.json();
console.log('All data:', allData.data);
// Get specific data
const specificDataResponse = await fetch('https://your-worker.your-subdomain.workers.dev/api/data/1');
const specificData = await specificDataResponse.json();
console.log('Specific data:', specificData.data);
// Update data
const updateResponse = await fetch('https://your-worker.your-subdomain.workers.dev/api/data/1', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'John Updated',
email: '[email protected]'
})
});
// Delete data
const deleteResponse = await fetch('https://your-worker.your-subdomain.workers.dev/api/data/1', {
method: 'DELETE'
});
All endpoints return consistent error responses:
{
"success": false,
"message": "Error description",
"error": "Detailed error information"
}
Common HTTP status codes:
400
- Bad Request (invalid JSON, missing required fields)404
- Not Found (record doesn't exist)500
- Internal Server Error (database errors)