Files
hermes-cpanel-agent/includes/rest-controller.php
T

345 lines
12 KiB
PHP

<?php
namespace HermesAiBridge;
defined('ABSPATH') or die();
class RestController {
protected $namespace = 'hermes/v1';
public function register_routes() {
// Health
register_rest_route($this->namespace, '/health', [
'methods' => 'GET',
'callback' => [$this, 'health'],
'permission_callback' => '__return_true',
]);
// Content generation
register_rest_route($this->namespace, '/content/generate', [
'methods' => 'POST',
'callback' => [$this, 'generate_content'],
'permission_callback' => [$this, 'check_auth'],
'args' => [
'prompt' => ['required' => true, 'type' => 'string'],
'post_type' => ['default' => 'post', 'type' => 'string'],
'status' => ['default' => 'draft', 'type' => 'string'],
'title' => ['type' => 'string'],
],
]);
// Content schedule
register_rest_route($this->namespace, '/content/schedule', [
'methods' => 'POST',
'callback' => [$this, 'schedule_content'],
'permission_callback' => [$this, 'check_auth'],
'args' => [
'prompt' => ['required' => true, 'type' => 'string'],
'scheduled_date' => ['required' => true, 'type' => 'string'],
'post_type' => ['default' => 'post', 'type' => 'string'],
'title' => ['type' => 'string'],
],
]);
// Posts CRUD
register_rest_route($this->namespace, '/posts', [
'methods' => 'GET',
'callback' => [$this, 'list_posts'],
'permission_callback' => [$this, 'check_auth'],
]);
register_rest_route($this->namespace, '/posts/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => [$this, 'get_post'],
'permission_callback' => [$this, 'check_auth'],
]);
register_rest_route($this->namespace, '/posts/(?P<id>\d+)', [
'methods' => ['PUT', 'PATCH'],
'callback' => [$this, 'update_post'],
'permission_callback' => [$this, 'check_auth'],
]);
register_rest_route($this->namespace, '/posts/(?P<id>\d+)', [
'methods' => 'DELETE',
'callback' => [$this, 'delete_post'],
'permission_callback' => [$this, 'check_auth'],
]);
// Users
register_rest_route($this->namespace, '/users', [
'methods' => 'GET',
'callback' => [$this, 'list_users'],
'permission_callback' => [$this, 'check_auth'],
]);
register_rest_route($this->namespace, '/users/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => [$this, 'get_user'],
'permission_callback' => [$this, 'check_auth'],
]);
// Settings
register_rest_route($this->namespace, '/settings', [
'methods' => 'GET',
'callback' => [$this, 'get_settings'],
'permission_callback' => [$this, 'check_auth'],
]);
register_rest_route($this->namespace, '/settings', [
'methods' => 'PUT',
'callback' => [$this, 'update_settings'],
'permission_callback' => [$this, 'check_auth'],
]);
// Schedules
register_rest_route($this->namespace, '/schedules', [
'methods' => 'GET',
'callback' => [$this, 'list_schedules'],
'permission_callback' => [$this, 'check_auth'],
]);
// Logs
register_rest_route($this->namespace, '/logs', [
'methods' => 'GET',
'callback' => [$this, 'get_logs'],
'permission_callback' => [$this, 'check_auth'],
'args' => [
'level' => ['type' => 'string'],
'limit' => ['default' => 50, 'type' => 'integer'],
'offset' => ['default' => 0, 'type' => 'integer'],
],
]);
}
public function check_auth($request) {
$key = $request->get_header('X-API-Key');
if (!$key) {
// Also check query param for webhook compatibility
$key = $request->get_param('api_key');
}
$stored = get_option('hermes_api_key');
return $stored && hash_equals($stored, $key);
}
public function health() {
return [
'status' => 'ok',
'version' => HERMES_AI_BRIDGE_VERSION,
'wordpress' => get_bloginfo('version'),
'php' => PHP_VERSION,
'site' => get_bloginfo('url'),
];
}
public function generate_content($request) {
$prompt = $request->get_param('prompt');
$post_type = $request->get_param('post_type');
$status = $request->get_param('status');
$title = $request->get_param('title');
$ai = new AiProvider();
$content = $ai->generate($prompt, 'post');
if (is_wp_error($content)) {
return new \WP_REST_Response(['error' => $content->get_error_message()], 500);
}
$post_id = wp_insert_post([
'post_title' => $title ?: wp_trim_words($content, 10, ''),
'post_content' => $content,
'post_type' => $post_type,
'post_status' => $status,
]);
if (is_wp_error($post_id)) {
return new \WP_REST_Response(['error' => $post_id->get_error_message()], 500);
}
self::log('info', 'Content generated', ['post_id' => $post_id, 'prompt' => substr($prompt, 0, 100)]);
return [
'success' => true,
'post_id' => $post_id,
'edit_url' => get_edit_post_link($post_id, 'raw'),
];
}
public function schedule_content($request) {
$prompt = $request->get_param('prompt');
$scheduled_date = $request->get_param('scheduled_date');
$post_type = $request->get_param('post_type');
$title = $request->get_param('title');
$task_id = wp_insert_post([
'post_title' => '[AI Task] ' . ($title ?: 'Scheduled: ' . date('Y-m-d H:i')),
'post_content' => wp_json_encode([
'prompt' => $prompt,
'post_type' => $post_type,
'scheduled_date' => $scheduled_date,
]),
'post_type' => 'hermes_schedule',
'post_status' => 'publish',
'meta_input' => [
'_hermes_scheduled_date' => $scheduled_date,
'_hermes_task_type' => 'content_generation',
'_hermes_prompt' => $prompt,
],
]);
if (is_wp_error($task_id)) {
return new \WP_REST_Response(['error' => $task_id->get_error_message()], 500);
}
self::log('info', 'Content scheduled', ['task_id' => $task_id, 'date' => $scheduled_date]);
return [
'success' => true,
'task_id' => $task_id,
'scheduled_date' => $scheduled_date,
];
}
public function list_posts($request) {
$posts = get_posts([
'post_type' => 'post',
'posts_per_page' => $request->get_param('per_page') ?: 20,
'post_status' => 'any',
]);
return array_map(function($p) {
return [
'id' => $p->ID,
'title' => $p->post_title,
'status' => $p->post_status,
'date' => $p->post_date,
'modified' => $p->post_modified,
'permalink' => get_permalink($p->ID),
];
}, $posts);
}
public function get_post($request) {
$post = get_post($request->get_param('id'));
if (!$post) return new \WP_REST_Response(['error' => 'Post not found'], 404);
return [
'id' => $post->ID,
'title' => $post->post_title,
'content' => $post->post_content,
'excerpt' => $post->post_excerpt,
'status' => $post->post_status,
'date' => $post->post_date,
'modified' => $post->post_modified,
'permalink' => get_permalink($post->ID),
];
}
public function update_post($request) {
$post_id = $request->get_param('id');
$data = [];
if ($request->has_param('title')) $data['post_title'] = $request->get_param('title');
if ($request->has_param('content')) $data['post_content'] = $request->get_param('content');
if ($request->has_param('status')) $data['post_status'] = $request->get_param('status');
if ($request->has_param('excerpt')) $data['post_excerpt'] = $request->get_param('excerpt');
$data['ID'] = $post_id;
$result = wp_update_post($data, true);
if (is_wp_error($result)) {
return new \WP_REST_Response(['error' => $result->get_error_message()], 500);
}
return ['success' => true, 'post_id' => $result];
}
public function delete_post($request) {
$result = wp_delete_post($request->get_param('id'), true);
if (!$result) return new \WP_REST_Response(['error' => 'Delete failed'], 500);
return ['success' => true, 'deleted' => $result->ID];
}
public function list_users($request) {
$users = get_users(['fields' => ['ID', 'user_login', 'user_email', 'display_name', 'roles']]);
return $users;
}
public function get_user($request) {
$user = get_userdata($request->get_param('id'));
if (!$user) return new \WP_REST_Response(['error' => 'User not found'], 404);
return [
'id' => $user->ID,
'login' => $user->user_login,
'email' => $user->user_email,
'display_name' => $user->display_name,
'roles' => array_values($user->roles),
];
}
public function get_settings() {
return [
'site_name' => get_bloginfo('name'),
'site_url' => get_bloginfo('url'),
'admin_email' => get_bloginfo('admin_email'),
'language' => get_bloginfo('language'),
'timezone' => get_option('timezone_string'),
'date_format' => get_option('date_format'),
'time_format' => get_option('time_format'),
'posts_per_page' => get_option('posts_per_page'),
'ai_provider' => get_option('hermes_ai_provider'),
'ai_model' => get_option('hermes_ai_model'),
];
}
public function update_settings($request) {
$allowed = ['hermes_ai_provider', 'hermes_ai_model', 'hermes_ai_api_key', 'hermes_ai_base_url', 'hermes_ai_max_tokens', 'hermes_cron_interval'];
foreach ($request->get_params() as $key => $value) {
if (in_array($key, $allowed)) {
update_option($key, sanitize_text_field($value));
}
}
return ['success' => true];
}
public function list_schedules() {
$tasks = get_posts([
'post_type' => 'hermes_schedule',
'posts_per_page' => 50,
'post_status' => 'any',
]);
return array_map(function($t) {
return [
'id' => $t->ID,
'type' => get_post_meta($t->ID, '_hermes_task_type', true),
'scheduled_date' => get_post_meta($t->ID, '_hermes_scheduled_date', true),
'status' => $t->post_status,
'created' => $t->post_date,
];
}, $tasks);
}
public function get_logs($request) {
global $wpdb;
$where = '1=1';
if ($request->get_param('level')) {
$where .= $wpdb->prepare(' AND level = %s', $request->get_param('level'));
}
$limit = intval($request->get_param('limit'));
$offset = intval($request->get_param('offset'));
$results = $wpdb->get_results(
"SELECT id, level, message, context, created_at FROM {$wpdb->prefix}hermes_log
WHERE $where ORDER BY id DESC LIMIT $offset, $limit"
);
return array_map(function($r) {
$r->context = $r->context ? json_decode($r->context) : null;
return $r;
}, $results);
}
public static function log($level, $message, $context = []) {
global $wpdb;
$wpdb->insert(
$wpdb->prefix . 'hermes_log',
[
'level' => $level,
'message' => $message,
'context' => wp_json_encode($context),
'created_at' => current_time('mysql'),
]
);
}
}