Search, chat and research over parliamentary speeches and documents
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

58 lines
2.3 KiB

"""Per-account settings blob — currently the user's AI provider config.
Zero-knowledge, same contract as chats and research boards: the browser
encrypts under the user's DEK and the server stores an opaque "v1:" blob. That
is what lets an API key follow a user between devices without the server (or
anyone with the database) being able to read it.
Guests have no account to hang a blob on and keep their settings in
localStorage instead; these routes are logged-in only.
"""
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from backend.services import crypto_blob
from backend.services.auth import get_current_user
from postgres_client import pg
router = APIRouter(prefix="/api/me", tags=["settings"])
class SettingsRequest(BaseModel):
enc_settings: str = Field(..., min_length=4, max_length=20000)
@router.get("/settings")
def get_settings(user: dict = Depends(get_current_user)) -> dict:
rows = pg.execute(
"SELECT enc_settings, settings_updated_at::text AS updated_at "
"FROM users WHERE id = %s",
(user["user_id"],),
)
if not rows:
raise HTTPException(status_code=404, detail="Kontot finns inte längre")
return {"enc_settings": rows[0]["enc_settings"], "updated_at": rows[0]["updated_at"]}
@router.put("/settings", status_code=204, response_model=None)
def put_settings(payload: SettingsRequest, user: dict = Depends(get_current_user)) -> None:
# The one server-side invariant worth asserting: this column may only ever
# hold ciphertext. A client bug that posted a plaintext key would otherwise
# persist it silently.
if not crypto_blob.is_encrypted(payload.enc_settings):
raise HTTPException(status_code=400, detail="Inställningar måste vara krypterade")
pg.execute_void(
"UPDATE users SET enc_settings = %s, settings_updated_at = NOW() WHERE id = %s",
(payload.enc_settings, user["user_id"]),
)
@router.delete("/settings", status_code=204, response_model=None)
def delete_settings(user: dict = Depends(get_current_user)) -> None:
"""'Glöm min nyckel' — drop the stored blob without touching the account."""
pg.execute_void(
"UPDATE users SET enc_settings = NULL, settings_updated_at = NOW() WHERE id = %s",
(user["user_id"],),
)