Files
giglez/src/api/main_orm_legacy.py
T
Trilltechnician 4237c4bdb8 Phase 1 & 2: Cleanup redundant code and integrate pattern decoder
## Phase 1: Code Cleanup (~1,859 lines removed)

**Deleted Redundant Matchers:**
-  strategies_orm.py (356 lines) - Old ORM-based strategies
-  simple_matcher.py (351 lines) - Replaced by strategies.py
-  rtl433_matcher.py (352 lines) - Replaced by strategies.py

**Archived Old Scripts:**
- Moved 11 one-time analysis/import scripts to scripts/archive/
- Scripts: analyze_flipper_signatures, analyze_tembed_files, identify_tembed_devices,
  import_flipper_sqlite, import_tembed_signatures, match_tembed_with_db,
  match_with_flipper_db, rematch_captures, test_gps_extraction,
  test_tembed_matching, test_wardriving_import

**Consolidated API:**
- Renamed main.py → main_orm_legacy.py (archived old ORM-based API)
- main_simple.py is now the primary production API

## Phase 2: Pattern Decoder Integration 

**CRITICAL FIX: Pattern decoder now integrated into production API!**

**Changes:**
1. Updated main_simple.py to use unified SignatureMatcher
2. Added 6 strategies to matcher pipeline:
   - ExactMatcher (protocol + frequency)
   - FrequencyMatcher (frequency-based)
   - BitPatternMatcher (data patterns)
   - TimingMatcher (timing-based)
   - RTL433DecoderStrategy (RTL_433 decoder)
   - PatternBasedStrategy (NEW! Pattern decoder for short captures)

3. Created MockDB class for simplified mode (no real database)
4. Replaced old get_matcher() with get_matcher_engine()
5. Updated upload handler to use MatchResult format
6. All matches now include confidence scores and match methods

**Result:**
- Pattern decoder is NOW ACTIVE in production 🎉
- Unified matching pipeline with 6 strategies
- Cleaner codebase (-1,859 lines)
- Single source of truth for matching logic

🎉 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 22:33:32 -08:00

281 lines
8.7 KiB
Python

