mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-25 04:20:16 +00:00
132 lines
3.4 KiB
Go
132 lines
3.4 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-rod/rod/lib/proto"
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/store"
|
|
"github.com/nextlevelbuilder/goclaw/pkg/browser"
|
|
)
|
|
|
|
type storeBrowserCookieProvider struct {
|
|
cookies store.BrowserCookieStore
|
|
}
|
|
|
|
func newStoreBrowserCookieProvider(cookies store.BrowserCookieStore) browser.CookieProvider {
|
|
if cookies == nil {
|
|
return nil
|
|
}
|
|
return &storeBrowserCookieProvider{cookies: cookies}
|
|
}
|
|
|
|
func (p *storeBrowserCookieProvider) CookiesForURL(ctx context.Context, scope browser.BrowserScope, targetURL string) ([]*proto.NetworkCookieParam, error) {
|
|
u, err := url.Parse(targetURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse target url: %w", err)
|
|
}
|
|
if u.Scheme != "http" && u.Scheme != "https" {
|
|
return nil, nil
|
|
}
|
|
storeScope, err := browserScopeToCookieScope(scope)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cookies, err := p.cookies.List(ctx, storeScope, store.BrowserCookieFilter{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
host := strings.ToLower(u.Hostname())
|
|
path := u.EscapedPath()
|
|
if path == "" {
|
|
path = "/"
|
|
}
|
|
now := time.Now().UTC()
|
|
params := make([]*proto.NetworkCookieParam, 0, len(cookies))
|
|
for _, c := range cookies {
|
|
if !browserCookieMatchesURL(c, host, path, now) {
|
|
continue
|
|
}
|
|
param := &proto.NetworkCookieParam{
|
|
Name: c.Name,
|
|
Value: c.Value,
|
|
URL: targetURL,
|
|
Path: c.Path,
|
|
Secure: c.Secure,
|
|
HTTPOnly: c.HTTPOnly,
|
|
SameSite: browserCookieSameSite(c.SameSite),
|
|
}
|
|
if strings.HasPrefix(c.Domain, ".") {
|
|
param.Domain = c.Domain
|
|
}
|
|
if c.ExpiresAt != nil {
|
|
param.Expires = proto.TimeSinceEpoch(float64(c.ExpiresAt.Unix()))
|
|
}
|
|
params = append(params, param)
|
|
}
|
|
return params, nil
|
|
}
|
|
|
|
func browserScopeToCookieScope(scope browser.BrowserScope) (store.BrowserCookieScope, error) {
|
|
tenantID := store.MasterTenantID
|
|
if strings.TrimSpace(scope.TenantID) != "" {
|
|
parsed, err := uuid.Parse(strings.TrimSpace(scope.TenantID))
|
|
if err != nil {
|
|
return store.BrowserCookieScope{}, fmt.Errorf("invalid browser tenant scope: %w", err)
|
|
}
|
|
tenantID = parsed
|
|
}
|
|
cookieScope := store.BrowserCookieScope{
|
|
TenantID: tenantID,
|
|
UserID: strings.TrimSpace(scope.UserID),
|
|
AgentID: strings.TrimSpace(scope.AgentID),
|
|
}
|
|
if err := cookieScope.Validate(); err != nil {
|
|
return store.BrowserCookieScope{}, err
|
|
}
|
|
return cookieScope, nil
|
|
}
|
|
|
|
func browserCookieMatchesURL(c store.BrowserCookie, host, requestPath string, now time.Time) bool {
|
|
domain := strings.ToLower(strings.TrimSpace(c.Domain))
|
|
if domain == "" {
|
|
return false
|
|
}
|
|
if c.ExpiresAt != nil && !c.ExpiresAt.After(now) {
|
|
return false
|
|
}
|
|
hostOnly := !strings.HasPrefix(domain, ".")
|
|
matchDomain := strings.TrimPrefix(domain, ".")
|
|
if hostOnly {
|
|
if host != matchDomain {
|
|
return false
|
|
}
|
|
} else if host != matchDomain && !strings.HasSuffix(host, "."+matchDomain) {
|
|
return false
|
|
}
|
|
cookiePath := c.Path
|
|
if cookiePath == "" {
|
|
cookiePath = "/"
|
|
}
|
|
return requestPath == cookiePath || strings.HasPrefix(requestPath, strings.TrimRight(cookiePath, "/")+"/")
|
|
}
|
|
|
|
func browserCookieSameSite(value string) proto.NetworkCookieSameSite {
|
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
|
case "strict":
|
|
return proto.NetworkCookieSameSiteStrict
|
|
case "lax":
|
|
return proto.NetworkCookieSameSiteLax
|
|
case "none", "no_restriction", "no-restriction":
|
|
return proto.NetworkCookieSameSiteNone
|
|
default:
|
|
return ""
|
|
}
|
|
}
|