settings = $settings; $this->engine = $engine; } /** * Registers REST API hooks for the Site Sync plugin. * * @return void */ public function hooks(): void { add_action( 'rest_api_init', array( $this, 'register_routes' ) ); } /** * Registers all REST API routes for the Site Sync plugin. * * @return void */ public function register_routes(): void { register_rest_route( self::ROUTE_NAMESPACE, '/handshake', array( 'methods' => 'GET', 'callback' => array( $this, 'handshake' ), 'permission_callback' => '__return_true', ) ); register_rest_route( self::ROUTE_NAMESPACE, '/inbox', array( 'methods' => 'POST', 'callback' => array( $this, 'receive_inbox' ), 'permission_callback' => array( $this, 'check_auth' ), ) ); register_rest_route( self::ROUTE_NAMESPACE, '/outbox', array( 'methods' => 'GET', 'callback' => array( $this, 'send_outbox' ), 'permission_callback' => array( $this, 'check_auth' ), ) ); register_rest_route( self::ROUTE_NAMESPACE, '/media', array( 'methods' => 'POST', 'callback' => array( $this, 'handle_media' ), 'permission_callback' => array( $this, 'check_auth' ), 'args' => array( 'external_id' => array( 'required' => true, 'type' => 'string', ), 'mime_type' => array( 'required' => true, 'type' => 'string', ), 'filename' => array( 'required' => false, 'type' => 'string', ), ), ) ); register_rest_route( self::ROUTE_NAMESPACE, '/media', array( 'methods' => 'GET', 'callback' => array( $this, 'send_media' ), 'permission_callback' => array( $this, 'check_auth' ), 'args' => array( 'external_id' => array( 'required' => true, 'type' => 'string', ), ), ) ); } /** * Handles the handshake endpoint for the Site Sync plugin. * * Returns basic site and plugin information for synchronization. * * @return WP_REST_Response */ public function handshake() { $settings = $this->settings->ensure_defaults(); return new WP_REST_Response( array( 'site_uuid' => $settings['site_uuid'], 'schema' => 'v1', 'site_url' => home_url(), 'version' => SITE_SYNC_VERSION, 'capabilities' => array( 'content' => true, 'taxonomies' => true, 'media' => true, ), ) ); } /** * Handles the inbox endpoint for receiving synchronization payloads. * * @param WP_REST_Request $request The REST request object containing the payload. * @return WP_REST_Response The response after processing the inbox payload. */ public function receive_inbox( WP_REST_Request $request ) { $payload = $request->get_json_params() ? $request->get_json_params() : array(); $result = $this->engine->handle_inbox( $payload ); return new WP_REST_Response( $result ); } /** * Handles the outbox endpoint for sending synchronization payloads. * * @return WP_REST_Response The response containing the outbox payload. */ public function send_outbox() { $result = $this->engine->provide_outbox( true ); $this->engine->commit_outbox( $result['cursor'], $result['meta']['counts'] ?? array() ); if ( isset( $result['meta'] ) ) { unset( $result['meta'] ); } return new WP_REST_Response( $result ); } /** * Handles the media endpoint for receiving and storing media files. * * @param WP_REST_Request $request The REST request object containing media data. * @return WP_REST_Response|WP_Error The response after processing the media upload or WP_Error on failure. */ public function handle_media( WP_REST_Request $request ) { $result = $this->engine->receive_media_stream( $request ); if ( is_wp_error( $result ) ) { return $result; } return new WP_REST_Response( array( 'status' => 'ok', 'stored' => $result, ) ); } /** * Handles the media endpoint for sending media files. * * @param WP_REST_Request $request The REST request object containing the external_id parameter. * @return WP_REST_Response|WP_Error The response containing the media file or WP_Error on failure. */ public function send_media( WP_REST_Request $request ) { $externalId = $request->get_param( 'external_id' ); $media = $this->engine->get_media_file( $externalId ); if ( is_wp_error( $media ) ) { return $media; } $response = new WP_REST_Response( $media['contents'] ); $response->set_status( 200 ); $response->header( 'Content-Type', $media['mime'] ); $response->header( 'Content-Disposition', 'attachment; filename="' . $media['filename'] . '"' ); $response->header( 'X-Site-Sync-Checksum', $media['checksum'] ); return $response; } /** * Checks authentication for REST API requests. * * @param WP_REST_Request $request The REST request object. * @return true|WP_Error True if authentication passes, WP_Error otherwise. */ public function check_auth( WP_REST_Request $request ) { $result = Auth::verify( $request, $this->settings ); if ( is_wp_error( $result ) ) { return $result; } return true; } }