11"""Secrets routes: write-only storage of user secrets.
22
3- Values are Fernet- encrypted at rest and never returned by the API —
4- only names and timestamps are listable. The sandbox manager (not the
5- agent, not the browser) is the intended consumer of decrypted values .
3+ HTTP wiring only. Values are encrypted at rest and never returned by
4+ the API -- only names and timestamps are listable; see
5+ ``controllers.secrets`` .
66"""
77
88from __future__ import annotations
99
10- from fastapi import APIRouter , Depends , HTTPException , status
11- from sqlalchemy import select
10+ from fastapi import APIRouter , Depends , status
1211from sqlalchemy .orm import Session
1312
14- from ..infra import secrets_store
15- from ..models import Secret , User , new_id
16- from ..models .db import commit_now , get_db
13+ from ..controllers import secrets as secrets_controller
14+ from ..models import Secret , User
15+ from ..models .db import get_db
1716from ..routes .auth import authenticate_user
1817from ..validation .schemas import SecretIn , SecretOut
1918
2019router = APIRouter (prefix = "/secrets" , tags = ["secrets" ])
2120
2221
22+ def _secret_out (secret : Secret ) -> SecretOut :
23+ return SecretOut (name = secret .name , created_at = secret .created_at )
24+
25+
2326@router .put ("/{name}" , response_model = SecretOut , status_code = status .HTTP_201_CREATED )
2427def put_secret (
2528 name : str ,
2629 body : SecretIn ,
2730 user : User = Depends (authenticate_user ),
2831 db : Session = Depends (get_db ),
2932) -> SecretOut :
30- if body .name != name :
31- raise HTTPException (status .HTTP_400_BAD_REQUEST , "name mismatch with path" )
32- existing = db .scalar (select (Secret ).where (Secret .user_id == user .id , Secret .name == name ))
33- if existing is not None :
34- existing .value_encrypted = secrets_store .encrypt (body .value )
35- db .flush ()
36- commit_now (db )
37- return SecretOut (name = name , created_at = existing .created_at )
38- secret = Secret (
39- id = new_id ("sec" ),
40- user_id = user .id ,
41- name = name ,
42- value_encrypted = secrets_store .encrypt (body .value ),
43- )
44- db .add (secret )
45- db .flush ()
46- commit_now (db )
47- return SecretOut (name = name , created_at = secret .created_at )
33+ secret = secrets_controller .put (db , user , name , body_name = body .name , value = body .value )
34+ return _secret_out (secret )
4835
4936
5037@router .get ("" , response_model = list [SecretOut ])
5138def list_secrets (
5239 user : User = Depends (authenticate_user ), db : Session = Depends (get_db )
5340) -> list [SecretOut ]:
54- rows = db .scalars (select (Secret ).where (Secret .user_id == user .id )).all ()
55- return [SecretOut (name = s .name , created_at = s .created_at ) for s in rows ]
41+ return [_secret_out (s ) for s in secrets_controller .list_for_user (db , user )]
5642
5743
5844@router .delete ("/{name}" , status_code = status .HTTP_204_NO_CONTENT )
@@ -61,8 +47,4 @@ def delete_secret(
6147 user : User = Depends (authenticate_user ),
6248 db : Session = Depends (get_db ),
6349) -> None :
64- secret = db .scalar (select (Secret ).where (Secret .user_id == user .id , Secret .name == name ))
65- if secret is None :
66- raise HTTPException (status .HTTP_404_NOT_FOUND , "secret not found" )
67- db .delete (secret )
68- commit_now (db )
50+ secrets_controller .delete (db , user , name )
0 commit comments