diff --git a/AppImage/scripts/oci/description_templates.py b/AppImage/scripts/oci/description_templates.py new file mode 100644 index 00000000..c340d808 --- /dev/null +++ b/AppImage/scripts/oci/description_templates.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +""" +ProxMenux - HTML Description Templates for OCI Containers +========================================================== +Generates beautiful HTML descriptions for the Proxmox Notes panel. +Can be used from both Python (oci_manager.py) and bash scripts. + +Usage from bash: + python3 description_templates.py --app-id "secure-gateway" --hostname "my-gateway" + +Usage from Python: + from description_templates import generate_description + html = generate_description(app_def, container_def, hostname) +""" + +import sys +import json +import argparse +import urllib.parse +from pathlib import Path +from typing import Dict, Optional + +# Default paths +CATALOG_PATH = Path(__file__).parent / "catalog.json" + + +def get_shield_icon_svg(color: str = "#0EA5E9") -> str: + """Generate a shield icon SVG with checkmark.""" + return f"""""" + + +def get_default_icon_svg(color: str = "#0EA5E9") -> str: + """Generate a default container icon SVG.""" + return f"""""" + + +# Pre-defined icon types +ICON_TYPES = { + "shield": get_shield_icon_svg, + "container": get_default_icon_svg, + "default": get_default_icon_svg, +} + + +def generate_description( + app_def: Dict, + container_def: Optional[Dict] = None, + hostname: str = "", + extra_info: str = "" +) -> str: + """ + Generate HTML description for Proxmox Notes panel. + + Args: + app_def: Application definition from catalog + container_def: Container definition (optional) + hostname: Container hostname + extra_info: Additional info to display (e.g., disk info) + + Returns: + HTML string for the description + """ + # Extract app info + app_name = app_def.get("name", "ProxMenux App") + app_subtitle = app_def.get("subtitle", "") + app_color = app_def.get("color", "#0EA5E9") + app_icon_type = app_def.get("icon_type", "default") + doc_url = app_def.get("documentation_url", "https://macrimi.github.io/ProxMenux/") + code_url = app_def.get("code_url", "https://github.com/MacRimi/ProxMenux") + installer_url = app_def.get("installer_url", "") + kofi_url = "https://ko-fi.com/macrimi" + + # Get the icon SVG + icon_func = ICON_TYPES.get(app_icon_type, ICON_TYPES["default"]) + icon_svg = icon_func(app_color) + icon_data = "data:image/svg+xml," + urllib.parse.quote(icon_svg) + + # Build badge buttons + badges = [] + badges.append(f"Docs") + badges.append(f"Code") + + if installer_url: + badges.append(f"Installer") + + badges.append(f"Ko-fi") + + badges_html = "\n".join(badges) + + # Build footer info + footer_parts = [] + if hostname: + footer_parts.append(f"Hostname: {hostname}") + if extra_info: + footer_parts.append(extra_info) + footer_html = "
".join(footer_parts) if footer_parts else "" + + # Build the complete HTML + html = f"""
+ + + + + +
+ProxMenux Logo + +

{app_name}

+

Created with ProxMenuX

+
+ +
+ + + + + +
+Icon + +{app_name}
+{app_subtitle} +
+
+ +

+{badges_html} +

+""" + + if footer_html: + html += f""" +

+{footer_html} +

+""" + + html += "
" + + return html + + +def generate_vm_description( + vm_name: str, + vm_version: str = "", + doc_url: str = "", + code_url: str = "", + installer_url: str = "", + extra_info: str = "", + icon_url: str = "" +) -> str: + """ + Generate HTML description for VMs (like ZimaOS). + + Args: + vm_name: Name of the VM + vm_version: Version string + doc_url: Documentation URL + code_url: Code repository URL + installer_url: Installer URL + extra_info: Additional info (e.g., disk info) + icon_url: Custom icon URL for the VM + + Returns: + HTML string for the description + """ + # Build badge buttons + badges = [] + if doc_url: + badges.append(f"Docs") + if code_url: + badges.append(f"Code") + if installer_url: + badges.append(f"Installer") + badges.append("Ko-fi") + + badges_html = "\n".join(badges) + + # Version line + version_html = f"

{vm_version}

" if vm_version else "" + + # Extra info + extra_html = f"

{extra_info}

" if extra_info else "" + + html = f"""
+ + + + + +
+ProxMenux Logo + +

{vm_name}

+

Created with ProxMenuX

+{version_html} +
+ +

+{badges_html} +

