mirror of
https://github.com/smittix/intercept.git
synced 2026-06-13 08:13:32 -07:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc0850d339 | |||
| 2bbf896e7c | |||
| faf57741a1 | |||
| fd7d01fc7d | |||
| 8ef9dca6ee | |||
| 4610804de6 | |||
| 6d8836ddfc | |||
| 17944554e6 | |||
| 47a7376632 |
+5
-1
@@ -40,7 +40,11 @@ tasks/
|
||||
|
||||
# Runtime data (mounted as volume)
|
||||
instance/
|
||||
data/
|
||||
|
||||
# data/ is a Python package — only exclude non-code files
|
||||
data/*.json
|
||||
data/*.csv
|
||||
data/*.db
|
||||
|
||||
# Build scripts
|
||||
build-multiarch.sh
|
||||
|
||||
@@ -2,6 +2,49 @@
|
||||
|
||||
All notable changes to iNTERCEPT will be documented in this file.
|
||||
|
||||
## [2.26.6] - 2026-03-14
|
||||
|
||||
### Fixed
|
||||
- **Oversized branded 'i' logo on dashboards** — `.logo span { display: inline }` in dashboard CSS had higher specificity (0,1,1) than `.brand-i { display: inline-block }` (0,1,0), forcing the branded "i" SVG to render as inline which ignores width/height. Added `.logo .brand-i` selector (0,2,0) to retain `inline-block` display. (#189)
|
||||
|
||||
---
|
||||
|
||||
## [2.26.5] - 2026-03-14
|
||||
|
||||
### Fixed
|
||||
- **Database errors crash entire UI** — `get_setting()` now catches `sqlite3.OperationalError` and returns the default value instead of propagating the exception. Previously, if the database was inaccessible (e.g. root-owned `instance/` directory from running with `sudo`), the `inject_offline_settings` context processor would crash every page render with a 500 Internal Server Error. (#190)
|
||||
|
||||
---
|
||||
|
||||
## [2.26.4] - 2026-03-14
|
||||
|
||||
### Fixed
|
||||
- **Environment Configurator crash** — `read_env_var()` crashed with "Setup failed at line 2333" when `.env` existed but didn't contain the variable being looked up. `grep` returned exit code 1 (no match), which `pipefail` propagated and `set -e` turned into a fatal error. Fixed by appending `|| true` to the pipeline. (#191)
|
||||
|
||||
---
|
||||
|
||||
## [2.26.3] - 2026-03-13
|
||||
|
||||
### Fixed
|
||||
- **SatDump AVX2 crash** — SatDump now compiles with `-march=x86-64` on x86_64 platforms (Docker and `setup.sh`), preventing "Illegal instruction" crashes on CPUs without AVX2. SIMD plugins still use runtime detection for acceleration on capable hardware. (#185)
|
||||
|
||||
---
|
||||
|
||||
## [2.26.2] - 2026-03-13
|
||||
|
||||
### Fixed
|
||||
- **Docker startup crash** — `.dockerignore` excluded the entire `data/` directory, which is now a Python package (`data.oui`, `data.patterns`, `data.satellites`). Caused `ModuleNotFoundError: No module named 'data.oui'` on container startup. Fixed by only excluding non-code files from `data/`.
|
||||
|
||||
---
|
||||
|
||||
## [2.26.1] - 2026-03-13
|
||||
|
||||
### Fixed
|
||||
- **Default admin credentials** — Default `ADMIN_PASSWORD` changed from empty string to `admin`, matching the README documentation (`admin:admin`)
|
||||
- **Config credential sync** — Admin password changes in `config.py` or via `INTERCEPT_ADMIN_PASSWORD` env var now sync to the database on restart, without needing to delete the DB
|
||||
|
||||
---
|
||||
|
||||
## [2.26.0] - 2026-03-13
|
||||
|
||||
### Fixed
|
||||
|
||||
+4
-1
@@ -130,7 +130,10 @@ RUN cd /tmp \
|
||||
&& git clone --depth 1 --branch 1.2.2 https://github.com/SatDump/SatDump.git \
|
||||
&& cd SatDump \
|
||||
&& mkdir build && cd build \
|
||||
&& cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_GUI=OFF -DCMAKE_INSTALL_LIBDIR=lib .. \
|
||||
&& ARCH_FLAGS=""; if [ "$(uname -m)" = "x86_64" ]; then ARCH_FLAGS="-march=x86-64"; fi \
|
||||
&& cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_GUI=OFF -DCMAKE_INSTALL_LIBDIR=lib \
|
||||
-DCMAKE_C_FLAGS="$ARCH_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$ARCH_FLAGS" .. \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
|
||||
@@ -7,10 +7,53 @@ import os
|
||||
import sys
|
||||
|
||||
# Application version
|
||||
VERSION = "2.26.0"
|
||||
VERSION = "2.26.6"
|
||||
|
||||
# Changelog - latest release notes (shown on welcome screen)
|
||||
CHANGELOG = [
|
||||
{
|
||||
"version": "2.26.6",
|
||||
"date": "March 2026",
|
||||
"highlights": [
|
||||
"Fix oversized branded 'i' logo on Aircraft & Vessel dashboards",
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.26.5",
|
||||
"date": "March 2026",
|
||||
"highlights": [
|
||||
"Fix database errors crashing the entire UI — pages now degrade gracefully",
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.26.4",
|
||||
"date": "March 2026",
|
||||
"highlights": [
|
||||
"Fix Environment Configurator crash when .env exists but variable is missing",
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.26.3",
|
||||
"date": "March 2026",
|
||||
"highlights": [
|
||||
"Fix SatDump AVX2 crash on older CPUs — build now targets baseline x86-64",
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.26.2",
|
||||
"date": "March 2026",
|
||||
"highlights": [
|
||||
"Fix Docker startup crash — data/ Python package was excluded by .dockerignore",
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.26.1",
|
||||
"date": "March 2026",
|
||||
"highlights": [
|
||||
"Fix default admin credentials — now matches README (admin:admin)",
|
||||
"Admin password changes in config.py / env vars now sync to DB on restart",
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.26.0",
|
||||
"date": "March 2026",
|
||||
@@ -418,7 +461,7 @@ ALERT_WEBHOOK_TIMEOUT = _get_env_int('ALERT_WEBHOOK_TIMEOUT', 5)
|
||||
|
||||
# Admin credentials
|
||||
ADMIN_USERNAME = _get_env('ADMIN_USERNAME', 'admin')
|
||||
ADMIN_PASSWORD = _get_env('ADMIN_PASSWORD', '')
|
||||
ADMIN_PASSWORD = _get_env('ADMIN_PASSWORD', 'admin')
|
||||
|
||||
|
||||
def configure_logging() -> None:
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@
|
||||
<canvas id="bg-canvas"></canvas>
|
||||
<nav class="navbar">
|
||||
<div class="nav-container">
|
||||
<a href="#" class="nav-logo">iNTERCEPT</a>
|
||||
<a href="#" class="nav-logo"><span class="brand-i"><svg viewBox="36 14 28 68" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="20" r="6" fill="#00ff88"/><rect x="44" y="33" width="12" height="45" rx="2" fill="#00d4ff"/><rect x="38" y="33" width="24" height="4" rx="1" fill="#00d4ff"/><rect x="38" y="74" width="24" height="4" rx="1" fill="#00d4ff"/></svg></span>NTERCEPT</a>
|
||||
<div class="nav-links">
|
||||
<a href="#features">Features</a>
|
||||
<a href="#screenshots">Screenshots</a>
|
||||
@@ -28,7 +28,7 @@
|
||||
<header class="hero">
|
||||
<div class="hero-content">
|
||||
<div class="hero-badge">Open Source SIGINT Platform</div>
|
||||
<h1>iNTERCEPT</h1>
|
||||
<h1><span class="brand-i"><svg viewBox="36 14 28 68" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="20" r="6" fill="#00ff88"/><rect x="44" y="33" width="12" height="45" rx="2" fill="#00d4ff"/><rect x="38" y="33" width="24" height="4" rx="1" fill="#00d4ff"/><rect x="38" y="74" width="24" height="4" rx="1" fill="#00d4ff"/></svg></span>NTERCEPT</h1>
|
||||
<p class="hero-subtitle">A unified web interface for software-defined radio tools. Monitor pagers, track aircraft, scan WiFi networks, and more — all from your browser.</p>
|
||||
<div class="hero-buttons">
|
||||
<a href="#installation" class="btn btn-primary">Get Started</a>
|
||||
@@ -435,7 +435,7 @@ docker compose --profile basic up -d --build</code></pre>
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-brand">
|
||||
<span class="footer-logo">iNTERCEPT</span>
|
||||
<span class="footer-logo"><span class="brand-i"><svg viewBox="36 14 28 68" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="20" r="6" fill="#00ff88"/><rect x="44" y="33" width="12" height="45" rx="2" fill="#00d4ff"/><rect x="38" y="33" width="24" height="4" rx="1" fill="#00d4ff"/><rect x="38" y="74" width="24" height="4" rx="1" fill="#00d4ff"/></svg></span>NTERCEPT</span>
|
||||
<p>Signal Intelligence Platform</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
|
||||
@@ -86,6 +86,21 @@ body {
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* Branded "i" — inline SVG glyph matching the app logo */
|
||||
.brand-i {
|
||||
display: inline-block;
|
||||
width: 0.55em;
|
||||
height: 0.9em;
|
||||
vertical-align: baseline;
|
||||
position: relative;
|
||||
top: 0.05em;
|
||||
}
|
||||
.brand-i svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "intercept"
|
||||
version = "2.26.0"
|
||||
version = "2.26.6"
|
||||
description = "Signal Intelligence Platform - Pager/433MHz/ADS-B/Satellite/WiFi/Bluetooth"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -174,7 +174,7 @@ read_env_var() {
|
||||
local fallback="${2:-}"
|
||||
if [[ -f "$SCRIPT_DIR/.env" ]]; then
|
||||
local val
|
||||
val=$(grep -E "^${key}=" "$SCRIPT_DIR/.env" 2>/dev/null | tail -1 | cut -d'=' -f2-)
|
||||
val=$(grep -E "^${key}=" "$SCRIPT_DIR/.env" 2>/dev/null | tail -1 | cut -d'=' -f2- || true)
|
||||
if [[ -n "$val" ]]; then
|
||||
# Strip surrounding quotes
|
||||
val="${val#\"}"
|
||||
@@ -957,8 +957,14 @@ install_satdump_from_source_debian() {
|
||||
) &
|
||||
progress_pid=$!
|
||||
|
||||
local arch_flags=""
|
||||
if [[ "$(uname -m)" == "x86_64" ]]; then
|
||||
arch_flags="-march=x86-64"
|
||||
fi
|
||||
|
||||
if cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_GUI=OFF -DCMAKE_INSTALL_LIBDIR=lib \
|
||||
-DCMAKE_CXX_FLAGS="-Wno-template-body" .. >"$build_log" 2>&1 \
|
||||
-DCMAKE_C_FLAGS="$arch_flags" \
|
||||
-DCMAKE_CXX_FLAGS="$arch_flags -Wno-template-body" .. >"$build_log" 2>&1 \
|
||||
&& make -j "$(nproc)" >>"$build_log" 2>&1; then
|
||||
kill $progress_pid 2>/dev/null; wait $progress_pid 2>/dev/null
|
||||
$SUDO make install >/dev/null 2>&1
|
||||
|
||||
@@ -88,8 +88,11 @@
|
||||
}
|
||||
|
||||
/* Branded "i" — inline SVG that matches the logo icon.
|
||||
Sized to 0.9em so it sits naturally alongside text at any font-size. */
|
||||
.brand-i {
|
||||
Sized to 0.9em so it sits naturally alongside text at any font-size.
|
||||
Uses .logo .brand-i (0,2,0) to beat .logo span (0,1,1) in dashboard CSS
|
||||
which otherwise forces display:inline and breaks width/height. */
|
||||
.brand-i,
|
||||
.logo .brand-i {
|
||||
display: inline-block;
|
||||
width: 0.55em;
|
||||
height: 0.9em;
|
||||
|
||||
+55
-29
@@ -12,7 +12,7 @@ from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from werkzeug.security import generate_password_hash
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
logger = logging.getLogger('intercept.database')
|
||||
|
||||
@@ -252,14 +252,15 @@ def init_db() -> None:
|
||||
)
|
||||
''')
|
||||
|
||||
from config import ADMIN_PASSWORD, ADMIN_USERNAME
|
||||
|
||||
cursor = conn.execute('SELECT COUNT(*) FROM users')
|
||||
if cursor.fetchone()[0] == 0:
|
||||
import secrets as _secrets
|
||||
|
||||
from config import ADMIN_PASSWORD, ADMIN_USERNAME
|
||||
|
||||
# First run — seed the admin user from config / env vars.
|
||||
admin_password = ADMIN_PASSWORD
|
||||
if not admin_password:
|
||||
import secrets as _secrets
|
||||
|
||||
admin_password = _secrets.token_urlsafe(16)
|
||||
logger.warning(f"Generated admin password: {admin_password}")
|
||||
logger.warning("Set INTERCEPT_ADMIN_PASSWORD env var to use a fixed password.")
|
||||
@@ -277,6 +278,27 @@ def init_db() -> None:
|
||||
INSERT INTO users (username, password_hash, role)
|
||||
VALUES (?, ?, ?)
|
||||
''', (ADMIN_USERNAME, hashed_pw, 'admin'))
|
||||
elif ADMIN_PASSWORD:
|
||||
# Sync admin credentials from config on every startup so that
|
||||
# changes to config.py / env vars take effect without wiping the DB.
|
||||
row = conn.execute(
|
||||
'SELECT password_hash FROM users WHERE username = ? AND role = ?',
|
||||
(ADMIN_USERNAME, 'admin'),
|
||||
).fetchone()
|
||||
if row:
|
||||
if not check_password_hash(row['password_hash'], ADMIN_PASSWORD):
|
||||
conn.execute(
|
||||
'UPDATE users SET password_hash = ? WHERE username = ? AND role = ?',
|
||||
(generate_password_hash(ADMIN_PASSWORD), ADMIN_USERNAME, 'admin'),
|
||||
)
|
||||
logger.info(f"Admin password updated from config for user '{ADMIN_USERNAME}'.")
|
||||
else:
|
||||
# Admin user doesn't exist (maybe renamed) — create it.
|
||||
conn.execute(
|
||||
'INSERT OR IGNORE INTO users (username, password_hash, role) VALUES (?, ?, ?)',
|
||||
(ADMIN_USERNAME, generate_password_hash(ADMIN_PASSWORD), 'admin'),
|
||||
)
|
||||
logger.info(f"Created admin user '{ADMIN_USERNAME}' from config.")
|
||||
# =====================================================================
|
||||
# TSCM (Technical Surveillance Countermeasures) Tables
|
||||
# =====================================================================
|
||||
@@ -639,32 +661,36 @@ def get_setting(key: str, default: Any = None) -> Any:
|
||||
Returns:
|
||||
Setting value (auto-converted from JSON for complex types)
|
||||
"""
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'SELECT value, value_type FROM settings WHERE key = ?',
|
||||
(key,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
try:
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
'SELECT value, value_type FROM settings WHERE key = ?',
|
||||
(key,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row is None:
|
||||
return default
|
||||
|
||||
value, value_type = row['value'], row['value_type']
|
||||
|
||||
# Convert based on type
|
||||
if value_type == 'json':
|
||||
try:
|
||||
return json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
if row is None:
|
||||
return default
|
||||
elif value_type == 'int':
|
||||
return int(value)
|
||||
elif value_type == 'float':
|
||||
return float(value)
|
||||
elif value_type == 'bool':
|
||||
return value.lower() in ('true', '1', 'yes')
|
||||
else:
|
||||
return value
|
||||
|
||||
value, value_type = row['value'], row['value_type']
|
||||
|
||||
# Convert based on type
|
||||
if value_type == 'json':
|
||||
try:
|
||||
return json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
return default
|
||||
elif value_type == 'int':
|
||||
return int(value)
|
||||
elif value_type == 'float':
|
||||
return float(value)
|
||||
elif value_type == 'bool':
|
||||
return value.lower() in ('true', '1', 'yes')
|
||||
else:
|
||||
return value
|
||||
except sqlite3.OperationalError:
|
||||
logger.warning("Database unavailable reading setting '%s', using default", key)
|
||||
return default
|
||||
|
||||
|
||||
def set_setting(key: str, value: Any) -> None:
|
||||
|
||||
Reference in New Issue
Block a user