@akinon/next 2.0.0-beta.2 → 2.0.0-beta.21
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/.eslintrc.js +12 -0
- package/CHANGELOG.md +400 -7
- package/__tests__/next-config.test.ts +83 -0
- package/__tests__/tsconfig.json +23 -0
- package/api/auth.ts +381 -60
- package/api/barcode-search.ts +59 -0
- package/api/cache.ts +41 -5
- package/api/client.ts +21 -4
- package/api/form.ts +85 -0
- package/api/image-proxy.ts +75 -0
- package/api/product-categories.ts +53 -0
- package/api/similar-product-list.ts +63 -0
- package/api/similar-products.ts +111 -0
- package/api/virtual-try-on.ts +382 -0
- package/assets/styles/index.scss +84 -0
- package/babel.config.js +6 -0
- package/bin/pz-generate-routes.js +115 -0
- package/bin/pz-install-plugins.js +1 -1
- package/bin/pz-prebuild.js +1 -0
- package/bin/pz-predev.js +1 -0
- package/bin/pz-run-tests.js +99 -0
- package/bin/run-prebuild-tests.js +46 -0
- package/components/accordion.tsx +20 -5
- package/components/button.tsx +51 -36
- package/components/client-root.tsx +138 -2
- package/components/file-input.tsx +65 -3
- package/components/index.ts +1 -0
- package/components/input.tsx +1 -1
- package/components/link.tsx +46 -16
- package/components/logger-popup.tsx +213 -0
- package/components/modal.tsx +32 -16
- package/components/plugin-module.tsx +62 -3
- package/components/price.tsx +2 -2
- package/components/select.tsx +1 -1
- package/components/selected-payment-option-view.tsx +21 -0
- package/data/client/account.ts +17 -2
- package/data/client/api.ts +2 -0
- package/data/client/basket.ts +66 -5
- package/data/client/checkout.ts +391 -99
- package/data/client/misc.ts +38 -2
- package/data/client/product.ts +19 -2
- package/data/client/user.ts +16 -8
- package/data/server/category.ts +11 -9
- package/data/server/flatpage.ts +11 -4
- package/data/server/form.ts +15 -4
- package/data/server/landingpage.ts +11 -4
- package/data/server/list.ts +5 -4
- package/data/server/menu.ts +11 -3
- package/data/server/product.ts +111 -55
- package/data/server/seo.ts +14 -4
- package/data/server/special-page.ts +5 -4
- package/data/server/widget.ts +90 -5
- package/data/urls.ts +16 -5
- package/hocs/client/with-segment-defaults.tsx +2 -2
- package/hocs/server/with-segment-defaults.tsx +65 -20
- package/hooks/index.ts +4 -0
- package/hooks/use-localization.ts +24 -10
- package/hooks/use-logger-context.tsx +114 -0
- package/hooks/use-logger.ts +92 -0
- package/hooks/use-loyalty-availability.ts +21 -0
- package/hooks/use-payment-options.ts +2 -1
- package/hooks/use-pz-params.ts +37 -0
- package/hooks/use-router.ts +51 -14
- package/hooks/use-sentry-uncaught-errors.ts +24 -0
- package/instrumentation/index.ts +10 -1
- package/instrumentation/node.ts +2 -20
- package/jest.config.js +25 -0
- package/lib/cache-handler.mjs +534 -16
- package/lib/cache.ts +272 -37
- package/localization/index.ts +2 -1
- package/localization/provider.tsx +2 -5
- package/middlewares/bfcache-headers.ts +18 -0
- package/middlewares/checkout-provider.ts +1 -1
- package/middlewares/complete-gpay.ts +32 -26
- package/middlewares/complete-masterpass.ts +33 -26
- package/middlewares/complete-wallet.ts +182 -0
- package/middlewares/default.ts +360 -215
- package/middlewares/index.ts +10 -2
- package/middlewares/locale.ts +34 -11
- package/middlewares/masterpass-rest-callback.ts +230 -0
- package/middlewares/oauth-login.ts +200 -57
- package/middlewares/pretty-url.ts +21 -8
- package/middlewares/redirection-payment.ts +32 -26
- package/middlewares/saved-card-redirection.ts +33 -26
- package/middlewares/three-d-redirection.ts +32 -26
- package/middlewares/url-redirection.ts +11 -1
- package/middlewares/wallet-complete-redirection.ts +206 -0
- package/package.json +24 -9
- package/plugins.d.ts +19 -4
- package/plugins.js +10 -1
- package/redux/actions.ts +47 -0
- package/redux/middlewares/checkout.ts +63 -138
- package/redux/middlewares/index.ts +14 -10
- package/redux/middlewares/pre-order/address.ts +7 -2
- package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/data-source-shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/delivery-option.ts +7 -1
- package/redux/middlewares/pre-order/index.ts +16 -10
- package/redux/middlewares/pre-order/installment-option.ts +8 -1
- package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
- package/redux/middlewares/pre-order/payment-option.ts +7 -1
- package/redux/middlewares/pre-order/pre-order-validation.ts +8 -3
- package/redux/middlewares/pre-order/redirection.ts +8 -2
- package/redux/middlewares/pre-order/set-pre-order.ts +6 -2
- package/redux/middlewares/pre-order/shipping-option.ts +7 -1
- package/redux/middlewares/pre-order/shipping-step.ts +5 -1
- package/redux/reducers/checkout.ts +23 -3
- package/redux/reducers/index.ts +11 -3
- package/redux/reducers/root.ts +7 -2
- package/redux/reducers/widget.ts +80 -0
- package/sentry/index.ts +69 -13
- package/tailwind/content.js +16 -0
- package/types/commerce/account.ts +5 -1
- package/types/commerce/checkout.ts +35 -1
- package/types/commerce/widget.ts +33 -0
- package/types/index.ts +114 -6
- package/types/next-auth.d.ts +2 -2
- package/types/widget.ts +80 -0
- package/utils/app-fetch.ts +7 -2
- package/utils/generate-commerce-search-params.ts +3 -2
- package/utils/get-checkout-path.ts +3 -0
- package/utils/get-root-hostname.ts +28 -0
- package/utils/index.ts +64 -10
- package/utils/localization.ts +4 -0
- package/utils/mobile-3d-iframe.ts +8 -2
- package/utils/override-middleware.ts +7 -12
- package/utils/pz-segments.ts +92 -0
- package/utils/redirect-ignore.ts +35 -0
- package/utils/redirect.ts +9 -3
- package/utils/redirection-iframe.ts +8 -2
- package/utils/widget-styles.ts +107 -0
- package/views/error-page.tsx +93 -0
- package/with-pz-config.js +21 -7
package/api/auth.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import { NextApiRequest, NextApiResponse } from 'next';
|
|
2
1
|
import NextAuth, { Session } from 'next-auth';
|
|
3
|
-
import
|
|
2
|
+
import Credentials from 'next-auth/providers/credentials';
|
|
4
3
|
import { ROUTES } from 'routes';
|
|
5
4
|
import { URLS, user } from '../data/urls';
|
|
6
5
|
import Settings from 'settings';
|
|
7
6
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
8
7
|
import logger from '@akinon/next/utils/log';
|
|
9
8
|
import { AuthError } from '../types';
|
|
9
|
+
import getRootHostname from '../utils/get-root-hostname';
|
|
10
|
+
import { LocaleUrlStrategy } from '../localization';
|
|
11
|
+
import { cookies, headers } from 'next/headers';
|
|
10
12
|
|
|
13
|
+
// Shared helper
|
|
11
14
|
async function getCurrentUser(sessionId: string, currency = '') {
|
|
12
|
-
const
|
|
15
|
+
const reqHeaders = {
|
|
13
16
|
'Content-Type': 'application/json',
|
|
14
17
|
Cookie: `osessionid=${sessionId}`,
|
|
15
18
|
'x-currency': currency
|
|
@@ -17,7 +20,7 @@ async function getCurrentUser(sessionId: string, currency = '') {
|
|
|
17
20
|
|
|
18
21
|
const currentUser = await (
|
|
19
22
|
await fetch(URLS.user.currentUser, {
|
|
20
|
-
headers
|
|
23
|
+
headers: reqHeaders
|
|
21
24
|
})
|
|
22
25
|
).json();
|
|
23
26
|
|
|
@@ -40,14 +43,305 @@ async function getCurrentUser(sessionId: string, currency = '') {
|
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
// Runtime-detected error throwing: uses CredentialsSignin on v5, plain Error on v4
|
|
47
|
+
function throwAuthError(errors: AuthError[]): never {
|
|
48
|
+
const nextAuthModule = require('next-auth');
|
|
49
|
+
if (nextAuthModule.CredentialsSignin) {
|
|
50
|
+
class PzCredentialsError extends nextAuthModule.CredentialsSignin {
|
|
51
|
+
code = 'credentials';
|
|
52
|
+
constructor(errs: AuthError[]) {
|
|
53
|
+
super();
|
|
54
|
+
this.code = JSON.stringify(errs);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
throw new PzCredentialsError(errors);
|
|
58
|
+
}
|
|
59
|
+
throw new Error(JSON.stringify(errors));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Shared credential config used by both v4 and v5 error handling
|
|
63
|
+
function buildFieldErrors(response: any) {
|
|
64
|
+
const errors = [] as AuthError[];
|
|
65
|
+
|
|
66
|
+
const fieldErrors = Object.keys(response ?? {})
|
|
67
|
+
.filter((key) => key !== 'non_field_errors')
|
|
68
|
+
.map((key) => ({
|
|
69
|
+
name: key,
|
|
70
|
+
value: response[key]
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
if (response.redirect_url?.includes('captcha')) {
|
|
74
|
+
errors.push({ type: 'captcha' });
|
|
75
|
+
} else if (fieldErrors.length) {
|
|
76
|
+
errors.push({ type: 'field_errors', data: fieldErrors });
|
|
77
|
+
} else if (response.non_field_errors) {
|
|
78
|
+
errors.push({ type: 'non_field_errors', data: response.non_field_errors });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return errors;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ============================================================
|
|
85
|
+
// v5 (App Router / next-auth v5) — named export for new brands
|
|
86
|
+
// ============================================================
|
|
87
|
+
|
|
88
|
+
const getDefaultAuthConfig = () => {
|
|
44
89
|
return {
|
|
45
90
|
providers: [
|
|
46
|
-
|
|
91
|
+
Credentials({
|
|
47
92
|
id: 'oauth',
|
|
48
93
|
name: 'credentials',
|
|
49
94
|
credentials: {},
|
|
50
|
-
authorize: async (credentials) => {
|
|
95
|
+
authorize: async (credentials: any) => {
|
|
96
|
+
const cookieStore = await cookies();
|
|
97
|
+
const sessionId = cookieStore.get('osessionid')?.value;
|
|
98
|
+
|
|
99
|
+
if (!sessionId) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const currentUser = await getCurrentUser(
|
|
104
|
+
sessionId,
|
|
105
|
+
cookieStore.get('pz-currency')?.value ?? ''
|
|
106
|
+
);
|
|
107
|
+
return currentUser;
|
|
108
|
+
}
|
|
109
|
+
}),
|
|
110
|
+
Credentials({
|
|
111
|
+
id: 'default',
|
|
112
|
+
name: 'credentials',
|
|
113
|
+
credentials: {
|
|
114
|
+
email: { label: 'Email', type: 'email', placeholder: 'Email' },
|
|
115
|
+
password: {
|
|
116
|
+
label: 'Password',
|
|
117
|
+
type: 'password',
|
|
118
|
+
placeholder: 'Password'
|
|
119
|
+
},
|
|
120
|
+
formType: {},
|
|
121
|
+
locale: {},
|
|
122
|
+
captchaValidated: {}
|
|
123
|
+
},
|
|
124
|
+
authorize: async (credentials: any) => {
|
|
125
|
+
const cookieStore = await cookies();
|
|
126
|
+
const headerStore = await headers();
|
|
127
|
+
|
|
128
|
+
const reqHeaders: HeadersInit = new Headers();
|
|
129
|
+
const language = Settings.localization.locales.find(
|
|
130
|
+
(item: any) => item.value === credentials.locale
|
|
131
|
+
).apiValue;
|
|
132
|
+
const userIp = headerStore.get('x-forwarded-for') ?? '';
|
|
133
|
+
|
|
134
|
+
reqHeaders.set('Content-Type', 'application/json');
|
|
135
|
+
reqHeaders.set('cookie', headerStore.get('cookie') ?? '');
|
|
136
|
+
reqHeaders.set('Accept-Language', `${language}`);
|
|
137
|
+
reqHeaders.set(
|
|
138
|
+
'x-currency',
|
|
139
|
+
cookieStore.get('pz-currency')?.value ?? ''
|
|
140
|
+
);
|
|
141
|
+
reqHeaders.set('x-forwarded-for', userIp);
|
|
142
|
+
reqHeaders.set(
|
|
143
|
+
'x-app-device',
|
|
144
|
+
headerStore.get('x-app-device') ?? ''
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
reqHeaders.set(
|
|
148
|
+
'x-frontend-id',
|
|
149
|
+
cookieStore.get('pz-frontend-id')?.value || ''
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
logger.debug('Trying to login/register', {
|
|
153
|
+
formType: credentials.formType,
|
|
154
|
+
userIp
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const checkCurrentUser = await getCurrentUser(
|
|
158
|
+
cookieStore.get('osessionid')?.value ?? '',
|
|
159
|
+
cookieStore.get('pz-currency')?.value ?? ''
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (checkCurrentUser?.pk) {
|
|
163
|
+
const sessionCookie = reqHeaders
|
|
164
|
+
.get('cookie')
|
|
165
|
+
?.match(/osessionid=\w+/)?.[0]
|
|
166
|
+
.replace(/osessionid=/, '');
|
|
167
|
+
if (sessionCookie) {
|
|
168
|
+
reqHeaders.set('cookie', sessionCookie);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const apiRequest = await fetch(
|
|
173
|
+
`${Settings.commerceUrl}${user[credentials.formType]}`,
|
|
174
|
+
{
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers: reqHeaders,
|
|
177
|
+
body: JSON.stringify(credentials)
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
logger.info(`Login/Register request result: ${apiRequest.status}`, {
|
|
182
|
+
userIp
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const response = (await apiRequest.json()) as {
|
|
186
|
+
key: string;
|
|
187
|
+
non_field_errors: string[];
|
|
188
|
+
redirect_url: string;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
logger.debug(`Login/Register response: ${JSON.stringify(response)}`);
|
|
192
|
+
|
|
193
|
+
let sessionId = '';
|
|
194
|
+
const setCookieHeader = apiRequest.headers.get('set-cookie');
|
|
195
|
+
if (setCookieHeader) {
|
|
196
|
+
sessionId =
|
|
197
|
+
setCookieHeader
|
|
198
|
+
.match(/osessionid=\w+/)?.[0]
|
|
199
|
+
.replace(/osessionid=/, '') || '';
|
|
200
|
+
|
|
201
|
+
logger.debug(`Login/Register session id: ${sessionId}`);
|
|
202
|
+
} else {
|
|
203
|
+
logger.warn('No set-cookie header found in response');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (sessionId) {
|
|
207
|
+
const maxAge = 30 * 24 * 60 * 60;
|
|
208
|
+
const { localeUrlStrategy } = Settings.localization;
|
|
209
|
+
|
|
210
|
+
const fallbackHost =
|
|
211
|
+
headerStore.get('x-forwarded-host') ||
|
|
212
|
+
headerStore.get('host');
|
|
213
|
+
const hostname =
|
|
214
|
+
process.env.NEXT_PUBLIC_URL || `https://${fallbackHost}`;
|
|
215
|
+
const rootHostname =
|
|
216
|
+
localeUrlStrategy === LocaleUrlStrategy.Subdomain
|
|
217
|
+
? getRootHostname(hostname)
|
|
218
|
+
: null;
|
|
219
|
+
|
|
220
|
+
const cookieOptions = {
|
|
221
|
+
path: '/',
|
|
222
|
+
httpOnly: true,
|
|
223
|
+
secure: true,
|
|
224
|
+
maxAge,
|
|
225
|
+
...(rootHostname ? { domain: rootHostname } : {})
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
cookieStore.set('osessionid', sessionId, cookieOptions);
|
|
229
|
+
cookieStore.set('sessionid', sessionId, cookieOptions);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!response.key) {
|
|
233
|
+
const errors = buildFieldErrors(response);
|
|
234
|
+
|
|
235
|
+
if (apiRequest.status === 202) {
|
|
236
|
+
errors.push({ type: 'otp' });
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (errors.length) {
|
|
240
|
+
throwAuthError(errors);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const currentUser = await getCurrentUser(
|
|
245
|
+
sessionId,
|
|
246
|
+
cookieStore.get('pz-currency')?.value ?? ''
|
|
247
|
+
);
|
|
248
|
+
return currentUser;
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
],
|
|
252
|
+
callbacks: {
|
|
253
|
+
jwt: async ({ token, user }: any) => {
|
|
254
|
+
if (user) {
|
|
255
|
+
token.user = user;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return token;
|
|
259
|
+
},
|
|
260
|
+
async session({ session, token }: any) {
|
|
261
|
+
session.user = token.user as any;
|
|
262
|
+
return session;
|
|
263
|
+
},
|
|
264
|
+
async redirect({ url, baseUrl }: { url: string; baseUrl: string }) {
|
|
265
|
+
const headerStore = await headers();
|
|
266
|
+
const pathname = url.startsWith('/') ? url : url.replace(baseUrl, '');
|
|
267
|
+
const pathnameWithoutLocale = pathname.replace(
|
|
268
|
+
urlLocaleMatcherRegex,
|
|
269
|
+
''
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const localeResults = headerStore
|
|
273
|
+
.get('referer')
|
|
274
|
+
?.replace(baseUrl, '')
|
|
275
|
+
?.match(urlLocaleMatcherRegex);
|
|
276
|
+
|
|
277
|
+
return `${baseUrl}${localeResults?.[0] || ''}${pathnameWithoutLocale}`;
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
events: {
|
|
281
|
+
signIn: () => {
|
|
282
|
+
logger.debug('Successfully signed in');
|
|
283
|
+
},
|
|
284
|
+
signOut: async () => {
|
|
285
|
+
const cookieStore = await cookies();
|
|
286
|
+
cookieStore.set('osessionid', '', {
|
|
287
|
+
path: '/',
|
|
288
|
+
maxAge: 0
|
|
289
|
+
});
|
|
290
|
+
cookieStore.set('sessionid', '', {
|
|
291
|
+
path: '/',
|
|
292
|
+
maxAge: 0
|
|
293
|
+
});
|
|
294
|
+
logger.debug('Successfully signed out');
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
pages: {
|
|
298
|
+
signIn: ROUTES.AUTH,
|
|
299
|
+
error: ROUTES.AUTH
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export const createAuth = (customOptions?: () => Partial<any>) => {
|
|
305
|
+
const baseConfig = getDefaultAuthConfig();
|
|
306
|
+
const customConfig = customOptions ? customOptions() : {};
|
|
307
|
+
|
|
308
|
+
const mergedConfig = {
|
|
309
|
+
trustHost: true,
|
|
310
|
+
...baseConfig,
|
|
311
|
+
...customConfig,
|
|
312
|
+
providers: [
|
|
313
|
+
...baseConfig.providers,
|
|
314
|
+
...(customConfig.providers || [])
|
|
315
|
+
],
|
|
316
|
+
callbacks: {
|
|
317
|
+
...baseConfig.callbacks,
|
|
318
|
+
...customConfig.callbacks
|
|
319
|
+
},
|
|
320
|
+
events: {
|
|
321
|
+
...baseConfig.events,
|
|
322
|
+
...customConfig.events
|
|
323
|
+
},
|
|
324
|
+
pages: {
|
|
325
|
+
...baseConfig.pages,
|
|
326
|
+
...customConfig.pages
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
return (NextAuth as any)(mergedConfig);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// ============================================================
|
|
334
|
+
// v4 (Pages Router / next-auth v4) — default export for backward compat
|
|
335
|
+
// ============================================================
|
|
336
|
+
|
|
337
|
+
const defaultNextAuthOptionsV4 = (req: any, res: any) => {
|
|
338
|
+
return {
|
|
339
|
+
providers: [
|
|
340
|
+
Credentials({
|
|
341
|
+
id: 'oauth',
|
|
342
|
+
name: 'credentials',
|
|
343
|
+
credentials: {},
|
|
344
|
+
authorize: async () => {
|
|
51
345
|
const sessionId = req.cookies['osessionid'];
|
|
52
346
|
|
|
53
347
|
if (!sessionId) {
|
|
@@ -61,7 +355,7 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
61
355
|
return currentUser;
|
|
62
356
|
}
|
|
63
357
|
}),
|
|
64
|
-
|
|
358
|
+
Credentials({
|
|
65
359
|
id: 'default',
|
|
66
360
|
name: 'credentials',
|
|
67
361
|
credentials: {
|
|
@@ -75,33 +369,50 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
75
369
|
locale: {},
|
|
76
370
|
captchaValidated: {}
|
|
77
371
|
},
|
|
78
|
-
authorize: async (credentials) => {
|
|
79
|
-
const
|
|
372
|
+
authorize: async (credentials: any) => {
|
|
373
|
+
const reqHeaders: HeadersInit = new Headers();
|
|
80
374
|
const language = Settings.localization.locales.find(
|
|
81
|
-
(item) => item.value === credentials.locale
|
|
375
|
+
(item: any) => item.value === credentials.locale
|
|
82
376
|
).apiValue;
|
|
83
377
|
const userIp = req.headers['x-forwarded-for']?.toString() ?? '';
|
|
84
378
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
379
|
+
reqHeaders.set('Content-Type', 'application/json');
|
|
380
|
+
reqHeaders.set('cookie', `${req.headers.cookie}`);
|
|
381
|
+
reqHeaders.set('Accept-Language', `${language}`);
|
|
382
|
+
reqHeaders.set('x-currency', req.cookies['pz-currency'] ?? '');
|
|
383
|
+
reqHeaders.set('x-forwarded-for', userIp);
|
|
384
|
+
reqHeaders.set(
|
|
91
385
|
'x-app-device',
|
|
92
386
|
req.headers['x-app-device']?.toString() ?? ''
|
|
93
387
|
);
|
|
94
388
|
|
|
389
|
+
reqHeaders.set('x-frontend-id', req.cookies['pz-frontend-id'] || '');
|
|
390
|
+
|
|
95
391
|
logger.debug('Trying to login/register', {
|
|
96
392
|
formType: credentials.formType,
|
|
97
393
|
userIp
|
|
98
394
|
});
|
|
99
395
|
|
|
396
|
+
const checkCurrentUser = await getCurrentUser(
|
|
397
|
+
req.cookies['osessionid'] ?? '',
|
|
398
|
+
req.cookies['pz-currency'] ?? ''
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
if (checkCurrentUser?.pk) {
|
|
402
|
+
const sessionCookie = reqHeaders
|
|
403
|
+
.get('cookie')
|
|
404
|
+
?.match(/osessionid=\w+/)?.[0]
|
|
405
|
+
.replace(/osessionid=/, '');
|
|
406
|
+
if (sessionCookie) {
|
|
407
|
+
reqHeaders.set('cookie', sessionCookie);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
100
411
|
const apiRequest = await fetch(
|
|
101
412
|
`${Settings.commerceUrl}${user[credentials.formType]}`,
|
|
102
413
|
{
|
|
103
414
|
method: 'POST',
|
|
104
|
-
headers,
|
|
415
|
+
headers: reqHeaders,
|
|
105
416
|
body: JSON.stringify(credentials)
|
|
106
417
|
}
|
|
107
418
|
);
|
|
@@ -132,54 +443,36 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
132
443
|
}
|
|
133
444
|
|
|
134
445
|
if (sessionId) {
|
|
135
|
-
const maxAge = 30 * 24 * 60 * 60;
|
|
136
|
-
const
|
|
446
|
+
const maxAge = 30 * 24 * 60 * 60;
|
|
447
|
+
const { localeUrlStrategy } = Settings.localization;
|
|
448
|
+
|
|
449
|
+
const fallbackHost =
|
|
450
|
+
req.headers['x-forwarded-host']?.toString() ||
|
|
451
|
+
req.headers.host?.toString();
|
|
452
|
+
const hostname =
|
|
453
|
+
process.env.NEXT_PUBLIC_URL || `https://${fallbackHost}`;
|
|
454
|
+
const rootHostname =
|
|
455
|
+
localeUrlStrategy === LocaleUrlStrategy.Subdomain
|
|
456
|
+
? getRootHostname(hostname)
|
|
457
|
+
: null;
|
|
458
|
+
const domainOption = rootHostname ? `Domain=${rootHostname};` : '';
|
|
459
|
+
const cookieOptions = `Path=/; HttpOnly; Secure; Max-Age=${maxAge}; ${domainOption}`;
|
|
137
460
|
|
|
138
461
|
res.setHeader('Set-Cookie', [
|
|
139
462
|
`osessionid=${sessionId}; ${cookieOptions}`,
|
|
140
|
-
`sessionid=${sessionId}; ${cookieOptions}`
|
|
463
|
+
`sessionid=${sessionId}; ${cookieOptions}`
|
|
141
464
|
]);
|
|
142
465
|
}
|
|
143
466
|
|
|
144
467
|
if (!response.key) {
|
|
145
|
-
const errors =
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
.
|
|
149
|
-
.map((key) => {
|
|
150
|
-
return {
|
|
151
|
-
name: key,
|
|
152
|
-
value: response[key]
|
|
153
|
-
};
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
if (response.redirect_url?.includes('captcha')) {
|
|
157
|
-
errors.push({
|
|
158
|
-
type: 'captcha'
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
logger.debug('Captcha required', {
|
|
162
|
-
email: credentials.email,
|
|
163
|
-
formType: credentials.formType
|
|
164
|
-
});
|
|
165
|
-
} else if (apiRequest.status === 202) {
|
|
166
|
-
errors.push({
|
|
167
|
-
type: 'otp'
|
|
168
|
-
});
|
|
169
|
-
} else if (fieldErrors.length) {
|
|
170
|
-
errors.push({
|
|
171
|
-
type: 'field_errors',
|
|
172
|
-
data: fieldErrors
|
|
173
|
-
});
|
|
174
|
-
} else if (response.non_field_errors) {
|
|
175
|
-
errors.push({
|
|
176
|
-
type: 'non_field_errors',
|
|
177
|
-
data: response.non_field_errors
|
|
178
|
-
});
|
|
468
|
+
const errors = buildFieldErrors(response);
|
|
469
|
+
|
|
470
|
+
if (apiRequest.status === 202) {
|
|
471
|
+
errors.push({ type: 'otp' });
|
|
179
472
|
}
|
|
180
473
|
|
|
181
474
|
if (errors.length) {
|
|
182
|
-
|
|
475
|
+
throwAuthError(errors);
|
|
183
476
|
}
|
|
184
477
|
}
|
|
185
478
|
|
|
@@ -192,14 +485,14 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
192
485
|
})
|
|
193
486
|
],
|
|
194
487
|
callbacks: {
|
|
195
|
-
jwt: async ({ token, user }) => {
|
|
488
|
+
jwt: async ({ token, user }: any) => {
|
|
196
489
|
if (user) {
|
|
197
490
|
token.user = user;
|
|
198
491
|
}
|
|
199
492
|
|
|
200
493
|
return token;
|
|
201
494
|
},
|
|
202
|
-
async session({ session, user, token }) {
|
|
495
|
+
async session({ session, user, token }: any) {
|
|
203
496
|
session.user = token.user as Session['user'];
|
|
204
497
|
return session;
|
|
205
498
|
},
|
|
@@ -236,8 +529,36 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
236
529
|
};
|
|
237
530
|
};
|
|
238
531
|
|
|
239
|
-
const Auth = (
|
|
240
|
-
|
|
532
|
+
const Auth = (
|
|
533
|
+
req: any,
|
|
534
|
+
res: any,
|
|
535
|
+
customOptions?: (req: any, res: any) => Partial<any>
|
|
536
|
+
) => {
|
|
537
|
+
const baseOptions = defaultNextAuthOptionsV4(req, res);
|
|
538
|
+
const customOptionsResult = customOptions ? customOptions(req, res) : {};
|
|
539
|
+
|
|
540
|
+
const mergedOptions = {
|
|
541
|
+
...baseOptions,
|
|
542
|
+
...customOptionsResult,
|
|
543
|
+
providers: [
|
|
544
|
+
...baseOptions.providers,
|
|
545
|
+
...(customOptionsResult.providers || [])
|
|
546
|
+
],
|
|
547
|
+
callbacks: {
|
|
548
|
+
...baseOptions.callbacks,
|
|
549
|
+
...customOptionsResult.callbacks
|
|
550
|
+
},
|
|
551
|
+
events: {
|
|
552
|
+
...baseOptions.events,
|
|
553
|
+
...customOptionsResult.events
|
|
554
|
+
},
|
|
555
|
+
pages: {
|
|
556
|
+
...baseOptions.pages,
|
|
557
|
+
...customOptionsResult.pages
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
return (NextAuth as any)(req, res, mergedOptions);
|
|
241
562
|
};
|
|
242
563
|
|
|
243
564
|
export default Auth;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import Settings from 'settings';
|
|
3
|
+
|
|
4
|
+
export async function GET(request: NextRequest) {
|
|
5
|
+
try {
|
|
6
|
+
const { searchParams } = new URL(request.url);
|
|
7
|
+
const barcode = searchParams.get('search_text');
|
|
8
|
+
|
|
9
|
+
if (!barcode) {
|
|
10
|
+
return NextResponse.json(
|
|
11
|
+
{ error: 'Missing search_text parameter (barcode)' },
|
|
12
|
+
{ status: 400 }
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (Settings.commerceUrl === 'default') {
|
|
17
|
+
return NextResponse.json(
|
|
18
|
+
{ error: 'Commerce URL is not configured' },
|
|
19
|
+
{ status: 500 }
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const queryParams = new URLSearchParams();
|
|
24
|
+
queryParams.append('search_text', barcode);
|
|
25
|
+
|
|
26
|
+
searchParams.forEach((value, key) => {
|
|
27
|
+
if (key !== 'search_text') {
|
|
28
|
+
queryParams.append(key, value);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const apiUrl = `${Settings.commerceUrl}/list/?${queryParams.toString()}`;
|
|
33
|
+
|
|
34
|
+
const headers: Record<string, string> = {
|
|
35
|
+
Accept: 'application/json',
|
|
36
|
+
'Content-Type': 'application/json'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const response = await fetch(apiUrl, {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
headers
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
return NextResponse.json(
|
|
46
|
+
{ error: `API request failed with status: ${response.status}` },
|
|
47
|
+
{ status: response.status }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const data = await response.json();
|
|
52
|
+
return NextResponse.json(data);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return NextResponse.json(
|
|
55
|
+
{ error: (error as Error).message },
|
|
56
|
+
{ status: 500 }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
package/api/cache.ts
CHANGED
|
@@ -21,20 +21,56 @@ async function handleRequest(...args) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const formData = await req.formData();
|
|
24
|
-
const body = {} as {
|
|
24
|
+
const body = {} as {
|
|
25
|
+
key: string;
|
|
26
|
+
value?: string;
|
|
27
|
+
expire?: number;
|
|
28
|
+
keyValuePairs?: string;
|
|
29
|
+
compressed?: string;
|
|
30
|
+
};
|
|
25
31
|
|
|
26
32
|
formData.forEach((value, key) => {
|
|
27
33
|
body[key] = value;
|
|
28
34
|
});
|
|
29
35
|
|
|
30
|
-
const { key, value, expire } = body;
|
|
31
|
-
let response:
|
|
36
|
+
const { key, value, expire, keyValuePairs, compressed } = body;
|
|
37
|
+
let response: any;
|
|
32
38
|
|
|
33
39
|
try {
|
|
34
40
|
if (req.method === 'POST') {
|
|
35
|
-
|
|
41
|
+
// GET request - check if compressed flag is set
|
|
42
|
+
if (compressed === 'true') {
|
|
43
|
+
response = await Cache.getCompressed(key);
|
|
44
|
+
} else {
|
|
45
|
+
response = await Cache.get(key);
|
|
46
|
+
}
|
|
36
47
|
} else if (req.method === 'PUT') {
|
|
37
|
-
|
|
48
|
+
if (keyValuePairs) {
|
|
49
|
+
try {
|
|
50
|
+
const parsedKeyValuePairs = JSON.parse(keyValuePairs);
|
|
51
|
+
if (
|
|
52
|
+
typeof parsedKeyValuePairs !== 'object' ||
|
|
53
|
+
parsedKeyValuePairs === null ||
|
|
54
|
+
Array.isArray(parsedKeyValuePairs)
|
|
55
|
+
) {
|
|
56
|
+
throw new Error('Invalid keyValuePairs format - must be an object');
|
|
57
|
+
}
|
|
58
|
+
response = await Cache.mset(parsedKeyValuePairs, expire);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.error('Invalid keyValuePairs in mset request', { error });
|
|
61
|
+
return NextResponse.json(
|
|
62
|
+
{ error: 'Invalid keyValuePairs format' },
|
|
63
|
+
{ status: 400 }
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
// SET request - check if compressed flag is set
|
|
68
|
+
if (compressed === 'true') {
|
|
69
|
+
response = await Cache.setCompressed(key, value, expire);
|
|
70
|
+
} else {
|
|
71
|
+
response = await Cache.set(key, value, expire);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
38
74
|
}
|
|
39
75
|
} catch (error) {
|
|
40
76
|
logger.error(error);
|