PluginProbe
Extendify / 3.2.0
Extendify v3.2.0
3.2.1 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 All 127 releases
extendify / src / Shared / api / wp.js

wp.js in Extendify 3.2.0, at src/Shared/api/wp.js

192 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { AI_HOST } from '@constants';
2 import { renderAiLabelPill, stampAiLabel } from '@shared/lib/ai-label';
3 import apiFetch from '@wordpress/api-fetch';
4 import { sprintf } from '@wordpress/i18n';
5 import { addQueryArgs } from '@wordpress/url';
6
7 export const getPlugin = async (slug) => {
8 const response = await apiFetch({
9 path: addQueryArgs('/wp/v2/plugins', { search: slug }),
10 });
11
12 const plugin = response?.[0];
13
14 if (!plugin) throw new Error('Plugin not found');
15
16 return plugin;
17 };
18
19 export const getAllPlugins = async () => {
20 const response = await apiFetch({
21 path: '/wp/v2/plugins',
22 });
23
24 if (!response) {
25 throw new Error('Failed to fetch installed plugins');
26 }
27
28 return response;
29 };
30
31 export const enableAutoUpdate = async (plugin) => {
32 if (!plugin) return;
33 try {
34 await apiFetch({
35 path: '/extendify/v1/shared/enable-auto-update',
36 method: 'POST',
37 data: { plugin },
38 });
39 } catch (_e) {
40 // Best-effort: the install already succeeded.
41 }
42 };
43
44 export const installPlugin = async (slug) => {
45 const plugin = await apiFetch({
46 path: '/wp/v2/plugins',
47 method: 'POST',
48 data: {
49 slug,
50 },
51 });
52
53 await enableAutoUpdate(plugin?.plugin);
54
55 return plugin;
56 };
57
58 export const activatePlugin = async (slug) => {
59 const plugin = await getPlugin(slug);
60
61 return await apiFetch({
62 path: `/wp/v2/plugins/${plugin.plugin}`,
63 method: 'POST',
64 data: {
65 status: 'active',
66 },
67 });
68 };
69
70 export const loadImage = (img) => {
71 return new Promise((resolve, reject) => {
72 img.onload = () => resolve(img);
73 img.onerror = (e) => reject(e);
74 });
75 };
76
77 const shouldDisclose = (metadata) =>
78 Boolean(metadata.aiGenerated) && Boolean(metadata.disclose);
79
80 const altText = (metadata) => {
81 if (!shouldDisclose(metadata)) return metadata.alt ?? '';
82 const pattern = window.extSharedData?.aiImageAltPattern ?? 'AI Generated: %s';
83 return sprintf(pattern, metadata.alt ?? '').trim();
84 };
85
86 export const importImage = async (imageUrl, metadata = {}) => {
87 const image = new Image();
88 image.src = imageUrl;
89 image.crossOrigin = 'anonymous';
90 await loadImage(image);
91
92 const canvas = document.createElement('canvas');
93 canvas.width = image.width;
94 canvas.height = image.height;
95
96 const ctx = canvas.getContext('2d');
97 if (!ctx) return;
98 ctx.drawImage(image, 0, 0);
99 if (shouldDisclose(metadata)) {
100 stampAiLabel(ctx, canvas.width, canvas.height);
101 }
102
103 const blob = await new Promise((resolve) => {
104 canvas.toBlob((blob) => {
105 blob && resolve(blob);
106 }, 'image/jpeg');
107 });
108
109 const formData = new FormData();
110 formData.append('file', new File([blob], metadata.filename));
111 formData.append('alt_text', altText(metadata));
112 formData.append('caption', metadata.caption ?? '');
113 formData.append('status', 'publish');
114 if (metadata.aiGenerated) {
115 formData.append('meta[extendify_ai_generated]', '1');
116 }
117
118 return await apiFetch({
119 path: 'wp/v2/media',
120 method: 'POST',
121 body: formData,
122 });
123 };
124
125 export const importImageServer = async (src, metadata = {}) => {
126 const formData = new FormData();
127 formData.append('source', src);
128 // Fallback doesn't support custom file_name
129 formData.append('alt_text', altText(metadata));
130 formData.append('caption', metadata.caption ?? '');
131 if (metadata.aiGenerated) {
132 formData.append('ai_generated', '1');
133 if (shouldDisclose(metadata)) {
134 const pill = await renderAiLabelPill();
135 if (pill) {
136 formData.append(
137 'disclosure_label',
138 new File([pill], 'disclosure-label.png', { type: 'image/png' }),
139 );
140 }
141 }
142 }
143
144 return await apiFetch({
145 path: '/extendify/v1/draft/upload-image',
146 method: 'POST',
147 body: formData,
148 });
149 };
150
151 export const downloadImage = async (
152 id,
153 src,
154 source,
155 unsplashId,
156 metadata = { alt: '', caption: '' },
157 ) => {
158 let image;
159 const aiGenerated = source === 'ai-generated';
160 if (unsplashId) {
161 await downloadPing(id, source, { unsplashId });
162 } else if (aiGenerated && id) {
163 await downloadPing(id, source, { disclose: metadata.disclose });
164 }
165 try {
166 image = await importImage(src, {
167 alt: metadata.alt,
168 filename: 'image.jpg',
169 caption: metadata.caption,
170 aiGenerated,
171 disclose: metadata.disclose,
172 });
173 } catch (_e) {
174 image = await importImageServer(src, {
175 alt: metadata.alt,
176 filename: 'image.jpg',
177 caption: metadata.caption,
178 aiGenerated,
179 disclose: metadata.disclose,
180 });
181 }
182
183 return image;
184 };
185
186 export const downloadPing = (id, source, details = {}) =>
187 fetch(`${AI_HOST}/api/draft/image/download`, {
188 method: 'POST',
189 headers: { 'Content-Type': 'application/json' },
190 body: JSON.stringify({ id, source, ...details }),
191 });
192