Skip to content

Commit 1927ba0

Browse files
committed
added a cloudfront function, bryanchasko-com-url-rewrite, redirect help page to services page
1 parent 1ffb32b commit 1927ba0

File tree

3 files changed

+194
-5
lines changed

3 files changed

+194
-5
lines changed

README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,40 @@ hugo --minify --gc
2929
Deploy Command:
3030
hugo && aws s3 sync public/ s3://bryanchasko.com --profile websites-bryanchasko
3131

32+
## 🏗️ Architecture Diagrams
33+
34+
### Website Architecture
35+
36+
```mermaid
37+
architecture-beta
38+
service user(logos:aws-route53)[User Browser]
39+
service dns(logos:aws-route53)[Route 53 DNS]
40+
service cdn(logos:aws-cloudfront)[CloudFront CDN]
41+
service functions(logos:aws-lambda)[CloudFront Functions]
42+
service bucket(logos:aws-s3)[S3 Bucket]
43+
44+
user:R -- L:dns
45+
dns:R -- L:cdn
46+
cdn:B -- T:functions
47+
functions:B -- T:bucket
48+
```
49+
50+
### CI/CD Pipeline
51+
52+
```mermaid
53+
architecture-beta
54+
service github(logos:github-actions)[GitHub]
55+
service runner(logos:github-actions)[GitHub Actions]
56+
service cli(logos:aws-cli)[AWS CLI]
57+
service s3(logos:aws-s3)[S3 Deploy]
58+
service cdn_invalidate(logos:aws-cloudfront)[CloudFront Invalidate]
59+
60+
github:R -- L:runner
61+
runner:B -- T:cli
62+
cli:B -- T:s3
63+
s3:R -- L:cdn_invalidate
64+
```
65+
3266
## 🚀 How to Replicate This Stack for Your Own Site
3367

3468
This project is designed to be reproducible for any Hugo site wanting WebGL visual effects with professional testing.
@@ -51,6 +85,8 @@ This project is designed to be reproducible for any Hugo site wanting WebGL visu
5185
| Baseline Storage | AWS S3 | Visual regression screenshots |
5286
| CI/CD | GitHub Actions | Automated test pipeline |
5387
| CSS Architecture | CSS Custom Properties | 3-palette theming system |
88+
| CDN | AWS CloudFront | Global content delivery |
89+
| Edge Logic | CloudFront Functions | URL rewriting & redirects |
5490

5591
### Quick Start for New Project
5692

