5b10b419a2
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
99 lines
3.1 KiB
Go
99 lines
3.1 KiB
Go
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
|
|
}
|