first commit
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from .models import Host
|
||||
|
||||
|
||||
def data_dir() -> Path:
|
||||
root = os.environ.get("XDG_DATA_HOME")
|
||||
if root:
|
||||
path = Path(root) / "librenet-scanner"
|
||||
else:
|
||||
path = Path.home() / ".local" / "share" / "librenet-scanner"
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
class HistoryStore:
|
||||
def __init__(self, db_path: Path | None = None) -> None:
|
||||
self.db_path = db_path or data_dir() / "history.sqlite3"
|
||||
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
self._init_db()
|
||||
|
||||
def _connect(self) -> sqlite3.Connection:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
def _init_db(self) -> None:
|
||||
with self._connect() as conn:
|
||||
conn.executescript(
|
||||
"""
|
||||
PRAGMA journal_mode=WAL;
|
||||
CREATE TABLE IF NOT EXISTS scans (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at TEXT NOT NULL,
|
||||
target TEXT NOT NULL,
|
||||
profile TEXT NOT NULL,
|
||||
host_count INTEGER NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS scan_hosts (
|
||||
scan_id INTEGER NOT NULL,
|
||||
ip TEXT NOT NULL,
|
||||
hostname TEXT,
|
||||
mac TEXT,
|
||||
vendor TEXT,
|
||||
os_name TEXT,
|
||||
ports_json TEXT NOT NULL,
|
||||
FOREIGN KEY(scan_id) REFERENCES scans(id) ON DELETE CASCADE
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
def save_scan(self, target: str, profile: str, hosts: list[Host]) -> int:
|
||||
now = datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
|
||||
with self._connect() as conn:
|
||||
cur = conn.execute(
|
||||
"INSERT INTO scans(created_at, target, profile, host_count) VALUES (?, ?, ?, ?)",
|
||||
(now, target, profile, len(hosts)),
|
||||
)
|
||||
scan_id = int(cur.lastrowid)
|
||||
for host in hosts:
|
||||
ports = [
|
||||
{
|
||||
"port": p.port,
|
||||
"protocol": p.protocol,
|
||||
"service": p.service,
|
||||
"product": p.product,
|
||||
"version": p.version,
|
||||
}
|
||||
for p in host.ports
|
||||
]
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO scan_hosts(scan_id, ip, hostname, mac, vendor, os_name, ports_json)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(scan_id, host.ip, host.hostname, host.mac, host.vendor, host.os_name, json.dumps(ports, ensure_ascii=False)),
|
||||
)
|
||||
return scan_id
|
||||
|
||||
def recent_scans(self, limit: int = 100) -> list[sqlite3.Row]:
|
||||
with self._connect() as conn:
|
||||
return list(
|
||||
conn.execute(
|
||||
"SELECT id, created_at, target, profile, host_count FROM scans ORDER BY id DESC LIMIT ?",
|
||||
(limit,),
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user