diff --git a/cortex/routers/settings.py b/cortex/routers/settings.py
index b2e8a88..fde57c3 100644
--- a/cortex/routers/settings.py
+++ b/cortex/routers/settings.py
@@ -16,7 +16,7 @@ import jwt
from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse, RedirectResponse
-from auth_utils import COOKIE_NAME, decode_token, check_credentials, set_password
+from auth_utils import COOKIE_NAME, decode_token, check_credentials, set_password, _read_auth, _write_auth
from persona import list_user_personas
from config import settings as app_settings
@@ -41,6 +41,20 @@ def _get_session_user(request: Request) -> str | None:
def _settings_page(username: str, personas: list[str], success: str = "", error: str = "") -> str:
html = (_STATIC / "settings.html").read_text()
html = html.replace("{{ username }}", username)
+
+ # Connected Google account
+ auth_data = _read_auth(username)
+ google_email = auth_data.get("google_email") or ""
+ html = html.replace("{{ google_email }}", google_email)
+
+ # Gemini API key — show masked hint only, never the full key
+ gemini_key = auth_data.get("gemini_api_key") or ""
+ if gemini_key:
+ hint = f"Saved (…{gemini_key[-4:]})"
+ else:
+ hint = "Using server key"
+ html = html.replace("{{ gemini_key_hint }}", hint)
+ html = html.replace("{{ gemini_key_set }}", "true" if gemini_key else "false")
persona_items = "\n".join(
f'''
{p}
@@ -139,6 +153,30 @@ async def rename_username(
return resp
+@router.post("/settings/gemini-key", include_in_schema=False)
+async def save_gemini_key(
+ request: Request,
+ gemini_api_key: str = Form(...),
+):
+ username = _get_session_user(request)
+ if not username:
+ return RedirectResponse("/login", status_code=302)
+
+ personas = list_user_personas(username)
+ gemini_api_key = gemini_api_key.strip()
+
+ data = _read_auth(username)
+ if gemini_api_key:
+ data["gemini_api_key"] = gemini_api_key
+ msg = "Gemini API key saved."
+ else:
+ data.pop("gemini_api_key", None)
+ msg = "Gemini API key removed — using server key."
+ _write_auth(username, data)
+ logger.info("gemini key updated: %s", username)
+ return HTMLResponse(_settings_page(username, personas, success=msg))
+
+
@router.post("/settings/persona/rename", include_in_schema=False)
async def rename_persona(
request: Request,
diff --git a/cortex/static/settings.html b/cortex/static/settings.html
index 246a627..f33c319 100644
--- a/cortex/static/settings.html
+++ b/cortex/static/settings.html
@@ -232,6 +232,45 @@
+
+
+
Connected Accounts
+
+
+
+
+
+ To link or change your Google account, contact Scott.
+
+
+
+
+
+
Gemini API Key
+
+ Paste your personal key from
+ aistudio.google.com/apikey
+ to use your own Gemini quota. Leave blank to use the shared server key.
+
+
+
+ Current: {{ gemini_key_hint }}
+
+ — remove
+
+
+
+
Change Password
@@ -287,6 +326,16 @@
document.getElementById('show-rename-user').style.display = '';
});
+ // Gemini key — "remove" link clears the input and submits the form
+ const geminiRemove = document.getElementById('gemini-remove-link');
+ if (geminiRemove) {
+ geminiRemove.addEventListener('click', e => {
+ e.preventDefault();
+ document.getElementById('gemini_api_key').value = '';
+ document.querySelector('form[action="/settings/gemini-key"]').submit();
+ });
+ }
+
// Persona rename toggle
document.querySelectorAll('.persona-rename-toggle').forEach(btn => {
btn.addEventListener('click', () => {