Files
CA/backend/auth/service.py
Akiba So fc468464b2 feat: Initial CBPOA commit — 武汉儿童呼吸疾病风险评估系统
Context: Build a spatial risk assessment system correlating air quality
data with children's respiratory disease incidence across Wuhan.

Approach: FastAPI backend serving PostGIS spatial queries, React
frontend with Deck.gl maps, and a PyTorch SpatialTemporalGCN pipeline
for multi-day (1d/3d/7d) risk prediction.

Changes:
- backend/ — FastAPI API with auth (JWT), alerts, risk analysis,
  geocoded case data, grid statistics, and report endpoints
- frontend/ — React dashboard with interactive risk maps, alert
  monitoring, district comparison charts, and timeline player
- models/ — SpatialTemporalGCN model with trained weights and ONNX
  export for inference
- scripts/ — ETL pipeline for weather + medical data, grid generation,
  feature engineering, training, and daily inference
- deploy/ — Docker Compose configs for backend, frontend, and MLflow
- docs/ — API docs, deployment guide, user guide, and code review

Impact: Enables spatial risk visualization, alert monitoring, and
ML-driven health risk forecasting for environmental health teams.
2026-06-05 02:13:49 +08:00

67 lines
2.0 KiB
Python

"""JWT token creation and password hashing utilities."""
import os
import logging
from datetime import datetime, timedelta, timezone
from jose import JWTError, jwt
from passlib.context import CryptContext
logger = logging.getLogger("cbpoa.auth")
SECRET_KEY = os.getenv("AUTH_SECRET_KEY", "cbpoa-dev-secret-change-in-production")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("AUTH_TOKEN_EXPIRE_MINUTES", "480"))
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# In-memory user store (replace with DB table when auth matures)
_users: dict[str, str] = {}
def seed_default_admin() -> None:
"""Create default admin user if no users exist."""
if not _users:
default_user = os.getenv("AUTH_DEFAULT_USER", "admin")
default_pass = os.getenv("AUTH_DEFAULT_PASSWORD", "admin123")
_users[default_user] = pwd_context.hash(default_pass)
logger.info("Seeded default user '%s'", default_user)
def verify_password(plain: str, hashed: str) -> bool:
return pwd_context.verify(plain, hashed)
def hash_password(password: str) -> str:
return pwd_context.hash(password)
def authenticate_user(username: str, password: str) -> bool:
hashed = _users.get(username)
if not hashed:
return False
return verify_password(password, hashed)
def create_user(username: str, password: str) -> bool:
"""Register a new user. Returns False if username already exists."""
if username in _users:
return False
_users[username] = hash_password(password)
logger.info("Registered new user '%s'", username)
return True
def create_access_token(data: dict) -> str:
to_encode = data.copy()
expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
def decode_access_token(token: str) -> dict | None:
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError:
return None