@akinon/next 1.21.0-rc.1 → 1.21.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/api/auth.ts +11 -0
- package/api/client.ts +6 -1
- package/bin/pz-install-plugins.js +59 -27
- package/bin/pz-install-theme.js +0 -0
- package/bin/pz-postbuild.js +0 -0
- package/bin/pz-postdev.js +0 -0
- package/bin/pz-postinstall.js +0 -0
- package/bin/pz-poststart.js +0 -0
- package/bin/pz-prebuild.js +0 -0
- package/bin/pz-predev.js +0 -0
- package/bin/pz-prestart.js +0 -0
- package/data/client/checkout.ts +15 -5
- package/data/client/user.ts +18 -2
- package/data/client/wishlist.ts +2 -0
- package/data/server/category.ts +3 -3
- package/data/server/list.ts +1 -1
- package/data/urls.ts +2 -1
- package/hooks/use-pagination.ts +0 -4
- package/middlewares/checkout-provider.ts +88 -0
- package/middlewares/default.ts +145 -104
- package/middlewares/index.ts +3 -1
- package/package.json +2 -2
- package/plugins.d.ts +4 -0
- package/redux/reducers/index.ts +3 -1
- package/sentry/index.ts +20 -14
- package/types/gtm.ts +48 -0
- package/types/index.ts +12 -3
- package/utils/app-fetch.ts +17 -6
- package/utils/image-loader.ts +11 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.21.0-rc.10
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0181251: ZERO-2440: move otp popup state to redux
|
|
8
|
+
|
|
9
|
+
## 1.21.0-rc.9
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 8d6caba: ZERO-2434: enhance error handling and logging in appFetch function
|
|
14
|
+
|
|
15
|
+
## 1.21.0-rc.8
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 07cc81a: Add infinite and more types to pagination
|
|
20
|
+
|
|
21
|
+
## 1.21.0-rc.7
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 5ace3508: ZERO-2439: Add anonymous tracking page
|
|
26
|
+
- be04d041: ZERO-2452: UA events converted to be compatible with GA4 and types are declared
|
|
27
|
+
- c75e1e45: ZERO-2452: GA4 support for gtm events
|
|
28
|
+
|
|
29
|
+
## 1.21.0-rc.6
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- b4452e9d: ZERO-2463: Refactor Sentry initialization and add Sentry DSN option to settings
|
|
34
|
+
|
|
35
|
+
## 1.21.0-rc.5
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- ZERO-2296: Fix ROUTES import
|
|
40
|
+
|
|
41
|
+
## 1.21.0-rc.4
|
|
42
|
+
|
|
43
|
+
### Minor Changes
|
|
44
|
+
|
|
45
|
+
- Revert ZERO-2435
|
|
46
|
+
|
|
47
|
+
## 1.21.0-rc.3
|
|
48
|
+
|
|
49
|
+
### Minor Changes
|
|
50
|
+
|
|
51
|
+
- af8e38e0: ZERO-2247: Add additionalParams to MasterpassProvider and set them in buildPurchaseForm and buildDirectPurchaseForm
|
|
52
|
+
- 16027410: ZERO-2451: Refactor extraHeaders assignment in client.ts
|
|
53
|
+
- 9edd725b: Datalayer init changed to 3rd party script. Declare is the same as next
|
|
54
|
+
|
|
55
|
+
## 1.21.0-rc.2
|
|
56
|
+
|
|
57
|
+
### Minor Changes
|
|
58
|
+
|
|
59
|
+
- 8075006: install plugins check the akinon version
|
|
60
|
+
- 32668ed: ZERO-2296: Implement Checkout Provider Middleware and Enhancements
|
|
61
|
+
- 29191da: ImageLoader supports crop none
|
|
62
|
+
|
|
3
63
|
## 1.21.0-rc.1
|
|
4
64
|
|
|
5
65
|
### Minor Changes
|
package/api/auth.ts
CHANGED
|
@@ -220,6 +220,17 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
220
220
|
pages: {
|
|
221
221
|
signIn: ROUTES.AUTH,
|
|
222
222
|
error: ROUTES.AUTH
|
|
223
|
+
},
|
|
224
|
+
cookies: {
|
|
225
|
+
sessionToken: {
|
|
226
|
+
name: `__Secure-next-auth.session-token`,
|
|
227
|
+
options: {
|
|
228
|
+
httpOnly: true,
|
|
229
|
+
sameSite: 'none',
|
|
230
|
+
path: '/',
|
|
231
|
+
secure: true
|
|
232
|
+
}
|
|
233
|
+
}
|
|
223
234
|
}
|
|
224
235
|
};
|
|
225
236
|
};
|
package/api/client.ts
CHANGED
|
@@ -43,13 +43,18 @@ async function proxyRequest(...args) {
|
|
|
43
43
|
urlSearchParams.append(key, `${searchParams.get(key)}`);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
const extraHeaders =
|
|
46
|
+
const extraHeaders: Record<string, string> = {};
|
|
47
|
+
|
|
48
|
+
for (const [key, value] of Array.from(req.headers.entries())) {
|
|
49
|
+
extraHeaders[key.toLowerCase()] = value;
|
|
50
|
+
}
|
|
47
51
|
|
|
48
52
|
[
|
|
49
53
|
'x-forwarded-host',
|
|
50
54
|
'x-forwarded-for',
|
|
51
55
|
'x-forwarded-proto',
|
|
52
56
|
'x-forwarded-port',
|
|
57
|
+
'x-requested-with',
|
|
53
58
|
'origin',
|
|
54
59
|
'host',
|
|
55
60
|
'referer',
|
|
@@ -1,39 +1,71 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
const fs = require('fs');
|
|
4
2
|
const path = require('path');
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
function findBaseDir() {
|
|
6
|
+
const insideNodeModules = __dirname.includes('node_modules');
|
|
7
|
+
return insideNodeModules
|
|
8
|
+
? process.cwd()
|
|
9
|
+
: path.resolve(__dirname, '../../../apps/projectzeronext');
|
|
10
|
+
}
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
const BASE_DIR = findBaseDir();
|
|
13
|
+
const getFullPath = (relativePath) => path.join(BASE_DIR, relativePath);
|
|
14
|
+
|
|
15
|
+
const packageJsonPath = getFullPath('package.json');
|
|
16
|
+
let packageJson;
|
|
10
17
|
|
|
11
18
|
try {
|
|
12
|
-
|
|
19
|
+
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
13
20
|
} catch (error) {
|
|
14
|
-
console.error('
|
|
15
|
-
process.exit(
|
|
21
|
+
console.error('Error reading package.json:', error);
|
|
22
|
+
process.exit(1);
|
|
16
23
|
}
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
const pluginsJsPath = getFullPath('src/plugins.js');
|
|
26
|
+
let installedPlugins;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
installedPlugins = require(pluginsJsPath);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Error loading installed plugins:', error);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
.filter((p) => plugins?.includes(p))
|
|
22
|
-
.forEach((name) => {
|
|
23
|
-
installCmd.push(`@akinon/${name}`);
|
|
24
|
-
});
|
|
35
|
+
const protectedPackages = ['@akinon/next', 'next'];
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
const pluginsToRemove = Object.keys(packageJson.dependencies || {}).filter(
|
|
38
|
+
(dep) =>
|
|
39
|
+
dep.startsWith('@akinon/') &&
|
|
40
|
+
!installedPlugins.includes(dep.replace('@akinon/', '')) &&
|
|
41
|
+
!protectedPackages.includes(dep)
|
|
42
|
+
);
|
|
27
43
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
44
|
+
pluginsToRemove.forEach((plugin) => {
|
|
45
|
+
try {
|
|
46
|
+
execSync(`yarn remove ${plugin}`, { stdio: 'inherit', cwd: BASE_DIR });
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.warn(
|
|
49
|
+
`Warning: Could not remove ${plugin}. It may not have been installed.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
31
53
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
installedPlugins.forEach((plugin) => {
|
|
55
|
+
const packageName = `@akinon/${plugin}`;
|
|
56
|
+
if (
|
|
57
|
+
!Object.keys(packageJson.dependencies || {}).includes(packageName) &&
|
|
58
|
+
!protectedPackages.includes(packageName)
|
|
59
|
+
) {
|
|
60
|
+
try {
|
|
61
|
+
const version = packageJson.dependencies['@akinon/next'].replace('^', '');
|
|
62
|
+
const command = `yarn add ${packageName}@${version} --exact --ignore-scripts`;
|
|
63
|
+
execSync(command, { stdio: 'inherit', cwd: BASE_DIR });
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.warn(
|
|
66
|
+
'\n\x1b[33m%s\x1b[0m',
|
|
67
|
+
`Error adding ${packageName}: ${error}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
package/bin/pz-install-theme.js
CHANGED
|
File without changes
|
package/bin/pz-postbuild.js
CHANGED
|
File without changes
|
package/bin/pz-postdev.js
CHANGED
|
File without changes
|
package/bin/pz-postinstall.js
CHANGED
|
File without changes
|
package/bin/pz-poststart.js
CHANGED
|
File without changes
|
package/bin/pz-prebuild.js
CHANGED
|
File without changes
|
package/bin/pz-predev.js
CHANGED
|
File without changes
|
package/bin/pz-prestart.js
CHANGED
|
File without changes
|
package/data/client/checkout.ts
CHANGED
|
@@ -83,12 +83,18 @@ const completeMasterpassPayment = async (
|
|
|
83
83
|
reduxStore: AppStore
|
|
84
84
|
) => {
|
|
85
85
|
const state = reduxStore.getState();
|
|
86
|
-
const { token, order_no } = context.page_context;
|
|
86
|
+
const { token, order_no, extras } = context.page_context;
|
|
87
87
|
const { endpoints: apiEndpoints } = checkoutApi;
|
|
88
88
|
const { preOrder } = state.checkout ?? {};
|
|
89
89
|
|
|
90
|
-
const {
|
|
91
|
-
|
|
90
|
+
const {
|
|
91
|
+
isDirectPurchase,
|
|
92
|
+
referenceNo,
|
|
93
|
+
credentials,
|
|
94
|
+
msisdn,
|
|
95
|
+
selectedCard,
|
|
96
|
+
additionalParams
|
|
97
|
+
} = state.masterpass ?? {};
|
|
92
98
|
|
|
93
99
|
const commonFormValues = {
|
|
94
100
|
token,
|
|
@@ -96,12 +102,16 @@ const completeMasterpassPayment = async (
|
|
|
96
102
|
referenceNo,
|
|
97
103
|
merchantId: credentials?.merchant_id ?? null,
|
|
98
104
|
msisdn,
|
|
99
|
-
amount: preOrder?.unpaid_amount?.replace('.', '') ?? ''
|
|
105
|
+
amount: preOrder?.unpaid_amount?.replace('.', '') ?? '',
|
|
106
|
+
additionalParams: additionalParams ?? extras?.additionalParams
|
|
100
107
|
};
|
|
101
108
|
|
|
102
109
|
const form = isDirectPurchase
|
|
103
110
|
? await buildDirectPurchaseForm(commonFormValues, params)
|
|
104
|
-
: await buildPurchaseForm({
|
|
111
|
+
: await buildPurchaseForm({
|
|
112
|
+
...commonFormValues,
|
|
113
|
+
selectedCard
|
|
114
|
+
});
|
|
105
115
|
|
|
106
116
|
window.MFS?.[
|
|
107
117
|
state.masterpass.isDirectPurchase ? 'directPurchase' : 'purchase'
|
package/data/client/user.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { api } from './api';
|
|
2
2
|
import { user } from '../urls';
|
|
3
|
-
import { ForgotPasswordFormType } from '../../types';
|
|
3
|
+
import { ForgotPasswordFormType, Order } from '../../types';
|
|
4
4
|
import { buildClientRequestUrl } from '../../utils';
|
|
5
5
|
|
|
6
6
|
interface GetCaptchaResponse {
|
|
@@ -84,6 +84,21 @@ const userApi = api.injectEndpoints({
|
|
|
84
84
|
contentType: 'application/json'
|
|
85
85
|
})
|
|
86
86
|
})
|
|
87
|
+
}),
|
|
88
|
+
getAnonymousTracking: build.mutation<
|
|
89
|
+
Order,
|
|
90
|
+
{ email: string; number: string }
|
|
91
|
+
>({
|
|
92
|
+
query: ({ email, number }) => ({
|
|
93
|
+
url: buildClientRequestUrl(user.anonymousOrderTracking, {
|
|
94
|
+
useFormData: true
|
|
95
|
+
}),
|
|
96
|
+
method: 'POST',
|
|
97
|
+
body: {
|
|
98
|
+
email,
|
|
99
|
+
number
|
|
100
|
+
}
|
|
101
|
+
})
|
|
87
102
|
})
|
|
88
103
|
}),
|
|
89
104
|
overrideExisting: false
|
|
@@ -95,5 +110,6 @@ export const {
|
|
|
95
110
|
useConfirmEmailVerificationQuery,
|
|
96
111
|
useValidateCaptchaMutation,
|
|
97
112
|
useLogoutMutation,
|
|
98
|
-
useForgotPasswordMutation
|
|
113
|
+
useForgotPasswordMutation,
|
|
114
|
+
useGetAnonymousTrackingMutation
|
|
99
115
|
} = userApi;
|
package/data/client/wishlist.ts
CHANGED
package/data/server/category.ts
CHANGED
|
@@ -15,7 +15,7 @@ function getCategoryDataHandler(
|
|
|
15
15
|
const params = generateCommerceSearchParams(searchParams);
|
|
16
16
|
|
|
17
17
|
const rawData = await appFetch<string>(
|
|
18
|
-
`${category.getCategoryByPk(pk)}${params ? params : ''}`,
|
|
18
|
+
`${category.getCategoryByPk(pk)}s${params ? params : ''}`,
|
|
19
19
|
{
|
|
20
20
|
headers: {
|
|
21
21
|
Accept: 'application/json',
|
|
@@ -39,7 +39,7 @@ function getCategoryDataHandler(
|
|
|
39
39
|
numberValueParser
|
|
40
40
|
) as GetCategoryResponse;
|
|
41
41
|
} catch (error) {
|
|
42
|
-
logger.
|
|
42
|
+
logger.fatal('Error while parsing category data', {
|
|
43
43
|
handler: 'getCategoryDataHandler',
|
|
44
44
|
error,
|
|
45
45
|
rawData: rawData.startsWith('<!DOCTYPE html>')
|
|
@@ -108,7 +108,7 @@ function getCategoryBySlugDataHandler(slug: string) {
|
|
|
108
108
|
numberValueParser
|
|
109
109
|
) as GetCategoryResponse;
|
|
110
110
|
} catch (error) {
|
|
111
|
-
logger.
|
|
111
|
+
logger.fatal('Error while parsing category data', {
|
|
112
112
|
handler: 'getCategoryBySlugDataHandler',
|
|
113
113
|
error,
|
|
114
114
|
rawData: rawData.startsWith('<!DOCTYPE html>')
|
package/data/server/list.ts
CHANGED
|
@@ -38,7 +38,7 @@ const getListDataHandler = (
|
|
|
38
38
|
numberValueParser
|
|
39
39
|
) as GetCategoryResponse;
|
|
40
40
|
} catch (error) {
|
|
41
|
-
logger.
|
|
41
|
+
logger.fatal('Error while parsing list data', {
|
|
42
42
|
error,
|
|
43
43
|
rawData: rawData.startsWith('<!DOCTYPE html>')
|
|
44
44
|
? `${rawData.substring(0, 50)}...`
|
package/data/urls.ts
CHANGED
|
@@ -172,7 +172,8 @@ export const user = {
|
|
|
172
172
|
`/users/email-set-primary/${token}/`,
|
|
173
173
|
confirmEmailVerification: (token: string) =>
|
|
174
174
|
`/users/registration/account-confirm-email/${token}/`,
|
|
175
|
-
csrfToken: '/csrf_token/'
|
|
175
|
+
csrfToken: '/csrf_token/',
|
|
176
|
+
anonymousOrderTracking: '/users/orders/anonymous'
|
|
176
177
|
};
|
|
177
178
|
|
|
178
179
|
export const b2b = {
|
package/hooks/use-pagination.ts
CHANGED
|
@@ -70,10 +70,6 @@ export default function usePagination(
|
|
|
70
70
|
dispatch({ type: 'setLimit', payload: limit });
|
|
71
71
|
}, [limit]);
|
|
72
72
|
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
window.scrollTo(0, 0);
|
|
75
|
-
}, [state.page, state.limit]);
|
|
76
|
-
|
|
77
73
|
const setTotal = useCallback(
|
|
78
74
|
(total: number) => {
|
|
79
75
|
dispatch({ type: 'setTotal', payload: total });
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
|
|
2
|
+
import settings from 'settings';
|
|
3
|
+
import { PzNextRequest } from '.';
|
|
4
|
+
import logger from '../utils/log';
|
|
5
|
+
import { urlLocaleMatcherRegex } from '../utils';
|
|
6
|
+
import { getUrlPathWithLocale } from '../utils/localization';
|
|
7
|
+
|
|
8
|
+
const withCheckoutProvider =
|
|
9
|
+
(middleware: NextMiddleware) =>
|
|
10
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
11
|
+
const url = req.nextUrl.clone();
|
|
12
|
+
const ip = req.headers.get('x-forwarded-for') ?? '';
|
|
13
|
+
const pathnameWithoutLocale = url.pathname.replace(
|
|
14
|
+
urlLocaleMatcherRegex,
|
|
15
|
+
''
|
|
16
|
+
);
|
|
17
|
+
const searchParams = new URLSearchParams(url.search);
|
|
18
|
+
|
|
19
|
+
if (pathnameWithoutLocale.startsWith('/orders/checkout-provider/')) {
|
|
20
|
+
try {
|
|
21
|
+
const request = await fetch(
|
|
22
|
+
`${encodeURI(settings.commerceUrl)}${url.pathname.replace(
|
|
23
|
+
urlLocaleMatcherRegex,
|
|
24
|
+
''
|
|
25
|
+
)}?${searchParams.toString()}`,
|
|
26
|
+
{
|
|
27
|
+
next: {
|
|
28
|
+
revalidate: 0
|
|
29
|
+
},
|
|
30
|
+
headers: {
|
|
31
|
+
Cookie: req.headers.get('cookie') || '',
|
|
32
|
+
Accept: 'application/json',
|
|
33
|
+
'X-Requested-With': 'XMLHttpRequest'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return NextResponse.json(await request.json());
|
|
39
|
+
} catch (error) {
|
|
40
|
+
logger.error('withCheckoutProvider error', {
|
|
41
|
+
error,
|
|
42
|
+
ip
|
|
43
|
+
});
|
|
44
|
+
return NextResponse.next();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (pathnameWithoutLocale.startsWith('/orders/checkout-provider-cancel')) {
|
|
49
|
+
try {
|
|
50
|
+
const request = await fetch(
|
|
51
|
+
`${settings.commerceUrl}${pathnameWithoutLocale}${url.search}`,
|
|
52
|
+
{
|
|
53
|
+
redirect: 'manual',
|
|
54
|
+
next: {
|
|
55
|
+
revalidate: 0
|
|
56
|
+
},
|
|
57
|
+
headers: {
|
|
58
|
+
Cookie: req.headers.get('cookie') || ''
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (request.headers.get('location')) {
|
|
64
|
+
const location = request.headers.get('location');
|
|
65
|
+
const redirectUrl = new URL(
|
|
66
|
+
request.headers.get('location'),
|
|
67
|
+
location.startsWith('http') ? '' : process.env.NEXT_PUBLIC_URL
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
redirectUrl.pathname = getUrlPathWithLocale(
|
|
71
|
+
redirectUrl.pathname,
|
|
72
|
+
req.middlewareParams.rewrites.locale
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return Response.redirect(redirectUrl.toString(), request.status);
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
logger.error('withCheckoutProvider error', {
|
|
79
|
+
error,
|
|
80
|
+
ip
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return middleware(req, event);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default withCheckoutProvider;
|
package/middlewares/default.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
|
|
2
2
|
import Settings from 'settings';
|
|
3
|
+
import { ROUTES } from 'routes';
|
|
3
4
|
import {
|
|
4
5
|
PzNextRequest,
|
|
6
|
+
withCheckoutProvider,
|
|
5
7
|
withCompleteGpay,
|
|
6
8
|
withCompleteMasterpass,
|
|
7
9
|
withOauthLogin,
|
|
@@ -112,6 +114,36 @@ const withPzDefault =
|
|
|
112
114
|
return NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
if (req.nextUrl.pathname.startsWith('/orders/checkout-provider/')) {
|
|
118
|
+
try {
|
|
119
|
+
const data = await req.json();
|
|
120
|
+
|
|
121
|
+
const request = await fetch(
|
|
122
|
+
`${encodeURI(Settings.commerceUrl)}${url.pathname.replace(
|
|
123
|
+
urlLocaleMatcherRegex,
|
|
124
|
+
''
|
|
125
|
+
)}?${searchParams.toString()}`,
|
|
126
|
+
{
|
|
127
|
+
method: 'POST',
|
|
128
|
+
body: JSON.stringify(data),
|
|
129
|
+
next: {
|
|
130
|
+
revalidate: 0
|
|
131
|
+
},
|
|
132
|
+
headers: {
|
|
133
|
+
Cookie: req.headers.get('cookie') || '',
|
|
134
|
+
Accept: 'application/json',
|
|
135
|
+
'Content-Type': 'application/json',
|
|
136
|
+
'X-Requested-With': 'XMLHttpRequest'
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
return NextResponse.json(await request.json());
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return NextResponse.redirect(ROUTES.BASKET);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
115
147
|
req.middlewareParams = {
|
|
116
148
|
commerceUrl,
|
|
117
149
|
rewrites: {}
|
|
@@ -123,64 +155,82 @@ const withPzDefault =
|
|
|
123
155
|
withPrettyUrl(
|
|
124
156
|
withRedirectionPayment(
|
|
125
157
|
withThreeDRedirection(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
NextResponse
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
url.basePath = `/${commerceUrl}`;
|
|
144
|
-
url.pathname = `/${
|
|
145
|
-
locale.length ? `${locale}/` : ''
|
|
146
|
-
}${currency}${prettyUrl ?? pathnameWithoutLocale}`;
|
|
147
|
-
|
|
148
|
-
Settings.rewrites.forEach((rewrite) => {
|
|
149
|
-
url.pathname = url.pathname.replace(
|
|
150
|
-
rewrite.source,
|
|
151
|
-
rewrite.destination
|
|
158
|
+
withCheckoutProvider(
|
|
159
|
+
withUrlRedirection(
|
|
160
|
+
withCompleteGpay(
|
|
161
|
+
withCompleteMasterpass(
|
|
162
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
163
|
+
let middlewareResult: NextResponse | void =
|
|
164
|
+
NextResponse.next();
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const { locale, prettyUrl, currency } =
|
|
168
|
+
req.middlewareParams.rewrites;
|
|
169
|
+
const { defaultLocaleValue } =
|
|
170
|
+
Settings.localization;
|
|
171
|
+
const url = req.nextUrl.clone();
|
|
172
|
+
const pathnameWithoutLocale = url.pathname.replace(
|
|
173
|
+
urlLocaleMatcherRegex,
|
|
174
|
+
''
|
|
152
175
|
);
|
|
153
|
-
});
|
|
154
176
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
177
|
+
url.basePath = `/${commerceUrl}`;
|
|
178
|
+
url.pathname = `/${
|
|
179
|
+
locale.length ? `${locale}/` : ''
|
|
180
|
+
}${currency}${prettyUrl ?? pathnameWithoutLocale}`;
|
|
181
|
+
|
|
182
|
+
Settings.rewrites.forEach((rewrite) => {
|
|
183
|
+
url.pathname = url.pathname.replace(
|
|
184
|
+
rewrite.source,
|
|
185
|
+
rewrite.destination
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
middlewareResult = (await middleware(
|
|
190
|
+
req,
|
|
191
|
+
event
|
|
192
|
+
)) as NextResponse | void;
|
|
193
|
+
|
|
194
|
+
// if middleware.ts has a return value for current url
|
|
195
|
+
if (middlewareResult instanceof NextResponse) {
|
|
196
|
+
// pz-override-response header is used to prevent 404 page for custom responses.
|
|
197
|
+
if (
|
|
198
|
+
middlewareResult.headers.get(
|
|
199
|
+
'pz-override-response'
|
|
200
|
+
) !== 'true'
|
|
201
|
+
) {
|
|
202
|
+
middlewareResult.headers.set(
|
|
203
|
+
'x-middleware-rewrite',
|
|
204
|
+
url.href
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
// if middleware.ts doesn't have a return value.
|
|
209
|
+
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
210
|
+
|
|
211
|
+
middlewareResult = NextResponse.rewrite(url);
|
|
212
|
+
}
|
|
159
213
|
|
|
160
|
-
// if middleware.ts has a return value for current url
|
|
161
|
-
if (middlewareResult instanceof NextResponse) {
|
|
162
|
-
// pz-override-response header is used to prevent 404 page for custom responses.
|
|
163
214
|
if (
|
|
164
|
-
|
|
165
|
-
'pz-override-response'
|
|
166
|
-
) !== 'true'
|
|
215
|
+
!url.pathname.startsWith(`/${currency}/orders`)
|
|
167
216
|
) {
|
|
168
|
-
middlewareResult.
|
|
169
|
-
'
|
|
170
|
-
|
|
217
|
+
middlewareResult.cookies.set(
|
|
218
|
+
'pz-locale',
|
|
219
|
+
locale?.length > 0
|
|
220
|
+
? locale
|
|
221
|
+
: defaultLocaleValue,
|
|
222
|
+
{
|
|
223
|
+
sameSite: 'none',
|
|
224
|
+
secure: true,
|
|
225
|
+
expires: new Date(
|
|
226
|
+
Date.now() + 1000 * 60 * 60 * 24 * 7
|
|
227
|
+
) // 7 days
|
|
228
|
+
}
|
|
171
229
|
);
|
|
172
230
|
}
|
|
173
|
-
} else {
|
|
174
|
-
// if middleware.ts doesn't have a return value.
|
|
175
|
-
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
176
|
-
|
|
177
|
-
middlewareResult = NextResponse.rewrite(url);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (!url.pathname.startsWith(`/${currency}/orders`)) {
|
|
181
231
|
middlewareResult.cookies.set(
|
|
182
|
-
'pz-
|
|
183
|
-
|
|
232
|
+
'pz-currency',
|
|
233
|
+
currency,
|
|
184
234
|
{
|
|
185
235
|
sameSite: 'none',
|
|
186
236
|
secure: true,
|
|
@@ -189,74 +239,65 @@ const withPzDefault =
|
|
|
189
239
|
) // 7 days
|
|
190
240
|
}
|
|
191
241
|
);
|
|
192
|
-
}
|
|
193
|
-
middlewareResult.cookies.set(
|
|
194
|
-
'pz-currency',
|
|
195
|
-
currency,
|
|
196
|
-
{
|
|
197
|
-
sameSite: 'none',
|
|
198
|
-
secure: true,
|
|
199
|
-
expires: new Date(
|
|
200
|
-
Date.now() + 1000 * 60 * 60 * 24 * 7
|
|
201
|
-
) // 7 days
|
|
202
|
-
}
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
req.cookies.get('pz-locale') &&
|
|
207
|
-
req.cookies.get('pz-locale').value !== locale
|
|
208
|
-
) {
|
|
209
|
-
logger.debug('Locale changed', {
|
|
210
|
-
locale,
|
|
211
|
-
oldLocale: req.cookies.get('pz-locale')?.value,
|
|
212
|
-
ip
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
middlewareResult.headers.set(
|
|
217
|
-
'pz-url',
|
|
218
|
-
req.nextUrl.toString()
|
|
219
|
-
);
|
|
220
242
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
243
|
+
if (
|
|
244
|
+
req.cookies.get('pz-locale') &&
|
|
245
|
+
req.cookies.get('pz-locale').value !== locale
|
|
246
|
+
) {
|
|
247
|
+
logger.debug('Locale changed', {
|
|
248
|
+
locale,
|
|
249
|
+
oldLocale: req.cookies.get('pz-locale')?.value,
|
|
250
|
+
ip
|
|
251
|
+
});
|
|
252
|
+
}
|
|
224
253
|
|
|
225
|
-
if (process.env.ACC_APP_VERSION) {
|
|
226
254
|
middlewareResult.headers.set(
|
|
227
|
-
'
|
|
228
|
-
|
|
255
|
+
'pz-url',
|
|
256
|
+
req.nextUrl.toString()
|
|
229
257
|
);
|
|
230
|
-
}
|
|
231
258
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
259
|
+
if (req.cookies.get('pz-set-currency')) {
|
|
260
|
+
middlewareResult.cookies.delete(
|
|
261
|
+
'pz-set-currency'
|
|
262
|
+
);
|
|
263
|
+
}
|
|
235
264
|
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
middlewareResult.cookies.set(
|
|
241
|
-
'csrftoken',
|
|
242
|
-
csrf_token
|
|
265
|
+
if (process.env.ACC_APP_VERSION) {
|
|
266
|
+
middlewareResult.headers.set(
|
|
267
|
+
'acc-app-version',
|
|
268
|
+
process.env.ACC_APP_VERSION
|
|
243
269
|
);
|
|
244
270
|
}
|
|
271
|
+
|
|
272
|
+
// Set CSRF token if not set
|
|
273
|
+
try {
|
|
274
|
+
const url = `${Settings.commerceUrl}${user.csrfToken}`;
|
|
275
|
+
|
|
276
|
+
if (!req.cookies.get('csrftoken')) {
|
|
277
|
+
const { csrf_token } = await (
|
|
278
|
+
await fetch(url)
|
|
279
|
+
).json();
|
|
280
|
+
middlewareResult.cookies.set(
|
|
281
|
+
'csrftoken',
|
|
282
|
+
csrf_token
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger.error('CSRF Error', {
|
|
287
|
+
error,
|
|
288
|
+
ip
|
|
289
|
+
});
|
|
290
|
+
}
|
|
245
291
|
} catch (error) {
|
|
246
|
-
logger.error('
|
|
292
|
+
logger.error('withPzDefault Error', {
|
|
247
293
|
error,
|
|
248
294
|
ip
|
|
249
295
|
});
|
|
250
296
|
}
|
|
251
|
-
} catch (error) {
|
|
252
|
-
logger.error('withPzDefault Error', {
|
|
253
|
-
error,
|
|
254
|
-
ip
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
297
|
|
|
258
|
-
|
|
259
|
-
|
|
298
|
+
return middlewareResult;
|
|
299
|
+
}
|
|
300
|
+
)
|
|
260
301
|
)
|
|
261
302
|
)
|
|
262
303
|
)
|
package/middlewares/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import withOauthLogin from './oauth-login';
|
|
|
7
7
|
import withUrlRedirection from './url-redirection';
|
|
8
8
|
import withCompleteGpay from './complete-gpay';
|
|
9
9
|
import withCompleteMasterpass from './complete-masterpass';
|
|
10
|
+
import withCheckoutProvider from './checkout-provider';
|
|
10
11
|
import { NextRequest } from 'next/server';
|
|
11
12
|
|
|
12
13
|
export {
|
|
@@ -18,7 +19,8 @@ export {
|
|
|
18
19
|
withOauthLogin,
|
|
19
20
|
withUrlRedirection,
|
|
20
21
|
withCompleteGpay,
|
|
21
|
-
withCompleteMasterpass
|
|
22
|
+
withCompleteMasterpass,
|
|
23
|
+
withCheckoutProvider
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export interface PzNextRequest extends NextRequest {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.21.0-rc.
|
|
4
|
+
"version": "1.21.0-rc.10",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
33
33
|
"@typescript-eslint/parser": "6.7.4",
|
|
34
34
|
"eslint": "^8.14.0",
|
|
35
|
-
"@akinon/eslint-plugin-projectzero": "1.21.0-rc.
|
|
35
|
+
"@akinon/eslint-plugin-projectzero": "1.21.0-rc.10",
|
|
36
36
|
"eslint-config-prettier": "8.5.0"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/plugins.d.ts
CHANGED
package/redux/reducers/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { api } from '../../data/client/api';
|
|
|
6
6
|
|
|
7
7
|
// Plugin reducers
|
|
8
8
|
import { masterpassReducer } from '@akinon/pz-masterpass';
|
|
9
|
+
import { otpReducer } from '@akinon/pz-otp';
|
|
9
10
|
|
|
10
11
|
const reducers = {
|
|
11
12
|
[api.reducerPath]: api.reducer,
|
|
@@ -13,7 +14,8 @@ const reducers = {
|
|
|
13
14
|
checkout: checkoutReducer,
|
|
14
15
|
config: configReducer,
|
|
15
16
|
header: headerReducer,
|
|
16
|
-
masterpass: masterpassReducer
|
|
17
|
+
masterpass: masterpassReducer,
|
|
18
|
+
otp: otpReducer
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
export default reducers;
|
package/sentry/index.ts
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
import * as Sentry from '@sentry/nextjs';
|
|
2
|
+
import settings from 'settings';
|
|
2
3
|
|
|
3
4
|
const SENTRY_DSN: string =
|
|
4
|
-
|
|
5
|
+
settings.sentryDsn ||
|
|
6
|
+
process.env.SENTRY_DSN ||
|
|
7
|
+
process.env.NEXT_PUBLIC_SENTRY_DSN;
|
|
5
8
|
|
|
6
9
|
export const initSentry = (
|
|
7
10
|
type: 'Server' | 'Client' | 'Edge',
|
|
8
|
-
options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {
|
|
11
|
+
options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {
|
|
12
|
+
dsn: SENTRY_DSN,
|
|
13
|
+
integrations: [],
|
|
14
|
+
tracesSampleRate: 1.0
|
|
15
|
+
}
|
|
9
16
|
) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// TODO: Remove Zero Project DSN
|
|
13
|
-
|
|
14
|
-
Sentry.init({
|
|
15
|
-
dsn:
|
|
16
|
-
SENTRY_DSN ||
|
|
17
|
-
'https://d8558ef8997543deacf376c7d8d7cf4b@o64293.ingest.sentry.io/4504338423742464',
|
|
17
|
+
const initOptions = {
|
|
18
|
+
...options,
|
|
18
19
|
initialScope: {
|
|
19
20
|
tags: {
|
|
21
|
+
...((
|
|
22
|
+
options.initialScope as {
|
|
23
|
+
tags?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
)?.tags ?? {}),
|
|
20
26
|
APP_TYPE: 'ProjectZeroNext',
|
|
21
27
|
TYPE: type
|
|
22
28
|
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
Sentry.init(initOptions);
|
|
27
33
|
};
|
package/types/gtm.ts
CHANGED
|
@@ -14,3 +14,51 @@ export interface GTMUserType {
|
|
|
14
14
|
gender: string;
|
|
15
15
|
emailAllowed: boolean;
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
export interface GA4EventItem {
|
|
19
|
+
item_id: string | number;
|
|
20
|
+
item_name: string | number;
|
|
21
|
+
coupon?: string | number;
|
|
22
|
+
discount?: string | null;
|
|
23
|
+
index?: number;
|
|
24
|
+
item_brand?: string;
|
|
25
|
+
item_category?: string;
|
|
26
|
+
item_category2?: string;
|
|
27
|
+
item_category3?: string;
|
|
28
|
+
item_category4?: string;
|
|
29
|
+
item_category5?: string;
|
|
30
|
+
item_list_id?: string;
|
|
31
|
+
item_list_name?: string;
|
|
32
|
+
item_variant?: string;
|
|
33
|
+
price?: string | number;
|
|
34
|
+
quantity?: number;
|
|
35
|
+
sale_status?: string;
|
|
36
|
+
stock_status?: string;
|
|
37
|
+
discount_percent?: number;
|
|
38
|
+
discount_price?: string;
|
|
39
|
+
product_gender?: string;
|
|
40
|
+
product_size?: string;
|
|
41
|
+
[key: string]: any;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface GA4EventParams {
|
|
45
|
+
item_list_id?: string;
|
|
46
|
+
item_list_name?: string;
|
|
47
|
+
value?: number;
|
|
48
|
+
currency?: string;
|
|
49
|
+
shipping_tier?: string;
|
|
50
|
+
coupon?: string;
|
|
51
|
+
selected_shipping_option?: string;
|
|
52
|
+
creative_name?: string;
|
|
53
|
+
creative_slot?: string;
|
|
54
|
+
promotion_id?: string;
|
|
55
|
+
promotion_name?: string;
|
|
56
|
+
items?: GA4EventItem[];
|
|
57
|
+
transaction_id?: string;
|
|
58
|
+
tax?: number;
|
|
59
|
+
shipping?: number;
|
|
60
|
+
shipping_country?: string;
|
|
61
|
+
shipping_city?: string;
|
|
62
|
+
payment_method?: string;
|
|
63
|
+
[key: string]: any;
|
|
64
|
+
}
|
package/types/index.ts
CHANGED
|
@@ -4,8 +4,11 @@ import { Control, FieldError } from 'react-hook-form';
|
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
|
|
6
6
|
declare global {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
interface Window {
|
|
8
|
+
// we did it like this because declare types needs to be identical, if not it will fail
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
10
|
+
dataLayer?: Object[];
|
|
11
|
+
[key: string]: any;
|
|
9
12
|
}
|
|
10
13
|
}
|
|
11
14
|
|
|
@@ -68,6 +71,12 @@ export interface Currency {
|
|
|
68
71
|
|
|
69
72
|
export interface Settings {
|
|
70
73
|
commerceUrl: string;
|
|
74
|
+
/**
|
|
75
|
+
* This option allows you to track Sentry events on the client side, in addition to server and edge environments.
|
|
76
|
+
*
|
|
77
|
+
* It overrides process.env.NEXT_PUBLIC_SENTRY_DSN and process.env.SENTRY_DSN.
|
|
78
|
+
*/
|
|
79
|
+
sentryDsn?: string;
|
|
71
80
|
redis: {
|
|
72
81
|
defaultExpirationTime: number;
|
|
73
82
|
};
|
|
@@ -198,7 +207,7 @@ export type CDNOptions = {
|
|
|
198
207
|
width?: number;
|
|
199
208
|
height?: number;
|
|
200
209
|
quality?: number;
|
|
201
|
-
crop?: 'center' | 'top' | 'bottom' | 'left' | 'right';
|
|
210
|
+
crop?: 'center' | 'top' | 'bottom' | 'left' | 'right' | 'none';
|
|
202
211
|
upscale?: boolean;
|
|
203
212
|
};
|
|
204
213
|
|
package/utils/app-fetch.ts
CHANGED
|
@@ -12,7 +12,7 @@ const appFetch = async <T>(
|
|
|
12
12
|
url: RequestInfo,
|
|
13
13
|
init: RequestInit = {},
|
|
14
14
|
responseType = FetchResponseType.JSON
|
|
15
|
-
) => {
|
|
15
|
+
): Promise<T> => {
|
|
16
16
|
let response: T;
|
|
17
17
|
let status: number;
|
|
18
18
|
let ip = '';
|
|
@@ -28,7 +28,7 @@ const appFetch = async <T>(
|
|
|
28
28
|
|
|
29
29
|
if (commerceUrl === 'default') {
|
|
30
30
|
logger.error('Commerce URL is not set. Current value is "default"');
|
|
31
|
-
|
|
31
|
+
throw new Error('Commerce URL is not set');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const requestURL = `${decodeURIComponent(commerceUrl)}${url}`;
|
|
@@ -48,19 +48,30 @@ const appFetch = async <T>(
|
|
|
48
48
|
status = req.status;
|
|
49
49
|
logger.debug(`FETCH END ${url}`, { status: req.status, ip });
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
if (!req.ok) {
|
|
52
|
+
throw new Error(`Request failed with status ${status}`);
|
|
53
|
+
}
|
|
52
54
|
|
|
53
55
|
if (responseType === FetchResponseType.JSON) {
|
|
54
|
-
response =
|
|
56
|
+
response = (await req.json()) as T;
|
|
55
57
|
} else {
|
|
56
|
-
response =
|
|
58
|
+
response = (await req.text()) as unknown as T;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
logger.trace(`FETCH RESPONSE`, { url, response, ip });
|
|
60
62
|
} catch (error) {
|
|
63
|
+
const logType = status === 500 ? 'fatal' : 'error';
|
|
64
|
+
|
|
61
65
|
if (!url.toString().includes('/cms/seo/')) {
|
|
62
|
-
logger
|
|
66
|
+
logger[logType](`FETCH FAILED`, { url, status, error, ip });
|
|
63
67
|
}
|
|
68
|
+
|
|
69
|
+
// throw the error if it's fatal, so it can be caught and handled at higher levels
|
|
70
|
+
if (logType === 'fatal') {
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return Promise.reject(error);
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
return response;
|
package/utils/image-loader.ts
CHANGED
|
@@ -19,12 +19,19 @@ export const ImageLoader = ({
|
|
|
19
19
|
aspectRatio
|
|
20
20
|
}: ImageLoaderParams) => {
|
|
21
21
|
if (src.match(/akinoncdn|akinoncloud|b-cdn\.net/)) {
|
|
22
|
-
|
|
22
|
+
const height =
|
|
23
|
+
fill && aspectRatio
|
|
24
|
+
? parseInt(String(Number(width) / aspectRatio))
|
|
25
|
+
: undefined;
|
|
26
|
+
|
|
27
|
+
const config: CDNOptions = {
|
|
23
28
|
width,
|
|
24
|
-
height
|
|
29
|
+
height,
|
|
25
30
|
quality,
|
|
26
|
-
crop
|
|
27
|
-
}
|
|
31
|
+
crop: crop !== 'none' ? crop : undefined
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return buildCDNUrl(src, config);
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
return src;
|