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,98 @@
|
||||
package browser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// jsQuote serializa s como literal string JavaScript con comillas dobles y
|
||||
// caracteres escapados correctamente. Usa json.Marshal internamente para
|
||||
// reutilizar el mismo escapado que JSON (compatible con JS).
|
||||
func jsQuote(s string) string {
|
||||
b, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
// Fallback seguro: comillas dobles escapando backslash y comilla doble
|
||||
return fmt.Sprintf("%q", s)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// CdpLoadStorageState lee el JSON generado por CdpSaveStorageState y restaura
|
||||
// cookies y localStorage en la pestaña activa. Permite retomar una sesion
|
||||
// autenticada sin repetir el login.
|
||||
//
|
||||
// CRITICO: el localStorage es por-origen. Antes de llamar a esta funcion hay
|
||||
// que haber navegado al origen correcto (CdpNavigate al dominio). Orden
|
||||
// correcto: navegar -> CdpLoadStorageState -> recargar pagina.
|
||||
func CdpLoadStorageState(c *CDPConn, inPath string) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("cdp load storage state: conexion nula")
|
||||
}
|
||||
if inPath == "" {
|
||||
return fmt.Errorf("cdp load storage state: inPath vacio")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(inPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cdp load storage state: leer archivo: %w", err)
|
||||
}
|
||||
|
||||
var state CdpStorageState
|
||||
if err := json.Unmarshal(data, &state); err != nil {
|
||||
return fmt.Errorf("cdp load storage state: unmarshal: %w", err)
|
||||
}
|
||||
|
||||
// Habilitar dominio Network para manipular cookies
|
||||
if _, err := c.sendCDP("Network.enable", nil); err != nil {
|
||||
return fmt.Errorf("cdp load storage state: Network.enable: %w", err)
|
||||
}
|
||||
|
||||
// Restaurar cookies. Network.setCookies aplica de forma fiable las cookies
|
||||
// (sobre todo httpOnly y de sesión) cuando cada una lleva el campo `url`: de
|
||||
// ahí deriva scheme y scope. getAllCookies no lo incluye, así que lo
|
||||
// sintetizamos a partir de domain/secure/path cuando falta.
|
||||
if len(state.Cookies) > 0 {
|
||||
for _, ck := range state.Cookies {
|
||||
if _, has := ck["url"]; has {
|
||||
continue
|
||||
}
|
||||
dom, _ := ck["domain"].(string)
|
||||
dom = strings.TrimPrefix(dom, ".")
|
||||
if dom == "" {
|
||||
continue
|
||||
}
|
||||
scheme := "http"
|
||||
if sec, _ := ck["secure"].(bool); sec {
|
||||
scheme = "https"
|
||||
}
|
||||
path, _ := ck["path"].(string)
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
ck["url"] = scheme + "://" + dom + path
|
||||
}
|
||||
if _, err := c.sendCDP("Network.setCookies", map[string]any{
|
||||
"cookies": state.Cookies,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("cdp load storage state: setCookies: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Restaurar localStorage y sessionStorage — setItem por cada par clave/valor
|
||||
for k, v := range state.LocalStorage {
|
||||
expr := fmt.Sprintf("window.localStorage.setItem(%s, %s)", jsQuote(k), jsQuote(v))
|
||||
if _, err := CdpEvaluate(c, expr); err != nil {
|
||||
return fmt.Errorf("cdp load storage state: localStorage setItem %q: %w", k, err)
|
||||
}
|
||||
}
|
||||
for k, v := range state.SessionStorage {
|
||||
expr := fmt.Sprintf("window.sessionStorage.setItem(%s, %s)", jsQuote(k), jsQuote(v))
|
||||
if _, err := CdpEvaluate(c, expr); err != nil {
|
||||
return fmt.Errorf("cdp load storage state: sessionStorage setItem %q: %w", k, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user