382 lines
12 KiB
PHP
382 lines
12 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 shared secret
|
|
*
|
|
* @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 shared secret from settings
|
|
$shared_secret = igny8_get_webhook_secret();
|
|
|
|
if (empty($shared_secret)) {
|
|
return new WP_Error(
|
|
'rest_forbidden',
|
|
__('Webhook secret not configured', 'igny8-bridge'),
|
|
array('status' => 403)
|
|
);
|
|
}
|
|
|
|
// Check X-IGNY8-Signature header
|
|
$signature = $request->get_header('X-IGNY8-Signature');
|
|
|
|
if (empty($signature)) {
|
|
return new WP_Error(
|
|
'rest_forbidden',
|
|
__('Missing webhook signature', 'igny8-bridge'),
|
|
array('status' => 401)
|
|
);
|
|
}
|
|
|
|
// Verify signature
|
|
$body = $request->get_body();
|
|
$expected_signature = hash_hmac('sha256', $body, $shared_secret);
|
|
|
|
if (!hash_equals($expected_signature, $signature)) {
|
|
igny8_log_webhook_activity(array(
|
|
'event' => 'authentication_failed',
|
|
'ip' => $request->get_header('X-Forwarded-For') ?: $request->get_header('Remote-Addr'),
|
|
'error' => 'Invalid signature'
|
|
));
|
|
|
|
return new WP_Error(
|
|
'rest_forbidden',
|
|
__('Invalid webhook signature', '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();
|
|
|