diff --git a/README.md b/README.md index de97960..b817051 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This project creates customized error pages that mimics the well-known Cloudflar ## 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/). ![Editor](https://github.com/donlon/cloudflare-error-page/blob/images/editor.png?raw=true) diff --git a/editor/web/index.html b/editor/web/index.html index 93415a1..cf6bde3 100644 --- a/editor/web/index.html +++ b/editor/web/index.html @@ -173,7 +173,7 @@ max-height: 1200px !important; font-family: monospace; font-size: 0.8em !important; - overflow: auto; + overflow: hidden; } .save-as-dialog__buttons { @@ -475,7 +475,7 @@ @@ -547,7 +547,9 @@ Save -

+            
+

+            
diff --git a/editor/web/src/codegen/index.ts b/editor/web/src/codegen/index.ts index b871936..91ca7a8 100644 --- a/editor/web/src/codegen/index.ts +++ b/editor/web/src/codegen/index.ts @@ -1,4 +1,5 @@ import ejs from 'ejs'; +import { ErrorPageParams } from 'cloudflare-error-page'; import jsTemplate from './js.ejs?raw'; import jsonTemplate from './json.ejs?raw'; @@ -7,23 +8,97 @@ import pythonTemplate from './python.ejs?raw'; interface CodeGen { name: string; language: string; - generate(params: any): string; + generate(params: ErrorPageParams): string; } class EjsCodeGen implements CodeGen { name: string; language: string; private template: ejs.TemplateFunction; - constructor(name: string, language: string, templateContent: any) { + constructor(name: string, language: string, templateContent: string) { this.name = name; this.language = language; this.template = ejs.compile(templateContent); } - generate(params: any): string { - return this.template({ params }); + protected prepareTemplateArgs(params: ErrorPageParams): Record { + return {}; + } + generate(params: ErrorPageParams): string { + const moreArgs = this.prepareTemplateArgs(params); + return this.template({ params, ...moreArgs }); } } -export const jsCodeGen = new EjsCodeGen('NodeJS Example', 'javascript', jsTemplate); +function getErrorCode(error_code?: string | number) { + const errorCode = error_code || ''; + 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 { + 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 { + 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 EjsCodeGen('Python Example', 'python', pythonTemplate); +export const pythonCodeGen = new PythonCodeGen(pythonTemplate); diff --git a/editor/web/src/codegen/js.ejs b/editor/web/src/codegen/js.ejs index 9f4a506..31aa8a6 100644 --- a/editor/web/src/codegen/js.ejs +++ b/editor/web/src/codegen/js.ejs @@ -1,3 +1,11 @@ +#!/usr/bin/env node +/* + * JavaScript Example: Render and serve the Cloudflare error page using Express server + * + * Prerequisits: + * npm install cloudflare-error-page + */ + import express from 'express'; import { render as render_cf_error_page } from 'cloudflare-error-page'; @@ -5,10 +13,8 @@ const app = express(); const port = 3000; // Define a route for GET requests to the root URL -<%# TODO: format to JS-style object (key w/o parens) _%> -<% const errorCode = params.error_code || 500 _%> app.get('/', (req, res) => { - res.status(<%= /\d{3}/.test(errorCode + '') ? errorCode : 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 diff --git a/editor/web/src/codegen/json.ejs b/editor/web/src/codegen/json.ejs index ff9d4ad..f506349 100644 --- a/editor/web/src/codegen/json.ejs +++ b/editor/web/src/codegen/json.ejs @@ -1 +1 @@ -<%-JSON.stringify(params, null, 4)%> +<%- JSON.stringify(params, null, 4) %> diff --git a/editor/web/src/codegen/python.ejs b/editor/web/src/codegen/python.ejs index 1266634..772b1a6 100644 --- a/editor/web/src/codegen/python.ejs +++ b/editor/web/src/codegen/python.ejs @@ -1,31 +1,21 @@ -<% -// Covert the parameters to Python format object -const randomKey = Math.random() + '' -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') +#!/usr/bin/env python3 +# +# Python Example: Render and serve the Cloudflare error page using Flask server +# +# Prerequisits: +# pip install cloudflare-error-page +# -const errorCode = params.error_code || 500 -_%> -from flask import Flask +from flask import Flask, request from cloudflare_error_page import render as render_cf_error_page app = Flask(__name__) # Define a route for GET requests to the root URL -@app.route('/') +@app.route("/") def index(): # Render the error page - return render_cf_error_page(<%- paramsArg.replaceAll('\n', '\n ') %>), <%= /\d{3}/.test(errorCode + '') ? errorCode : 500 %> + return render_cf_error_page(<%- indentedParams %>), <%= errorCode %> -if __name__ == '__main__': +if __name__ == "__main__": app.run(debug=True, port=5000) diff --git a/editor/web/src/index.js b/editor/web/src/index.js index 6dff9d4..70830db 100644 --- a/editor/web/src/index.js +++ b/editor/web/src/index.js @@ -431,6 +431,7 @@ function updateSaveAsDialog(e) { } const params = { ...lastCfg }; delete params.time; + delete params.ray_id; let language; if (codegen) { saveAsContent = codegen.generate(params); @@ -445,6 +446,7 @@ function updateSaveAsDialog(e) { const html = Prism.highlight(saveAsContent, Prism.languages[language], language); $('saveAsDialogCode').innerHTML = html; + $('saveAsDialogCode').scrollTop = 0; document.querySelectorAll('#saveAsDialogTypes button').forEach((element) => { const isCurrent = element.dataset.type == saveAsType; diff --git a/javascript/src/index.ts b/javascript/src/index.ts index 8c2d99c..f827ce8 100644 --- a/javascript/src/index.ts +++ b/javascript/src/index.ts @@ -52,7 +52,9 @@ export interface ErrorPageParams { } // Load EJS template -export const baseTemplate: ejs.TemplateFunction = ejs.compile(templateString); +export const baseTemplate: ejs.TemplateFunction = ejs.compile(templateString, { + rmWhitespace: true +}); /** * Generate random hex string for ray-id