diff --git a/app/api/endpoints/export.py b/app/api/endpoints/export.py index 3c5849c..1bfe8c2 100644 --- a/app/api/endpoints/export.py +++ b/app/api/endpoints/export.py @@ -1,9 +1,11 @@ -"""Export endpoints — CSV/Excel of receipt and inventory data.""" +"""Export endpoints — CSV and JSON export of user data.""" from __future__ import annotations import asyncio import csv import io +import json +from datetime import datetime, timezone from fastapi import APIRouter, Depends from fastapi.responses import StreamingResponse @@ -45,3 +47,33 @@ async def export_inventory_csv(store: Store = Depends(get_store)): media_type="text/csv", headers={"Content-Disposition": "attachment; filename=inventory.csv"}, ) + + +@router.get("/json") +async def export_full_json(store: Store = Depends(get_store)): + """Export full pantry inventory + saved recipes as a single JSON file. + + Intended for data portability — users can import this into another + Kiwi instance or keep it as an offline backup. + """ + inventory, saved = await asyncio.gather( + asyncio.to_thread(store.list_inventory), + asyncio.to_thread(store.get_saved_recipes, 1000, 0), + ) + + export_doc = { + "kiwi_export": { + "version": "1.0", + "exported_at": datetime.now(timezone.utc).isoformat(), + "inventory": [dict(row) for row in inventory], + "saved_recipes": [dict(row) for row in saved], + } + } + + body = json.dumps(export_doc, default=str, indent=2) + filename = f"kiwi-export-{datetime.now(timezone.utc).strftime('%Y%m%d')}.json" + return StreamingResponse( + iter([body]), + media_type="application/json", + headers={"Content-Disposition": f"attachment; filename={filename}"}, + ) diff --git a/frontend/src/components/InventoryList.vue b/frontend/src/components/InventoryList.vue index 1e73c18..b59fc96 100644 --- a/frontend/src/components/InventoryList.vue +++ b/frontend/src/components/InventoryList.vue @@ -392,10 +392,14 @@
+ JSON includes pantry + saved recipes. Import it into another Kiwi instance any time. +