802 lines
22 KiB
Markdown
802 lines
22 KiB
Markdown
# GigLez Deployment Considerations: Development vs Production
|
|
|
|
**Created**: 2026-01-12
|
|
**Purpose**: Identify critical differences between local development and production deployment
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
GigLez needs to support two distinct deployment scenarios:
|
|
|
|
1. **Local Development** (Termux on Android)
|
|
- Resource-constrained (mobile device)
|
|
- Single-user access
|
|
- Direct USB serial communication
|
|
- Local file storage
|
|
- Development/testing workflow
|
|
|
|
2. **Production Server** (Cloud/VPS)
|
|
- Multi-user platform
|
|
- Public API access
|
|
- No hardware connection
|
|
- Object storage for files
|
|
- High availability requirements
|
|
|
|
**Key Insight**: We need a **modular architecture** that supports both scenarios without code duplication.
|
|
|
|
---
|
|
|
|
## Architecture Strategy
|
|
|
|
### Hybrid Architecture: "Capture" vs "Platform" Mode
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ GigLez Application │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌────────────────┐ ┌────────────────┐ │
|
|
│ │ Capture Mode │ │ Platform Mode │ │
|
|
│ │ (Termux) │ │ (Server) │ │
|
|
│ ├────────────────┤ ├────────────────┤ │
|
|
│ │ - T-Embed USB │ │ - No hardware │ │
|
|
│ │ - Local GPS │ │ - File uploads │ │
|
|
│ │ - Auto-capture │ │ - Multi-user │ │
|
|
│ │ - Local API │ │ - Public API │ │
|
|
│ └────────────────┘ └────────────────┘ │
|
|
│ │ │ │
|
|
│ └──────────┬───────────────┘ │
|
|
│ │ │
|
|
│ ┌───────▼────────┐ │
|
|
│ │ Shared Core │ │
|
|
│ ├────────────────┤ │
|
|
│ │ - Database │ │
|
|
│ │ - Parser │ │
|
|
│ │ - Matcher │ │
|
|
│ │ - GPS Validator│ │
|
|
│ │ - API Endpoints│ │
|
|
│ └────────────────┘ │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Detailed Comparison
|
|
|
|
### 1. Hardware Access
|
|
|
|
#### Development (Termux)
|
|
- **T-Embed Connection**: USB serial communication (pyserial)
|
|
- **GPS Source**: Android native GPS via SL4A or Termux:API
|
|
- **Real-time Capture**: Direct signal scanning and capture
|
|
- **Storage**: Local filesystem on Android device
|
|
|
|
**Implications**:
|
|
- Need hardware abstraction layer
|
|
- Support offline operation
|
|
- Handle USB disconnections
|
|
- Batch upload when network available
|
|
|
|
#### Production (Server)
|
|
- **No Hardware**: Accept pre-captured .sub files only
|
|
- **GPS Source**: Embedded in upload manifest
|
|
- **No Real-time**: Historical data only
|
|
- **Storage**: Object storage (S3, MinIO, or filesystem)
|
|
|
|
**Implications**:
|
|
- No serial communication code needed
|
|
- Network-dependent operation
|
|
- Focus on file processing pipeline
|
|
- Horizontal scaling possible
|
|
|
|
**Solution**:
|
|
```python
|
|
# Abstract hardware interface
|
|
class CaptureSource(ABC):
|
|
@abstractmethod
|
|
def scan_signals(self): pass
|
|
|
|
@abstractmethod
|
|
def get_gps(self): pass
|
|
|
|
class TEmbedSource(CaptureSource):
|
|
"""Development: Direct hardware"""
|
|
pass
|
|
|
|
class UploadSource(CaptureSource):
|
|
"""Production: File uploads"""
|
|
pass
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Database Configuration
|
|
|
|
#### Development (Termux)
|
|
- **PostgreSQL**: Local installation via `pkg install postgresql`
|
|
- **Host**: localhost or 127.0.0.1
|
|
- **Port**: 5432
|
|
- **Connections**: Small pool (5 + 5 overflow)
|
|
- **Storage**: Device internal storage or SD card
|
|
|
|
**Considerations**:
|
|
- Limited RAM on mobile devices
|
|
- Smaller connection pool
|
|
- May need to tune PostgreSQL for mobile
|
|
- Backup to SD card or cloud
|
|
|
|
#### Production (Server)
|
|
- **PostgreSQL**: Dedicated server or managed database (RDS, Cloud SQL)
|
|
- **Host**: Remote database server
|
|
- **Port**: 5432 (or custom)
|
|
- **Connections**: Larger pool (20 + 40 overflow)
|
|
- **Storage**: SSD/NVMe for performance
|
|
|
|
**Considerations**:
|
|
- Connection pooling via PgBouncer
|
|
- Read replicas for scaling
|
|
- Automated backups
|
|
- Point-in-time recovery
|
|
|
|
**Solution**: Environment-based configuration
|
|
```bash
|
|
# Development (.env)
|
|
GIGLEZ_DB_HOST=localhost
|
|
GIGLEZ_DB_POOL_SIZE=5
|
|
GIGLEZ_DB_MAX_OVERFLOW=5
|
|
|
|
# Production (.env.production)
|
|
GIGLEZ_DB_HOST=db.giglez.com
|
|
GIGLEZ_DB_POOL_SIZE=20
|
|
GIGLEZ_DB_MAX_OVERFLOW=40
|
|
```
|
|
|
|
---
|
|
|
|
### 3. File Storage
|
|
|
|
#### Development (Termux)
|
|
- **Location**: `/data/data/com.termux/files/home/giglez/storage/`
|
|
- **Structure**: Organized by session/date
|
|
- **Size**: Limited by device storage (typically < 64GB)
|
|
- **Access**: Direct filesystem access
|
|
|
|
**Structure**:
|
|
```
|
|
storage/
|
|
├── captures/
|
|
│ ├── 2026-01-12/
|
|
│ │ ├── session_abc123/
|
|
│ │ │ ├── capture_001.sub
|
|
│ │ │ ├── capture_002.sub
|
|
│ │ │ └── manifest.json
|
|
```
|
|
|
|
#### Production (Server)
|
|
- **Location**: Object storage (S3, MinIO, GCS) or networked filesystem
|
|
- **Structure**: Content-addressed by file hash
|
|
- **Size**: Unlimited (cloud storage)
|
|
- **Access**: Via storage API or CDN
|
|
|
|
**Structure**:
|
|
```
|
|
s3://giglez-captures/
|
|
├── ab/
|
|
│ ├── c1/
|
|
│ │ └── abc123...def456.sub # First 2+2 chars of hash for sharding
|
|
```
|
|
|
|
**Solution**: Storage abstraction
|
|
```python
|
|
class StorageBackend(ABC):
|
|
@abstractmethod
|
|
def save_file(self, file_hash: str, content: bytes): pass
|
|
|
|
@abstractmethod
|
|
def get_file(self, file_hash: str) -> bytes: pass
|
|
|
|
class LocalStorage(StorageBackend):
|
|
"""Development: Filesystem"""
|
|
pass
|
|
|
|
class S3Storage(StorageBackend):
|
|
"""Production: S3-compatible storage"""
|
|
pass
|
|
```
|
|
|
|
---
|
|
|
|
### 4. API Access & Security
|
|
|
|
#### Development (Termux)
|
|
- **Access**: Local only (127.0.0.1) or local network
|
|
- **Port**: 8000 (or any available)
|
|
- **HTTPS**: Not required (localhost)
|
|
- **Authentication**: Optional (single user)
|
|
- **CORS**: Permissive (development)
|
|
- **Rate Limiting**: None needed
|
|
|
|
**Use Cases**:
|
|
- Testing API endpoints
|
|
- Web UI development
|
|
- Local data management
|
|
- Debugging
|
|
|
|
#### Production (Server)
|
|
- **Access**: Public internet
|
|
- **Port**: 443 (HTTPS) behind reverse proxy
|
|
- **HTTPS**: Required (Let's Encrypt)
|
|
- **Authentication**: JWT + API keys mandatory
|
|
- **CORS**: Strict origin whitelisting
|
|
- **Rate Limiting**: Aggressive (per-user, per-IP)
|
|
|
|
**Security Requirements**:
|
|
- Nginx/Caddy reverse proxy
|
|
- SSL/TLS certificates
|
|
- API key rotation
|
|
- Request validation
|
|
- SQL injection prevention
|
|
- File upload limits
|
|
- DDoS protection
|
|
|
|
**Solution**: Environment-based security
|
|
```python
|
|
# config/security.py
|
|
if os.getenv("GIGLEZ_MODE") == "production":
|
|
REQUIRE_AUTH = True
|
|
RATE_LIMIT = "10/minute"
|
|
ALLOWED_ORIGINS = ["https://giglez.com"]
|
|
else:
|
|
REQUIRE_AUTH = False
|
|
RATE_LIMIT = None
|
|
ALLOWED_ORIGINS = ["*"]
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Performance & Scaling
|
|
|
|
#### Development (Termux)
|
|
- **Workers**: 1 Uvicorn worker
|
|
- **Concurrency**: Minimal (single user)
|
|
- **Caching**: In-memory only (LRU)
|
|
- **Background Tasks**: Simple queue or synchronous
|
|
- **Resource Limits**: RAM < 2GB, CPU 2-4 cores
|
|
|
|
**Optimization Focus**:
|
|
- Memory efficiency
|
|
- Minimal background processes
|
|
- Battery life considerations
|
|
- Offline operation
|
|
|
|
#### Production (Server)
|
|
- **Workers**: Multiple Uvicorn workers (4-8)
|
|
- **Concurrency**: High (100+ concurrent users)
|
|
- **Caching**: Redis + LRU
|
|
- **Background Tasks**: Celery + RabbitMQ/Redis
|
|
- **Resource Limits**: RAM 8-32GB, CPU 4-16 cores
|
|
|
|
**Optimization Focus**:
|
|
- Query performance
|
|
- Connection pooling
|
|
- Horizontal scaling
|
|
- CDN for static assets
|
|
- Load balancing
|
|
|
|
**Solution**: Conditional imports
|
|
```python
|
|
# src/api/main.py
|
|
if PRODUCTION_MODE:
|
|
from celery import Celery
|
|
celery_app = Celery('giglez', broker='redis://localhost')
|
|
else:
|
|
# Use FastAPI BackgroundTasks for development
|
|
celery_app = None
|
|
```
|
|
|
|
---
|
|
|
|
### 6. Logging & Monitoring
|
|
|
|
#### Development (Termux)
|
|
- **Logging**: Console output (stdout)
|
|
- **Level**: DEBUG
|
|
- **Format**: Colorized, verbose
|
|
- **Storage**: Optional file logging
|
|
- **Monitoring**: Manual observation
|
|
|
|
**Example**:
|
|
```python
|
|
logger.add(sys.stdout, level="DEBUG", colorize=True)
|
|
```
|
|
|
|
#### Production (Server)
|
|
- **Logging**: Structured JSON to file + syslog
|
|
- **Level**: INFO/WARNING
|
|
- **Format**: JSON for parsing
|
|
- **Storage**: Log rotation + cloud storage
|
|
- **Monitoring**: Prometheus + Grafana, error tracking (Sentry)
|
|
|
|
**Example**:
|
|
```python
|
|
logger.add(
|
|
"/var/log/giglez/app.log",
|
|
level="INFO",
|
|
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
|
|
rotation="100 MB",
|
|
compression="gz"
|
|
)
|
|
```
|
|
|
|
**Solution**: Environment-based logging
|
|
```python
|
|
# config/logging.py
|
|
if PRODUCTION_MODE:
|
|
setup_production_logging()
|
|
else:
|
|
setup_development_logging()
|
|
```
|
|
|
|
---
|
|
|
|
### 7. Backup & Disaster Recovery
|
|
|
|
#### Development (Termux)
|
|
- **Database**: Manual pg_dump to SD card
|
|
- **Files**: Synced to cloud storage (Google Drive, Dropbox)
|
|
- **Frequency**: On-demand
|
|
- **Recovery**: Restore from local backups
|
|
|
|
#### Production (Server)
|
|
- **Database**: Automated daily backups with PITR
|
|
- **Files**: Object storage replication (S3 versioning)
|
|
- **Frequency**: Hourly incremental, daily full
|
|
- **Recovery**: Automated restore procedures
|
|
|
|
**Backup Strategy**:
|
|
```bash
|
|
# Development
|
|
pg_dump giglez > /sdcard/giglez_backup_$(date +%Y%m%d).sql
|
|
|
|
# Production
|
|
pg_dump giglez | gzip | aws s3 cp - s3://giglez-backups/$(date +%Y%m%d).sql.gz
|
|
```
|
|
|
|
---
|
|
|
|
### 8. Network Considerations
|
|
|
|
#### Development (Termux)
|
|
- **Connectivity**: WiFi or cellular (intermittent)
|
|
- **Bandwidth**: Potentially limited (mobile data)
|
|
- **Latency**: Variable (mobile networks)
|
|
- **Offline Support**: Critical (wardriving in field)
|
|
|
|
**Implications**:
|
|
- Queue uploads for when network available
|
|
- Support offline capture and analysis
|
|
- Minimize API calls
|
|
- Compress uploads
|
|
|
|
#### Production (Server)
|
|
- **Connectivity**: Always online (datacenter)
|
|
- **Bandwidth**: High (1Gbps+)
|
|
- **Latency**: Low (< 50ms typical)
|
|
- **Offline Support**: Not applicable
|
|
|
|
**Implications**:
|
|
- Assume network availability
|
|
- No offline mode needed
|
|
- Can make external API calls freely
|
|
- Stream large responses
|
|
|
|
**Solution**: Offline queue
|
|
```python
|
|
# src/capture/uploader.py
|
|
class UploadQueue:
|
|
def queue_capture(self, capture_data):
|
|
if network_available():
|
|
upload_immediately(capture_data)
|
|
else:
|
|
save_to_local_queue(capture_data)
|
|
|
|
def process_queue(self):
|
|
"""Called when network becomes available"""
|
|
for item in get_queued_items():
|
|
try:
|
|
upload_immediately(item)
|
|
mark_as_uploaded(item)
|
|
except NetworkError:
|
|
break # Stop and try again later
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Strategy
|
|
|
|
### Environment Variables Approach
|
|
|
|
Use different `.env` files for each environment:
|
|
|
|
```bash
|
|
# .env.development (Termux)
|
|
GIGLEZ_MODE=development
|
|
GIGLEZ_DB_HOST=localhost
|
|
GIGLEZ_DB_POOL_SIZE=5
|
|
GIGLEZ_API_HOST=127.0.0.1
|
|
GIGLEZ_API_PORT=8000
|
|
GIGLEZ_STORAGE_TYPE=filesystem
|
|
GIGLEZ_STORAGE_PATH=/data/data/com.termux/files/home/giglez/storage
|
|
GIGLEZ_REQUIRE_AUTH=false
|
|
GIGLEZ_LOG_LEVEL=DEBUG
|
|
GIGLEZ_ENABLE_HARDWARE=true
|
|
GIGLEZ_TEMBED_PORT=/dev/ttyUSB0
|
|
|
|
# .env.production (Server)
|
|
GIGLEZ_MODE=production
|
|
GIGLEZ_DB_HOST=db.giglez.com
|
|
GIGLEZ_DB_POOL_SIZE=20
|
|
GIGLEZ_API_HOST=0.0.0.0
|
|
GIGLEZ_API_PORT=8000
|
|
GIGLEZ_STORAGE_TYPE=s3
|
|
GIGLEZ_STORAGE_BUCKET=giglez-captures
|
|
GIGLEZ_REQUIRE_AUTH=true
|
|
GIGLEZ_LOG_LEVEL=INFO
|
|
GIGLEZ_ENABLE_HARDWARE=false
|
|
```
|
|
|
|
### Configuration Loading
|
|
|
|
```python
|
|
# config/settings.py
|
|
import os
|
|
from enum import Enum
|
|
|
|
class DeploymentMode(Enum):
|
|
DEVELOPMENT = "development"
|
|
PRODUCTION = "production"
|
|
|
|
class Settings:
|
|
def __init__(self):
|
|
self.mode = DeploymentMode(os.getenv("GIGLEZ_MODE", "development"))
|
|
|
|
@property
|
|
def is_production(self) -> bool:
|
|
return self.mode == DeploymentMode.PRODUCTION
|
|
|
|
@property
|
|
def is_development(self) -> bool:
|
|
return self.mode == DeploymentMode.DEVELOPMENT
|
|
|
|
@property
|
|
def enable_hardware(self) -> bool:
|
|
"""Only enable hardware access in development"""
|
|
return os.getenv("GIGLEZ_ENABLE_HARDWARE", "false").lower() == "true"
|
|
|
|
@property
|
|
def storage_backend(self) -> str:
|
|
return os.getenv("GIGLEZ_STORAGE_TYPE", "filesystem")
|
|
|
|
settings = Settings()
|
|
```
|
|
|
|
---
|
|
|
|
## Recommended Architecture
|
|
|
|
### Modular Component Design
|
|
|
|
```
|
|
giglez/
|
|
├── src/
|
|
│ ├── core/ # Shared core (both modes)
|
|
│ │ ├── database/ # Database models & queries
|
|
│ │ ├── parser/ # .sub file parser
|
|
│ │ ├── matcher/ # Signature matching
|
|
│ │ ├── gps/ # GPS validation
|
|
│ │ └── storage/ # Storage abstraction
|
|
│ │
|
|
│ ├── capture/ # Capture mode only (Termux)
|
|
│ │ ├── tembed.py # T-Embed communication
|
|
│ │ ├── scanner.py # Signal scanning
|
|
│ │ ├── gps_manager.py # Android GPS integration
|
|
│ │ └── uploader.py # Offline queue & upload
|
|
│ │
|
|
│ ├── api/ # API endpoints (both modes)
|
|
│ │ ├── main.py # FastAPI app
|
|
│ │ ├── routes/
|
|
│ │ │ ├── captures.py # Upload endpoints
|
|
│ │ │ ├── devices.py # Device catalog
|
|
│ │ │ ├── query.py # Search endpoints
|
|
│ │ │ └── stats.py # Statistics
|
|
│ │ ├── auth.py # Authentication
|
|
│ │ ├── middleware.py # Rate limiting, CORS
|
|
│ │ └── dependencies.py # Dependency injection
|
|
│ │
|
|
│ └── web/ # Web interface (both modes)
|
|
│ ├── static/ # CSS, JS, images
|
|
│ └── templates/ # HTML templates
|
|
│
|
|
└── config/
|
|
├── settings.py # Environment-based config
|
|
├── database.py # Database connection
|
|
├── storage.py # Storage backend selection
|
|
└── logging.py # Logging configuration
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Recommendations
|
|
|
|
### 1. Use Feature Flags
|
|
|
|
```python
|
|
# src/api/main.py
|
|
from config.settings import settings
|
|
|
|
app = FastAPI()
|
|
|
|
# Conditional hardware endpoints
|
|
if settings.enable_hardware:
|
|
@app.post("/api/capture/start")
|
|
async def start_capture():
|
|
# Only available in Termux mode
|
|
pass
|
|
|
|
# Always available endpoints
|
|
@app.post("/api/captures/upload")
|
|
async def upload_captures():
|
|
# Available in both modes
|
|
pass
|
|
```
|
|
|
|
### 2. Storage Abstraction
|
|
|
|
```python
|
|
# config/storage.py
|
|
from src.core.storage import LocalStorage, S3Storage
|
|
|
|
def get_storage_backend():
|
|
if settings.storage_backend == "s3":
|
|
return S3Storage(
|
|
bucket=os.getenv("GIGLEZ_STORAGE_BUCKET"),
|
|
access_key=os.getenv("AWS_ACCESS_KEY_ID"),
|
|
secret_key=os.getenv("AWS_SECRET_ACCESS_KEY")
|
|
)
|
|
else:
|
|
return LocalStorage(
|
|
base_path=os.getenv("GIGLEZ_STORAGE_PATH")
|
|
)
|
|
|
|
storage = get_storage_backend()
|
|
```
|
|
|
|
### 3. Optional Dependencies
|
|
|
|
```python
|
|
# requirements.txt (core)
|
|
fastapi==0.109.0
|
|
sqlalchemy==2.0.25
|
|
psycopg2-binary==2.9.9
|
|
...
|
|
|
|
# requirements-dev.txt (Termux additions)
|
|
-r requirements.txt
|
|
pyserial==3.5
|
|
aioserial==1.3.2
|
|
|
|
# requirements-prod.txt (Server additions)
|
|
-r requirements.txt
|
|
boto3==1.34.0 # S3 storage
|
|
redis==5.0.1 # Caching
|
|
celery==5.3.4 # Background tasks
|
|
gunicorn==21.2.0 # WSGI server
|
|
```
|
|
|
|
### 4. Graceful Degradation
|
|
|
|
```python
|
|
# src/api/routes/captures.py
|
|
@app.post("/api/captures/upload")
|
|
async def upload_captures(files: List[UploadFile]):
|
|
# Core functionality works in both modes
|
|
captures = []
|
|
|
|
for file in files:
|
|
# Parse .sub file (works everywhere)
|
|
metadata = parse_sub_file(file)
|
|
|
|
# Store file (abstracted)
|
|
storage.save_file(metadata.file_hash, file.content)
|
|
|
|
# Save to database (works everywhere)
|
|
capture = Capture(**metadata)
|
|
db.add(capture)
|
|
|
|
captures.append(capture)
|
|
|
|
db.commit()
|
|
|
|
# Background matching (production) or synchronous (development)
|
|
if settings.is_production:
|
|
celery_app.send_task('match_signatures', args=[capture.file_hash])
|
|
else:
|
|
match_signatures_sync(capture.file_hash)
|
|
|
|
return {"uploaded": len(captures)}
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
### Development Testing (Termux)
|
|
- Direct hardware testing
|
|
- USB serial communication
|
|
- GPS acquisition
|
|
- Offline queue processing
|
|
- Local file storage
|
|
|
|
### Production Testing (Server)
|
|
- API endpoint testing
|
|
- File upload validation
|
|
- Multi-user concurrency
|
|
- Rate limiting
|
|
- Authentication
|
|
- S3 storage integration
|
|
|
|
### Shared Testing
|
|
- Database operations
|
|
- .sub file parsing
|
|
- Signature matching
|
|
- GPS validation
|
|
- Query performance
|
|
|
|
**Test Structure**:
|
|
```
|
|
tests/
|
|
├── unit/ # Unit tests (both modes)
|
|
│ ├── test_parser.py
|
|
│ ├── test_matcher.py
|
|
│ └── test_gps.py
|
|
│
|
|
├── integration/ # Integration tests
|
|
│ ├── test_api.py # Both modes
|
|
│ ├── test_hardware.py # Development only
|
|
│ └── test_storage.py # Both modes
|
|
│
|
|
└── e2e/ # End-to-end tests
|
|
├── test_capture.py # Development only
|
|
└── test_upload.py # Both modes
|
|
```
|
|
|
|
---
|
|
|
|
## Deployment Checklist
|
|
|
|
### Development (Termux) Setup
|
|
- [ ] Install Termux from F-Droid
|
|
- [ ] Install PostgreSQL: `pkg install postgresql`
|
|
- [ ] Install Python: `pkg install python`
|
|
- [ ] Install dependencies: `pip install -r requirements-dev.txt`
|
|
- [ ] Configure USB permissions for T-Embed
|
|
- [ ] Set up Termux:API for GPS
|
|
- [ ] Create `.env.development`
|
|
- [ ] Run database setup: `./scripts/setup_database.sh`
|
|
- [ ] Test hardware connection
|
|
- [ ] Start API: `python src/api/main.py`
|
|
|
|
### Production (Server) Setup
|
|
- [ ] Provision VPS (2GB+ RAM, 2+ cores)
|
|
- [ ] Install PostgreSQL 16 + PostGIS
|
|
- [ ] Install Python 3.10+
|
|
- [ ] Install Redis (for caching)
|
|
- [ ] Install Nginx (reverse proxy)
|
|
- [ ] Configure SSL with Let's Encrypt
|
|
- [ ] Set up S3/MinIO for file storage
|
|
- [ ] Configure firewall (allow 80, 443)
|
|
- [ ] Create `.env.production`
|
|
- [ ] Run database setup
|
|
- [ ] Set up systemd service
|
|
- [ ] Configure automated backups
|
|
- [ ] Set up monitoring (optional)
|
|
|
|
---
|
|
|
|
## Performance Considerations
|
|
|
|
### Development (Termux) Optimizations
|
|
- Smaller connection pool (5 + 5)
|
|
- In-memory caching only
|
|
- Synchronous processing (no Celery)
|
|
- Minimal logging
|
|
- Local file storage
|
|
|
|
### Production (Server) Optimizations
|
|
- Larger connection pool (20 + 40)
|
|
- Redis caching
|
|
- Async background tasks (Celery)
|
|
- Structured logging with rotation
|
|
- CDN for file delivery
|
|
- Read replicas for scaling
|
|
- Materialized view refresh (cron)
|
|
|
|
---
|
|
|
|
## Cost Considerations
|
|
|
|
### Development (Termux)
|
|
- **Hardware**: LilyGo T-Embed (~$30-50)
|
|
- **Android Device**: Existing phone/tablet
|
|
- **Infrastructure**: $0 (local)
|
|
- **Storage**: Device storage only
|
|
- **Total**: ~$30-50 one-time
|
|
|
|
### Production (Server)
|
|
- **VPS**: $10-50/month (2-8GB RAM)
|
|
- **Database**: Included or $10-30/month (managed)
|
|
- **Storage**: $0.02/GB/month (S3) or included (filesystem)
|
|
- **Bandwidth**: Typically included (1-5TB)
|
|
- **Domain**: $10-15/year
|
|
- **SSL**: $0 (Let's Encrypt)
|
|
- **Total**: $10-80/month
|
|
|
|
---
|
|
|
|
## Recommended Phase 2 Approach
|
|
|
|
### Build for Both from the Start
|
|
|
|
**Strategy**: Implement the API with abstraction layers that work in both environments.
|
|
|
|
**Implementation Order**:
|
|
1. ✅ Core API structure (FastAPI app)
|
|
2. ✅ Storage abstraction (filesystem + S3)
|
|
3. ✅ Upload endpoint (works in both modes)
|
|
4. ✅ Authentication (optional in dev, required in prod)
|
|
5. ✅ Background tasks (sync in dev, Celery in prod)
|
|
6. ✅ Environment-based configuration
|
|
|
|
**Key Principle**:
|
|
> Write code that works in both environments by default, with conditional imports/features for mode-specific functionality.
|
|
|
|
---
|
|
|
|
## Summary: Key Differences
|
|
|
|
| Aspect | Development (Termux) | Production (Server) |
|
|
|--------|---------------------|---------------------|
|
|
| **Hardware** | T-Embed USB + GPS | None (file uploads) |
|
|
| **Users** | Single user | Multi-user |
|
|
| **Database** | Local PostgreSQL (small pool) | Remote PostgreSQL (large pool) |
|
|
| **Storage** | Local filesystem | S3/object storage |
|
|
| **API Access** | localhost only | Public internet |
|
|
| **Security** | Optional auth | Required auth + HTTPS |
|
|
| **Background Tasks** | Synchronous | Celery async |
|
|
| **Caching** | In-memory LRU | Redis + LRU |
|
|
| **Logging** | Console (DEBUG) | File + JSON (INFO) |
|
|
| **Monitoring** | Manual | Automated (Prometheus) |
|
|
| **Scaling** | N/A | Horizontal |
|
|
| **Cost** | $0/month | $10-80/month |
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
Before implementing Phase 2, we need to:
|
|
|
|
1. ✅ **Create environment configuration** (`.env.development`, `.env.production`)
|
|
2. ✅ **Implement storage abstraction** (`src/core/storage/`)
|
|
3. ✅ **Create settings module** (`config/settings.py`)
|
|
4. ✅ **Document deployment procedures** (development guide, production guide)
|
|
5. ✅ **Begin FastAPI implementation** with environment awareness
|
|
|
|
**Recommendation**: Start with a **shared API implementation** that works in both modes, then add mode-specific features as needed.
|
|
|
|
---
|
|
|
|
**Document Version**: 1.0
|
|
**Status**: Ready for Phase 2 Implementation
|
|
**Next Action**: Create environment-aware FastAPI application structure
|