mirror of
https://github.com/donlon/cloudflare-error-page.git
synced 2025-12-24 17:29:16 +00:00
Compare commits
14 Commits
python-v0.
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59c1f0dcfb | ||
|
|
f90fa3501b | ||
|
|
3d470d55a4 | ||
|
|
d6cf4bbedb | ||
|
|
48169fd3fa | ||
|
|
ae8849f39f | ||
|
|
f50f81afd1 | ||
|
|
d39409c0ca | ||
|
|
8be87415fe | ||
|
|
fb52f6d523 | ||
|
|
5a633b5958 | ||
|
|
b2164729b4 | ||
|
|
dd29cf0904 | ||
|
|
d9af59f14f |
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,14 +1,13 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
.DS_Store
|
||||||
build/
|
|
||||||
*.egg-info/
|
|
||||||
__pycache__/
|
|
||||||
dist/
|
|
||||||
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
.venv/
|
.venv/
|
||||||
venv/
|
venv/
|
||||||
ttt/
|
|
||||||
|
node_modules/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
*.egg-info/
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
instance/
|
instance/
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -8,7 +8,7 @@ This project creates customized error pages that mimics the well-known Cloudflar
|
|||||||
|
|
||||||
## Online Editor
|
## Online Editor
|
||||||
|
|
||||||
Here's an online editor to create customized error pages. Try it out [here](https://virt.moe/cferr/editor/).
|
Here's an online editor to create customized error pages and example server apps. Try it out [here](https://virt.moe/cferr/editor/).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -66,12 +66,52 @@ You can also see live demo [here](https://virt.moe/cferr/examples/default).
|
|||||||
|
|
||||||
A demo server using Flask is also available in [flask_demo.py](examples/flask_demo.py).
|
A demo server using Flask is also available in [flask_demo.py](examples/flask_demo.py).
|
||||||
|
|
||||||
### Node.js/NPM
|
### JavaScript/NodeJS
|
||||||
|
|
||||||
A Node.js package is available in [nodejs](nodejs) folder. However currently it supports only Node.js but not web browsers,
|
Install the `cloudflare-error-page` package using npm:
|
||||||
and we plan to refactor it into a shared package, so it can work in both environments.
|
|
||||||
|
|
||||||
(Thanks [@junduck](https://github.com/junduck) for creating this.)
|
``` Bash
|
||||||
|
npm install cloudflare-error-page
|
||||||
|
```
|
||||||
|
|
||||||
|
The following example demonstrates how to create a simple Express server and return the error page to visitors.
|
||||||
|
|
||||||
|
``` JavaScript
|
||||||
|
import express from 'express';
|
||||||
|
import { render as render_cf_error_page } from 'cloudflare-error-page';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const port = 3000;
|
||||||
|
|
||||||
|
// Define a route for GET requests to the root URL
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.status(500).send(render_cf_error_page({
|
||||||
|
title: "Internal server error",
|
||||||
|
// Browser status is ok
|
||||||
|
browser_status: {
|
||||||
|
status: 'ok',
|
||||||
|
},
|
||||||
|
// Cloudflare status is error
|
||||||
|
cloudflare_status: {
|
||||||
|
status: 'error',
|
||||||
|
status_text: 'Error',
|
||||||
|
},
|
||||||
|
// Host status is also ok
|
||||||
|
host_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'example.com',
|
||||||
|
},
|
||||||
|
error_source: "cloudflare",
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the server and listen on the specified port
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Example app listening at http://localhost:${port}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
(Thanks [@junduck](https://github.com/junduck) for creating the original NodeJS version.)
|
||||||
|
|
||||||
### PHP
|
### PHP
|
||||||
|
|
||||||
|
|||||||
@@ -170,8 +170,10 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 700px !important;
|
min-height: 700px !important;
|
||||||
|
max-height: 1200px !important;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 0.8em !important;
|
font-size: 0.8em !important;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-as-dialog__buttons {
|
.save-as-dialog__buttons {
|
||||||
@@ -236,6 +238,7 @@
|
|||||||
<option value="empty">Empty</option>
|
<option value="empty">Empty</option>
|
||||||
<option value="catastrophic">Catastrophic failure</option>
|
<option value="catastrophic">Catastrophic failure</option>
|
||||||
<option value="working">Server working</option>
|
<option value="working">Server working</option>
|
||||||
|
<option value="teapot">Teapot</option>
|
||||||
<option value="consensual">Myth of consensual</option>
|
<option value="consensual">Myth of consensual</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -473,7 +476,7 @@
|
|||||||
<button type="button" id="btnOpen" class="btn btn-sm btn-primary">Preview in new tab</button>
|
<button type="button" id="btnOpen" class="btn btn-sm btn-primary">Preview in new tab</button>
|
||||||
<!-- Button trigger modal -->
|
<!-- Button trigger modal -->
|
||||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#saveAsDialog">
|
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#saveAsDialog">
|
||||||
Save as...
|
Save as & Examples ...
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -530,10 +533,10 @@
|
|||||||
Python Example
|
Python Example
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-type="js" class="list-group-item list-group-item-action">
|
<button type="button" data-type="js" class="list-group-item list-group-item-action">
|
||||||
NodeJS Example
|
JavaScript Example
|
||||||
</button>
|
</button>
|
||||||
<button type="button" data-type="static" class="list-group-item list-group-item-action">
|
<button type="button" data-type="static" class="list-group-item list-group-item-action">
|
||||||
Static Page
|
Static Webpage
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-1 save-as-dialog__buttons">
|
<div class="d-flex gap-1 save-as-dialog__buttons">
|
||||||
@@ -545,8 +548,9 @@
|
|||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="saveAsDialogCode" class="form-control save-as-dialog__code" spellcheck="false"
|
<div class="border rounded save-as-dialog__code">
|
||||||
readonly></textarea>
|
<pre id="saveAsDialogCode" class="px-2 py-1 h-100"></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"@types/ejs": "^3.1.5",
|
"@types/ejs": "^3.1.5",
|
||||||
"@types/html-minifier-terser": "^7.0.2",
|
"@types/html-minifier-terser": "^7.0.2",
|
||||||
"@types/node": "^24.10.2",
|
"@types/node": "^24.10.2",
|
||||||
|
"@types/prismjs": "^1.26.5",
|
||||||
"html-minifier-terser": "^7.2.0",
|
"html-minifier-terser": "^7.2.0",
|
||||||
"prettier": "3.7.4",
|
"prettier": "3.7.4",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
@@ -22,6 +23,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^5.3.8",
|
"bootstrap": "^5.3.8",
|
||||||
"ejs": "^3.1.10"
|
"cloudflare-error-page": "../../javascript",
|
||||||
|
"ejs": "^3.1.10",
|
||||||
|
"prismjs": "^1.30.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import ejs from 'ejs';
|
import ejs from 'ejs';
|
||||||
|
import { ErrorPageParams } from 'cloudflare-error-page';
|
||||||
|
|
||||||
import jsTemplate from './js.ejs?raw';
|
import jsTemplate from './js.ejs?raw';
|
||||||
import jsonTemplate from './json.ejs?raw';
|
import jsonTemplate from './json.ejs?raw';
|
||||||
@@ -6,21 +7,98 @@ import pythonTemplate from './python.ejs?raw';
|
|||||||
|
|
||||||
interface CodeGen {
|
interface CodeGen {
|
||||||
name: string;
|
name: string;
|
||||||
generate(params: any): string;
|
language: string;
|
||||||
|
generate(params: ErrorPageParams): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EjsCodeGen implements CodeGen {
|
class EjsCodeGen implements CodeGen {
|
||||||
name: string;
|
name: string;
|
||||||
|
language: string;
|
||||||
private template: ejs.TemplateFunction;
|
private template: ejs.TemplateFunction;
|
||||||
constructor(name: string, templateContent: any) {
|
constructor(name: string, language: string, templateContent: string) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.language = language;
|
||||||
this.template = ejs.compile(templateContent);
|
this.template = ejs.compile(templateContent);
|
||||||
}
|
}
|
||||||
generate(params: any): string {
|
protected prepareTemplateArgs(params: ErrorPageParams): Record<string, any> {
|
||||||
return this.template({ params });
|
return {};
|
||||||
|
}
|
||||||
|
generate(params: ErrorPageParams): string {
|
||||||
|
const moreArgs = this.prepareTemplateArgs(params);
|
||||||
|
return this.template({ params, ...moreArgs });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const jsCodeGen = new EjsCodeGen('NodeJS Example', jsTemplate);
|
function getErrorCode(error_code?: string | number) {
|
||||||
export const jsonCodeGen = new EjsCodeGen('JSON', jsonTemplate);
|
const errorCode = error_code || '';
|
||||||
export const pythonCodeGen = new EjsCodeGen('Python Example', pythonTemplate);
|
return /\d{3}/.test(errorCode + '') ? errorCode : 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
class JSCodeGen extends EjsCodeGen {
|
||||||
|
constructor(templateContent: string) {
|
||||||
|
super('JavaScript Example', 'javascript', templateContent);
|
||||||
|
}
|
||||||
|
private formatJSArgs(params: ErrorPageParams): string {
|
||||||
|
params = { ...params };
|
||||||
|
const rayIdKey = Math.random() + '';
|
||||||
|
const clientIpKey = Math.random() + '';
|
||||||
|
params.ray_id = rayIdKey;
|
||||||
|
params.client_ip = clientIpKey;
|
||||||
|
const paramsArg = JSON.stringify(params, null, 2)
|
||||||
|
.replace(`"${rayIdKey}"`, "(req.get('Cf-Ray') ?? '').substring(0, 16)")
|
||||||
|
.replace(`"${clientIpKey}"`, "req.get('X-Forwarded-For') || req.socket.remoteAddress");
|
||||||
|
return paramsArg;
|
||||||
|
}
|
||||||
|
protected prepareTemplateArgs(params: ErrorPageParams): Record<string, any> {
|
||||||
|
return {
|
||||||
|
errorCode: getErrorCode(params.error_code),
|
||||||
|
// TODO: format to JS-style object (key w/o parens)
|
||||||
|
indentedParams: this.formatJSArgs(params).replaceAll('\n', '\n '),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PythonCodeGen extends EjsCodeGen {
|
||||||
|
constructor(templateContent: string) {
|
||||||
|
super('Python Example', 'python', templateContent);
|
||||||
|
}
|
||||||
|
private formatPythonArgs(params: ErrorPageParams): string {
|
||||||
|
// Covert the parameters to Python format object
|
||||||
|
params = { ...params };
|
||||||
|
const randomKey = Math.random() + '';
|
||||||
|
const rayIdKey = Math.random() + '';
|
||||||
|
const clientIpKey = Math.random() + '';
|
||||||
|
params.ray_id = rayIdKey;
|
||||||
|
params.client_ip = clientIpKey;
|
||||||
|
const paramsArg = JSON.stringify(
|
||||||
|
params,
|
||||||
|
(key, value) => {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return randomKey + value.toString();
|
||||||
|
} else if (value === null) {
|
||||||
|
return randomKey + 'null';
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
4
|
||||||
|
)
|
||||||
|
.replace(`"${randomKey}true"`, 'True')
|
||||||
|
.replace(`"${randomKey}false"`, 'False')
|
||||||
|
.replace(`"${randomKey}null"`, 'None')
|
||||||
|
.replace(`"${rayIdKey}"`, 'request.headers.get("Cf-Ray", "")[:16]')
|
||||||
|
.replace(`"${clientIpKey}"`, 'request.headers.get("X-Forwarded-For") or request.remote_addr');
|
||||||
|
return paramsArg;
|
||||||
|
}
|
||||||
|
protected prepareTemplateArgs(params: ErrorPageParams): Record<string, any> {
|
||||||
|
return {
|
||||||
|
errorCode: getErrorCode(params.error_code),
|
||||||
|
// TODO: format to JS-style object (key w/o parens)
|
||||||
|
indentedParams: this.formatPythonArgs(params).replaceAll('\n', '\n '),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const jsCodeGen = new JSCodeGen(jsTemplate);
|
||||||
|
export const jsonCodeGen = new EjsCodeGen('JSON', 'json', jsonTemplate);
|
||||||
|
export const pythonCodeGen = new PythonCodeGen(pythonTemplate);
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/*
|
||||||
|
* JavaScript Example: Render and serve the Cloudflare error page using Express server
|
||||||
|
*
|
||||||
|
* Prerequisits:
|
||||||
|
* npm install express cloudflare-error-page
|
||||||
|
*/
|
||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { render as render_cf_error_page } from 'cloudflare-error-page';
|
import { render as render_cf_error_page } from 'cloudflare-error-page';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 3000;
|
const port = 3000;
|
||||||
|
|
||||||
|
<%# TODO: use the library as middleware? _%>
|
||||||
// Define a route for GET requests to the root URL
|
// Define a route for GET requests to the root URL
|
||||||
<%# TODO: format to JS-style object (key w/o parens) _%>
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
res.status(500).send(render_cf_error_page(<%-JSON.stringify(params, null, 2).replaceAll('\n', '\n ')%>));
|
res.status(<%= errorCode %>).send(render_cf_error_page(<%- indentedParams %>));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start the server and listen on the specified port
|
// Start the server and listen on the specified port
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<%-JSON.stringify(params, null, 4)%>
|
<%- JSON.stringify(params, null, 4) %>
|
||||||
|
|||||||
@@ -1,29 +1,22 @@
|
|||||||
<%
|
#!/usr/bin/env python3
|
||||||
// Covert the parameters to Python format object
|
#
|
||||||
const randomKey = Math.random() + ''
|
# Python Example: Render and serve the Cloudflare error page using Flask server
|
||||||
const paramsArg = JSON.stringify(params, (key, value) => {
|
#
|
||||||
if (typeof value === 'boolean') {
|
# Prerequisits:
|
||||||
return randomKey + value.toString()
|
# pip install flask cloudflare-error-page
|
||||||
} else if (value === null) {
|
#
|
||||||
return randomKey + 'null'
|
|
||||||
} else {
|
from flask import Flask, request
|
||||||
return value
|
|
||||||
}
|
|
||||||
}, 4)
|
|
||||||
.replace(`"${randomKey}true"`, 'True')
|
|
||||||
.replace(`"${randomKey}false"`, 'False')
|
|
||||||
.replace(`"${randomKey}null"`, 'None')
|
|
||||||
_%>
|
|
||||||
from flask import Flask
|
|
||||||
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__)
|
||||||
|
|
||||||
|
<%# TODO: use the library as middleware? _%>
|
||||||
# Define a route for GET requests to the root URL
|
# Define a route for GET requests to the root URL
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
# Render the error page
|
# Render the error page
|
||||||
return render_cf_error_page(<%- paramsArg.replaceAll('\n', '\n ') %>), 500
|
return render_cf_error_page(<%- indentedParams %>), <%= errorCode %>
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(debug=True, port=5000)
|
app.run(debug=True, port=5000)
|
||||||
|
|||||||
@@ -5,17 +5,19 @@
|
|||||||
- inputs call render() on change
|
- inputs call render() on change
|
||||||
- "Open in new tab" opens the rendered HTML in a new window using a blob URL
|
- "Open in new tab" opens the rendered HTML in a new window using a blob URL
|
||||||
*/
|
*/
|
||||||
import ejs from 'ejs';
|
|
||||||
import templateContent from './template.ejs?raw';
|
|
||||||
|
|
||||||
import 'bootstrap/js/src/modal.js';
|
import 'bootstrap/js/src/modal.js';
|
||||||
import Popover from 'bootstrap/js/src/popover.js';
|
import Popover from 'bootstrap/js/src/popover.js';
|
||||||
|
import Prism from 'prismjs';
|
||||||
|
import 'prismjs/components/prism-json.js';
|
||||||
|
import 'prismjs/components/prism-python.js';
|
||||||
|
import { render as render_cf_error_page } from 'cloudflare-error-page';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
import 'prismjs/themes/prism.css';
|
||||||
|
|
||||||
import { jsCodeGen, jsonCodeGen, pythonCodeGen } from './codegen';
|
import { jsCodeGen, jsonCodeGen, pythonCodeGen } from './codegen';
|
||||||
|
|
||||||
let template = ejs.compile(templateContent);
|
|
||||||
|
|
||||||
// can be changed if specified by '?from=<name>'
|
// can be changed if specified by '?from=<name>'
|
||||||
let initialConfig = {
|
let initialConfig = {
|
||||||
title: 'Internal server error',
|
title: 'Internal server error',
|
||||||
@@ -101,6 +103,31 @@ const PRESETS = {
|
|||||||
what_happened: 'This site is still working. And it looks great.',
|
what_happened: 'This site is still working. And it looks great.',
|
||||||
what_can_i_do: 'Visit the site before it crashes someday.',
|
what_can_i_do: 'Visit the site before it crashes someday.',
|
||||||
},
|
},
|
||||||
|
teapot: {
|
||||||
|
title: "I'm a teapot",
|
||||||
|
error_code: '418',
|
||||||
|
more_information: {
|
||||||
|
text: 'rfc2324',
|
||||||
|
link: 'https://www.rfc-editor.org/rfc/rfc2324',
|
||||||
|
},
|
||||||
|
browser_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'You',
|
||||||
|
status_text: 'Working',
|
||||||
|
},
|
||||||
|
cloudflare_status: {
|
||||||
|
status: 'ok',
|
||||||
|
status_text: 'Working',
|
||||||
|
},
|
||||||
|
host_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'Teapot',
|
||||||
|
status_text: 'Working',
|
||||||
|
},
|
||||||
|
error_source: 'host',
|
||||||
|
what_happened: "The server can not brew coffee for it is a teapot.",
|
||||||
|
what_can_i_do: 'Please try a different coffee machine.',
|
||||||
|
},
|
||||||
consensual: {
|
consensual: {
|
||||||
title: 'The Myth Of "Consensual" Internet',
|
title: 'The Myth Of "Consensual" Internet',
|
||||||
error_code: 'lmao',
|
error_code: 'lmao',
|
||||||
@@ -255,24 +282,9 @@ function readConfig() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function formatUtcTimestamp() {
|
|
||||||
const d = new Date();
|
|
||||||
|
|
||||||
const year = d.getUTCFullYear();
|
|
||||||
const month = String(d.getUTCMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(d.getUTCDate()).padStart(2, '0');
|
|
||||||
|
|
||||||
const hours = String(d.getUTCHours()).padStart(2, '0');
|
|
||||||
const minutes = String(d.getUTCMinutes()).padStart(2, '0');
|
|
||||||
const seconds = String(d.getUTCSeconds()).padStart(2, '0');
|
|
||||||
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderEjs(params) {
|
function renderEjs(params) {
|
||||||
return template({
|
return render_cf_error_page(params, false);
|
||||||
params: params,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Basic render: build HTML string from config and put into iframe.srcdoc */
|
/* Basic render: build HTML string from config and put into iframe.srcdoc */
|
||||||
@@ -280,9 +292,8 @@ function render() {
|
|||||||
const cfg = readConfig();
|
const cfg = readConfig();
|
||||||
window.lastCfg = cfg;
|
window.lastCfg = cfg;
|
||||||
|
|
||||||
cfg.time = formatUtcTimestamp();
|
// TODO: input box for Ray ID / Client IP?
|
||||||
cfg.ray_id = '0123456789abcdef';
|
cfg.ray_id = '0123456789abcdef';
|
||||||
cfg.client_ip = '1.1.1.1';
|
|
||||||
if (Number.isNaN(Number(cfg.error_code))) {
|
if (Number.isNaN(Number(cfg.error_code))) {
|
||||||
cfg.html_title = cfg.title || 'Internal server error';
|
cfg.html_title = cfg.title || 'Internal server error';
|
||||||
}
|
}
|
||||||
@@ -305,8 +316,8 @@ function render() {
|
|||||||
let lastRenderedHtml = '';
|
let lastRenderedHtml = '';
|
||||||
function openInNewTab() {
|
function openInNewTab() {
|
||||||
if (!lastRenderedHtml) render();
|
if (!lastRenderedHtml) render();
|
||||||
const wnd = window.open()
|
const wnd = window.open();
|
||||||
wnd.document.documentElement.innerHTML = lastRenderedHtml
|
wnd.document.documentElement.innerHTML = lastRenderedHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createShareableLink() {
|
function createShareableLink() {
|
||||||
@@ -445,15 +456,21 @@ function updateSaveAsDialog(e) {
|
|||||||
}
|
}
|
||||||
const params = { ...lastCfg };
|
const params = { ...lastCfg };
|
||||||
delete params.time;
|
delete params.time;
|
||||||
|
delete params.ray_id;
|
||||||
|
let language;
|
||||||
if (codegen) {
|
if (codegen) {
|
||||||
saveAsContent = codegen.generate(params);
|
saveAsContent = codegen.generate(params);
|
||||||
|
language = codegen.language;
|
||||||
} else if (saveAsType == 'static') {
|
} else if (saveAsType == 'static') {
|
||||||
render() // rerender the page
|
render(); // rerender the page
|
||||||
saveAsContent = lastRenderedHtml;
|
saveAsContent = lastRenderedHtml;
|
||||||
|
language = 'html';
|
||||||
} else {
|
} else {
|
||||||
throw new Error('unexpected saveAsType=' + saveAsType)
|
throw new Error('unexpected saveAsType=' + saveAsType);
|
||||||
}
|
}
|
||||||
$('saveAsDialogCode').value = saveAsContent;
|
const html = Prism.highlight(saveAsContent, Prism.languages[language], language);
|
||||||
|
|
||||||
|
$('saveAsDialogCode').innerHTML = html;
|
||||||
$('saveAsDialogCode').scrollTop = 0;
|
$('saveAsDialogCode').scrollTop = 0;
|
||||||
|
|
||||||
document.querySelectorAll('#saveAsDialogTypes button').forEach((element) => {
|
document.querySelectorAll('#saveAsDialogTypes button').forEach((element) => {
|
||||||
@@ -475,32 +492,37 @@ document.querySelectorAll('#saveAsDialogTypes button').forEach((element) => {
|
|||||||
|
|
||||||
const saveAsDialogCopyPopover = new Popover($('saveAsDialogCopyBtn'));
|
const saveAsDialogCopyPopover = new Popover($('saveAsDialogCopyBtn'));
|
||||||
$('saveAsDialogCopyBtn').addEventListener('click', (e) => {
|
$('saveAsDialogCopyBtn').addEventListener('click', (e) => {
|
||||||
const field = $('saveAsDialogCode');
|
navigator.clipboard.writeText(saveAsContent).then(() => {
|
||||||
field.select();
|
|
||||||
// field.setSelectionRange(0, field.value.length);
|
|
||||||
navigator.clipboard.writeText(field.value).then(() => {
|
|
||||||
saveAsDialogCopyPopover.show();
|
saveAsDialogCopyPopover.show();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
saveAsDialogCopyPopover.hide();
|
saveAsDialogCopyPopover.hide();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
function toSnakeCase(str, separator = '_') {
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize('NFKC')
|
||||||
|
.replace(/[^\p{L}\p{N}]+/gu, '_') // keep letters & numbers from ALL languages
|
||||||
|
.replace(/^_+|_+$/g, '')
|
||||||
|
.replaceAll('_', separator);
|
||||||
|
}
|
||||||
|
const saveAsExtensions = {
|
||||||
|
js: 'js',
|
||||||
|
json: 'json',
|
||||||
|
python: 'py',
|
||||||
|
static: 'html',
|
||||||
|
};
|
||||||
$('saveAsDialogSaveBtn').addEventListener('click', (e) => {
|
$('saveAsDialogSaveBtn').addEventListener('click', (e) => {
|
||||||
let saveName = '';
|
let name = lastCfg.title || 'Internal server error';
|
||||||
switch (saveAsType) {
|
switch (saveAsType) {
|
||||||
case 'js':
|
case 'js':
|
||||||
saveName = 'cf-error-page-example.js';
|
|
||||||
break;
|
|
||||||
case 'json':
|
|
||||||
saveName = 'cf-error-page-params.json';
|
|
||||||
break;
|
|
||||||
case 'python':
|
case 'python':
|
||||||
saveName = 'cf_error_page_example.py';
|
name += ' example';
|
||||||
break;
|
break;
|
||||||
case 'static':
|
|
||||||
saveName = 'cf_error_page.html';
|
|
||||||
break;
|
|
||||||
// TODO: name output files using page title
|
|
||||||
}
|
}
|
||||||
|
const separator = saveAsType == 'python' ? '_' : '-';
|
||||||
|
const ext = saveAsExtensions[saveAsType] ?? txt;
|
||||||
|
const saveName = `${toSnakeCase(name, separator)}.${ext}`;
|
||||||
saveFile(saveAsContent, saveName);
|
saveFile(saveAsContent, saveName);
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -13,6 +13,9 @@ export default defineConfig(({ mode }) => {
|
|||||||
minify: true,
|
minify: true,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
dedupe: ['ejs'],
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
proxy: {
|
proxy: {
|
||||||
|
|||||||
@@ -310,6 +310,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~7.16.0"
|
undici-types "~7.16.0"
|
||||||
|
|
||||||
|
"@types/prismjs@^1.26.5":
|
||||||
|
version "1.26.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.5.tgz#72499abbb4c4ec9982446509d2f14fb8483869d6"
|
||||||
|
integrity sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==
|
||||||
|
|
||||||
acorn@^8.15.0:
|
acorn@^8.15.0:
|
||||||
version "8.15.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
|
||||||
@@ -392,6 +397,11 @@ clean-css@~5.3.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
source-map "~0.6.0"
|
source-map "~0.6.0"
|
||||||
|
|
||||||
|
cloudflare-error-page@../../javascript:
|
||||||
|
version "0.1.0"
|
||||||
|
dependencies:
|
||||||
|
ejs "^3.1.10"
|
||||||
|
|
||||||
commander@^10.0.0:
|
commander@^10.0.0:
|
||||||
version "10.0.1"
|
version "10.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
|
||||||
@@ -613,6 +623,11 @@ prettier@3.7.4:
|
|||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f"
|
||||||
integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==
|
integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==
|
||||||
|
|
||||||
|
prismjs@^1.30.0:
|
||||||
|
version "1.30.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.30.0.tgz#d9709969d9d4e16403f6f348c63553b19f0975a9"
|
||||||
|
integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==
|
||||||
|
|
||||||
readdirp@~3.6.0:
|
readdirp@~3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||||
|
|||||||
2
nodejs/.gitignore → javascript/.gitignore
vendored
2
nodejs/.gitignore → javascript/.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
|
*.tgz
|
||||||
|
|
||||||
*.log
|
*.log
|
||||||
*.html
|
*.html
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@@ -11,7 +11,7 @@ npm install cloudflare-error-page
|
|||||||
Or install from GitHub:
|
Or install from GitHub:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install git+https://github.com/donlon/cloudflare-error-page.git#main:nodejs
|
npm install git+https://github.com/donlon/cloudflare-error-page.git#javascriptnodejs
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
@@ -34,7 +34,7 @@ fs.writeFileSync('error.html', errorPage);
|
|||||||
|
|
||||||
## API Reference
|
## API Reference
|
||||||
|
|
||||||
### `render(params: ErrorPageParams, allowHtml?: boolean): string`
|
### `render(params: ErrorPageParams, allowHtml?: boolean, moreArgs?: { [name: string]: any; }): string`
|
||||||
|
|
||||||
Generates an HTML error page based on the provided parameters.
|
Generates an HTML error page based on the provided parameters.
|
||||||
|
|
||||||
@@ -42,13 +42,14 @@ Generates an HTML error page based on the provided parameters.
|
|||||||
|
|
||||||
- `params`: An object containing error page configuration
|
- `params`: An object containing error page configuration
|
||||||
- `allowHtml` (optional): Whether to allow HTML in `what_happened` and `what_can_i_do` fields. Default: `true`
|
- `allowHtml` (optional): Whether to allow HTML in `what_happened` and `what_can_i_do` fields. Default: `true`
|
||||||
|
- `moreArgs` (optional): More arguments passed to the ejs template
|
||||||
|
|
||||||
#### ErrorPageParams Interface
|
#### ErrorPageParams Interface
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface ErrorPageParams {
|
interface ErrorPageParams {
|
||||||
// Basic information
|
// Basic information
|
||||||
error_code?: number; // Default: 500
|
error_code?: string | number; // Default: 500
|
||||||
title?: string; // Default: 'Internal server error'
|
title?: string; // Default: 'Internal server error'
|
||||||
html_title?: string; // Default: '{error_code}: {title}'
|
html_title?: string; // Default: '{error_code}: {title}'
|
||||||
time?: string; // Auto-generated if not provided
|
time?: string; // Auto-generated if not provided
|
||||||
677
javascript/package-lock.json
generated
Normal file
677
javascript/package-lock.json
generated
Normal file
@@ -0,0 +1,677 @@
|
|||||||
|
{
|
||||||
|
"name": "cloudflare-error-page",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "cloudflare-error-page",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ejs": "^3.1.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-typescript": "^12.3.0",
|
||||||
|
"@rollup/pluginutils": "^5.3.0",
|
||||||
|
"@types/ejs": "^3.1.5",
|
||||||
|
"@types/node": "^20.0.0",
|
||||||
|
"rollup": "^4.54.0",
|
||||||
|
"tslib": "^2.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-typescript": {
|
||||||
|
"version": "12.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.3.0.tgz",
|
||||||
|
"integrity": "sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^5.1.0",
|
||||||
|
"resolve": "^1.22.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^2.14.0||^3.0.0||^4.0.0",
|
||||||
|
"tslib": "*",
|
||||||
|
"typescript": ">=3.7.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/pluginutils": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "^1.0.0",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"picomatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@types/ejs": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/estree": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.19.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.26.tgz",
|
||||||
|
"integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/async": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ejs": {
|
||||||
|
"version": "3.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||||
|
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"jake": "^10.8.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ejs": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/filelist": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hasown": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-core-module": {
|
||||||
|
"version": "2.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||||
|
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jake": {
|
||||||
|
"version": "10.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
|
||||||
|
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"async": "^3.2.6",
|
||||||
|
"filelist": "^1.0.4",
|
||||||
|
"picocolors": "^1.1.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"jake": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-parse": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/resolve": {
|
||||||
|
"version": "1.22.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
||||||
|
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-core-module": "^2.16.1",
|
||||||
|
"path-parse": "^1.0.7",
|
||||||
|
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"resolve": "bin/resolve"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "4.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
|
||||||
|
"integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "1.0.8"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-android-arm-eabi": "4.54.0",
|
||||||
|
"@rollup/rollup-android-arm64": "4.54.0",
|
||||||
|
"@rollup/rollup-darwin-arm64": "4.54.0",
|
||||||
|
"@rollup/rollup-darwin-x64": "4.54.0",
|
||||||
|
"@rollup/rollup-freebsd-arm64": "4.54.0",
|
||||||
|
"@rollup/rollup-freebsd-x64": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-arm64-musl": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-loong64-gnu": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "4.54.0",
|
||||||
|
"@rollup/rollup-linux-x64-musl": "4.54.0",
|
||||||
|
"@rollup/rollup-openharmony-arm64": "4.54.0",
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": "4.54.0",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.54.0",
|
||||||
|
"@rollup/rollup-win32-x64-gnu": "4.54.0",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.54.0",
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "0BSD",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare-error-page",
|
"name": "cloudflare-error-page",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"description": "Cloudflare Error Page Generator",
|
"description": "Cloudflare Error Page Generator",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "node scripts/copy-files.js && rollup -c",
|
||||||
|
"dev": "rollup -c -w",
|
||||||
"prepublishOnly": "pnpm run build",
|
"prepublishOnly": "pnpm run build",
|
||||||
"example": "node examples/example.js"
|
"example": "node examples/example.js"
|
||||||
},
|
},
|
||||||
@@ -32,9 +33,12 @@
|
|||||||
"ejs": "^3.1.10"
|
"ejs": "^3.1.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-typescript": "^12.3.0",
|
||||||
|
"@rollup/pluginutils": "^5.3.0",
|
||||||
"@types/ejs": "^3.1.5",
|
"@types/ejs": "^3.1.5",
|
||||||
"@types/node": "^20.0.0",
|
"@types/node": "^20.0.0",
|
||||||
"typescript": "^5.3.0"
|
"rollup": "^4.54.0",
|
||||||
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist/**/*",
|
"dist/**/*",
|
||||||
35
javascript/rollup.config.ts
Normal file
35
javascript/rollup.config.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import typescript from "@rollup/plugin-typescript";
|
||||||
|
import { createFilter } from "@rollup/pluginutils";
|
||||||
|
// import pkg from './package.json' with { type: 'json' };
|
||||||
|
|
||||||
|
function createRawImportPlugin(include: string) {
|
||||||
|
const rawFilter = createFilter(include);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "raw-import",
|
||||||
|
|
||||||
|
transform(code: string, id: string): any {
|
||||||
|
if (rawFilter(id)) {
|
||||||
|
return {
|
||||||
|
code: `export default ${JSON.stringify(code)};`,
|
||||||
|
map: { mappings: "" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: "src/index.ts",
|
||||||
|
output: {
|
||||||
|
// file: pkg.module,
|
||||||
|
file: "dist/index.js",
|
||||||
|
format: "esm",
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
include: "src/**",
|
||||||
|
},
|
||||||
|
external: ["ejs"],
|
||||||
|
plugins: [typescript(), createRawImportPlugin("**/templates/**")],
|
||||||
|
};
|
||||||
14
javascript/scripts/copy-files.js
Normal file
14
javascript/scripts/copy-files.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
// Resolve the directory of this script
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
// Define paths relative to scripts/copy.js
|
||||||
|
const src = path.resolve(__dirname, "../../resources/styles/main.css");
|
||||||
|
const dest = path.resolve(__dirname, "../src/templates/main.css");
|
||||||
|
|
||||||
|
// Copy file
|
||||||
|
fs.copyFileSync(src, dest);
|
||||||
4
javascript/src/assets.d.ts
vendored
Normal file
4
javascript/src/assets.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
declare module "./templates/*" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
100
javascript/src/index.ts
Normal file
100
javascript/src/index.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import * as ejs from "ejs";
|
||||||
|
|
||||||
|
import templateString from "./templates/template.ejs";
|
||||||
|
import cssString from "./templates/main.css";
|
||||||
|
|
||||||
|
export interface StatusItem {
|
||||||
|
status?: "ok" | "error";
|
||||||
|
status_text?: string;
|
||||||
|
status_text_color?: string;
|
||||||
|
location?: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MoreInformation {
|
||||||
|
hidden?: boolean;
|
||||||
|
link?: string;
|
||||||
|
text?: string;
|
||||||
|
for?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerfSecBy {
|
||||||
|
link?: string;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatorInfo {
|
||||||
|
hidden?: boolean;
|
||||||
|
link?: string;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ErrorPageParams {
|
||||||
|
error_code?: string | number;
|
||||||
|
title?: string;
|
||||||
|
html_title?: string;
|
||||||
|
time?: string;
|
||||||
|
ray_id?: string;
|
||||||
|
client_ip?: string;
|
||||||
|
|
||||||
|
browser_status?: StatusItem;
|
||||||
|
cloudflare_status?: StatusItem;
|
||||||
|
host_status?: StatusItem;
|
||||||
|
|
||||||
|
error_source?: "browser" | "cloudflare" | "host";
|
||||||
|
|
||||||
|
what_happened?: string;
|
||||||
|
what_can_i_do?: string;
|
||||||
|
|
||||||
|
more_information?: MoreInformation;
|
||||||
|
perf_sec_by?: PerfSecBy;
|
||||||
|
creator_info?: CreatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load EJS template
|
||||||
|
export const baseTemplate: ejs.TemplateFunction = ejs.compile(templateString);
|
||||||
|
/**
|
||||||
|
* Generate random hex string for ray-id
|
||||||
|
*/
|
||||||
|
function genHexString(digits: number): string {
|
||||||
|
const hex = "0123456789ABCDEF";
|
||||||
|
let output = "";
|
||||||
|
for (let i = 0; i < digits; i++) {
|
||||||
|
output += hex.charAt(Math.floor(Math.random() * hex.length));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a customized Cloudflare error page
|
||||||
|
* @param params - The parameters for the error page
|
||||||
|
* @param allowHtml - Whether to allow HTML in what_happened and what_can_i_do fields (default: true)
|
||||||
|
* @param moreArgs - More arguments passed to the ejs template
|
||||||
|
* @returns The rendered HTML string
|
||||||
|
*/
|
||||||
|
export function render(
|
||||||
|
params: ErrorPageParams,
|
||||||
|
allowHtml: boolean = true,
|
||||||
|
moreArgs: {
|
||||||
|
[name: string]: any;
|
||||||
|
} = {}
|
||||||
|
): string {
|
||||||
|
params = { ...params };
|
||||||
|
|
||||||
|
if (!params.time) {
|
||||||
|
const now = new Date();
|
||||||
|
params.time = now.toISOString().replace("T", " ").substring(0, 19) + " UTC";
|
||||||
|
}
|
||||||
|
if (!params.ray_id) {
|
||||||
|
params.ray_id = genHexString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowHtml) {
|
||||||
|
params.what_happened = ejs.escapeXML(params.what_happened ?? "");
|
||||||
|
params.what_can_i_do = ejs.escapeXML(params.what_can_i_do ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseTemplate({ params, html_style: cssString, ...moreArgs });
|
||||||
|
}
|
||||||
|
|
||||||
|
export default render;
|
||||||
1
javascript/src/templates/.gitignore
vendored
Normal file
1
javascript/src/templates/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
main.css
|
||||||
@@ -4,59 +4,48 @@
|
|||||||
<!--[if IE 8]> <html class="no-js ie8 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]-->
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en-US"> <!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<%
|
<% const error_code = params.error_code || 500; -%>
|
||||||
let error_code = params.error_code || 500;
|
<% const title = params.title || 'Internal server error'; -%>
|
||||||
let title = params.title || 'Internal server error';
|
<% const html_title = params.html_title || (error_code + ': ' + title); -%>
|
||||||
let html_title_output = params.html_title || (error_code + ': ' + title);
|
<title><%= html_title %></title>
|
||||||
%>
|
|
||||||
<title><%= html_title_output %></title>
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
<!-- @INLINE_CSS_HERE@ -->
|
<style>
|
||||||
|
<%- html_style -%>
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="cf-wrapper">
|
<div id="cf-wrapper">
|
||||||
<div id="cf-error-details" class="p-0">
|
<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">
|
<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">
|
<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="inline-block"><%= title %></span>
|
||||||
<span class="code-label">Error code <%= error_code %></span>
|
<span class="code-label">Error code <%= error_code %></span>
|
||||||
</h1>
|
</h1>
|
||||||
<% let more_info = params.more_information || {}; %>
|
<%_ const more_info = params.more_information || {}; -%>
|
||||||
<% if (!more_info.hidden) { %>
|
<%_ if (!more_info.hidden) { -%>
|
||||||
<div>
|
<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" %>.
|
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>
|
||||||
<% } %>
|
<%_ } -%>
|
||||||
<div class="<%= more_info.hidden ? '' : 'mt-3' %>"><%= params.time %></div>
|
<div class="<%= more_info.hidden ? '' : 'mt-3' %>"><%= params.time %></div>
|
||||||
</header>
|
</header>
|
||||||
<div class="my-8 bg-gradient-gray">
|
<div class="my-8 bg-gradient-gray">
|
||||||
<div class="w-240 lg:w-full mx-auto">
|
<div class="w-240 lg:w-full mx-auto">
|
||||||
<div class="clearfix md:px-8">
|
<div class="clearfix md:px-8">
|
||||||
<% for (let item_id of ['browser', 'cloudflare', 'host']) { %>
|
<%_
|
||||||
<%
|
const items = [
|
||||||
let icon, default_location, default_name, text_color, status_text;
|
{ id: 'browser', icon: 'browser', default_location: 'You', default_name: 'Browser' },
|
||||||
|
{ id: 'cloudflare', icon: 'cloud', default_location: 'San Francisco', default_name: 'Cloudflare' },
|
||||||
if (item_id === 'browser') {
|
{ id: 'host', icon: 'server', default_location: 'Website', default_name: 'Host' }
|
||||||
icon = 'browser';
|
];
|
||||||
default_location = 'You';
|
items.forEach(({ id, icon, default_location, default_name }) => {
|
||||||
default_name = 'Browser';
|
const item = params[id + '_status'] || {};
|
||||||
} else if (item_id === 'cloudflare') {
|
const status = item.status || 'ok';
|
||||||
icon = 'cloud';
|
let text_color;
|
||||||
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) {
|
if (item.status_text_color) {
|
||||||
text_color = item.status_text_color;
|
text_color = item.status_text_color;
|
||||||
} else if (status === 'ok') {
|
} else if (status === 'ok') {
|
||||||
@@ -64,27 +53,19 @@ let html_title_output = params.html_title || (error_code + ': ' + title);
|
|||||||
} else if (status === 'error') {
|
} else if (status === 'error') {
|
||||||
text_color = '#bd2426'; // text-red-error
|
text_color = '#bd2426'; // text-red-error
|
||||||
}
|
}
|
||||||
|
const status_text = item.status_text || (status === 'ok' ? 'Working' : 'Error');
|
||||||
status_text = item.status_text || (status === 'ok' ? 'Working' : 'Error');
|
const is_error_source = params.error_source === id;
|
||||||
%>
|
-%>
|
||||||
<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 id="cf-<%= id %>-status" class="<%= is_error_source ? '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">
|
<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-<%= 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>
|
<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>
|
</div>
|
||||||
<span class="md:block w-full truncate"><%= item.location || default_location %></span>
|
<span class="md:block w-full truncate"><%= item.location || default_location %></span>
|
||||||
<%
|
<h3 class="md:inline-block mt-3 md:mt-0 text-2xl text-gray-600 font-light leading-1.3" <%- (item.name || default_name) === 'Cloudflare' ? 'style="color: #2f7bbf;"' : '' %>><%= item.name || default_name %></h3>
|
||||||
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>
|
<span class="leading-1.3 text-2xl" style="color: <%= text_color %>"><%= status_text %></span>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<%_ }); -%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,11 +74,11 @@ let html_title_output = params.html_title || (error_code + ': ' + title);
|
|||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
<div class="w-1/2 md:w-full float-left pr-6 md:pb-10 md:pr-0 leading-relaxed">
|
<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>
|
<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.') %>
|
<%- params.what_happened || '<p>There is an internal server error on Cloudflare\'s network.</p>' %>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-1/2 md:w-full float-left leading-relaxed">
|
<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>
|
<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.') %>
|
<%- params.what_can_i_do || '<p>Please try again in a few minutes.</p>' %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,12 +93,17 @@ let html_title_output = params.html_title || (error_code + ': ' + title);
|
|||||||
<span class="hidden" id="cf-footer-ip"><%= params.client_ip || '1.1.1.1' %></span>
|
<span class="hidden" id="cf-footer-ip"><%= params.client_ip || '1.1.1.1' %></span>
|
||||||
<span class="cf-footer-separator sm:hidden">•</span>
|
<span class="cf-footer-separator sm:hidden">•</span>
|
||||||
</span>
|
</span>
|
||||||
<% let perf_sec_by = params.perf_sec_by || {}; %>
|
<% const perf_sec_by = params.perf_sec_by || {}; %>
|
||||||
<span class="cf-footer-item sm:block sm:mb-1"><span>Performance & 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>
|
<span class="cf-footer-item sm:block sm:mb-1"><span>Performance & 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>
|
||||||
|
<%_ const creator_info = params.creator_info || {}; _%>
|
||||||
|
<%_ if (!(creator_info.hidden ?? true)) { _%>
|
||||||
|
<span class="cf-footer-separator sm:hidden">•</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>
|
||||||
|
<%_ } -%>
|
||||||
</p>
|
</p>
|
||||||
</div><!-- /.error-footer -->
|
</div><!-- /.error-footer -->
|
||||||
</div>
|
</div>
|
||||||
</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>
|
<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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
142
nodejs/package-lock.json
generated
142
nodejs/package-lock.json
generated
@@ -1,142 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cloudflare-error-page",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "cloudflare-error-page",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ejs": "^3.1.10"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/ejs": "^3.1.5",
|
|
||||||
"@types/node": "^20.0.0",
|
|
||||||
"typescript": "^5.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/ejs": {
|
|
||||||
"version": "3.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz",
|
|
||||||
"integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/node": {
|
|
||||||
"version": "20.19.26",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.26.tgz",
|
|
||||||
"integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"undici-types": "~6.21.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/async": {
|
|
||||||
"version": "3.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
|
||||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/brace-expansion": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ejs": {
|
|
||||||
"version": "3.1.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
|
||||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"jake": "^10.8.5"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"ejs": "bin/cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/filelist": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"minimatch": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jake": {
|
|
||||||
"version": "10.9.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
|
|
||||||
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"async": "^3.2.6",
|
|
||||||
"filelist": "^1.0.4",
|
|
||||||
"picocolors": "^1.1.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"jake": "bin/cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minimatch": {
|
|
||||||
"version": "5.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
|
||||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/picocolors": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
|
||||||
"version": "5.9.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici-types": {
|
|
||||||
"version": "6.21.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
import * as ejs from "ejs";
|
|
||||||
import * as crypto from "crypto";
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
|
|
||||||
export interface StatusItem {
|
|
||||||
status?: "ok" | "error";
|
|
||||||
status_text?: string;
|
|
||||||
status_text_color?: string;
|
|
||||||
location?: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MoreInformation {
|
|
||||||
hidden?: boolean;
|
|
||||||
link?: string;
|
|
||||||
text?: string;
|
|
||||||
for?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PerfSecBy {
|
|
||||||
link?: string;
|
|
||||||
text?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreatorInfo {
|
|
||||||
hidden?: boolean;
|
|
||||||
link?: string;
|
|
||||||
text?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ErrorPageParams {
|
|
||||||
error_code?: number;
|
|
||||||
title?: string;
|
|
||||||
html_title?: string;
|
|
||||||
time?: string;
|
|
||||||
ray_id?: string;
|
|
||||||
client_ip?: string;
|
|
||||||
|
|
||||||
browser_status?: StatusItem;
|
|
||||||
cloudflare_status?: StatusItem;
|
|
||||||
host_status?: StatusItem;
|
|
||||||
|
|
||||||
error_source?: "browser" | "cloudflare" | "host";
|
|
||||||
|
|
||||||
what_happened?: string;
|
|
||||||
what_can_i_do?: string;
|
|
||||||
|
|
||||||
more_information?: MoreInformation;
|
|
||||||
perf_sec_by?: PerfSecBy;
|
|
||||||
creator_info?: CreatorInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill default parameters if not provided
|
|
||||||
*/
|
|
||||||
function fillParams(params: ErrorPageParams): ErrorPageParams {
|
|
||||||
const filledParams = { ...params };
|
|
||||||
|
|
||||||
if (!filledParams.time) {
|
|
||||||
const now = new Date();
|
|
||||||
filledParams.time =
|
|
||||||
now.toISOString().replace("T", " ").substring(0, 19) + " UTC";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filledParams.ray_id) {
|
|
||||||
filledParams.ray_id = crypto.randomBytes(8).toString("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
return filledParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape HTML special characters
|
|
||||||
*/
|
|
||||||
function escapeHtml(text: string): string {
|
|
||||||
const htmlEscapeMap: Record<string, string> = {
|
|
||||||
"&": "&",
|
|
||||||
"<": "<",
|
|
||||||
">": ">",
|
|
||||||
'"': """,
|
|
||||||
"'": "'",
|
|
||||||
};
|
|
||||||
return text.replace(/[&<>"']/g, (char) => htmlEscapeMap[char] || char);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render a customized Cloudflare error page
|
|
||||||
* @param params - The parameters for the error page
|
|
||||||
* @param allowHtml - Whether to allow HTML in what_happened and what_can_i_do fields (default: true)
|
|
||||||
* @returns The rendered HTML string
|
|
||||||
*/
|
|
||||||
export function render(
|
|
||||||
params: ErrorPageParams,
|
|
||||||
allowHtml: boolean = true
|
|
||||||
): string {
|
|
||||||
let processedParams = fillParams(params);
|
|
||||||
|
|
||||||
if (!allowHtml) {
|
|
||||||
processedParams = { ...processedParams };
|
|
||||||
if (processedParams.what_happened) {
|
|
||||||
processedParams.what_happened = escapeHtml(processedParams.what_happened);
|
|
||||||
}
|
|
||||||
if (processedParams.what_can_i_do) {
|
|
||||||
processedParams.what_can_i_do = escapeHtml(processedParams.what_can_i_do);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load EJS template
|
|
||||||
const templatePath = path.join(__dirname, "..", "templates", "error.ejs");
|
|
||||||
|
|
||||||
const template = fs.readFileSync(templatePath, "utf-8");
|
|
||||||
|
|
||||||
const rendered = ejs.render(template, { params: processedParams });
|
|
||||||
|
|
||||||
return rendered;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default render;
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -65,8 +65,3 @@ def generate_inlined_css():
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
generate_inlined_css()
|
generate_inlined_css()
|
||||||
inline_css_resource(
|
|
||||||
os.path.join(resources_folder, 'templates/error.ejs'),
|
|
||||||
os.path.join(resources_folder, 'styles/main.css'),
|
|
||||||
os.path.join(root, 'editor/web/src/template.ejs'),
|
|
||||||
)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user