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, ) 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']