@akinon/next 1.21.0-rc.9 → 1.21.0
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 +9 -62
- package/api/auth.ts +0 -11
- package/bin/pz-install-plugins.js +27 -59
- package/data/client/wishlist.ts +0 -2
- package/data/server/category.ts +3 -3
- package/data/server/list.ts +1 -1
- package/hooks/use-pagination.ts +4 -0
- package/lib/cache.ts +27 -55
- package/middlewares/default.ts +104 -145
- package/middlewares/index.ts +1 -3
- package/package.json +4 -5
- package/sentry/index.ts +14 -20
- package/types/index.ts +0 -6
- package/utils/app-fetch.ts +6 -17
- package/middlewares/checkout-provider.ts +0 -88
package/CHANGELOG.md
CHANGED
|
@@ -1,71 +1,18 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
-
## 1.21.0
|
|
4
|
-
|
|
5
|
-
### Minor Changes
|
|
6
|
-
|
|
7
|
-
- 8d6caba: ZERO-2434: enhance error handling and logging in appFetch function
|
|
8
|
-
|
|
9
|
-
## 1.21.0-rc.8
|
|
10
|
-
|
|
11
|
-
### Minor Changes
|
|
12
|
-
|
|
13
|
-
- 07cc81a: Add infinite and more types to pagination
|
|
14
|
-
|
|
15
|
-
## 1.21.0-rc.7
|
|
16
|
-
|
|
17
|
-
### Minor Changes
|
|
18
|
-
|
|
19
|
-
- 5ace3508: ZERO-2439: Add anonymous tracking page
|
|
20
|
-
- be04d041: ZERO-2452: UA events converted to be compatible with GA4 and types are declared
|
|
21
|
-
- c75e1e45: ZERO-2452: GA4 support for gtm events
|
|
22
|
-
|
|
23
|
-
## 1.21.0-rc.6
|
|
24
|
-
|
|
25
|
-
### Minor Changes
|
|
26
|
-
|
|
27
|
-
- b4452e9d: ZERO-2463: Refactor Sentry initialization and add Sentry DSN option to settings
|
|
28
|
-
|
|
29
|
-
## 1.21.0-rc.5
|
|
30
|
-
|
|
31
|
-
### Patch Changes
|
|
32
|
-
|
|
33
|
-
- ZERO-2296: Fix ROUTES import
|
|
34
|
-
|
|
35
|
-
## 1.21.0-rc.4
|
|
36
|
-
|
|
37
|
-
### Minor Changes
|
|
38
|
-
|
|
39
|
-
- Revert ZERO-2435
|
|
40
|
-
|
|
41
|
-
## 1.21.0-rc.3
|
|
42
|
-
|
|
43
|
-
### Minor Changes
|
|
44
|
-
|
|
45
|
-
- af8e38e0: ZERO-2247: Add additionalParams to MasterpassProvider and set them in buildPurchaseForm and buildDirectPurchaseForm
|
|
46
|
-
- 16027410: ZERO-2451: Refactor extraHeaders assignment in client.ts
|
|
47
|
-
- 9edd725b: Datalayer init changed to 3rd party script. Declare is the same as next
|
|
48
|
-
|
|
49
|
-
## 1.21.0-rc.2
|
|
50
|
-
|
|
51
|
-
### Minor Changes
|
|
52
|
-
|
|
53
|
-
- 8075006: install plugins check the akinon version
|
|
54
|
-
- 32668ed: ZERO-2296: Implement Checkout Provider Middleware and Enhancements
|
|
55
|
-
- 29191da: ImageLoader supports crop none
|
|
56
|
-
|
|
57
|
-
## 1.21.0-rc.1
|
|
58
|
-
|
|
59
|
-
### Minor Changes
|
|
60
|
-
|
|
61
|
-
- e910b228: ZERO-2421: Upgrade next version to 14.1.0
|
|
62
|
-
- 1b4b0fa1: Redis connection change to connection pool
|
|
63
|
-
|
|
64
|
-
## 1.21.0-rc.0
|
|
3
|
+
## 1.21.0
|
|
65
4
|
|
|
66
5
|
### Minor Changes
|
|
67
6
|
|
|
7
|
+
- 5ace350: ZERO-2439: Add anonymous tracking page
|
|
8
|
+
- be04d04: ZERO-2452: UA events converted to be compatible with GA4 and types are declared
|
|
9
|
+
- e910b22: ZERO-2421: Upgrade next version to 14.1.0
|
|
10
|
+
- af8e38e: ZERO-2247: Add additionalParams to MasterpassProvider and set them in buildPurchaseForm and buildDirectPurchaseForm
|
|
68
11
|
- 26a74c9: ZERO-2436: Add getCoupons and setCoupon API endpoints
|
|
12
|
+
- 1602741: ZERO-2451: Refactor extraHeaders assignment in client.ts
|
|
13
|
+
- 9edd725: Datalayer init changed to 3rd party script. Declare is the same as next
|
|
14
|
+
- c75e1e4: ZERO-2452: GA4 support for gtm events
|
|
15
|
+
- 29191da: ImageLoader supports crop none
|
|
69
16
|
|
|
70
17
|
## 1.20.0
|
|
71
18
|
|
package/api/auth.ts
CHANGED
|
@@ -220,17 +220,6 @@ 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
|
-
}
|
|
234
223
|
}
|
|
235
224
|
};
|
|
236
225
|
};
|
|
@@ -1,71 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
const fs = require('fs');
|
|
2
4
|
const path = require('path');
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const insideNodeModules = __dirname.includes('node_modules');
|
|
7
|
-
return insideNodeModules
|
|
8
|
-
? process.cwd()
|
|
9
|
-
: path.resolve(__dirname, '../../../apps/projectzeronext');
|
|
10
|
-
}
|
|
5
|
+
const rootDir = path.resolve(process.cwd());
|
|
6
|
+
const spawn = require('cross-spawn');
|
|
7
|
+
const availablePlugins = require('../plugins');
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
const getFullPath = (relativePath) => path.join(BASE_DIR, relativePath);
|
|
14
|
-
|
|
15
|
-
const packageJsonPath = getFullPath('package.json');
|
|
16
|
-
let packageJson;
|
|
9
|
+
let plugins;
|
|
17
10
|
|
|
18
11
|
try {
|
|
19
|
-
|
|
12
|
+
plugins = require(path.resolve(rootDir, './src/plugins.js'));
|
|
20
13
|
} catch (error) {
|
|
21
|
-
console.error('
|
|
22
|
-
process.exit(
|
|
14
|
+
console.error('No plugins.js file found, skipping plugin installation.');
|
|
15
|
+
process.exit(0);
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
|
|
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
|
-
}
|
|
18
|
+
let installCmd = [];
|
|
34
19
|
|
|
35
|
-
|
|
20
|
+
availablePlugins
|
|
21
|
+
.filter((p) => plugins?.includes(p))
|
|
22
|
+
.forEach((name) => {
|
|
23
|
+
installCmd.push(`@akinon/${name}`);
|
|
24
|
+
});
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
(dep) =>
|
|
39
|
-
dep.startsWith('@akinon/') &&
|
|
40
|
-
!installedPlugins.includes(dep.replace('@akinon/', '')) &&
|
|
41
|
-
!protectedPackages.includes(dep)
|
|
42
|
-
);
|
|
26
|
+
spawn.sync('yarn', ['cache clean']);
|
|
43
27
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.warn(
|
|
49
|
-
`Warning: Could not remove ${plugin}. It may not have been installed.`
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
28
|
+
for (const plugin of availablePlugins) {
|
|
29
|
+
spawn.sync('yarn', ['remove', `@akinon/${plugin}`]);
|
|
30
|
+
}
|
|
53
31
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
});
|
|
32
|
+
if (
|
|
33
|
+
installCmd.length > 0 &&
|
|
34
|
+
!fs.existsSync(path.resolve(rootDir, '../../turbo.json'))
|
|
35
|
+
) {
|
|
36
|
+
spawn.sync('yarn', ['add', ...installCmd, '--ignore-scripts'], {
|
|
37
|
+
stdio: 'inherit'
|
|
38
|
+
});
|
|
39
|
+
}
|
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)}
|
|
18
|
+
`${category.getCategoryByPk(pk)}${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.error('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.error('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.error('Error while parsing list data', {
|
|
42
42
|
error,
|
|
43
43
|
rawData: rawData.startsWith('<!DOCTYPE html>')
|
|
44
44
|
? `${rawData.substring(0, 50)}...`
|
package/hooks/use-pagination.ts
CHANGED
|
@@ -70,6 +70,10 @@ 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
|
+
|
|
73
77
|
const setTotal = useCallback(
|
|
74
78
|
(total: number) => {
|
|
75
79
|
dispatch({ type: 'setTotal', payload: total });
|
package/lib/cache.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createPool, Pool } from 'generic-pool';
|
|
2
1
|
import { RedisClientType } from 'redis';
|
|
3
2
|
import Settings from 'settings';
|
|
4
3
|
import { CacheOptions } from '../types';
|
|
@@ -65,38 +64,21 @@ export class Cache {
|
|
|
65
64
|
);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
static
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
process.env.CACHE_PORT
|
|
74
|
-
}/${process.env.CACHE_BUCKET ?? '0'}`;
|
|
75
|
-
|
|
76
|
-
const client: RedisClientType = createClient({
|
|
77
|
-
url: redisUrl
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
client.on('error', (error) => {
|
|
81
|
-
logger.error('Redis client error', { redisUrl, error });
|
|
82
|
-
});
|
|
67
|
+
static async getClient() {
|
|
68
|
+
const { createClient } = await import('redis');
|
|
69
|
+
const redisUrl = `redis://${process.env.CACHE_HOST}:${
|
|
70
|
+
process.env.CACHE_PORT
|
|
71
|
+
}/${process.env.CACHE_BUCKET ?? '0'}`;
|
|
83
72
|
|
|
84
|
-
|
|
73
|
+
const client: RedisClientType = createClient({
|
|
74
|
+
url: redisUrl
|
|
75
|
+
});
|
|
85
76
|
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
await client.disconnect();
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
max: 500,
|
|
94
|
-
min: 2
|
|
95
|
-
}
|
|
96
|
-
);
|
|
77
|
+
client.on('error', (error) => {
|
|
78
|
+
logger.error('Redis client error', { redisUrl, error });
|
|
79
|
+
});
|
|
97
80
|
|
|
98
|
-
|
|
99
|
-
return await Cache.clientPool.acquire();
|
|
81
|
+
return client;
|
|
100
82
|
}
|
|
101
83
|
|
|
102
84
|
static async get(key: string) {
|
|
@@ -105,49 +87,39 @@ export class Cache {
|
|
|
105
87
|
|
|
106
88
|
try {
|
|
107
89
|
client = await Cache.getClient();
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
logger.debug('Redis get success', { key, value });
|
|
90
|
+
await client.connect();
|
|
91
|
+
value = JSON.parse(await client.get(key));
|
|
92
|
+
|
|
93
|
+
logger.debug('Redis get success', { key });
|
|
94
|
+
logger.trace('Redis get success', { key, value });
|
|
115
95
|
} catch (error) {
|
|
116
96
|
logger.error('Redis get error', { key, error });
|
|
117
|
-
value = null;
|
|
118
97
|
} finally {
|
|
119
|
-
|
|
120
|
-
await Cache.clientPool.release(client);
|
|
121
|
-
}
|
|
98
|
+
await client?.disconnect();
|
|
122
99
|
}
|
|
123
100
|
|
|
124
101
|
return value;
|
|
125
102
|
}
|
|
126
103
|
|
|
127
|
-
static async set(key: string, value:
|
|
104
|
+
static async set(key: string, value: string, expire?: number) {
|
|
128
105
|
let success = false;
|
|
129
106
|
let client;
|
|
130
107
|
|
|
131
108
|
try {
|
|
132
109
|
client = await Cache.getClient();
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
await client.set(key, serializedValue, { EX: expire });
|
|
138
|
-
} else {
|
|
139
|
-
await client.set(key, serializedValue);
|
|
140
|
-
}
|
|
110
|
+
await client.connect();
|
|
111
|
+
await client.set(key, value, {
|
|
112
|
+
EX: expire
|
|
113
|
+
});
|
|
141
114
|
|
|
142
115
|
success = true;
|
|
143
|
-
|
|
116
|
+
|
|
117
|
+
logger.debug('Redis set success', { key });
|
|
118
|
+
logger.trace('Redis set success', { key, value });
|
|
144
119
|
} catch (error) {
|
|
145
120
|
logger.error('Redis set error', { key, error });
|
|
146
|
-
success = false;
|
|
147
121
|
} finally {
|
|
148
|
-
|
|
149
|
-
await Cache.clientPool.release(client);
|
|
150
|
-
}
|
|
122
|
+
await client?.disconnect();
|
|
151
123
|
}
|
|
152
124
|
|
|
153
125
|
return success;
|
package/middlewares/default.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
|
|
2
2
|
import Settings from 'settings';
|
|
3
|
-
import { ROUTES } from 'routes';
|
|
4
3
|
import {
|
|
5
4
|
PzNextRequest,
|
|
6
|
-
withCheckoutProvider,
|
|
7
5
|
withCompleteGpay,
|
|
8
6
|
withCompleteMasterpass,
|
|
9
7
|
withOauthLogin,
|
|
@@ -114,36 +112,6 @@ const withPzDefault =
|
|
|
114
112
|
return NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
115
113
|
}
|
|
116
114
|
|
|
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
|
-
|
|
147
115
|
req.middlewareParams = {
|
|
148
116
|
commerceUrl,
|
|
149
117
|
rewrites: {}
|
|
@@ -155,82 +123,64 @@ const withPzDefault =
|
|
|
155
123
|
withPrettyUrl(
|
|
156
124
|
withRedirectionPayment(
|
|
157
125
|
withThreeDRedirection(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
126
|
+
withUrlRedirection(
|
|
127
|
+
withCompleteGpay(
|
|
128
|
+
withCompleteMasterpass(
|
|
129
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
130
|
+
let middlewareResult: NextResponse | void =
|
|
131
|
+
NextResponse.next();
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const { locale, prettyUrl, currency } =
|
|
135
|
+
req.middlewareParams.rewrites;
|
|
136
|
+
const { defaultLocaleValue } = Settings.localization;
|
|
137
|
+
const url = req.nextUrl.clone();
|
|
138
|
+
const pathnameWithoutLocale = url.pathname.replace(
|
|
139
|
+
urlLocaleMatcherRegex,
|
|
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
|
|
175
152
|
);
|
|
153
|
+
});
|
|
176
154
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
}
|
|
155
|
+
middlewareResult = (await middleware(
|
|
156
|
+
req,
|
|
157
|
+
event
|
|
158
|
+
)) as NextResponse | void;
|
|
213
159
|
|
|
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.
|
|
214
163
|
if (
|
|
215
|
-
|
|
164
|
+
middlewareResult.headers.get(
|
|
165
|
+
'pz-override-response'
|
|
166
|
+
) !== 'true'
|
|
216
167
|
) {
|
|
217
|
-
middlewareResult.
|
|
218
|
-
'
|
|
219
|
-
|
|
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
|
-
}
|
|
168
|
+
middlewareResult.headers.set(
|
|
169
|
+
'x-middleware-rewrite',
|
|
170
|
+
url.href
|
|
229
171
|
);
|
|
230
172
|
}
|
|
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`)) {
|
|
231
181
|
middlewareResult.cookies.set(
|
|
232
|
-
'pz-
|
|
233
|
-
|
|
182
|
+
'pz-locale',
|
|
183
|
+
locale?.length > 0 ? locale : defaultLocaleValue,
|
|
234
184
|
{
|
|
235
185
|
sameSite: 'none',
|
|
236
186
|
secure: true,
|
|
@@ -239,65 +189,74 @@ const withPzDefault =
|
|
|
239
189
|
) // 7 days
|
|
240
190
|
}
|
|
241
191
|
);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
|
252
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
|
+
}
|
|
253
215
|
|
|
216
|
+
middlewareResult.headers.set(
|
|
217
|
+
'pz-url',
|
|
218
|
+
req.nextUrl.toString()
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (req.cookies.get('pz-set-currency')) {
|
|
222
|
+
middlewareResult.cookies.delete('pz-set-currency');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (process.env.ACC_APP_VERSION) {
|
|
254
226
|
middlewareResult.headers.set(
|
|
255
|
-
'
|
|
256
|
-
|
|
227
|
+
'acc-app-version',
|
|
228
|
+
process.env.ACC_APP_VERSION
|
|
257
229
|
);
|
|
230
|
+
}
|
|
258
231
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
);
|
|
263
|
-
}
|
|
232
|
+
// Set CSRF token if not set
|
|
233
|
+
try {
|
|
234
|
+
const url = `${Settings.commerceUrl}${user.csrfToken}`;
|
|
264
235
|
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
236
|
+
if (!req.cookies.get('csrftoken')) {
|
|
237
|
+
const { csrf_token } = await (
|
|
238
|
+
await fetch(url)
|
|
239
|
+
).json();
|
|
240
|
+
middlewareResult.cookies.set(
|
|
241
|
+
'csrftoken',
|
|
242
|
+
csrf_token
|
|
269
243
|
);
|
|
270
244
|
}
|
|
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
|
-
}
|
|
291
245
|
} catch (error) {
|
|
292
|
-
logger.error('
|
|
246
|
+
logger.error('CSRF Error', {
|
|
293
247
|
error,
|
|
294
248
|
ip
|
|
295
249
|
});
|
|
296
250
|
}
|
|
297
|
-
|
|
298
|
-
|
|
251
|
+
} catch (error) {
|
|
252
|
+
logger.error('withPzDefault Error', {
|
|
253
|
+
error,
|
|
254
|
+
ip
|
|
255
|
+
});
|
|
299
256
|
}
|
|
300
|
-
|
|
257
|
+
|
|
258
|
+
return middlewareResult;
|
|
259
|
+
}
|
|
301
260
|
)
|
|
302
261
|
)
|
|
303
262
|
)
|
package/middlewares/index.ts
CHANGED
|
@@ -7,7 +7,6 @@ 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';
|
|
11
10
|
import { NextRequest } from 'next/server';
|
|
12
11
|
|
|
13
12
|
export {
|
|
@@ -19,8 +18,7 @@ export {
|
|
|
19
18
|
withOauthLogin,
|
|
20
19
|
withUrlRedirection,
|
|
21
20
|
withCompleteGpay,
|
|
22
|
-
withCompleteMasterpass
|
|
23
|
-
withCheckoutProvider
|
|
21
|
+
withCompleteMasterpass
|
|
24
22
|
};
|
|
25
23
|
|
|
26
24
|
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
|
|
4
|
+
"version": "1.21.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -14,14 +14,13 @@
|
|
|
14
14
|
"pz-postdev": "bin/pz-postdev.js"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"@opentelemetry/sdk-node": "0.46.0",
|
|
17
18
|
"@opentelemetry/exporter-trace-otlp-http": "0.46.0",
|
|
18
19
|
"@opentelemetry/resources": "1.19.0",
|
|
19
|
-
"@opentelemetry/sdk-node": "0.46.0",
|
|
20
|
-
"@opentelemetry/sdk-trace-node": "1.19.0",
|
|
21
20
|
"@opentelemetry/semantic-conventions": "1.19.0",
|
|
21
|
+
"@opentelemetry/sdk-trace-node": "1.19.0",
|
|
22
22
|
"@reduxjs/toolkit": "1.9.7",
|
|
23
23
|
"cross-spawn": "7.0.3",
|
|
24
|
-
"generic-pool": "3.9.0",
|
|
25
24
|
"react-redux": "8.1.3",
|
|
26
25
|
"react-string-replace": "1.1.1",
|
|
27
26
|
"redis": "4.5.1",
|
|
@@ -32,7 +31,7 @@
|
|
|
32
31
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
33
32
|
"@typescript-eslint/parser": "6.7.4",
|
|
34
33
|
"eslint": "^8.14.0",
|
|
35
|
-
"@akinon/eslint-plugin-projectzero": "1.21.0
|
|
34
|
+
"@akinon/eslint-plugin-projectzero": "1.21.0",
|
|
36
35
|
"eslint-config-prettier": "8.5.0"
|
|
37
36
|
}
|
|
38
37
|
}
|
package/sentry/index.ts
CHANGED
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
import * as Sentry from '@sentry/nextjs';
|
|
2
|
-
import settings from 'settings';
|
|
3
2
|
|
|
4
3
|
const SENTRY_DSN: string =
|
|
5
|
-
|
|
6
|
-
process.env.SENTRY_DSN ||
|
|
7
|
-
process.env.NEXT_PUBLIC_SENTRY_DSN;
|
|
4
|
+
process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
|
|
8
5
|
|
|
9
6
|
export const initSentry = (
|
|
10
7
|
type: 'Server' | 'Client' | 'Edge',
|
|
11
|
-
options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {
|
|
12
|
-
dsn: SENTRY_DSN,
|
|
13
|
-
integrations: [],
|
|
14
|
-
tracesSampleRate: 1.0
|
|
15
|
-
}
|
|
8
|
+
options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {}
|
|
16
9
|
) => {
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
// TODO: Handle options with ESLint rules
|
|
11
|
+
|
|
12
|
+
// TODO: Remove Zero Project DSN
|
|
13
|
+
|
|
14
|
+
Sentry.init({
|
|
15
|
+
dsn:
|
|
16
|
+
SENTRY_DSN ||
|
|
17
|
+
'https://d8558ef8997543deacf376c7d8d7cf4b@o64293.ingest.sentry.io/4504338423742464',
|
|
19
18
|
initialScope: {
|
|
20
19
|
tags: {
|
|
21
|
-
...((
|
|
22
|
-
options.initialScope as {
|
|
23
|
-
tags?: Record<string, string>;
|
|
24
|
-
}
|
|
25
|
-
)?.tags ?? {}),
|
|
26
20
|
APP_TYPE: 'ProjectZeroNext',
|
|
27
21
|
TYPE: type
|
|
28
22
|
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
},
|
|
24
|
+
tracesSampleRate: 1.0,
|
|
25
|
+
integrations: []
|
|
26
|
+
});
|
|
33
27
|
};
|
package/types/index.ts
CHANGED
|
@@ -71,12 +71,6 @@ export interface Currency {
|
|
|
71
71
|
|
|
72
72
|
export interface Settings {
|
|
73
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;
|
|
80
74
|
redis: {
|
|
81
75
|
defaultExpirationTime: number;
|
|
82
76
|
};
|
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
|
+
) => {
|
|
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
|
+
return undefined;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const requestURL = `${decodeURIComponent(commerceUrl)}${url}`;
|
|
@@ -48,30 +48,19 @@ const appFetch = async <T>(
|
|
|
48
48
|
status = req.status;
|
|
49
49
|
logger.debug(`FETCH END ${url}`, { status: req.status, ip });
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
throw new Error(`Request failed with status ${status}`);
|
|
53
|
-
}
|
|
51
|
+
const rawData = await req.text();
|
|
54
52
|
|
|
55
53
|
if (responseType === FetchResponseType.JSON) {
|
|
56
|
-
response =
|
|
54
|
+
response = JSON.parse(rawData);
|
|
57
55
|
} else {
|
|
58
|
-
response =
|
|
56
|
+
response = rawData as unknown as T;
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
logger.trace(`FETCH RESPONSE`, { url, response, ip });
|
|
62
60
|
} catch (error) {
|
|
63
|
-
const logType = status === 500 ? 'fatal' : 'error';
|
|
64
|
-
|
|
65
61
|
if (!url.toString().includes('/cms/seo/')) {
|
|
66
|
-
logger
|
|
62
|
+
logger.error(`FETCH FAILED`, { url, status, error, ip });
|
|
67
63
|
}
|
|
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);
|
|
75
64
|
}
|
|
76
65
|
|
|
77
66
|
return response;
|
|
@@ -1,88 +0,0 @@
|
|
|
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;
|