- settings.py: JSON-on-volume store with env fallback; secrets never rendered. - /settings page (⚙️ in nav) to set Gmail address + app password + reminder prefs; password fields are write-only (blank keeps existing). - reminders.py reads Gmail creds from Settings instead of a hardcoded password (resolves #2 in code — note the old password is still in git history; rotate it). - mac_notification no longer crashes off-macOS; FLASK_DEBUG gates debug (#7). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
1.5 KiB
Python
53 lines
1.5 KiB
Python
"""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))
|