+ +{extra_html} +
""" + + return html + + +def load_catalog() -> Dict: + """Load the OCI catalog.""" + if CATALOG_PATH.exists(): + with open(CATALOG_PATH) as f: + return json.load(f) + return {"apps": {}} + + +def main(): + """CLI interface for generating descriptions.""" + parser = argparse.ArgumentParser(description="Generate HTML descriptions for Proxmox") + parser.add_argument("--app-id", help="Application ID from catalog") + parser.add_argument("--hostname", default="", help="Container hostname") + parser.add_argument("--extra-info", default="", help="Additional info to display") + parser.add_argument("--output", choices=["html", "encoded"], default="html", + help="Output format: html or url-encoded") + + # For VM descriptions (not from catalog) + parser.add_argument("--vm-name", help="VM name (for non-catalog VMs)") + parser.add_argument("--vm-version", default="", help="VM version") + parser.add_argument("--doc-url", default="", help="Documentation URL") + parser.add_argument("--code-url", default="", help="Code repository URL") + parser.add_argument("--installer-url", default="", help="Installer URL") + + args = parser.parse_args() + + if args.app_id: + # Generate from catalog + catalog = load_catalog() + apps = catalog.get("apps", {}) + + if args.app_id not in apps: + print(f"Error: App '{args.app_id}' not found in catalog", file=sys.stderr) + sys.exit(1) + + app_def = apps[args.app_id] + html = generate_description(app_def, hostname=args.hostname, extra_info=args.extra_info) + + elif args.vm_name: + # Generate for VM + html = generate_vm_description( + vm_name=args.vm_name, + vm_version=args.vm_version, + doc_url=args.doc_url, + code_url=args.code_url, + installer_url=args.installer_url, + extra_info=args.extra_info + ) + else: + parser.print_help() + sys.exit(1) + + if args.output == "encoded": + print(urllib.parse.quote(html)) + else: + print(html) + + +if __name__ == "__main__": + main() diff --git a/AppImage/scripts/oci_manager.py b/AppImage/scripts/oci_manager.py index a6767e36..b8fc3ff7 100644 --- a/AppImage/scripts/oci_manager.py +++ b/AppImage/scripts/oci_manager.py @@ -773,6 +773,15 @@ def deploy_app(app_id: str, config: Dict[str, Any], installed_by: str = "web") - except Exception as e: logger.warning(f"Could not apply extra LXC config: {e}") + # Step 3.1: Set HTML description for Proxmox Notes panel + html_desc = _generate_html_description(app_def, container_def, hostname) + if html_desc: + rc, _, err = _run_pve_cmd(["pct", "set", str(vmid), "-description", html_desc]) + if rc == 0: + logger.info(f"Set HTML description for container {vmid}") + else: + logger.warning(f"Could not set description: {err}") + # Step 4: Configure environment variables (only for OCI containers) if use_oci: env_vars = [] @@ -859,6 +868,94 @@ def deploy_app(app_id: str, config: Dict[str, Any], installed_by: str = "web") - return result +def _generate_html_description(app_def: Dict, container_def: Dict, hostname: str) -> str: + """ + Generate HTML description for Proxmox Notes panel. + + Tries to use the shared description_templates module if available, + otherwise falls back to inline implementation. + """ + try: + # Try to import the shared templates module + import sys + templates_path = Path("/usr/local/share/proxmenux/scripts/oci") + if templates_path.exists(): + sys.path.insert(0, str(templates_path)) + + from description_templates import generate_description + return generate_description(app_def, container_def, hostname) + except ImportError: + pass + + # Fallback: inline implementation + import urllib.parse + + app_name = app_def.get("name", "ProxMenux App") + app_subtitle = app_def.get("subtitle", app_def.get("short_name", "")) + app_color = app_def.get("color", "#0EA5E9") + app_icon_type = app_def.get("icon_type", "default") + doc_url = app_def.get("documentation_url", "https://macrimi.github.io/ProxMenux/") + code_url = app_def.get("code_url", "https://github.com/MacRimi/ProxMenux") + installer_url = app_def.get("installer_url", "") + + # Generate icon based on type + if app_icon_type == "shield": + icon_svg = f"""""" + else: + icon_svg = f"""""" + + icon_data = "data:image/svg+xml," + urllib.parse.quote(icon_svg) + + # Build badges + badges = [ + f"Docs", + f"Code", + ] + if installer_url: + badges.append(f"Installer") + badges.append("Ko-fi") + + badges_html = "\n".join(badges) + + html_desc = f"""
+ + + + + +
+ProxMenux Logo + +

{app_name}

+

Created with ProxMenuX

+
+ +
+ + + + + +
+Icon + +{app_name}
+{app_subtitle} +
+
+ +

+{badges_html} +

+ +

+Hostname: {hostname} +

+
""" + + return html_desc + + def _enable_host_ip_forwarding() -> bool: """Enable IP forwarding on the Proxmox host.""" logger.info("Enabling IP forwarding on host")