Files
igny8/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-webhooks.php
2026-01-09 22:45:30 +00:00

393 lines
13 KiB
PHP

<?php
/**
* IGNY8 Webhooks Handler
*
* Handles incoming webhooks from IGNY8 SaaS
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Igny8Webhooks Class
*/
class Igny8Webhooks {
/**
* Constructor
*/
public function __construct() {
add_action('rest_api_init', array($this, 'register_webhook_routes'));
}
/**
* Register webhook REST routes
*/
public function register_webhook_routes() {
// Main webhook endpoint
register_rest_route('igny8/v1', '/event', array(
'methods' => 'POST',
'callback' => array($this, 'handle_webhook'),
'permission_callback' => array($this, 'verify_webhook_secret'),
'args' => array(
'event' => array(
'required' => true,
'type' => 'string',
'description' => 'Event type'
),
'data' => array(
'required' => true,
'type' => 'object',
'description' => 'Event data'
)
)
));
}
/**
* Verify webhook API key authentication
*
* @param WP_REST_Request $request Request object
* @return bool|WP_Error
*/
public function verify_webhook_secret($request) {
// First 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)
);
}
// Get API key from plugin settings
$stored_api_key = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_api_key') : get_option('igny8_api_key');
if (empty($stored_api_key)) {
return new WP_Error(
'rest_forbidden',
__('API key not configured', 'igny8-bridge'),
array('status' => 403)
);
}
// Check X-IGNY8-API-KEY header
$header_api_key = $request->get_header('X-IGNY8-API-KEY');
// Also check Authorization Bearer header
if (empty($header_api_key)) {
$auth_header = $request->get_header('Authorization');
if ($auth_header && strpos($auth_header, 'Bearer ') === 0) {
$header_api_key = substr($auth_header, 7);
}
}
if (empty($header_api_key)) {
igny8_log_webhook_activity(array(
'event' => 'authentication_failed',
'ip' => $request->get_header('X-Forwarded-For') ?: $request->get_header('Remote-Addr'),
'error' => 'Missing API key'
));
return new WP_Error(
'rest_forbidden',
__('Missing API key in request headers', 'igny8-bridge'),
array('status' => 401)
);
}
// Verify API key matches
if (!hash_equals($stored_api_key, $header_api_key)) {
igny8_log_webhook_activity(array(
'event' => 'authentication_failed',
'ip' => $request->get_header('X-Forwarded-For') ?: $request->get_header('Remote-Addr'),
'error' => 'Invalid API key'
));
return new WP_Error(
'rest_forbidden',
__('Invalid API key', 'igny8-bridge'),
array('status' => 401)
);
}
return true;
}
/**
* Handle incoming webhook
*
* @param WP_REST_Request $request Request object
* @return WP_REST_Response|WP_Error
*/
public function handle_webhook($request) {
// Double-check connection is enabled
if (!igny8_is_connection_enabled()) {
return new WP_Error(
'rest_forbidden',
__('IGNY8 connection is disabled', 'igny8-bridge'),
array('status' => 403)
);
}
$event = $request->get_param('event');
$data = $request->get_param('data');
if (empty($event) || empty($data)) {
return new WP_Error(
'rest_invalid_param',
__('Missing event or data parameter', 'igny8-bridge'),
array('status' => 400)
);
}
// Log webhook receipt
$log_id = igny8_log_webhook_activity(array(
'event' => $event,
'data' => $data,
'ip' => $request->get_header('X-Forwarded-For') ?: $request->get_header('Remote-Addr'),
'user_agent' => $request->get_header('User-Agent'),
'status' => 'received'
));
// Route to appropriate handler
$result = null;
switch ($event) {
case 'task_published':
case 'task_completed':
$result = $this->handle_task_published($data);
break;
case 'link_recommendation':
case 'insert_link':
$result = $this->handle_link_recommendation($data);
break;
case 'optimizer_request':
case 'optimizer_job_completed':
$result = $this->handle_optimizer_request($data);
break;
default:
$result = array(
'success' => false,
'error' => 'Unknown event type: ' . $event
);
}
// Update log with result
if ($log_id) {
igny8_update_webhook_log($log_id, array(
'status' => $result['success'] ? 'processed' : 'failed',
'response' => $result,
'processed_at' => current_time('mysql')
));
}
return rest_ensure_response($result);
}
/**
* Handle task published event
*
* @param array $data Event data
* @return array Result
*/
private function handle_task_published($data) {
if (!igny8_is_connection_enabled()) {
return array('success' => false, 'error' => 'Connection disabled');
}
if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('writer')) {
return array('success' => false, 'error' => 'Writer module disabled');
}
$task_id = $data['task_id'] ?? null;
if (!$task_id) {
return array('success' => false, 'error' => 'Missing task_id');
}
// Check if post already exists
$existing_posts = get_posts(array(
'meta_key' => '_igny8_task_id',
'meta_value' => $task_id,
'post_type' => 'any',
'posts_per_page' => 1
));
if (!empty($existing_posts)) {
// Post already exists, just update status if needed
$post_id = $existing_posts[0]->ID;
$status = $data['status'] ?? 'publish';
if ($status === 'publish' || $status === 'completed') {
wp_update_post(array(
'ID' => $post_id,
'post_status' => 'publish'
));
}
return array(
'success' => true,
'message' => 'Post updated',
'post_id' => $post_id
);
}
// Fetch full task data and create post
$api = new Igny8API();
$task_response = $api->get("/writer/tasks/{$task_id}/");
if (!$task_response['success']) {
return array(
'success' => false,
'error' => 'Failed to fetch task: ' . ($task_response['error'] ?? 'Unknown error')
);
}
$task = $task_response['data'];
$enabled_post_types = igny8_get_enabled_post_types();
$content_data = array(
'task_id' => $task['id'],
'title' => $task['title'] ?? 'Untitled',
'content' => $task['content'] ?? '',
'status' => $task['status'] ?? 'draft',
'cluster_id' => $task['cluster_id'] ?? null,
'sector_id' => $task['sector_id'] ?? null,
'keyword_ids' => $task['keyword_ids'] ?? array(),
'content_type' => $task['content_type'] ?? 'post',
'categories' => $task['categories'] ?? array(),
'tags' => $task['tags'] ?? array(),
'featured_image' => $task['featured_image'] ?? null,
'gallery_images' => $task['gallery_images'] ?? array(),
'meta_title' => $task['meta_title'] ?? null,
'meta_description' => $task['meta_description'] ?? null
);
$post_id = igny8_create_wordpress_post_from_task($content_data, $enabled_post_types);
if (is_wp_error($post_id)) {
return array(
'success' => false,
'error' => $post_id->get_error_message()
);
}
return array(
'success' => true,
'message' => 'Post created',
'post_id' => $post_id
);
}
/**
* Handle link recommendation event
*
* @param array $data Event data
* @return array Result
*/
private function handle_link_recommendation($data) {
if (!igny8_is_connection_enabled()) {
return array('success' => false, 'error' => 'Connection disabled');
}
if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('linker')) {
return array('success' => false, 'error' => 'Linker module disabled');
}
$post_id = $data['post_id'] ?? null;
$target_url = $data['target_url'] ?? null;
$anchor = $data['anchor'] ?? $data['anchor_text'] ?? null;
if (!$post_id || !$target_url || !$anchor) {
return array(
'success' => false,
'error' => 'Missing required parameters: post_id, target_url, anchor'
);
}
// Queue link insertion
$queued = igny8_queue_link_insertion(array(
'post_id' => intval($post_id),
'target_url' => esc_url_raw($target_url),
'anchor' => sanitize_text_field($anchor),
'source' => 'igny8_linker',
'priority' => $data['priority'] ?? 'normal',
'created_at' => current_time('mysql')
));
if ($queued) {
return array(
'success' => true,
'message' => 'Link queued for insertion',
'queue_id' => $queued
);
}
return array(
'success' => false,
'error' => 'Failed to queue link insertion'
);
}
/**
* Handle optimizer request event
*
* @param array $data Event data
* @return array Result
*/
private function handle_optimizer_request($data) {
if (!igny8_is_connection_enabled()) {
return array('success' => false, 'error' => 'Connection disabled');
}
if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('optimizer')) {
return array('success' => false, 'error' => 'Optimizer module disabled');
}
$post_id = $data['post_id'] ?? null;
$job_id = $data['job_id'] ?? null;
$status = $data['status'] ?? null;
$score_changes = $data['score_changes'] ?? null;
$recommendations = $data['recommendations'] ?? null;
if (!$post_id) {
return array('success' => false, 'error' => 'Missing post_id');
}
// Update optimizer status if job_id provided
if ($job_id) {
update_post_meta($post_id, '_igny8_optimizer_job_id', $job_id);
}
if ($status) {
update_post_meta($post_id, '_igny8_optimizer_status', $status);
}
if ($score_changes) {
update_post_meta($post_id, '_igny8_optimizer_score_changes', $score_changes);
}
if ($recommendations) {
update_post_meta($post_id, '_igny8_optimizer_recommendations', $recommendations);
}
return array(
'success' => true,
'message' => 'Optimizer data updated',
'post_id' => $post_id
);
}
}
// Initialize webhooks
new Igny8Webhooks();