From 01ae1d7e92e5f5d13ce3efd156037880851980fe Mon Sep 17 00:00:00 2001 From: my Date: Thu, 29 Jan 2026 12:24:19 +0100 Subject: [PATCH] handle yaml errors --- octomode.py | 71 ++++++++++++++++++++++++++++++++++++++++---- pad_filters.py | 3 +- settings.py | 10 ++++--- static/main.css | 10 +++++-- templates/error.html | 35 ++++++++++++++++++++++ templates/start.html | 12 ++++++++ 6 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 templates/error.html diff --git a/octomode.py b/octomode.py index 0cf8e03..28172dc 100755 --- a/octomode.py +++ b/octomode.py @@ -25,6 +25,13 @@ APP.config.from_pyfile('settings.py') # --- +# my attempt as showing helpful error messages. +class MarkdownRenderError(RuntimeError): + def __init__(self, kind, message, original): + super().__init__(message) + self.kind = kind + self.original = original + def get_pad_content(pad_name, ext=""): if ext: pad_name = f'{ pad_name }{ ext }' @@ -62,6 +69,22 @@ def all_pads(): return response +# get all pads that end in .md +def all_publications(): + pads = all_pads() + pubs = [] + print(pads) + if(pads and pads['data'] and pads['data']['padIDs']): + for pad in pads['data']['padIDs']: + # if extension is .md add it to pubs + if pad.endswith('.md'): + # strip the .md + name = pad.removesuffix('.md') + pubs.append(name) + return pubs + else: + return [] + def create_pad_on_first_run(name, ext): pads = all_pads() pad = name+ext @@ -87,7 +110,21 @@ def create_pad_on_first_run(name, ext): def md_to_html(md_pad_content): # Convert Markdown to HTML # html = markdown.markdown(md_pad_content, extensions=['meta', 'attr_list']) # attr_list does not work - html = pypandoc.convert_text(md_pad_content, 'html', format='md') + try: + html = pypandoc.convert_text(md_pad_content, 'html', format='md') + except RuntimeError as exc: + message = str(exc) + if "YAML" in message or "YAML metadata" in message: + raise MarkdownRenderError( + kind="yaml", + message="Invalid YAML metadata. Use `key: value` and add a blank line.", + original=str(exc) + ) from exc + raise MarkdownRenderError( + kind="markdown", + message = "Markdown conversion failed. Please check the markdown for errors.", + original = str(exc) + ) from exc # Sanitize the Markdown # html = bleach.clean(html) @@ -125,6 +162,17 @@ def get_app_root(): def get_meta(metadata, key, default=None): return metadata.get(key, [default])[0] +def render_markdown_error(name, error): + print("ERROR", error) + return render_template( + 'error.html', + name=name.strip(), + error_text=str(error), + error=error, + lang="en", + title="Markdown error" + ), 400 + # --- @APP.route('/', methods=['GET', 'POST']) @@ -140,7 +188,8 @@ def index(): create_pad_on_first_run(name, ext) return redirect(url_for("pad", name=name)) else: - return render_template('start.html', home_pad_url=APP.config['HOME_PAD_URL']) + pubs = all_publications() + return render_template('start.html', pubs = pubs, home_pad_url=APP.config['HOME_PAD_URL']) @APP.route('//') def main(name): @@ -190,7 +239,10 @@ def css(name): def preview(name): # TO GENERATE THE PREVIEW WEBPAGE md_pad_content = get_pad_content(name, ext='.md') - html = md_to_html(md_pad_content) + try: + html = md_to_html(md_pad_content) + except MarkdownRenderError as exc: + return render_markdown_error(name, exc) metadata = get_md_metadata(md_pad_content) lang = get_meta(metadata, 'language', 'en') title = get_meta(metadata, 'title', 'Untitled') @@ -201,21 +253,26 @@ def preview(name): def pagedjs(name): # TO GENERATE THE PAGED.JS WEBPAGE md_pad_content = get_pad_content(name, ext='.md') - html = md_to_html(md_pad_content) + try: + html = md_to_html(md_pad_content) + except MarkdownRenderError as exc: + return render_markdown_error(name, exc) metadata = get_md_metadata(md_pad_content) lang = get_meta(metadata, 'language', 'en') title = get_meta(metadata, 'title', 'Untitled') cover = get_meta(metadata, 'cover', None) impose = False #request.args.get("impose") == "true" - return render_template('pagedjs.html', name=name.strip(), pad_content=html, lang=lang, title=title, cover=cover, impose=impose) @APP.route('//imposed.html') def imposed(name): # TO GENERATE THE IMPOSED WEBPAGE md_pad_content = get_pad_content(name, ext='.md') - html = md_to_html(md_pad_content) + try: + html = md_to_html(md_pad_content) + except MarkdownRenderError as exc: + return render_markdown_error(name, exc) metadata = get_md_metadata(md_pad_content) lang = get_meta(metadata, 'language', 'en') title = get_meta(metadata, 'title', 'Untitled') @@ -230,4 +287,6 @@ def imposed(name): if __name__ == '__main__': APP.debug = True APP.env = "development" + # APP.debug = False + # APP.env = "production" APP.run(host="0.0.0.0", port=APP.config["PORTNUMBER"], threaded=True) diff --git a/pad_filters.py b/pad_filters.py index a91048b..8bb4dda 100644 --- a/pad_filters.py +++ b/pad_filters.py @@ -4,7 +4,8 @@ from filter_registry import register_filter @register_filter def remove_star_before_img(text): # regex version - return re.sub(r'\*\s*!?\[\]\(', '![](', text) + # return re.sub(r'\*\s*!?\[\]\(', '![](', text) + return re.sub(r'\*\s*!?\[\.*]\(', '![](', text) # @register_filter # def remove_star_before_img(text): diff --git a/settings.py b/settings.py index 52b6f33..f2699e9 100644 --- a/settings.py +++ b/settings.py @@ -1,16 +1,18 @@ import os +from pathlib import Path from dotenv import load_dotenv -# Load environment variables from the .env file -load_dotenv() +# Load environment variables from the .env file alongside this module. +ENV_PATH = Path(__file__).resolve().parent / ".env" +load_dotenv(dotenv_path=ENV_PATH) # Bind them to Python variables APPLICATION_ROOT = os.environ.get('OCTOMODE_APPLICATION_ROOT', '/') PORTNUMBER = int(os.environ.get('OCTOMODE_PORTNUMBER', 5001)) PAD_URL = os.environ.get('OCTOMODE_PAD_URL', 'https://pad.vvvvvvaria.org') PAD_API_URL = os.environ.get('OCTOMODE_PAD_API_URL', 'https://pad.vvvvvvaria.org/api/1.2.15') -PAD_API_KEY = "97e3bf6a626eaaa3426833db3d936f3a" -#os.environ.get('OCTOMODE_PAD_API_KEY', '') +HOME_PAD_URL = os.environ.get('OCTOMODE_HOME_PAD_URL', '') +PAD_API_KEY = os.environ.get('OCTOMODE_PAD_API_KEY', '') # Check if API key is provided if not PAD_API_KEY or PAD_API_KEY == "XXX": diff --git a/static/main.css b/static/main.css index b344f5b..8418868 100644 --- a/static/main.css +++ b/static/main.css @@ -100,10 +100,16 @@ div#nav{ } +.cols { + display: grid; + grid-template-columns: 1fr 3fr; + margin-top: 3rem; +} + .home_pad_iframe { height: 75vh; - margin: 5rem; - width: calc(100vw - 13rem); + /* margin: 5rem; */ + /* width: calc(100vw - 13rem); */ border: 1px solid black; border-radius: 5px; } diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 0000000..9d998c1 --- /dev/null +++ b/templates/error.html @@ -0,0 +1,35 @@ + + + + + + + {{ title }} - {{ name }} + + +

{{ title }}

+

The document "{{name}}" has an error.

+ {% if error.kind == "yaml" %} +

It seems like that there's something wrong with the YAML at the top of your document.
+ It should be structured like this: +

+
+    ---
+    title: My Title
+    lang: en
+    cover: https://example.com/image.jpg
+    ---
+    
+

+ Make sure that there is a space between the ":" and the value. +

+ {% endif %} + {% elif error.kind == "markdown" %} + There's something wrong with the markdown in your pad. Please check the document or revert some of your recent changes. + {% endif %} +
+ Details + {{error.original}} +
+ + \ No newline at end of file diff --git a/templates/start.html b/templates/start.html index e6d21b9..7ef20cc 100644 --- a/templates/start.html +++ b/templates/start.html @@ -5,11 +5,23 @@ octomode +

in octomode

+
+

Publications

+

Below a list of the publications on the server.

+
    + {% for pub in pubs %} + {{pub}} + {% endfor %} +
+