mirror of
https://github.com/donlon/cloudflare-error-page.git
synced 2025-12-19 14:59:28 +00:00
112 lines
3.5 KiB
Python
112 lines
3.5 KiB
Python
import html
|
|
import secrets
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from typing import Any, TypedDict, Literal
|
|
|
|
if sys.version_info >= (3, 11):
|
|
from typing import NotRequired
|
|
else:
|
|
from typing import _SpecialForm
|
|
NotRequired: _SpecialForm
|
|
|
|
|
|
from jinja2 import Environment, PackageLoader, Template, select_autoescape
|
|
|
|
env = Environment(
|
|
loader=PackageLoader(__name__),
|
|
autoescape=select_autoescape(),
|
|
trim_blocks=True,
|
|
lstrip_blocks=True,
|
|
)
|
|
|
|
# TODO: rename to base_template
|
|
default_template: Template = env.get_template("error.html")
|
|
|
|
|
|
class ErrorPageParams(TypedDict):
|
|
class MoreInformation(TypedDict):
|
|
hidden: NotRequired[bool]
|
|
text: NotRequired[str]
|
|
link: NotRequired[str]
|
|
for_text: NotRequired[str] # renamed to avoid Python keyword conflict
|
|
|
|
class StatusItem(TypedDict):
|
|
status: NotRequired[Literal["ok", "error"]]
|
|
location: NotRequired[str]
|
|
name: NotRequired[str]
|
|
status_text: NotRequired[str]
|
|
status_text_color: NotRequired[str]
|
|
|
|
class PerfSecBy(TypedDict):
|
|
text: NotRequired[str]
|
|
link: NotRequired[str]
|
|
|
|
class CreatorInfo(TypedDict):
|
|
hidden: NotRequired[bool]
|
|
link: NotRequired[str]
|
|
text: NotRequired[str]
|
|
|
|
html_title: NotRequired[str]
|
|
title: NotRequired[str]
|
|
error_code: NotRequired[str]
|
|
time: NotRequired[str]
|
|
|
|
more_information: NotRequired[MoreInformation]
|
|
|
|
browser_status: NotRequired[StatusItem]
|
|
cloudflare_status: NotRequired[StatusItem]
|
|
host_status: NotRequired[StatusItem]
|
|
|
|
error_source: NotRequired[Literal["browser", "cloudflare", "host"]]
|
|
|
|
what_happened: NotRequired[str]
|
|
what_can_i_do: NotRequired[str]
|
|
|
|
ray_id: NotRequired[str]
|
|
client_ip: NotRequired[str]
|
|
|
|
perf_sec_by: NotRequired[PerfSecBy]
|
|
creator_info: NotRequired[CreatorInfo]
|
|
|
|
|
|
def render(params: ErrorPageParams,
|
|
allow_html: bool = True,
|
|
template: Template | None = None,
|
|
*args: Any,
|
|
**kwargs: Any) -> str:
|
|
'''Render a customized Cloudflare error page.
|
|
|
|
:param params: Parameters passed to the template. Refer to the project homepage for more information.
|
|
:param allow_html: Allow output raw HTML content from parameters. Set to False if you don't trust the source of the params.
|
|
:param template: Jinja template used to render the error page. Default template will be used if ``template`` is None.
|
|
Override this to extend or customize the base template.
|
|
:param args: Additional positional arguments passed to ``Template.render`` function.
|
|
:param kwargs: Additional keyword arguments passed to ``Template.render`` function.
|
|
:return: The rendered error page as a string.
|
|
'''
|
|
if not template:
|
|
template = default_template
|
|
|
|
params = {**params}
|
|
|
|
more_information = params.get('more_information')
|
|
if more_information:
|
|
for_text = more_information.get('for_text')
|
|
if for_text is not None:
|
|
more_information['for'] = for_text
|
|
|
|
if not params.get('time'):
|
|
utc_now = datetime.now(timezone.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:
|
|
params['what_happened'] = html.escape(params.get('what_happened', ''))
|
|
params['what_can_i_do'] = html.escape(params.get('what_can_i_do', ''))
|
|
|
|
return template.render(params=params, *args, **kwargs)
|
|
|
|
__version__ = "0.1.0"
|
|
__all__ = ['default_template', 'render']
|