feat(account): CLI lifecycle conturi + accounts.active (PRD 3.1)
Inlocuieste crearea conturilor prin INSERT SQL manual cu un tool admin dedicat, simetric cu tools/apikey.py. Fundatia Etapei 3 (3.2/3.3). - app/accounts.py: create_account/set_active/list_accounts (helper pur, partajat CLI + viitor flux web 3.3). Normalizeaza CUI (trim+upper), prinde IntegrityError -> ValueError cu cauza+fix. - accounts.active (lifecycle cont) + index unic partial ux_accounts_cui (unicitate la nivel de index, fara fereastra de coliziune). Migrare idempotenta in _migrate. - tools/account.py: create (--name/--cui/--inactive/--with-key atomic), list [--pending], activate/deactivate --account N. Erori -> exit 2. - 20 teste noi (12 helper + 8 CLI); suita 299 passed. active e inert pana la gate-ul worker din 3.3 (documentat). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
70
app/accounts.py
Normal file
70
app/accounts.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Lifecycle conturi ROAAUTO (admin, fara suprafata HTTP).
|
||||
|
||||
Functii pure de creare/listare/(de)activare cont, partajate intre CLI
|
||||
(`tools/account.py`, Etapa 3.1) si fluxul web de self-onboarding (Etapa 3.3,
|
||||
care reuseaza `create_account` + `active`). Identitatea de login (email/parola)
|
||||
NU traieste aici — apartine 3.3.
|
||||
|
||||
NOTA lifecycle `active`: coloana `accounts.active` este un flag de lifecycle
|
||||
consumat de 3.3 (gate „cont in asteptare", `active=0`). Pana la gate-ul worker din
|
||||
3.3, `active=0` NU opreste trimiterile (worker-ul nu citeste contul, doar
|
||||
`api_keys.active`). `deactivate` marcheaza intentia administrativa; nu blocheaza
|
||||
inca fluxul de trimitere. (Addendum A2.)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
|
||||
|
||||
def _norm_cui(cui: str | None) -> str | None:
|
||||
"""trim + upper; sir gol -> None (tratat ca „fara CUI")."""
|
||||
if cui is None:
|
||||
return None
|
||||
cui = cui.strip().upper()
|
||||
return cui or None
|
||||
|
||||
|
||||
def create_account(
|
||||
conn: sqlite3.Connection, name: str, cui: str | None = None, active: bool = True
|
||||
) -> int:
|
||||
"""Insereaza un cont si intoarce id-ul nou (AUTOINCREMENT, deci >=2 — nu atinge default id=1).
|
||||
|
||||
`name` gol/whitespace -> ValueError. `cui` se normalizeaza (trim+upper); un CUI
|
||||
deja folosit -> ValueError cu cauza+fix. Unicitatea e impusa de indexul partial
|
||||
`ux_accounts_cui` (nu de un check separat), deci e sigura la concurenta.
|
||||
"""
|
||||
name = (name or "").strip()
|
||||
if not name:
|
||||
raise ValueError("name gol (un cont are nevoie de nume)")
|
||||
cui = _norm_cui(cui)
|
||||
try:
|
||||
cur = conn.execute(
|
||||
"INSERT INTO accounts (name, cui, active) VALUES (?, ?, ?)",
|
||||
(name, cui, 1 if active else 0),
|
||||
)
|
||||
except sqlite3.IntegrityError:
|
||||
existing = conn.execute("SELECT id FROM accounts WHERE cui=?", (cui,)).fetchone()
|
||||
owner = existing["id"] if existing else "?"
|
||||
raise ValueError(
|
||||
f"CUI {cui} e deja folosit de contul {owner} "
|
||||
f"(foloseste 'activate --account {owner}' sau alt CUI)"
|
||||
)
|
||||
return int(cur.lastrowid or 0)
|
||||
|
||||
|
||||
def set_active(conn: sqlite3.Connection, account_id: int, active: bool) -> None:
|
||||
"""Comuta `accounts.active`. Idempotent (set activ pe activ nu arunca).
|
||||
Cont inexistent -> ValueError."""
|
||||
row = conn.execute("SELECT 1 FROM accounts WHERE id=?", (account_id,)).fetchone()
|
||||
if not row:
|
||||
raise ValueError(f"cont inexistent: {account_id}")
|
||||
conn.execute("UPDATE accounts SET active=? WHERE id=?", (1 if active else 0, account_id))
|
||||
|
||||
|
||||
def list_accounts(conn: sqlite3.Connection) -> list[dict]:
|
||||
"""Metadate conturi (FARA `rar_creds_enc`), ordonate dupa id."""
|
||||
rows = conn.execute(
|
||||
"SELECT id, name, cui, active, created_at FROM accounts ORDER BY id"
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
Reference in New Issue
Block a user