Harden JSON storage (atomic writes + crash-safe loads) + suggestions lightbulb

- storage.py: atomic save_json (temp+fsync+os.replace) and load_json that
  survives missing/corrupt files (moves corrupt aside). Closes #11/#12.
- route all data modules + app.py helpers through storage (closes #31 core);
  fixes mutable DEFAULT_FUNDS/DEFAULT_CARS return (#23).
- gunicorn --workers 1 --threads 4 to remove the write race (#13); strip
  Dockerfile template scaffolding (#40).
- base.html: 💡 suggestions link in the shared nav (shows on every page).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 16:34:06 -05:00
parent 494f5e3b5b
commit be74ca1eb7
10 changed files with 95 additions and 86 deletions

View File

@@ -2,6 +2,7 @@ import json
import os
from datetime import date
import math
from storage import load_json, save_json
DATA_DIR = os.environ.get("DATA_DIR", os.path.join(os.path.dirname(__file__), "data"))
DEBTS_FILE = os.path.join(DATA_DIR, "debts.json")
@@ -116,27 +117,19 @@ OWNER_COLORS = {
def load_debts():
if os.path.exists(DEBTS_FILE):
with open(DEBTS_FILE) as f:
return json.load(f)
return []
return load_json(DEBTS_FILE, [])
def save_debts(debts):
with open(DEBTS_FILE, "w") as f:
json.dump(debts, f, indent=2)
save_json(DEBTS_FILE, debts)
def load_debt_log():
if os.path.exists(DEBT_LOG_FILE):
with open(DEBT_LOG_FILE) as f:
return json.load(f)
return {}
return load_json(DEBT_LOG_FILE, {})
def save_debt_log(log):
with open(DEBT_LOG_FILE, "w") as f:
json.dump(log, f, indent=2)
save_json(DEBT_LOG_FILE, log)
def month_key(year, month):