fix: row_factory in _household_store; validate household_id from Heimdall; monkeypatch CLOUD_DATA_ROOT in accept test; add KIWI_BASE_URL to compose.cloud.yml

This commit is contained in:
pyr0ball 2026-04-04 22:59:06 -07:00
parent 11a0d1f3a6
commit 6a59c8dfd1
3 changed files with 22 additions and 5 deletions

View file

@ -6,6 +6,8 @@ import os
import secrets import secrets
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
import sqlite3
import requests import requests
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
@ -42,11 +44,14 @@ def _require_household_owner(session: CloudUser = Depends(_require_premium)) ->
def _household_store(household_id: str) -> Store: def _household_store(household_id: str) -> Store:
"""Open the household DB directly (used during invite acceptance).""" """Open the household DB directly (used during invite acceptance).
from pathlib import Path Sets row_factory so dict-style column access works on raw conn queries.
"""
db_path = CLOUD_DATA_ROOT / f"household_{household_id}" / "kiwi.db" db_path = CLOUD_DATA_ROOT / f"household_{household_id}" / "kiwi.db"
db_path.parent.mkdir(parents=True, exist_ok=True) db_path.parent.mkdir(parents=True, exist_ok=True)
return Store(db_path) store = Store(db_path)
store.conn.row_factory = sqlite3.Row
return store
def _heimdall_post(path: str, body: dict) -> dict: def _heimdall_post(path: str, body: dict) -> dict:
@ -74,7 +79,13 @@ async def create_household(session: CloudUser = Depends(_require_premium)):
if session.household_id: if session.household_id:
raise HTTPException(status_code=409, detail="You are already in a household.") raise HTTPException(status_code=409, detail="You are already in a household.")
data = _heimdall_post("/admin/household/create", {"owner_user_id": session.user_id}) data = _heimdall_post("/admin/household/create", {"owner_user_id": session.user_id})
household_id = data.get("household_id", "local-household") household_id = data.get("household_id")
if not household_id:
# Heimdall returned OK but without a household_id — treat as server error.
# Fall back to a local stub only when HEIMDALL_ADMIN_TOKEN is unset (dev mode).
if HEIMDALL_ADMIN_TOKEN:
raise HTTPException(status_code=500, detail="Heimdall did not return a household_id.")
household_id = "local-household"
return HouseholdCreateResponse( return HouseholdCreateResponse(
household_id=household_id, household_id=household_id,
message="Household created. Share an invite link to add members.", message="Household created. Share an invite link to add members.",

View file

@ -13,6 +13,7 @@ services:
environment: environment:
CLOUD_MODE: "true" CLOUD_MODE: "true"
CLOUD_DATA_ROOT: /devl/kiwi-cloud-data CLOUD_DATA_ROOT: /devl/kiwi-cloud-data
KIWI_BASE_URL: https://menagerie.circuitforge.tech/kiwi
# DIRECTUS_JWT_SECRET, HEIMDALL_URL, HEIMDALL_ADMIN_TOKEN — set in .env # DIRECTUS_JWT_SECRET, HEIMDALL_URL, HEIMDALL_ADMIN_TOKEN — set in .env
# DEV ONLY: comma-separated IPs that bypass JWT auth (LAN testing without Caddy). # DEV ONLY: comma-separated IPs that bypass JWT auth (LAN testing without Caddy).
# Production deployments must NOT set this. Leave blank or omit entirely. # Production deployments must NOT set this. Leave blank or omit entirely.

View file

@ -86,8 +86,13 @@ def test_invite_generates_token():
app.dependency_overrides.clear() app.dependency_overrides.clear()
def test_accept_invalid_token_returns_404(): def test_accept_invalid_token_returns_404(tmp_path, monkeypatch):
"""Accepting a non-existent token returns 404.""" """Accepting a non-existent token returns 404."""
import app.api.endpoints.household as hh_ep
import app.cloud_session as cs
monkeypatch.setattr(hh_ep, "CLOUD_DATA_ROOT", tmp_path)
monkeypatch.setattr(cs, "CLOUD_DATA_ROOT", tmp_path)
from app.main import app from app.main import app
from app.cloud_session import get_session from app.cloud_session import get_session
import tempfile, pathlib import tempfile, pathlib