28 KiB
TJWater ServerBinary - Secondary Development Documentation Guide
Executive Summary
TJWaterServerBinary is a FastAPI-based water distribution network management system with integrated EPANET simulation, SCADA data management, and comprehensive security architecture. This guide identifies key components and patterns essential for secondary development.
1. Project Structure & Key Directories
Overall Architecture
TJWaterServerBinary/
├── app/
│ ├── main.py # FastAPI application entry + lifecycle management
│ ├── api/v1/ # API routes and endpoints
│ ├── algorithms/ # Core algorithms (simulation, leakage detection, etc.)
│ ├── auth/ # Authentication logic and dependencies
│ ├── core/ # Core configuration, security, encryption
│ ├── domain/ # Domain models and Pydantic schemas
│ ├── infra/ # Infrastructure layer (DB, cache, audit)
│ ├── services/ # Business logic services
│ ├── native/ # Native module integration (binary/compiled code)
│ └── utils/ # Utility functions
├── infra/docker/ # Docker and deployment configs
├── resources/sql/ # SQL initialization scripts
├── tests/ # Unit and integration tests
└── requirements.txt # Python dependencies
Key Directory Details
| Directory | Purpose | Key Files |
|---|---|---|
| api/v1 | REST API endpoints organized by domain | router.py (centralized registration), endpoints/ (individual controllers) |
| domain | Data models and schemas | models/role.py, schemas/user.py, schemas/audit.py |
| infra | Database access, caching, audit | db/postgresql/, db/timescaledb/, cache/, audit/ |
| auth | JWT/OAuth2 validation, permissions | dependencies.py, permissions.py, keycloak_dependencies.py |
| core | Security, encryption, config | config.py, security.py, encryption.py, audit.py |
| native | Python-to-binary integration | api/ module wrapping compiled code |
| services | Business logic & orchestration | tjnetwork.py (project management), simulation.py, etc. |
2. API Endpoint Addition (Router Registration)
How to Add New API Endpoints
Step 1: Create Endpoint File
Create a new file in app/api/v1/endpoints/ with your endpoint handlers:
# app/api/v1/endpoints/my_feature.py
from fastapi import APIRouter, Depends, HTTPException, status
from app.auth.dependencies import get_current_active_user
from app.domain.schemas.user import UserInDB
from app.infra.repositories.my_repository import MyRepository
from app.auth.dependencies import get_user_repository
router = APIRouter()
@router.get("/my-feature/")
async def get_my_feature(user: UserInDB = Depends(get_current_active_user)):
"""Get feature data"""
return {"feature": "data"}
@router.post("/my-feature/")
async def create_my_feature(
data: dict,
user: UserInDB = Depends(get_current_active_user),
):
"""Create new feature"""
return {"status": "created"}
Step 2: Register Router in Central Router
Edit app/api/v1/router.py to include your new router:
# app/api/v1/router.py
from app.api.v1.endpoints import my_feature # Import your endpoint module
api_router = APIRouter()
# Add your router
api_router.include_router(
my_feature.router,
prefix="/my-feature", # Optional: URL prefix
tags=["My Feature"] # For OpenAPI documentation
)
Step 3: Authentication & Authorization
- No Auth Required: Endpoint is public
- Authenticated Users: Use
Depends(get_current_active_user) - Role-Based Access: Use
Depends(require_admin)orDepends(require_role(UserRole.OPERATOR))
from app.auth.permissions import require_admin, require_role
from app.domain.models.role import UserRole
@router.post("/admin-only/")
async def admin_only(user: UserInDB = Depends(require_admin)):
return {"admin": user.username}
Step 4: Response Models
Use Pydantic schemas for validation:
from pydantic import BaseModel
from typing import Optional
class MyFeatureResponse(BaseModel):
id: int
name: str
status: Optional[str] = None
@router.get("/my-feature/{id}", response_model=MyFeatureResponse)
async def get_feature(id: int):
return MyFeatureResponse(id=id, name="Feature", status="active")
Router Registration Pattern
The central router (app/api/v1/router.py) uses include_router() to compose all endpoints:
# Current structure (98 lines):
api_router.include_router(auth.router, prefix="/auth", tags=["Auth"])
api_router.include_router(project.router, tags=["Project"])
api_router.include_router(simulation.router, tags=["Simulation Control"])
# ... 30+ more routers
3. Database Interaction Patterns
ORM & Database Strategy
TJWater uses psycopg (PostgreSQL async driver) directly, NOT SQLAlchemy ORM
Multiple Database Types
- PostgreSQL: Main relational data (users, metadata)
- TimescaleDB: Time-series data (SCADA, sensor readings)
- InfluxDB: Alternative time-series storage (optional)
- Metadata DB: Separate instance (
system_hubdatabase)
Database Layer Architecture
1. Database Instances (app/infra/db/postgresql/database.py)
class Database:
"""Manages async connection pooling"""
def __init__(self, db_name=None):
self.pool = None
self.db_name = db_name
def init_pool(self, db_name=None):
"""Initialize connection pool"""
self.pool = psycopg_pool.AsyncConnectionPool(...)
async def get_connection(self) -> AsyncGenerator:
"""Yield connection from pool"""
async with self.pool.connection() as conn:
yield conn
# Global instances
db = Database() # Default PostgreSQL
_database_instances = {} # Per-project caches
2. Repository Pattern (app/infra/repositories/)
class UserRepository:
"""Data access layer for users"""
def __init__(self, db: Database):
self.db = db
async def create_user(self, user: UserCreate) -> Optional[UserInDB]:
query = """
INSERT INTO users (username, email, hashed_password, role)
VALUES (%(username)s, %(email)s, %(hashed_password)s, %(role)s)
RETURNING id, username, email, role, created_at
"""
async with self.db.get_connection() as conn:
async with conn.cursor() as cur:
await cur.execute(query, {
'username': user.username,
'email': user.email,
'hashed_password': get_password_hash(user.password),
'role': user.role.value
})
row = await cur.fetchone()
return UserInDB(**row) if row else None
3. Query Execution Pattern
# Reading data
async with db.get_connection() as conn:
async with conn.cursor() as cur:
await cur.execute("SELECT * FROM users WHERE id = %s", (user_id,))
row = await cur.fetchone() # Returns dict_row
# Writing data
async with db.get_connection() as conn:
async with conn.cursor() as cur:
await cur.execute(
"UPDATE users SET email = %s WHERE id = %s",
(new_email, user_id)
)
# conn auto-commits (if autocommit=True in pool config)
Database Configuration
Via app/core/config.py and .env:
# Main PostgreSQL (users, metadata)
DB_HOST = "localhost"
DB_PORT = "5432"
DB_NAME = "tjwater"
DB_USER = "postgres"
DB_PASSWORD = "password"
# TimescaleDB (time-series)
TIMESCALEDB_DB_HOST = "localhost"
TIMESCALEDB_DB_PORT = "5433"
TIMESCALEDB_DB_NAME = "szh"
# Metadata DB (system info)
METADATA_DB_NAME = "system_hub"
Dynamic Multi-Database Support
Project-specific databases are managed dynamically:
# app/infra/db/dynamic_manager.py
async def get_database_instance(db_name: Optional[str] = None) -> Database:
"""Get or create DB instance for project"""
if not db_name:
return db # Default instance
if db_name not in _database_instances:
instance = Database(db_name=db_name)
instance.init_pool()
await instance.open()
_database_instances[db_name] = instance
return _database_instances[db_name]
Migrations & Schema Creation
- Location:
resources/sql/contains initialization scripts - Naming: Numbered order (e.g.,
002_create_audit_logs_table.sql) - Approach: SQL scripts are manually executed; no Alembic/Flyway framework
- Example:
resources/sql/002_create_audit_logs_table.sql
4. Native Module Integration
Architecture: Python ↔ Binary Code
TJWater integrates compiled C/Go binaries for water network simulation:
Python (FastAPI)
↓
app/native/wndb/
├── project.py (list, create, delete, open projects)
├── s2_junctions.py (CRUD for junctions)
├── s5_pipes.py (CRUD for pipes)
└── [40+ schema modules] (network elements)
↓
app/native/api_encap/ (Low-level binary wrappers)
└── api.so / api.dll (Compiled binary library)
How Python Calls Native Code
1. Native API Initialization
# app/native/wndb/__init__.py
from app.native.api_encap.api import (
ChangeSet, # Change tracking
API_ADD, API_UPDATE, API_DELETE, # Operations
list_project, create_project, open_project, close_project,
# ... 100+ functions
)
2. Example: Project Management
# app/services/tjnetwork.py
from app.native.wndb import list_project, open_project, close_project
def list_project() -> list[str]:
"""List all project databases"""
# Calls into compiled binary to enumerate DB names
return api.list_project()
def open_project(name: str) -> None:
"""Open project and load into memory"""
# Binary loads .inp file or DB data
api.open_project(name)
3. ChangeSet Tracking
# For tracking network modifications
class ChangeSet:
"""Tracks ADD/UPDATE/DELETE operations on network elements"""
def __init__(self):
self.adds = []
self.updates = []
self.deletes = []
# Usage in services
def update_junction(name: str, demand: float):
change = api.set_junction(name, {"demand": demand})
# Returns ChangeSet for auditing/undo-redo
4. Network CRUD Operations
# All operations follow this pattern:
from app.native.wndb import get_all_junctions, add_junction, set_junction
# Read
junctions = get_all_junctions() # Returns list of dicts
# Create
add_junction({"id": "J1", "x": 0.0, "y": 0.0, "demand": 100.0})
# Update
set_junction("J1", {"demand": 150.0})
# Delete
delete_junction("J1")
Data Flow Example: Simulation
FastAPI Request (POST /simulation/run)
↓
app/api/v1/endpoints/simulation.py
↓
app/services/simulation.py (business logic)
↓
app/native/wndb/ (calls binary functions)
├── open_project("szh")
├── set_option("QUALITY", "CHEMICAL")
├── run_simulation()
└── get_result()
↓
TimescaleDB/InfluxDB (store results)
↓
Response to client
Python Module Constants
Native module exports constants for enumerations:
from app.native.wndb import (
OPTION_UNITS_LPS, # Flow units
OPTION_PRESSURE_KPA, # Pressure units
OPTION_QUALITY_CHEMICAL, # Quality type
PIPE_STATUS_OPEN, # Pipe status
SCADA_DEVICE_TYPE_PRESSURE,
)
5. Authentication & Authorization Mechanisms
Architecture: JWT + OAuth2 + Role-Based Access Control (RBAC)
1. Authentication Flow
User Registration
# app/api/v1/endpoints/auth.py
@router.post("/register", response_model=UserResponse)
async def register(user_data: UserCreate):
# 1. Hash password
hashed = get_password_hash(user_data.password)
# 2. Store in DB via repository
user = await user_repo.create_user({
'username': user_data.username,
'email': user_data.email,
'hashed_password': hashed,
'role': 'USER'
})
return UserResponse.model_validate(user)
User Login & Token Generation
@router.post("/login", response_model=Token)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# 1. Verify credentials
user = await user_repo.get_user_by_username(form_data.username)
if not verify_password(form_data.password, user.hashed_password):
raise HTTPException(status_code=401, detail="Invalid credentials")
# 2. Generate JWT tokens
access_token = create_access_token(subject=user.username)
refresh_token = create_refresh_token(subject=user.username)
return Token(
access_token=access_token,
refresh_token=refresh_token,
token_type="bearer",
expires_in=settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60
)
2. JWT Token Configuration
# app/core/config.py
SECRET_KEY: str = "your-secret-key" # Change in production
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
3. Token Validation & Current User
# app/auth/dependencies.py
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
async def get_current_user(token: str = Depends(oauth2_scheme)) -> UserInDB:
"""Validate JWT token and extract user"""
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username: str = payload.get("sub")
token_type: str = payload.get("type", "access")
if username is None or token_type != "access":
raise HTTPException(status_code=401, detail="Invalid token")
# Fetch user from DB
user = await user_repo.get_user_by_username(username)
if user is None:
raise HTTPException(status_code=401, detail="User not found")
return user
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
4. Role-Based Access Control (RBAC)
User Roles Enum
# app/domain/models/role.py
class UserRole(str, Enum):
ADMIN = "ADMIN" # Full permissions
OPERATOR = "OPERATOR" # Modify data
USER = "USER" # Read/write
VIEWER = "VIEWER" # Read-only
# Hierarchy
get_hierarchy() = {
VIEWER: 1,
USER: 2,
OPERATOR: 3,
ADMIN: 4,
}
Permission Decorators
# app/auth/permissions.py
def require_admin(current_user: UserInDB = Depends(get_current_active_user)) -> UserInDB:
"""Restrict to admin users"""
if current_user.role != UserRole.ADMIN:
raise HTTPException(status_code=403, detail="Admin access required")
return current_user
def require_role(role: UserRole):
"""Factory for role-based permission checks"""
async def permission_checker(
current_user: UserInDB = Depends(get_current_active_user)
) -> UserInDB:
if not UserRole(current_user.role).has_permission(role):
raise HTTPException(status_code=403, detail=f"Role {role} required")
return current_user
return permission_checker
def require_operator(current_user: UserInDB = Depends(get_current_active_user)) -> UserInDB:
return current_user if require_role(UserRole.OPERATOR) else None
Using Permissions in Endpoints
# Require admin
@router.delete("/users/{user_id}")
async def delete_user(
user_id: int,
admin: UserInDB = Depends(require_admin)
):
await user_repo.delete_user(user_id)
return {"status": "deleted"}
# Require operator or higher
@router.post("/projects/")
async def create_project(
data: dict,
operator: UserInDB = Depends(require_operator)
):
return {"project": "created"}
5. Optional Keycloak Integration
# app/auth/keycloak_dependencies.py
# Alternative: Validate tokens from external Keycloak server
KEYCLOAK_PUBLIC_KEY: str = "" # From .env
KEYCLOAK_ALGORITHM: str = "RS256"
# Similar JWT validation but with Keycloak's public key
6. Token Schema
# app/domain/schemas/user.py
class Token(BaseModel):
access_token: str
refresh_token: Optional[str] = None
token_type: str = "bearer"
expires_in: int # Seconds
class UserInDB(BaseModel):
id: int
username: str
email: str
role: str # "ADMIN", "USER", etc.
is_active: bool
is_superuser: bool
6. Configuration Management
Configuration Hierarchy
Environment Variables (.env file)
↓
app/core/config.py (Pydantic Settings)
↓
Injected via Depends() in endpoints/services
↓
Runtime usage
Config File: app/core/config.py
Uses Pydantic v2 BaseSettings with .env support:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
# App
PROJECT_NAME: str = "TJWater Server"
API_V1_STR: str = "/api/v1"
# Security
SECRET_KEY: str = "your-secret"
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
# Encryption (Fernet symmetric)
ENCRYPTION_KEY: str = "" # Required from .env
DATABASE_ENCRYPTION_KEY: str = ""
# PostgreSQL
DB_NAME: str = "tjwater"
DB_HOST: str = "localhost"
DB_PORT: str = "5432"
DB_USER: str = "postgres"
DB_PASSWORD: str = "password"
# TimescaleDB
TIMESCALEDB_DB_NAME: str = "tjwater"
TIMESCALEDB_DB_HOST: str = "localhost"
TIMESCALEDB_DB_PORT: str = "5433"
# Metadata DB
METADATA_DB_NAME: str = "system_hub"
METADATA_DB_HOST: str = "localhost"
METADATA_DB_PORT: str = "5432"
# Cache sizing
PROJECT_PG_CACHE_SIZE: int = 50
PROJECT_TS_CACHE_SIZE: int = 50
# Connection pooling
PROJECT_PG_POOL_SIZE: int = 5
PROJECT_TS_POOL_MIN_SIZE: int = 1
PROJECT_TS_POOL_MAX_SIZE: int = 10
# InfluxDB (optional)
INFLUXDB_URL: str = "http://localhost:8086"
INFLUXDB_TOKEN: str = "token"
@property
def SQLALCHEMY_DATABASE_URI(self) -> str:
return f"postgresql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
model_config = SettingsConfigDict(
env_file=".env",
extra="ignore",
)
settings = Settings()
Environment File: .env.example
# Security
SECRET_KEY=your-secret-key-change-in-production
ENCRYPTION_KEY=<generated via Fernet.generate_key()>
DATABASE_ENCRYPTION_KEY=
# PostgreSQL
DB_HOST=localhost
DB_PORT=5432
DB_NAME=tjwater
DB_USER=postgres
DB_PASSWORD=password
# TimescaleDB
TIMESCALEDB_DB_NAME=szh
TIMESCALEDB_DB_HOST=localhost
TIMESCALEDB_DB_PORT=5433
TIMESCALEDB_DB_USER=tjwater
TIMESCALEDB_DB_PASSWORD=Tjwater@123456
# Metadata DB
METADATA_DB_NAME=system_hub
METADATA_DB_HOST=localhost
# Cache/Pool
PROJECT_PG_CACHE_SIZE=50
PROJECT_TS_CACHE_SIZE=50
Using Configuration in Code
from app.core.config import settings
# Direct access
db_url = settings.SQLALCHEMY_DATABASE_URI
secret = settings.SECRET_KEY
# In FastAPI lifespan
@asynccontextmanager
async def lifespan(app: FastAPI):
# Initialize based on settings
db.init_pool(settings.DB_NAME)
yield
await db.close()
Encryption Configuration
# app/core/encryption.py
from app.core.config import settings
class Encryptor:
def __init__(self, key: Optional[bytes] = None):
if key is None:
key_str = settings.ENCRYPTION_KEY
if not key_str:
raise ValueError("ENCRYPTION_KEY not configured")
key = key_str.encode()
self.fernet = Fernet(key)
def encrypt(self, data: str) -> str:
return self.fernet.encrypt(data.encode()).decode()
def decrypt(self, data: str) -> str:
return self.fernet.decrypt(data.encode()).decode()
@staticmethod
def generate_key() -> str:
"""Generate new encryption key"""
return Fernet.generate_key().decode()
7. Testing Setup
Test Structure
tests/
├── conftest.py # Pytest configuration & fixtures
├── api/ # API integration tests
│ ├── test_api_integration.py
│ └── test_leakage_endpoints.py
├── unit/ # Unit tests
│ ├── test_burst_location_service.py
│ ├── test_metadata_repository_dsn_decrypt.py
│ └── test_pipeline_health_analyzer.py
└── auth/ # Auth tests
└── test_encryption.py
Configuration: tests/conftest.py
import pytest
import sys
import os
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
def run_this_test(test_file):
"""Run a single test file"""
test_name = os.path.splitext(os.path.basename(test_file))[0]
pytest.main([test_file, "-v"])
Testing Tools
# requirements.txt includes:
pytest==8.3.5 # Test runner
# httpx (via FastAPI) for async HTTP client
Example: API Integration Test
# tests/api/test_api_integration.py
import pytest
@pytest.mark.parametrize(
"module_name, desc",
[
("app.core.encryption", "Encryption"),
("app.auth.permissions", "Permissions"),
("app.api.v1.endpoints.auth", "Auth endpoints"),
],
)
def test_module_imports(module_name, desc):
"""Verify critical modules can be imported"""
try:
__import__(module_name)
except ImportError as e:
pytest.fail(f"Cannot import {desc}: {e}")
def test_router_configuration():
"""Verify router is properly configured"""
from app.api.v1 import router
api_router = router.api_router
routes = [r.path for r in api_router.routes if hasattr(r, "path")]
assert len(routes) > 0
Example: Encryption Test
# tests/auth/test_encryption.py
from app.core.encryption import Encryptor
def test_encrypt_decrypt():
"""Test encryption roundtrip"""
encryptor = Encryptor.generate_key()
enc = Encryptor(encryptor)
plaintext = "sensitive_data"
encrypted = enc.encrypt(plaintext)
decrypted = enc.decrypt(encrypted)
assert decrypted == plaintext
Running Tests
# Run all tests
pytest tests/ -v
# Run specific test file
pytest tests/api/test_api_integration.py -v
# Run specific test
pytest tests/auth/test_encryption.py::test_encrypt_decrypt -v
8. Audit & Security Features
Audit Middleware
Automatically logs all critical operations:
# app/infra/audit/middleware.py
class AuditMiddleware(BaseHTTPMiddleware):
AUDIT_METHODS = ["POST", "PUT", "DELETE", "PATCH"]
AUDIT_TAGS = ["Audit", "Users", "Project", "Junctions", "Pipes", ...]
async def dispatch(self, request: Request, call_next):
# 1. Capture request body (for writes)
# 2. Execute request
# 3. Log to audit DB if matches criteria
# 4. Return response
Audit Logging
# app/core/audit.py
async def log_audit_event(
action: AuditAction, # "CREATE", "UPDATE", "DELETE", "LOGIN"
resource_type: str, # "user", "project", "pipe"
resource_id: str,
actor_id: int, # User ID
details: dict = None
):
"""Log operation to audit table"""
# Persists to audit_logs table via AuditRepository
Audit Schema
# app/domain/schemas/audit.py
class AuditLog(BaseModel):
id: int
timestamp: datetime
action: str
resource_type: str
resource_id: str
actor_id: int
details: Optional[dict] = None
9. Key Dependencies & Tech Stack
Core Framework
FastAPI==0.128.0 # Web framework
uvicorn==0.34.0 # ASGI server
Pydantic==2.10.6 # Data validation
pydantic-settings==2.12.0 # Configuration management
Database
psycopg==3.2.5 # PostgreSQL async driver
psycopg-pool==3.3.0 # Connection pooling
GeoAlchemy2==0.17.1 # GIS support
SQLAlchemy==2.0.41 # ORM (optional)
Security & Auth
PyJWT==2.10.1 # JWT handling
python-jose==3.5.0 # Token validation
passlib==1.7.4 # Password hashing
cryptography==46.0.3 # Encryption (Fernet)
Authlib==1.6.6 # OAuth2 support
Data Science / Simulation
wntr==1.3.2 # Water network toolkit (EPANET)
numpy==1.26.2 # Numerical computing
pandas==2.2.3 # Data frames
scipy==1.15.2 # Scientific computing
scikit-learn==1.6.1 # ML algorithms
Time-Series & Data Storage
influxdb-client==1.48.0 # InfluxDB client
redis==5.2.1 # Caching & sessions
Utilities
python-dotenv==1.2.1 # .env file support
python-multipart==0.0.20 # File upload handling
email-validator==2.3.0 # Email validation
10. Documentation & Deployment
Key Documentation Files
- SECURITY_README.md: Complete security implementation guide
- DEPLOYMENT.md: Production deployment checklist
- INTEGRATION_CHECKLIST.md: System integration steps
- setup_server.md: Initial server setup
- setup_influxdb.md: InfluxDB configuration
Docker Deployment
# infra/docker/docker-compose.yml
services:
api:
build: ...
ports: ["8000:8000"]
environment:
- DB_HOST=postgis
- TIMESCALEDB_HOST=timescaledb
- REDIS_HOST=redis
postgis:
image: postgis/postgis:14-3.5
timescaledb:
image: timescale/timescaledb:latest-pg15
redis:
image: redis:latest
influxdb:
image: influxdb:2.7
keycloak:
image: keycloak/keycloak:latest
grafana:
image: grafana/grafana:latest
11. Recommended Documentation to Create
For Secondary Development Team:
-
API Development Guide
- How to add new endpoints (covered in Section 2)
- Common endpoint patterns
- Error handling standards
- Response format conventions
-
Database Development Guide
- Repository pattern usage
- Query building best practices
- Migration procedures
- Multi-database considerations
-
Native Module Integration Guide
- How to add new native functions
- ChangeSet tracking
- Binary library updates
- Debugging native code calls
-
Authentication & Authorization
- Token generation and validation
- Role-based access control implementation
- Permission decorator usage
- Keycloak integration
-
Testing & CI/CD
- Unit test patterns
- Integration test setup
- Test database configuration
- GitHub Actions workflow
-
Deployment & DevOps
- Docker build & deployment
- Kubernetes manifests
- Environment variable management
- Production security checklist
-
Troubleshooting Guide
- Common issues and solutions
- Database connection problems
- Authentication debugging
- Native module errors
Summary Table: Key Components
| Component | Location | Purpose | Key Technology |
|---|---|---|---|
| API Routes | app/api/v1/endpoints/ |
REST endpoints | FastAPI, Pydantic |
| Router | app/api/v1/router.py |
Central route registration | APIRouter |
| Auth | app/auth/ |
JWT/OAuth2 validation | PyJWT, passlib |
| Database | app/infra/db/ |
Data persistence | psycopg, async pools |
| Repository | app/infra/repositories/ |
Data access layer | psycopg cursor |
| Domain | app/domain/ |
Models & schemas | Pydantic, Enum |
| Services | app/services/ |
Business logic | Python, native APIs |
| Native API | app/native/wndb/ |
Binary integration | ctypes/cffi, .so/.dll |
| Config | app/core/config.py |
Settings management | Pydantic Settings |
| Security | app/core/security.py |
Encryption, hashing | cryptography, passlib |
| Audit | app/infra/audit/ |
Operation logging | Middleware, Repository |
| Tests | tests/ |
Test suite | pytest |
This guide provides the essential patterns and structures needed for secondary development on TJWaterServerBinary. Refer to individual source files for detailed implementation examples.