9
0
mirror of https://github.com/donlon/cloudflare-error-page.git synced 2025-12-24 01:09:18 +00:00

7 Commits

Author SHA1 Message Date
Anthony Donlon
5a5311212e python: bump package version to 0.2.0 2025-12-22 22:48:41 +08:00
Anthony Donlon
9fbcba1a34 python: separate css from html template 2025-12-22 22:28:46 +08:00
Anthony Donlon
c0e576478a editor/web: fix generating Python code 2025-12-22 22:27:36 +08:00
Anthony Donlon
6233cec91f editor/web: support exporting generated webpage 2025-12-22 22:01:58 +08:00
Anthony Donlon
728ce52529 editor: support ok/error icon in shared page 2025-12-22 21:52:50 +08:00
Anthony Donlon
d0a05329d5 editor/web: open preview in about:blank tab 2025-12-22 21:44:14 +08:00
Anthony Donlon
51b61a8a2d Merge pull request #8 from donlon/save-as-dialog
Add Save As dialog to provide language examples
2025-12-22 21:34:48 +08:00
18 changed files with 80 additions and 146 deletions

View File

@@ -19,6 +19,10 @@ Here's an online editor to create customized error pages. Try it out [here](http
Install `cloudflare-error-page` with pip.
``` Bash
# Install from PyPI
pip install cloudflare-error-page
# Or, install the latest version from this repo
pip install git+https://github.com/donlon/cloudflare-error-page.git
```

View File

