feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
"""Registra/actualiza un perfil de Chromium (y opcionalmente sus cuentas) en osint_db.
|
||||
|
||||
Wrapper cliente del service local `osint_db` (FastAPI + DuckDB single-writer) que
|
||||
mantiene el catalogo de perfiles del navegador usados para investigaciones multicuenta
|
||||
OSINT. En una sola llamada hace:
|
||||
|
||||
1. POST /api/browser-profile con la metadata del perfil (upsert idempotente).
|
||||
2. Un POST /api/browser-profile/account por cada cuenta de la lista `accounts`.
|
||||
|
||||
Funcion impura: hace red (HTTP al service). No lanza; devuelve un dict de estado.
|
||||
El service responde SIEMPRE HTTP 200 con body `{"status":...}` (se parsea el body).
|
||||
"""
|
||||
|
||||
from browser._osint_db_client import post_json
|
||||
|
||||
|
||||
def browser_profile_register(
|
||||
profile_dir: str,
|
||||
label: str = "",
|
||||
persona: str = "",
|
||||
purpose: str = "",
|
||||
note_path: str = "",
|
||||
tags: list | None = None,
|
||||
notes: str = "",
|
||||
user_data_dir: str = "",
|
||||
status: str = "active",
|
||||
accounts: list | None = None,
|
||||
base_url: str = "http://127.0.0.1:8771",
|
||||
) -> dict:
|
||||
"""Registra o actualiza un perfil Chromium y sus cuentas en el catalogo osint_db.
|
||||
|
||||
Args:
|
||||
profile_dir: nombre del directorio real del perfil Chromium (ej. "Profile 1",
|
||||
"Default", "osint_01"). Es la PK del perfil; el upsert es idempotente sobre el.
|
||||
label: etiqueta humana del perfil (ej. "Persona Maria - OSINT"). "" para omitir.
|
||||
persona: identidad/alias ficticio asociado al perfil. "" para omitir.
|
||||
purpose: proposito de la investigacion (ej. "rastreo cuentas falsas"). "" para omitir.
|
||||
note_path: ruta (rel al vault) de la nota OSINT ligada al perfil. "" para omitir.
|
||||
tags: lista de strings de etiquetas (ej. ["osint", "sock-puppet"]). None -> [].
|
||||
notes: notas libres sobre el perfil. "" para omitir.
|
||||
user_data_dir: directorio user-data-dir de Chromium si NO es el default del wrapper.
|
||||
"" -> el perfil hereda el default chromium-cdp al abrirlo.
|
||||
status: estado del perfil (active|archived|burned...). Default "active".
|
||||
accounts: lista de dicts de cuentas a registrar, cada uno
|
||||
{service, identity, secret_ref?, role?, status?, notes?}. None -> sin cuentas.
|
||||
`secret_ref` es una REFERENCIA al secreto (ej. "pass show osint/p1/gmail"),
|
||||
NUNCA el password en claro.
|
||||
base_url: base del service osint_db. Default http://127.0.0.1:8771.
|
||||
|
||||
Returns:
|
||||
Caso ok: {"status":"ok", "profile_dir": str, "accounts": int (cuentas registradas
|
||||
con exito), "account_errors": list (errores por cuenta, vacia si todo OK)}.
|
||||
Caso error (fallo del POST del perfil): {"status":"error", "error": str}.
|
||||
"""
|
||||
try:
|
||||
profile_payload: dict = {"profile_dir": profile_dir, "status": status}
|
||||
if label:
|
||||
profile_payload["label"] = label
|
||||
if persona:
|
||||
profile_payload["persona"] = persona
|
||||
if purpose:
|
||||
profile_payload["purpose"] = purpose
|
||||
if note_path:
|
||||
profile_payload["note_path"] = note_path
|
||||
if tags:
|
||||
profile_payload["tags"] = list(tags)
|
||||
if notes:
|
||||
profile_payload["notes"] = notes
|
||||
if user_data_dir:
|
||||
profile_payload["user_data_dir"] = user_data_dir
|
||||
|
||||
resp = post_json(base_url, "/api/browser-profile", profile_payload)
|
||||
if resp.get("status") != "ok":
|
||||
return {
|
||||
"status": "error",
|
||||
"error": resp.get("error", f"el service rechazo el perfil: {resp}"),
|
||||
}
|
||||
|
||||
registered_accounts = 0
|
||||
account_errors: list = []
|
||||
for acc in accounts or []:
|
||||
if not isinstance(acc, dict) or not acc.get("service") or not acc.get("identity"):
|
||||
account_errors.append(
|
||||
{"account": acc, "error": "cuenta requiere al menos {service, identity}"}
|
||||
)
|
||||
continue
|
||||
acc_payload = {"profile_dir": profile_dir}
|
||||
for key in ("service", "identity", "secret_ref", "role", "status", "notes"):
|
||||
if acc.get(key):
|
||||
acc_payload[key] = acc[key]
|
||||
acc_resp = post_json(base_url, "/api/browser-profile/account", acc_payload)
|
||||
if acc_resp.get("status") == "ok":
|
||||
registered_accounts += 1
|
||||
else:
|
||||
account_errors.append(
|
||||
{
|
||||
"account": {"service": acc.get("service"), "identity": acc.get("identity")},
|
||||
"error": acc_resp.get("error", str(acc_resp)),
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
"profile_dir": profile_dir,
|
||||
"accounts": registered_accounts,
|
||||
"account_errors": account_errors,
|
||||
}
|
||||
except Exception as e: # noqa: BLE001 - contrato: nunca lanzar
|
||||
return {"status": "error", "error": f"{type(e).__name__}: {e}"}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Smoke contra un puerto muerto: ejercita la degradacion graceful (service inaccesible).
|
||||
res = browser_profile_register(
|
||||
"Profile 1",
|
||||
label="Persona Maria - OSINT",
|
||||
persona="maria_ficticia",
|
||||
purpose="rastreo cuentas falsas",
|
||||
tags=["osint", "sock-puppet"],
|
||||
accounts=[{"service": "gmail", "identity": "maria@example.com",
|
||||
"secret_ref": "pass show osint/p1/gmail"}],
|
||||
base_url="http://127.0.0.1:1",
|
||||
)
|
||||
assert res["status"] == "error", res
|
||||
print("browser_profile_register smoke OK (service caido -> status error)")
|
||||
print(f" {res}")
|
||||
Reference in New Issue
Block a user