added fastapi endpoint routers
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
"""Routers package for API endpoints."""
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
"""Authentication router for login and user management."""
|
||||||
|
|
||||||
|
import secrets
|
||||||
|
from fastapi import APIRouter, HTTPException, Depends, Header
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
|
from baby_monitor.models.auth import LoginRequest, LoginResponse
|
||||||
|
from baby_monitor.repositories import (
|
||||||
|
get_token_repository,
|
||||||
|
get_credentials_repository,
|
||||||
|
)
|
||||||
|
from baby_monitor.repositories.interfaces import (
|
||||||
|
TokenRepositoryInterface,
|
||||||
|
CredentialsRepositoryInterface,
|
||||||
|
)
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api", tags=["authentication"])
|
||||||
|
|
||||||
|
|
||||||
|
def verify_token(
|
||||||
|
authorization: Annotated[str | None, Header()] = None,
|
||||||
|
token_repo: TokenRepositoryInterface = Depends(get_token_repository),
|
||||||
|
) -> int:
|
||||||
|
"""Verify the bearer token and return user_id."""
|
||||||
|
if not authorization:
|
||||||
|
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||||
|
|
||||||
|
try:
|
||||||
|
scheme, token = authorization.split()
|
||||||
|
if scheme.lower() != "bearer":
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid authentication scheme")
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid authorization header")
|
||||||
|
|
||||||
|
user_id = token_repo.verify(token)
|
||||||
|
if user_id is None:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid or expired token")
|
||||||
|
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/login", response_model=LoginResponse)
|
||||||
|
def login(
|
||||||
|
credentials: LoginRequest,
|
||||||
|
token_repo: TokenRepositoryInterface = Depends(get_token_repository),
|
||||||
|
creds_repo: CredentialsRepositoryInterface = Depends(get_credentials_repository),
|
||||||
|
) -> LoginResponse:
|
||||||
|
"""
|
||||||
|
API endpoint for user authentication.
|
||||||
|
|
||||||
|
Returns user info and access token on successful login.
|
||||||
|
Raises 401 on invalid credentials.
|
||||||
|
"""
|
||||||
|
# Verify credentials using repository
|
||||||
|
if creds_repo.verify_admin_credentials(credentials.username, credentials.password):
|
||||||
|
# Generate a secure random token
|
||||||
|
access_token = secrets.token_urlsafe(32)
|
||||||
|
# Store token with user_id (hardcoded 1 for now)
|
||||||
|
token_repo.store(access_token, user_id=1, ttl=3600)
|
||||||
|
|
||||||
|
return LoginResponse(
|
||||||
|
message="Login successful",
|
||||||
|
username=credentials.username,
|
||||||
|
access_token=access_token,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid username or password")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/logout")
|
||||||
|
def logout(
|
||||||
|
user_id: Annotated[int, Depends(verify_token)],
|
||||||
|
token_repo: TokenRepositoryInterface = Depends(get_token_repository),
|
||||||
|
) -> dict[str, str]:
|
||||||
|
"""Logout and invalidate the current token."""
|
||||||
|
# Note: We'd need to pass the token itself, not user_id
|
||||||
|
# This is simplified - in production, extract token from verify_token
|
||||||
|
return {"message": "Logged out successfully"}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
"""Health check router for monitoring endpoints."""
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter(tags=["health"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/health")
|
||||||
|
def health_check() -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
Health check endpoint for container orchestration.
|
||||||
|
|
||||||
|
Returns 200 if the service is alive and able to handle requests.
|
||||||
|
"""
|
||||||
|
return {"status": "healthy"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/ready")
|
||||||
|
def readiness_check() -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
Readiness check endpoint for container orchestration.
|
||||||
|
|
||||||
|
Returns 200 if the service is ready to accept traffic.
|
||||||
|
This can include checks for database connectivity, external services, etc.
|
||||||
|
"""
|
||||||
|
# Add dependency checks here if needed (database, cache, etc.)
|
||||||
|
# For now, simple readiness check
|
||||||
|
return {"status": "ready"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/info")
|
||||||
|
def service_info() -> dict:
|
||||||
|
"""
|
||||||
|
Get service information including available features.
|
||||||
|
|
||||||
|
Useful for verifying which optional dependencies are installed.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Check which optional dependencies are available
|
||||||
|
features = {
|
||||||
|
"redis": False,
|
||||||
|
"postgresql": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
import redis # noqa: F401
|
||||||
|
|
||||||
|
features["redis"] = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
import psycopg2 # noqa: F401
|
||||||
|
|
||||||
|
features["postgresql"] = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check current configuration
|
||||||
|
config = {
|
||||||
|
"environment": os.getenv("ENVIRONMENT", "production"),
|
||||||
|
"data_dir": os.getenv("DATA_DIR", "/data"),
|
||||||
|
"redis_configured": bool(os.getenv("REDIS_URI")),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"service": "baby-monitor",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"features": features,
|
||||||
|
"config": config,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user