customer address is not updating
Customer address is updating after refreshing the page
Hi, thanks for your feedback, is it something related to the latest update? ( Qwik v2 )
same issue on version 1.11, commit console log output
be [Error]: CSRF check failed. Cross-site POST form submissions are forbidden.
The request origin "https://storefront.dingpack.com" does not match the server origin "http://storefront.dingpack.com".
at Object.error (file:///mnt/ext-hdd/projects/vendure/storefront/fresh/storefront-qwik-starter/server/entry.express.js:15:8104)
at jt (file:///mnt/ext-hdd/projects/vendure/storefront/fresh/storefront-qwik-starter/server/entry.express.js:17:1221)
at AsyncLocalStorage.run (node:async_hooks:335:14)
at Object.Me [as next] (file:///mnt/ext-hdd/projects/vendure/storefront/fresh/storefront-qwik-starter/server/entry.express.js:15:6930)
at ce (file:///mnt/ext-hdd/projects/vendure/storefront/fresh/storefront-qwik-starter/server/entry.express.js:15:5531)
at AsyncLocalStorage.run (node:async_hooks:346:14)
at Rt (file:///mnt/ext-hdd/projects/vendure/storefront/fresh/storefront-qwik-starter/server/entry.express.js:15:5473)
at Gt (file:///mnt/ext-hdd/projects/vendure/storefront/fresh/storefront-qwik-starter/server/entry.express.js:18:2375)
at async router (file:///mnt/ext-hdd/projects/vendure/storefront/fresh/storefront-qwik-starter/server/entry.express.js:18:5930) {
status: 403
}
Cross-Origin issue, please ignore this
Cross-Origin issue, please ignore this
Can we close this issue?
Yes sir
On Tue, 24 Dec, 2024, 21:40 Giorgio Boa, @.***> wrote:
Cross-Origin issue, please ignore this
Can we close this issue?
— Reply to this email directly, view it on GitHub https://github.com/vendure-ecommerce/storefront-qwik-starter/issues/171#issuecomment-2561262394, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD4NU72JUJZFIX3BOBEG4NT2HGBPLAVCNFSM6AAAAABUEJAVJ6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKNRRGI3DEMZZGQ . You are receiving this because you authored the thread.Message ID: @.*** com>
Hi @gioboa , I rechecked, and the issue persists.
api url https://core.vendure.lan,
nginx conf file
# Upstream server for Vendure storefront
upstream storefront_lan {
server localhost:3030;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name storefront.lan www.storefront.lan;
return 301 https://www.storefront.lan$request_uri;
}
# HTTPS server
server {
listen 443 ssl;
server_name www.storefront.lan;
# SSL certificate configuration
ssl_certificate /etc/nginx/certs/nginx.crt;
ssl_certificate_key /etc/nginx/certs/nginx.key;
# Optional: Enable SSL ciphers and protocols
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-CBC-SHA256:ECDHE-ECDSA-AES128-CBC-SHA256:ECDHE-RSA-AES256-CBC-SHA:ECDHE-ECDSA-AES256-CBC-SHA:RSA-AES128-CBC-SHA256:RSA-AES256-CBC-SHA;
ssl_protocols TLSv1.2 TLSv1.3;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "same-origin" always;
# CORS headers
add_header 'Access-Control-Allow-Origin' 'https://www.storefront.lan' always; # Adjust as needed
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With, Accept' always;
add_header 'Access-Control-Allow-Credentials' 'true' always; # Required if using cookies or authentication
# Handle preflight requests
if ($request_method = OPTIONS) {
return 204;
}
client_max_body_size 5M;
location / {
proxy_pass http://storefront_lan;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
}
}
and vendure configuration file
apiOptions: {
hostname: process.env.apiOptions_HOSTNAME ?? "192.168.1.32",
port: serverPort,
adminApiPath: 'admin-api',
shopApiPath: 'shop-api',
middleware: [
{
route: 'admin-api',
handler: apiRateLimiter,
},
{
route: 'shop-api',
handler: apiRateLimiter,
},
],
cors: {
origin: ['https://www.storefront.lan', 'https://core.vendure.lan'],
credentials: true,
methods: ['GET', 'POST', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'vendure-token'],
},
introspection: IS_DEV ? true : false,
...(IS_DEV ? {
adminApiPlayground: {
settings: { 'request.credentials': 'include' },
},
adminApiDebug: true,
shopApiPlayground: {
settings: { 'request.credentials': 'include' },
},
shopApiDebug: true,
} : {
adminApiPlayground: false,
adminApiDebug: false,
shopApiPlayground: false,
shopApiDebug: false,
}),
},
Thanks for your update. I'll reopen it
Analyzing Your Code
Looking at your src/routes/account/index.tsx file, within the updateCustomer function:
const updateCustomer = $(async (): Promise<void> => {
await updateCustomerMutation(appState.customer);
appState.customer.emailAddress !== newEmail.value
? (showModal.value = true)
: (isEditing.value = false);
});
And the useVisibleTask$ block where appState.customer is populated:
useVisibleTask$(async () => {
const activeCustomer = await getActiveCustomerQuery();
appState.customer = {
title: activeCustomer.title ?? '',
firstName: activeCustomer.firstName,
id: activeCustomer.id,
lastName: activeCustomer.lastName,
emailAddress: activeCustomer.emailAddress,
phoneNumber: activeCustomer.phoneNumber ?? '',
};
newEmail.value = activeCustomer?.emailAddress as string;
});
You are correctly fetching the activeCustomer data, which includes the id. However, when you call updateCustomerMutation(appState.customer), you are passing the entire appState.customer object as the input to the mutation. This object contains the id field, which is not allowed by the UpdateCustomerInput type in your GraphQL schema.
How to Fix It
You need to ensure that the object you pass to updateCustomerMutation only includes the fields defined in the UpdateCustomerInput type. You can do this by creating a new object with only the allowed fields when calling the mutation.
Modify your updateCustomer function in src/routes/account/index.tsx like this:
const updateCustomer = $(async (): Promise<void> => {
const updateInput = {
title: appState.customer.title,
firstName: appState.customer.firstName,
lastName: appState.customer.lastName,
phoneNumber: appState.customer.phoneNumber,
// customFields: appState.customer.customFields, // If you have customFields in your appState
};
await updateCustomerMutation(updateInput);
appState.customer.emailAddress !== newEmail.value
? (showModal.value = true)
: (isEditing.value = false);
});
Complete Updated Snippet
src/routes/account/index.tsx
import { $, component$, useContext, useSignal, useVisibleTask$ } from '@qwik.dev/core';
import { isBrowser } from '@qwik.dev/core/build';
import { Image } from 'qwik-image';
import { Button } from '~/components/buttons/Button';
import { HighlightedButton } from '~/components/buttons/HighlightedButton';
import { ErrorMessage } from '~/components/error-message/ErrorMessage';
import CheckIcon from '~/components/icons/CheckIcon';
import PencilSquareIcon from '~/components/icons/PencilSquareIcon';
import ShieldCheckIcon from '~/components/icons/ShieldCheckIcon';
import XMarkIcon from '~/components/icons/XMarkIcon';
import { Modal } from '~/components/modal/Modal';
import { APP_STATE } from '~/constants';
import {
requestUpdateCustomerEmailAddressMutation,
updateCustomerMutation,
} from '~/providers/shop/account/account';
import { getActiveCustomerQuery } from '~/providers/shop/customer/customer';
import { ActiveCustomer } from '~/types';
export default component$(() => {
const appState = useContext(APP_STATE);
const isEditing = useSignal(false);
const showModal = useSignal(false);
const newEmail = useSignal('');
const errorMessage = useSignal('');
const currentPassword = useSignal('');
const update = {
customer: {} as ActiveCustomer,
};
useVisibleTask$(async () => {
const activeCustomer = await getActiveCustomerQuery();
appState.customer = {
title: activeCustomer.title ?? '',
firstName: activeCustomer.firstName,
id: activeCustomer.id,
lastName: activeCustomer.lastName,
emailAddress: activeCustomer.emailAddress,
phoneNumber: activeCustomer.phoneNumber ?? '',
// customFields: activeCustomer.customFields ?? {},
};
newEmail.value = activeCustomer?.emailAddress as string;
});
const updateCustomer = $(async (): Promise<void> => {
const updateInput = {
title: appState.customer.title,
firstName: appState.customer.firstName,
lastName: appState.customer.lastName,
phoneNumber: appState.customer.phoneNumber,
// customFields: appState.customer.customFields,
};
await updateCustomerMutation(updateInput);
appState.customer.emailAddress !== newEmail.value
? (showModal.value = true)
: (isEditing.value = false);
});
const updateEmail = $(async (password: string, newEmail: string) => {
const { requestUpdateCustomerEmailAddress } = await requestUpdateCustomerEmailAddressMutation(
password,
newEmail
);
if (requestUpdateCustomerEmailAddress.__typename === 'InvalidCredentialsError') {
errorMessage.value = requestUpdateCustomerEmailAddress.message || '';
} else {
errorMessage.value = '';
isEditing.value = false;
showModal.value = false;
}
});
return (
<div>
<div class="min-h-[24rem] max-w-6xl m-auto rounded-lg p-4 space-y-4 ">
<div class="flex flex-col justify-center items-center">
<div class="relative flex flex-col items-center rounded-[20px] w-[400px] mx-auto p-4 bg-white bg-clip-border shadow-xl hover:shadow-2xl">
<div class="relative flex h-32 w-full justify-center rounded-xl bg-cover">
<Image
layout="fullWidth"
src="/account-background.png"
class="absolute flex h-32 w-full justify-center rounded-xl bg-cover"
alt="background"
/>
<div class="absolute -bottom-12 flex h-[87px] w-[87px] items-center justify-center rounded-full border-[4px] border-white bg-pink-400">
<Image
layout="fullWidth"
class="h-full w-full rounded-full"
src="/user-icon.webp"
alt="user icon"
/>
</div>
<div class="absolute -bottom-12 right-0">
<button
class="hover:text-primary-700"
onClick$={() => {
isEditing.value = !isEditing.value;
if (isBrowser) {
window.scrollTo(0, 100);
}
if (!isEditing.value && isBrowser) {
window.scrollTo(0, 0);
}
}}
>
<PencilSquareIcon />
</button>
</div>
</div>
<div class="mt-16 flex flex-col items-center pb-4">
<h4 class="text-xl md:text-3xl font-bold">
{appState.customer?.title && (
<span class="text-base font-normal mr-1">{appState.customer?.title}</span>
)}
{appState.customer?.firstName} {appState.customer?.lastName}
</h4>
</div>
<div class="flex flex-col items-center justify-center text-center">
{appState.customer?.phoneNumber && (
<div class="text-sm md:text-lg">
Phone:
<span class="font-bold px-2">{appState.customer?.phoneNumber}</span>
</div>
)}
<div class="text-sm md:text-lg">
Email:
<span class="font-bold px-2">{appState.customer?.emailAddress}</span>
</div>
</div>
</div>
</div>
</div>
<div class="min-h-[24rem] rounded-lg p-4 space-y-4">
<Modal
open={showModal.value}
title="Confirm E-Mail address change"
onSubmit$={() => {
updateEmail(currentPassword.value, newEmail.value);
}}
onCancel$={() => {
showModal.value = false;
}}
>
<div q:slot="modalIcon">
<ShieldCheckIcon forcedClass="h-10 w-10 text-primary-500" />
</div>
<div q:slot="modalContent" class="space-y-4">
<p>We will send a verification E-Mail to {newEmail.value}</p>
<div class="space-y-1">
<label html-for="password">Confirm the change by entering your password:</label>
<input
type="password"
name="password"
onChange$={(_, el) => {
currentPassword.value = el.value;
}}
class="w-full"
/>
</div>
{errorMessage.value !== '' && (
<ErrorMessage
heading="We ran into a problem changing your E-Mail!"
message={errorMessage.value}
/>
)}
</div>
</Modal>
{isEditing.value && (
<div class="max-w-3xl m-auto">
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<div class="md:col-span-2 md:w-1/4">
<h3 class="text-sm text-gray-500">Title</h3>
<input
type="text"
value={appState.customer?.title}
onInput$={(_, el) => {
update.customer.title = el.value;
}}
class="block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
/>
</div>
<div>
<label html-for="firstName" class="text-sm text-gray-500">
First Name
</label>
<input
type="text"
value={appState.customer?.firstName}
onChange$={(_, el) => {
if (el.value !== '') {
update.customer.firstName = el.value;
}
}}
class="block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
/>
</div>
<div>
<label html-for="lastName" class="text-sm text-gray-500">
Last Name
</label>
<input
type="text"
value={appState.customer?.lastName}
onChange$={(_, el) => {
if (el.value !== '') {
update.customer.lastName = el.value;
}
}}
class="block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
/>
</div>
<div>
<h3 class="text-sm text-gray-500">E-Mail</h3>
<input
type="email"
value={appState.customer?.emailAddress}
onChange$={(_, el) => {
if (el.value !== '') {
newEmail.value = el.value;
}
}}
class="block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
/>
</div>
<div>
<h3 class="text-sm text-gray-500">Phone Nr.</h3>
<input
type="tel"
value={appState.customer?.phoneNumber}
onChange$={(_, el) => {
update.customer.phoneNumber = el.value;
}}
class="block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
/>
</div>
</div>
<div class="flex gap-x-4 mt-8">
<HighlightedButton
onClick$={() => {
appState.customer = { ...appState.customer, ...update.customer };
updateCustomer();
}}
>
<CheckIcon /> Save
</HighlightedButton>
<Button
onClick$={() => {
isEditing.value = false;
}}
>
<XMarkIcon forcedClass="w-4 h-4" /> Cancel
</Button>
</div>
</div>
)}
</div>
</div>
);
});
Thanks @ashishkpaul for your help. Would you like to create a PR with this fix?
Please do it from your end
Thanks @ashishkpaul 👍 I did the fix, I added you as co-author 💯