# 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: ```python # 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: ```python # 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)` or `Depends(require_role(UserRole.OPERATOR))` ```python 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: ```python 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: ```python # 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_hub` database) ### Database Layer Architecture #### 1. Database Instances (`app/infra/db/postgresql/database.py`) ```python 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/`) ```python 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 ```python # 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`: ```python # 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: ```python # 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 ```python # 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 ```python # 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 ```python # 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 ```python # 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: ```python 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 ```python # 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 ```python @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 ```python # 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 ```python # 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 ```python # 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 ```python # 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 ```python # 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 ```python # 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 ```python # 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: ```python 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` ```bash # Security SECRET_KEY=your-secret-key-change-in-production ENCRYPTION_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 ```python 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 ```python # 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` ```python 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 ```python # requirements.txt includes: pytest==8.3.5 # Test runner # httpx (via FastAPI) for async HTTP client ``` ### Example: API Integration Test ```python # 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 ```python # 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 ```bash # 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: ```python # 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 ```python # 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 ```python # 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 ```yaml # 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: 1. **API Development Guide** - How to add new endpoints (covered in Section 2) - Common endpoint patterns - Error handling standards - Response format conventions 2. **Database Development Guide** - Repository pattern usage - Query building best practices - Migration procedures - Multi-database considerations 3. **Native Module Integration Guide** - How to add new native functions - ChangeSet tracking - Binary library updates - Debugging native code calls 4. **Authentication & Authorization** - Token generation and validation - Role-based access control implementation - Permission decorator usage - Keycloak integration 5. **Testing & CI/CD** - Unit test patterns - Integration test setup - Test database configuration - GitHub Actions workflow 6. **Deployment & DevOps** - Docker build & deployment - Kubernetes manifests - Environment variable management - Production security checklist 7. **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.*