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

[Rules] New templates for Transform Rules and Snippets #18481

Merged
merged 2 commits into from
Nov 29, 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
100 changes: 100 additions & 0 deletions src/content/docs/rules/snippets/examples/custom-cache.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
type: example
summary: Control cache programmatically. Use this template to optimize performance and implement custom caching strategies.
goal:
- Other
operation:
- Cache
products:
- Snippets
pcx_content_type: example
title: Custom cache
description: Store, retrieve, and remove assets from cache programmatically. Use this template to optimize performance and implement custom caching strategies.
---

```js
// Define configurable cache duration in seconds (default: 30 days)
const CACHE_DURATION_SECONDS = 30 * 24 * 60 * 60;

// Define which parts of the request to include in the cache key
const USE_PATH = true; // Include path in the cache key
const USE_QUERY_STRING = true; // Include query string in the cache key
const INCLUDE_HEADERS = ["User-Agent"]; // Headers to include in the cache key

export default {
async fetch(request, env, ctx) {
// Generate a custom cache key based on user preferences
const cacheKey = createCacheKey(request);
console.log(`Retrieving cache for: ${cacheKey.url}.`)

// Access the default Cache API
const cache = caches.default;

// Attempt to retrieve the cached response
let response = await cache.match(cacheKey);

if (!response) {
// Cache miss: Fetch the asset from the origin
console.log(`Cache miss for: ${cacheKey.url}. Fetching from origin...`);
response = await fetch(request);

// Wrap the origin response for caching
response = new Response(response.body, response);

// Set Cache-Control headers to define the TTL
response.headers.set("Cache-Control", `s-maxage=${CACHE_DURATION_SECONDS}`);
response.headers.set("x-snippets-cache", "stored");

// Store the response in the cache
await cache.put(cacheKey, response.clone());
} else {
// Cache hit: Return the cached response
console.log(`Cache hit for: ${cacheKey.url}.`);
response = new Response(response.body, response);
response.headers.set("x-snippets-cache", "hit");

// Optionally check if the cache should expire based on age
const ageHeader = response.headers.get("Age");
if (ageHeader && parseInt(ageHeader, 10) > CACHE_DURATION_SECONDS) {
console.log(`Cache expired for: ${cacheKey.url}. Deleting cached response...`);
await cache.delete(cacheKey);
response.headers.set("x-snippets-cache", "deleted");
}
}

// Return the response to the client
return response;
},
};

/**
* Function to create a custom cache key based on request properties
* @param {Request} request - The incoming request object
* @returns {Request} - A valid cache key based on the URL
*/
function createCacheKey(request) {
const url = new URL(request.url); // Use the request's base URL
const cacheKey = new URL(url.origin); // Start with the origin (scheme + hostname)

// Optionally include the path
if (USE_PATH) {
cacheKey.pathname = url.pathname;
}

// Optionally include the query string
if (USE_QUERY_STRING) {
cacheKey.search = url.search;
}

// Optionally include specific headers
if (INCLUDE_HEADERS.length > 0) {
const headerParts = INCLUDE_HEADERS.map(header => `${header}=${request.headers.get(header) || ""}`).join("&");
cacheKey.searchParams.append("headers", headerParts);
}

// Return the constructed URL as the cache key
return new Request(cacheKey.toString(), {
method: "GET"
});
}
```
39 changes: 39 additions & 0 deletions src/content/docs/rules/snippets/examples/hex-timestamp.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
type: example
summary: Add a custom header to requests sent to the origin server with the current timestamp in hexadecimal format.
goal:
- Manage headers
operation:
- Request modification
products:
- Snippets
pcx_content_type: example
title: Add HEX timestamp to a request header
description: Add a custom header to requests sent to the origin server with the current timestamp in hexadecimal format for debugging, tracking, or custom routing purposes.
---

```js
export default {
async fetch(request) {
// Get the current timestamp
const timestamp = Date.now();

// Convert the timestamp to hexadecimal format
const hexTimestamp = timestamp.toString(16);

// Clone the request and add the custom header
const modifiedRequest = new Request(request, {
headers: new Headers(request.headers)
});
modifiedRequest.headers.set("X-Hex-Timestamp", hexTimestamp);

// Log the custom header for debugging
console.log(`X-Hex-Timestamp: ${hexTimestamp}`);

// Pass the modified request to the origin
const response = await fetch(modifiedRequest);

return response;
},
};
```
102 changes: 102 additions & 0 deletions src/content/docs/rules/snippets/examples/maintenance.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
type: example
summary: Serve a custom maintenance page. Ideal for downtime notifications, planned maintenance, or emergency messages.
goal:
- Routing
operation:
- Redirect
products:
- Snippets
pcx_content_type: example
title: Maintenance page
description: Serve a custom maintenance page instead of fetching content from the origin server or cache. Ideal for downtime notifications, planned maintenance, or emergency messages.
---

