Files
2026-10-06 21:08:12 +03:00

691 lines
9.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
HTML шаблон WiFi GPS Mapper Report.
Все страницы используют единый шаблон.
В дальнейшем здесь будет располагаться:
• CSS
• JavaScript
• Навигация
• Каркас страниц
• Footer
"""
from datetime import datetime, timezone
from reports.utils import (
format_datetime as format_report_datetime,
)
# ============================================================
# Report context
# ============================================================
class ReportContext:
def __init__(
self,
mode="static",
page="root",
):
if mode not in (
"static",
"server",
):
raise ValueError(
f"Unknown report mode: {mode}"
)
if page not in (
"root",
"session",
"trips",
):
raise ValueError(
f"Unknown report page: {page}"
)
self.mode = mode
self.page = page
def session_page(self):
return ReportContext(
mode=self.mode,
page="session",
)
def home_url(self):
if self.mode == "server":
return "/"
if self.page == "session":
return "../index.html"
return "index.html"
def dashboard_url(self):
if self.mode == "server":
return "/reports/"
if self.page == "session":
return "../index.html"
return "index.html"
def capture_sessions_url(self):
if self.mode == "server":
return "/reports/sessions/"
if self.page == "session":
return "../capture_sessions.html"
return "capture_sessions.html"
def trips_url(
self,
):
if self.mode == "server":
return "/reports/trips/"
return "index.html"
def search_url(
self,
):
if self.mode == "server":
return "/reports/search/"
return "index.html"
def session_url(
self,
session_id,
):
if self.mode == "server":
return (
f"/reports/session/{session_id}"
)
return (
f"sessions/{session_id}.html"
)
def asset_url(
self,
path,
):
path = str(path).lstrip("/")
if self.mode == "server":
return (
f"/reports/static/{path}"
)
if self.page == "session":
return (
f"../static/{path}"
)
return (
f"static/{path}"
)
# ============================================================
# HTML Header
# ============================================================
def html_header(
context=None,
):
if context is None:
context = ReportContext()
return f"""<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WiFi GPS Mapper Report</title>
<link
rel="stylesheet"
href="{context.asset_url('report.css')}"
>
<script>
function filterTable(id,value)
{{
let table=document.getElementById(id);
if(table===null)
return;
value=value.toLowerCase();
let rows=table.getElementsByTagName("tr");
for(let i=1;i<rows.length;i++)
{{
let text=rows[i].innerText.toLowerCase();
rows[i].style.display=
text.includes(value)
?
""
:
"none";
}}
}}
</script>
</head>
<body>
"""
HTML_END = """
</body>
</html>
"""
# ============================================================
# Navigation
# ============================================================
def navigation(
active="dashboard",
context=None,
):
if context is None:
context = ReportContext()
menu = [
(
"dashboard",
"Dashboard",
context.dashboard_url(),
),
(
"capture_sessions",
"Capture Sessions",
context.capture_sessions_url(),
),
]
if context.mode == "server":
menu.append(
(
"trips",
"Trips",
context.trips_url(),
)
)
if context.mode == "server":
menu.extend(
[
(
"access_points",
"Access Points",
"/reports/access-points/",
),
(
"vendors",
"Vendors",
"/reports/vendors/",
),
(
"hashcat",
"Hashes",
"/reports/hashcat/",
),
]
)
menu.append(
(
"search",
"Search",
context.search_url(),
)
)
html = """
<header class="report-header">
<div class="report-header-inner">
<a
class="report-brand"
href="{}"
>
<span class="report-brand-title">
WiFi GPS Mapper
</span>
<span class="report-brand-subtitle">
Wireless network scanner
</span>
</a>
<nav class="report-nav">
""".format(
context.home_url()
)
for key, title, url in menu:
css = ""
if key == active:
css = "active"
html += f"""
<a
class="{css}"
href="{url}">
{title}
</a>
"""
html += """
</nav>
</div>
</header>
"""
return html
# ============================================================
# Page begin
# ============================================================
def page_begin(
title,
description="",
active="dashboard",
context=None,
):
if context is None:
context = ReportContext()
html = html_header(
context
)
html += """
<div class="wrapper">
"""
html += navigation(
active,
context,
)
html += f"""
<div class="content">
<h1 class="page-title">
{title}
</h1>
"""
if description:
html += f"""
<div class="page-description">
{description}
</div>
"""
return html
# ============================================================
# Page header
# ============================================================
def page_header(
title,
description="",
):
html = f"""
<h1 class="page-title">
{title}
</h1>
"""
if description:
html += f"""
<div class="page-description">
{description}
</div>
"""
return html
# ============================================================
# Page end
# ============================================================
def page_end():
generated = format_report_datetime(
datetime.now(
timezone.utc
).isoformat()
)
return f"""
<div class="footer">
Generated:
<b>
{generated}
</b>
<br><br>
WiFi GPS Mapper Report
</div>
</div>
</div>
{HTML_END}
"""
# ============================================================
# Card
# ============================================================
def card(
title,
body
):
return f"""
<div class="card">
<h2>
{title}
</h2>
{body}
</div>
"""
# ============================================================
# Info
# ============================================================
def info(text):
return f"""
<div class="info">
{text}
</div>
"""
# ============================================================
# Badge
# ============================================================
def badge(
text,
color="gray"
):
return f"""
<span class="badge badge-{color}">
{text}
</span>
"""
# ============================================================
# Button
# ============================================================
def button(
title,
url
):
return f"""
<a
class="button"
draggable="false"
href="{url}">
{title}
</a>
"""
# ============================================================
# Statistics card
# ============================================================
def stat(
value,
title
):
return f"""
<div class="stat-card">
<div class="stat-value">
{value}
</div>
<div class="stat-title">
{title}
</div>
</div>
"""
# ============================================================
# Statistics grid
# ============================================================
def stats_grid(items):
html = """
<div class="stats-grid">
"""
html += "".join(items)
html += """
</div>
"""
return html
# ============================================================
# Search field
# ============================================================
def search_box(
table_id,
placeholder="Search..."
):
return f"""
<input
class="search"
type="text"
placeholder="{placeholder}"
onkeyup="filterTable('{table_id}',this.value)"
>
"""
# ============================================================
# Table container
# ============================================================
def table_container(content):
return f"""
<div class="table-container">
{content}
</div>
"""
# ============================================================
# Horizontal separator
# ============================================================
def hr():
return "<hr>"