Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get notified #19

Merged
merged 20 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions cors-proxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Serverless CORS Proxy

This is a simple serverless CORS proxy that supports `POST` requests.

This is used by the contact form.

## Features

- Supports `POST` requests to a single `PROXY_TARGET_URL`
- Adds CORS headers for cross-origin requests

## Serverless.yaml

See [./serverless.yaml] to use [serverless.com config](https://www.serverless.com/)

## Environment Variables

These are the environment variables used by the handler.

- `PROXY_TARGET_URL`: Where the proxy will redirect the requests to
- `PROXY_ALLOWED_ORIGIN`: (Default `'*'`) The `Access-Control-Allow-Origin` string for the local server.

## Setup

### 1. Running Locally

To run the CORS proxy locally:

1. Run the proxy locally:

```bash
PROXY_TARGET_URL="https://example.com/submit-form" npm run start
```

- Replace `https://example.com/submit-form` with the target URL.

2. Use the following `curl` example to test a `POST` request from your terminal:

```bash
curl -X POST "http://localhost:3000/" \
-d "param1=value1&param2=value2"
```

- Replace `param1=value1&param2=value2` with your actual form data.

### 2. Using with an HTML Form and JavaScript Fetch

You can use this proxy to submit a form using JavaScript `fetch`. Here’s an example:

#### HTML Form:

```html
<form id="exampleForm">
<input type="text" name="param1" value="value1" />
<input type="text" name="param2" value="value2" />
<button type="submit">Submit</button>
</form>

<script>
document.getElementById('exampleForm').addEventListener('submit', async (e) => {
e.preventDefault(); // Prevent the default form submission

const formData = new FormData(e.target);
const params = new URLSearchParams(formData).toString();

try {
const response = await fetch('https://localhost:3000', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params,
});

const result = await response.json();
console.log(result); // Handle the response
} catch (error) {
console.error('Error:', error);
}
});
</script>
```
49 changes: 49 additions & 0 deletions cors-proxy/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import https from 'node:https';
import process from 'node:process';

export const handler = async (event) => {
const targetUrl = process.env.PROXY_TARGET_URL;

const postData = event.body;

const options = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
};

return new Promise((resolve, _reject) => {
const req = https.request(targetUrl, options, (response) => {
let data = '';

// Collect response data
response.on('data', (chunk) => (data += chunk));

// When the request is complete
response.on('end', () => {
resolve({
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
status: response.statusCode,
headers: response.headers,
body: data,
}),
});
});
});

req.on('error', (error) => {
resolve({
statusCode: 500,
body: JSON.stringify({ message: 'Error in request', error: error.message }),
});
});

req.write(postData);
req.end();
});
};
11 changes: 11 additions & 0 deletions cors-proxy/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "cors-proxy",
"type": "module",
"version": "1.0.0",
"description": "A simple CORS proxy for serverless environments.",
"main": "index.mjs",
"scripts": {
"start": "node server.mjs"
},
"dependencies": {}
}
67 changes: 67 additions & 0 deletions cors-proxy/server.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import http from 'http';
import { handler } from './index.mjs';
import url from 'url';
import process from 'node:process';

const allowedOrigin = process.env.PROXY_ALLOWED_ORIGIN || '*';

// Function to parse the HTTP request body
const getRequestBody = (req) => {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', (chunk) => {
body += chunk.toString();
});
req.on('end', () => {
resolve(body);
});
req.on('error', (err) => {
reject(err);
});
});
};

// Start the HTTP server
const server = http.createServer(async (req, res) => {
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', allowedOrigin);
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

// Handle OPTIONS request for CORS preflight
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}

if (req.method === 'POST') {
const parsedUrl = url.parse(req.url, true);
const body = await getRequestBody(req);

// Convert HTTP request into a Lambda event format
const lambdaEvent = {
httpMethod: req.method,
queryStringParameters: parsedUrl.query,
body: body,
};

// Call the handler (same code used in AWS Lambda)
const lambdaResponse = await handler(lambdaEvent);

// Send response
res.writeHead(lambdaResponse.statusCode, {
...lambdaResponse.headers,
'Access-Control-Allow-Origin': '*', // Make sure to include CORS in response too
});
res.end(lambdaResponse.body);
} else {
res.statusCode = 405;
res.end('Only POST requests are supported');
}
});

// Start server on port 3000
server.listen(3000, () => {
console.log('Local server running on http://localhost:3000');
});
18 changes: 18 additions & 0 deletions cors-proxy/serverless.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
service: xyz-form-cors-proxy

frameworkVersion: '4'

provider:
name: aws
runtime: nodejs20.x

functions:
hello:
handler: handler.handler
events:
- http:
method: post
cors:
origin: 'https://*.frequency.xyz'
headers:
- Content-Type
6 changes: 3 additions & 3 deletions src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<link rel="icon" href="%sveltekit.assets%/favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="%sveltekit.assets%/apple-touch-icon.png" />
<link rel="manifest" href="%sveltekit.assets%/manifest.webmanifest" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

<meta property="og:title" content="Frequency" />
<meta property="og:url" content="" />
Expand All @@ -35,8 +35,8 @@
%sveltekit.head%

<!-- Klaro - make sure the config gets loaded before Klaro -->
<script defer type="text/javascript" src="klaro-config.js"></script>
<script defer type="text/javascript" src="klaro.js"></script>
<script defer type="text/javascript" src="/klaro-config.js"></script>
<script defer type="text/javascript" src="/klaro.js"></script>
<!-- Matomo -->
<script>
var _paq = (window._paq = window._paq || []);
Expand Down
2 changes: 2 additions & 0 deletions src/components/Footer.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<footer class="freq-container flex h-[50px] items-center justify-end gap-3 text-xs font-semibold">
<div>© {new Date().getFullYear()} Frequency Network Foundation</div>
<span class="h-4 w-[2px] bg-black" />
<a href="/privacy">Privacy Policy</a>
<span class="h-4 w-[2px] bg-black" />
<div class="whitespace-nowrap">All Rights Reserved</div>
</footer>
Loading