Initial commit: moon-household-budget

This commit is contained in:
Bonna
2026-06-06 19:12:43 -05:00
commit a3b91f6d6b
57 changed files with 11944 additions and 0 deletions

52
settings.py Normal file
View File

@@ -0,0 +1,52 @@
"""App settings + secret storage (Gmail credentials, reminder prefs, etc.).
Stored as JSON on the data volume (DATA_DIR/settings.json) via the atomic
storage layer — so it is NEVER committed to source control. Secrets resolve
with an environment-variable fallback, so a value can come from the settings
page OR from the container's .env.
This is what lets us pull the hardcoded Gmail app password out of reminders.py
(issue #2): the password lives in settings.json on the volume instead.
"""
import os
from storage import load_json, save_json, DATA_DIR
SETTINGS_FILE = os.path.join(DATA_DIR, "settings.json")
# Keys holding secrets — their values are never rendered back to the browser.
SECRET_KEYS = {"gmail_app_password"}
DEFAULTS = {
"gmail_address": "",
"gmail_app_password": "",
"reminder_recipient": "", # blank => send to gmail_address
"reminders_enabled": True,
}
def load_settings():
data = load_json(SETTINGS_FILE, {}) or {}
merged = dict(DEFAULTS)
merged.update(data)
return merged
def save_settings(data):
save_json(SETTINGS_FILE, data)
def get_setting(key, env=None, default=""):
"""Resolve a setting: stored value first, then env var, then default."""
val = load_settings().get(key)
if val not in (None, ""):
return val
if env:
env_val = os.environ.get(env)
if env_val not in (None, ""):
return env_val
return default
def is_set(key):
"""True if a (typically secret) value is present, without revealing it."""
return bool(load_settings().get(key))