Update Dockerfile (#2523)

Co-authored-by: Francisco Marques <franciscopcmarques@gmail.com>
This commit is contained in:
Krzysztof Żuraw 2022-11-16 16:01:34 +01:00 committed by GitHub
parent 1eecdfa5c1
commit ac063c6410
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 222 additions and 76 deletions

View file

@ -0,0 +1,74 @@
name: Publish container image
on:
push:
tags:
# Matches stable and pre-releases
- "[0-9]+.[0-9]+.[0-9]+*"
jobs:
docker:
runs-on: ubuntu-20.04
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v3
# Outputs the name of the repository (owner/repo)
- name: Get image name
id: image
run: |
IMAGE_NAME=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')
echo ::set-output name=image_name::ghcr.io/${IMAGE_NAME}
# Tags stable versions as :latest
# Pre-releases, alphas, etc. as :snapshot
- name: Output image tags from git tag events
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |
# Remove everything else than the tagged version
DASHBOARD_VERSION=${GITHUB_REF#refs/tags/}
echo "
DASHBOARD_VERSION=${DASHBOARD_VERSION}
CONTAINER_TAGS=${{ steps.image.outputs.image_name }}:${DASHBOARD_VERSION}
" >> "${GITHUB_ENV}"
- name: Set up Docker QEMU
uses: docker/setup-qemu-action@v1
with:
platforms: arm64
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
context: ./
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.CONTAINER_TAGS }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
build-args: |
COMMIT_ID=${{ github.sha }}
PROJECT_VERSION=$DASHBOARD_VERSION
- name: Output image digest
run: |
echo $"\
Digest: ${{ steps.docker_build.outputs.digest }}
Tags: ${{ env.CONTAINER_TAGS }}"

View file

@ -1,19 +1,46 @@
FROM node:18 as builder
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ARG APP_MOUNT_URI
RUN npm ci --legacy-peer-deps
COPY nginx/ nginx/
COPY assets/ assets/
COPY locale/ locale/
COPY testUtils testUtils/
COPY codegen.yml .
COPY webpack.config.js .
COPY tsconfig.json .
COPY *.d.ts .
COPY schema.graphql .
COPY introspection.json .
COPY src/ src/
ARG API_URI
ARG APP_MOUNT_URI
ARG MARKETPLACE_URL
ARG SALEOR_APPS_ENDPOINT
ARG STATIC_URL
ENV API_URI ${API_URI:-http://localhost:8000/graphql/}
ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/dashboard/}
ENV MARKETPLACE_URL ${MARKETPLACE_URL}
ENV SALEOR_APPS_ENDPOINT=${SALEOR_APPS_ENDPOINT}
ENV STATIC_URL ${STATIC_URL:-/dashboard/}
RUN STATIC_URL=${STATIC_URL} API_URI=${API_URI} MARKETPLACE_URL=${MARKETPLACE_URL} SALEOR_APPS_ENDPOINT=${SALEOR_APPS_ENDPOINT} APP_MOUNT_URI=${APP_MOUNT_URI} npm run build
FROM nginx:stable
RUN npm run build
FROM nginx:stable-alpine as runner
WORKDIR /app
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY ./nginx/replace-api-url.sh /docker-entrypoint.d/50-replace-api-url.sh
COPY --from=builder /app/build/ /app/
LABEL org.opencontainers.image.title="saleor/saleor-dashboard" \
org.opencontainers.image.description="A GraphQL-powered, single-page dashboard application for Saleor." \
org.opencontainers.image.url="https://saleor.io/" \
org.opencontainers.image.source="https://github.com/saleor/saleor-dashboard" \
org.opencontainers.image.revision="$COMMIT_ID" \
org.opencontainers.image.version="$PROJECT_VERSION" \
org.opencontainers.image.authors="Saleor Commerce (https://saleor.io)" \
org.opencontainers.image.licenses="BSD 3"

View file

@ -1,16 +0,0 @@
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ARG APP_MOUNT_URI
ARG API_URI
ARG MARKETPLACE_URL
ARG SALEOR_APPS_ENDPOINT
ARG STATIC_URL
ENV API_URI ${API_URI:-http://localhost:8000/graphql/}
ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/}
ENV STATIC_URL ${STATIC_URL:-/}
EXPOSE 9000
CMD npm start -- --host 0.0.0.0

View file

@ -145,6 +145,22 @@ You are ready to run cypress commands like:
npm run cy:open
```
### Usage with docker
Build docker image:
```shell
docker build --tag saleor-dashboard .
```
Run nginx from docker and bind it to port on your machine (in this example 8080):
```shell
docker run --publish 8080:80 --env "API_URL=<YOUR_API_URL>" saleor-dashboard
```
Enter `http://localhost:8080/` to use dashboard.
##### Usage with Sentry adapter:
Sentry is used as the default tracker so no changes in code are necessary and the configuration is done via environment variables.

View file

@ -29,7 +29,7 @@ export const urlList = {
variants: "variant/",
vouchers: "discounts/vouchers/",
variant: "variant/",
warehouses: "warehouses/"
warehouses: "warehouses/",
};
export const addVariantUrl = productId =>

View file

@ -38,7 +38,7 @@ module.exports = async (on, config) => {
config.env.mailHogUrl = process.env.CYPRESS_mailHogUrl;
config.env.grepTags = process.env.CYPRESS_grepTags;
on("before:browser:launch", ({}, launchOptions) => {
on("before:browser:launch", (_browser = {}, launchOptions) => {
launchOptions.args.push("--proxy-bypass-list=<-loopback>");
return launchOptions;
});

View file

@ -1,19 +1,19 @@
// / <reference types="cypress" />
import "./customCommands/user";
import "cypress-file-upload";
import "cypress-mailhog";
import "cypress-mochawesome-reporter/register";
import "./customCommands/basicOperations";
import "./customCommands/deleteElementsViaApi";
import "./customCommands/softAssertions";
import "./customCommands/sharedElementsOperations/addressForm.js";
import "./customCommands/sharedElementsOperations/assignElementsForm.js";
import "./customCommands/sharedElementsOperations/confirmationMessages.js";
import "./customCommands/sharedElementsOperations/deleteElement";
import "./customCommands/sharedElementsOperations/progressBar.js";
import "./customCommands/sharedElementsOperations/selects.js";
import "./customCommands/sharedElementsOperations/tables";
import "./customCommands/sharedElementsOperations/deleteElement";
import "cypress-mailhog";
import "cypress-file-upload";
import "cypress-mochawesome-reporter/register";
import "./customCommands/softAssertions";
import "./customCommands/user";
import { commandTimings } from "cypress-timings";
@ -49,7 +49,7 @@ Cypress.Commands.add("addAliasToGraphRequest", operationName => {
Cypress.on(
"uncaught:exception",
(err, runnable) =>
(_err, _runnable) =>
// returning false here prevents Cypress from
// failing the test
false,

16
nginx/replace-api-url.sh Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/env sh
# Replaces the API_URL from the bundle's index.html file with the $API_URL env var.
# This script is automatically picked up by the nginx entrypoint on startup.
set -e
INDEX_BUNDLE_PATH="/app/dashboard/index.html"
if [ -z "${API_URL}" ]; then
echo "No API_URL provided, using defaults."
else
echo "Setting API_URL to: $API_URL"
sed -i "s#API_URL:.*#API_URL: \"$API_URL\",#" "$INDEX_BUNDLE_PATH"
fi

View file

@ -6,7 +6,7 @@ import {
RedirectAction,
} from "@saleor/app-sdk/app-bridge";
import { appPath } from "@saleor/apps/urls";
import { APP_MOUNT_URI } from "@saleor/config";
import { getAppMountUri } from "@saleor/config";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import React from "react";
@ -78,7 +78,7 @@ export const useAppActions = (
if (newContext) {
window.open(to);
} else if (appDeepUrlChange) {
const exactLocation = urlJoin(APP_MOUNT_URI, to);
const exactLocation = urlJoin(getAppMountUri(), to);
// Change only url without reloading if we are in the same app
window.history.pushState(null, "", exactLocation);

View file

@ -10,14 +10,14 @@ import { FormattedMessage } from "react-intl";
import { useStyles } from "./styles";
export interface CustomAppDefaultTokenProps {
apiUri: string;
apiUrl: string;
token: string;
onApiUriClick: () => void;
onApiUrlClick: () => void;
onTokenClose: () => void;
}
const CustomAppDefaultToken: React.FC<CustomAppDefaultTokenProps> = props => {
const { apiUri, token, onApiUriClick, onTokenClose } = props;
const { apiUrl, token, onApiUrlClick, onTokenClose } = props;
const classes = useStyles(props);
const [copied, copy] = useClipboard();
@ -38,8 +38,8 @@ const CustomAppDefaultToken: React.FC<CustomAppDefaultTokenProps> = props => {
defaultMessage="This token gives you access to your shop's API, which you'll find here: {url}"
values={{
url: (
<Link href={apiUri} onClick={onApiUriClick}>
{apiUri}
<Link href={apiUrl} onClick={onApiUrlClick}>
{apiUrl}
</Link>
),
}}

View file

@ -36,14 +36,14 @@ export interface CustomAppDetailsPageFormData {
permissions: PermissionEnum[];
}
export interface CustomAppDetailsPageProps {
apiUri: string;
apiUrl: string;
disabled: boolean;
errors: AppErrorFragment[];
permissions: ShopInfoQuery["shop"]["permissions"];
saveButtonBarState: ConfirmButtonTransitionState;
app: AppUpdateMutation["appUpdate"]["app"];
token: string;
onApiUriClick: () => void;
onApiUrlClick: () => void;
onTokenDelete: (id: string) => void;
onTokenClose: () => void;
onTokenCreate: () => void;
@ -58,14 +58,14 @@ export interface CustomAppDetailsPageProps {
const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
const {
apiUri,
apiUrl,
disabled,
errors,
permissions,
saveButtonBarState,
app,
token,
onApiUriClick,
onApiUrlClick,
onTokenClose,
onTokenCreate,
onTokenDelete,
@ -136,9 +136,9 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
{token && (
<>
<CustomAppDefaultToken
apiUri={apiUri}
apiUrl={apiUrl}
token={token}
onApiUriClick={onApiUriClick}
onApiUrlClick={onApiUrlClick}
onTokenClose={onTokenClose}
/>
<CardSpacer />

View file

@ -5,7 +5,7 @@ import TokenDeleteDialog from "@saleor/apps/components/TokenDeleteDialog";
import { appMessages } from "@saleor/apps/messages";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { WindowTitle } from "@saleor/components/WindowTitle";
import { API_URI } from "@saleor/config";
import { getApiUrl } from "@saleor/config";
import {
AppTokenCreateMutation,
AppTokenDeleteMutation,
@ -220,11 +220,11 @@ export const CustomAppDetails: React.FC<OrderListProps> = ({
<>
<WindowTitle title={getStringOrPlaceholder(customApp?.name)} />
<CustomAppDetailsPage
apiUri={API_URI}
apiUrl={getApiUrl()}
disabled={loading}
errors={updateAppOpts.data?.appUpdate?.errors || []}
token={token}
onApiUriClick={() => open(API_URI, "blank")}
onApiUrlClick={() => open(getApiUrl(), "blank")}
onSubmit={handleSubmit}
onTokenClose={onTokenClose}
onTokenCreate={() => openModal("create-token")}

View file

@ -1,3 +1,4 @@
import { getApiUrl } from "@saleor/config";
import { createSaleorClient, SaleorProvider } from "@saleor/sdk";
import setupApi from "@test/api";
import { act, renderHook } from "@testing-library/react-hooks";
@ -15,7 +16,7 @@ function renderAuthProvider() {
};
const notify = jest.fn();
const saleorClient = createSaleorClient({
apiUrl: process.env.API_URI,
apiUrl: getApiUrl(),
channel: "",
});
const wrapper = ({ children }) => (
@ -50,7 +51,7 @@ beforeEach(() => {
sessionStorage.clear();
});
xdescribe("User", () => {
describe("User", () => {
it("will be logged in if has valid credentials", async done => {
const hook = renderAuthProvider();

View file

@ -3,7 +3,7 @@ import { Button } from "@saleor/components/Button";
import Form from "@saleor/components/Form";
import FormSpacer from "@saleor/components/FormSpacer";
import { IconButton } from "@saleor/components/IconButton";
import { APP_MOUNT_URI } from "@saleor/config";
import { getAppMountUri } from "@saleor/config";
import { RequestPasswordResetMutation } from "@saleor/graphql";
import { SubmitPromise } from "@saleor/hooks/useForm";
import { commonMessages } from "@saleor/intl";
@ -36,7 +36,7 @@ const ResetPasswordPage: React.FC<ResetPasswordPageProps> = props => {
<Form initial={{ email: "" }} onSubmit={onSubmit}>
{({ change: handleChange, data, submit: handleSubmit }) => (
<>
<IconButton className={classes.backBtn} href={APP_MOUNT_URI}>
<IconButton className={classes.backBtn} href={getAppMountUri()}>
<ArrowRightIcon className={classes.arrow} />
</IconButton>
<Typography variant="h3" className={classes.header}>

View file

@ -1,6 +1,6 @@
import { ApolloClient } from "@apollo/client";
import { IMessageContext } from "@saleor/components/messages";
import { APP_DEFAULT_URI, APP_MOUNT_URI, DEMO_MODE } from "@saleor/config";
import { DEMO_MODE } from "@saleor/config";
import { useUserDetailsQuery } from "@saleor/graphql";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import useNavigator from "@saleor/hooks/useNavigator";
@ -16,6 +16,7 @@ import {
login as loginWithCredentialsManagementAPI,
saveCredentials,
} from "@saleor/utils/credentialsManagement";
import { getAppMountUriForRedirect } from "@saleor/utils/urls";
import { useEffect, useRef, useState } from "react";
import { IntlShape } from "react-intl";
import urlJoin from "url-join";
@ -88,8 +89,10 @@ export function useAuthProvider({
});
const handleLogout = async () => {
const path = APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI;
const returnTo = urlJoin(window.location.origin, path);
const returnTo = urlJoin(
window.location.origin,
getAppMountUriForRedirect(),
);
const result = await logout({
input: JSON.stringify({

View file

@ -1,7 +1,7 @@
import { APP_DEFAULT_URI, APP_MOUNT_URI } from "@saleor/config";
import { useAvailableExternalAuthenticationsQuery } from "@saleor/graphql";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import useNavigator from "@saleor/hooks/useNavigator";
import { getAppMountUriForRedirect } from "@saleor/utils/urls";
import React, { useEffect } from "react";
import urlJoin from "url-join";
import useRouter from "use-react-router";
@ -52,7 +52,7 @@ const LoginView: React.FC<LoginViewProps> = ({ params }) => {
const result = await requestLoginByExternalPlugin(pluginId, {
redirectUri: urlJoin(
window.location.origin,
APP_MOUNT_URI === APP_DEFAULT_URI ? "" : APP_MOUNT_URI,
getAppMountUriForRedirect(),
loginCallbackPath,
),
});

View file

@ -1,8 +1,8 @@
import { APP_MOUNT_URI } from "@saleor/config";
import { useRequestPasswordResetMutation } from "@saleor/graphql";
import useNavigator from "@saleor/hooks/useNavigator";
import { commonMessages } from "@saleor/intl";
import { extractMutationErrors } from "@saleor/misc";
import { getAppMountUriForRedirect } from "@saleor/utils/urls";
import React from "react";
import { useIntl } from "react-intl";
import urlJoin from "url-join";
@ -49,7 +49,7 @@ const ResetPasswordView: React.FC = () => {
email: data.email,
redirectUrl: urlJoin(
window.location.origin,
APP_MOUNT_URI === "/" ? "" : APP_MOUNT_URI,
getAppMountUriForRedirect(),
newPasswordUrl().replace(/\?/, ""),
),
},

View file

@ -1,4 +1,4 @@
import { APP_MOUNT_URI } from "@saleor/config";
import { getAppMountUri } from "@saleor/config";
import useNavigator from "@saleor/hooks/useNavigator";
import React from "react";
@ -7,7 +7,7 @@ import ResetPasswordSuccessPage from "../components/ResetPasswordSuccessPage";
const ResetPasswordSuccessView: React.FC = () => {
const navigate = useNavigator();
return <ResetPasswordSuccessPage onBack={() => navigate(APP_MOUNT_URI)} />;
return <ResetPasswordSuccessPage onBack={() => navigate(getAppMountUri())} />;
};
ResetPasswordSuccessView.displayName = "ResetPasswordSuccessView";
export default ResetPasswordSuccessView;

View file

@ -2,10 +2,10 @@ import packageInfo from "../package.json";
import { SearchVariables } from "./hooks/makeSearch";
import { ListSettings, ListViews, Pagination } from "./types";
export const IS_TEST = process.env.NODE_ENV === "test";
export const APP_MOUNT_URI = IS_TEST ? "/" : process.env.APP_MOUNT_URI || "/";
export const APP_DEFAULT_URI = "/";
export const API_URI = process.env.API_URI;
export const getAppDefaultUri = () => "/";
export const getAppMountUri = () =>
window.__SALEOR_CONFIG__.APP_MOUNT_URI || getAppDefaultUri();
export const getApiUrl = () => window.__SALEOR_CONFIG__.API_URL;
export const SW_INTERVAL = parseInt(process.env.SW_INTERVAL, 10);
export const IS_CLOUD_INSTANCE = process.env.IS_CLOUD_INSTANCE === "true";
export const MARKETPLACE_URL = process.env.MARKETPLACE_URL;

View file

@ -4,7 +4,7 @@ import { ApolloClient, ApolloLink, InMemoryCache } from "@apollo/client";
import { createFetch, createSaleorClient } from "@saleor/sdk";
import { createUploadLink } from "apollo-upload-client";
import { API_URI } from "../config";
import { getApiUrl } from "../config";
import introspectionQueryResultData from "./fragmentTypes.generated";
import { TypedTypePolicies } from "./typePolicies.generated";
@ -21,7 +21,7 @@ const attachVariablesLink = new ApolloLink((operation, forward) =>
export const link = attachVariablesLink.concat(
createUploadLink({
credentials: "include",
uri: API_URI,
uri: getApiUrl(),
fetch: createFetch(),
}),
);
@ -70,6 +70,6 @@ export const apolloClient = new ApolloClient({
});
export const saleorClient = createSaleorClient({
apiUrl: API_URI,
apiUrl: getApiUrl(),
channel: "",
});

View file

@ -8,6 +8,12 @@
/>
<meta name="robots" content="noindex" />
<title>Saleor e-commerce</title>
<script>
window.__SALEOR_CONFIG__ = {
API_URL: "<%= API_URL %>",
APP_MOUNT_URI: "<%= APP_MOUNT_URI %>",
};
</script>
</head>
<body>

View file

@ -36,7 +36,7 @@ import { LocaleProvider } from "./components/Locale";
import MessageManagerProvider from "./components/messages";
import { ShopProvider } from "./components/Shop";
import { WindowTitle } from "./components/WindowTitle";
import { APP_MOUNT_URI, DEMO_MODE, GTM_ID } from "./config";
import { DEMO_MODE, getAppMountUri, GTM_ID } from "./config";
import ConfigurationSection from "./configuration";
import { getConfigMenuItemsPermissions } from "./configuration/utils";
import AppStateProvider from "./containers/AppState";
@ -80,7 +80,7 @@ errorTracker.init();
const App: React.FC = () => (
<SaleorProvider client={saleorClient}>
<ApolloProvider client={apolloClient}>
<BrowserRouter basename={APP_MOUNT_URI}>
<BrowserRouter basename={getAppMountUri()}>
<ThemeProvider overrides={themeOverrides}>
<DateProvider>
<LocaleProvider>

View file

@ -4,7 +4,7 @@ import SaveFilterTabDialog, {
SaveFilterTabDialogFormData,
} from "@saleor/components/SaveFilterTabDialog";
import { useShopLimitsQuery } from "@saleor/components/Shop/queries";
import { APP_MOUNT_URI, DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import { useStaffListQuery, useStaffMemberAddMutation } from "@saleor/graphql";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
@ -23,6 +23,7 @@ import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
import createSortHandler from "@saleor/utils/handlers/sortHandler";
import { mapEdgesToItems } from "@saleor/utils/maps";
import { getSortParams } from "@saleor/utils/sort";
import { getAppMountUriForRedirect } from "@saleor/utils/urls";
import React from "react";
import { useIntl } from "react-intl";
import urlJoin from "url-join";
@ -159,7 +160,7 @@ export const StaffList: React.FC<StaffListProps> = ({ params }) => {
lastName: variables.lastName,
redirectUrl: urlJoin(
window.location.origin,
APP_MOUNT_URI === "/" ? "" : APP_MOUNT_URI,
getAppMountUriForRedirect(),
newPasswordUrl().replace(/\?/, ""),
),
},

View file

@ -10,7 +10,7 @@ import { ApolloMockedProvider } from "../../testUtils/ApolloMockedProvider";
import { Provider as DateProvider } from "../components/Date/DateContext";
import MessageManagerProvider from "../components/messages";
import { TimezoneProvider } from "../components/Timezone";
import { APP_MOUNT_URI } from "../config";
import { getAppMountUri } from "../config";
export const Decorator = storyFn => (
<ApolloMockedProvider>
@ -24,7 +24,7 @@ export const Decorator = storyFn => (
<DateProvider value={+new Date("2018-08-07T14:30:44+00:00")}>
<TimezoneProvider value="America/New_York">
<ThemeProvider overrides={themeOverrides}>
<BrowserRouter basename={APP_MOUNT_URI}>
<BrowserRouter basename={getAppMountUri()}>
<ExternalAppProvider>
<MessageManagerProvider>
<div

View file

@ -1,3 +1,4 @@
import { getAppDefaultUri, getAppMountUri } from "@saleor/config";
import isArray from "lodash/isArray";
import { stringify } from "qs";
@ -20,3 +21,6 @@ export function getArrayQueryParam(param: string | string[]): string[] {
}
export const isExternalURL = url => /^https?:\/\//.test(url);
export const getAppMountUriForRedirect = () =>
getAppMountUri() === getAppDefaultUri() ? "" : getAppMountUri();

View file

@ -3,6 +3,7 @@ import { BatchHttpLink } from "@apollo/client/link/batch-http";
import NodeHttpAdapter from "@pollyjs/adapter-node-http";
import { Polly } from "@pollyjs/core";
import FSPersister from "@pollyjs/persister-fs";
import { getApiUrl } from "@saleor/config";
import { createFetch } from "@saleor/sdk";
import path from "path";
import { setupPolly } from "setup-polly-jest";
@ -36,7 +37,7 @@ function setupApi() {
const cache = new InMemoryCache();
const link = new BatchHttpLink({
fetch: createFetch(),
uri: process.env.API_URI || "http://localhost:8000/graphql/",
uri: getApiUrl(),
});
const apolloClient = new ApolloClient({
cache,

View file

@ -1 +1,6 @@
document.getElementById = () => document.createElement("div");
window.__SALEOR_CONFIG__ = {
API_URL: "http://localhost:8000/graphql/",
APP_MOUNT_URI: "/",
};

View file

@ -35,12 +35,16 @@ const htmlWebpackPlugin = new HtmlWebpackPlugin({
filename: "index.html",
hash: true,
template: "./src/index.html",
templateParameters: {
// URI is kept for backwards compatibility.
// See more at https://github.com/saleor/saleor-dashboard/issues/2502
API_URL: process.env.API_URI,
APP_MOUNT_URI: process.env.APP_MOUNT_URI,
},
});
const environmentPlugin = new webpack.EnvironmentPlugin({
API_URI: "",
MARKETPLACE_URL: "",
SALEOR_APPS_ENDPOINT: "",
APP_MOUNT_URI: "/",
DEMO_MODE: false,
ENVIRONMENT: "",
GTM_ID: "",

4
webpack.d.ts vendored
View file

@ -7,4 +7,8 @@ declare module "*.svg" {
declare interface Window {
PasswordCredential: PasswordCredential;
__SALEOR_CONFIG__: {
API_URL: string;
APP_MOUNT_URI: string;
};
}