@@ -13,14 +13,14 @@ else:
from jinja2 import Environment, PackageLoader, Template, select_autoescape
env = Environment(
jinja_env = Environment(
loader=PackageLoader(__name__),
autoescape=select_autoescape(),
trim_blocks=True,
lstrip_blocks=True,
)
base_template: Template = env.get_template("error.html")
base_template: Template = jinja_env.get_template('template.html')
class ErrorPageParams(TypedDict):
@@ -31,7 +31,7 @@ class ErrorPageParams(TypedDict):
for_text: NotRequired[str] # renamed to avoid Python keyword conflict
class StatusItem(TypedDict):
status: NotRequired[Literal["ok", "error"]]
status: NotRequired[Literal['ok', 'error']]
location: NotRequired[str]
name: NotRequired[str]
status_text: NotRequired[str]
@@ -57,7 +57,7 @@ class ErrorPageParams(TypedDict):
cloudflare_status: NotRequired[StatusItem]
host_status: NotRequired[StatusItem]
error_source: NotRequired[Literal["browser", "cloudflare", "host"]]
error_source: NotRequired[Literal['browser', 'cloudflare', 'host']]
what_happened: NotRequired[str]
what_can_i_do: NotRequired[str]
@@ -97,7 +97,7 @@ def render(params: ErrorPageParams,
if not params.get('time'):
utc_now = datetime.now(timezone.utc)
params['time'] = utc_now.strftime("%Y-%m-%d %H:%M:%S UTC")
params['time'] = utc_now.strftime('%Y-%m-%d %H:%M:%S UTC')
if not params.get('ray_id'):
params['ray_id'] = secrets.token_hex(8)
if not allow_html:
@@ -106,5 +106,5 @@ def render(params: ErrorPageParams,
return template.render(params=params, *args, **kwargs)
__version__ = "0.1.0"
__all__ = ['base_template', 'render']
__version__ = '0.2.0'
__all__ = ['jinja_env', 'base_template', 'render']

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,15 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{% block html_head %}{% endblock %}
<!-- @INLINE_CSS_HERE@ -->
<style>
{% if html_style %}
{# Support custom stylesheet #}
{{ html_style | safe }}
{% else %}
{# Default stylesheet #}
{% include 'main.css' %}
{% endif %}
</style>
</head>
<body>
<div id="cf-wrapper">

View File

@@ -8,11 +8,14 @@ SHARE_LINK_DIGITS = 7
SHORT_SHARE_URL = false
# Icon URL for rendered pages
PAGE_ICON_URL = ''
PAGE_ICON_URL = 'https://virt.moe/cferr/editor/assets/icon-{status}-32x32.png'
# MIME type of page icon
PAGE_ICON_TYPE = 'image/png'
# Image URL for rendered pages
PAGE_IMAGE_URL = 'https://virt.moe/cferr/editor/assets/icon-{status}-large-white.png'
# Set to true if trust X-Forwarded-For/X-Forwarded-Proto header
BEHIND_PROXY = true

View File

@@ -122,21 +122,23 @@ def render_extended_template(params: ErrorPageParams,
*args: Any,
**kwargs: Any) -> str:
fill_cf_template_params(params)
description = params.get('what_happened') or 'Cloudflare error page'
description = re.sub(r'</?.*?>', '', description).strip()
description = params.get('what_happened') or 'There is an internal server error on Cloudflare\'s network.'
description = re.sub(r'<\/?.*?>', '', description).strip()
page_image_id = 'ok'
status = 'ok'
cf_status_obj = params.get('cloudflare_status')
if cf_status_obj:
cf_status = cf_status_obj.get('status')
if cf_status == 'error':
page_image_id = 'error'
page_image_url = f'https://virt.moe/cferr/editor/assets/icon-{page_image_id}-large.png'
status = 'error'
page_icon_url = current_app.config.get('PAGE_ICON_URL', '').replace('{status}', status)
page_icon_type = current_app.config.get('PAGE_ICON_TYPE')
page_image_url = current_app.config.get('PAGE_IMAGE_URL', '').replace('{status}', status)
return render_cf_error_page(params=params,
template=template,
base=base_template,
page_icon_url=current_app.config.get('PAGE_ICON_URL'),
page_icon_type=current_app.config.get('PAGE_ICON_TYPE'),
page_icon_url=page_icon_url,
page_icon_type=page_icon_type,
page_url=request.url,
page_description=description,
page_image_url=page_image_url,

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 708 B

After

Width:  |  Height:  |  Size: 708 B

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -14,7 +14,7 @@
<meta name="description" content="Online editor to create customized Cloudflare-style error pages.">
<meta name="keywords" content="cloudflare,error,page,editor">
<link rel="canonical" href="https://virt.moe/cferr/editor/" />
<link rel="icon" type="image/png" href="assets/icon-32x32.png">
<link rel="icon" type="image/png" href="assets/icon-ok-32x32.png">
<meta property="og:type" content="website" />
<meta property="og:site_name" content="moe::virt" />
@@ -532,6 +532,9 @@
<button type="button" data-type="js" class="list-group-item list-group-item-action">
NodeJS Example
</button>
<button type="button" data-type="static" class="list-group-item list-group-item-action">
Static Page
</button>
</div>
<div class="d-flex gap-1 save-as-dialog__buttons">
<button type="button" class="btn btn-success" id="saveAsDialogCopyBtn" data-bs-toggle="popover"

View File

@@ -23,7 +23,7 @@ app = Flask(__name__)
@app.route('/')
def index():
# Render the error page
return render_cf_error_page(<%=paramsArg.replaceAll('\n', '\n ')%>), 500
return render_cf_error_page(<%- paramsArg.replaceAll('\n', '\n ') %>), 500
if __name__ == '__main__':
app.run(debug=True, port=5000)

View File

@@ -305,10 +305,8 @@ function render() {
let lastRenderedHtml = '';
function openInNewTab() {
if (!lastRenderedHtml) render();
const blob = new Blob([lastRenderedHtml], { type: 'text/html' });
const url = URL.createObjectURL(blob);
window.open(url, '_blank', 'noopener');
// note that this url won't be revoked
const wnd = window.open()
wnd.document.documentElement.innerHTML = lastRenderedHtml
}
function createShareableLink() {
@@ -447,7 +445,15 @@ function updateSaveAsDialog(e) {
}
const params = { ...lastCfg };
delete params.time;
$('saveAsDialogCode').innerHTML = saveAsContent = codegen.generate(params);
if (codegen) {
saveAsContent = codegen.generate(params);
} else if (saveAsType == 'static') {
render() // rerender the page
saveAsContent = lastRenderedHtml;
} else {
throw new Error('unexpected saveAsType=' + saveAsType)
}
$('saveAsDialogCode').value = saveAsContent;
$('saveAsDialogCode').scrollTop = 0;
document.querySelectorAll('#saveAsDialogTypes button').forEach((element) => {
@@ -491,6 +497,10 @@ $('saveAsDialogSaveBtn').addEventListener('click', (e) => {
case 'python':
saveName = 'cf_error_page_example.py';
break;
case 'static':
saveName = 'cf_error_page.html';
break;
// TODO: name output files using page title
}
saveFile(saveAsContent, saveName);
});

View File

@@ -23,5 +23,8 @@ include = [
"cloudflare_error_page/templates/*",
]
[tool.hatch.build.targets.wheel.hooks.custom]
path = "scripts/hatch_build.py"
[tool.hatch.version]
path = "cloudflare_error_page/__init__.py"

File diff suppressed because one or more lines are too long

17
scripts/hatch_build.py Normal file
View File

@@ -0,0 +1,17 @@
import os
import sys
import shutil
from pathlib import Path
from typing import Any
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
sys.path.append(os.path.dirname(__file__))
from inline_resources import generate_inlined_css
class CustomBuildHook(BuildHookInterface):
def initialize(self, version: str, build_data: dict[str, Any]):
generate_inlined_css()
src = Path(self.root) / 'resources' / 'styles' / 'main.css'
dst = Path(self.root) / 'cloudflare_error_page' / 'templates'
shutil.copy(src, dst)

View File

@@ -4,7 +4,7 @@ from urllib.parse import quote
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
resources_folder = os.path.join(root,'resources')
resources_folder = os.path.join(root, 'resources')
def read_file(path: str) -> str:
@@ -49,7 +49,7 @@ def inline_css_resource(original_file: str, css_file: str, output_file: str):
write_file(output_file, original_data)
if __name__ == '__main__':
def generate_inlined_css():
inline_svg_resources(
os.path.join(resources_folder, 'styles/main-original.css'),
[
@@ -61,11 +61,10 @@ if __name__ == '__main__':
],
os.path.join(resources_folder, 'styles/main.css'),
)
inline_css_resource(
os.path.join(resources_folder, 'templates/error.html'),
os.path.join(resources_folder, 'styles/main.css'),
os.path.join(root, 'cloudflare_error_page/templates/error.html'),
)
if __name__ == '__main__':
generate_inlined_css()
inline_css_resource(
os.path.join(resources_folder, 'templates/error.ejs'),
os.path.join(resources_folder, 'styles/main.css'),