Archived
103 lines
5.0 KiB
Python
103 lines
5.0 KiB
Python
from __future__ import annotations
|
|||
|
|
|
||
|
|
from .html_utils import attr, escape
|
||
|
|
|
||
|
|
|
||
|
|
def _account_row(account: dict[str, str | int | None], csrf: str = "", admin_path: str = "/admin") -> str:
|
||
|
|
hostname = escape(account["hostname"])
|
||
|
|
username = escape(account["username"])
|
||
|
|
ipv4 = escape(account.get("ipv4") or "-")
|
||
|
|
ipv6 = escape(account.get("ipv6") or "-")
|
||
|
|
updated = escape(account.get("updated_at") or "Never")
|
||
|
|
safe_admin_path = attr(admin_path)
|
||
|
|
return f"""
|
||
|
|
<tr>
|
||
|
|
<td>{hostname}</td><td>{username}</td><td>{ipv4}</td><td>{ipv6}</td><td>{updated}</td>
|
||
|
|
<td>
|
||
|
|
<form method="post" action="{safe_admin_path}/accounts/rotate" class="d-flex gap-2 flex-wrap">
|
||
|
|
<input type="hidden" name="csrf" value="{attr(csrf)}">
|
||
|
|
<input type="hidden" name="hostname" value="{hostname}">
|
||
|
|
<button name="action" value="update" class="btn btn-sm btn-light px-3" title="Rotate update URL" aria-label="Rotate update URL for {hostname}">Update URL</button>
|
||
|
|
<button name="action" value="management" class="btn btn-sm btn-light px-3" title="Rotate management link" aria-label="Rotate management link for {hostname}">Manage link</button>
|
||
|
|
<button name="action" value="password" class="btn btn-sm btn-light px-3" title="Rotate router password" aria-label="Rotate router password for {hostname}">Password</button>
|
||
|
|
</form>
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<form method="post" action="{safe_admin_path}/accounts/delete">
|
||
|
|
<input type="hidden" name="csrf" value="{attr(csrf)}">
|
||
|
|
<input type="hidden" name="hostname" value="{hostname}">
|
||
|
|
<button class="btn btn-sm btn-light px-2 text-danger" title="Delete hostname" aria-label="Delete {hostname}">x</button>
|
||
|
|
</form>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
"""
|
||
|
|
|
||
|
|
|
||
|
|
def _admin_table_toolbar(table_id: str, search_label: str) -> str:
|
||
|
|
safe_id = attr(table_id)
|
||
|
|
safe_label = attr(search_label)
|
||
|
|
return f"""
|
||
|
|
<div class="admin-table-shell mt-3" data-admin-table>
|
||
|
|
<div class="d-flex align-items-center justify-content-between gap-3 flex-column flex-md-row mb-3">
|
||
|
|
<label class="w-100 mb-0" for="{safe_id}-search">
|
||
|
|
<span class="visually-hidden">{safe_label}</span>
|
||
|
|
<input id="{safe_id}-search" class="form-control" type="search" placeholder="{safe_label}" data-table-search autocomplete="off">
|
||
|
|
</label>
|
||
|
|
<div class="d-flex align-items-center gap-2 flex-shrink-0">
|
||
|
|
<span class="small text-secondary text-nowrap" data-table-count>0-0 of 0</span>
|
||
|
|
<label class="visually-hidden" for="{safe_id}-page-size">Rows per page</label>
|
||
|
|
<select id="{safe_id}-page-size" class="form-select form-select-sm table-page-size" data-table-page-size>
|
||
|
|
<option value="10">10</option>
|
||
|
|
<option value="25" selected>25</option>
|
||
|
|
<option value="50">50</option>
|
||
|
|
<option value="100">100</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
"""
|
||
|
|
|
||
|
|
|
||
|
|
def _admin_table_pagination() -> str:
|
||
|
|
return """
|
||
|
|
<nav class="mt-3" aria-label="Table pagination">
|
||
|
|
<ul class="pagination pagination-sm justify-content-end mb-0" data-table-pagination></ul>
|
||
|
|
</nav>
|
||
|
|
</div>
|
||
|
|
"""
|
||
|
|
|
||
|
|
|
||
|
|
def _domain_claim_row(challenge: dict[str, str | None]) -> str:
|
||
|
|
domain = escape(challenge["domain"])
|
||
|
|
owner = escape(challenge.get("owner_hostname") or "-")
|
||
|
|
created = escape(challenge.get("created_at") or "-")
|
||
|
|
verified = escape(challenge.get("verified_at") or "-")
|
||
|
|
status = "Verified" if challenge.get("verified_at") else "Pending"
|
||
|
|
return f"<tr><td>{domain}</td><td>{owner}</td><td>{status}</td><td>{created}</td><td>{verified}</td></tr>"
|
||
|
|
|
||
|
|
|
||
|
|
def _cleanup_row(run: dict[str, str | int]) -> str:
|
||
|
|
created = escape(run.get("created_at") or "-")
|
||
|
|
domains = escape(run.get("domain_challenges_deleted") or 0)
|
||
|
|
accounts = escape(run.get("unused_accounts_deleted") or 0)
|
||
|
|
return f"<tr><td>{created}</td><td>{domains}</td><td>{accounts}</td></tr>"
|
||
|
|
|
||
|
|
|
||
|
|
def _scheduled_task_row(task: dict[str, str | int | None]) -> str:
|
||
|
|
name = escape(task.get("name") or "-")
|
||
|
|
status = escape(task.get("last_status") or "-")
|
||
|
|
next_run = escape(task.get("next_run_at") or "-")
|
||
|
|
lease_expires = escape(task.get("lease_expires_at") or "-")
|
||
|
|
last_finished = escape(task.get("last_finished_at") or "-")
|
||
|
|
last_error = escape(task.get("last_error") or "-")
|
||
|
|
return f"<tr><td>{name}</td><td>{status}</td><td>{next_run}</td><td>{lease_expires}</td><td>{last_finished}</td><td>{last_error}</td></tr>"
|
||
|
|
|
||
|
|
|
||
|
|
def _event_row(event: dict[str, str | int | None]) -> str:
|
||
|
|
created = escape(event.get("created_at") or "-")
|
||
|
|
hostname = escape(event.get("hostname") or "-")
|
||
|
|
status = escape(event.get("status") or "-")
|
||
|
|
ipv4 = escape(event.get("ipv4") or "-")
|
||
|
|
ipv6 = escape(event.get("ipv6") or "-")
|
||
|
|
count = escape(event.get("count") or 1)
|
||
|
|
detail = escape(event.get("detail") or "-")
|
||
|
|
return f"<tr><td>{created}</td><td>{hostname}</td><td>{status}</td><td>{ipv4}</td><td>{ipv6}</td><td>{count}</td><td>{detail}</td></tr>"
|