9
0
mirror of https://github.com/donlon/cloudflare-error-page.git synced 2026-01-06 15:41:45 +00:00

format Python files with ruff

This commit is contained in:
Anthony Donlon
2025-12-28 23:05:51 +08:00
parent 51cfa6aebc
commit e92ffaf506
9 changed files with 92 additions and 57 deletions

1
.gitignore vendored
View File

@@ -9,5 +9,6 @@ build/
dist/ dist/
*.egg-info/ *.egg-info/
__pycache__/ __pycache__/
.ruff_cache/
instance/ instance/

View File

@@ -8,6 +8,7 @@ if sys.version_info >= (3, 11):
from typing import NotRequired from typing import NotRequired
else: else:
from typing import _SpecialForm from typing import _SpecialForm
NotRequired: _SpecialForm NotRequired: _SpecialForm
@@ -69,12 +70,14 @@ class ErrorPageParams(TypedDict):
creator_info: NotRequired[CreatorInfo] creator_info: NotRequired[CreatorInfo]
def render(params: ErrorPageParams, def render(
allow_html: bool = True, params: ErrorPageParams,
template: Template | None = None, allow_html: bool = True,
*args: Any, template: Template | None = None,
**kwargs: Any) -> str: *args: Any,
'''Render a customized Cloudflare error page. **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 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 allow_html: Allow output raw HTML content from parameters. Set to False if you don't trust the source of the params.
@@ -83,7 +86,7 @@ def render(params: ErrorPageParams,
:param args: Additional positional arguments passed to ``Template.render`` function. :param args: Additional positional arguments passed to ``Template.render`` function.
:param kwargs: Additional keyword arguments passed to ``Template.render`` function. :param kwargs: Additional keyword arguments passed to ``Template.render`` function.
:return: The rendered error page as a string. :return: The rendered error page as a string.
''' """
if not template: if not template:
template = base_template template = base_template
@@ -106,5 +109,6 @@ def render(params: ErrorPageParams,
return template.render(params=params, *args, **kwargs) return template.render(params=params, *args, **kwargs)
__version__ = '0.2.0' __version__ = '0.2.0'
__all__ = ['jinja_env', 'base_template', 'render'] __all__ = ['jinja_env', 'base_template', 'render']

View File

@@ -1,11 +1,10 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import os import os
from pathlib import Path
import secrets import secrets
import string import string
import sys
import tomllib import tomllib
from pathlib import Path
from flask import Flask, redirect, url_for from flask import Flask, redirect, url_for
from flask_limiter import Limiter from flask_limiter import Limiter
@@ -16,17 +15,24 @@ from werkzeug.middleware.proxy_fix import ProxyFix
root_dir = Path(__file__).parent.parent.parent.parent root_dir = Path(__file__).parent.parent.parent.parent
class Base(DeclarativeBase): class Base(DeclarativeBase):
pass pass
db: SQLAlchemy = SQLAlchemy(model_class=Base, session_options={
# 'autobegin': False, db: SQLAlchemy = SQLAlchemy(
# 'expire_on_commit': False, model_class=Base,
}) session_options={
# 'autobegin': False,
# 'expire_on_commit': False,
},
)
limiter: Limiter = Limiter( limiter: Limiter = Limiter(
key_func=get_remote_address, # Uses client's IP address by default key_func=get_remote_address, # Uses client's IP address by default
) )
static_dir: str | None = None
def _generate_secret(length=32) -> str: def _generate_secret(length=32) -> str:
characters = string.ascii_letters + string.digits # A-Z, a-z, 0-9 characters = string.ascii_letters + string.digits # A-Z, a-z, 0-9
@@ -34,10 +40,9 @@ def _generate_secret(length=32) -> str:
def _initialize_app_config(app: Flask): def _initialize_app_config(app: Flask):
global static_dir
if app.config.get('BEHIND_PROXY', True): if app.config.get('BEHIND_PROXY', True):
app.wsgi_app = ProxyFix( app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
app.wsgi_app, x_for=1, x_proto=1
)
secret_key = app.config.get('SECRET_KEY', '') secret_key = app.config.get('SECRET_KEY', '')
if secret_key: if secret_key:
app.secret_key = secret_key app.secret_key = secret_key
@@ -45,12 +50,14 @@ def _initialize_app_config(app: Flask):
app.logger.info('Using generated secret') app.logger.info('Using generated secret')
app.secret_key = _generate_secret() app.secret_key = _generate_secret()
app.config["SQLALCHEMY_DATABASE_URI"] = app.config.get('SQLALCHEMY_DATABASE_URI', 'sqlite:///example.db') app.config['SQLALCHEMY_DATABASE_URI'] = app.config.get('SQLALCHEMY_DATABASE_URI', 'sqlite:///example.db')
if app.config["SQLALCHEMY_DATABASE_URI"].startswith('sqlite'): if app.config['SQLALCHEMY_DATABASE_URI'].startswith('sqlite'):
app.config["SQLALCHEMY_ENGINE_OPTIONS"] = { app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
'isolation_level': 'SERIALIZABLE', 'isolation_level': 'SERIALIZABLE',
# "execution_options": {"autobegin": False} # "execution_options": {"autobegin": False}
} }
static_dir = os.path.join(app.instance_path, app.config.get('STATIC_DIR', '../../web/dist'))
app.logger.info(f'Static directory: {static_dir}')
def create_app(test_config=None) -> Flask: def create_app(test_config=None) -> Flask:
@@ -60,16 +67,12 @@ def create_app(test_config=None) -> Flask:
os.makedirs(instance_path, exist_ok=True) os.makedirs(instance_path, exist_ok=True)
print(f'App instance path: {instance_path}') print(f'App instance path: {instance_path}')
app = Flask(__name__, app = Flask(__name__, instance_path=instance_path, instance_relative_config=True)
instance_path=instance_path, app.config.from_file('config.toml', load=tomllib.load, text=False)
instance_relative_config=True
)
app.config.from_file("config.toml", load=tomllib.load, text=False)
_initialize_app_config(app) _initialize_app_config(app)
from . import utils # noqa: F401
from . import utils from . import models # noqa: F401
from . import models
from . import examples from . import examples
from . import editor from . import editor
from . import share from . import share

View File

@@ -30,13 +30,14 @@ bp_short = Blueprint('share_short', __name__, url_prefix='/')
rand_charset = string.ascii_lowercase + string.digits rand_charset = string.ascii_lowercase + string.digits
def get_rand_name(digits=8): def get_rand_name(digits=8):
return ''.join(random.choice(rand_charset) for _ in range(digits)) return ''.join(random.choice(rand_charset) for _ in range(digits))
@bp.post('/create') @bp.post('/create')
@limiter.limit("20 per minute") @limiter.limit('20 per minute')
@limiter.limit("500 per hour") @limiter.limit('500 per hour')
def create(): def create():
if len(request.data) > 4096: if len(request.data) > 4096:
abort(413) abort(413)
@@ -82,9 +83,7 @@ def get(name: str):
item = db.session.query(models.Item).filter_by(name=name).first() item = db.session.query(models.Item).filter_by(name=name).first()
if not item: if not item:
if is_json: if is_json:
return { return {'status': 'notfound'}
'status': 'notfound'
}
else: else:
return abort(404) return abort(404)
params = cast(ErrorPageParams, item.params) params = cast(ErrorPageParams, item.params)
@@ -104,8 +103,7 @@ def get(name: str):
'link': request.host_url[:-1] + url_for('editor.index') + f'#from={name}', 'link': request.host_url[:-1] + url_for('editor.index') + f'#from={name}',
} }
sanitize_page_param_links(params) sanitize_page_param_links(params)
return render_extended_template(params=params, return render_extended_template(params=params, allow_html=False)
allow_html=False)
@bp.get('/<name>') @bp.get('/<name>')

View File

@@ -17,7 +17,7 @@ env = Environment(
trim_blocks=True, trim_blocks=True,
lstrip_blocks=True, lstrip_blocks=True,
) )
template = env.from_string('''{% extends base %} template = env.from_string("""{% extends base %}
{% block html_head %} {% block html_head %}
{% if page_icon_url %} {% if page_icon_url %}
@@ -45,11 +45,12 @@ template = env.from_string('''{% extends base %}
<meta property="twitter:image" content="{{ page_image_url }}" /> <meta property="twitter:image" content="{{ page_image_url }}" />
{% endif %} {% endif %}
{% endblock %} {% endblock %}
''') """)
loc_data: dict = None loc_data: dict = None
def read_loc_file(path: str): def read_loc_file(path: str):
try: try:
with open(os.path.join(Path(__file__).parent / path)) as f: with open(os.path.join(Path(__file__).parent / path)) as f:
@@ -117,11 +118,9 @@ def sanitize_page_param_links(param: ErrorPageParams):
perf_sec_by['link'] = sanitize_user_link(link) perf_sec_by['link'] = sanitize_user_link(link)
def render_extended_template(params: ErrorPageParams, def render_extended_template(params: ErrorPageParams, *args: Any, **kwargs: Any) -> str:
*args: Any,
**kwargs: Any) -> str:
fill_cf_template_params(params) fill_cf_template_params(params)
description = params.get('what_happened') or 'There is an internal server error on Cloudflare\'s network.' description = params.get('what_happened') or "There is an internal server error on Cloudflare's network."
description = re.sub(r'<\/?.*?>', '', description).strip() description = re.sub(r'<\/?.*?>', '', description).strip()
status = 'ok' status = 'ok'
@@ -133,13 +132,15 @@ def render_extended_template(params: ErrorPageParams,
page_icon_url = current_app.config.get('PAGE_ICON_URL', '').replace('{status}', status) page_icon_url = current_app.config.get('PAGE_ICON_URL', '').replace('{status}', status)
page_icon_type = current_app.config.get('PAGE_ICON_TYPE') page_icon_type = current_app.config.get('PAGE_ICON_TYPE')
page_image_url = current_app.config.get('PAGE_IMAGE_URL', '').replace('{status}', status) page_image_url = current_app.config.get('PAGE_IMAGE_URL', '').replace('{status}', status)
return render_cf_error_page(params=params, return render_cf_error_page(
template=template, params=params,
base=base_template, template=template,
page_icon_url=page_icon_url, base=base_template,
page_icon_type=page_icon_type, page_icon_url=page_icon_url,
page_url=request.url, page_icon_type=page_icon_type,
page_description=description, page_url=request.url,
page_image_url=page_image_url, page_description=description,
*args, page_image_url=page_image_url,
**kwargs) *args,
**kwargs,
)

View File

@@ -24,3 +24,15 @@ include = ["app/**/*"]
[tool.hatch.build.targets.wheel.hooks.custom] [tool.hatch.build.targets.wheel.hooks.custom]
path = "hatch_build.py" path = "hatch_build.py"
[tool.ruff]
line-length = 120
target-version = "py313"
[tool.ruff.lint]
ignore = [
'E722', # Bare-except
]
[tool.ruff.format]
quote-style = "single"

View File

@@ -46,3 +46,19 @@ path = "scripts/hatch_build.py"
[tool.hatch.version] [tool.hatch.version]
path = "cloudflare_error_page/__init__.py" path = "cloudflare_error_page/__init__.py"
[tool.ruff]
line-length = 120
target-version = "py310"
include = [
"cloudflare_error_page/**/*.py",
"scripts/**/*.py",
]
[tool.ruff.lint]
ignore = [
'E722', # Bare-except
]
[tool.ruff.format]
quote-style = "single"

View File

@@ -9,6 +9,7 @@ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
sys.path.append(os.path.dirname(__file__)) sys.path.append(os.path.dirname(__file__))
from inline_resources import generate_inlined_css from inline_resources import generate_inlined_css
class CustomBuildHook(BuildHookInterface): class CustomBuildHook(BuildHookInterface):
def initialize(self, version: str, build_data: dict[str, Any]): def initialize(self, version: str, build_data: dict[str, Any]):
generate_inlined_css() generate_inlined_css()

View File

@@ -38,8 +38,7 @@ def inline_svg_resources(css_file: str, svg_files: list[str], output_file: str):
def inline_css_resource(original_file: str, css_file: str, output_file: str): def inline_css_resource(original_file: str, css_file: str, output_file: str):
css_data = read_file(css_file) css_data = read_file(css_file)
original_data = read_file(original_file) original_data = read_file(original_file)
original_data = original_data.replace('<!-- @INLINE_CSS_HERE@ -->', original_data = original_data.replace('<!-- @INLINE_CSS_HERE@ -->', f'<style>{css_data}</style>')
f'<style>{css_data}</style>')
note = 'Note: This file is generated with scripts/inline_resources.py. Please do not edit manually.' note = 'Note: This file is generated with scripts/inline_resources.py. Please do not edit manually.'
if original_file.endswith('.ejs'): if original_file.endswith('.ejs'):
original_data = f'<%# {note} %>\n' + original_data original_data = f'<%# {note} %>\n' + original_data
@@ -60,7 +59,7 @@ def generate_inlined_css():
'../images/cf-icon-error.svg', '../images/cf-icon-error.svg',
], ],
os.path.join(resources_folder, 'styles/main.css'), os.path.join(resources_folder, 'styles/main.css'),
) )
if __name__ == '__main__': if __name__ == '__main__':