691 lines
9.4 KiB
Python
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>"
|