345 lines
12 KiB
PHP
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'),
|
|
]
|
|
);
|
|
}
|
|
}
|