@akinon/next 2.0.0-beta.9 → 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.
Files changed (121) hide show
  1. package/CHANGELOG.md +434 -23
  2. package/__tests__/next-config.test.ts +83 -0
  3. package/__tests__/tsconfig.json +23 -0
  4. package/api/auth.ts +367 -63
  5. package/api/barcode-search.ts +59 -0
  6. package/api/cache.ts +41 -5
  7. package/api/client.ts +21 -4
  8. package/api/form.ts +85 -0
  9. package/api/image-proxy.ts +75 -0
  10. package/api/product-categories.ts +53 -0
  11. package/api/similar-product-list.ts +63 -0
  12. package/api/similar-products.ts +111 -0
  13. package/api/virtual-try-on.ts +382 -0
  14. package/assets/styles/index.scss +84 -0
  15. package/babel.config.js +6 -0
  16. package/bin/pz-generate-routes.js +115 -0
  17. package/bin/pz-install-plugins.js +1 -1
  18. package/bin/pz-prebuild.js +1 -0
  19. package/bin/pz-predev.js +1 -0
  20. package/bin/pz-run-tests.js +99 -0
  21. package/components/accordion.tsx +21 -6
  22. package/components/client-root.tsx +119 -3
  23. package/components/file-input.tsx +65 -3
  24. package/components/index.ts +1 -0
  25. package/components/input.tsx +2 -2
  26. package/components/link.tsx +46 -16
  27. package/components/logger-popup.tsx +213 -0
  28. package/components/modal.tsx +32 -16
  29. package/components/plugin-module.tsx +62 -3
  30. package/components/price.tsx +2 -2
  31. package/components/select.tsx +3 -3
  32. package/components/selected-payment-option-view.tsx +21 -0
  33. package/data/client/account.ts +17 -2
  34. package/data/client/basket.ts +39 -0
  35. package/data/client/checkout.ts +336 -99
  36. package/data/client/misc.ts +13 -1
  37. package/data/server/category.ts +11 -9
  38. package/data/server/flatpage.ts +4 -1
  39. package/data/server/form.ts +4 -1
  40. package/data/server/landingpage.ts +4 -1
  41. package/data/server/list.ts +5 -4
  42. package/data/server/menu.ts +4 -1
  43. package/data/server/product.ts +97 -52
  44. package/data/server/seo.ts +4 -1
  45. package/data/server/special-page.ts +5 -4
  46. package/data/server/widget.ts +71 -1
  47. package/data/urls.ts +6 -3
  48. package/hocs/client/with-segment-defaults.tsx +2 -2
  49. package/hocs/server/with-segment-defaults.tsx +81 -20
  50. package/hooks/index.ts +3 -0
  51. package/hooks/use-localization.ts +24 -10
  52. package/hooks/use-logger-context.tsx +114 -0
  53. package/hooks/use-logger.ts +92 -0
  54. package/hooks/use-loyalty-availability.ts +21 -0
  55. package/hooks/use-payment-options.ts +2 -1
  56. package/hooks/use-pz-params.ts +37 -0
  57. package/hooks/use-router.ts +53 -19
  58. package/instrumentation/index.ts +0 -1
  59. package/instrumentation/node.ts +2 -20
  60. package/jest.config.js +25 -0
  61. package/lib/cache-handler.mjs +534 -16
  62. package/lib/cache.ts +269 -34
  63. package/localization/provider.tsx +2 -5
  64. package/middlewares/bfcache-headers.ts +18 -0
  65. package/middlewares/checkout-provider.ts +1 -1
  66. package/middlewares/complete-gpay.ts +32 -26
  67. package/middlewares/complete-masterpass.ts +33 -26
  68. package/middlewares/complete-wallet.ts +182 -0
  69. package/middlewares/default.ts +357 -203
  70. package/middlewares/index.ts +10 -2
  71. package/middlewares/locale.ts +5 -3
  72. package/middlewares/masterpass-rest-callback.ts +230 -0
  73. package/middlewares/oauth-login.ts +200 -57
  74. package/middlewares/pretty-url.ts +21 -8
  75. package/middlewares/redirection-payment.ts +32 -26
  76. package/middlewares/saved-card-redirection.ts +33 -26
  77. package/middlewares/three-d-redirection.ts +32 -26
  78. package/middlewares/url-redirection.ts +9 -15
  79. package/middlewares/wallet-complete-redirection.ts +206 -0
  80. package/package.json +24 -10
  81. package/plugins.d.ts +19 -4
  82. package/plugins.js +9 -1
  83. package/redux/actions.ts +47 -0
  84. package/redux/middlewares/checkout.ts +61 -8
  85. package/redux/middlewares/index.ts +14 -10
  86. package/redux/middlewares/pre-order/address.ts +1 -1
  87. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +1 -1
  88. package/redux/middlewares/pre-order/data-source-shipping-option.ts +1 -1
  89. package/redux/middlewares/pre-order/delivery-option.ts +1 -1
  90. package/redux/middlewares/pre-order/index.ts +3 -1
  91. package/redux/middlewares/pre-order/installment-option.ts +2 -1
  92. package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
  93. package/redux/middlewares/pre-order/payment-option.ts +1 -1
  94. package/redux/middlewares/pre-order/pre-order-validation.ts +4 -3
  95. package/redux/middlewares/pre-order/redirection.ts +2 -2
  96. package/redux/middlewares/pre-order/set-pre-order.ts +2 -2
  97. package/redux/middlewares/pre-order/shipping-option.ts +1 -1
  98. package/redux/middlewares/pre-order/shipping-step.ts +1 -1
  99. package/redux/reducers/checkout.ts +15 -1
  100. package/redux/reducers/index.ts +7 -1
  101. package/redux/reducers/widget.ts +80 -0
  102. package/sentry/index.ts +54 -17
  103. package/tailwind/content.js +16 -0
  104. package/types/commerce/checkout.ts +26 -1
  105. package/types/commerce/widget.ts +33 -0
  106. package/types/index.ts +114 -5
  107. package/types/next-auth.d.ts +2 -2
  108. package/types/widget.ts +80 -0
  109. package/utils/app-fetch.ts +7 -2
  110. package/utils/generate-commerce-search-params.ts +3 -2
  111. package/utils/get-checkout-path.ts +3 -0
  112. package/utils/get-root-hostname.ts +28 -0
  113. package/utils/index.ts +69 -18
  114. package/utils/mobile-3d-iframe.ts +8 -2
  115. package/utils/override-middleware.ts +1 -0
  116. package/utils/pz-segments.ts +92 -0
  117. package/utils/redirect-ignore.ts +35 -0
  118. package/utils/redirect.ts +9 -3
  119. package/utils/redirection-iframe.ts +8 -2
  120. package/utils/widget-styles.ts +107 -0
  121. 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 CredentialProvider from 'next-auth/providers/credentials';
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 headers = {
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
- const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
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
- CredentialProvider({
340
+ Credentials({
47
341
  id: 'oauth',
48
342
  name: 'credentials',
49
343
  credentials: {},
50
- authorize: async (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
- CredentialProvider({
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 headers: HeadersInit = new Headers();
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
- headers.set('Content-Type', 'application/json');
86
- headers.set('cookie', `${req.headers.cookie}`);
87
- headers.set('Accept-Language', `${language}`);
88
- headers.set('x-currency', req.cookies['pz-currency'] ?? '');
89
- headers.set('x-forwarded-for', userIp);
90
- headers.set(
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
- headers.set('x-frontend-id', req.cookies['pz-frontend-id'] || '');
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 = headers
402
+ const sessionCookie = reqHeaders
109
403
  .get('cookie')
110
404
  ?.match(/osessionid=\w+/)?.[0]
111
405
  .replace(/osessionid=/, '');
112
406
  if (sessionCookie) {
113
- headers.set('cookie', sessionCookie);
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; // 30 days in seconds
153
- const cookieOptions = `Path=/; HttpOnly; Secure; Max-Age=${maxAge}`;
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}` // required to get 3D redirection form
463
+ `sessionid=${sessionId}; ${cookieOptions}`
158
464
  ]);
159
465
  }
160
466
 
161
467
  if (!response.key) {
162
- const errors = [] as AuthError[];
163
-
164
- const fieldErrors = Object.keys(response ?? {})
165
- .filter((key) => key !== 'non_field_errors')
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
- throw new Error(JSON.stringify(errors));
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 = (req, res) => {
257
- return NextAuth(req, res, nextAuthOptions(req, res));
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 { key: string; value?: string; expire?: number };
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: string | boolean;
36
+ const { key, value, expire, keyValuePairs, compressed } = body;
37
+ let response: any;
32
38
 
33
39
  try {
34
40
  if (req.method === 'POST') {
35
- response = await Cache.get(key);
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
- response = await Cache.set(key, value, expire);
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);