```js
// Define your customizable inputs
const title = "We'll Be Right Back!";
const message = "Our site is currently undergoing scheduled maintenance. We’re working hard to bring you a better experience. Thank you for your patience and understanding.";
const estimatedTime = "1 hour";
const contactEmail = "[email protected]";
const contactPhone = "+1 234 567 89";

export default {
async fetch(request) {
// Serve the maintenance page as a response
return new Response(generateMaintenancePage(), {
headers: { "Content-Type": "text/html" },
});
},
};

function generateMaintenancePage() {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f4f4f4;
color: #333;
text-align: center;
}
.container {
max-width: 600px;
padding: 20px;
}
h1 {
font-size: 2rem;
color: #0056b3;
margin-bottom: 10px;
}
p {
font-size: 1rem;
margin-bottom: 20px;
line-height: 1.5;
}
.contact {
margin-top: 20px;
font-size: 0.9rem;
color: #666;
}
.contact a {
color: #0056b3;
text-decoration: none;
}
.contact a:hover {
text-decoration: underline;
}
.logo {
margin: 20px 0;
max-width: 150px;
}
.timer {
font-weight: bold;
color: #e63946;
}
</style>
</head>
<body>
<div class="container">
<h1>${title}</h1>
<p>${message}</p>
<p>If all goes to plan, we'll be back online in <span class="timer">${estimatedTime}</span>. 🚀</p>
<p class="contact">
Need help? Reach out to us at <a href="mailto:${contactEmail}">${contactEmail}</a>
or call us at <a href="tel:${contactPhone}">${contactPhone}</a>.
</p>
</div>
</body>
</html>
`;
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ operation:
products:
- Snippets
pcx_content_type: example
title: Redirect `403 Forbidden` to a different page
title: Redirect 403 Forbidden to a different page
description: If origin responded with `403 Forbidden` error code, redirect to
different page.
---
Expand Down
54 changes: 54 additions & 0 deletions src/content/docs/rules/snippets/examples/rewrite-site-links.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
type: example
summary: Dynamically rewrite links in HTML responses.
goal:
- Other
operation:
- Response modification
products:
- Snippets
pcx_content_type: example
title: Rewrite links on HTML pages
description: Dynamically rewrite links in HTML responses. This is useful for site migrations and branding updates.
---

```js
export default {
async fetch(request) {
// Define the old hostname here.
const OLD_URL = "oldsite.com";
// Then add your new hostname that should replace the old one.
const NEW_URL = "newsite.com";

class AttributeRewriter {
constructor(attributeName) {
this.attributeName = attributeName;
}
element(element) {
const attribute = element.getAttribute(this.attributeName);
if (attribute) {
element.setAttribute(
this.attributeName,
attribute.replace(OLD_URL, NEW_URL),
);
}
}
}

const rewriter = new HTMLRewriter()
.on("a", new AttributeRewriter("href"))
.on("img", new AttributeRewriter("src"));

const res = await fetch(request);
const contentType = res.headers.get("Content-Type");

// If the response is HTML, it can be transformed with
// HTMLRewriter -- otherwise, it should pass through
if (contentType.startsWith("text/html")) {
return rewriter.transform(res);
} else {
return res;
}
},
};
```
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,19 @@ description: Create a rewrite URL rule (part of Transform Rules) to rewrite

import { Example } from "~/components";

To rewrite everything under `/blog/<PATH>` to `/marketing/<PATH>` you must modify the first component of the path (`/blog/`). Create a rewrite URL rule and use the [`regex_replace()`](/ruleset-engine/rules-language/functions/#regex_replace) function for this purpose:
To rewrite everything under `/blog/<PATH>` to `/marketing/<PATH>`, create a new rewrite URL rule and define a dynamic URL path rewrite using [wildcard pattern parameters](/rules/transform/url-rewrite/create-dashboard/#wildcard-pattern-parameters):

<Example>

Text in **Expression Editor**:
**When incoming requests match**

```txt
starts_with(http.request.uri.path, "/blog/")
```
- **Wildcard pattern**
- **Request URL**: `https://<YOUR_HOSTNAME>/blog/*`

Text after **Path** > **Rewrite to...** > _Dynamic_:

```txt
regex_replace(http.request.uri.path, "^/blog/", "/marketing/")
```
**Then rewrite the path and/or query**
- **Target path**: `/blog/*`
- **Rewrite to**: `/marketing/${1}`

</Example>

The `regex_replace()` function matches the path component on a regular expression (`^/blog/`) and then provides a replacement for that match (`/marketing/`).
Make sure to replace `<YOUR_HOSTNAME>` with your actual hostname and adjust the example paths according to your setup.
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,19 @@ description: Create a rewrite URL rule (part of Transform Rules) to rewrite any

import { Example } from "~/components";

To rewrite all requests to `/news/2012/...` to `/archive/news/2012/...` you must add a reference to the content of the original URL. Create a new rewrite URL rule and define a dynamic URL path rewrite using an expression:
To rewrite all requests to `/news/2012/...` to `/archive/news/2012/...` you must add a reference to the content of the original URL. Create a new rewrite URL rule and define a dynamic URL path rewrite using [wildcard pattern parameters](/rules/transform/url-rewrite/create-dashboard/#wildcard-pattern-parameters):

<Example>

Text in **Expression Editor**:
**When incoming requests match**

```txt
starts_with(http.request.uri.path, "/news/2012/")
```
- **Wildcard pattern**
- **Request URL**: `https://<YOUR_HOSTNAME>/news/2012/*`

Text after **Path** > **Rewrite to...** > _Dynamic_:

```txt
concat("/archive", http.request.uri.path)
```
**Then rewrite the path and/or query**
- **Target path**: `/news/2012/*`
- **Rewrite to**: `/archive/news/2012/${1}`

</Example>

The filter uses the [`starts_with()`](/ruleset-engine/rules-language/functions/#starts_with) function all paths starting with `/news/2012/`. The dynamic path rewrite uses the [`concat()`](/ruleset-engine/rules-language/functions/#concat) function to concatenate a prefix to the original URL path of the HTTP request.
Make sure to replace `<YOUR_HOSTNAME>` with your actual hostname and adjust the example paths according to your setup.
Loading
Loading