Skip to content

Commit

Permalink
feat: improve setup support (#404)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrocksiNet authored Oct 23, 2023
1 parent 7dfd70c commit f356675
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 108 deletions.
5 changes: 5 additions & 0 deletions .changeset/hip-pumpkins-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/nuxt3-module": patch
---

Refactoring and sorting of used composables
5 changes: 5 additions & 0 deletions .changeset/unlucky-snakes-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vue-demo-store": patch
---

Update error template, add terminal output for wrong nuxt config
10 changes: 9 additions & 1 deletion apps/docs/src/resources/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@ Collection of common issues you may run into while working with Shopware Composa

## Which SalesChannel type to use for Frontends?

Currently you should use the default **Storefront SalesChannel type**. This sounds wrong, but if you using the Headless SalesChannel type you will not have nice speaking seo urls at the moment. Because the generation of seo urls will only be executed for all SalesChannels with the type Storefront. We working on a more flexible solution with the core team to not have this confusion in the future.
Currently you should use the default **Storefront SalesChannel type**. This sounds wrong, but if you using the Headless SalesChannel type you will not have nice speaking seo urls at the moment. Because the generation of seo urls will only be executed for SalesChannels with the type Storefront. We working on a more flexible solution with the core team to not have this confusion in the future.

## SSR throws error in local environment with DDEV?

If you are using DDEV as a local environment with SSR = true (Nuxt config for routes) and you always get a 500 error message that the context is not provided for category, you may have a problem with the SSL certificate. Try to use `NODE_TLS_REJECT_UNAUTHORIZED = 0` in [.env file](https://nuxt.com/docs/guide/directory-structure/env) (this is a issue with self-signed certificates). To validate if this is your problem: Connect the local Frontend with a valid SSL from a cloud instance and check it against this instance. Also check if you can reach any local store API endpoint with some API client.

## 412 error page during local development?

The HTTP status code 412 (Precondition Failed) usually means in the Shopware `store API` context that the specified `shopwareAccessToken` is incorrect or not correct for the specified `shopwareEndpoint`. Check your `nuxt.config.ts` file, if you do not see an error, please try connecting directly to your `store API` endpoint using an API client.

## Access from origin 127.0.0.1:3000 has been blocked by CORS policy

Depending on your server, you may need to set the `Access-Control-Allow-Origin` header to access your server from an external origin. And yes, your local development server is also an external origin in this case. Also, have a look at this [documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSMissingAllowOrigin) from MDN.
60 changes: 30 additions & 30 deletions packages/composables/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,47 @@ import { resolveComponent } from "vue";

export * from "./types";
export * from "./cms";
export * from "./useShopwareContext";
export * from "./createShopwareContext";
export * from "./useAddToCart";
export * from "./useAddress";
export * from "./useBreadcrumbs";
export * from "./useCart";
export * from "./useCartItem";
export * from "./useCategory";
export * from "./useCategorySearch";
export * from "./useProductConfigurator";
export * from "./useProductReviews";
export * from "./useProductAssociations";
export * from "./useCheckout";
export * from "./useCmsBlock";
export * from "./useCmsMeta";
export * from "./useCmsSection";
export * from "./useNavigation";
export * from "./useCart";
export * from "./useCartItem";
export * from "./useUser";
export * from "./useSessionContext";
export * from "./useAddToCart";
export * from "./useNotifications";
export * from "./useCountries";
export * from "./useCustomerOrders";
export * from "./useCustomerPassword";
export * from "./useInternationalization";
export * from "./useLandingSearch";
export * from "./useListing";
export * from "./useProduct";
export * from "./useProductSearch";
export * from "./useCheckout";
export * from "./useSalutations";
export * from "./useCountries";
export * from "./useLocalWishlist";
export * from "./useNavigation";
export * from "./useNavigationContext";
export * from "./useNavigationSearch";
export * from "./useNewsletter";
export * from "./useNotifications";
export * from "./useOrderDetails";
export * from "./useOrderPayment";
export * from "./useLocalWishlist";
export * from "./useSyncWishlist";
export * from "./useProductSearchSuggest";
export * from "./useCustomerPassword";
export * from "./usePrice";
export * from "./useCustomerOrders";
export * from "./createShopwareContext";
export * from "./useAddress";
export * from "./useProduct";
export * from "./useProductAssociations";
export * from "./useProductConfigurator";
export * from "./useProductPrice";
export * from "./useInternationalization";
export * from "./useCmsMeta";
export * from "./useNewsletter";
export * from "./useNavigationContext";
export * from "./useNavigationSearch";
export * from "./useWishlist";
export * from "./useProductReviews";
export * from "./useProductSearch";
export * from "./useProductSearchSuggest";
export * from "./useProductWishlist";
export * from "./useBreadcrumbs";
export * from "./useSalutations";
export * from "./useSessionContext";
export * from "./useShopwareContext";
export * from "./useSyncWishlist";
export * from "./useUser";
export * from "./useWishlist";

export function resolveCmsComponent(content: CmsSection | CmsBlock | CmsSlot) {
const componentName = content.type;
Expand Down
137 changes: 89 additions & 48 deletions packages/nuxt3-module/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,75 +13,44 @@ const ShopwarePlugin = {
install(app, options) {
const runtimeConfig = useRuntimeConfig();

const useUserCookieContext =
const isUserCookieContext =
!!runtimeConfig.public?.shopware?.useUserContextInSSR ||
!options.isServer;

const contextToken = useCookie("sw-context-token", {
maxAge: 60 * 60 * 24 * 365,
sameSite: "Lax",
path: "/",
});
const languageId = useCookie("sw-language-id", {
maxAge: 60 * 60 * 24 * 365,
sameSite: "Lax",
path: "/",
});
const contextToken = getCookieContextToken();
const languageId = getCookieLanguageId();

// workaround for SSG case, where cookies contains additional dot in name, related: https://github.com/shopware/frontends/commit/ee5b8a71e1e016c973a7852efa3b85a136e6ea14
const cookieContextToken = Cookies.get("sw-context-token");
const cookieLanguageId = Cookies.get("sw-language-id");
const shopwareEndpoint =
runtimeConfig?.shopware?.shopwareEndpoint ||
runtimeConfig.public.shopware.shopwareEndpoint;

if (
!runtimeConfig.public?.shopware?.shopwareEndpoint ||
!shopwareEndpoint ||
!runtimeConfig.public?.shopware?.shopwareAccessToken
) {
throw new Error(
"Make sure that shopwareEndpoint and shopwareAccessToken are settled in the configuration",
);
}

/**
* Server config has bigger prio than client
*/
const instance = createInstance({
endpoint:
runtimeConfig?.shopware?.shopwareEndpoint ||
runtimeConfig.public.shopware.shopwareEndpoint,
accessToken: runtimeConfig.public.shopware.shopwareAccessToken,
timeout: runtimeConfig.public.shopware.shopwareAccessToken || 10000,
auth: {
username:
"<%= options.shopwareApiClient.auth ? options.shopwareApiClient.auth.username : undefined %>",
password:
"<%= options.shopwareApiClient.auth ? options.shopwareApiClient.auth.password : undefined %>",
},
contextToken: useUserCookieContext
? contextToken.value || cookieContextToken
: "",
languageId: useUserCookieContext
? languageId.value || cookieLanguageId
: undefined,
});
const instance = createApiClientInstance(
shopwareEndpoint,
runtimeConfig.public.shopware.shopwareAccessToken,
getContextToken(isUserCookieContext),
getLanguageId(isUserCookieContext),
);

/**
* Save current contextToken when its change
*/
instance.onConfigChange(({ config }) => {
try {
// only save cookies on client side render
if (useUserCookieContext) {
if (isUserCookieContext) {
contextToken.value = config.contextToken;
languageId.value = config.languageId;
Cookies.set("sw-context-token", config.contextToken || "", {
expires: 365, //days
sameSite: "Lax",
path: "/",
});
Cookies.set("sw-language-id", config.languageId || "", {
expires: 365, //days
sameSite: "Lax",
path: "/",
});
setCookieContextToken(config.contextToken);
setCookieLanguageId(config.languageId);
}
} catch (e) {
// Sometimes cookie is set on server after request is send, it can fail silently
Expand All @@ -101,6 +70,78 @@ const ShopwarePlugin = {
},
};

function getCookieContextToken() {
return useCookie("sw-context-token", {
maxAge: 60 * 60 * 24 * 365,
sameSite: "Lax",
path: "/",
});
}

function setCookieContextToken(contextToken) {
Cookies.set("sw-context-token", contextToken || "", {
expires: 365, //days
sameSite: "Lax",
path: "/",
});
}

function getContextToken(isUserCookieContext) {
const contextToken = getCookieContextToken();
// workaround for SSG case, where cookies contains additional dot in name, related: https://github.com/shopware/frontends/commit/ee5b8a71e1e016c973a7852efa3b85a136e6ea14
const cookieContextToken = Cookies.get("sw-context-token");

return isUserCookieContext ? contextToken.value || cookieContextToken : "";
}

function getCookieLanguageId() {
return useCookie("sw-language-id", {
maxAge: 60 * 60 * 24 * 365,
sameSite: "Lax",
path: "/",
});
}

function setCookieLanguageId(languageId) {
Cookies.set("sw-language-id", languageId || "", {
expires: 365, //days
sameSite: "Lax",
path: "/",
});
}

function getLanguageId(isUserCookieContext) {
const languageId = getCookieLanguageId();
// workaround for SSG case, where cookies contains additional dot in name, related: https://github.com/shopware/frontends/commit/ee5b8a71e1e016c973a7852efa3b85a136e6ea14
const cookieLanguageId = Cookies.get("sw-language-id");

return isUserCookieContext ? languageId.value || cookieLanguageId : undefined;
}

/**
* Server config has bigger prio than client
*/
function createApiClientInstance(
shopwareEndpoint,
shopwareAccessToken,
contextToken,
languageId,
) {
return createInstance({
endpoint: shopwareEndpoint,
accessToken: shopwareAccessToken,
timeout: shopwareAccessToken || 10000,
auth: {
username:
"<%= options.shopwareApiClient.auth ? options.shopwareApiClient.auth.username : undefined %>",
password:
"<%= options.shopwareApiClient.auth ? options.shopwareApiClient.auth.password : undefined %>",
},
contextToken: contextToken,
languageId: languageId,
});
}

export default defineNuxtPlugin(async (nuxtApp) => {
nuxtApp.vueApp.use(ShopwarePlugin, {
apiDefaults: getDefaultApiParams(),
Expand Down
52 changes: 27 additions & 25 deletions packages/nuxt3-module/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,27 @@ export default defineNuxtModule<ShopwareNuxtOptions>({
configKey: "shopware",
},
async setup(moduleConfig, nuxt) {
const shopwareEndpoint =
moduleConfig.shopwareEndpoint ?? "https://demo-frontends.shopware.store";
const accessToken =
moduleConfig.shopwareAccessToken ?? "SWSCBHFSNTVMAWNZDNFKSHLAYW";

addPluginTemplate({
filename: "runtime/shopware.plugin.mjs",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
src: resolve(__dirname, "../plugin.ts"),
options: {
shopwareEndpoint:
moduleConfig.shopwareEndpoint ??
"https://demo-frontends.shopware.store",
shopwareAccessToken:
moduleConfig.shopwareAccessToken ?? "SWSCBHFSNTVMAWNZDNFKSHLAYW",
shopwareEndpoint: shopwareEndpoint,
shopwareAccessToken: accessToken,
shopwareApiClient: {
timeout: moduleConfig.apiClientConfig?.timeout ?? "10000",
},
},
});

// TODO: remove it once nitro server build contains all external packages of nuxt3-module (composables-next)
nuxt.options.build.transpile.push("@shopware-pwa/composables-next");
//nuxt.options.build.transpile.push("@shopware-pwa/composables-next");

// use a module's dependency in order to not install it again within an end-project to keep compatibility
const apiClientDependencyPath = await resolveOwnDependency(
Expand Down Expand Up @@ -62,26 +64,33 @@ export default defineNuxtModule<ShopwareNuxtOptions>({
dirs.push({
from: "@shopware-pwa/composables-next",
imports: [
"useAddress",
"useAddToCart",
"useCheckout",
"useCountries",
"useCustomerPassword",
"useCmsElementConfig",
"useShopwareContext",
"useSessionContext",
"useBreadcrumbs",
"useCart",
"useCartItem",
"useCategory",
"useCategorySearch",
"useCheckout",
"useCms",
"useCmsBlock",
"useCmsElementConfig",
"useCmsMeta",
"useCmsSection",
"useCountries",
"useCustomerOrders",
"useCustomerPassword",
"useInternationalization",
"useLandingSearch",
"useListing",
"useNavigation",
"useNavigationContext",
"useNavigationSearch",
"useNewsletter",
"useNotifications",
"useLandingSearch",
"useListing",
"useCms",
"useCmsBlock",
"useOrderDetails",
"useOrderPayment",
"usePrice",
"useProduct",
"useProductAssociations",
"useProductConfigurator",
Expand All @@ -91,17 +100,10 @@ export default defineNuxtModule<ShopwareNuxtOptions>({
"useProductSearchSuggest",
"useProductWishlist",
"useSalutations",
"useSessionContext",
"useShopwareContext",
"useUser",
"useWishlist",
"usePrice",
"useCustomerOrders",
"useAddress",
"useCmsMeta",
"useNewsletter",
"useNavigationContext",
"useOrderPayment",
"useBreadcrumbs",
"useInternationalization",
],
});
});
Expand Down
Loading

2 comments on commit f356675

@vercel
Copy link

@vercel vercel bot commented on f356675 Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on f356675 Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

frontends-demo – ./templates/vue-demo-store

frontends-demo-git-main-shopware-frontends.vercel.app
frontends-demo-shopware-frontends.vercel.app
frontends-demo.vercel.app

Please sign in to comment.