Files
yolkbook/backend/routers/flock.py
derekc 392a48dfb9 Go-live hardening: server_tokens, resource limits, pinned images, CVE fixes
- Add server_tokens off to nginx (suppress version leakage)
- Add deploy.resources.limits to all containers (db: 512M, api: 256M, nginx: 64M)
- Pin image tags: mysql:8.0 → 8.0.45, nginx:alpine → 1.29.6-alpine
- Fix CVEs: cryptography 43.0.3 → 46.0.5 (HIGH), python-jose 3.3.0 → 3.4.0 (CRITICAL)
- Add .limit(500) to GET /api/flock and GET /api/admin/users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 00:16:28 -07:00

115 lines
3.4 KiB
Python

from datetime import date
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from database import get_db
from models import FlockHistory, User
from schemas import FlockHistoryCreate, FlockHistoryUpdate, FlockHistoryOut
from auth import get_current_user
router = APIRouter(prefix="/api/flock", tags=["flock"])
@router.get("", response_model=list[FlockHistoryOut])
def list_flock_history(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
q = (
select(FlockHistory)
.where(FlockHistory.user_id == current_user.id)
.order_by(FlockHistory.date.desc())
.limit(500)
)
return db.scalars(q).all()
@router.get("/current", response_model=Optional[FlockHistoryOut])
def get_current_flock(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
q = (
select(FlockHistory)
.where(FlockHistory.user_id == current_user.id)
.order_by(FlockHistory.date.desc())
.limit(1)
)
return db.scalars(q).first()
@router.get("/at/{target_date}", response_model=Optional[FlockHistoryOut])
def get_flock_at_date(
target_date: date,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
q = (
select(FlockHistory)
.where(FlockHistory.user_id == current_user.id)
.where(FlockHistory.date <= target_date)
.order_by(FlockHistory.date.desc())
.limit(1)
)
return db.scalars(q).first()
@router.post("", response_model=FlockHistoryOut, status_code=201)
def create_flock_entry(
body: FlockHistoryCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
record = FlockHistory(**body.model_dump(), user_id=current_user.id)
db.add(record)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail=f"A flock entry for {body.date} already exists.")
db.refresh(record)
return record
@router.put("/{record_id}", response_model=FlockHistoryOut)
def update_flock_entry(
record_id: int,
body: FlockHistoryUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
record = db.scalars(
select(FlockHistory)
.where(FlockHistory.id == record_id, FlockHistory.user_id == current_user.id)
).first()
if not record:
raise HTTPException(status_code=404, detail="Record not found")
for field, value in body.model_dump(exclude_none=True).items():
setattr(record, field, value)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail="A flock entry for that date already exists.")
db.refresh(record)
return record
@router.delete("/{record_id}", status_code=204)
def delete_flock_entry(
record_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
record = db.scalars(
select(FlockHistory)
.where(FlockHistory.id == record_id, FlockHistory.user_id == current_user.id)
).first()
if not record:
raise HTTPException(status_code=404, detail="Record not found")
db.delete(record)
db.commit()