@akinon/next 2.0.0-beta.8 → 2.0.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 +438 -21
- package/__tests__/next-config.test.ts +83 -0
- package/__tests__/tsconfig.json +23 -0
- package/api/auth.ts +367 -63
- 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/components/accordion.tsx +21 -6
- package/components/client-root.tsx +119 -3
- package/components/file-input.tsx +65 -3
- package/components/index.ts +1 -0
- package/components/input.tsx +2 -2
- 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 +3 -3
- package/components/selected-payment-option-view.tsx +21 -0
- package/data/client/account.ts +17 -2
- package/data/client/basket.ts +39 -0
- package/data/client/checkout.ts +336 -99
- package/data/client/misc.ts +13 -1
- package/data/server/category.ts +11 -9
- package/data/server/flatpage.ts +4 -1
- package/data/server/form.ts +4 -1
- package/data/server/landingpage.ts +4 -1
- package/data/server/list.ts +5 -4
- package/data/server/menu.ts +4 -1
- package/data/server/product.ts +97 -52
- package/data/server/seo.ts +4 -1
- package/data/server/special-page.ts +5 -4
- package/data/server/widget.ts +71 -1
- package/data/urls.ts +6 -3
- package/hocs/client/with-segment-defaults.tsx +2 -2
- package/hocs/server/with-segment-defaults.tsx +81 -20
- package/hooks/index.ts +3 -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 +53 -19
- package/instrumentation/index.ts +0 -1
- package/instrumentation/node.ts +2 -20
- package/jest.config.js +25 -0
- package/lib/cache-handler.mjs +534 -16
- package/lib/cache.ts +269 -34
- 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 +357 -203
- package/middlewares/index.ts +10 -2
- package/middlewares/locale.ts +5 -3
- 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 +9 -15
- package/middlewares/wallet-complete-redirection.ts +206 -0
- package/package.json +24 -10
- package/plugins.d.ts +19 -4
- package/plugins.js +9 -1
- package/redux/actions.ts +47 -0
- package/redux/middlewares/checkout.ts +61 -8
- package/redux/middlewares/index.ts +14 -10
- package/redux/middlewares/pre-order/address.ts +1 -1
- package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +1 -1
- package/redux/middlewares/pre-order/data-source-shipping-option.ts +1 -1
- package/redux/middlewares/pre-order/delivery-option.ts +1 -1
- package/redux/middlewares/pre-order/index.ts +3 -1
- package/redux/middlewares/pre-order/installment-option.ts +2 -1
- package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
- package/redux/middlewares/pre-order/payment-option.ts +1 -1
- package/redux/middlewares/pre-order/pre-order-validation.ts +4 -3
- package/redux/middlewares/pre-order/redirection.ts +2 -2
- package/redux/middlewares/pre-order/set-pre-order.ts +2 -2
- package/redux/middlewares/pre-order/shipping-option.ts +1 -1
- package/redux/middlewares/pre-order/shipping-step.ts +1 -1
- package/redux/reducers/checkout.ts +15 -1
- package/redux/reducers/index.ts +7 -1
- package/redux/reducers/widget.ts +80 -0
- package/sentry/index.ts +54 -17
- package/tailwind/content.js +16 -0
- package/types/commerce/checkout.ts +26 -1
- package/types/commerce/widget.ts +33 -0
- package/types/index.ts +114 -5
- 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 +69 -18
- package/utils/mobile-3d-iframe.ts +8 -2
- package/utils/override-middleware.ts +1 -0
- 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/with-pz-config.js +20 -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 = () => {
|
|
89
|
+
return {
|
|
90
|
+
providers: [
|
|
91
|
+
Credentials({
|
|
92
|
+
id: 'oauth',
|
|
93
|
+
name: 'credentials',
|
|
94
|
+
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) => {
|
|
44
338
|
return {
|
|
45
339
|
providers: [
|
|
46
|
-
|
|
340
|
+
Credentials({
|
|
47
341
|
id: 'oauth',
|
|
48
342
|
name: 'credentials',
|
|
49
343
|
credentials: {},
|
|
50
|
-
authorize: async (
|
|
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,24 +369,24 @@ 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
|
|
|
95
|
-
|
|
389
|
+
reqHeaders.set('x-frontend-id', req.cookies['pz-frontend-id'] || '');
|
|
96
390
|
|
|
97
391
|
logger.debug('Trying to login/register', {
|
|
98
392
|
formType: credentials.formType,
|
|
@@ -105,12 +399,12 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
105
399
|
);
|
|
106
400
|
|
|
107
401
|
if (checkCurrentUser?.pk) {
|
|
108
|
-
const sessionCookie =
|
|
402
|
+
const sessionCookie = reqHeaders
|
|
109
403
|
.get('cookie')
|
|
110
404
|
?.match(/osessionid=\w+/)?.[0]
|
|
111
405
|
.replace(/osessionid=/, '');
|
|
112
406
|
if (sessionCookie) {
|
|
113
|
-
|
|
407
|
+
reqHeaders.set('cookie', sessionCookie);
|
|
114
408
|
}
|
|
115
409
|
}
|
|
116
410
|
|
|
@@ -118,7 +412,7 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
118
412
|
`${Settings.commerceUrl}${user[credentials.formType]}`,
|
|
119
413
|
{
|
|
120
414
|
method: 'POST',
|
|
121
|
-
headers,
|
|
415
|
+
headers: reqHeaders,
|
|
122
416
|
body: JSON.stringify(credentials)
|
|
123
417
|
}
|
|
124
418
|
);
|
|
@@ -149,54 +443,36 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
149
443
|
}
|
|
150
444
|
|
|
151
445
|
if (sessionId) {
|
|
152
|
-
const maxAge = 30 * 24 * 60 * 60;
|
|
153
|
-
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}`;
|
|
154
460
|
|
|
155
461
|
res.setHeader('Set-Cookie', [
|
|
156
462
|
`osessionid=${sessionId}; ${cookieOptions}`,
|
|
157
|
-
`sessionid=${sessionId}; ${cookieOptions}`
|
|
463
|
+
`sessionid=${sessionId}; ${cookieOptions}`
|
|
158
464
|
]);
|
|
159
465
|
}
|
|
160
466
|
|
|
161
467
|
if (!response.key) {
|
|
162
|
-
const errors =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
.
|
|
166
|
-
.map((key) => {
|
|
167
|
-
return {
|
|
168
|
-
name: key,
|
|
169
|
-
value: response[key]
|
|
170
|
-
};
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
if (response.redirect_url?.includes('captcha')) {
|
|
174
|
-
errors.push({
|
|
175
|
-
type: 'captcha'
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
logger.debug('Captcha required', {
|
|
179
|
-
email: credentials.email,
|
|
180
|
-
formType: credentials.formType
|
|
181
|
-
});
|
|
182
|
-
} else if (apiRequest.status === 202) {
|
|
183
|
-
errors.push({
|
|
184
|
-
type: 'otp'
|
|
185
|
-
});
|
|
186
|
-
} else if (fieldErrors.length) {
|
|
187
|
-
errors.push({
|
|
188
|
-
type: 'field_errors',
|
|
189
|
-
data: fieldErrors
|
|
190
|
-
});
|
|
191
|
-
} else if (response.non_field_errors) {
|
|
192
|
-
errors.push({
|
|
193
|
-
type: 'non_field_errors',
|
|
194
|
-
data: response.non_field_errors
|
|
195
|
-
});
|
|
468
|
+
const errors = buildFieldErrors(response);
|
|
469
|
+
|
|
470
|
+
if (apiRequest.status === 202) {
|
|
471
|
+
errors.push({ type: 'otp' });
|
|
196
472
|
}
|
|
197
473
|
|
|
198
474
|
if (errors.length) {
|
|
199
|
-
|
|
475
|
+
throwAuthError(errors);
|
|
200
476
|
}
|
|
201
477
|
}
|
|
202
478
|
|
|
@@ -209,14 +485,14 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
209
485
|
})
|
|
210
486
|
],
|
|
211
487
|
callbacks: {
|
|
212
|
-
jwt: async ({ token, user }) => {
|
|
488
|
+
jwt: async ({ token, user }: any) => {
|
|
213
489
|
if (user) {
|
|
214
490
|
token.user = user;
|
|
215
491
|
}
|
|
216
492
|
|
|
217
493
|
return token;
|
|
218
494
|
},
|
|
219
|
-
async session({ session, user, token }) {
|
|
495
|
+
async session({ session, user, token }: any) {
|
|
220
496
|
session.user = token.user as Session['user'];
|
|
221
497
|
return session;
|
|
222
498
|
},
|
|
@@ -253,8 +529,36 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
253
529
|
};
|
|
254
530
|
};
|
|
255
531
|
|
|
256
|
-
const Auth = (
|
|
257
|
-
|
|
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);
|
|
258
562
|
};
|
|
259
563
|
|
|
260
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);
|