Consistency sweep: enum members, keyword status codes, SimpleCookie

analytics.py filters by TransactionType.COMPRA instead of the string
literal; budget.py uses keyword HTTPException args; the agent
middleware parses cookies with stdlib SimpleCookie instead of a regex
(ARCH-10); auth.py's lazy imports are hoisted — there was never an
actual auth/db cycle (ARCH-20); transactions.py hoists or_/and_.
Verified: dev container boots, agent auth paths return 401 for
missing/garbage cookies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-06-10 15:23:21 -06:00
parent 82f10a5d7c
commit 440da3e394
5 changed files with 15 additions and 16 deletions

View File

@@ -8,7 +8,7 @@ from sqlmodel import Session, func, select
from app.auth import get_current_user from app.auth import get_current_user
from app.db import get_session from app.db import get_session
from app.models.models import Category, Transaction from app.models.models import Category, Transaction, TransactionType
from app.services.budget_projection import get_cycle_range from app.services.budget_projection import get_cycle_range
from app.services.exchange_rate import get_converted_amount_expr from app.services.exchange_rate import get_converted_amount_expr
@@ -53,7 +53,7 @@ def spending_by_category(
func.sum(amount_crc).label("total"), func.sum(amount_crc).label("total"),
func.count().label("count"), func.count().label("count"),
) )
.where(Transaction.transaction_type == "COMPRA") .where(Transaction.transaction_type == TransactionType.COMPRA)
.group_by(Transaction.category_id) .group_by(Transaction.category_id)
) )
@@ -123,7 +123,7 @@ def monthly_trend(
), ),
) )
.where( .where(
Transaction.transaction_type == "COMPRA", Transaction.transaction_type == TransactionType.COMPRA,
Transaction.date >= start, Transaction.date >= start,
Transaction.date < end, Transaction.date < end,
) )
@@ -171,7 +171,7 @@ def daily_spending(
func.sum(amount_crc).label("total"), func.sum(amount_crc).label("total"),
func.count().label("count"), func.count().label("count"),
) )
.where(Transaction.transaction_type == "COMPRA") .where(Transaction.transaction_type == TransactionType.COMPRA)
.group_by(func.date(Transaction.date)) .group_by(func.date(Transaction.date))
.order_by(func.date(Transaction.date)) .order_by(func.date(Transaction.date))
) )

View File

@@ -248,9 +248,9 @@ def upsert_balance_override(
_user: str = Depends(get_current_user), _user: str = Depends(get_current_user),
): ):
if year < MIN_YEAR or year > MAX_YEAR: if year < MIN_YEAR or year > MAX_YEAR:
raise HTTPException(400, f"Year must be between {MIN_YEAR} and {MAX_YEAR}") raise HTTPException(status_code=400, detail=f"Year must be between {MIN_YEAR} and {MAX_YEAR}")
if year == FRESH_START_YEAR and month < FRESH_START_MONTH: if year == FRESH_START_YEAR and month < FRESH_START_MONTH:
raise HTTPException(400, f"Cannot override before {FRESH_START_YEAR}-{FRESH_START_MONTH:02d}") raise HTTPException(status_code=400, detail=f"Cannot override before {FRESH_START_YEAR}-{FRESH_START_MONTH:02d}")
existing = session.exec( existing = session.exec(
select(BalanceOverride).where( select(BalanceOverride).where(
@@ -288,6 +288,6 @@ def delete_balance_override(
) )
).first() ).first()
if not existing: if not existing:
raise HTTPException(404, "No override found for this month") raise HTTPException(status_code=404, detail="No override found for this month")
session.delete(existing) session.delete(existing)
session.commit() session.commit()

View File

@@ -3,6 +3,7 @@ from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy import and_, or_
from sqlmodel import Session, col, func, select from sqlmodel import Session, col, func, select
from app.auth import get_current_user from app.auth import get_current_user
@@ -73,7 +74,6 @@ def list_transactions(
prev_y, prev_m = get_previous_cycle(cycle_year, cycle_month) prev_y, prev_m = get_previous_cycle(cycle_year, cycle_month)
prev_start, prev_end = get_cycle_range(prev_y, prev_m) prev_start, prev_end = get_cycle_range(prev_y, prev_m)
# Normal transactions in this cycle (not deferred) + deferred from previous cycle # Normal transactions in this cycle (not deferred) + deferred from previous cycle
from sqlalchemy import or_, and_
query = query.where( query = query.where(
or_( or_(
and_( and_(

View File

@@ -12,6 +12,8 @@ from jose import JWTError, jwt
from sqlmodel import Session, select from sqlmodel import Session, select
from app.config import settings from app.config import settings
from app.db import get_session
from app.models.models import APIToken
from app.timeutil import utcnow from app.timeutil import utcnow
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
@@ -91,9 +93,6 @@ def _validate_token(token: str) -> str:
pass pass
# Fallback: check API token # Fallback: check API token
from app.db import get_session
from app.models.models import APIToken
token_hash = hash_token(token) token_hash = hash_token(token)
with next(get_session()) as session: with next(get_session()) as session:
api_token = session.exec( api_token = session.exec(

View File

@@ -1,7 +1,7 @@
import asyncio import asyncio
import json import json
import re
import uuid import uuid
from http.cookies import SimpleCookie
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from agent_framework_ag_ui import add_agent_framework_fastapi_endpoint from agent_framework_ag_ui import add_agent_framework_fastapi_endpoint
@@ -105,10 +105,10 @@ async def agent_auth_and_session(request: Request, call_next):
if auth_header.lower().startswith("bearer "): if auth_header.lower().startswith("bearer "):
token = auth_header.split(" ", 1)[1].strip() token = auth_header.split(" ", 1)[1].strip()
else: else:
cookie_header = request.headers.get("cookie", "") cookies = SimpleCookie()
m = re.search(r"(?:^|;\s*)ws_token=([^;]+)", cookie_header) cookies.load(request.headers.get("cookie", ""))
if m: if "ws_token" in cookies:
token = m.group(1) token = cookies["ws_token"].value
if not token: if not token:
return Response(status_code=401, content="Missing auth") return Response(status_code=401, content="Missing auth")