PluginProbe
Extendify / 2.2.0
Extendify v2.2.0
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / src / Launch / api / DataApi.js

DataApi.js in Extendify 2.2.0, at src/Launch/api/DataApi.js

473 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { PATTERNS_HOST, AI_HOST, IMAGES_HOST } from '@constants';
2 import { formatSiteQuestionsForAPI } from '@shared/utils/format-site-questions-for-api';
3 import { mergeRequiredPlugins } from '@shared/utils/merge-required-plugins';
4 import { getHeadersAndFooters } from '@launch/api/WPApi';
5 import { useUserSelectionStore } from '@launch/state/user-selections';
6
7 // Optionally add items to request body
8 const allowList = [
9 'partnerId',
10 'devbuild',
11 'version',
12 'siteId',
13 'wpLanguage',
14 'wpVersion',
15 'siteProfile',
16 ];
17
18 const extraBody = {
19 ...Object.fromEntries(
20 Object.entries(window.extSharedData).filter(([key]) =>
21 allowList.includes(key),
22 ),
23 ),
24 };
25
26 const fetchTemplates = async (type, siteType, otherData = {}) => {
27 const { showLocalizedCopy } = window.extSharedData;
28 const otherDataProcessed = Object.entries(otherData).reduce(
29 (result, [key, value]) => {
30 if (value == null) result;
31 return {
32 ...result,
33 [key]: typeof value === 'object' ? JSON.stringify(value) : value,
34 };
35 },
36 {},
37 );
38
39 const res = await fetch(`${PATTERNS_HOST}/api/${type}-templates`, {
40 method: 'POST',
41 headers: { 'Content-Type': 'application/json' },
42 body: JSON.stringify({
43 ...extraBody,
44 siteType: siteType?.slug,
45 showLocalizedCopy: !!showLocalizedCopy,
46 ...otherDataProcessed,
47 }),
48 });
49
50 if (!res.ok) throw new Error('Bad response from server');
51
52 return await res.json();
53 };
54
55 export const getHomeTemplates = async ({
56 siteType,
57 siteStructure,
58 siteProfile,
59 siteStrings,
60 siteImages,
61 siteStyles,
62 siteObjective,
63 siteQuestions = [],
64 sitePlugins = [],
65 }) => {
66 const styles = await fetchTemplates('home', siteType, {
67 siteStructure,
68 siteProfile,
69 siteStrings,
70 siteImages,
71 siteStyles,
72 siteObjective,
73 siteQuestions,
74 sitePlugins,
75 });
76 const { wpLanguage, showImprint } = window.extSharedData || {};
77
78 // Check if we should show footer navigation
79 // This is based on the imprint page and the language of the site
80 const hasFooterNav = Array.isArray(showImprint)
81 ? showImprint.includes(wpLanguage ?? '') &&
82 siteProfile?.aiSiteCategory === 'Business'
83 : false;
84
85 const { headers, footers } = await getHeadersAndFooters(hasFooterNav);
86 if (!styles?.length) {
87 throw new Error('Could not get styles');
88 }
89 return styles.map((template, index) => {
90 // Cycle through the headers and footers
91 const header = headers[index % headers.length];
92 const footer = footers[index % footers.length];
93 return {
94 ...template,
95 headerCode: header?.content?.raw?.trim() ?? '',
96 footerCode: footer?.content?.raw?.trim() ?? '',
97 };
98 });
99 };
100
101 export const getPageTemplates = async ({
102 siteType,
103 siteStructure,
104 siteStrings,
105 siteImages,
106 siteStyle,
107 siteQuestions = [],
108 sitePlugins = [],
109 }) => {
110 const { siteInformation, siteProfile } = useUserSelectionStore.getState();
111 const pages = await fetchTemplates('page', siteType, {
112 siteInformation,
113 siteStructure,
114 siteStrings,
115 siteImages,
116 siteStyle,
117 siteProfile,
118 siteQuestions,
119 sitePlugins,
120 });
121 if (!pages?.recommended) {
122 throw new Error('Could not get pages');
123 }
124 return {
125 recommended: pages.recommended.map(({ slug, ...rest }) => ({
126 ...rest,
127 slug,
128 id: slug,
129 })),
130 optional: pages.optional.map(({ slug, ...rest }) => ({
131 ...rest,
132 slug,
133 id: slug,
134 })),
135 };
136 };
137
138 export const generateCustomPatterns = async (page, userState, siteProfile) => {
139 const res = await fetch(`${AI_HOST}/api/patterns`, {
140 method: 'POST',
141 headers: { 'Content-Type': 'application/json' },
142 body: JSON.stringify({
143 ...extraBody,
144 page,
145 userState,
146 siteProfile,
147 }),
148 });
149
150 if (!res.ok) throw new Error('Bad response from server');
151 return await res.json();
152 };
153
154 export const getLinkSuggestions = async (pageContent, availablePages) => {
155 const { siteType } = useUserSelectionStore.getState();
156 try {
157 const res = await fetch(`${AI_HOST}/api/link-pages`, {
158 method: 'POST',
159 headers: { 'Content-Type': 'application/json' },
160 body: JSON.stringify({
161 ...extraBody,
162 siteType: siteType?.slug,
163 pageContent,
164 availablePages,
165 }),
166 });
167 if (!res.ok) throw new Error('Bad response from server');
168 return await res.json();
169 } catch (error) {
170 // fail gracefully
171 return {};
172 }
173 };
174
175 export const getSiteProfile = async ({ title, description }) => {
176 const url = `${AI_HOST}/api/site-profile`;
177 const method = 'POST';
178 const headers = { 'Content-Type': 'application/json' };
179 const body = JSON.stringify({
180 ...extraBody,
181 title,
182 description,
183 });
184 const fallback = {
185 aiSiteType: null,
186 aiSiteCategory: null,
187 aiDescription: null,
188 aiKeywords: [],
189 logoObjectName: null,
190 };
191 let response;
192 try {
193 response = await fetch(url, { method, headers, body });
194 } catch (error) {
195 // try one more time
196 try {
197 response = await fetch(url, { method, headers, body });
198 } catch {
199 return fallback;
200 }
201 }
202
203 if (!response?.ok) return fallback;
204
205 try {
206 return (await response.json()) || fallback;
207 } catch (error) {
208 return fallback;
209 }
210 };
211
212 export const getSiteStrings = async (siteProfile) => {
213 const url = `${AI_HOST}/api/site-strings`;
214 const method = 'POST';
215 const headers = { 'Content-Type': 'application/json' };
216 const body = JSON.stringify({ ...extraBody, siteProfile });
217 const fallback = { aiHeaders: [], aiBlogTitles: [] };
218 let response;
219 try {
220 response = await fetch(url, { method, headers, body });
221 } catch (error) {
222 // try one more time
223 try {
224 response = await fetch(url, { method, headers, body });
225 } catch {
226 return fallback;
227 }
228 }
229 if (!response?.ok) return fallback;
230 let data;
231 try {
232 data = await response.json();
233 } catch (error) {
234 return fallback;
235 }
236 return data?.aiHeaders ? data : fallback;
237 };
238
239 export const getSiteImages = async (siteProfile) => {
240 const { aiSiteType, aiSiteCategory, aiDescription, aiKeywords } = siteProfile;
241 const { siteInformation } = useUserSelectionStore.getState();
242 const search = new URLSearchParams({
243 aiSiteType,
244 aiSiteCategory,
245 aiDescription,
246 aiKeywords,
247 ...extraBody,
248 source: 'launch',
249 });
250 if (siteInformation?.title) search.append('title', siteInformation.title);
251 const url = `${IMAGES_HOST}/api/search?${search}`;
252 const method = 'GET';
253 const headers = { 'Content-Type': 'application/json' };
254 const fallback = { siteImages: [] };
255 const requestTimeOut = 10000;
256
257 let response;
258 try {
259 response = await fetch(url, {
260 method,
261 headers,
262 signal: AbortSignal.timeout(requestTimeOut),
263 });
264 } catch (error) {
265 // try one more time
266 try {
267 response = await fetch(url, {
268 method,
269 headers,
270 signal: AbortSignal.timeout(requestTimeOut),
271 });
272 } catch {
273 return fallback;
274 }
275 }
276
277 if (!response?.ok) return fallback;
278 let data;
279 try {
280 data = await response.json();
281 } catch (error) {
282 return fallback;
283 }
284 return data?.siteImages ? data : fallback;
285 };
286
287 export const getSiteStyles = async ({ title, siteProfile }) => {
288 const request = new Request(`${AI_HOST}/api/styles`, {
289 method: 'POST',
290 headers: { 'Content-Type': 'application/json' },
291 body: JSON.stringify({ ...extraBody, title, siteProfile }),
292 });
293 const fallback = [];
294
295 let response;
296
297 try {
298 response = await fetch(request);
299 } catch (error) {
300 // try one more time
301 try {
302 response = await fetch(request);
303 } catch {
304 return fallback;
305 }
306 }
307
308 if (!response?.ok) {
309 return fallback;
310 }
311
312 try {
313 return await response.json();
314 } catch (_) {
315 return fallback;
316 }
317 };
318
319 export const getSiteLogo = async (objectName) => {
320 const url = `${AI_HOST}/api/site-profile/generate-logo`;
321 const method = 'POST';
322 const headers = { 'Content-Type': 'application/json' };
323 const siteId = window.extSharedData?.siteId ?? '';
324 const partnerId = window.extSharedData?.partnerId ?? '';
325 const showAILogo = window.extSharedData?.showAILogo ?? false;
326 const fallback =
327 'https://images.extendify-cdn.com/demo-content/logos/ext-custom-logo-default.webp';
328
329 if (!showAILogo || !objectName) {
330 return fallback;
331 }
332
333 if (!siteId || !partnerId) {
334 throw new Error(
335 'Missing required parameter (siteId, partnerId or objectName)',
336 );
337 }
338
339 const body = JSON.stringify({ objectName, siteId, partnerId });
340
341 let response;
342 try {
343 response = await fetch(url, { method, headers, body });
344 if (!response?.ok) throw new Error('Bad response from server');
345 } catch (error) {
346 try {
347 response = await fetch(url, { method, headers, body });
348 } catch {
349 return fallback;
350 }
351 }
352
353 if (!response?.ok) return fallback;
354
355 try {
356 const data = await response.json();
357 return data?.logoUrl ?? fallback;
358 } catch (error) {
359 return fallback;
360 }
361 };
362
363 export const getSiteQuestions = async ({ siteProfile }) => {
364 const url = `${AI_HOST}/api/site-questions`;
365 const method = 'POST';
366 const headers = { 'Content-Type': 'application/json' };
367 const fallback = { questions: [] };
368
369 if (!siteProfile) {
370 return fallback;
371 }
372
373 const { wpLanguage } = window.extSharedData;
374 const { businessInformation, siteObjective } =
375 useUserSelectionStore.getState();
376 const body = JSON.stringify({
377 ...siteProfile,
378 description: businessInformation?.description || '',
379 siteObjective: siteObjective || '',
380 wpLanguage,
381 });
382
383 let response;
384 try {
385 response = await fetch(url, { method, headers, body });
386 if (!response?.ok) throw new Error('Bad response from server');
387 } catch (error) {
388 try {
389 response = await fetch(url, { method, headers, body });
390 } catch {
391 return fallback;
392 }
393 }
394
395 if (!response?.ok) return fallback;
396
397 try {
398 const data = await response.json();
399 return data?.questions ?? fallback;
400 } catch (error) {
401 return fallback;
402 }
403 };
404
405 export const getSitePlugins = async ({ siteProfile, siteQA }) => {
406 const url = `${AI_HOST}/api/site-plugins`;
407 const method = 'POST';
408 const headers = { 'Content-Type': 'application/json' };
409 const fallback = mergeRequiredPlugins([]);
410
411 if (!siteProfile) {
412 return fallback;
413 }
414
415 const { wpLanguage, partnerId, pluginGroupId } = window.extSharedData;
416 const { siteObjective } = useUserSelectionStore.getState();
417
418 const body = JSON.stringify({
419 ...siteProfile,
420 siteQuestions: formatSiteQuestionsForAPI(siteQA),
421 siteObjective: siteObjective || '',
422 wpLanguage,
423 partnerId,
424 pluginGroupId,
425 });
426
427 let response;
428
429 try {
430 response = await fetch(url, { method, headers, body });
431 if (!response?.ok) throw new Error('Bad response from server');
432 } catch (error) {
433 try {
434 response = await fetch(url, { method, headers, body });
435 } catch {
436 return fallback;
437 }
438 }
439
440 if (!response?.ok) return fallback;
441
442 try {
443 const data = await response.json();
444 const suggestedPlugins = data?.selectedPlugins ?? fallback;
445 return mergeRequiredPlugins(suggestedPlugins);
446 } catch (error) {
447 return fallback;
448 }
449 };
450
451 export const getImprintPageTemplate = async (siteStyle = {}) => {
452 const endpoint = `${PATTERNS_HOST}/api/page-imprint`;
453
454 const res = await fetch(endpoint, {
455 method: 'POST',
456 headers: { 'Content-Type': 'application/json' },
457 body: JSON.stringify({
458 ...extraBody,
459 siteStyle: JSON.stringify(siteStyle),
460 }),
461 });
462
463 if (!res.ok) throw new Error('Could not get imprint page');
464
465 const response = await res.json();
466
467 if (!response?.template) {
468 throw new Error('No template found for imprint page');
469 }
470
471 return { ...response.template };
472 };
473