@akinon/next 1.114.0-snapshot-ZERO-3890-20251209002502 → 1.114.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 +5 -3
- package/components/plugin-module.tsx +3 -30
- package/hocs/server/with-segment-defaults.tsx +5 -2
- package/lib/cache-handler.mjs +4 -12
- package/package.json +2 -2
- package/plugins.js +1 -3
- package/utils/app-fetch.ts +2 -2
- package/api/image-proxy.ts +0 -75
- package/api/product-categories.ts +0 -53
- package/api/similar-product-list.ts +0 -63
- package/api/similar-products.ts +0 -111
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
-
## 1.114.0
|
|
3
|
+
## 1.114.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- 65d3b862: ZERO-3054: Update headers in appFetch
|
|
8
|
+
- 43c182ee: ZERO-3054: Update Redis variable checks to conditionally include CACHE_SECRET
|
|
9
|
+
- eeb20bea: Revert "ZERO-3054: Refactor cache handler to use custom Redis handler and implement key hashing"
|
|
10
|
+
- fdd255ee: ZERO-3054: Refactor cache handler to use custom Redis handler and implement key hashing
|
|
9
11
|
|
|
10
12
|
## 1.113.0
|
|
11
13
|
|
|
@@ -24,9 +24,7 @@ enum Plugin {
|
|
|
24
24
|
FlowPayment = 'pz-flow-payment',
|
|
25
25
|
VirtualTryOn = 'pz-virtual-try-on',
|
|
26
26
|
Hepsipay = 'pz-hepsipay',
|
|
27
|
-
MasterpassRest = 'pz-masterpass-rest'
|
|
28
|
-
SimilarProducts = 'pz-similar-products',
|
|
29
|
-
Haso = 'pz-haso'
|
|
27
|
+
MasterpassRest = 'pz-masterpass-rest'
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
export enum Component {
|
|
@@ -57,15 +55,7 @@ export enum Component {
|
|
|
57
55
|
IyzicoSavedCard = 'IyzicoSavedCardOption',
|
|
58
56
|
Hepsipay = 'Hepsipay',
|
|
59
57
|
FlowPayment = 'FlowPayment',
|
|
60
|
-
MasterpassRest = 'MasterpassRestOption'
|
|
61
|
-
SimilarProductsModal = 'SimilarProductsModal',
|
|
62
|
-
SimilarProductsFilterSidebar = 'SimilarProductsFilterSidebar',
|
|
63
|
-
SimilarProductsResultsGrid = 'SimilarProductsResultsGrid',
|
|
64
|
-
SimilarProductsPlugin = 'SimilarProductsPlugin',
|
|
65
|
-
ProductImageSearchFeature = 'ProductImageSearchFeature',
|
|
66
|
-
ImageSearchButton = 'ImageSearchButton',
|
|
67
|
-
HeaderImageSearchFeature = 'HeaderImageSearchFeature',
|
|
68
|
-
HasoPaymentGateway = 'HasoPaymentGateway'
|
|
58
|
+
MasterpassRest = 'MasterpassRestOption'
|
|
69
59
|
}
|
|
70
60
|
|
|
71
61
|
const PluginComponents = new Map([
|
|
@@ -98,18 +88,6 @@ const PluginComponents = new Map([
|
|
|
98
88
|
[Component.AkifastQuickLoginButton, Component.AkifastCheckoutButton]
|
|
99
89
|
],
|
|
100
90
|
[Plugin.MultiBasket, [Component.MultiBasket]],
|
|
101
|
-
[
|
|
102
|
-
Plugin.SimilarProducts,
|
|
103
|
-
[
|
|
104
|
-
Component.SimilarProductsModal,
|
|
105
|
-
Component.SimilarProductsFilterSidebar,
|
|
106
|
-
Component.SimilarProductsResultsGrid,
|
|
107
|
-
Component.SimilarProductsPlugin,
|
|
108
|
-
Component.ProductImageSearchFeature,
|
|
109
|
-
Component.ImageSearchButton,
|
|
110
|
-
Component.HeaderImageSearchFeature
|
|
111
|
-
]
|
|
112
|
-
],
|
|
113
91
|
[Plugin.SavedCard, [Component.SavedCard, Component.IyzicoSavedCard]],
|
|
114
92
|
[Plugin.SavedCard, [Component.SavedCard]],
|
|
115
93
|
[Plugin.FlowPayment, [Component.FlowPayment]],
|
|
@@ -119,8 +97,7 @@ const PluginComponents = new Map([
|
|
|
119
97
|
],
|
|
120
98
|
[Plugin.Hepsipay, [Component.Hepsipay]],
|
|
121
99
|
[Plugin.Hepsipay, [Component.Hepsipay]],
|
|
122
|
-
[Plugin.MasterpassRest, [Component.MasterpassRest]]
|
|
123
|
-
[Plugin.Haso, [Component.HasoPaymentGateway]]
|
|
100
|
+
[Plugin.MasterpassRest, [Component.MasterpassRest]]
|
|
124
101
|
]);
|
|
125
102
|
|
|
126
103
|
const getPlugin = (component: Component) => {
|
|
@@ -193,10 +170,6 @@ export default function PluginModule({
|
|
|
193
170
|
promise = import(`${'@akinon/pz-virtual-try-on'}`);
|
|
194
171
|
} else if (plugin === Plugin.MasterpassRest) {
|
|
195
172
|
promise = import(`${'@akinon/pz-masterpass-rest'}`);
|
|
196
|
-
} else if (plugin === Plugin.SimilarProducts) {
|
|
197
|
-
promise = import(`${'@akinon/pz-similar-products'}`);
|
|
198
|
-
} else if (plugin === Plugin.Haso) {
|
|
199
|
-
promise = import(`${'@akinon/pz-haso'}`);
|
|
200
173
|
}
|
|
201
174
|
} catch (error) {
|
|
202
175
|
logger.error(error);
|
|
@@ -72,10 +72,13 @@ const addRootLayoutProps = async (componentProps: RootLayoutProps) => {
|
|
|
72
72
|
const checkRedisVariables = () => {
|
|
73
73
|
const requiredVariableValues = [
|
|
74
74
|
process.env.CACHE_HOST,
|
|
75
|
-
process.env.CACHE_PORT
|
|
76
|
-
process.env.CACHE_SECRET
|
|
75
|
+
process.env.CACHE_PORT
|
|
77
76
|
];
|
|
78
77
|
|
|
78
|
+
if (!settings.usePrettyUrlRoute) {
|
|
79
|
+
requiredVariableValues.push(process.env.CACHE_SECRET);
|
|
80
|
+
}
|
|
81
|
+
|
|
79
82
|
if (
|
|
80
83
|
!requiredVariableValues.every((v) => v) &&
|
|
81
84
|
process.env.NODE_ENV === 'production'
|
package/lib/cache-handler.mjs
CHANGED
|
@@ -463,14 +463,6 @@ CacheHandler.onCreation(async () => {
|
|
|
463
463
|
set: async (key, value, context) => {
|
|
464
464
|
const vKey = versionKey(key);
|
|
465
465
|
|
|
466
|
-
const stripTags = (val) => {
|
|
467
|
-
if (val && typeof val === 'object') {
|
|
468
|
-
const { tags, ...rest } = val;
|
|
469
|
-
return { ...rest, tags: [] };
|
|
470
|
-
}
|
|
471
|
-
return val;
|
|
472
|
-
};
|
|
473
|
-
|
|
474
466
|
let compressedValue;
|
|
475
467
|
let shouldUseCompressed = false;
|
|
476
468
|
|
|
@@ -490,12 +482,12 @@ CacheHandler.onCreation(async () => {
|
|
|
490
482
|
|
|
491
483
|
if (shouldUseCompressed) {
|
|
492
484
|
try {
|
|
493
|
-
await redisHandler.set(vKey,
|
|
485
|
+
await redisHandler.set(vKey, compressedValue, context);
|
|
494
486
|
|
|
495
487
|
redisSetResult = { status: 'fulfilled' };
|
|
496
488
|
} catch (compressionError) {
|
|
497
489
|
try {
|
|
498
|
-
await redisHandler.set(vKey,
|
|
490
|
+
await redisHandler.set(vKey, value, context);
|
|
499
491
|
|
|
500
492
|
redisSetResult = { status: 'fulfilled' };
|
|
501
493
|
} catch (fallbackError) {
|
|
@@ -504,7 +496,7 @@ CacheHandler.onCreation(async () => {
|
|
|
504
496
|
}
|
|
505
497
|
} else {
|
|
506
498
|
try {
|
|
507
|
-
await redisHandler.set(vKey,
|
|
499
|
+
await redisHandler.set(vKey, value, context);
|
|
508
500
|
redisSetResult = { status: 'fulfilled' };
|
|
509
501
|
} catch (error) {
|
|
510
502
|
redisSetResult = { status: 'rejected', reason: error };
|
|
@@ -518,8 +510,8 @@ CacheHandler.onCreation(async () => {
|
|
|
518
510
|
localSetResult = { status: 'fulfilled' };
|
|
519
511
|
} catch (error) {
|
|
520
512
|
localSetResult = { status: 'rejected', reason: error };
|
|
513
|
+
return localSetResult;
|
|
521
514
|
}
|
|
522
|
-
return localSetResult;
|
|
523
515
|
},
|
|
524
516
|
delete: async (key, context) => {
|
|
525
517
|
const vKey = versionKey(key);
|
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.114.0
|
|
4
|
+
"version": "1.114.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"set-cookie-parser": "2.6.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@akinon/eslint-plugin-projectzero": "1.114.0
|
|
38
|
+
"@akinon/eslint-plugin-projectzero": "1.114.0",
|
|
39
39
|
"@babel/core": "7.26.10",
|
|
40
40
|
"@babel/preset-env": "7.26.9",
|
|
41
41
|
"@babel/preset-typescript": "7.27.0",
|
package/plugins.js
CHANGED
package/utils/app-fetch.ts
CHANGED
|
@@ -43,12 +43,12 @@ const appFetch = async <T>({
|
|
|
43
43
|
const requestURL = `${decodeURIComponent(commerceUrl)}${url}`;
|
|
44
44
|
|
|
45
45
|
init.headers = {
|
|
46
|
+
cookie: nextCookies.toString(),
|
|
46
47
|
...(init.headers ?? {}),
|
|
47
48
|
...(ServerVariables.globalHeaders ?? {}),
|
|
48
49
|
'Accept-Language': currentLocale.apiValue,
|
|
49
50
|
'x-currency': currency,
|
|
50
|
-
'x-forwarded-for': ip
|
|
51
|
-
cookie: nextCookies.toString()
|
|
51
|
+
'x-forwarded-for': ip
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
init.next = {
|
package/api/image-proxy.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
|
|
3
|
-
async function proxyImage(imageUrl: string) {
|
|
4
|
-
if (!imageUrl) {
|
|
5
|
-
return NextResponse.json(
|
|
6
|
-
{ success: false, error: 'Missing url parameter' },
|
|
7
|
-
{ status: 400 }
|
|
8
|
-
);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const imageResponse = await fetch(imageUrl);
|
|
12
|
-
|
|
13
|
-
if (!imageResponse.ok) {
|
|
14
|
-
return NextResponse.json(
|
|
15
|
-
{ success: false, error: 'Failed to fetch image from URL' },
|
|
16
|
-
{ status: 400 }
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const imageBuffer = await imageResponse.arrayBuffer();
|
|
21
|
-
const base64Image = Buffer.from(imageBuffer).toString('base64');
|
|
22
|
-
const contentType = imageResponse.headers.get('content-type') || 'image/jpeg';
|
|
23
|
-
|
|
24
|
-
return { base64Image, contentType, imageBuffer };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function GET(request: NextRequest) {
|
|
28
|
-
try {
|
|
29
|
-
const { searchParams } = new URL(request.url);
|
|
30
|
-
const imageUrl = searchParams.get('url');
|
|
31
|
-
|
|
32
|
-
const result = await proxyImage(imageUrl!);
|
|
33
|
-
if (result instanceof NextResponse) {
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return new NextResponse(result.imageBuffer, {
|
|
38
|
-
status: 200,
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': result.contentType,
|
|
41
|
-
'Access-Control-Allow-Origin': '*',
|
|
42
|
-
'Cache-Control': 'public, max-age=3600'
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.error('Image proxy error:', error);
|
|
47
|
-
return NextResponse.json(
|
|
48
|
-
{ success: false, error: 'Internal server error' },
|
|
49
|
-
{ status: 500 }
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function POST(request: NextRequest) {
|
|
55
|
-
try {
|
|
56
|
-
const body = await request.json();
|
|
57
|
-
const imageUrl = body.imageUrl;
|
|
58
|
-
|
|
59
|
-
const result = await proxyImage(imageUrl);
|
|
60
|
-
if (result instanceof NextResponse) {
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return NextResponse.json({
|
|
65
|
-
success: true,
|
|
66
|
-
base64Image: `data:${result.contentType};base64,${result.base64Image}`
|
|
67
|
-
});
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('Image proxy POST error:', error);
|
|
70
|
-
return NextResponse.json(
|
|
71
|
-
{ success: false, error: 'Internal server error' },
|
|
72
|
-
{ status: 500 }
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { getProductData } from '@akinon/next/data/server';
|
|
3
|
-
|
|
4
|
-
export async function GET(request: NextRequest) {
|
|
5
|
-
try {
|
|
6
|
-
const { searchParams } = new URL(request.url);
|
|
7
|
-
const pksParam = searchParams.get('pks');
|
|
8
|
-
|
|
9
|
-
if (!pksParam) {
|
|
10
|
-
return NextResponse.json(
|
|
11
|
-
{ error: 'pks parameter required' },
|
|
12
|
-
{ status: 400 }
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const pks = pksParam.split(',').map(Number).filter(Boolean);
|
|
17
|
-
|
|
18
|
-
if (pks.length === 0) {
|
|
19
|
-
return NextResponse.json({ error: 'Invalid pks' }, { status: 400 });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const results = await Promise.all(
|
|
23
|
-
pks.map(async (pk) => {
|
|
24
|
-
try {
|
|
25
|
-
const { breadcrumbData } = await getProductData({ pk });
|
|
26
|
-
|
|
27
|
-
const categoryIds =
|
|
28
|
-
breadcrumbData
|
|
29
|
-
?.map((item: any) => item.extra_context?.attributes?.category_id)
|
|
30
|
-
.filter(Boolean) || [];
|
|
31
|
-
|
|
32
|
-
return { pk, categoryIds };
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.error(`Error fetching product ${pk}:`, error);
|
|
35
|
-
return { pk, categoryIds: [] };
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const mapping: Record<string, number[]> = {};
|
|
41
|
-
results.forEach(({ pk, categoryIds }) => {
|
|
42
|
-
mapping[String(pk)] = categoryIds;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
return NextResponse.json(mapping);
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.error('Error in product-categories API:', error);
|
|
48
|
-
return NextResponse.json(
|
|
49
|
-
{ error: 'Internal server error' },
|
|
50
|
-
{ status: 500 }
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
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 dynamicFilter = request.headers.get('x-search-dynamic-filter');
|
|
8
|
-
const dynamicExclude = request.headers.get('x-search-dynamic-exclude');
|
|
9
|
-
|
|
10
|
-
if (!dynamicFilter && !dynamicExclude) {
|
|
11
|
-
return NextResponse.json(
|
|
12
|
-
{
|
|
13
|
-
error:
|
|
14
|
-
'Missing x-search-dynamic-filter or x-search-dynamic-exclude header'
|
|
15
|
-
},
|
|
16
|
-
{ status: 400 }
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (Settings.commerceUrl === 'default') {
|
|
21
|
-
return NextResponse.json(
|
|
22
|
-
{ error: 'Commerce URL is not configured' },
|
|
23
|
-
{ status: 500 }
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const queryString = searchParams.toString();
|
|
28
|
-
const apiUrl = `${Settings.commerceUrl}/list${
|
|
29
|
-
queryString ? `?${queryString}` : ''
|
|
30
|
-
}`;
|
|
31
|
-
|
|
32
|
-
const headers: Record<string, string> = {
|
|
33
|
-
Accept: 'application/json',
|
|
34
|
-
'Content-Type': 'application/json'
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
if (dynamicFilter) {
|
|
38
|
-
headers['x-search-dynamic-filter'] = dynamicFilter;
|
|
39
|
-
} else if (dynamicExclude) {
|
|
40
|
-
headers['x-search-dynamic-exclude'] = dynamicExclude;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const response = await fetch(apiUrl, {
|
|
44
|
-
method: 'GET',
|
|
45
|
-
headers
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
return NextResponse.json(
|
|
50
|
-
{ error: `API request failed with status: ${response.status}` },
|
|
51
|
-
{ status: response.status }
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const data = await response.json();
|
|
56
|
-
return NextResponse.json(data);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
return NextResponse.json(
|
|
59
|
-
{ error: (error as Error).message },
|
|
60
|
-
{ status: 500 }
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
}
|
package/api/similar-products.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import Settings from 'settings';
|
|
3
|
-
|
|
4
|
-
const IMAGE_SEARCH_API_URL = Settings.commerceUrl + '/image-search/';
|
|
5
|
-
|
|
6
|
-
const errorResponse = (message: string, status: number) => {
|
|
7
|
-
return NextResponse.json({ error: message }, { status });
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export async function GET(request: Request) {
|
|
11
|
-
const { searchParams } = new URL(request.url);
|
|
12
|
-
const limit = searchParams.get('limit') || '20';
|
|
13
|
-
const url = searchParams.get('url');
|
|
14
|
-
const excludedProductIds = searchParams.get('excluded_product_ids');
|
|
15
|
-
|
|
16
|
-
if (!url) {
|
|
17
|
-
return errorResponse('URL parameter is required', 400);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (Settings.commerceUrl === 'default') {
|
|
21
|
-
return errorResponse('Commerce URL is not configured', 500);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const apiParams = new URLSearchParams();
|
|
25
|
-
apiParams.append('limit', limit);
|
|
26
|
-
apiParams.append('url', url);
|
|
27
|
-
|
|
28
|
-
if (excludedProductIds) {
|
|
29
|
-
apiParams.append('excluded_product_ids', excludedProductIds);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const apiUrl = `${IMAGE_SEARCH_API_URL}?${apiParams.toString()}`;
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
const response = await fetch(apiUrl, {
|
|
36
|
-
method: 'GET',
|
|
37
|
-
headers: {
|
|
38
|
-
Accept: 'application/json'
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
const errorText = await response.text();
|
|
44
|
-
return errorResponse(
|
|
45
|
-
errorText || `API request failed with status: ${response.status}`,
|
|
46
|
-
response.status
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const responseText = await response.text();
|
|
51
|
-
|
|
52
|
-
return NextResponse.json(JSON.parse(responseText));
|
|
53
|
-
} catch (error) {
|
|
54
|
-
return errorResponse((error as Error).message, 500);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function POST(request: Request) {
|
|
59
|
-
const { searchParams } = new URL(request.url);
|
|
60
|
-
const limit = searchParams.get('limit') || '20';
|
|
61
|
-
|
|
62
|
-
if (Settings.commerceUrl === 'default') {
|
|
63
|
-
return errorResponse('Commerce URL is not configured', 500);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let requestBody;
|
|
67
|
-
try {
|
|
68
|
-
requestBody = await request.json();
|
|
69
|
-
} catch (error) {
|
|
70
|
-
return errorResponse('Invalid JSON in request body', 400);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (!requestBody.image) {
|
|
74
|
-
return errorResponse('Image data is required in request body', 400);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const apiParams = new URLSearchParams();
|
|
78
|
-
apiParams.append('limit', limit);
|
|
79
|
-
|
|
80
|
-
const apiUrl = `${IMAGE_SEARCH_API_URL}?${apiParams.toString()}`;
|
|
81
|
-
|
|
82
|
-
const bodyData: any = { image: requestBody.image };
|
|
83
|
-
|
|
84
|
-
if (requestBody.excluded_product_ids) {
|
|
85
|
-
bodyData.excluded_product_ids = requestBody.excluded_product_ids;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const response = await fetch(apiUrl, {
|
|
90
|
-
method: 'POST',
|
|
91
|
-
headers: {
|
|
92
|
-
'Content-Type': 'application/json',
|
|
93
|
-
Accept: 'application/json'
|
|
94
|
-
},
|
|
95
|
-
body: JSON.stringify(bodyData)
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (!response.ok) {
|
|
99
|
-
const errorText = await response.text();
|
|
100
|
-
return errorResponse(
|
|
101
|
-
errorText || `API request failed with status: ${response.status}`,
|
|
102
|
-
response.status
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const responseData = await response.json();
|
|
107
|
-
return NextResponse.json(responseData);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
return errorResponse((error as Error).message, 500);
|
|
110
|
-
}
|
|
111
|
-
}
|