fix cookie -- not working
This commit is contained in:
@@ -6,7 +6,7 @@ ASSET_MANIFEST_PATH=web/dist/manifest.json
|
|||||||
# Public shop URL and upstream proxy target
|
# Public shop URL and upstream proxy target
|
||||||
PRESTASHOP_BASE_URL=http://localhost
|
PRESTASHOP_BASE_URL=http://localhost
|
||||||
PRESTASHOP_PROXY_TARGET=http://localhost
|
PRESTASHOP_PROXY_TARGET=http://localhost
|
||||||
PRESTASHOP_VERSION=1.7.3
|
PRESTASHOP_VERSION=1.7.2
|
||||||
|
|
||||||
# Cookie settings
|
# Cookie settings
|
||||||
# Optional explicit override. If omitted, the app derives the cookie name from
|
# Optional explicit override. If omitted, the app derives the cookie name from
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ func run() error {
|
|||||||
customerService := pscustomer.NewService(prestaDB, cfg.PrestaShopTablePrefix)
|
customerService := pscustomer.NewService(prestaDB, cfg.PrestaShopTablePrefix)
|
||||||
cartService := pscart.NewService(prestaDB, cfg.PrestaShopTablePrefix)
|
cartService := pscart.NewService(prestaDB, cfg.PrestaShopTablePrefix)
|
||||||
routeService := psroutes.NewService(prestaDB, cfg.PrestaShopTablePrefix)
|
routeService := psroutes.NewService(prestaDB, cfg.PrestaShopTablePrefix)
|
||||||
sessionService := pssession.NewService(prestaDB, cfg.PrestaShopTablePrefix)
|
sessionService := pssession.NewService(prestaDB, cfg.PrestaShopTablePrefix, cfg.PrestaShopVersion)
|
||||||
productRoute, err := routeService.LoadProductRoute(context.Background())
|
productRoute, err := routeService.LoadProductRoute(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("load product route rule: %w", err)
|
return fmt.Errorf("load product route rule: %w", err)
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
pscatalog "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/catalog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAssignMarketSwitchLinksUsesCountryCurrencyID(t *testing.T) {
|
||||||
|
req := httptest.NewRequest("GET", "https://shop.example.com/pl/product/test", nil)
|
||||||
|
locale := pscatalog.HeaderLocaleData{
|
||||||
|
Countries: []pscatalog.LocaleOption{
|
||||||
|
{ID: 36, Code: "PL", CurrencyID: 6, Label: "Polska PLN"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assignMarketSwitchLinks(req, &locale)
|
||||||
|
|
||||||
|
if got := locale.Countries[0].URL; got != "/pl/product/test?market=36%3APL%3A6" {
|
||||||
|
t.Fatalf("market url = %q, want %q", got, "/pl/product/test?market=36%3APL%3A6")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
pscookie "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cookie"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApplyRequestMarketUsesSelectedCountryCurrency(t *testing.T) {
|
||||||
|
session := &pscookie.SessionContext{
|
||||||
|
Values: map[string]string{
|
||||||
|
"date_add": "2026-05-12 10:28:57",
|
||||||
|
"id_guest": "10",
|
||||||
|
"id_connections": "11",
|
||||||
|
"id_lang": "2",
|
||||||
|
"id_language": "2",
|
||||||
|
"id_currency": "1",
|
||||||
|
"id_shop": "1",
|
||||||
|
"id_cart": "55",
|
||||||
|
"checksum": "old",
|
||||||
|
},
|
||||||
|
OrderedKeys: []string{"date_add", "id_lang", "id_language", "id_currency", "id_guest", "id_connections", "id_shop", "id_cart", "checksum"},
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRequestMarket(session, marketSelection{
|
||||||
|
CountryID: 36,
|
||||||
|
CountryISO: "PL",
|
||||||
|
CurrencyID: 6,
|
||||||
|
})
|
||||||
|
|
||||||
|
if got := session.Values["iso_code_country"]; got != "PL" {
|
||||||
|
t.Fatalf("iso_code_country = %q, want %q", got, "PL")
|
||||||
|
}
|
||||||
|
if _, ok := session.Values["id_country"]; ok {
|
||||||
|
t.Fatalf("id_country should not be persisted in anonymous market cookie")
|
||||||
|
}
|
||||||
|
if got := session.Values["id_currency"]; got != "6" {
|
||||||
|
t.Fatalf("id_currency = %q, want %q", got, "6")
|
||||||
|
}
|
||||||
|
if session.CurrencyID == nil || *session.CurrencyID != 6 {
|
||||||
|
t.Fatalf("CurrencyID = %v, want 6", session.CurrencyID)
|
||||||
|
}
|
||||||
|
if _, ok := session.Values["id_shop"]; ok {
|
||||||
|
t.Fatalf("id_shop should not be persisted in anonymous market cookie")
|
||||||
|
}
|
||||||
|
if _, ok := session.Values["id_cart"]; ok {
|
||||||
|
t.Fatalf("id_cart should not be persisted in anonymous market cookie")
|
||||||
|
}
|
||||||
|
wantOrder := []string{"date_add", "id_lang", "id_language", "iso_code_country", "id_currency", "id_guest", "id_connections", "checksum"}
|
||||||
|
for i, key := range wantOrder {
|
||||||
|
if i >= len(session.OrderedKeys) || session.OrderedKeys[i] != key {
|
||||||
|
t.Fatalf("OrderedKeys[%d] = %q, want %q; full=%v", i, session.OrderedKeys[i], key, session.OrderedKeys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -21,6 +18,14 @@ type AnonymousSessionInitializer interface {
|
|||||||
NewAnonymous(ctx context.Context, req *http.Request, cookieName string) (*pscookie.SessionContext, error)
|
NewAnonymous(ctx context.Context, req *http.Request, cookieName string) (*pscookie.SessionContext, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionExpiryRefresher interface {
|
||||||
|
RefreshExpiry(ctx context.Context, session *pscookie.SessionContext) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionCookieNameResolver interface {
|
||||||
|
ResolveCookieName(ctx context.Context, req *http.Request) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
type LanguageResolver interface {
|
type LanguageResolver interface {
|
||||||
ResolveLanguageID(ctx context.Context, req *http.Request, fallback int64) int64
|
ResolveLanguageID(ctx context.Context, req *http.Request, fallback int64) int64
|
||||||
}
|
}
|
||||||
@@ -31,11 +36,22 @@ type ProductRouteMatcher interface {
|
|||||||
|
|
||||||
func Session(cfg psconfig.Config, codec pscookie.Codec, initializer AnonymousSessionInitializer, languageResolver LanguageResolver, matcher ProductRouteMatcher) echo.MiddlewareFunc {
|
func Session(cfg psconfig.Config, codec pscookie.Codec, initializer AnonymousSessionInitializer, languageResolver LanguageResolver, matcher ProductRouteMatcher) echo.MiddlewareFunc {
|
||||||
ownership := cfg.ParseRouteOwnership()
|
ownership := cfg.ParseRouteOwnership()
|
||||||
|
expiryRefresher, _ := initializer.(SessionExpiryRefresher)
|
||||||
|
cookieNameResolver, _ := initializer.(SessionCookieNameResolver)
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
ownedRoute := ownsProductRoute(ownership.ProductPrefixes, c.Request().URL.Path, matcher)
|
ownedRoute := ownsProductRoute(ownership.ProductPrefixes, c.Request().URL.Path, matcher)
|
||||||
configuredCookieName := cfg.DeriveCookieName(requestCookieHost(c.Request()))
|
configuredCookieName := cfg.DeriveCookieName(requestCookieHost(c.Request()))
|
||||||
|
if cookieNameResolver != nil {
|
||||||
|
resolvedCookieName, err := cookieNameResolver.ResolveCookieName(c.Request().Context(), c.Request())
|
||||||
|
if err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("prestashop cookie name resolution failed: %v", err))
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(resolvedCookieName) != "" {
|
||||||
|
configuredCookieName = resolvedCookieName
|
||||||
|
}
|
||||||
|
}
|
||||||
cookieName, rawCookie := findPrestaShopCookie(c.Request(), configuredCookieName)
|
cookieName, rawCookie := findPrestaShopCookie(c.Request(), configuredCookieName)
|
||||||
if cookieName == "" {
|
if cookieName == "" {
|
||||||
cookieName = configuredCookieName
|
cookieName = configuredCookieName
|
||||||
@@ -66,12 +82,17 @@ func Session(cfg psconfig.Config, codec pscookie.Codec, initializer AnonymousSes
|
|||||||
applyRequestMarket(session, requestMarketSelection(c.Request()))
|
applyRequestMarket(session, requestMarketSelection(c.Request()))
|
||||||
}
|
}
|
||||||
if ownedRoute && shouldSetSessionCookie(rawCookie, session) {
|
if ownedRoute && shouldSetSessionCookie(rawCookie, session) {
|
||||||
|
if expiryRefresher != nil {
|
||||||
|
if err := expiryRefresher.RefreshExpiry(c.Request().Context(), session); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("prestashop session expiry refresh failed: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
encoded, err := codec.Encode(session)
|
encoded, err := codec.Encode(session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "prestashop cookie encode failed")
|
return echo.NewHTTPError(http.StatusInternalServerError, "prestashop cookie encode failed")
|
||||||
}
|
}
|
||||||
session.RawCookie = encoded
|
session.RawCookie = encoded
|
||||||
setPrestaShopCookie(c.Request(), c.Response(), ownership.ProductPrefixes, cookieName, encoded)
|
setPrestaShopCookie(c.Request(), c.Response(), session, cookieName, encoded)
|
||||||
if redirectURL, ok := clearMarketSelectionURL(c.Request()); ok {
|
if redirectURL, ok := clearMarketSelectionURL(c.Request()); ok {
|
||||||
return c.Redirect(http.StatusSeeOther, redirectURL)
|
return c.Redirect(http.StatusSeeOther, redirectURL)
|
||||||
}
|
}
|
||||||
@@ -213,19 +234,18 @@ func applyRequestMarket(session *pscookie.SessionContext, selection marketSelect
|
|||||||
|
|
||||||
session.CurrencyID = int64Ptr(selection.CurrencyID)
|
session.CurrencyID = int64Ptr(selection.CurrencyID)
|
||||||
session.Values["iso_code_country"] = selection.CountryISO
|
session.Values["iso_code_country"] = selection.CountryISO
|
||||||
if selection.CountryID > 0 {
|
|
||||||
session.Values["id_country"] = strconv.FormatInt(selection.CountryID, 10)
|
|
||||||
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "id_country", 5)
|
|
||||||
}
|
|
||||||
session.Values["id_currency"] = strconv.FormatInt(selection.CurrencyID, 10)
|
session.Values["id_currency"] = strconv.FormatInt(selection.CurrencyID, 10)
|
||||||
|
delete(session.Values, "id_country")
|
||||||
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "iso_code_country", 4)
|
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "iso_code_country", 4)
|
||||||
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "id_currency", 6)
|
session.OrderedKeys = removeOrderedKey(session.OrderedKeys, "id_country")
|
||||||
|
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "id_currency", 5)
|
||||||
|
|
||||||
if !session.IsLoggedIn {
|
if !session.IsLoggedIn {
|
||||||
if checksum := anonymousSessionChecksum(session, sessionLanguageID(session)); checksum != "" {
|
trimAnonymousCookieValues(session)
|
||||||
session.Values["checksum"] = checksum
|
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "id_guest", 6)
|
||||||
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "checksum", len(session.OrderedKeys))
|
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "id_connections", 7)
|
||||||
}
|
session.OrderedKeys = removeOrderedKey(session.OrderedKeys, "checksum")
|
||||||
|
session.OrderedKeys = ensureOrderedKey(session.OrderedKeys, "checksum", len(session.OrderedKeys))
|
||||||
}
|
}
|
||||||
|
|
||||||
session.Plaintext = ""
|
session.Plaintext = ""
|
||||||
@@ -284,6 +304,46 @@ func ensureOrderedKey(keys []string, key string, index int) []string {
|
|||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeOrderedKey(keys []string, key string) []string {
|
||||||
|
for i, existing := range keys {
|
||||||
|
if existing == key {
|
||||||
|
return append(keys[:i], keys[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimAnonymousCookieValues(session *pscookie.SessionContext) {
|
||||||
|
if session == nil || session.Values == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed := map[string]struct{}{
|
||||||
|
"date_add": {},
|
||||||
|
"id_lang": {},
|
||||||
|
"id_language": {},
|
||||||
|
"iso_code_country": {},
|
||||||
|
"id_currency": {},
|
||||||
|
"id_guest": {},
|
||||||
|
"id_connections": {},
|
||||||
|
"checksum": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for key := range session.Values {
|
||||||
|
if _, ok := allowed[key]; !ok {
|
||||||
|
delete(session.Values, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered := make([]string, 0, len(session.OrderedKeys))
|
||||||
|
for _, key := range session.OrderedKeys {
|
||||||
|
if _, ok := allowed[key]; ok {
|
||||||
|
filtered = append(filtered, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.OrderedKeys = filtered
|
||||||
|
}
|
||||||
|
|
||||||
func int64Ptr(value int64) *int64 {
|
func int64Ptr(value int64) *int64 {
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -353,53 +413,17 @@ func clearMarketSelectionURL(req *http.Request) (string, bool) {
|
|||||||
return cleanPath, true
|
return cleanPath, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPrestaShopCookie(req *http.Request, res *echo.Response, ownedPrefixes []string, name, value string) {
|
func setPrestaShopCookie(req *http.Request, res *echo.Response, session *pscookie.SessionContext, name, value string) {
|
||||||
http.SetCookie(res.Writer, &http.Cookie{
|
maxAge := 1
|
||||||
Name: name,
|
if session != nil && session.ExpiresAt != nil {
|
||||||
Value: value,
|
maxAge = int(session.ExpiresAt.UTC().Unix())
|
||||||
Path: requestCookiePath(req.URL.Path, ownedPrefixes),
|
|
||||||
Domain: requestCookieDomain(req),
|
|
||||||
Secure: requestCookieSecure(req),
|
|
||||||
HttpOnly: true,
|
|
||||||
SameSite: http.SameSiteLaxMode,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestCookiePath(requestPath string, ownedPrefixes []string) string {
|
|
||||||
for _, prefix := range ownedPrefixes {
|
|
||||||
if prefix == "" || !strings.HasPrefix(requestPath, prefix) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
base := path.Clean(strings.TrimSuffix(prefix, "/"))
|
|
||||||
if base == "." || base == "/" {
|
|
||||||
return "/"
|
|
||||||
}
|
|
||||||
parent := path.Dir(base)
|
|
||||||
if parent == "." {
|
|
||||||
return "/"
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(parent, "/") {
|
|
||||||
parent += "/"
|
|
||||||
}
|
|
||||||
return parent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "/"
|
header := fmt.Sprintf("%s=%s; path=/; max-age=%d; HttpOnly; SameSite=Lax", name, value, maxAge)
|
||||||
}
|
if requestCookieSecure(req) {
|
||||||
|
header += "; Secure"
|
||||||
func requestCookieDomain(req *http.Request) string {
|
|
||||||
host := requestCookieHost(req)
|
|
||||||
if host == "" {
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
if parsed, err := url.Parse("http://" + host); err == nil {
|
res.Header().Add(echo.HeaderSetCookie, header)
|
||||||
host = parsed.Hostname()
|
|
||||||
}
|
|
||||||
host = strings.TrimSpace(strings.TrimPrefix(host, "."))
|
|
||||||
if host == "" || strings.EqualFold(host, "localhost") || net.ParseIP(host) != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return host
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestCookieHost(req *http.Request) string {
|
func requestCookieHost(req *http.Request) string {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
|
||||||
|
pscookie "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cookie"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetPrestaShopCookiePersistsExpiry(t *testing.T) {
|
||||||
|
e := echo.New()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "https://shop.example.com/product/test", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
res := e.NewContext(req, rec).Response()
|
||||||
|
expiresAt := time.Now().UTC().Add(4 * time.Hour).Truncate(time.Second)
|
||||||
|
|
||||||
|
setPrestaShopCookie(req, res, &pscookie.SessionContext{
|
||||||
|
ExpiresAt: &expiresAt,
|
||||||
|
}, "PrestaShop-test", "value")
|
||||||
|
|
||||||
|
setCookie := rec.Header().Get("Set-Cookie")
|
||||||
|
if !strings.Contains(setCookie, "max-age=") {
|
||||||
|
t.Fatalf("Set-Cookie missing max-age: %q", setCookie)
|
||||||
|
}
|
||||||
|
if strings.Contains(setCookie, "Expires=") {
|
||||||
|
t.Fatalf("Set-Cookie should not include Expires: %q", setCookie)
|
||||||
|
}
|
||||||
|
if !strings.Contains(setCookie, "path=/") {
|
||||||
|
t.Fatalf("Set-Cookie missing path=/: %q", setCookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -391,6 +391,9 @@ ORDER BY cl.name ASC
|
|||||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(countryQuery), countryArgs...).Scan(&locale.Countries).Error; err != nil {
|
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(countryQuery), countryArgs...).Scan(&locale.Countries).Error; err != nil {
|
||||||
return HeaderLocaleData{}, err
|
return HeaderLocaleData{}, err
|
||||||
}
|
}
|
||||||
|
for i := range locale.Countries {
|
||||||
|
locale.Countries[i] = formatCountryLocaleOption(locale.Countries[i])
|
||||||
|
}
|
||||||
|
|
||||||
locale.CurrentLanguage = pickLocaleOptionByID(locale.Languages, languageID)
|
locale.CurrentLanguage = pickLocaleOptionByID(locale.Languages, languageID)
|
||||||
locale.CurrentCountry = pickLocaleOptionByCode(locale.Countries, countryISO)
|
locale.CurrentCountry = pickLocaleOptionByCode(locale.Countries, countryISO)
|
||||||
@@ -499,3 +502,17 @@ func pickLocaleOptionByCode(options []LocaleOption, code string) LocaleOption {
|
|||||||
}
|
}
|
||||||
return LocaleOption{Code: code, Label: code, Meta: code}
|
return LocaleOption{Code: code, Label: code, Meta: code}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatCountryLocaleOption(option LocaleOption) LocaleOption {
|
||||||
|
label := strings.TrimSpace(option.Label)
|
||||||
|
currencyCode := strings.TrimSpace(option.Meta)
|
||||||
|
if idx := strings.IndexByte(currencyCode, ' '); idx >= 0 {
|
||||||
|
currencyCode = currencyCode[:idx]
|
||||||
|
}
|
||||||
|
currencyCode = strings.TrimSpace(currencyCode)
|
||||||
|
if label == "" || currencyCode == "" {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
option.Label = label + " " + currencyCode
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package catalog
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFormatCountryLocaleOptionAddsCurrencyCodeToLabel(t *testing.T) {
|
||||||
|
option := LocaleOption{
|
||||||
|
Label: "Polska",
|
||||||
|
Meta: "PLN zl",
|
||||||
|
}
|
||||||
|
|
||||||
|
got := formatCountryLocaleOption(option)
|
||||||
|
if got.Label != "Polska PLN" {
|
||||||
|
t.Fatalf("formatCountryLocaleOption().Label = %q, want %q", got.Label, "Polska PLN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatCountryLocaleOptionFallsBackWithoutMeta(t *testing.T) {
|
||||||
|
option := LocaleOption{
|
||||||
|
Label: "Polska",
|
||||||
|
}
|
||||||
|
|
||||||
|
got := formatCountryLocaleOption(option)
|
||||||
|
if got.Label != "Polska" {
|
||||||
|
t.Fatalf("formatCountryLocaleOption().Label = %q, want %q", got.Label, "Polska")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -140,15 +140,15 @@ func (c Config) DeriveCookieName(host string) string {
|
|||||||
return c.PrestaShopCookieName
|
return c.PrestaShopCookieName
|
||||||
}
|
}
|
||||||
|
|
||||||
domain := normalizedCookieDomain(host)
|
domain := fallbackCookieHashDomain(host)
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
domain = normalizedCookieDomain(c.PrestaShopBaseURL)
|
domain = fallbackCookieHashDomain(c.PrestaShopBaseURL)
|
||||||
}
|
}
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
domain = normalizedCookieDomain(c.PrestaShopProxyTarget)
|
domain = fallbackCookieHashDomain(c.PrestaShopProxyTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
sum := md5.Sum([]byte(c.PrestaShopVersion + "PrestaShop" + domain))
|
sum := md5.Sum([]byte(c.PrestaShopVersion + "ps-s1" + domain))
|
||||||
return fmt.Sprintf("PrestaShop-%x", sum)
|
return fmt.Sprintf("PrestaShop-%x", sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +256,17 @@ func normalizedCookieDomain(input string) string {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fallbackCookieHashDomain(input string) string {
|
||||||
|
value := normalizedCookieDomain(input)
|
||||||
|
if value == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if net.ParseIP(value) != nil || !strings.Contains(value, ".") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
func dbDSNFromEnv() string {
|
func dbDSNFromEnv() string {
|
||||||
return mysqlDSN(
|
return mysqlDSN(
|
||||||
firstNonEmpty(os.Getenv("PRESTASHOP_DB_HOST"), os.Getenv("DB_HOST")),
|
firstNonEmpty(os.Getenv("PRESTASHOP_DB_HOST"), os.Getenv("DB_HOST")),
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeriveCookieNameMatchesFallbackPrestashopRule(t *testing.T) {
|
||||||
|
cfg := Config{PrestaShopVersion: "1.7.3"}
|
||||||
|
|
||||||
|
got := cfg.DeriveCookieName("localhost")
|
||||||
|
sum := md5.Sum([]byte("1.7.3" + "ps-s1"))
|
||||||
|
want := fmt.Sprintf("PrestaShop-%x", sum)
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("DeriveCookieName() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"hash/crc32"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -99,7 +100,7 @@ func (c *nativeCodec) Encode(session *SessionContext) (string, error) {
|
|||||||
|
|
||||||
plaintext := session.Plaintext
|
plaintext := session.Plaintext
|
||||||
if plaintext == "" {
|
if plaintext == "" {
|
||||||
plaintext = serializeValues(session.Values, session.OrderedKeys)
|
plaintext = serializeCookieValues(session.Values, session.OrderedKeys, c.cfg.CookieIV)
|
||||||
}
|
}
|
||||||
return c.encryptInternal(plaintext)
|
return c.encryptInternal(plaintext)
|
||||||
}
|
}
|
||||||
@@ -321,6 +322,67 @@ func serializeValues(values map[string]string, orderedKeys []string) string {
|
|||||||
return strings.Join(pairs, fieldSeparator)
|
return strings.Join(pairs, fieldSeparator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serializeCookieValues(values map[string]string, orderedKeys []string, cookieIV string) string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := orderedValueKeys(values, orderedKeys, "checksum")
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
for _, key := range keys {
|
||||||
|
builder.WriteString(key)
|
||||||
|
builder.WriteString(pairSeparator)
|
||||||
|
builder.WriteString(values[key])
|
||||||
|
builder.WriteString(fieldSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum := crc32.ChecksumIEEE([]byte(cookieIV + builder.String()))
|
||||||
|
builder.WriteString("checksum")
|
||||||
|
builder.WriteString(pairSeparator)
|
||||||
|
builder.WriteString(fmt.Sprintf("%d", checksum))
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func orderedValueKeys(values map[string]string, orderedKeys []string, excluded ...string) []string {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
excludeSet := make(map[string]struct{}, len(excluded))
|
||||||
|
for _, key := range excluded {
|
||||||
|
excludeSet[key] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(values))
|
||||||
|
seen := map[string]struct{}{}
|
||||||
|
for _, key := range orderedKeys {
|
||||||
|
if _, skip := excludeSet[key]; skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := values[key]; ok {
|
||||||
|
keys = append(keys, key)
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extra := make([]string, 0)
|
||||||
|
for key := range values {
|
||||||
|
if _, skip := excludeSet[key]; skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[key]; !ok {
|
||||||
|
extra = append(extra, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(extra)
|
||||||
|
keys = append(keys, extra...)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
func int64Ptr(value string) *int64 {
|
func int64Ptr(value string) *int64 {
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package cookie
|
package cookie
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"hash/crc32"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testCookieKey = "def000008bf3d70e7012b7493c382d561e193218d0c74ab162fb0ea8029ce20e926531b4bcf0aaec9381152e6c161f198e06918b2d1aad67cc7cf40819a51ee328c63830"
|
testCookieKey = "def000008bf3d70e7012b7493c382d561e193218d0c74ab162fb0ea8029ce20e926531b4bcf0aaec9381152e6c161f198e06918b2d1aad67cc7cf40819a51ee328c63830"
|
||||||
@@ -66,3 +71,44 @@ func TestNativeCodecRoundTrip(t *testing.T) {
|
|||||||
t.Fatalf("plaintext mismatch after roundtrip\n got: %s\nwant: %s", redecoded.Plaintext, decoded.Plaintext)
|
t.Fatalf("plaintext mismatch after roundtrip\n got: %s\nwant: %s", redecoded.Plaintext, decoded.Plaintext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNativeCodecEncodeRecomputesPrestashopChecksum(t *testing.T) {
|
||||||
|
codec, err := NewCodec(Config{
|
||||||
|
CookieName: "PrestaShop-test",
|
||||||
|
CookieKey: testCookieKey,
|
||||||
|
CookieIV: "vfRFMV42",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewCodec() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, err := codec.Decode(testCookie)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Decode() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded.Values["iso_code_country"] = "PL"
|
||||||
|
decoded.Values["id_currency"] = "6"
|
||||||
|
decoded.Values["checksum"] = "stale"
|
||||||
|
decoded.Plaintext = ""
|
||||||
|
|
||||||
|
encoded, err := codec.Encode(decoded)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Encode() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
redecoded, err := codec.Decode(encoded)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Decode(encoded) error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs := strings.Split(redecoded.Plaintext, fieldSeparator)
|
||||||
|
if len(pairs) < 2 {
|
||||||
|
t.Fatalf("plaintext too short: %q", redecoded.Plaintext)
|
||||||
|
}
|
||||||
|
body := strings.Join(pairs[:len(pairs)-1], fieldSeparator) + fieldSeparator
|
||||||
|
wantChecksum := fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte("vfRFMV42"+body)))
|
||||||
|
if got := redecoded.Values["checksum"]; got != wantChecksum {
|
||||||
|
t.Fatalf("checksum = %q, want %q", got, wantChecksum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package session
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -16,8 +19,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
prefix string
|
prefix string
|
||||||
|
version string
|
||||||
}
|
}
|
||||||
|
|
||||||
type defaults struct {
|
type defaults struct {
|
||||||
@@ -26,10 +30,11 @@ type defaults struct {
|
|||||||
ShopID int64
|
ShopID int64
|
||||||
ShopGroupID int64
|
ShopGroupID int64
|
||||||
CountryISO string
|
CountryISO string
|
||||||
|
CookieHours int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(db *gorm.DB, prefix string) *Service {
|
func NewService(db *gorm.DB, prefix, version string) *Service {
|
||||||
return &Service{db: db, prefix: prefix}
|
return &Service{db: db, prefix: prefix, version: version}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) NewAnonymous(ctx context.Context, req *http.Request, cookieName string) (*pscookie.SessionContext, error) {
|
func (s *Service) NewAnonymous(ctx context.Context, req *http.Request, cookieName string) (*pscookie.SessionContext, error) {
|
||||||
@@ -88,12 +93,52 @@ func (s *Service) NewAnonymous(ctx context.Context, req *http.Request, cookieNam
|
|||||||
ShopID: int64Ptr(def.ShopID),
|
ShopID: int64Ptr(def.ShopID),
|
||||||
GuestID: int64Ptr(guestID),
|
GuestID: int64Ptr(guestID),
|
||||||
IsLoggedIn: false,
|
IsLoggedIn: false,
|
||||||
|
ExpiresAt: cookieExpiry(now, def.CookieHours),
|
||||||
Values: values,
|
Values: values,
|
||||||
OrderedKeys: orderedKeys,
|
OrderedKeys: orderedKeys,
|
||||||
ParseStatus: pscookie.ParseStatusAnonymous,
|
ParseStatus: pscookie.ParseStatusAnonymous,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) RefreshExpiry(ctx context.Context, session *pscookie.SessionContext) error {
|
||||||
|
if s == nil || session == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
def, err := s.loadDefaults(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
session.ExpiresAt = cookieExpiry(time.Now().UTC(), def.CookieHours)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ResolveCookieName(ctx context.Context, req *http.Request) (string, error) {
|
||||||
|
if s == nil || s.db == nil {
|
||||||
|
return "", fmt.Errorf("prestashop session service is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
host := requestHost(req)
|
||||||
|
shop, err := s.loadCookieShopContext(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
baseName := "ps-s" + strconv.FormatInt(shop.ShopID, 10)
|
||||||
|
sharedDomains := []string(nil)
|
||||||
|
if shop.ShareOrder {
|
||||||
|
baseName = "ps-sg" + strconv.FormatInt(shop.ShopGroupID, 10)
|
||||||
|
sharedDomains, err = s.loadSharedCartDomains(ctx, shop.ShopGroupID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := md5.Sum([]byte(s.version + baseName + prestashopCookieDomain(host, sharedDomains)))
|
||||||
|
return fmt.Sprintf("PrestaShop-%x", sum), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) loadDefaults(ctx context.Context) (*defaults, error) {
|
func (s *Service) loadDefaults(ctx context.Context) (*defaults, error) {
|
||||||
def := &defaults{
|
def := &defaults{
|
||||||
LanguageID: 1,
|
LanguageID: 1,
|
||||||
@@ -101,6 +146,7 @@ func (s *Service) loadDefaults(ctx context.Context) (*defaults, error) {
|
|||||||
ShopID: 1,
|
ShopID: 1,
|
||||||
ShopGroupID: 1,
|
ShopGroupID: 1,
|
||||||
CountryISO: "US",
|
CountryISO: "US",
|
||||||
|
CookieHours: 480,
|
||||||
}
|
}
|
||||||
|
|
||||||
configTable := s.prefix + "configuration"
|
configTable := s.prefix + "configuration"
|
||||||
@@ -111,7 +157,7 @@ func (s *Service) loadDefaults(ctx context.Context) (*defaults, error) {
|
|||||||
Name string
|
Name string
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
configQuery := fmt.Sprintf("SELECT name, value FROM %s WHERE name IN ('PS_LANG_DEFAULT', 'PS_CURRENCY_DEFAULT', 'PS_COUNTRY_DEFAULT')", configTable)
|
configQuery := fmt.Sprintf("SELECT name, value FROM %s WHERE name IN ('PS_LANG_DEFAULT', 'PS_CURRENCY_DEFAULT', 'PS_COUNTRY_DEFAULT', 'PS_COOKIE_LIFETIME_FO')", configTable)
|
||||||
if err := s.db.WithContext(ctx).Raw(configQuery).Scan(&configs).Error; err != nil {
|
if err := s.db.WithContext(ctx).Raw(configQuery).Scan(&configs).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -131,6 +177,10 @@ func (s *Service) loadDefaults(ctx context.Context) (*defaults, error) {
|
|||||||
if parsed, err := strconv.ParseInt(cfg.Value, 10, 64); err == nil && parsed > 0 {
|
if parsed, err := strconv.ParseInt(cfg.Value, 10, 64); err == nil && parsed > 0 {
|
||||||
countryID = parsed
|
countryID = parsed
|
||||||
}
|
}
|
||||||
|
case "PS_COOKIE_LIFETIME_FO":
|
||||||
|
if parsed, err := strconv.ParseInt(cfg.Value, 10, 64); err == nil && parsed > 0 {
|
||||||
|
def.CookieHours = parsed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,6 +322,195 @@ func (s *Service) connectionInsert(ctx context.Context, def *defaults, guestID i
|
|||||||
return columns, values, nil
|
return columns, values, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cookieExpiry(now time.Time, lifetimeHours int64) *time.Time {
|
||||||
|
if lifetimeHours <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
expiresAt := now.Add(time.Duration(lifetimeHours) * time.Hour)
|
||||||
|
return &expiresAt
|
||||||
|
}
|
||||||
|
|
||||||
|
type cookieShopContext struct {
|
||||||
|
ShopID int64 `gorm:"column:id_shop"`
|
||||||
|
ShopGroupID int64 `gorm:"column:id_shop_group"`
|
||||||
|
ShareOrder bool `gorm:"column:share_order"`
|
||||||
|
URI string `gorm:"column:uri"`
|
||||||
|
Main bool `gorm:"column:main"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loadCookieShopContext(ctx context.Context, req *http.Request) (*cookieShopContext, error) {
|
||||||
|
normalizedHost := requestHost(req)
|
||||||
|
requestURI := requestPath(req)
|
||||||
|
|
||||||
|
shopURLTable := s.prefix + "shop_url"
|
||||||
|
shopTable := s.prefix + "shop"
|
||||||
|
shopGroupTable := s.prefix + "shop_group"
|
||||||
|
|
||||||
|
if normalizedHost != "" {
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
SELECT s.id_shop, s.id_shop_group, sg.share_order, CONCAT(su.physical_uri, su.virtual_uri) AS uri, su.main
|
||||||
|
FROM %s s
|
||||||
|
JOIN %s sg ON sg.id_shop_group = s.id_shop_group
|
||||||
|
JOIN %s su ON su.id_shop = s.id_shop
|
||||||
|
WHERE su.active = 1
|
||||||
|
AND s.active = 1
|
||||||
|
AND s.deleted = 0
|
||||||
|
AND (LOWER(su.domain) = ? OR LOWER(su.domain_ssl) = ?)
|
||||||
|
ORDER BY LENGTH(CONCAT(su.physical_uri, su.virtual_uri)) DESC, su.main DESC, s.id_shop ASC
|
||||||
|
`, shopTable, shopGroupTable, shopURLTable)
|
||||||
|
var shops []cookieShopContext
|
||||||
|
if err := s.db.WithContext(ctx).Raw(query, normalizedHost, normalizedHost).Scan(&shops).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, shop := range shops {
|
||||||
|
if uriMatchesRequest(shop.URI, requestURI) {
|
||||||
|
return &shop, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackQuery := fmt.Sprintf(`
|
||||||
|
SELECT s.id_shop, s.id_shop_group, sg.share_order, '' AS uri, 1 AS main
|
||||||
|
FROM %s s
|
||||||
|
JOIN %s sg ON sg.id_shop_group = s.id_shop_group
|
||||||
|
WHERE s.active = 1
|
||||||
|
AND s.deleted = 0
|
||||||
|
ORDER BY s.id_shop ASC
|
||||||
|
LIMIT 1
|
||||||
|
`, shopTable, shopGroupTable)
|
||||||
|
var shop cookieShopContext
|
||||||
|
if err := s.db.WithContext(ctx).Raw(fallbackQuery).Scan(&shop).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if shop.ShopID == 0 {
|
||||||
|
return nil, fmt.Errorf("prestashop shop context not found")
|
||||||
|
}
|
||||||
|
return &shop, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loadSharedCartDomains(ctx context.Context, shopGroupID int64) ([]string, error) {
|
||||||
|
if shopGroupID == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type row struct {
|
||||||
|
Domain string `gorm:"column:domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
shopURLTable := s.prefix + "shop_url"
|
||||||
|
shopTable := s.prefix + "shop"
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
SELECT su.domain
|
||||||
|
FROM %s su
|
||||||
|
JOIN %s s ON s.id_shop = su.id_shop
|
||||||
|
WHERE su.main = 1
|
||||||
|
AND su.active = 1
|
||||||
|
AND s.id_shop_group = ?
|
||||||
|
`, shopURLTable, shopTable)
|
||||||
|
|
||||||
|
var rows []row
|
||||||
|
if err := s.db.WithContext(ctx).Raw(query, shopGroupID).Scan(&rows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
domains := make([]string, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
if host := normalizeRequestHost(row.Domain); host != "" {
|
||||||
|
domains = append(domains, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return domains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeRequestHost(input string) string {
|
||||||
|
value := strings.TrimSpace(input)
|
||||||
|
if value == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if strings.Contains(value, "://") {
|
||||||
|
if parsed, err := url.Parse(value); err == nil {
|
||||||
|
value = parsed.Hostname()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if host, _, err := net.SplitHostPort(value); err == nil {
|
||||||
|
value = host
|
||||||
|
}
|
||||||
|
return strings.ToLower(strings.TrimSpace(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestHost(req *http.Request) string {
|
||||||
|
if req == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
host := req.Header.Get("X-Forwarded-Host")
|
||||||
|
if host == "" {
|
||||||
|
host = req.Host
|
||||||
|
}
|
||||||
|
if strings.Contains(host, ",") {
|
||||||
|
host = strings.TrimSpace(strings.Split(host, ",")[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeRequestHost(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestPath(req *http.Request) string {
|
||||||
|
if req == nil || req.URL == nil {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
path := req.URL.EscapedPath()
|
||||||
|
if path == "" {
|
||||||
|
path = req.URL.Path
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
decoded, err := url.PathUnescape(path)
|
||||||
|
if err == nil && decoded != "" {
|
||||||
|
path = decoded
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
path = "/" + path
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func uriMatchesRequest(uri, requestURI string) bool {
|
||||||
|
if uri == "" {
|
||||||
|
uri = "/"
|
||||||
|
}
|
||||||
|
if requestURI == "" {
|
||||||
|
requestURI = "/"
|
||||||
|
}
|
||||||
|
return strings.HasPrefix(strings.ToLower(requestURI), strings.ToLower(uri))
|
||||||
|
}
|
||||||
|
|
||||||
|
var sharedDomainPattern = regexp.MustCompile(`^(?:.*\.)?([^.]*(?:.{2,4})?\..{2,3})$`)
|
||||||
|
|
||||||
|
func prestashopCookieDomain(host string, sharedURLs []string) string {
|
||||||
|
normalizedHost := normalizeRequestHost(host)
|
||||||
|
if normalizedHost == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if net.ParseIP(normalizedHost) != nil || !strings.Contains(normalizedHost, ".") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sharedURL := range sharedURLs {
|
||||||
|
if normalizeRequestHost(sharedURL) != normalizedHost {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matches := sharedDomainPattern.FindStringSubmatch(normalizedHost)
|
||||||
|
if len(matches) == 2 {
|
||||||
|
return "." + matches[1]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedHost
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) tableColumns(ctx context.Context, tableName string) (map[string]bool, error) {
|
func (s *Service) tableColumns(ctx context.Context, tableName string) (map[string]bool, error) {
|
||||||
type columnRow struct {
|
type columnRow struct {
|
||||||
ColumnName string `gorm:"column:COLUMN_NAME"`
|
ColumnName string `gorm:"column:COLUMN_NAME"`
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package session
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPrestashopCookieDomain(t *testing.T) {
|
||||||
|
if got := prestashopCookieDomain("localhost", nil); got != "" {
|
||||||
|
t.Fatalf("prestashopCookieDomain(localhost) = %q, want empty", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := prestashopCookieDomain("shop.example.com", []string{"shop.example.com"}); got != ".example.com" {
|
||||||
|
t.Fatalf("prestashopCookieDomain(shared) = %q, want %q", got, ".example.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := prestashopCookieDomain("shop.example.com", nil); got != "shop.example.com" {
|
||||||
|
t.Fatalf("prestashopCookieDomain(single) = %q, want %q", got, "shop.example.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestURIMatchesRequest(t *testing.T) {
|
||||||
|
if !uriMatchesRequest("/shop/fr/", "/shop/fr/product/test") {
|
||||||
|
t.Fatalf("expected nested shop URI to match request path")
|
||||||
|
}
|
||||||
|
|
||||||
|
if uriMatchesRequest("/shop/fr/", "/shop/en/product/test") {
|
||||||
|
t.Fatalf("unexpected match for different shop URI")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
if ($argc < 3) {
|
|
||||||
fwrite(STDERR, "usage: php prestashop_cookie_bridge.php <mode> <bootstrap>\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$mode = $argv[1];
|
|
||||||
$bootstrap = $argv[2];
|
|
||||||
$input = json_decode(stream_get_contents(STDIN), true);
|
|
||||||
|
|
||||||
if (!is_array($input)) {
|
|
||||||
fwrite(STDERR, "invalid input\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_file($bootstrap)) {
|
|
||||||
fwrite(STDERR, "bootstrap not found\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$cookieName = $input['cookie_name'] ?? null;
|
|
||||||
if (!$cookieName) {
|
|
||||||
fwrite(STDERR, "cookie name missing\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
|
||||||
$_SERVER['REQUEST_METHOD'] = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
||||||
$_SERVER['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
|
|
||||||
$_COOKIE[$cookieName] = $input['raw_cookie'] ?? '';
|
|
||||||
|
|
||||||
require_once $bootstrap;
|
|
||||||
|
|
||||||
if (!class_exists('Cookie')) {
|
|
||||||
fwrite(STDERR, "prestashop cookie class unavailable\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$cookie = new Cookie($cookieName);
|
|
||||||
|
|
||||||
if ($mode !== 'decode') {
|
|
||||||
fwrite(STDERR, "unsupported mode\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$reflection = new ReflectionClass($cookie);
|
|
||||||
$content = [];
|
|
||||||
if ($reflection->hasProperty('_content')) {
|
|
||||||
$property = $reflection->getProperty('_content');
|
|
||||||
$property->setAccessible(true);
|
|
||||||
$content = $property->getValue($cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = [
|
|
||||||
'customer_id' => isset($content['id_customer']) ? (int) $content['id_customer'] : null,
|
|
||||||
'cart_id' => isset($content['id_cart']) ? (int) $content['id_cart'] : null,
|
|
||||||
'language_id' => isset($content['id_lang']) ? (int) $content['id_lang'] : null,
|
|
||||||
'currency_id' => isset($content['id_currency']) ? (int) $content['id_currency'] : null,
|
|
||||||
'shop_id' => isset($content['id_shop']) ? (int) $content['id_shop'] : null,
|
|
||||||
'guest_id' => isset($content['id_guest']) ? (int) $content['id_guest'] : null,
|
|
||||||
'is_logged_in' => !empty($content['logged']),
|
|
||||||
'expires_at' => null,
|
|
||||||
'values' => array_map(static function ($value): string {
|
|
||||||
if (is_bool($value)) {
|
|
||||||
return $value ? '1' : '0';
|
|
||||||
}
|
|
||||||
if (is_scalar($value) || $value === null) {
|
|
||||||
return (string) $value;
|
|
||||||
}
|
|
||||||
return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '';
|
|
||||||
}, $content),
|
|
||||||
'raw_cookie' => $input['raw_cookie'] ?? '',
|
|
||||||
];
|
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
||||||
@@ -43,6 +43,27 @@ templ CategoryPage(data viewmodel.CategoryPageData, cssPath string, jsPath strin
|
|||||||
</article>
|
</article>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
if data.Session != nil {
|
||||||
|
<section class="rounded-[2rem] border border-white/10 bg-slate-950/70 p-8">
|
||||||
|
<p class="text-xs uppercase tracking-[0.28em] text-emerald-300">Go Cookie Debug</p>
|
||||||
|
<div class="mt-6 grid gap-6 lg:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-semibold text-white">Raw Cookie</p>
|
||||||
|
<pre class="mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-slate-300">{ data.Session.RawCookie }</pre>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-semibold text-white">Decoded Values</p>
|
||||||
|
<pre class="mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-slate-300">
|
||||||
|
for _, line := range sessionCookieLines(data.Session) {
|
||||||
|
{ line }
|
||||||
|
{"\n"}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,58 @@ func CategoryPage(data viewmodel.CategoryPageData, cssPath string, jsPath string
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</section></div></main>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Session != nil {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<section class=\"rounded-[2rem] border border-white/10 bg-slate-950/70 p-8\"><p class=\"text-xs uppercase tracking-[0.28em] text-emerald-300\">Go Cookie Debug</p><div class=\"mt-6 grid gap-6 lg:grid-cols-2\"><div><p class=\"text-sm font-semibold text-white\">Raw Cookie</p><pre class=\"mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-slate-300\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.Session.RawCookie)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/category.templ`, Line: 53, Col: 127}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</pre></div><div><p class=\"text-sm font-semibold text-white\">Decoded Values</p><pre class=\"mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-slate-300\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, line := range sessionCookieLines(data.Session) {
|
||||||
|
var templ_7745c5c3_Var11 string
|
||||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(line)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/category.templ`, Line: 59, Col: 16}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var12 string
|
||||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs("\n")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/category.templ`, Line: 60, Col: 15}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</pre></div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div></main>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ templ Layout(title string, cssPath string, jsPath string, menu []pscatalog.MenuI
|
|||||||
</div>
|
</div>
|
||||||
if hasHeaderLocale(locale) {
|
if hasHeaderLocale(locale) {
|
||||||
<div class="header-locale">
|
<div class="header-locale">
|
||||||
@LocalePicker("Market", locale.CurrentCountry, locale.Countries, true)
|
@LocalePicker("Market", locale.CurrentCountry, locale.Countries, true, false)
|
||||||
@LocalePicker("Language", locale.CurrentLanguage, locale.Languages, true)
|
@LocalePicker("Language", locale.CurrentLanguage, locale.Languages, true, true)
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -67,7 +67,7 @@ templ Layout(title string, cssPath string, jsPath string, menu []pscatalog.MenuI
|
|||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ LocalePicker(title string, current pscatalog.LocaleOption, options []pscatalog.LocaleOption, navigable bool) {
|
templ LocalePicker(title string, current pscatalog.LocaleOption, options []pscatalog.LocaleOption, navigable bool, showMeta bool) {
|
||||||
<details class="locale-picker">
|
<details class="locale-picker">
|
||||||
<summary class="locale-picker__summary">
|
<summary class="locale-picker__summary">
|
||||||
<span class="locale-picker__value">
|
<span class="locale-picker__value">
|
||||||
@@ -77,7 +77,7 @@ templ LocalePicker(title string, current pscatalog.LocaleOption, options []pscat
|
|||||||
{ title }
|
{ title }
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
if current.Code != "" {
|
if showMeta && current.Code != "" {
|
||||||
<span class="locale-picker__code">{ current.Code }</span>
|
<span class="locale-picker__code">{ current.Code }</span>
|
||||||
}
|
}
|
||||||
<span class="locale-picker__chevron">⌄</span>
|
<span class="locale-picker__chevron">⌄</span>
|
||||||
@@ -91,16 +91,16 @@ templ LocalePicker(title string, current pscatalog.LocaleOption, options []pscat
|
|||||||
if navigable && option.URL != "" {
|
if navigable && option.URL != "" {
|
||||||
<a class={ localeOptionClass(option, current) } href={ option.URL }>
|
<a class={ localeOptionClass(option, current) } href={ option.URL }>
|
||||||
<span>{ option.Label }</span>
|
<span>{ option.Label }</span>
|
||||||
if option.Meta != "" {
|
if showMeta && option.Meta != "" {
|
||||||
<span class="locale-picker__item-meta">{ option.Meta }</span>
|
<span class="locale-picker__item-meta">{ option.Meta }</span>
|
||||||
} else if option.Code != "" {
|
} else if showMeta && option.Code != "" {
|
||||||
<span class="locale-picker__item-meta">{ option.Code }</span>
|
<span class="locale-picker__item-meta">{ option.Code }</span>
|
||||||
}
|
}
|
||||||
</a>
|
</a>
|
||||||
} else {
|
} else {
|
||||||
<span class={ localeOptionClass(option, current) }>
|
<span class={ localeOptionClass(option, current) }>
|
||||||
<span>{ option.Label }</span>
|
<span>{ option.Label }</span>
|
||||||
if option.Meta != "" {
|
if showMeta && option.Meta != "" {
|
||||||
<span class="locale-picker__item-meta">{ option.Meta }</span>
|
<span class="locale-picker__item-meta">{ option.Meta }</span>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
pscatalog "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/catalog"
|
pscatalog "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/catalog"
|
||||||
|
pscookie "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cookie"
|
||||||
)
|
)
|
||||||
|
|
||||||
func menuListClass(depth int) string {
|
func menuListClass(depth int) string {
|
||||||
@@ -76,3 +78,21 @@ func menuPanelID(id int64) string {
|
|||||||
}
|
}
|
||||||
return "mega-menu-panel-" + strconv.FormatInt(id, 10)
|
return "mega-menu-panel-" + strconv.FormatInt(id, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sessionCookieLines(session *pscookie.SessionContext) []string {
|
||||||
|
if session == nil || len(session.Values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(session.Values))
|
||||||
|
for key := range session.Values {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
lines := make([]string, 0, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
lines = append(lines, key+"="+session.Values[key])
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|||||||
@@ -92,11 +92,11 @@ func Layout(title string, cssPath string, jsPath string, menu []pscatalog.MenuIt
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = LocalePicker("Market", locale.CurrentCountry, locale.Countries, true).Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = LocalePicker("Market", locale.CurrentCountry, locale.Countries, true, false).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = LocalePicker("Language", locale.CurrentLanguage, locale.Languages, true).Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = LocalePicker("Language", locale.CurrentLanguage, locale.Languages, true, true).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ func Layout(title string, cssPath string, jsPath string, menu []pscatalog.MenuIt
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func LocalePicker(title string, current pscatalog.LocaleOption, options []pscatalog.LocaleOption, navigable bool) templ.Component {
|
func LocalePicker(title string, current pscatalog.LocaleOption, options []pscatalog.LocaleOption, navigable bool, showMeta bool) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@@ -197,7 +197,7 @@ func LocalePicker(title string, current pscatalog.LocaleOption, options []pscata
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if current.Code != "" {
|
if showMeta && current.Code != "" {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<span class=\"locale-picker__code\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<span class=\"locale-picker__code\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
@@ -292,7 +292,7 @@ func LocalePicker(title string, current pscatalog.LocaleOption, options []pscata
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if option.Meta != "" {
|
if showMeta && option.Meta != "" {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<span class=\"locale-picker__item-meta\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<span class=\"locale-picker__item-meta\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
@@ -310,7 +310,7 @@ func LocalePicker(title string, current pscatalog.LocaleOption, options []pscata
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
} else if option.Code != "" {
|
} else if showMeta && option.Code != "" {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<span class=\"locale-picker__item-meta\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<span class=\"locale-picker__item-meta\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
@@ -369,7 +369,7 @@ func LocalePicker(title string, current pscatalog.LocaleOption, options []pscata
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if option.Meta != "" {
|
if showMeta && option.Meta != "" {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<span class=\"locale-picker__item-meta\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<span class=\"locale-picker__item-meta\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
|
|||||||
@@ -52,6 +52,27 @@ templ ProductPage(data viewmodel.ProductPageData, cssPath string, jsPath string)
|
|||||||
</form>
|
</form>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
if data.Session != nil {
|
||||||
|
<section class="rounded-3xl border border-stone-800 bg-stone-950/80 p-8">
|
||||||
|
<p class="text-xs uppercase tracking-[0.28em] text-stone-500">Go Cookie Debug</p>
|
||||||
|
<div class="mt-6 grid gap-6 lg:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-semibold text-stone-200">Raw Cookie</p>
|
||||||
|
<pre class="mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-stone-300">{ data.Session.RawCookie }</pre>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-semibold text-stone-200">Decoded Values</p>
|
||||||
|
<pre class="mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-stone-300">
|
||||||
|
for _, line := range sessionCookieLines(data.Session) {
|
||||||
|
{ line }
|
||||||
|
{"\n"}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,7 +229,58 @@ func ProductPage(data viewmodel.ProductPageData, cssPath string, jsPath string)
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\">Account and login remain on PrestaShop</a></form></aside></section></div></main>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\">Account and login remain on PrestaShop</a></form></aside></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Session != nil {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<section class=\"rounded-3xl border border-stone-800 bg-stone-950/80 p-8\"><p class=\"text-xs uppercase tracking-[0.28em] text-stone-500\">Go Cookie Debug</p><div class=\"mt-6 grid gap-6 lg:grid-cols-2\"><div><p class=\"text-sm font-semibold text-stone-200\">Raw Cookie</p><pre class=\"mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-stone-300\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var14 string
|
||||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(data.Session.RawCookie)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/product.templ`, Line: 62, Col: 127}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</pre></div><div><p class=\"text-sm font-semibold text-stone-200\">Decoded Values</p><pre class=\"mt-3 overflow-x-auto rounded-2xl bg-black/30 p-4 text-xs leading-6 text-stone-300\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, line := range sessionCookieLines(data.Session) {
|
||||||
|
var templ_7745c5c3_Var15 string
|
||||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(line)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/product.templ`, Line: 68, Col: 16}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var16 string
|
||||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs("\n")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/product.templ`, Line: 69, Col: 15}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</pre></div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</div></main>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user