Skip to content

Commit b0b7394

Browse files
authored
Get notified (#19)
# Description Adds a get notified tag and form and privacy policy - Closes #18 ## Type of change Please delete options that are not relevant. - [x] Content Change (Required approval in Slack: `#frequency-xyz` and remember that changes along the way trigger re-approval.) - [x] Approved HE (Required for technical wording) - [x] Approved CM - [x] New Feature # How to Test? Please describe how to test that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration 1. Pull and run locally 2. See that the get notified button on the right doesn't cover important text 3. Click the button and see that it loads the form 4. Form errors 5. Thank you after submitting the form # Checklist: - [x] I have performed a self-review of my code - [ ] I have commented my code & PR, particularly in hard-to-understand areas - [x] I have checked at all the breakpoints to make sure it works on all screen sizes
1 parent eeb64b4 commit b0b7394

27 files changed

+1050
-63
lines changed

cors-proxy/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Serverless CORS Proxy
2+
3+
This is a simple serverless CORS proxy that supports `POST` requests.
4+
5+
This is used by the contact form.
6+
7+
## Features
8+
9+
- Supports `POST` requests to a single `PROXY_TARGET_URL`
10+
- Adds CORS headers for cross-origin requests
11+
12+
## Serverless.yaml
13+
14+
See [./serverless.yaml] to use [serverless.com config](https://www.serverless.com/)
15+
16+
## Environment Variables
17+
18+
These are the environment variables used by the handler.
19+
20+
- `PROXY_TARGET_URL`: Where the proxy will redirect the requests to
21+
- `PROXY_ALLOWED_ORIGIN`: (Default `'*'`) The `Access-Control-Allow-Origin` string for the local server.
22+
23+
## Setup
24+
25+
### 1. Running Locally
26+
27+
To run the CORS proxy locally:
28+
29+
1. Run the proxy locally:
30+
31+
```bash
32+
PROXY_TARGET_URL="https://example.com/submit-form" npm run start
33+
```
34+
35+
- Replace `https://example.com/submit-form` with the target URL.
36+
37+
2. Use the following `curl` example to test a `POST` request from your terminal:
38+
39+
```bash
40+
curl -X POST "http://localhost:3000/" \
41+
-d "param1=value1&param2=value2"
42+
```
43+
44+
- Replace `param1=value1&param2=value2` with your actual form data.
45+
46+
### 2. Using with an HTML Form and JavaScript Fetch
47+
48+
You can use this proxy to submit a form using JavaScript `fetch`. Here’s an example:
49+
50+
#### HTML Form:
51+
52+
```html
53+
<form id="exampleForm">
54+
<input type="text" name="param1" value="value1" />
55+
<input type="text" name="param2" value="value2" />
56+
<button type="submit">Submit</button>
57+
</form>
58+
59+
<script>
60+
document.getElementById('exampleForm').addEventListener('submit', async (e) => {
61+
e.preventDefault(); // Prevent the default form submission
62+
63+
const formData = new FormData(e.target);
64+
const params = new URLSearchParams(formData).toString();
65+
66+
try {
67+
const response = await fetch('https://localhost:3000', {
68+
method: 'POST',
69+
headers: {
70+
'Content-Type': 'application/x-www-form-urlencoded',
71+
},
72+
body: params,
73+
});
74+
75+
const result = await response.json();
76+
console.log(result); // Handle the response
77+
} catch (error) {
78+
console.error('Error:', error);
79+
}
80+
});
81+
</script>
82+
```

cors-proxy/index.mjs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import https from 'node:https';
2+
import process from 'node:process';
3+
4+
export const handler = async (event) => {
5+
const targetUrl = process.env.PROXY_TARGET_URL;
6+
7+
const postData = event.body;
8+
9+
const options = {
10+
method: 'POST',
11+
headers: {
12+
'Content-Type': 'application/x-www-form-urlencoded',
13+
},
14+
};
15+
16+
return new Promise((resolve, _reject) => {
17+
const req = https.request(targetUrl, options, (response) => {
18+
let data = '';
19+
20+
// Collect response data
21+
response.on('data', (chunk) => (data += chunk));
22+
23+
// When the request is complete
24+
response.on('end', () => {
25+
resolve({
26+
statusCode: 200,
27+
headers: {
28+
'Content-Type': 'application/json',
29+
},
30+
body: JSON.stringify({
31+
status: response.statusCode,
32+
headers: response.headers,
33+
body: data,
34+
}),
35+
});
36+
});
37+
});
38+
39+
req.on('error', (error) => {
40+
resolve({
41+
statusCode: 500,
42+
body: JSON.stringify({ message: 'Error in request', error: error.message }),
43+
});
44+
});
45+
46+
req.write(postData);
47+
req.end();
48+
});
49+
};

cors-proxy/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "cors-proxy",
3+
"type": "module",
4+
"version": "1.0.0",
5+
"description": "A simple CORS proxy for serverless environments.",
6+
"main": "index.mjs",
7+
"scripts": {
8+
"start": "node server.mjs"
9+
},
10+
"dependencies": {}
11+
}

cors-proxy/server.mjs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import http from 'http';
2+
import { handler } from './index.mjs';
3+
import url from 'url';
4+
import process from 'node:process';
5+
6+
const allowedOrigin = process.env.PROXY_ALLOWED_ORIGIN || '*';
7+
8+
// Function to parse the HTTP request body
9+
const getRequestBody = (req) => {
10+
return new Promise((resolve, reject) => {
11+
let body = '';
12+
req.on('data', (chunk) => {
13+
body += chunk.toString();
14+
});
15+
req.on('end', () => {
16+
resolve(body);
17+
});
18+
req.on('error', (err) => {
19+
reject(err);
20+
});
21+
});
22+
};
23+
24+
// Start the HTTP server
25+
const server = http.createServer(async (req, res) => {
26+
// Set CORS headers
27+
res.setHeader('Access-Control-Allow-Origin', allowedOrigin);
28+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
29+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
30+
31+
// Handle OPTIONS request for CORS preflight
32+
if (req.method === 'OPTIONS') {
33+
res.writeHead(200);
34+
res.end();
35+
return;
36+
}
37+
38+
if (req.method === 'POST') {
39+
const parsedUrl = url.parse(req.url, true);
40+
const body = await getRequestBody(req);
41+
42+
// Convert HTTP request into a Lambda event format
43+
const lambdaEvent = {
44+
httpMethod: req.method,
45+
queryStringParameters: parsedUrl.query,
46+
body: body,
47+
};
48+
49+
// Call the handler (same code used in AWS Lambda)
50+
const lambdaResponse = await handler(lambdaEvent);
51+
52+
// Send response
53+
res.writeHead(lambdaResponse.statusCode, {
54+
...lambdaResponse.headers,
55+
'Access-Control-Allow-Origin': '*', // Make sure to include CORS in response too
56+
});
57+
res.end(lambdaResponse.body);
58+
} else {
59+
res.statusCode = 405;
60+
res.end('Only POST requests are supported');
61+
}
62+
});
63+
64+
// Start server on port 3000
65+
server.listen(3000, () => {
66+
console.log('Local server running on http://localhost:3000');
67+
});

cors-proxy/serverless.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
service: xyz-form-cors-proxy
2+
3+
frameworkVersion: '4'
4+
5+
provider:
6+
name: aws
7+
runtime: nodejs20.x
8+
9+
functions:
10+
hello:
11+
handler: handler.handler
12+
events:
13+
- http:
14+
method: post
15+
cors:
16+
origin: 'https://*.frequency.xyz'
17+
headers:
18+
- Content-Type

src/app.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<link rel="icon" href="%sveltekit.assets%/favicon.svg" type="image/svg+xml" />
99
<link rel="apple-touch-icon" href="%sveltekit.assets%/apple-touch-icon.png" />
1010
<link rel="manifest" href="%sveltekit.assets%/manifest.webmanifest" />
11-
<meta name="viewport" content="width=device-width, initial-scale=1" />
11+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
1212

1313
<meta property="og:title" content="Frequency" />
1414
<meta property="og:url" content="" />
@@ -35,8 +35,8 @@
3535
%sveltekit.head%
3636

3737
<!-- Klaro - make sure the config gets loaded before Klaro -->
38-
<script defer type="text/javascript" src="klaro-config.js"></script>
39-
<script defer type="text/javascript" src="klaro.js"></script>
38+
<script defer type="text/javascript" src="/klaro-config.js"></script>
39+
<script defer type="text/javascript" src="/klaro.js"></script>
4040
<!-- Matomo -->
4141
<script>
4242
var _paq = (window._paq = window._paq || []);

src/components/Footer.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<footer class="freq-container flex h-[50px] items-center justify-end gap-3 text-xs font-semibold">
22
<div>© {new Date().getFullYear()} Frequency Network Foundation</div>
33
<span class="h-4 w-[2px] bg-black" />
4+
<a href="/privacy">Privacy Policy</a>
5+
<span class="h-4 w-[2px] bg-black" />
46
<div class="whitespace-nowrap">All Rights Reserved</div>
57
</footer>

0 commit comments

Comments
 (0)