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

inline css/svg files to avoid the use of external resources

This commit is contained in:
Anthony Donlon
2025-12-09 23:56:28 +08:00
parent 6a066b5ff8
commit 38d19fa12d
18 changed files with 324 additions and 36 deletions

View File

@@ -14,13 +14,6 @@ env = Environment(
default_template = env.get_template("error.html")
def get_resources_folder() -> str:
"""
Get resources folder that contains stylesheet and icons for the error page.
If you pass resources_use_cdn=True to render(), local resources are not used.
"""
return os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources')
def fill_params(params: dict):
if not params.get('time'):
@@ -30,7 +23,7 @@ def fill_params(params: dict):
params['ray_id'] = secrets.token_hex(8)
def render(params: dict, allow_html: bool=True, use_cdn: bool=True) -> str:
def render(params: dict, allow_html: bool=True) -> str:
"""
Render a customized Cloudflare error page.
"""
@@ -40,4 +33,4 @@ def render(params: dict, allow_html: bool=True, use_cdn: bool=True) -> str:
params['what_happened'] = html.escape(params.get('what_happened', ''))
params['what_can_i_do'] = html.escape(params.get('what_can_i_do', ''))
return default_template.render(params=params, resources_use_cdn=use_cdn)
return default_template.render(params=params)

File diff suppressed because one or more lines are too long

View File

@@ -299,10 +299,9 @@
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC`;
}
function renderEjs(params, use_cdn = true) {
function renderEjs(params) {
return template({
params: params,
resources_use_cdn: use_cdn
})
}

File diff suppressed because one or more lines are too long

View File

@@ -53,4 +53,4 @@ def index(name: str):
fill_cf_template_params(params)
# Render the error page
return render_cf_error_page(params, use_cdn=True)
return render_cf_error_page(params)

View File

@@ -125,5 +125,4 @@ def get(name: str):
return template.render(base=cf_template,
params=params,
url=request.url,
description='Cloudflare error page',
resources_use_cdn=True)
description='Cloudflare error page')

View File

@@ -14,22 +14,10 @@ from flask import (
examples_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(examples_dir))
from cloudflare_error_page import get_resources_folder, render as render_cf_error_page
from cloudflare_error_page import render as render_cf_error_page
app = Flask(__name__)
# Resources required for the error page can be loaded from Cloudflare CDN. But in case of changes, you can set use_cdn = False to use bundled resources.
use_cdn = True
if not use_cdn:
res_folder = get_resources_folder()
# This handler is used to provide stylesheet and icon resources for the error page. If you pass use_cdn=True to render_cf_error_page
# or if your site is under proxy of Cloudflare (the cdn-cgi folder is already provided by Cloudflare), this handler can be removed.
@app.route('/cdn-cgi/<path:path>')
def cdn_cgi(path: str):
return send_from_directory(res_folder, path)
@app.route('/')
def index():
@@ -66,7 +54,7 @@ def index():
})
# Render the error page
return render_cf_error_page(params, use_cdn=use_cdn), 500
return render_cf_error_page(params), 500
if __name__ == '__main__':

View File

@@ -20,6 +20,5 @@ Homepage = "https://github.com/donlon/cloudflare-error-page"
[tool.hatch.build]
include = [
"cloudflare_error_page/*.py",
"cloudflare_error_page/resources/*",
"cloudflare_error_page/templates/*",
]

View File

Before

Width:  |  Height:  |  Size: 725 B

After

Width:  |  Height:  |  Size: 725 B

View File

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 675 B

View File

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 519 B

View File

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 346 B

View File

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 810 B

1
resources/styles/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
main.css

View File

@@ -0,0 +1,123 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en-US"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en-US"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en-US"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en-US"> <!--<![endif]-->
<head>
<%
let error_code = params.error_code || 500;
let title = params.title || 'Internal server error';
let html_title_output = params.html_title || (error_code + ': ' + title);
%>
<title><%= html_title_output %></title>
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<!-- @INLINE_CSS_HERE@ -->
</head>
<body>
<div id="cf-wrapper">
<div id="cf-error-details" class="p-0">
<header class="mx-auto pt-10 lg:pt-6 lg:px-8 w-240 lg:w-full mb-8">
<h1 class="inline-block sm:block sm:mb-2 font-light text-60 lg:text-4xl text-black-dark leading-tight mr-2">
<span class="inline-block"><%= title %></span>
<span class="code-label">Error code <%= error_code %></span>
</h1>
<% let more_info = params.more_information || {}; %>
<% if (!more_info.hidden) { %>
<div>
Visit <a href="<%= more_info.link || 'https://www.cloudflare.com/' %>" target="_blank" rel="noopener noreferrer"><%= more_info.text || 'cloudflare.com' %></a> for <%= more_info.for || "more information" %>.
</div>
<% } %>
<div class="<%= more_info.hidden ? '' : 'mt-3' %>"><%= params.time %></div>
</header>
<div class="my-8 bg-gradient-gray">
<div class="w-240 lg:w-full mx-auto">
<div class="clearfix md:px-8">
<% for (let item_id of ['browser', 'cloudflare', 'host']) { %>
<%
let icon, default_location, default_name, text_color, status_text;
if (item_id === 'browser') {
icon = 'browser';
default_location = 'You';
default_name = 'Browser';
} else if (item_id === 'cloudflare') {
icon = 'cloud';
default_location = 'San Francisco';
default_name = 'Cloudflare';
} else {
icon = 'server';
default_location = 'Website';
default_name = 'Host';
}
let item = params[item_id + '_status'] || {};
let status = item.status || 'ok';
if (item.status_text_color) {
text_color = item.status_text_color;
} else if (status === 'ok') {
text_color = '#9bca3e'; // text-green-success
} else if (status === 'error') {
text_color = '#bd2426'; // text-red-error
}
status_text = item.status_text || (status === 'ok' ? 'Working' : 'Error');
%>
<div id="cf-<%= item_id %>-status" class="<% if (params.error_source === item_id) { %>cf-error-source<% } %> relative w-1/3 md:w-full py-15 md:p-0 md:py-8 md:text-left md:border-solid md:border-0 md:border-b md:border-gray-400 overflow-hidden float-left md:float-none text-center">
<div class="relative mb-10 md:m-0">
<span class="cf-icon-<%= icon %> block md:hidden h-20 bg-center bg-no-repeat"></span>
<span class="cf-icon-<%= status %> w-12 h-12 absolute left-1/2 md:left-auto md:right-0 md:top-0 -ml-6 -bottom-4"></span>
</div>
<span class="md:block w-full truncate"><%= item.location || default_location %></span>
<%
let _name_style;
if ((item.name || default_name) === 'Cloudflare') {
_name_style = 'style="color: #2f7bbf;"'
} else{
_name_style = ''
}
%>
<h3 class="md:inline-block mt-3 md:mt-0 text-2xl text-gray-600 font-light leading-1.3" <%-_name_style %>><%= item.name || default_name %></h3>
<span class="leading-1.3 text-2xl" style="color: <%= text_color %>"><%= status_text %></span>
</div>
<% } %>
</div>
</div>
</div>
<div class="w-240 lg:w-full mx-auto mb-8 lg:px-8">
<div class="clearfix">
<div class="w-1/2 md:w-full float-left pr-6 md:pb-10 md:pr-0 leading-relaxed">
<h2 class="text-3xl font-normal leading-1.3 mb-4">What happened?</h2>
<%= (params.what_happened || 'There is an internal server error on Cloudflare\'s network.') %>
</div>
<div class="w-1/2 md:w-full float-left leading-relaxed">
<h2 class="text-3xl font-normal leading-1.3 mb-4">What can I do?</h2>
<%= (params.what_can_i_do || 'Please try again in a few minutes.') %>
</div>
</div>
</div>
<div class="cf-error-footer cf-wrapper w-240 lg:w-full py-10 sm:py-4 sm:px-8 mx-auto text-center sm:text-left border-solid border-0 border-t border-gray-300">
<p class="text-13">
<span class="cf-footer-item sm:block sm:mb-1">Ray ID: <strong class="font-semibold"><%= params.ray_id %></strong></span>
<span class="cf-footer-separator sm:hidden">&bull;</span>
<span id="cf-footer-item-ip" class="cf-footer-item hidden sm:block sm:mb-1">
Your IP:
<button type="button" id="cf-footer-ip-reveal" class="cf-footer-ip-reveal-btn">Click to reveal</button>
<span class="hidden" id="cf-footer-ip"><%= params.client_ip || '1.1.1.1' %></span>
<span class="cf-footer-separator sm:hidden">&bull;</span>
</span>
<% let perf_sec_by = params.perf_sec_by || {}; %>
<span class="cf-footer-item sm:block sm:mb-1"><span>Performance &amp; security by</span> <a rel="noopener noreferrer" href="<%= perf_sec_by.link || 'https://www.cloudflare.com/' %>" id="brand_link" target="_blank"><%= perf_sec_by.text || 'Cloudflare' %></a></span>
</p>
</div><!-- /.error-footer -->
</div>
</div>
<script>(function(){function d(){var b=a.getElementById("cf-footer-item-ip"),c=a.getElementById("cf-footer-ip-reveal");b&&"classList"in b&&(b.classList.remove("hidden"),c.addEventListener("click",function(){c.classList.add("hidden");a.getElementById("cf-footer-ip").classList.remove("hidden")}))}var a=document;document.addEventListener&&a.addEventListener("DOMContentLoaded",d)})();</script>
</body>
</html>

View File

@@ -0,0 +1,114 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en-US"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en-US"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en-US"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en-US"> <!--<![endif]-->
<head>
{% set error_code = params.error_code or 500 %}
{% set title = params.title or 'Internal server error' %}
{% set html_title = params.html_title or ((error_code | string) + ': ' + title) %}
<title>{{ html_title }}</title>
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{% block header %}{% endblock %}
<!-- @INLINE_CSS_HERE@ -->
</head>
<body>
<div id="cf-wrapper">
<div id="cf-error-details" class="p-0">
<header class="mx-auto pt-10 lg:pt-6 lg:px-8 w-240 lg:w-full mb-8">
<h1 class="inline-block sm:block sm:mb-2 font-light text-60 lg:text-4xl text-black-dark leading-tight mr-2">
<span class="inline-block">{{ title }}</span>
<span class="code-label">Error code {{ error_code }}</span>
</h1>
{% set more_info = params.more_information or {} %}
{% if not more_info.hidden or false %}{# default: shown #}
<div>
Visit <a href="{{more_info.link or 'https://www.cloudflare.com/'}}" target="_blank" rel="noopener noreferrer">{{more_info.text or 'cloudflare.com'}}</a> for {{more_info.for or 'more information'}}.
</div>
{% endif %}
<div class="mt-3">{{ params.time }}</div>
</header>
<div class="my-8 bg-gradient-gray">
<div class="w-240 lg:w-full mx-auto">
<div class="clearfix md:px-8">
{% for item_id in ['browser', 'cloudflare', 'host'] %}
{% if item_id == 'browser' %}
{% set icon = 'browser' %}
{% set default_location = 'You' %}
{% set default_name = 'Browser' %}
{% elif item_id == 'cloudflare' %}
{% set icon = 'cloud' %}
{% set default_location = 'San Francisco' %}
{% set default_name = 'Cloudflare' %}
{% else %}
{% set icon = 'server' %}
{% set default_location = 'Website' %}
{% set default_name = 'Host' %}
{% endif %}
{% set item = params.get(item_id + '_status', {}) %}
{% set status = item.status or 'ok' %}
{% if item.status_text_color %}
{% set text_color = item.status_text_color %}
{% elif status == 'ok' %}
{% set text_color = '#9bca3e' %}{# text-green-success #}
{% elif status == 'error' %}
{% set text_color = '#bd2426' %}{# text-red-error #}
{% endif %}
{% set status_text = item.status_text or ('Working' if status == 'ok' else 'Error') %}
<div id="cf-{{item_id}}-status" class="{{'cf-error-source' if params.error_source == item_id else ''}} relative w-1/3 md:w-full py-15 md:p-0 md:py-8 md:text-left md:border-solid md:border-0 md:border-b md:border-gray-400 overflow-hidden float-left md:float-none text-center">
<div class="relative mb-10 md:m-0">
<span class="cf-icon-{{icon}} block md:hidden h-20 bg-center bg-no-repeat"></span>
<span class="cf-icon-{{status}} w-12 h-12 absolute left-1/2 md:left-auto md:right-0 md:top-0 -ml-6 -bottom-4"></span>
</div>
<span class="md:block w-full truncate">{{item.location or default_location}}</span>
{% set _name_style = 'style="color: #2f7bbf;"' if ((item.name or default_name) == 'Cloudflare') else '' %}
<h3 class="md:inline-block mt-3 md:mt-0 text-2xl text-gray-600 font-light leading-1.3" {{_name_style | safe}}>{{item.name or default_name}}</h3>
<span class="leading-1.3 text-2xl" style="color: {{text_color}}">{{status_text}}</span>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="w-240 lg:w-full mx-auto mb-8 lg:px-8">
<div class="clearfix">
<div class="w-1/2 md:w-full float-left pr-6 md:pb-10 md:pr-0 leading-relaxed">
<h2 class="text-3xl font-normal leading-1.3 mb-4">What happened?</h2>
{{ (params.what_happened or '<p>There is an internal server error on Cloudflare\'s network.</p>') | safe }}
</div>
<div class="w-1/2 md:w-full float-left leading-relaxed">
<h2 class="text-3xl font-normal leading-1.3 mb-4">What can I do?</h2>
{{ (params.what_can_i_do or '<p>Please try again in a few minutes.</p>') | safe }}
</div>
</div>
</div>
<div class="cf-error-footer cf-wrapper w-240 lg:w-full py-10 sm:py-4 sm:px-8 mx-auto text-center sm:text-left border-solid border-0 border-t border-gray-300">
<p class="text-13">
<span class="cf-footer-item sm:block sm:mb-1">Ray ID: <strong class="font-semibold">{{ params.ray_id }}</strong></span>
<span class="cf-footer-separator sm:hidden">&bull;</span>
<span id="cf-footer-item-ip" class="cf-footer-item hidden sm:block sm:mb-1">
Your IP:
<button type="button" id="cf-footer-ip-reveal" class="cf-footer-ip-reveal-btn">Click to reveal</button>
<span class="hidden" id="cf-footer-ip">{{ params.client_ip or '1.1.1.1' }}</span>
<span class="cf-footer-separator sm:hidden">&bull;</span>
</span>
{% set perf_sec_by = params.perf_sec_by or {} %}
<span class="cf-footer-item sm:block sm:mb-1"><span>Performance &amp; security by</span> <a rel="noopener noreferrer" href="{{perf_sec_by.link or 'https://www.cloudflare.com/'}}" id="brand_link" target="_blank">{{perf_sec_by.text or 'Cloudflare'}}</a></span>
{% set creator_info = params.creator_info or {} %}
{% if not creator_info.get('hidden', True) %}{# default: hidden #}
<span class="cf-footer-separator sm:hidden">&bull;</span>
<span class="cf-footer-item sm:block sm:mb-1">Created with <a href="{{creator_info.link}}" target="_blank">{{creator_info.text}}</a></span>
{% endif %}
</p>
</div><!-- /.error-footer -->
</div>
</div>
<script>(function(){function d(){var b=a.getElementById("cf-footer-item-ip"),c=a.getElementById("cf-footer-ip-reveal");b&&"classList"in b&&(b.classList.remove("hidden"),c.addEventListener("click",function(){c.classList.add("hidden");a.getElementById("cf-footer-ip").classList.remove("hidden")}))}var a=document;document.addEventListener&&a.addEventListener("DOMContentLoaded",d)})();</script>
</body>
</html>

View File

@@ -0,0 +1,73 @@
import os
import re
from urllib.parse import quote
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
resources_folder = os.path.join(root,'resources')
def read_file(path: str) -> str:
with open(path, 'r', encoding='utf-8') as f:
return f.read()
def write_file(path: str, data: str):
with open(path, 'w', encoding='utf-8') as f:
f.write(data)
def convert_svg_to_data_uri(data: str) -> str:
data = data.replace('<?xml version="1.0" encoding="UTF-8"?>', '')
data = re.sub(r'\n\s*', '', data, flags=re.DOTALL)
uri = 'data:image/svg+xml;utf8,'
uri += quote(data)
return uri
def inline_svg_resources(css_file: str, svg_files: list[str], output_file: str):
css_data = read_file(css_file)
for svg_file in svg_files:
svg_data = read_file(os.path.join(os.path.dirname(css_file), svg_file))
svg_uri = convert_svg_to_data_uri(svg_data)
css_data = css_data.replace(svg_file, svg_uri)
print(f'inline_svg_resources writing to {output_file}')
write_file(output_file, css_data)
def inline_css_resource(original_file: str, css_file: str, output_file: str):
css_data = read_file(css_file)
original_data = read_file(original_file)
original_data = original_data.replace('<!-- @INLINE_CSS_HERE@ -->',
f'<style>{css_data}</style>')
note = 'Note: This is generated with scripts/inline_resources.py. Please do not edit this file manually.'
if original_file.endswith('.ejs'):
original_data = f'<%# {note} %>\n' + original_data
else:
original_data = f'{{# {note} #}}\n' + original_data
print(f'inline_css_resource writing to {output_file}')
write_file(output_file, original_data)
if __name__ == '__main__':
inline_svg_resources(
os.path.join(resources_folder, 'styles/main-original.css'),
[
'../images/cf-icon-browser.svg',
'../images/cf-icon-cloud.svg',
'../images/cf-icon-server.svg',
'../images/cf-icon-ok.svg',
'../images/cf-icon-error.svg',
],
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'),
)
inline_css_resource(
os.path.join(resources_folder, 'templates/error.ejs'),
os.path.join(resources_folder, 'styles/main.css'),
os.path.join(root, 'editor/resources/template.ejs'),
)