PluginProbe
WP-Stateless – Google Cloud Storage / trunk
WP-Stateless – Google Cloud Storage vtrunk
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
← All changes | lib/classes/class-api.php +252 -145 2.2.2trunk View file →
@@ -1,5 +1,6 @@
1 1 <?php
2 +
2 3 /**
3 4 * API Handler
4 5 *
5 6 *
@@ -5,224 +6,330 @@
5 6 *
6 7 *
7 8 * @since 1.0.0
8 9 */
10 +
9 11 namespace wpCloud\StatelessMedia {
10 12
11 - if( !class_exists( 'wpCloud\StatelessMedia\API' ) ) {
13 + use wpCloud\StatelessMedia\Sync\FileSync;
14 + use wpCloud\StatelessMedia\Sync\ImageSync;
15 + use wpCloud\StatelessMedia\Sync\NonLibrarySync;
12 16
17 + if (!class_exists('wpCloud\StatelessMedia\API')) {
13 18
19 + /**
20 + * Class API
21 + *
22 + * @package wpCloud\StatelessMedia
23 + */
14 24 final class API {
15 25
16 26 /**
27 + * Decoded auth_token data
28 + *
29 + * @var null|\stdClass
30 + */
31 + static private $tokenData = null;
32 +
33 + /**
34 + * Validate auth token and save data for further processing
35 + *
36 + * @param \WP_REST_Request $request
37 + * @return bool|\WP_Error
38 + */
39 + static public function authCheck(\WP_REST_Request $request) {
40 + $auth_token = $request->get_header('authorization');
41 + // Allow using custom `x-wps-auth` header if authorization hedaer is disabled
42 + if (!$auth_token) $auth_token = $request->get_header('x-wps-auth');
43 +
44 + if (!$auth_token) return false;
45 +
46 + try {
47 + self::$tokenData = Utility::verify_jwt_token($auth_token);
48 + } catch (\Exception $e) {
49 + self::$tokenData = null;
50 + return new \WP_Error('auth_failed', $e->getMessage(), ['status' => 401]);
51 + }
52 + return true;
53 + }
54 +
55 + /**
17 56 * API Status Endpoint.
18 57 *
19 - * @return array
58 + * @return \WP_REST_Response
20 59 */
21 60 static public function status() {
22 -
23 - return array(
61 + return new \WP_REST_Response(array(
24 62 "ok" => true,
25 63 "message" => "API up."
26 - );
27 -
64 + ), 200);
28 65 }
29 66
30 67 /**
31 - * Jobs Endpoint.
68 + * Get settings
32 69 *
33 - * @return array
70 + * @todo Implement this if needed
71 + * @param \WP_REST_Request $request
72 + * @return \WP_REST_Response|\WP_Error
34 73 */
35 - static public function jobs() {
36 -
37 - return array(
38 - "ok" => true,
39 - "message" => "Job endpoint up.",
40 - "jobs" => array()
41 - );
42 -
74 + static public function getSettings(\WP_REST_Request $request) {
75 + return new \WP_Error('not_implemented', 'Method not implemented', ['status' => 501]);
43 76 }
44 77
45 78 /**
46 - * Get settings Endpoint.
79 + * Update settings
47 80 *
48 - * @param $request
49 - * @return array
81 + * @param \WP_REST_Request $request
82 + * @return \WP_REST_Response|\WP_Error
50 83 */
51 - static public function getSettings( $request ) {
84 + static public function updateSettings(\WP_REST_Request $request) {
85 + if (self::$tokenData === null || empty(self::$tokenData->user_id)) {
86 + return new \WP_Error('unauthorized', 'Auth token looks incorrect', ['status' => 401]);
87 + }
88 + $is_gae = apply_filters('wp_stateless_is_app_engine', false);
89 + $upload_dir = wp_upload_dir();
90 + $is_upload_dir_writable = is_writable($upload_dir['basedir']);
52 91
53 - if( !self::authRequest( $request ) ) {
54 - return array("ok" => false, "message" => __( "Auth fail." ));
55 - }
92 + try {
93 + $queryParams = $request->get_json_params();
94 + if (empty($queryParams)) throw new \Exception('Query is empty');
56 95
57 - $settings = apply_filters('stateless::get_settings', array());
96 + $bucketName = isset($queryParams['bucket_name']) ? $queryParams['bucket_name'] : null;
97 + $privateKeyData = isset($queryParams['private_key_data']) ? $queryParams['private_key_data'] : null;
58 98
59 - return array(
60 - "ok" => true,
61 - "message" => "getSettings endpoint.",
62 - "settings" => $settings
63 - );
99 + if (!$bucketName || !$privateKeyData) {
100 + throw new \Exception('bucket_name and private_key_data are required');
101 + }
64 102
65 - }
103 + if ($privateKeyData) {
104 + $privateKeyData = base64_decode($privateKeyData);
105 + }
66 106
67 - /**
68 - * Get media library Endpoint.
69 - *
70 - * @param $request
71 - * @return array
72 - */
73 - static public function getMediaLibrary( $request ) {
107 + switch (self::$tokenData->is_network) {
108 + case true:
109 + if (!user_can(self::$tokenData->user_id, 'manage_network_options')) {
110 + return new \WP_Error('not_allowed', 'Sorry, you are not allowed to perform this action', ['status' => 403]);
111 + }
112 + /**
113 + * If Google App Engine detected - set Stateless mode
114 + * and Google App Engine compatibility by default
115 + */
116 + if ($is_gae || !$is_upload_dir_writable) {
117 + update_site_option('sm_mode', 'stateless');
74 118
75 - if( !self::authRequest( $request ) ) {
76 - return array("ok" => false, "message" => __( "Auth fail." ));
77 - }
119 + $modules = get_site_option('stateless-modules', array());
120 + if ($is_gae && empty($modules['google-app-engine']) || $modules['google-app-engine'] != 'true') {
121 + $modules['google-app-engine'] = 'true';
122 + update_site_option('stateless-modules', $modules);
123 + }
124 + } elseif (get_site_option('sm_mode', 'disabled') == 'disabled') {
125 + update_site_option('sm_mode', 'ephemeral');
126 + }
127 + update_site_option('sm_bucket', $bucketName);
128 + update_site_option('sm_key_json', $privateKeyData);
129 + break;
78 130
79 - if( !self::switchBlog( $request ) ){
80 - return array("ok" => false, "message" => __( "Missed blog param." ));
81 - }
131 + case false:
132 + if (!user_can(self::$tokenData->user_id, 'manage_options')) {
133 + return new \WP_Error('not_allowed', 'Sorry, you are not allowed to perform this action', ['status' => 403]);
134 + }
135 + /**
136 + * If Google App Engine detected - set Stateless mode
137 + * and Google App Engine compatibility by default
138 + */
139 + if ($is_gae || !$is_upload_dir_writable) {
140 + update_option('sm_mode', 'stateless');
82 141
83 - $query_images_args = array(
84 - 'post_type' => 'attachment',
85 - 'post_mime_type' =>'image',
86 - 'post_status' => 'inherit',
87 - 'posts_per_page' => -1,
88 - );
142 + $modules = get_option('stateless-modules', array());
143 + if ($is_gae && empty($modules['google-app-engine']) || $modules['google-app-engine'] != 'true') {
144 + $modules['google-app-engine'] = 'true';
145 + update_option('stateless-modules', $modules);
146 + }
147 + } elseif (get_option('sm_mode', 'disabled') == 'disabled') {
148 + update_option('sm_mode', 'ephemeral');
149 + }
150 + update_option('sm_bucket', $bucketName);
151 + update_option('sm_key_json', $privateKeyData);
152 + break;
153 + }
89 154
90 - $query_images = new \WP_Query( $query_images_args );
91 - $media = array();
92 - foreach ( $query_images->posts as $image) {
93 - $media[] = self::mediaMapping($image);
155 + return new \WP_REST_Response(array(
156 + 'ok' => true,
157 + 'message' => 'Settings updated successfully'
158 + ));
159 + } catch (\Throwable $e) {
160 + return new \WP_Error('internal_server_error', $e->getMessage(), ['status' => 500]);
94 161 }
162 + }
95 163
96 - return array(
97 - "ok" => true,
98 - "message" => "getMediaLibrary endpoint.",
99 - "mediaLibrary" => $media
100 - );
164 + /**
165 + * Get all available processes with their states
166 + *
167 + * @return \WP_REST_Response|\WP_Error
168 + */
169 + static public function syncGetProcesses() {
170 + try {
171 + if (!user_can(self::$tokenData->user_id, 'manage_options')) {
172 + return new \WP_Error('not_allowed', 'Sorry, you are not allowed to perform this action', ['status' => 403]);
173 + }
101 174
175 + return new \WP_REST_Response(array(
176 + 'ok' => true,
177 + 'data' => Utility::get_available_sync_classes()
178 + ));
179 + } catch (\Throwable $e) {
180 + return new \WP_Error('internal_server_error', $e->getMessage(), ['status' => 500]);
181 + }
102 182 }
103 183
104 184 /**
105 - * Get media item Endpoint.
106 - *
107 - * @param $request
108 - * @return array
185 + * Get a single process by id (base64 encoded class name)
186 + *
187 + * @param \WP_REST_Request $request
188 + * @return \WP_Error|\WP_REST_Response
109 189 */
110 - static public function getMediaItem( $request ) {
190 + static public function syncGetProcess(\WP_REST_Request $request) {
191 + try {
192 + if (!user_can(self::$tokenData->user_id, 'manage_options')) {
193 + return new \WP_Error('not_allowed', 'Sorry, you are not allowed to perform this action', ['status' => 403]);
194 + }
111 195
112 - if( !self::authRequest( $request ) ) {
113 - return array("ok" => false, "message" => __( "Auth fail." ));
114 - }
196 + $id = base64_decode($request->get_param('id'));
197 + if (!class_exists($id)) {
198 + throw new \Exception(sprintf('Could not get process by id %s', $id));
199 + }
115 200
116 - if( !self::switchBlog( $request ) ){
117 - return array("ok" => false, "message" => __( "Missed blog param." ));
118 - }
201 + $syncClasses = Utility::get_available_sync_classes();
119 202
120 - $attachment_id = $request->get_param('attachment_id');
121 - if(!$attachment_id){
122 - return array("ok" => false, "message" => __( "Missing attachment id." ));
203 + if (!array_key_exists($id, $syncClasses)) {
204 + throw new \Exception(sprintf('Could not get process by id %s', $id));
205 + }
206 +
207 + return new \WP_REST_Response(array(
208 + 'ok' => true,
209 + 'data' => $syncClasses[$id]
210 + ));
211 + } catch (\Throwable $e) {
212 + return new \WP_Error('internal_server_error', $e->getMessage(), ['status' => 500]);
123 213 }
214 + }
124 215
125 - $attachment = get_post($attachment_id);
216 + /**
217 + * Run sync by processing class id
218 + *
219 + * @param \WP_REST_Request $request
220 + * @return \WP_Error|\WP_REST_Response
221 + */
222 + static public function syncRun(\WP_REST_Request $request) {
223 + try {
224 + if (!user_can(self::$tokenData->user_id, 'manage_options')) {
225 + return new \WP_Error('not_allowed', 'Sorry, you are not allowed to perform this action', ['status' => 403]);
226 + }
126 227
127 - if(!$attachment){
128 - return array("ok" => false, "message" => __( "Wrong attachment id." ));
129 - }
228 + $params = wp_parse_args($request->get_params(), [
229 + 'id' => null,
230 + 'limit' => null,
231 + 'order' => null,
232 + ]);
130 233
131 - $item = self::mediaMapping($attachment);
234 + if (empty($params['id']) || !class_exists($params['id'])) {
235 + throw new \Exception(sprintf('Processing class not found: %s', $params['id']));
236 + }
132 237
133 - return array(
134 - "ok" => true,
135 - "message" => "getMediaItem endpoint.",
136 - "mediaItem" => $item
137 - );
238 + $processingClass = $params['id'];
138 239
240 + return new \WP_REST_Response(array(
241 + 'ok' => $processingClass::instance()->start($params)
242 + ));
243 + } catch (\Throwable $e) {
244 + return new \WP_Error('internal_server_error', $e->getMessage(), ['status' => 500]);
245 + }
139 246 }
140 247
141 248 /**
142 - * Handle Auth.
143 - *
144 - * @param $request
145 - * @return bool
249 + * Stop sync by processing class id
250 + *
251 + * @param \WP_REST_Request $request
252 + * @return \WP_Error|\WP_REST_Response
146 253 */
147 - static public function authRequest( $request = false ) {
254 + static public function syncStop(\WP_REST_Request $request) {
255 + try {
256 + if (!user_can(self::$tokenData->user_id, 'manage_options')) {
257 + return new \WP_Error('not_allowed', 'Sorry, you are not allowed to perform this action', ['status' => 403]);
258 + }
148 259
149 - if( !$request ) {
150 - return false;
151 - }
260 + $params = wp_parse_args($request->get_params(), [
261 + 'id' => null
262 + ]);
152 263
153 - if( !$request->get_param('key') ) {
154 - return false;
155 - }
264 + if (empty($params['id']) || !class_exists($params['id'])) {
265 + throw new \Exception(sprintf('Processing class not found: %s', $params['id']));
266 + }
156 267
157 - $settings = apply_filters('stateless::get_settings', array());
268 + $processingClass = $params['id'];
158 269
159 - if( !$settings[ 'api_key' ] ) {
160 - return false;
270 + return new \WP_REST_Response(array(
271 + 'ok' => $processingClass::instance()->stop()
272 + ));
273 + } catch (\Throwable $e) {
274 + return new \WP_Error('internal_server_error', $e->getMessage(), ['status' => 500]);
161 275 }
276 + }
162 277
163 - if( $request->get_param('key') !== $settings[ 'api_key' ] ) {
164 - return false;
278 + /**
279 + * Batch Status Endpoint.
280 + *
281 + * @param \WP_REST_Request $request
282 + * @return \WP_REST_Response
283 + */
284 + static public function batchState(\WP_REST_Request $request) {
285 + try {
286 + return new \WP_REST_Response(array(
287 + 'ok' => true,
288 + 'data' => apply_filters('wp_stateless_batch_state', [], $request->get_params()),
289 + ));
290 + } catch (\Throwable $e) {
291 + return new \WP_Error('internal_server_error', $e->getMessage(), ['status' => 500]);
165 292 }
166 -
167 - return true;
168 -
169 -
170 293 }
171 294
172 295 /**
173 - * Check blog param and switch to requested blog
174 - *
175 - * @param bool $request
176 - * @return bool
296 + * Batch action (start/pause/resume)
297 + *
298 + * @param \WP_REST_Request $request
299 + * @return \WP_Error|\WP_REST_Response
177 300 */
178 - static public function switchBlog( $request = false ){
301 + static public function batchAction(\WP_REST_Request $request) {
302 + try {
303 + if (!user_can(self::$tokenData->user_id, 'manage_options')) {
304 + return new \WP_Error('not_allowed', 'Sorry, you are not allowed to perform this action', ['status' => 403]);
305 + }
179 306
180 - if(!is_multisite()){
181 - return true;
182 - }
307 + wp_set_current_user(self::$tokenData->user_id);
183 308
184 - if( !$request ) {
185 - return false;
186 - }
309 + $params = wp_parse_args($request->get_params(), [
310 + 'action' => '',
311 + ]);
187 312
188 - $blog_id = $request->get_param('blog');
189 - if( !$blog_id ) {
190 - return false;
191 - }
313 + if ( empty($params['action']) ) {
314 + throw new \Exception('Batch action not set');
315 + }
192 316
193 - $current_blog_id = get_current_blog_id();
194 - if($current_blog_id == $blog_id){
195 - return true;
196 - }
317 + $action = $params['action'];
197 318
198 - return switch_to_blog( $blog_id );
199 - }
319 + $data = apply_filters("wp_stateless_batch_action_$action", [], $params);
200 320
201 - /**
202 - * Applying mapping for media item
203 - *
204 - * @param $image
205 - * @return array
206 - */
207 - static private function mediaMapping($image){
321 + $data = array_merge(
322 + $data,
323 + apply_filters("wp_stateless_batch_state", [], ['force_migrations' => true]),
324 + );
208 325
209 - if(!$image){
210 - return array();
326 + return new \WP_REST_Response(array(
327 + 'data' => $data,
328 + ));
329 + } catch (\Throwable $e) {
330 + return new \WP_Error('internal_server_error', $e->getMessage(), ['status' => 500]);
211 331 }
212 -
213 - return array(
214 - 'attachment_id' => $image->ID,
215 - 'date_create' => $image->post_date,
216 - 'parent' => $image->post_parent,
217 - 'link' => wp_get_attachment_url($image->ID),
218 - 'title' => $image->post_title,
219 - 'description' => $image->post_content,
220 - 'thumbnail' => wp_get_attachment_image_src($image->ID)[0]
221 - );
222 332 }
223 -
224 333 }
225 -
226 334 }
227 -
228 335 }