#!/usr/bin/env python3
"""Render the Proxmox Notes for one ProxMenux OCI container."""
from __future__ import annotations
import argparse
import html
import ipaddress
import json
from pathlib import Path
from urllib.parse import urlparse
DOCS = 'https://macrimi.github.io/ProxMenux/docs/oci-manager'
CODE = 'https://github.com/MacRimi/ProxMenux/tree/main/oci'
LOGO = 'https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logo_desc.png'
def safe_url(value):
if not isinstance(value, str):
return ''
parsed = urlparse(value.strip())
return value.strip() if parsed.scheme in ('http', 'https') and parsed.netloc else ''
def link(label, url):
url = safe_url(url)
if not url:
return ''
return (f'{html.escape(label)}')
def image_page(reference):
name = reference.split('@', 1)[0]
if ':' in name.rsplit('/', 1)[-1]:
name = name.rsplit(':', 1)[0]
if name.startswith('lscr.io/linuxserver/'):
return 'https://docs.linuxserver.io/images/docker-' + name.rsplit('/', 1)[-1] + '/'
if name.startswith('ghcr.io/'):
parts = name.split('/')
if len(parts) >= 3:
return f'https://github.com/{parts[1]}/' + '/'.join(parts[2:])
if name.startswith('docker.io/'):
name = name[len('docker.io/'):]
if '/' not in name and name and '.' not in name:
return 'https://hub.docker.com/_/' + name
if '/' in name and '.' not in name.split('/', 1)[0]:
return 'https://hub.docker.com/r/' + name
return ''
def render(template, digest, instance_id, ip=''):
ui = template.get('catalog_ui') or {}
contract = template.get('container_contract') or {}
image = contract.get('image') or {}
title = ui.get('title') or {}
if isinstance(title, dict):
title = title.get('en_US') or next(iter(title.values()), '')
title = str(title or contract.get('service_name') or template.get('id') or 'OCI')
reference = str(image.get('reference') or '')
source = template.get('source') or {}
image_url = (source.get('image_repository_url') or image_page(reference)
or ui.get('repository') or source.get('repository'))
resources = [('Image', image_url), ('App', ui.get('website'))]
resources = [link(label, url) for label, url in resources]
resources = [item for item in resources if item]
try:
address = str(ipaddress.ip_address(ip)) if ip else ''
except ValueError:
address = ''
endpoints = (template.get('first_run') or {}).get('endpoints') or []
if not endpoints and ui.get('launch'):
endpoints = [dict(ui['launch'], label='Web UI')]
if template.get('id') == 'image-adguard-home':
endpoints = [{'label': 'Setup (first run)', 'scheme': 'http', 'port': 3000, 'path': '/'},
{'label': 'Web UI (after setup)', 'scheme': 'http', 'port': 80, 'path': '/'}]
access = []
if address:
for endpoint in endpoints:
scheme = endpoint.get('scheme')
port = endpoint.get('port')
path = endpoint.get('path') or '/'
if scheme not in ('http', 'https') or not isinstance(port, int) or not 1 <= port <= 65535:
continue
if not isinstance(path, str) or not path.startswith('/'):
path = '/'
url = f'{scheme}://{address}:{port}{path}'
item = f'🌐 {link(str(endpoint.get("label") or "Web UI"), url)}: {link(url, url)}'
if item:
access.append(item)
badges = (
('Docs', DOCS, 'https://img.shields.io/badge/%F0%9F%93%9A_Docs-blue'),
('Code', CODE, 'https://img.shields.io/badge/%F0%9F%92%BB_Code-green'),
('Ko-fi', 'https://ko-fi.com/macrimi', 'https://img.shields.io/badge/%E2%98%95_Ko--fi-red'),
)
badge_links = ' '.join(
f''
f''
for label, url, badge in badges
)
return f'''
{html.escape(title)} OCICreated with ProxMenux |
{badge_links}
Image: {html.escape(reference)} {' · '.join(resources)}
{item}
' for item in access)}