9
0
mirror of https://github.com/donlon/cloudflare-error-page.git synced 2026-01-06 15:41:45 +00:00

misc updates

This commit is contained in:
Anthony Donlon
2025-12-27 01:39:06 +08:00
parent e2226ff5bb
commit f2f4a8223d
11 changed files with 69 additions and 52 deletions

View File

@@ -4,7 +4,7 @@
## What does this project do?
This project creates customized error pages that mimics the well-known Cloudflare error page. You can also embed it into your website.
This project creates customized error pages that mimic the well-known Cloudflare error page. You can also embed it into your own website.
## Online Editor
@@ -16,7 +16,7 @@ Here's an online editor to create customized error pages and example server apps
### Python
Install `cloudflare-error-page` with pip.
Install `cloudflare-error-page` using pip.
``` Bash
# Install from PyPI
@@ -26,7 +26,7 @@ pip install cloudflare-error-page
pip install git+https://github.com/donlon/cloudflare-error-page.git
```
Then you can generate an error page using the `render` function. ([example.py](examples/example.py))
Then an error page can be generated using the `render` function provided by the package. ([example.py](examples/example.py))
``` Python
import webbrowser
@@ -48,7 +48,7 @@ error_page = render_cf_error_page({
"status": 'ok',
"location": 'example.com',
},
# can be 'browser', 'cloudflare', or 'host'
# Position of the error indicator, valid options are 'browser', 'cloudflare', and 'host'
'error_source': 'cloudflare',
# Texts shown in the bottom of the page
@@ -56,9 +56,11 @@ error_page = render_cf_error_page({
'what_can_i_do': '<p>Please try again in a few minutes.</p>',
})
# Write generated webpage to file
with open('error.html', 'w') as f:
f.write(error_page)
# Open the generated page in browser
webbrowser.open('error.html')
```
@@ -83,14 +85,14 @@ import { render as render_cf_error_page } from 'cloudflare-error-page';
const app = express();
app.get('/', (req, res) => {
/* Some code that break prod. Recently pushed by a new employee. */
/* Some code that break prod. Pushed by a new employee recently. */
let [feature_values, _] = features
.append_with_names(self.config.feature_names)
.unwrap();
}
/* Handle the error intelligently by using a custom error handler */
app.use((err, req, res) => {
/* Handle the error intelligently by using a custom handler */
res.status(500).send(render_cf_error_page({
"title": "Internal server error",
"error_code": "500",
@@ -182,15 +184,15 @@ params = {
Ray ID and user IP field in the error page can be set by `ray_id` and `client_ip` properties in the `params` argument passed to the render function. The real Cloudflare Ray ID and the data center location of current request can be extracted from the `Cf-Ray` request header (e.g. `Cf-Ray: 230b030023ae2822-SJC`). Detailed description of this header can be found at [Cloudflare documentation](https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-ray).
To lookup the city name of the data center corresponding to the three letter code in the header, you can use a location list from [here](https://github.com/Netrvin/cloudflare-colo-list/blob/main/DC-Colos.json)
To lookup the city name of the data center corresponding to the three letter code in the header, you can use a location list [here](https://github.com/Netrvin/cloudflare-colo-list/blob/main/DC-Colos.json)
The demo server runs in our website did handle these. Take a look at [this file](https://github.com/donlon/cloudflare-error-page/blob/94c3c4ddbe521dee0c9a880ef33fa7a9f0720cbe/editor/server/utils.py#L34) for reference.
The demo server runs in our website did handle these. Take a look at [this file](https://github.com/donlon/cloudflare-error-page/blob/e2226ff5bb7a877c9fe3ac09deadccdc58b0c1c7/editor/server/utils.py#L78) for reference.
## See also
- [cloudflare-error-page-3th.pages.dev](https://cloudflare-error-page-3th.pages.dev/)
Error page of every HTTP status code (reload to show random page).
Error page of every HTTP status code (reload to show random pages).
- [oftx/cloudflare-error-page](https://github.com/oftx/cloudflare-error-page)
@@ -205,7 +207,7 @@ The demo server runs in our website did handle these. Take a look at [this file]
"error_code": "500",
"time": "2025-11-18 12:34:56 UTC", // Current UTC time will be shown if empty
// Configuration of "Visit ... for more information" line
// Configuration of "Visit ... for more information"
"more_information": {
"hidden": false,
"text": "cloudflare.com",
@@ -213,7 +215,7 @@ The demo server runs in our website did handle these. Take a look at [this file]
"for": "more information",
},
// Configuration of the Browser/Cloudflare/Host status
// Configuration of the Browser/Cloudflare/Host status block
"browser_status": {
"status": "ok", // "ok" or "error"
"location": "You",
@@ -235,7 +237,7 @@ The demo server runs in our website did handle these. Take a look at [this file]
"status_text": "Working",
"status_text_color": "#9bca3e",
},
// Position of the error indicator, can be "browser", "cloudflare", or "host"
// Position of the error indicator, valid options are 'browser', 'cloudflare', and 'host'
"error_source": "host",
"what_happened": "<p>There is an internal server error on Cloudflare's network.</p>",

View File

@@ -169,7 +169,6 @@
grid-area: code;
width: 100%;
height: 100%;
min-height: 700px !important;
max-height: 1200px !important;
font-family: monospace;
font-size: 0.8em !important;
@@ -235,7 +234,7 @@
<label for="presetSelect">Preset</label>
<select id="presetSelect" class="form-select form-select-sm">
<option value="default">Internal server error (Default)</option>
<option value="empty">Empty</option>
<option value="empty">(Empty)</option>
<option value="catastrophic">Catastrophic failure</option>
<option value="working">Server working</option>
<option value="teapot">Teapot</option>

View File

@@ -29,9 +29,9 @@ class EjsCodeGen implements CodeGen {
}
}
function getErrorCode(error_code?: string | number) {
const errorCode = error_code || '';
return /\d{3}/.test(errorCode + '') ? errorCode : 500;
function getErrorCode(error_code?: string | number): string {
const errorCode = error_code ?? '';
return /\d{3}/.test(errorCode + '') ? errorCode : '500';
}
class JSCodeGen extends EjsCodeGen {
@@ -72,7 +72,7 @@ class PythonCodeGen extends EjsCodeGen {
params.client_ip = clientIpKey;
const paramsArg = JSON.stringify(
params,
(key, value) => {
(_, value) => {
if (typeof value === 'boolean') {
return randomKey + value.toString();
} else if (value === null) {
@@ -93,7 +93,6 @@ class PythonCodeGen extends EjsCodeGen {
protected prepareTemplateArgs(params: ErrorPageParams): Record<string, any> {
return {
errorCode: getErrorCode(params.error_code),
// TODO: format to JS-style object (key w/o parens)
indentedParams: this.formatPythonArgs(params).replaceAll('\n', '\n '),
};
}

View File

@@ -2,12 +2,12 @@
/*
* JavaScript Example: Render and serve the Cloudflare error page using Express server
*
* Prerequisits:
* Prerequisites:
* npm install express cloudflare-error-page
*/
import express from 'express';
import { render as render_cf_error_page } from 'cloudflare-error-page';
import { render } from 'cloudflare-error-page';
const app = express();
const port = 3000;
@@ -15,7 +15,8 @@ const port = 3000;
<%# TODO: use the library as middleware? _%>
// Define a route for GET requests to the root URL
app.get('/', (req, res) => {
res.status(<%= errorCode %>).send(render_cf_error_page(<%- indentedParams %>));
// Render the error page and send to client
res.status(<%= errorCode %>).send(render(<%- indentedParams %>));
});
// Start the server and listen on the specified port

View File

@@ -2,7 +2,7 @@
#
# Python Example: Render and serve the Cloudflare error page using Flask server
#
# Prerequisits:
# Prerequisites:
# pip install flask cloudflare-error-page
#
@@ -10,13 +10,15 @@ from flask import Flask, request
from cloudflare_error_page import render as render_cf_error_page
app = Flask(__name__)
port = 5000
<%# TODO: use the library as middleware? _%>
# Define a route for GET requests to the root URL
@app.route("/")
def index():
# Render the error page
# Render the error page and send to client
return render_cf_error_page(<%- indentedParams %>), <%= errorCode %>
if __name__ == "__main__":
app.run(debug=True, port=5000)
# Start the server and listen on the specified port
app.run(debug=True, port=port)

View File

@@ -53,9 +53,7 @@ let initialConfig = {
// Demo presets (content brief — replace or expand as needed)
const PRESETS = {
default: structuredClone(initialConfig),
empty: {
error_code: '500',
},
empty: {},
catastrophic: {
title: 'Catastrophic infrastructure failure',
error_code: '500',
@@ -104,7 +102,7 @@ const PRESETS = {
what_can_i_do: 'Visit the site before it crashes someday.',
},
teapot: {
title: "I'm a teapot",
title: "I'm a Teapot",
error_code: '418',
more_information: {
text: 'rfc2324',
@@ -112,20 +110,16 @@ const PRESETS = {
},
browser_status: {
status: 'ok',
location: 'You',
status_text: 'Working',
},
cloudflare_status: {
status: 'ok',
status_text: 'Working',
},
host_status: {
status: 'ok',
location: 'Teapot',
status_text: 'Working',
},
error_source: 'host',
what_happened: "The server can not brew coffee for it is a teapot.",
what_happened: 'The server can not brew coffee for it is a teapot.',
what_can_i_do: 'Please try a different coffee machine.',
},
consensual: {
@@ -306,7 +300,7 @@ function render() {
doc.write(pageHtml);
doc.close();
updateStatusBlockStyles();
updateStatusBlock();
// store last rendered HTML for "open in new tab"
lastRenderedHtml = pageHtml;
@@ -321,7 +315,7 @@ function openInNewTab() {
}
function createShareableLink() {
$('shareLink').value = '';
$('shareLink').value = 'Creating...';
fetch('../s/create', {
method: 'POST',
headers: {
@@ -333,16 +327,19 @@ function createShareableLink() {
})
.then((response) => {
if (!response.ok) {
alert('failed to create link');
throw new Error('failed to create link');
}
return response.json();
})
.then((result) => {
if (result.status != 'ok') {
alert('failed to create link');
return;
throw new Error('failed to create link');
}
$('shareLink').value = result.url;
})
.catch((e) => {
alert(e);
$('shareLink').value = '';
});
}
function resizePreviewFrame() {
@@ -352,7 +349,7 @@ function resizePreviewFrame() {
}
/* Update status block colors based on selected status and error_source */
function updateStatusBlockStyles() {
function updateStatusBlock() {
const browserOk = $('browser_status').value === 'ok';
const cfOk = $('cloudflare_status').value === 'ok';
const hostOk = $('host_status').value === 'ok';
@@ -360,6 +357,10 @@ function updateStatusBlockStyles() {
setBlockClass('block_browser', browserOk ? 'status-ok' : 'status-error');
setBlockClass('block_cloudflare', cfOk ? 'status-ok' : 'status-error');
setBlockClass('block_host', hostOk ? 'status-ok' : 'status-error');
$('browser_status_text').placeholder = browserOk ? 'Working' : 'Error';
$('cloudflare_status_text').placeholder = cfOk ? 'Working' : 'Error';
$('host_status_text').placeholder = hostOk ? 'Working' : 'Error';
}
function setBlockClass(id, cls) {

View File

@@ -34,7 +34,7 @@ fs.writeFileSync('error.html', errorPage);
## API Reference
### `render(params: ErrorPageParams, allowHtml?: boolean, moreArgs?: { [name: string]: any; }): string`
### `render(params: ErrorPageParams, allowHtml?: boolean, moreArgs?: Record<string, any>): string`
Generates an HTML error page based on the provided parameters.

View File

@@ -42,7 +42,6 @@
},
"files": [
"dist/**/*",
"templates/**/*",
"README.md",
"LICENSE"
]

View File

@@ -4,10 +4,8 @@ import { createFilter } from "@rollup/pluginutils";
function createRawImportPlugin(include: string) {
const rawFilter = createFilter(include);
return {
name: "raw-import",
transform(code: string, id: string): any {
if (rawFilter(id)) {
return {

View File

@@ -75,9 +75,7 @@ function genHexString(digits: number): string {
export function render(
params: ErrorPageParams,
allowHtml: boolean = true,
moreArgs: {
[name: string]: any;
} = {}
moreArgs: Record<string, any> = {}
): string {
params = { ...params };

View File

@@ -4,23 +4,41 @@ build-backend = "hatchling.build"
[project]
name = "cloudflare-error-page"
dynamic = [ "version" ]
description = "A customizable Cloudflare error page generator"
authors = [{ name = "Anthony Donlon" }]
license = { text = "MIT" }
readme = "README.md"
requires-python = ">=3.10"
license = "MIT"
authors = [{ name = "Anthony Donlon" }]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = [
"jinja2>=3.0"
]
dynamic = [ "version" ]
[project.urls]
Homepage = "https://github.com/donlon/cloudflare-error-page"
[tool.hatch.build]
include = [
"cloudflare_error_page/*.py",
"cloudflare_error_page/templates/*",
"cloudflare_error_page/**/*",
]
exclude = [".gitignore"]
[tool.hatch.build.targets.sdist]
include = [
"cloudflare_error_page/**/*",
"resources/**/*",
"scripts/**/*",
]
[tool.hatch.build.targets.wheel.hooks.custom]