Add in-app Settings page for secrets; move Gmail creds out of source
- 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>
This commit is contained in:
26
app.py
26
app.py
@@ -6,6 +6,7 @@ import re
|
||||
from datetime import datetime, date
|
||||
import calendar as cal_module
|
||||
from storage import load_json, save_json
|
||||
from settings import load_settings, save_settings, SECRET_KEYS
|
||||
from categories import CATEGORIES, CATEGORY_COLORS
|
||||
from bills import load_bills, save_bills, bills_for_month, calendar_grid, get_week_num
|
||||
from sinking import (load_funds, save_funds, FUND_COLORS, monthly_needed,
|
||||
@@ -2847,5 +2848,28 @@ def income_delete(entry_id):
|
||||
return redirect(url_for("income_view", year=year, month=month))
|
||||
|
||||
|
||||
@app.route("/settings", methods=["GET", "POST"])
|
||||
def settings_view():
|
||||
current = load_settings()
|
||||
if request.method == "POST":
|
||||
new = dict(current)
|
||||
new["gmail_address"] = request.form.get("gmail_address", "").strip()
|
||||
new["reminder_recipient"] = request.form.get("reminder_recipient", "").strip()
|
||||
new["reminders_enabled"] = request.form.get("reminders_enabled") == "on"
|
||||
# Secret fields: only overwrite when a new value is supplied; a blank
|
||||
# field keeps the existing stored secret.
|
||||
pw = request.form.get("gmail_app_password", "").strip()
|
||||
if pw:
|
||||
new["gmail_app_password"] = pw
|
||||
save_settings(new)
|
||||
return redirect(url_for("settings_view", saved=1))
|
||||
# Never send secret values to the template — only whether each one is set.
|
||||
safe = {k: ("" if k in SECRET_KEYS else v) for k, v in current.items()}
|
||||
secrets_status = {k: bool(current.get(k)) for k in SECRET_KEYS}
|
||||
return render_template("settings.html", settings=safe,
|
||||
secrets_status=secrets_status,
|
||||
saved=request.args.get("saved"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, port=5000)
|
||||
app.run(debug=os.environ.get("FLASK_DEBUG") == "1", port=5000)
|
||||
|
||||
Reference in New Issue
Block a user