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:
26
README.md
26
README.md
@@ -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>",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 '),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"templates/**/*",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
]
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user