'POST', 'callback' => 'suiko_rest_pair', 'permission_callback' => '__return_true' ) ); register_rest_route( $ns, '/ping', array( 'methods' => 'GET', 'callback' => 'suiko_rest_ping', 'permission_callback' => 'suiko_rest_auth' ) ); register_rest_route( $ns, '/articles', array( 'methods' => 'GET', 'callback' => 'suiko_rest_articles', 'permission_callback' => 'suiko_rest_auth' ) ); register_rest_route( $ns, '/articles/(?P\d+)', array( array( 'methods' => 'GET', 'callback' => 'suiko_rest_article', 'permission_callback' => 'suiko_rest_auth' ), array( 'methods' => 'PUT', 'callback' => 'suiko_rest_update', 'permission_callback' => 'suiko_rest_auth' ), ) ); } function suiko_rest_auth( $request ) { if ( suiko_key_matches( $request->get_header( 'x-suiko-key' ) ) ) { return true; } return new WP_Error( 'suiko_unauthorized', 'Missing or invalid Suiko key.', array( 'status' => 401 ) ); } /** Exchanges a pairing code for the permanent key. Body: code, site_id (Suiko's id for this site), organisation (name). */ function suiko_rest_pair( $request ) { $key = suiko_consume_pairing_code( $request->get_param( 'code' ) ); if ( ! $key ) { return new WP_Error( 'suiko_bad_code', 'Pairing code missing, expired or already used. Click "Connect to Suiko" again.', array( 'status' => 403 ) ); } $site_id = $request->get_param( 'site_id' ); $org = $request->get_param( 'organisation' ); if ( is_string( $site_id ) ) { update_option( 'suiko_site_id', sanitize_text_field( $site_id ), false ); } if ( is_string( $org ) ) { update_option( 'suiko_organisation', sanitize_text_field( $org ), false ); } return array( 'key' => $key, 'site' => suiko_site_info() ); } function suiko_rest_ping() { return suiko_site_info(); } /** Published articles, newest first. ?page, ?per_page (max 100), ?modified_after (ISO 8601), ?type (post|page). */ function suiko_rest_articles( $request ) { $per_page = min( 100, max( 1, (int) ( $request->get_param( 'per_page' ) ?: 50 ) ) ); $page = max( 1, (int) ( $request->get_param( 'page' ) ?: 1 ) ); $type = $request->get_param( 'type' ); $args = array( 'post_type' => $type && in_array( $type, suiko_post_types(), true ) ? $type : suiko_post_types(), 'post_status' => 'publish', 'posts_per_page' => $per_page, 'paged' => $page, 'orderby' => 'modified', 'order' => 'DESC', ); $after = $request->get_param( 'modified_after' ); if ( $after && strtotime( $after ) ) { $args['date_query'] = array( array( 'column' => 'post_modified_gmt', 'after' => gmdate( 'Y-m-d H:i:s', strtotime( $after ) ) ) ); } $q = new WP_Query( $args ); return array( 'articles' => array_map( 'suiko_article_summary', $q->posts ), 'page' => $page, 'pages' => (int) $q->max_num_pages, 'total' => (int) $q->found_posts, ); } function suiko_find_post( $request ) { $post = get_post( (int) $request['id'] ); if ( ! $post || ! in_array( $post->post_type, suiko_post_types(), true ) || 'publish' !== $post->post_status ) { return new WP_Error( 'suiko_not_found', 'No published article with this id.', array( 'status' => 404 ) ); } return $post; } function suiko_rest_article( $request ) { $post = suiko_find_post( $request ); return is_wp_error( $post ) ? $post : suiko_article_full( $post ); } function suiko_rest_update( $request ) { $post = suiko_find_post( $request ); if ( is_wp_error( $post ) ) { return $post; } $input = $request->get_json_params(); if ( ! is_array( $input ) ) { return new WP_Error( 'suiko_bad_request', 'A JSON body is required.', array( 'status' => 400 ) ); } return suiko_apply_update( $post, $input ); }