feat(browser): auto-commit con 44 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package browser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CdpStorageState agrupa cookies, localStorage y sessionStorage capturados de una
|
||||
// pestaña activa.
|
||||
type CdpStorageState struct {
|
||||
Cookies []map[string]any `json:"cookies"`
|
||||
LocalStorage map[string]string `json:"localStorage"`
|
||||
SessionStorage map[string]string `json:"sessionStorage"`
|
||||
}
|
||||
|
||||
// readWebStorage lee window.<store> (localStorage|sessionStorage) como mapa. Si el
|
||||
// origen no permite acceso (about:blank, chrome://) devuelve un mapa vacío.
|
||||
func readWebStorage(c *CDPConn, store string) map[string]string {
|
||||
raw, err := CdpEvaluate(c, "JSON.stringify(Object.assign({}, window."+store+"))")
|
||||
if err != nil {
|
||||
return map[string]string{}
|
||||
}
|
||||
if raw == "" || raw == "undefined" || raw == "null" {
|
||||
return map[string]string{}
|
||||
}
|
||||
var m map[string]string
|
||||
if err := json.Unmarshal([]byte(raw), &m); err != nil {
|
||||
return map[string]string{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// cookieDomainMatchesHost indica si una cookie con `domain` aplica al `host` dado.
|
||||
// Cubre el caso de dominios con punto inicial (".example.com") y subdominios.
|
||||
func cookieDomainMatchesHost(domain, host string) bool {
|
||||
if domain == "" || host == "" {
|
||||
return false
|
||||
}
|
||||
d := strings.TrimPrefix(domain, ".")
|
||||
return host == d || strings.HasSuffix(host, "."+d)
|
||||
}
|
||||
|
||||
// storageStateToMaps convierte []any (respuesta CDP) a []map[string]any.
|
||||
func storageStateToMaps(raw []any) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(raw))
|
||||
for _, item := range raw {
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
out = append(out, m)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// CdpSaveStorageState captura cookies y localStorage de la pagina actual y los
|
||||
// escribe como JSON a outPath. Permite restaurar la sesion autenticada en
|
||||
// ejecuciones posteriores sin repetir el login.
|
||||
func CdpSaveStorageState(c *CDPConn, outPath string) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("cdp save storage state: conexion nula")
|
||||
}
|
||||
if outPath == "" {
|
||||
return fmt.Errorf("cdp save storage state: outPath vacio")
|
||||
}
|
||||
|
||||
// Habilitar dominio Network para acceder a las cookies
|
||||
if _, err := c.sendCDP("Network.enable", nil); err != nil {
|
||||
return fmt.Errorf("cdp save storage state: Network.enable: %w", err)
|
||||
}
|
||||
|
||||
// Obtener todas las cookies del perfil
|
||||
res, err := c.sendCDP("Network.getAllCookies", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cdp save storage state: getAllCookies: %w", err)
|
||||
}
|
||||
|
||||
var cookies []map[string]any
|
||||
if rawCookies, ok := res["cookies"].([]any); ok {
|
||||
cookies = storageStateToMaps(rawCookies)
|
||||
} else {
|
||||
cookies = []map[string]any{}
|
||||
}
|
||||
|
||||
// Filtrar al origen actual: Network.getAllCookies devuelve cookies de TODOS
|
||||
// los dominios del perfil. Para guardar "la sesión de ESTE sitio" solo
|
||||
// conservamos las que aplican al host cargado, evitando arrastrar cookies de
|
||||
// otros sitios visitados en la misma sesión del navegador.
|
||||
if host, herr := CdpEvaluate(c, "location.hostname"); herr == nil {
|
||||
host = strings.TrimSpace(host)
|
||||
if host != "" && host != "undefined" {
|
||||
filtered := make([]map[string]any, 0, len(cookies))
|
||||
for _, ck := range cookies {
|
||||
dom, _ := ck["domain"].(string)
|
||||
if cookieDomainMatchesHost(dom, host) {
|
||||
filtered = append(filtered, ck)
|
||||
}
|
||||
}
|
||||
cookies = filtered
|
||||
}
|
||||
}
|
||||
|
||||
// Capturar localStorage y sessionStorage del origen actualmente cargado.
|
||||
state := CdpStorageState{
|
||||
Cookies: cookies,
|
||||
LocalStorage: readWebStorage(c, "localStorage"),
|
||||
SessionStorage: readWebStorage(c, "sessionStorage"),
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(state, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("cdp save storage state: marshal: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outPath, data, 0644); err != nil {
|
||||
return fmt.Errorf("cdp save storage state: escribir archivo: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user