mirror of
https://github.com/smittix/intercept.git
synced 2026-07-24 08:58:09 -07:00
feat: add gunicorn + gevent production server via start.sh
Add start.sh as the recommended production entry point with: - gunicorn + gevent worker for concurrent SSE/WebSocket handling - CLI flags for port, host, debug, HTTPS, and dependency checks - Auto-fallback to Flask dev server if gunicorn not installed - Conditional gevent monkey-patch in app.py via INTERCEPT_USE_GEVENT env var - Docker CMD updated to use start.sh - Updated all docs, setup.sh, and requirements.txt accordingly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,11 +15,13 @@ All notable changes to iNTERCEPT will be documented in this file.
|
|||||||
- **Multi-SDR WeFax** - Multiple SDR hardware support for WeFax decoder
|
- **Multi-SDR WeFax** - Multiple SDR hardware support for WeFax decoder
|
||||||
- **Tool Path Overrides** - `INTERCEPT_*_PATH` environment variables for custom tool locations
|
- **Tool Path Overrides** - `INTERCEPT_*_PATH` environment variables for custom tool locations
|
||||||
- **Homebrew Tool Detection** - Native path detection for Apple Silicon Homebrew installations
|
- **Homebrew Tool Detection** - Native path detection for Apple Silicon Homebrew installations
|
||||||
|
- **Production Server** - `start.sh` with gunicorn + gevent for concurrent SSE/WebSocket handling — eliminates multi-client page load delays
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Morse decoder rebuilt with custom Goertzel decoder, replacing multimon-ng dependency
|
- Morse decoder rebuilt with custom Goertzel decoder, replacing multimon-ng dependency
|
||||||
- GPS mode upgraded to textured 3D globe visualization
|
- GPS mode upgraded to textured 3D globe visualization
|
||||||
- Destroy lifecycle added to all mode modules to prevent resource leaks
|
- Destroy lifecycle added to all mode modules to prevent resource leaks
|
||||||
|
- Docker container now uses gunicorn + gevent by default via `start.sh`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- ADS-B device release leak and startup performance regression
|
- ADS-B device release leak and startup performance regression
|
||||||
|
|||||||
@@ -28,12 +28,11 @@ docker compose --profile basic up -d --build
|
|||||||
# Initial setup (installs dependencies and configures SDR tools)
|
# Initial setup (installs dependencies and configures SDR tools)
|
||||||
./setup.sh
|
./setup.sh
|
||||||
|
|
||||||
# Run the application (requires sudo for SDR/network access)
|
# Run with production server (gunicorn + gevent, handles concurrent SSE/WebSocket)
|
||||||
sudo -E venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
|
|
||||||
# Or activate venv first
|
# Or for quick local dev (Flask dev server)
|
||||||
source venv/bin/activate
|
sudo -E venv/bin/python intercept.py
|
||||||
sudo -E python intercept.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
@@ -69,8 +68,9 @@ mypy .
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Entry Points
|
### Entry Points
|
||||||
- `intercept.py` - Main entry point script
|
- `start.sh` - Production startup script (gunicorn + gevent auto-detection, CLI flags, HTTPS, fallback to Flask dev server)
|
||||||
- `app.py` - Flask application initialization, global state management, process lifecycle, SSE streaming infrastructure
|
- `intercept.py` - Direct Flask dev server entry point (quick local development)
|
||||||
|
- `app.py` - Flask application initialization, global state management, process lifecycle, SSE streaming infrastructure, conditional gevent monkey-patch
|
||||||
|
|
||||||
### Route Blueprints (routes/)
|
### Route Blueprints (routes/)
|
||||||
Each signal type has its own Flask blueprint:
|
Each signal type has its own Flask blueprint:
|
||||||
@@ -121,7 +121,7 @@ Each signal type has its own Flask blueprint:
|
|||||||
|
|
||||||
### Key Patterns
|
### Key Patterns
|
||||||
|
|
||||||
**Server-Sent Events (SSE)**: All real-time features stream via SSE endpoints (`/stream_pager`, `/stream_sensor`, etc.). Pattern uses `queue.Queue` with timeout and keepalive messages.
|
**Server-Sent Events (SSE)**: All real-time features stream via SSE endpoints (`/stream_pager`, `/stream_sensor`, etc.). Pattern uses `queue.Queue` with timeout and keepalive messages. Under gunicorn + gevent, each SSE connection is a lightweight greenlet instead of an OS thread.
|
||||||
|
|
||||||
**Process Management**: External decoders run as subprocesses with output threads feeding queues. Use `safe_terminate()` for cleanup. Global locks prevent race conditions.
|
**Process Management**: External decoders run as subprocesses with output threads feeding queues. Use `safe_terminate()` for cleanup. Global locks prevent race conditions.
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ Each signal type has its own Flask blueprint:
|
|||||||
- **Mode Integration**: Each mode needs entries in `index.html` at ~12 points: CSS include, welcome card, partial include, visuals container, JS include, `validModes` set, `modeGroups` map, classList toggle, `modeNames`, visuals display toggle, titles, and init call in `switchMode()`
|
- **Mode Integration**: Each mode needs entries in `index.html` at ~12 points: CSS include, welcome card, partial include, visuals container, JS include, `validModes` set, `modeGroups` map, classList toggle, `modeNames`, visuals display toggle, titles, and init call in `switchMode()`
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
- `Dockerfile` - Single-stage build with all SDR tools compiled from source (dump1090, AIS-catcher, slowrx, SatDump, etc.)
|
- `Dockerfile` - Single-stage build with all SDR tools compiled from source (dump1090, AIS-catcher, slowrx, SatDump, etc.). CMD runs `start.sh` (gunicorn + gevent)
|
||||||
- `docker-compose.yml` - Two profiles: `basic` (standalone) and `history` (with Postgres for ADS-B)
|
- `docker-compose.yml` - Two profiles: `basic` (standalone) and `history` (with Postgres for ADS-B)
|
||||||
- `build-multiarch.sh` - Multi-arch build script for amd64 + arm64 (RPi5)
|
- `build-multiarch.sh` - Multi-arch build script for amd64 + arm64 (RPi5)
|
||||||
- Data persisted via `./data:/app/data` volume mount
|
- Data persisted via `./data:/app/data` volume mount
|
||||||
|
|||||||
+1
-1
@@ -274,4 +274,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|||||||
CMD curl -sf http://localhost:5050/health || exit 1
|
CMD curl -sf http://localhost:5050/health || exit 1
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
CMD ["python", "intercept.py"]
|
CMD ["/bin/bash", "start.sh"]
|
||||||
|
|||||||
@@ -50,47 +50,49 @@ Support the developer of this open-source project
|
|||||||
- **Meshtastic** - LoRa mesh network integration
|
- **Meshtastic** - LoRa mesh network integration
|
||||||
- **Space Weather** - Real-time solar and geomagnetic data from NOAA SWPC, NASA SDO, and HamQSL (no SDR required)
|
- **Space Weather** - Real-time solar and geomagnetic data from NOAA SWPC, NASA SDO, and HamQSL (no SDR required)
|
||||||
- **Spy Stations** - Number stations and diplomatic HF network database
|
- **Spy Stations** - Number stations and diplomatic HF network database
|
||||||
- **Remote Agents** - Distributed SIGINT with remote sensor nodes
|
- **Remote Agents** - Distributed SIGINT with remote sensor nodes
|
||||||
- **Offline Mode** - Bundled assets for air-gapped/field deployments
|
- **Offline Mode** - Bundled assets for air-gapped/field deployments
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CW / Morse Decoder Notes
|
## CW / Morse Decoder Notes
|
||||||
|
|
||||||
Live backend:
|
Live backend:
|
||||||
- Uses `rtl_fm` piped into `multimon-ng` (`MORSE_CW`) for real-time decode.
|
- Uses `rtl_fm` piped into `multimon-ng` (`MORSE_CW`) for real-time decode.
|
||||||
|
|
||||||
Recommended baseline settings:
|
Recommended baseline settings:
|
||||||
- **Tone**: `700 Hz`
|
- **Tone**: `700 Hz`
|
||||||
- **Bandwidth**: `200 Hz` (use `100 Hz` for crowded bands, `400 Hz` for drifting signals)
|
- **Bandwidth**: `200 Hz` (use `100 Hz` for crowded bands, `400 Hz` for drifting signals)
|
||||||
- **Threshold Mode**: `Auto`
|
- **Threshold Mode**: `Auto`
|
||||||
- **WPM Mode**: `Auto`
|
- **WPM Mode**: `Auto`
|
||||||
|
|
||||||
Auto Tone Track behavior:
|
Auto Tone Track behavior:
|
||||||
- Continuously measures nearby tone energy around the configured CW pitch.
|
- Continuously measures nearby tone energy around the configured CW pitch.
|
||||||
- Steers the detector toward the strongest valid CW tone when signal-to-noise is sufficient.
|
- Steers the detector toward the strongest valid CW tone when signal-to-noise is sufficient.
|
||||||
- Use **Hold Tone Lock** to freeze tracking once the desired signal is centered.
|
- Use **Hold Tone Lock** to freeze tracking once the desired signal is centered.
|
||||||
|
|
||||||
Troubleshooting (no decode / noisy decode):
|
Troubleshooting (no decode / noisy decode):
|
||||||
- Confirm demod path is **USB/CW-compatible** and frequency is tuned correctly.
|
- Confirm demod path is **USB/CW-compatible** and frequency is tuned correctly.
|
||||||
- If multiple SDRs are connected and the selected one has no PCM output, Morse startup now auto-tries other detected SDR devices and reports the active device/serial in status logs.
|
- If multiple SDRs are connected and the selected one has no PCM output, Morse startup now auto-tries other detected SDR devices and reports the active device/serial in status logs.
|
||||||
- Match **tone** and **bandwidth** to the actual sidetone/pitch.
|
- Match **tone** and **bandwidth** to the actual sidetone/pitch.
|
||||||
- Try **Threshold Auto** first; if needed, switch to manual threshold and recalibrate.
|
- Try **Threshold Auto** first; if needed, switch to manual threshold and recalibrate.
|
||||||
- Use **Reset/Calibrate** after major frequency or band condition changes.
|
- Use **Reset/Calibrate** after major frequency or band condition changes.
|
||||||
- Raise **Minimum Signal Gate** to suppress random noise keying.
|
- Raise **Minimum Signal Gate** to suppress random noise keying.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Installation / Debian / Ubuntu / MacOS
|
## Installation / Debian / Ubuntu / MacOS
|
||||||
|
|
||||||
**1. Clone and run:**
|
**1. Clone and run:**
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/smittix/intercept.git
|
git clone https://github.com/smittix/intercept.git
|
||||||
cd intercept
|
cd intercept
|
||||||
./setup.sh
|
./setup.sh
|
||||||
sudo -E venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Production vs Dev server:** `start.sh` auto-detects gunicorn + gevent and runs a production server with cooperative greenlets — handles multiple SSE/WebSocket clients without blocking. Falls back to Flask dev server if gunicorn is not installed. For quick local development, you can still use `sudo -E venv/bin/python intercept.py` directly.
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -174,7 +176,7 @@ Set these as environment variables for either local installs or Docker:
|
|||||||
```bash
|
```bash
|
||||||
INTERCEPT_ADSB_AUTO_START=true \
|
INTERCEPT_ADSB_AUTO_START=true \
|
||||||
INTERCEPT_SHARED_OBSERVER_LOCATION=false \
|
INTERCEPT_SHARED_OBSERVER_LOCATION=false \
|
||||||
sudo -E venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
**Docker example (.env)**
|
**Docker example (.env)**
|
||||||
|
|||||||
@@ -6,6 +6,15 @@ Flask application and shared state.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
if _os.environ.get('INTERCEPT_USE_GEVENT') == '1':
|
||||||
|
try:
|
||||||
|
from gevent import monkey as _monkey
|
||||||
|
_monkey.patch_all(subprocess=False)
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
del _os
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import site
|
import site
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ CHANGELOG = [
|
|||||||
"WeFax (Weather Fax) decoder with auto-scheduler and broadcast timeline",
|
"WeFax (Weather Fax) decoder with auto-scheduler and broadcast timeline",
|
||||||
"System Health monitoring mode with telemetry dashboard",
|
"System Health monitoring mode with telemetry dashboard",
|
||||||
"HTTPS support, HackRF TSCM RF scan, ADS-B voice alerts",
|
"HTTPS support, HackRF TSCM RF scan, ADS-B voice alerts",
|
||||||
|
"Production server (start.sh) with gunicorn + gevent for concurrent multi-client support",
|
||||||
"Multi-SDR support for WeFax, tool path overrides, native Homebrew detection",
|
"Multi-SDR support for WeFax, tool path overrides, native Homebrew detection",
|
||||||
"GPS mode upgraded to textured 3D globe",
|
"GPS mode upgraded to textured 3D globe",
|
||||||
"Destroy lifecycle added to all mode modules to prevent resource leaks",
|
"Destroy lifecycle added to all mode modules to prevent resource leaks",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# INTERCEPT - Signal Intelligence Platform
|
# INTERCEPT - Signal Intelligence Platform
|
||||||
# Docker Compose configuration for easy deployment
|
# Docker Compose configuration for easy deployment
|
||||||
#
|
#
|
||||||
|
# Uses gunicorn + gevent production server via start.sh (handles concurrent SSE/WebSocket)
|
||||||
|
#
|
||||||
# Basic usage (build locally):
|
# Basic usage (build locally):
|
||||||
# docker compose --profile basic up -d --build
|
# docker compose --profile basic up -d --build
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -466,6 +466,7 @@ The settings modal shows availability status for each bundled asset:
|
|||||||
## General
|
## General
|
||||||
|
|
||||||
- **Web-based interface** - no desktop app needed
|
- **Web-based interface** - no desktop app needed
|
||||||
|
- **Production server** - gunicorn + gevent via `start.sh` for concurrent SSE/WebSocket handling (falls back to Flask dev server)
|
||||||
- **Live message streaming** via Server-Sent Events (SSE)
|
- **Live message streaming** via Server-Sent Events (SSE)
|
||||||
- **Audio alerts** with mute toggle
|
- **Audio alerts** with mute toggle
|
||||||
- **Message export** to CSV/JSON
|
- **Message export** to CSV/JSON
|
||||||
|
|||||||
+5
-2
@@ -139,10 +139,13 @@ pip install -r requirements.txt
|
|||||||
After installation:
|
After installation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo -E venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
|
|
||||||
# Custom port
|
# Custom port
|
||||||
INTERCEPT_PORT=8080 sudo -E venv/bin/python intercept.py
|
sudo ./start.sh -p 8080
|
||||||
|
|
||||||
|
# HTTPS
|
||||||
|
sudo ./start.sh --https
|
||||||
```
|
```
|
||||||
|
|
||||||
Open **http://localhost:5050** in your browser.
|
Open **http://localhost:5050** in your browser.
|
||||||
|
|||||||
+2
-3
@@ -18,10 +18,9 @@ By default, INTERCEPT binds to `0.0.0.0:5050`, making it accessible from any net
|
|||||||
echo "block in on en0 proto tcp from any to any port 5050" | sudo pfctl -ef -
|
echo "block in on en0 proto tcp from any to any port 5050" | sudo pfctl -ef -
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Bind to Localhost**: For local-only access, set the host environment variable:
|
2. **Bind to Localhost**: For local-only access, set the host or use the CLI flag:
|
||||||
```bash
|
```bash
|
||||||
export INTERCEPT_HOST=127.0.0.1
|
sudo ./start.sh -H 127.0.0.1
|
||||||
python intercept.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Trusted Networks Only**: Only run INTERCEPT on networks you trust. The application has no authentication mechanism.
|
3. **Trusted Networks Only**: Only run INTERCEPT on networks you trust. The application has no authentication mechanism.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ sudo apt install python3-flask python3-requests python3-serial python3-skyfield
|
|||||||
# Then create venv with system packages
|
# Then create venv with system packages
|
||||||
python3 -m venv --system-site-packages venv
|
python3 -m venv --system-site-packages venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
sudo venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### "error: externally-managed-environment" (pip blocked)
|
### "error: externally-managed-environment" (pip blocked)
|
||||||
@@ -61,7 +61,7 @@ sudo apt install python3.11 python3.11-venv python3-pip
|
|||||||
python3.11 -m venv venv
|
python3.11 -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
sudo venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Alternative: Use the setup script
|
### Alternative: Use the setup script
|
||||||
@@ -336,7 +336,7 @@ rtl_fm -M am -f 118000000 -s 24000 -r 24000 -g 40 2>/dev/null | \
|
|||||||
|
|
||||||
Run INTERCEPT with sudo:
|
Run INTERCEPT with sudo:
|
||||||
```bash
|
```bash
|
||||||
sudo -E venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interface not found after enabling monitor mode
|
### Interface not found after enabling monitor mode
|
||||||
|
|||||||
+18
-2
@@ -172,7 +172,7 @@ Set the following environment variables (Docker recommended):
|
|||||||
```bash
|
```bash
|
||||||
INTERCEPT_ADSB_AUTO_START=true \
|
INTERCEPT_ADSB_AUTO_START=true \
|
||||||
INTERCEPT_SHARED_OBSERVER_LOCATION=false \
|
INTERCEPT_SHARED_OBSERVER_LOCATION=false \
|
||||||
sudo -E venv/bin/python intercept.py
|
sudo ./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
**Docker example (.env)**
|
**Docker example (.env)**
|
||||||
@@ -518,10 +518,26 @@ INTERCEPT can be configured via environment variables:
|
|||||||
| `INTERCEPT_LOG_LEVEL` | `WARNING` | Log level (DEBUG, INFO, WARNING, ERROR) |
|
| `INTERCEPT_LOG_LEVEL` | `WARNING` | Log level (DEBUG, INFO, WARNING, ERROR) |
|
||||||
| `INTERCEPT_DEFAULT_GAIN` | `40` | Default RTL-SDR gain |
|
| `INTERCEPT_DEFAULT_GAIN` | `40` | Default RTL-SDR gain |
|
||||||
|
|
||||||
Example: `INTERCEPT_PORT=8080 sudo -E venv/bin/python intercept.py`
|
Example: `INTERCEPT_PORT=8080 sudo ./start.sh`
|
||||||
|
|
||||||
## Command-line Options
|
## Command-line Options
|
||||||
|
|
||||||
|
### Production server (recommended)
|
||||||
|
|
||||||
|
```
|
||||||
|
./start.sh --help
|
||||||
|
|
||||||
|
-p, --port PORT Port to listen on (default: 5050)
|
||||||
|
-H, --host HOST Host to bind to (default: 0.0.0.0)
|
||||||
|
-d, --debug Run in debug mode (Flask dev server)
|
||||||
|
--https Enable HTTPS with self-signed certificate
|
||||||
|
--check-deps Check dependencies and exit
|
||||||
|
```
|
||||||
|
|
||||||
|
`start.sh` auto-detects gunicorn + gevent and runs a production WSGI server with cooperative greenlets — this handles multiple SSE streams and WebSocket connections concurrently without blocking. Falls back to the Flask dev server if gunicorn is not installed.
|
||||||
|
|
||||||
|
### Development server
|
||||||
|
|
||||||
```
|
```
|
||||||
python3 intercept.py --help
|
python3 intercept.py --help
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -331,7 +331,7 @@
|
|||||||
<pre><code>git clone https://github.com/smittix/intercept.git
|
<pre><code>git clone https://github.com/smittix/intercept.git
|
||||||
cd intercept
|
cd intercept
|
||||||
./setup.sh
|
./setup.sh
|
||||||
sudo -E venv/bin/python intercept.py</code></pre>
|
sudo ./start.sh</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<p class="install-note">Requires Python 3.9+ and RTL-SDR drivers</p>
|
<p class="install-note">Requires Python 3.9+ and RTL-SDR drivers</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -47,3 +47,7 @@ websocket-client>=1.6.0
|
|||||||
|
|
||||||
# System health monitoring (optional - graceful fallback if unavailable)
|
# System health monitoring (optional - graceful fallback if unavailable)
|
||||||
psutil>=5.9.0
|
psutil>=5.9.0
|
||||||
|
|
||||||
|
# Production WSGI server (optional - falls back to Flask dev server)
|
||||||
|
gunicorn>=21.2.0
|
||||||
|
gevent>=23.9.0
|
||||||
|
|||||||
@@ -337,7 +337,8 @@ install_python_deps() {
|
|||||||
info "Installing optional packages..."
|
info "Installing optional packages..."
|
||||||
for pkg in "numpy>=1.24.0" "scipy>=1.10.0" "Pillow>=9.0.0" "skyfield>=1.45" \
|
for pkg in "numpy>=1.24.0" "scipy>=1.10.0" "Pillow>=9.0.0" "skyfield>=1.45" \
|
||||||
"bleak>=0.21.0" "psycopg2-binary>=2.9.9" "meshtastic>=2.0.0" \
|
"bleak>=0.21.0" "psycopg2-binary>=2.9.9" "meshtastic>=2.0.0" \
|
||||||
"scapy>=2.4.5" "qrcode[pil]>=7.4" "cryptography>=41.0.0"; do
|
"scapy>=2.4.5" "qrcode[pil]>=7.4" "cryptography>=41.0.0" \
|
||||||
|
"gunicorn>=21.2.0" "gevent>=23.9.0"; do
|
||||||
pkg_name="${pkg%%>=*}"
|
pkg_name="${pkg%%>=*}"
|
||||||
if ! $PIP install "$pkg" 2>/dev/null; then
|
if ! $PIP install "$pkg" 2>/dev/null; then
|
||||||
warn "${pkg_name} failed to install (optional - related features may be unavailable)"
|
warn "${pkg_name} failed to install (optional - related features may be unavailable)"
|
||||||
@@ -1590,6 +1591,9 @@ final_summary_and_hard_fail() {
|
|||||||
echo "============================================"
|
echo "============================================"
|
||||||
echo
|
echo
|
||||||
echo "To start INTERCEPT:"
|
echo "To start INTERCEPT:"
|
||||||
|
echo " sudo ./start.sh"
|
||||||
|
echo
|
||||||
|
echo "Or for quick local dev:"
|
||||||
echo " sudo -E venv/bin/python intercept.py"
|
echo " sudo -E venv/bin/python intercept.py"
|
||||||
echo
|
echo
|
||||||
echo "Then open http://localhost:5050 in your browser"
|
echo "Then open http://localhost:5050 in your browser"
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# INTERCEPT - Production Startup Script
|
||||||
|
#
|
||||||
|
# Starts INTERCEPT with gunicorn + gevent for production use.
|
||||||
|
# Falls back to Flask dev server if gunicorn is not installed.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./start.sh # Default: 0.0.0.0:5050
|
||||||
|
# ./start.sh -p 8080 # Custom port
|
||||||
|
# ./start.sh --https # HTTPS with self-signed cert
|
||||||
|
# ./start.sh --debug # Debug mode (Flask dev server)
|
||||||
|
# ./start.sh --check-deps # Check dependencies and exit
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Defaults (can be overridden by env vars or CLI flags) ────────────────────
|
||||||
|
HOST="${INTERCEPT_HOST:-0.0.0.0}"
|
||||||
|
PORT="${INTERCEPT_PORT:-5050}"
|
||||||
|
DEBUG=0
|
||||||
|
HTTPS=0
|
||||||
|
CHECK_DEPS=0
|
||||||
|
|
||||||
|
# ── Parse CLI arguments ─────────────────────────────────────────────────────
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-p|--port)
|
||||||
|
PORT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-H|--host)
|
||||||
|
HOST="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-d|--debug)
|
||||||
|
DEBUG=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--https)
|
||||||
|
HTTPS=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--check-deps)
|
||||||
|
CHECK_DEPS=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
echo "Usage: start.sh [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -p, --port PORT Port to listen on (default: 5050)"
|
||||||
|
echo " -H, --host HOST Host to bind to (default: 0.0.0.0)"
|
||||||
|
echo " -d, --debug Run in debug mode (Flask dev server)"
|
||||||
|
echo " --https Enable HTTPS with self-signed certificate"
|
||||||
|
echo " --check-deps Check dependencies and exit"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Export for config.py ─────────────────────────────────────────────────────
|
||||||
|
export INTERCEPT_HOST="$HOST"
|
||||||
|
export INTERCEPT_PORT="$PORT"
|
||||||
|
|
||||||
|
# ── Dependency check (delegate to intercept.py) ─────────────────────────────
|
||||||
|
if [[ "$CHECK_DEPS" -eq 1 ]]; then
|
||||||
|
exec python intercept.py --check-deps
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Debug mode always uses Flask dev server ──────────────────────────────────
|
||||||
|
if [[ "$DEBUG" -eq 1 ]]; then
|
||||||
|
echo "[INTERCEPT] Starting in debug mode (Flask dev server)..."
|
||||||
|
export INTERCEPT_DEBUG=1
|
||||||
|
exec python intercept.py --host "$HOST" --port "$PORT" --debug
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── HTTPS certificate generation ────────────────────────────────────────────
|
||||||
|
CERT_DIR="certs"
|
||||||
|
CERT_FILE="$CERT_DIR/intercept.crt"
|
||||||
|
KEY_FILE="$CERT_DIR/intercept.key"
|
||||||
|
|
||||||
|
if [[ "$HTTPS" -eq 1 ]]; then
|
||||||
|
if [[ ! -f "$CERT_FILE" || ! -f "$KEY_FILE" ]]; then
|
||||||
|
echo "[INTERCEPT] Generating self-signed SSL certificate..."
|
||||||
|
mkdir -p "$CERT_DIR"
|
||||||
|
openssl req -x509 -newkey rsa:2048 \
|
||||||
|
-keyout "$KEY_FILE" -out "$CERT_FILE" \
|
||||||
|
-days 365 -nodes \
|
||||||
|
-subj '/CN=intercept/O=INTERCEPT/C=US' 2>/dev/null
|
||||||
|
echo "[INTERCEPT] SSL certificate generated: $CERT_FILE"
|
||||||
|
else
|
||||||
|
echo "[INTERCEPT] Using existing SSL certificate: $CERT_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Detect gunicorn + gevent ─────────────────────────────────────────────────
|
||||||
|
HAS_GUNICORN=0
|
||||||
|
HAS_GEVENT=0
|
||||||
|
|
||||||
|
if python -c "import gunicorn" 2>/dev/null; then
|
||||||
|
HAS_GUNICORN=1
|
||||||
|
fi
|
||||||
|
if python -c "import gevent" 2>/dev/null; then
|
||||||
|
HAS_GEVENT=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Start the server ─────────────────────────────────────────────────────────
|
||||||
|
if [[ "$HAS_GUNICORN" -eq 1 && "$HAS_GEVENT" -eq 1 ]]; then
|
||||||
|
echo "[INTERCEPT] Starting production server (gunicorn + gevent)..."
|
||||||
|
echo "[INTERCEPT] Listening on ${HOST}:${PORT}"
|
||||||
|
|
||||||
|
export INTERCEPT_USE_GEVENT=1
|
||||||
|
|
||||||
|
GUNICORN_ARGS=(
|
||||||
|
-k gevent
|
||||||
|
-w 1
|
||||||
|
--timeout 300
|
||||||
|
--worker-connections 1000
|
||||||
|
--bind "${HOST}:${PORT}"
|
||||||
|
--access-logfile -
|
||||||
|
--error-logfile -
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "$HTTPS" -eq 1 ]]; then
|
||||||
|
GUNICORN_ARGS+=(--certfile "$CERT_FILE" --keyfile "$KEY_FILE")
|
||||||
|
echo "[INTERCEPT] HTTPS enabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec gunicorn "${GUNICORN_ARGS[@]}" app:app
|
||||||
|
else
|
||||||
|
if [[ "$HAS_GUNICORN" -eq 0 ]]; then
|
||||||
|
echo "[INTERCEPT] gunicorn not found — falling back to Flask dev server"
|
||||||
|
fi
|
||||||
|
if [[ "$HAS_GEVENT" -eq 0 ]]; then
|
||||||
|
echo "[INTERCEPT] gevent not found — falling back to Flask dev server"
|
||||||
|
fi
|
||||||
|
echo "[INTERCEPT] Install with: pip install gunicorn gevent"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
FLASK_ARGS=(--host "$HOST" --port "$PORT")
|
||||||
|
if [[ "$HTTPS" -eq 1 ]]; then
|
||||||
|
FLASK_ARGS+=(--https)
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec python intercept.py "${FLASK_ARGS[@]}"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user