@@ -277,6 +313,87 @@ aws iam create-access-key --user-name github-actions-webgl-tests \
277313
# Copy the AccessKeyId and SecretAccessKey into GitHub Secrets
278314
```
279315

316+
### CloudFront Functions for Edge Logic
317+
318+
This site uses **CloudFront Functions** to handle URL rewriting and redirects at the edge (viewer-request stage):
319+
320+
**Current Function: `bryanchasko-com-url-rewrite`**
321+
- Redirects `/help``/services` (case-insensitive)
322+
- Rewrites URLs for SPA routing (e.g., `/blog``/blog/index.html`)
323+
- Executes at CloudFront edge locations (~1ms latency)
324+
- Cost: ~$0.60/month (vs $0.50 per 1M requests for Lambda@Edge)
325+
326+
**Why CloudFront Functions?**
327+
- ✅ No cold starts (unlike Lambda@Edge)
328+
- ✅ Handles redirects without S3 objects
329+
- ✅ Case-insensitive URL matching
330+
- ✅ Cheaper and faster than Lambda@Edge
331+
332+
**To Update the Function:**
333+
```bash
334+
# Edit function code
335+
cat > /tmp/function.js << 'EOF'
336+
function handler(event) {
337+
var request = event.request;
338+
var uri = request.uri.toLowerCase();
339+
340+
// Add your redirects here
341+
if (uri === '/help' || uri === '/help/') {
342+
return {
343+
statusCode: 301,
344+
statusDescription: 'Moved Permanently',
345+
headers: { 'location': { value: '/services' } }
346+
};
347+
}
348+
349+
// URL rewriting for SPA routing
350+
if (!uri.includes('.') && !uri.endsWith('/')) {
351+
request.uri = uri + '/index.html';
352+
}
353+
else if (uri.endsWith('/') && !uri.endsWith('/index.html')) {
354+
request.uri = uri + 'index.html';
355+
}
356+
357+
return request;
358+
}
359+
EOF
360+
361+
# Get current DEVELOPMENT version ETag
362+
DEV_ETAG=$(aws cloudfront get-function \
363+
--name bryanchasko-com-url-rewrite \
364+
--stage DEVELOPMENT \
365+
--profile websites-bryanchasko \
366+
/tmp/dev-function.js 2>&1 | jq -r '.ETag')
367+
368+
# Update DEVELOPMENT version
369+
aws cloudfront update-function \
370+
--name bryanchasko-com-url-rewrite \
371+
--function-code fileb:///tmp/function.js \
372+
--function-config Comment="URL rewriting and redirects",Runtime=cloudfront-js-1.0 \
373+
--if-match "$DEV_ETAG" \
374+
--profile websites-bryanchasko
375+
376+
# Get new ETag and publish to LIVE
377+
NEW_ETAG=$(aws cloudfront get-function \
378+
--name bryanchasko-com-url-rewrite \
379+
--stage DEVELOPMENT \
380+
--profile websites-bryanchasko \
381+
/tmp/dev-function-updated.js 2>&1 | jq -r '.ETag')
382+
383+
aws cloudfront publish-function \
384+
--name bryanchasko-com-url-rewrite \
385+
--if-match "$NEW_ETAG" \
386+
--profile websites-bryanchasko
387+
388+
# Invalidate cache to apply changes
389+
aws cloudfront create-invalidation \
390+
--distribution-id E2E9BSL5RVN6DI \
391+
--paths "/*" \
392+
--profile websites-bryanchasko
393+
```
394+
395+
See [AWS_ARCHITECTURE.md](docs/deployment/AWS_ARCHITECTURE.md#6-cloudfront-functions) for complete CloudFront Functions documentation.
396+
280397
### Common Pitfalls
281398

282399
**Browser cache serving old WebGL code**

docs/deployment/AWS_ARCHITECTURE.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,70 @@ Automatic rollback: If you need to revert to a previous version, S3 keeps versio
202202
- Reduces CloudFront IP addressing complexity
203203
- Industry standard since 2010
204204

205-
### 6. Deployment Endpoints
205+
### 6. CloudFront Functions
206+
207+
**Purpose:** Execute lightweight logic at the edge (viewer-request stage) without Lambda overhead
208+
209+
**Current Functions:**
210+
211+
| Function | Stage | Purpose | Trigger | Source |
212+
|----------|-------|---------|---------|--------|
213+
| `bryanchasko-com-url-rewrite` | LIVE | URL rewriting + redirects | viewer-request | [`infrastructure/cloudfront-url-rewrite-function.js`](../../infrastructure/cloudfront-url-rewrite-function.js) |
214+
215+
**URL Rewrite Function Logic:**
216+
```javascript
217+
function handler(event) {
218+
var request = event.request;
219+
var uri = request.uri.toLowerCase();
220+
221+
// Redirect /help to /services (case-insensitive)
222+
if (uri === '/help' || uri === '/help/') {
223+
return {
224+
statusCode: 301,
225+
statusDescription: 'Moved Permanently',
226+
headers: {
227+
'location': { value: '/services' }
228+
}
229+
};
230+
}
231+
232+
// URL rewriting for SPA routing
233+
if (!uri.includes('.') && !uri.endsWith('/')) {
234+
request.uri = uri + '/index.html';
235+
}
236+
else if (uri.endsWith('/') && !uri.endsWith('/index.html')) {
237+
request.uri = uri + 'index.html';
238+
}
239+
240+
return request;
241+
}
242+
```
243+
244+
**Why CloudFront Functions?**
245+
- ✅ Executes at edge (low latency)
246+
- ✅ No cold starts (unlike Lambda@Edge)
247+
- ✅ Cheaper than Lambda@Edge (~$0.60/month vs $0.50 per 1M requests)
248+
- ✅ Handles redirects without S3 objects
249+
- ✅ Case-insensitive URL matching
250+
251+
**Deployment:**
252+
```bash
253+
# Update function code
254+
aws cloudfront update-function \
255+
--name bryanchasko-com-url-rewrite \
256+
--function-code fileb://./function.js \
257+
--function-config Comment="URL rewriting and redirects",Runtime=cloudfront-js-1.0 \
258+
--if-match [ETAG] \
259+
--profile websites-bryanchasko
260+
261+
# Publish to LIVE
262+
aws cloudfront publish-function \
263+
--name bryanchasko-com-url-rewrite \
264+
--if-match [NEW-ETAG] \
265+
--profile websites-bryanchasko
266+
```
267+
268+
### 7. Deployment Endpoints
206269

207270
**Development Server:**
208271
```

infrastructure/cloudfront-url-rewrite-function.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
function handler(event) {
22
var request = event.request;
3-
var uri = request.uri;
3+
var uri = request.uri.toLowerCase();
44

5-
// If the URI doesn't end with a file extension and doesn't end with /
5+
// Redirect /help to /services
6+
if (uri === '/help' || uri === '/help/') {
7+
return {
8+
statusCode: 301,
9+
statusDescription: 'Moved Permanently',
10+
headers: {
11+
'location': { value: '/services' }
12+
}
13+
};
14+
}
15+
16+
// URL rewriting for SPA routing
617
if (!uri.includes('.') && !uri.endsWith('/')) {
7-
// Append /index.html for directory-style requests
818
request.uri = uri + '/index.html';
919
}
10-
// If it ends with / but not /index.html, append index.html
1120
else if (uri.endsWith('/') && !uri.endsWith('/index.html')) {
1221
request.uri = uri + 'index.html';
1322
}

0 commit comments

Comments
 (0)