"""
GigLez FastAPI Application
Environment-aware API server supporting both development (Termux) and production (Server) modes
"""
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from contextlib import asynccontextmanager
from loguru import logger
from pathlib import Path
import time
import sys
# Add project root to Python path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from config.settings import settings
from config.database import get_db_config
# =============================================================================
# LIFESPAN MANAGEMENT
# =============================================================================
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Application lifespan manager
Handles startup and shutdown events
"""
# Startup
logger.info("=" * 80)
logger.info("GigLez API Starting")
logger.info("=" * 80)
# Display configuration
settings.display()
# Validate configuration
errors = settings.validate()
if errors:
logger.error("Configuration validation failed:")
for error in errors:
logger.error(f" ❌ {error}")
raise RuntimeError("Invalid configuration")
# Test database connection
db_config = get_db_config()
if not db_config.test_connection():
raise RuntimeError("Database connection failed")
if not db_config.test_postgis():
raise RuntimeError("PostGIS extension not available")
logger.success("✅ Database connection established")
logger.success("✅ PostGIS extension available")
# Initialize storage backend
from src.core.storage import get_storage_backend
storage = get_storage_backend()
logger.success(f"✅ Storage backend initialized: {type(storage).__name__}")
# Initialize background task system
if settings.use_celery:
logger.info("Using Celery for background tasks")
# Celery app will be imported when needed
else:
logger.info("Using synchronous background tasks")
logger.success("=" * 80)
logger.success(f"🚀 GigLez API ready on {settings.api_host}:{settings.api_port}")
logger.success(f"📡 Mode: {settings.mode.value}")
logger.success("=" * 80)
yield
# Shutdown
logger.info("=" * 80)
logger.info("GigLez API Shutting Down")
logger.info("=" * 80)
# Close database connections
db_config.close()
logger.success("✅ Database connections closed")
logger.info("Shutdown complete")
# =============================================================================
# APPLICATION INSTANCE
# =============================================================================
app = FastAPI(
title="GigLez API",
description="IoT RF Device Mapping Platform - Wigle for Sub-GHz Signals",
version="1.0.0",
lifespan=lifespan,
docs_url="/docs" if settings.debug_endpoints else None,
redoc_url="/redoc" if settings.debug_endpoints else None,
openapi_url="/openapi.json" if settings.debug_endpoints else None,
)
# Mount static files and templates
BASE_DIR = Path(__file__).parent.parent.parent
app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static")
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
# =============================================================================
# MIDDLEWARE
# =============================================================================
# CORS Middleware
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["X-Total-Count", "X-Page-Size"],
)
# GZip compression
app.add_middleware(GZipMiddleware, minimum_size=1000)
# Request timing middleware
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
"""Add request processing time header"""
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = f"{process_time:.4f}"
return response
# Request logging middleware
@app.middleware("http")
async def log_requests(request: Request, call_next):
"""Log all requests"""
logger.info(f"{request.method} {request.url.path}")
response = await call_next(request)
logger.info(f"{request.method} {request.url.path} - {response.status_code}")
return response
# =============================================================================
# EXCEPTION HANDLERS
# =============================================================================
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Global exception handler"""
logger.error(f"Unhandled exception: {exc}", exc_info=True)
if settings.debug_errors:
# Detailed error in development
return JSONResponse(
status_code=500,
content={
"error": "Internal Server Error",
"detail": str(exc),
"type": type(exc).__name__
}
)
else:
# Generic error in production
return JSONResponse(
status_code=500,
content={"error": "Internal Server Error"}
)
# =============================================================================
# ROOT ENDPOINTS
# =============================================================================
@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
"""Root endpoint - Serve web interface"""
return templates.TemplateResponse("index.html", {"request": request})
@app.get("/api")
async def api_root():
"""API information endpoint"""
return {
"name": "GigLez API",
"version": "1.0.0",
"description": "IoT RF Device Mapping Platform",
"mode": settings.mode.value,
"docs": "/docs" if settings.debug_endpoints else "disabled",
"status": "operational"
}
@app.get("/health")
async def health():
"""Health check endpoint"""
# Test database connection
db_config = get_db_config()
db_healthy = db_config.test_connection()
return {
"status": "healthy" if db_healthy else "unhealthy",
"database": "connected" if db_healthy else "disconnected",
"mode": settings.mode.value,
"hardware_enabled": settings.enable_hardware
}
@app.get("/version")
async def version():
"""Version information"""
return {
"version": "1.0.0",
"mode": settings.mode.value,
"api": "v1"
}
# =============================================================================
# IMPORT ROUTERS
# =============================================================================
# Import and register route modules
from src.api.routes import captures, devices, query, stats
# Register routers
app.include_router(captures.router, prefix="/api/v1", tags=["captures"])
app.include_router(devices.router, prefix="/api/v1", tags=["devices"])
app.include_router(query.router, prefix="/api/v1", tags=["query"])
app.include_router(stats.router, prefix="/api/v1", tags=["statistics"])
# Hardware endpoints (development mode only)
if settings.enable_hardware:
from src.api.routes import hardware
app.include_router(hardware.router, prefix="/api/v1", tags=["hardware"])
logger.info("✅ Hardware endpoints enabled")
# Debug endpoints (development mode only)
if settings.debug_endpoints:
from src.api.routes import debug
app.include_router(debug.router, prefix="/api/debug", tags=["debug"])
logger.info("✅ Debug endpoints enabled")
# =============================================================================
# STARTUP MESSAGE
# =============================================================================
logger.info("FastAPI application initialized")
logger.info(f"Mode: {settings.mode.value}")
logger.info(f"Authentication: {'Required' if settings.require_auth else 'Optional'}")
logger.info(f"CORS Origins: {settings.cors_origins}")
logger.info(f"Storage: {settings.storage_type.value}")
# =============================================================================
# RUN WITH UVICORN (for development)
# =============================================================================
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"src.api.main:app",
host=settings.api_host,
port=settings.api_port,
reload=settings.auto_reload,
log_level=settings.log_level.lower(),
access_log=True,
)