9
0
mirror of https://github.com/donlon/cloudflare-error-page.git synced 2025-12-19 14:59:28 +00:00

python: support type hinting for input params

This commit is contained in:
Anthony Donlon
2025-12-16 22:48:32 +08:00
parent ed711a2521
commit 58bf0d6b79
4 changed files with 71 additions and 9 deletions

View File

@@ -1,7 +1,15 @@
import html import html
import secrets import secrets
import sys
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any 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 from jinja2 import Environment, PackageLoader, Template, select_autoescape
@@ -15,7 +23,53 @@ env = Environment(
default_template: Template = env.get_template("error.html") default_template: Template = env.get_template("error.html")
def render(params: dict, 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, allow_html: bool = True,
template: Template | None = None, template: Template | None = None,
*args: Any, *args: Any,
@@ -35,6 +89,12 @@ def render(params: dict,
params = {**params} 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'): if not params.get('time'):
utc_now = datetime.now(timezone.utc) 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")

View File

@@ -11,6 +11,7 @@ from flask import (
redirect, redirect,
) )
from cloudflare_error_page import ErrorPageParams
from cloudflare_error_page import render as render_cf_error_page from cloudflare_error_page import render as render_cf_error_page
from .utils import fill_cf_template_params from .utils import fill_cf_template_params
@@ -22,7 +23,7 @@ bp = Blueprint('examples', __name__, url_prefix='/')
param_cache: dict[str, dict] = {} param_cache: dict[str, dict] = {}
def get_page_params(name: str) -> dict: def get_page_params(name: str) -> ErrorPageParams:
name = re.sub(r'[^\w]', '', name) name = re.sub(r'[^\w]', '', name)
params = param_cache.get(name) params = param_cache.get(name)
if params is not None: if params is not None:

View File

@@ -1,7 +1,9 @@
import json import json
import os import os
from cloudflare_error_page import ErrorPageParams
from flask import request from flask import request
from . import root_dir from . import root_dir
@@ -31,13 +33,13 @@ def get_cf_location(loc: str):
return data.get('city') return data.get('city')
def fill_cf_template_params(params: dict): def fill_cf_template_params(params: ErrorPageParams):
# Get the real Ray ID / data center location from Cloudflare header # Get the real Ray ID / data center location from Cloudflare header
ray_id_loc = request.headers.get('Cf-Ray') ray_id_loc = request.headers.get('Cf-Ray')
if ray_id_loc: if ray_id_loc:
params['ray_id'] = ray_id_loc[:16] params['ray_id'] = ray_id_loc[:16]
cf_status: dict = params.get('cloudflare_status') cf_status = params.get('cloudflare_status')
if cf_status is None: if cf_status is None:
cf_status = params['cloudflare_status'] = {} cf_status = params['cloudflare_status'] = {}
if not cf_status.get('location'): if not cf_status.get('location'):
@@ -61,7 +63,7 @@ def sanitize_user_link(link: str):
return '#' + link return '#' + link
def sanitize_page_param_links(param: dict): def sanitize_page_param_links(param: ErrorPageParams):
more_info = param.get('more_information') more_info = param.get('more_information')
if more_info: if more_info:
link = more_info.get('link') link = more_info.get('link')

View File

@@ -1,19 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import re
import sys import sys
from flask import ( from flask import (
Flask, Flask,
request, request,
send_from_directory
) )
# Append this directory to sys.path is not required if the package is already installed # Append this directory to sys.path is not required if the package is already installed
examples_dir = os.path.dirname(os.path.abspath(__file__)) examples_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(examples_dir)) sys.path.append(os.path.dirname(examples_dir))
from cloudflare_error_page import ErrorPageParams
from cloudflare_error_page import render as render_cf_error_page from cloudflare_error_page import render as render_cf_error_page
app = Flask(__name__) app = Flask(__name__)
@@ -21,7 +20,7 @@ app = Flask(__name__)
@app.route('/') @app.route('/')
def index(): def index():
params = { params: ErrorPageParams = {
"title": "Internal server error", "title": "Internal server error",
"error_code": 500, "error_code": 500,
"browser_status": { "browser_status": {