Add Site Metadata Endpoint and API Key Management
- Introduced a new Site Metadata endpoint (`GET /wp-json/igny8/v1/site-metadata/`) for retrieving available post types and taxonomies, including counts. - Added API key input in the admin settings for authentication, with secure storage and revocation functionality. - Implemented a toggle for enabling/disabling two-way sync operations. - Updated documentation to reflect new features and usage examples. - Enhanced permission checks for REST API calls to ensure secure access.
This commit is contained in:
@@ -69,6 +69,14 @@ class Igny8RestAPI {
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
// Site metadata - post types, taxonomies and counts (unified response format)
|
||||
register_rest_route('igny8/v1', '/site-metadata/', array(
|
||||
'methods' => 'GET',
|
||||
// We perform permission checks inside callback to ensure unified response format
|
||||
'callback' => array($this, 'get_site_metadata'),
|
||||
'permission_callback' => '__return_true',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,18 +86,22 @@ class Igny8RestAPI {
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function check_permission($request) {
|
||||
// Check if connection is enabled
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden',
|
||||
__('IGNY8 connection is disabled', 'igny8-bridge'),
|
||||
array('status' => 403)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if authenticated with IGNY8
|
||||
// Do NOT block endpoints when the plugin connection is disabled.
|
||||
// The plugin-side "Enable Sync Operations" option should only stop background sync actions,
|
||||
// but REST discovery endpoints should remain callable. Authentication is still required.
|
||||
|
||||
// Check if authenticated with IGNY8 via stored token OR X-IGNY8-API-KEY header
|
||||
$api = new Igny8API();
|
||||
|
||||
|
||||
// Accept explicit X-IGNY8-API-KEY header for incoming requests
|
||||
$header_api_key = $request->get_header('x-igny8-api-key');
|
||||
if ($header_api_key) {
|
||||
$stored_api_key = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_api_key') : get_option('igny8_api_key');
|
||||
if ($stored_api_key && hash_equals($stored_api_key, $header_api_key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$api->is_authenticated()) {
|
||||
return new WP_Error(
|
||||
'rest_forbidden',
|
||||
@@ -287,6 +299,126 @@ class Igny8RestAPI {
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: generate a request_id (UUIDv4 if available)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generate_request_id() {
|
||||
if (function_exists('wp_generate_uuid4')) {
|
||||
return wp_generate_uuid4();
|
||||
}
|
||||
|
||||
// Fallback: uniqid with more entropy
|
||||
return uniqid('', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Build unified API response and return WP_REST_Response
|
||||
*
|
||||
* @param bool $success
|
||||
* @param mixed $data
|
||||
* @param string|null $message
|
||||
* @param string|null $error
|
||||
* @param array|null $errors
|
||||
* @param int $status
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
private function build_unified_response($success, $data = null, $message = null, $error = null, $errors = null, $status = 200) {
|
||||
$payload = array(
|
||||
'success' => (bool) $success,
|
||||
'data' => $data,
|
||||
'message' => $message,
|
||||
'request_id' => $this->generate_request_id()
|
||||
);
|
||||
|
||||
if (!$success) {
|
||||
$payload['error'] = $error ?: 'Unknown error';
|
||||
if (!empty($errors)) {
|
||||
$payload['errors'] = $errors;
|
||||
}
|
||||
}
|
||||
|
||||
$response = rest_ensure_response($payload);
|
||||
$response->set_status($status);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /site-metadata/ - returns post types, taxonomies and counts in unified format
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function get_site_metadata($request) {
|
||||
// Use transient cache to avoid expensive counts on large sites
|
||||
$cache_key = 'igny8_site_metadata_v1';
|
||||
$cached = get_transient($cache_key);
|
||||
if ($cached !== false) {
|
||||
return $this->build_unified_response(true, $cached, 'Site metadata (cached)', null, null, 200);
|
||||
}
|
||||
|
||||
// Perform permission check and return unified error if not allowed
|
||||
$perm = $this->check_permission($request);
|
||||
if (is_wp_error($perm)) {
|
||||
$status = 403;
|
||||
$error_data = $perm->get_error_data();
|
||||
if (is_array($error_data) && isset($error_data['status'])) {
|
||||
$status = intval($error_data['status']);
|
||||
}
|
||||
return $this->build_unified_response(false, null, null, $perm->get_error_message(), null, $status);
|
||||
}
|
||||
|
||||
// Collect post types (public)
|
||||
$post_types_objects = get_post_types(array('public' => true), 'objects');
|
||||
$post_types = array();
|
||||
foreach ($post_types_objects as $slug => $obj) {
|
||||
// Get total count across statuses
|
||||
$count_obj = wp_count_posts($slug);
|
||||
$total = 0;
|
||||
if (is_object($count_obj)) {
|
||||
foreach (get_object_vars($count_obj) as $val) {
|
||||
$total += intval($val);
|
||||
}
|
||||
}
|
||||
$post_types[$slug] = array(
|
||||
'label' => $obj->labels->singular_name ?? $obj->label,
|
||||
'count' => $total
|
||||
);
|
||||
}
|
||||
|
||||
// Collect taxonomies (public)
|
||||
$taxonomy_objects = get_taxonomies(array('public' => true), 'objects');
|
||||
$taxonomies = array();
|
||||
foreach ($taxonomy_objects as $slug => $obj) {
|
||||
// Use wp_count_terms when available
|
||||
$term_count = 0;
|
||||
if (function_exists('wp_count_terms')) {
|
||||
$term_count = intval(wp_count_terms($slug));
|
||||
} else {
|
||||
$terms = get_terms(array('taxonomy' => $slug, 'hide_empty' => false, 'fields' => 'ids'));
|
||||
$term_count = is_array($terms) ? count($terms) : 0;
|
||||
}
|
||||
|
||||
$taxonomies[$slug] = array(
|
||||
'label' => $obj->labels->name ?? $obj->label,
|
||||
'count' => $term_count
|
||||
);
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'post_types' => $post_types,
|
||||
'taxonomies' => $taxonomies,
|
||||
'generated_at' => time(),
|
||||
'plugin_connection_enabled' => (bool) igny8_is_connection_enabled(),
|
||||
'two_way_sync_enabled' => (bool) get_option('igny8_enable_two_way_sync', 1)
|
||||
);
|
||||
// Cache for 5 minutes
|
||||
set_transient($cache_key, $data, 300);
|
||||
|
||||
return $this->build_unified_response(true, $data, 'Site metadata retrieved', null, null, 200);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize REST API
|
||||
|
||||
Reference in New Issue
Block a user