mirror of
https://github.com/donlon/cloudflare-error-page.git
synced 2025-12-23 08:49:25 +00:00
558 lines
18 KiB
HTML
558 lines
18 KiB
HTML
<!-- SPDX-License-Identifier: MIT -->
|
|
<!--
|
|
!!!
|
|
!!! Note: This file is vibely generated, and could be very hard to maintain.
|
|
!!!
|
|
-->
|
|
<!doctype html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>Cloudflare Error Page Editor</title>
|
|
|
|
<meta name="description" content="Online editor to create customized Cloudflare-style error pages.">
|
|
<meta name="keywords" content="cloudflare,error,page,editor">
|
|
<link rel="canonical" href="https://virt.moe/cferr/editor/" />
|
|
<link rel="icon" type="image/png" href="assets/icon-ok-32x32.png">
|
|
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:site_name" content="moe::virt" />
|
|
<meta property="og:title" content="Cloudflare error page editor" />
|
|
<meta property="og:url" content="https://virt.moe/cferr/editor/" />
|
|
<meta property="og:description" content="Online editor to create customized Cloudflare-style error pages" />
|
|
<meta property="og:image" content="https://virt.moe/cferr/editor/assets/icon-ok-large.png" />
|
|
|
|
<meta property="twitter:card" content="summary" />
|
|
<meta property="twitter:site" content="moe::virt" />
|
|
<meta property="twitter:title" content="Cloudflare error page editor" />
|
|
<meta property="twitter:description" content="Online editor to create customized Cloudflare-style error pages" />
|
|
<meta property="twitter:image" content="https://virt.moe/cferr/editor/assets/icon-ok-large.png" />
|
|
|
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />
|
|
|
|
<script type="module" src="src/index.js"></script>
|
|
<style>
|
|
/* Layout: editor + preview */
|
|
.app {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
height: 100vh;
|
|
/* padding: 0.8rem; */
|
|
}
|
|
|
|
/* On md and up, arrange horizontally: editor left, preview right */
|
|
@media (min-width: 768px) {
|
|
.app {
|
|
flex-direction: row;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.editor {
|
|
flex: 0 0;
|
|
min-width: 380px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.preview {
|
|
flex: 1 1 48%;
|
|
/* max-width: 48%; */
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
iframe.preview-frame {
|
|
height: 100% !important;
|
|
}
|
|
}
|
|
|
|
/* On small screens use stacked layout: editor then iframe */
|
|
.editor {
|
|
background: #fff;
|
|
border: 1px solid #e3e6ea;
|
|
border-radius: .5rem;
|
|
padding: 0.8rem;
|
|
}
|
|
|
|
.preview {
|
|
background: #fff;
|
|
border: 1px solid #e3e6ea;
|
|
border-radius: .5rem;
|
|
padding: 0.8rem 0 0 0;
|
|
}
|
|
|
|
/* Compact form: label and control same row */
|
|
.form-row {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: .6rem;
|
|
}
|
|
|
|
.form-row>label {
|
|
min-width: 6rem;
|
|
max-width: 12rem;
|
|
margin-bottom: 0;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.form-row>.control {
|
|
flex: 1;
|
|
}
|
|
|
|
/* Status block styling */
|
|
.status-block {
|
|
border: 1px solid #cfeadd;
|
|
border-radius: .375rem;
|
|
padding: .75rem;
|
|
margin-bottom: .5rem;
|
|
}
|
|
|
|
.status-ok {
|
|
background: #e9f7ef;
|
|
border: 1px solid #cfeadd;
|
|
}
|
|
|
|
.status-error {
|
|
background: #fff5f5;
|
|
border: 1px solid #f3c2c2;
|
|
}
|
|
|
|
/* Iframe styling */
|
|
iframe.preview-frame {
|
|
--expanded-height: 100%;
|
|
width: 100%;
|
|
height: var(--expanded-height);
|
|
border: 1px solid #ddd;
|
|
flex: 1 1 auto;
|
|
border-radius: .375rem;
|
|
box-shadow: 0 1px 4px rgba(0, 0, 0, .06);
|
|
}
|
|
|
|
/* Controls toolbar */
|
|
.toolbar {
|
|
display: flex;
|
|
gap: .5rem;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: .75rem;
|
|
}
|
|
|
|
/* Compact textarea resizing */
|
|
textarea.compact {
|
|
min-height: 80px;
|
|
resize: vertical;
|
|
}
|
|
|
|
.popover {
|
|
--bs-popover-body-padding-x: 0.7rem !important;
|
|
--bs-popover-body-padding-y: 0.5rem !important;
|
|
}
|
|
|
|
.save-as-dialog__container {
|
|
display: grid;
|
|
grid-template-areas:
|
|
"selector"
|
|
"code"
|
|
"buttons";
|
|
height: fit-content;
|
|
grid-template-columns: auto;
|
|
gap: 1em;
|
|
}
|
|
|
|
.save-as-dialog__selector {
|
|
grid-area: selector;
|
|
min-width: 10em;
|
|
}
|
|
|
|
.save-as-dialog__code {
|
|
grid-area: code;
|
|
width: 100%;
|
|
height: 100%;
|
|
min-height: 700px !important;
|
|
font-family: monospace;
|
|
font-size: 0.8em !important;
|
|
}
|
|
|
|
.save-as-dialog__buttons {
|
|
grid-area: buttons;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.save-as-dialog__buttons>* {
|
|
flex: 1 1 auto;
|
|
}
|
|
|
|
@media (min-width: 576px) {
|
|
.save-as-dialog__container {
|
|
grid-template-areas:
|
|
"selector code"
|
|
"buttons code";
|
|
grid-template-columns: auto 2fr;
|
|
height: 100%;
|
|
}
|
|
|
|
.save-as-dialog__selector {
|
|
text-align: right;
|
|
}
|
|
|
|
.save-as-dialog__code {
|
|
min-height: 0 !important;
|
|
}
|
|
|
|
.save-as-dialog__buttons {
|
|
grid-area: buttons;
|
|
flex-direction: column;
|
|
justify-content: flex-end !important;
|
|
}
|
|
|
|
.save-as-dialog__buttons>* {
|
|
flex: none;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 992px) {
|
|
.save-as-dialog__selector {
|
|
grid-area: selector;
|
|
min-width: 13em;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body class="bg-light">
|
|
|
|
<div class="container-fluid h-100">
|
|
<div class="app">
|
|
|
|
<!-- Editor column -->
|
|
<div class="editor">
|
|
<h5 class="form-row">Cloudflare Error Page Editor</h5>
|
|
<hr>
|
|
<div class="form-row mb-3">
|
|
<label for="presetSelect">Preset</label>
|
|
<select id="presetSelect" class="form-select form-select-sm">
|
|
<option value="default">Internal server error (Default)</option>
|
|
<option value="empty">Empty</option>
|
|
<option value="catastrophic">Catastrophic failure</option>
|
|
<option value="working">Server working</option>
|
|
<option value="consensual">Myth of consensual</option>
|
|
</select>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<form id="editorForm" class="needs-validation" novalidate>
|
|
<!-- Basic properties -->
|
|
<div class="mb-3">
|
|
<!-- <h6 class="mb-2">Page</h6> -->
|
|
|
|
<div class="form-row">
|
|
<label for="title">Title</label>
|
|
<div class="control">
|
|
<input id="title" class="form-control form-control-sm" placeholder="Internal server error" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="error_code">Error Code</label>
|
|
<div class="control">
|
|
<input id="error_code" class="form-control form-control-sm" placeholder="500" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<!-- Status blocks -->
|
|
<div class="mb-3">
|
|
<h6 class="mb-2">Status</h6>
|
|
|
|
<!-- Browser -->
|
|
<div id="block_browser" class="status-block status-ok">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<strong>Browser</strong>
|
|
<div>
|
|
<label for="err_browser" class="ms-1 small">
|
|
<input class="form-check-input" type="radio" name="error_source" id="err_browser" value="browser" />
|
|
Error here
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="browser_status">Status</label>
|
|
<div class="control">
|
|
<select id="browser_status" class="form-select form-select-sm">
|
|
<option value="ok">Ok</option>
|
|
<option value="error">Error</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="browser_location">Location</label>
|
|
<div class="control">
|
|
<input id="browser_location" class="form-control form-control-sm" placeholder="You" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="browser_name">Name</label>
|
|
<div class="control">
|
|
<input id="browser_name" class="form-control form-control-sm" placeholder="Browser" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="browser_status_text">Status Text</label>
|
|
<div class="control">
|
|
<input id="browser_status_text" class="form-control form-control-sm" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cloudflare -->
|
|
<div id="block_cloudflare" class="status-block status-error">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<strong>Cloudflare</strong>
|
|
<div>
|
|
<label for="err_cloudflare" class="ms-1 small">
|
|
<input class="form-check-input" type="radio" name="error_source" id="err_cloudflare"
|
|
value="cloudflare" />
|
|
Error here
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="cloudflare_status">Status</label>
|
|
<div class="control">
|
|
<select id="cloudflare_status" class="form-select form-select-sm">
|
|
<option value="ok">Ok</option>
|
|
<option value="error">Error</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="cloudflare_location">Location</label>
|
|
<div class="control">
|
|
<input id="cloudflare_location" class="form-control form-control-sm" placeholder="San Francisco" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="cloudflare_name">Name</label>
|
|
<div class="control">
|
|
<input id="cloudflare_name" class="form-control form-control-sm" placeholder="Cloudflare" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="cloudflare_status_text">Status Text</label>
|
|
<div class="control">
|
|
<input id="cloudflare_status_text" class="form-control form-control-sm" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Host -->
|
|
<div id="block_host" class="status-block status-ok">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<strong>Host</strong>
|
|
<div>
|
|
<label for="err_host" class="ms-1 small">
|
|
<input class="form-check-input" type="radio" name="error_source" id="err_host" value="host" />
|
|
Error here
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="host_status">Status</label>
|
|
<div class="control">
|
|
<select id="host_status" class="form-select form-select-sm">
|
|
<option value="ok">Ok</option>
|
|
<option value="error">Error</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="host_location">Location</label>
|
|
<div class="control">
|
|
<input id="host_location" class="form-control form-control-sm" placeholder="Website" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="host_name">Name</label>
|
|
<div class="control">
|
|
<input id="host_name" class="form-control form-control-sm" placeholder="Host" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="host_status_text">Status Text</label>
|
|
<div class="control">
|
|
<input id="host_status_text" class="form-control form-control-sm" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="status-block mt-3 mb-3">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<strong>Visit ...</strong>
|
|
<div>
|
|
<label for="more_hidden" class="ms-1 small">
|
|
<input id="more_hidden" class="form-check-input" type="checkbox" />
|
|
Hidden
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="more_text">Text</label>
|
|
<div class="control">
|
|
<input id="more_text" class="form-control form-control-sm" placeholder="cloudflare.com" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="more_link">Link</label>
|
|
<div class="control">
|
|
<input id="more_link" class="form-control form-control-sm" placeholder="https://www.cloudflare.com/" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="more_for">For</label>
|
|
<div class="control">
|
|
<input id="more_for" class="form-control form-control-sm" placeholder="more information" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<label for="what_happened" class="fw-semibold">What happened?</label>
|
|
<div class="control">
|
|
<textarea id="what_happened" class="form-control compact"
|
|
placeholder="There is an internal server error on Cloudflare's network."></textarea>
|
|
</div>
|
|
<!-- </div> -->
|
|
|
|
<!-- <div class=""> -->
|
|
<label for="what_can_i_do" class="fw-semibold mt-2">What can I do?</label>
|
|
<div class="control">
|
|
<textarea id="what_can_i_do" class="form-control compact"
|
|
placeholder="Please try again in a few minutes."></textarea>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<h6 class="form-row">Performance & security by ...</h6>
|
|
|
|
<div class="form-row">
|
|
<label for="perf_text">Text</label>
|
|
<div class="control">
|
|
<input id="perf_text" class="form-control form-control-sm" placeholder="Cloudflare" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="perf_link">Link</label>
|
|
<div class="control">
|
|
<input id="perf_link" class="form-control form-control-sm" placeholder="https://www.cloudflare.com/" />
|
|
</div>
|
|
</div>
|
|
<!-- </div> -->
|
|
|
|
<div class="d-flex gap-2 mt-2 mb-2">
|
|
<!-- <button id="btnRender" class="btn btn-sm btn-primary">Render</button> -->
|
|
<button type="button" id="btnOpen" class="btn btn-sm btn-primary">Preview in new tab</button>
|
|
<!-- Button trigger modal -->
|
|
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#saveAsDialog">
|
|
Save as...
|
|
</button>
|
|
</div>
|
|
|
|
<button type="button" id="btnShare" class="btn btn-sm btn-secondary">Create shareable link</button>
|
|
<div class="mt-2">
|
|
<div class="input-group input-group-sm">
|
|
<input id="shareLink" class="form-control" readonly />
|
|
<button type="button" id="btnCopyLink" class="btn btn-outline-secondary" data-bs-toggle="popover"
|
|
data-bs-placement="top" data-bs-trigger="manual" data-bs-content="Copied">Copy</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-2 text-center">>> Star this project on
|
|
<a href="https://github.com/donlon/cloudflare-error-page" target="_blank">GitHub</a> ⭐
|
|
</div>
|
|
|
|
<div class="mt-2" style="font-size: 0.9em;">You can also embed this error page into your own website. See
|
|
<a href="https://github.com/donlon/cloudflare-error-page#quickstart-for-programmers"
|
|
target="_blank">Quickstart</a> in the
|
|
homepage for steps.
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Preview column -->
|
|
<div class="preview">
|
|
<div class="d-flex justify-content-between align-items-center mb-1" style="padding: 0 0.8em;">
|
|
<h6><strong>Preview</strong></h6>
|
|
</div>
|
|
|
|
<!-- TODO: An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. -->
|
|
<iframe id="previewFrame" class="preview-frame" sandbox="allow-scripts allow-same-origin"></iframe>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal -->
|
|
<div class="modal fade" id="saveAsDialog" tabindex="-1" aria-labelledby="saveAsDialogLabel">
|
|
<div class="modal-dialog modal-xl modal-fullscreen-lg-down modal-dialog-scrollable">
|
|
<div class="modal-content h-100">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="saveAsDialogLabel">Save As ...</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
<div class="mx-2 save-as-dialog__container">
|
|
<div id="saveAsDialogTypes" class="list-group save-as-dialog__selector">
|
|
<button type="button" data-type="json" class="list-group-item list-group-item-action active">
|
|
JSON
|
|
</button>
|
|
<button type="button" data-type="python" class="list-group-item list-group-item-action">
|
|
Python Example
|
|
</button>
|
|
<button type="button" data-type="js" class="list-group-item list-group-item-action">
|
|
NodeJS Example
|
|
</button>
|
|
<button type="button" data-type="static" class="list-group-item list-group-item-action">
|
|
Static Page
|
|
</button>
|
|
</div>
|
|
<div class="d-flex gap-1 save-as-dialog__buttons">
|
|
<button type="button" class="btn btn-success" id="saveAsDialogCopyBtn" data-bs-toggle="popover"
|
|
data-bs-placement="right" data-bs-trigger="manual" data-bs-content="Copied">
|
|
Copy
|
|
</button>
|
|
<button type="button" class="btn btn-primary" id="saveAsDialogSaveBtn">
|
|
Save
|
|
</button>
|
|
</div>
|
|
<textarea id="saveAsDialogCode" class="form-control save-as-dialog__code" spellcheck="false"
|
|
readonly></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html> |