04/24 Migrated info which stored in .ini to database and keyring

This commit is contained in:
2026-04-24 15:37:58 -04:00
parent f2a8d3f15b
commit 5d6ca5a039
9 changed files with 479 additions and 299 deletions
+9 -21
View File
@@ -167,35 +167,23 @@ class AiSummaryView(ttk.Frame):
def _load_config(self):
try:
import configparser
from config import CONFIG_FILE
from config import get_setting
from utils.config_crypto import decrypt_value
cfg = configparser.ConfigParser()
cfg.read(CONFIG_FILE, encoding="utf-8")
if cfg.has_section(_CFG_SECTION):
raw_key = cfg.get(_CFG_SECTION, _CFG_KEY_KEY, fallback="")
raw_key = get_setting("groq.api_key", "")
if raw_key:
self._api_key_var.set(decrypt_value(raw_key))
model = cfg.get(_CFG_SECTION, _CFG_KEY_MODEL,
fallback=GROQ_MODELS[0])
if model in GROQ_MODELS:
self._model_var.set(model)
model = get_setting("groq.model", GROQ_MODELS[0])
if model in GROQ_MODELS:
self._model_var.set(model)
except Exception as e:
logger.warning(f"Could not load Groq config: {e}")
def _save_config(self):
try:
import configparser
from config import CONFIG_FILE
from config import set_setting
from utils.config_crypto import encrypt_value
cfg = configparser.ConfigParser()
cfg.read(CONFIG_FILE, encoding="utf-8")
if not cfg.has_section(_CFG_SECTION):
cfg.add_section(_CFG_SECTION)
cfg.set(_CFG_SECTION, _CFG_KEY_KEY,
encrypt_value(self._api_key_var.get().strip()))
cfg.set(_CFG_SECTION, _CFG_KEY_MODEL, self._model_var.get())
with open(CONFIG_FILE, "w", encoding="utf-8") as fh:
cfg.write(fh)
set_setting("groq.api_key", encrypt_value(self._api_key_var.get().strip()))
set_setting("groq.model", self._model_var.get())
except Exception as e:
logger.warning(f"Could not save Groq config: {e}")
+2 -2
View File
@@ -1,6 +1,6 @@
"""
views/email_settings_view.py — SMTP / scheduled report configuration dialog.
Admin-only. Persists to config.ini [email] section via utils/scheduler.py.
Admin-only. Persists to the app_settings database table via utils/scheduler.py.
"""
import tkinter as tk
@@ -326,4 +326,4 @@ class EmailSettingsView(tk.Toplevel):
f"security={security}.")
show_info("Email settings saved successfully.")
logger.info(f"Email settings saved by {self.current_user['username']}.")
self.destroy()
self.destroy()
+24 -15
View File
@@ -1,13 +1,12 @@
"""
views/settings_view.py — Database connection settings dialog.
Shown automatically on first run (no config.ini) and accessible
Shown automatically on first run (no stored credentials) and accessible
via the admin sidebar Settings nav item at any time.
Writes connection details to config.ini via config.save_config().
Does NOT store the password in plaintext beyond what config.ini holds
(which is acceptable for a locally-run desktop tool; operators should
restrict file-system access to config.ini in production).
Credentials are saved to the OS native keychain (Windows Credential Manager,
macOS Keychain, or Linux Secret Service) via the keyring library.
No config.ini or local file is written — the app is fully portable.
"""
import tkinter as tk
@@ -42,7 +41,7 @@ class SettingsView(tk.Toplevel):
self.title("Database Setup" if first_run else "Connection Settings")
self.configure(bg=COLOURS["bg"])
self.resizable(False, False)
self.resizable(False, True) # allow vertical resize for high-DPI
self.grab_set()
if first_run:
@@ -58,7 +57,9 @@ class SettingsView(tk.Toplevel):
def _centre(self):
self.update_idletasks()
w, h = 480, 480
# Height increased to 560 to ensure button row is always visible.
# Vertical resize allowed for high-DPI / large-font environments.
w, h = 480, 560
x = (self.winfo_screenwidth() - w) // 2
y = (self.winfo_screenheight() - h) // 2
self.geometry(f"{w}x{h}+{x}+{y}")
@@ -77,6 +78,12 @@ class SettingsView(tk.Toplevel):
font=FONT_SMALL, bg=COLOURS["accent"],
fg=COLOURS["white"]).pack(pady=(2, 0))
# ── Buttons (packed before form so they anchor to bottom) ─────────────
# Packing the button row before the expanding form guarantees it is
# always visible even when the form content exceeds the window height.
btn_row = tk.Frame(self, bg=COLOURS["bg"], padx=36, pady=16)
btn_row.pack(side="bottom", fill="x")
# ── Form ──────────────────────────────────────────────────────────────
form = tk.Frame(self, bg=COLOURS["bg"], padx=36, pady=20)
form.pack(fill="both", expand=True)
@@ -116,7 +123,8 @@ class SettingsView(tk.Toplevel):
# ── Hint ──────────────────────────────────────────────────────────────
hint = (
"Settings are saved to config.ini in the application folder.\n"
"Credentials are saved to your OS keychain (Windows Credential Manager, "
"macOS Keychain, or Linux Secret Service) — no config.ini required.\n"
"Ensure the database user has CREATE, INSERT, UPDATE, DELETE privileges."
)
tk.Label(form, text=hint, bg=COLOURS["bg"],
@@ -124,10 +132,7 @@ class SettingsView(tk.Toplevel):
justify="left", wraplength=380).grid(
row=6, column=0, columnspan=2, sticky="w", pady=(8, 0))
# ── Buttons ───────────────────────────────────────────────────────────
btn_row = tk.Frame(self, bg=COLOURS["bg"], padx=36, pady=16)
btn_row.pack(fill="x")
# ── Buttons (btn_row already packed at top of _build_ui) ─────────────
self._save_btn = tk.Button(
btn_row, text="Save & Connect",
command=self._save,
@@ -263,9 +268,13 @@ class SettingsView(tk.Toplevel):
)
conn.close()
from config import save_config
from config import save_config, reload_db_config
save_config(host, port, database, user, password)
logger.info(f"Settings saved: {user}@{host}:{port}/{database}")
# Immediately update the in-memory DB_CONFIG and reset the
# connection pool so the app connects with the new credentials
# without requiring a restart.
reload_db_config()
logger.info(f"Settings saved and applied: {user}@{host}:{port}/{database}")
self.after(0, self._on_save_success)
except Exception as e:
@@ -284,4 +293,4 @@ class SettingsView(tk.Toplevel):
def _finish(self):
self.destroy()
self.on_save_callback()
self.on_save_callback()