fixed Lua Runtime. Plugins now have priority for hooks and POST for routes if requested. also changed the CSS for main.css to color #1e1e1e for darkmode body
This commit is contained in:
196
lua/Actions.py
196
lua/Actions.py
@@ -1,12 +1,25 @@
|
||||
from pathlib import Path
|
||||
from log import Logger
|
||||
import re
|
||||
import json
|
||||
|
||||
PLUGINS_DIR = Path(__file__).parent / "plugins"
|
||||
HTML_DIR = Path(__file__).parent / "../html"
|
||||
HTML_DIR = Path(__file__).parent / "../html"
|
||||
MARKDOWN_DIR = Path(__file__).parent / ".." / "markdown"
|
||||
|
||||
class Actions:
|
||||
def __init__(self):
|
||||
"""Initialize Actions with directory paths"""
|
||||
self.plugins_dir = PLUGINS_DIR
|
||||
self.html_dir = HTML_DIR
|
||||
self.markdown_dir = MARKDOWN_DIR
|
||||
|
||||
# Ensure directories exist
|
||||
self.plugins_dir.mkdir(exist_ok=True)
|
||||
self.html_dir.mkdir(exist_ok=True)
|
||||
self.markdown_dir.mkdir(exist_ok=True)
|
||||
|
||||
# File I/O Operations
|
||||
def _safe_read_file(self, path):
|
||||
"""Safe file reading with path validation"""
|
||||
try:
|
||||
@@ -18,7 +31,7 @@ class Actions:
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error reading file {path}: {e}")
|
||||
return None
|
||||
@staticmethod
|
||||
|
||||
def _safe_write_file(self, path, content):
|
||||
"""Safe file writing with path validation"""
|
||||
try:
|
||||
@@ -32,8 +45,7 @@ class Actions:
|
||||
Logger.log_lua_error(f"Error writing file {path}: {e}")
|
||||
return False
|
||||
|
||||
# HTML/Markdown content operations
|
||||
@staticmethod
|
||||
# HTML/Markdown Content Operations
|
||||
def _read_content(self, base_dir, filename):
|
||||
"""Read content from HTML or Markdown directory"""
|
||||
try:
|
||||
@@ -47,8 +59,6 @@ class Actions:
|
||||
Logger.log_lua_error(f"Error reading {filename}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _write_content(self, base_dir, filename, content):
|
||||
"""Write content to HTML or Markdown directory"""
|
||||
try:
|
||||
@@ -61,7 +71,6 @@ class Actions:
|
||||
Logger.log_lua_error(f"Error writing {filename}: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _list_files(self, base_dir, extension):
|
||||
"""List files with given extension"""
|
||||
try:
|
||||
@@ -70,31 +79,26 @@ class Actions:
|
||||
Logger.log_lua_error(f"Error listing files: {e}")
|
||||
return []
|
||||
|
||||
# HTML manipulation helpers
|
||||
@staticmethod
|
||||
# HTML Manipulation Helpers
|
||||
def _html_find_tag(self, html, tag):
|
||||
"""Find first occurrence of HTML tag"""
|
||||
pattern = f"<{tag}[^>]*>.*?</{tag}>"
|
||||
match = re.search(pattern, html, re.DOTALL | re.IGNORECASE)
|
||||
return match.group(0) if match else None
|
||||
|
||||
@staticmethod
|
||||
def _html_replace_tag(self, html, tag, new_content):
|
||||
"""Replace HTML tag content"""
|
||||
pattern = f"(<{tag}[^>]*>).*?(</{tag}>)"
|
||||
return re.sub(pattern, f"\\1{new_content}\\2", html, flags=re.DOTALL | re.IGNORECASE)
|
||||
|
||||
@staticmethod
|
||||
def _html_insert_before(self, html, marker, content):
|
||||
"""Insert content before a marker"""
|
||||
return html.replace(marker, content + marker)
|
||||
|
||||
@staticmethod
|
||||
def _html_insert_after(self, html, marker, content):
|
||||
"""Insert content after a marker"""
|
||||
return html.replace(marker, marker + content)
|
||||
|
||||
@staticmethod
|
||||
def _html_wrap_content(self, html, tag, wrapper_tag, attrs=""):
|
||||
"""Wrap tag content with another tag"""
|
||||
pattern = f"(<{tag}[^>]*>)(.*?)(</{tag}>)"
|
||||
@@ -102,45 +106,185 @@ class Actions:
|
||||
open_tag, content, close_tag = match.groups()
|
||||
return f"{open_tag}<{wrapper_tag} {attrs}>{content}</{wrapper_tag}>{close_tag}"
|
||||
return re.sub(pattern, replacer, html, flags=re.DOTALL | re.IGNORECASE)
|
||||
|
||||
def _html_remove_tag(self, html, tag, keep_content=True):
|
||||
"""Remove HTML tag, optionally keeping its content"""
|
||||
if keep_content:
|
||||
pattern = f"<{tag}[^>]*>(.*?)</{tag}>"
|
||||
return re.sub(pattern, r"\1", html, flags=re.DOTALL | re.IGNORECASE)
|
||||
else:
|
||||
pattern = f"<{tag}[^>]*>.*?</{tag}>"
|
||||
return re.sub(pattern, "", html, flags=re.DOTALL | re.IGNORECASE)
|
||||
|
||||
def _html_add_class(self, html, tag, class_name):
|
||||
"""Add a CSS class to all instances of a tag"""
|
||||
def add_class_to_tag(match):
|
||||
tag_content = match.group(0)
|
||||
if 'class=' in tag_content:
|
||||
return re.sub(
|
||||
r'class="([^"]*)"',
|
||||
f'class="\\1 {class_name}"',
|
||||
tag_content
|
||||
)
|
||||
else:
|
||||
return tag_content.replace('>', f' class="{class_name}">', 1)
|
||||
|
||||
pattern = f"<{tag}[^>]*>"
|
||||
return re.sub(pattern, add_class_to_tag, html, flags=re.IGNORECASE)
|
||||
|
||||
def _html_add_attribute(self, html, tag, attr_name, attr_value):
|
||||
"""Add an attribute to all instances of a tag"""
|
||||
def add_attr_to_tag(match):
|
||||
tag_content = match.group(0)
|
||||
if attr_name in tag_content:
|
||||
return tag_content # Don't duplicate
|
||||
return tag_content.replace('>', f' {attr_name}="{attr_value}">', 1)
|
||||
|
||||
pattern = f"<{tag}[^>]*>"
|
||||
return re.sub(pattern, add_attr_to_tag, html, flags=re.IGNORECASE)
|
||||
|
||||
# Markdown manipulation helpers
|
||||
@staticmethod
|
||||
# Markdown Manipulation Helpers
|
||||
def _md_add_header(self, markdown, level, text):
|
||||
"""Add header to markdown"""
|
||||
prefix = "#" * level
|
||||
return f"{prefix} {text}\n\n{markdown}"
|
||||
|
||||
@staticmethod
|
||||
def _md_replace_section(self, markdown, header, new_content):
|
||||
"""Replace markdown section"""
|
||||
# Find section starting with header
|
||||
pattern = f"(#{1,6}\\s+{re.escape(header)}.*?)(?=#{1,6}\\s+|$)"
|
||||
return re.sub(pattern, f"## {header}\n\n{new_content}\n\n", markdown, flags=re.DOTALL)
|
||||
|
||||
@staticmethod
|
||||
def _md_append_content(self, markdown, content):
|
||||
"""Append content to markdown"""
|
||||
return markdown.rstrip() + "\n\n" + content
|
||||
|
||||
def _md_prepend_content(self, markdown, content):
|
||||
"""Prepend content to markdown"""
|
||||
return content + "\n\n" + markdown.lstrip()
|
||||
|
||||
def _md_insert_at_position(self, markdown, position, content):
|
||||
"""Insert content at specific line position"""
|
||||
lines = markdown.split('\n')
|
||||
if 0 <= position <= len(lines):
|
||||
lines.insert(position, content)
|
||||
return '\n'.join(lines)
|
||||
|
||||
def _md_find_section(self, markdown, header):
|
||||
"""Find a markdown section and return its content"""
|
||||
pattern = f"#{1,6}\\s+{re.escape(header)}\\s*\n(.*?)(?=#{1,6}\\s+|$)"
|
||||
match = re.search(pattern, markdown, re.DOTALL)
|
||||
return match.group(1).strip() if match else None
|
||||
|
||||
def _md_remove_section(self, markdown, header):
|
||||
"""Remove a markdown section"""
|
||||
pattern = f"#{1,6}\\s+{re.escape(header)}.*?(?=#{1,6}\\s+|$)"
|
||||
return re.sub(pattern, "", markdown, flags=re.DOTALL).strip()
|
||||
|
||||
def _md_add_list_item(self, markdown, item, ordered=False):
|
||||
"""Add an item to the end of markdown (as list item)"""
|
||||
prefix = "1. " if ordered else "- "
|
||||
return markdown.rstrip() + f"\n{prefix}{item}\n"
|
||||
|
||||
def _md_wrap_code_block(self, markdown, language=""):
|
||||
"""Wrap content in a markdown code block"""
|
||||
return f"```{language}\n{markdown}\n```"
|
||||
|
||||
# JSON conversion helpers
|
||||
@staticmethod
|
||||
# JSON Conversion Helpers
|
||||
def _table_to_json(self, lua_table):
|
||||
"""Convert Lua table to JSON string"""
|
||||
import json
|
||||
try:
|
||||
# Convert lupa table to Python dict
|
||||
py_dict = dict(lua_table)
|
||||
return json.dumps(py_dict)
|
||||
# Handle lupa tables by converting to dict
|
||||
if hasattr(lua_table, 'items'):
|
||||
py_dict = dict(lua_table.items())
|
||||
elif hasattr(lua_table, '__iter__') and not isinstance(lua_table, str):
|
||||
# Handle array-like lua tables
|
||||
py_dict = list(lua_table)
|
||||
else:
|
||||
py_dict = dict(lua_table)
|
||||
|
||||
return json.dumps(py_dict, indent=2, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error converting table to JSON: {e}")
|
||||
return "{}"
|
||||
|
||||
@staticmethod
|
||||
def _json_to_table(self, json_str):
|
||||
"""Convert JSON string to Lua table"""
|
||||
import json
|
||||
"""Convert JSON string to Lua table (Python dict)"""
|
||||
try:
|
||||
return json.loads(json_str)
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error parsing JSON: {e}")
|
||||
return {}
|
||||
|
||||
def _json_parse(self, json_str):
|
||||
"""Alias for _json_to_table"""
|
||||
return self._json_to_table(json_str)
|
||||
|
||||
def _json_stringify(self, data):
|
||||
"""Convert Python object to JSON string"""
|
||||
try:
|
||||
return json.dumps(data, indent=2, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error stringifying to JSON: {e}")
|
||||
return "{}"
|
||||
|
||||
# Utility Functions
|
||||
def _string_split(self, text, delimiter):
|
||||
"""Split string by delimiter"""
|
||||
return text.split(delimiter)
|
||||
|
||||
def _string_join(self, items, delimiter):
|
||||
"""Join list of strings with delimiter"""
|
||||
try:
|
||||
return delimiter.join(str(item) for item in items)
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error joining strings: {e}")
|
||||
return ""
|
||||
|
||||
def _string_replace(self, text, old, new):
|
||||
"""Replace all occurrences of old with new"""
|
||||
return text.replace(old, new)
|
||||
|
||||
def _string_match(self, text, pattern):
|
||||
"""Match string against regex pattern"""
|
||||
try:
|
||||
match = re.search(pattern, text)
|
||||
return match.group(0) if match else None
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error in regex match: {e}")
|
||||
return None
|
||||
|
||||
def _string_match_all(self, text, pattern):
|
||||
"""Find all matches of pattern in text"""
|
||||
try:
|
||||
return re.findall(pattern, text)
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error in regex findall: {e}")
|
||||
return []
|
||||
|
||||
def _file_exists(self, path):
|
||||
"""Check if file exists"""
|
||||
try:
|
||||
return Path(path).exists()
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error checking file existence: {e}")
|
||||
return False
|
||||
|
||||
def _file_size(self, path):
|
||||
"""Get file size in bytes"""
|
||||
try:
|
||||
return Path(path).stat().st_size
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error getting file size: {e}")
|
||||
return 0
|
||||
|
||||
def _list_directory(self, path):
|
||||
"""List all files and directories in path"""
|
||||
try:
|
||||
p = Path(path)
|
||||
if not p.exists():
|
||||
return []
|
||||
return [item.name for item in p.iterdir()]
|
||||
except Exception as e:
|
||||
Logger.log_lua_error(f"Error listing directory: {e}")
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user