array( 'name' => __( 'REST Logs', 'advanced-rest-api-devtools' ), 'singular_name' => __( 'REST Log', 'advanced-rest-api-devtools' ), ), 'public' => false, 'show_ui' => false, 'show_in_rest' => false, 'supports' => array( 'title', 'custom-fields' ), 'capability_type' => 'post', 'map_meta_cap' => true, ) ); } /** * Register AJAX handlers. */ public static function register_ajax() { add_action( 'wp_ajax_aradt_logs', array( __CLASS__, 'ajax_get_logs' ) ); add_action( 'wp_ajax_aradt_purge_logs', array( __CLASS__, 'ajax_purge_logs' ) ); add_action( 'wp_ajax_aradt_toggle_logging', array( __CLASS__, 'ajax_toggle_logging' ) ); } /** * Register hooks for logging and cron. */ public static function register_hooks() { add_action( 'rest_request_after_callbacks', array( __CLASS__, 'maybe_log_request' ), 999, 3 ); add_action( 'aradt_cleanup_logs', array( __CLASS__, 'cron_cleanup' ) ); } /** * Maybe log a REST request (if logging is enabled). * * @param \WP_REST_Response|\WP_Error $response Response object. * @param array $handler Handler info. * @param \WP_REST_Request $request Request object. * @return \WP_REST_Response|\WP_Error Passthrough. */ public static function maybe_log_request( $response, $handler, $request ) { $settings = get_option( 'aradt_settings', array() ); if ( empty( $settings['logging_enabled'] ) ) { return $response; } // Don't log our own AJAX calls. $route = $request->get_route(); if ( str_contains( $route, 'aradt' ) ) { return $response; } $status = 0; $body = ''; if ( is_wp_error( $response ) ) { $status = 500; $body = wp_json_encode( $response->get_error_messages() ); } elseif ( $response instanceof \WP_REST_Response ) { $status = $response->get_status(); $body = wp_json_encode( $response->get_data() ); } $log_data = array( 'method' => $request->get_method(), 'route' => $route, 'status' => $status, 'user_id' => get_current_user_id(), 'ip' => isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '', 'body_size' => strlen( $body ), 'timestamp' => current_time( 'mysql' ), ); $post_id = wp_insert_post( array( 'post_type' => 'aradt_log', 'post_status' => 'publish', 'post_title' => sprintf( '%s %s [%d]', $log_data['method'], $log_data['route'], $log_data['status'] ), 'post_content' => wp_json_encode( $log_data ), ) ); if ( $post_id && ! is_wp_error( $post_id ) ) { foreach ( $log_data as $key => $value ) { update_post_meta( $post_id, '_aradt_log_' . $key, $value ); } } return $response; } /** * AJAX: Get logs with filters. */ public static function ajax_get_logs() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $page = isset( $_POST['page'] ) ? max( 1, absint( $_POST['page'] ) ) : 1; $per_page = isset( $_POST['per_page'] ) ? min( 100, max( 10, absint( $_POST['per_page'] ) ) ) : 25; $route = isset( $_POST['route_filter'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['route_filter'] ) ) : ''; $method = isset( $_POST['method_filter'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['method_filter'] ) ) : ''; $status = isset( $_POST['status_filter'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['status_filter'] ) ) : ''; $meta_query = array(); if ( ! empty( $route ) ) { $meta_query[] = array( 'key' => '_aradt_log_route', 'value' => $route, 'compare' => 'LIKE', ); } if ( ! empty( $method ) ) { $meta_query[] = array( 'key' => '_aradt_log_method', 'value' => strtoupper( $method ), ); } if ( ! empty( $status ) ) { $meta_query[] = array( 'key' => '_aradt_log_status', 'value' => $status, ); } $args = array( 'post_type' => 'aradt_log', 'posts_per_page' => $per_page, 'paged' => $page, 'orderby' => 'date', 'order' => 'DESC', ); if ( ! empty( $meta_query ) ) { $args['meta_query'] = $meta_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query } $query = new WP_Query( $args ); $logs = array(); foreach ( $query->posts as $post ) { $data = json_decode( $post->post_content, true ); if ( ! $data ) { $data = array(); } $data['id'] = $post->ID; $data['date'] = $post->post_date; $logs[] = $data; } wp_send_json_success( array( 'logs' => $logs, 'total' => $query->found_posts, 'pages' => $query->max_num_pages, 'page' => $page, 'is_enabled' => self::is_logging_enabled(), ) ); } /** * AJAX: Purge old logs. */ public static function ajax_purge_logs() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $count = self::purge_logs(); wp_send_json_success( array( /* translators: %d: Number of log entries deleted */ 'message' => sprintf( esc_html__( 'Purged %d log entries.', 'advanced-rest-api-devtools' ), $count ), 'count' => $count, ) ); } /** * AJAX: Toggle logging on/off. */ public static function ajax_toggle_logging() { check_ajax_referer( 'aradt_nonce', 'nonce' ); Aradt_Utils::verify_ajax_request(); $settings = get_option( 'aradt_settings', array() ); $settings['logging_enabled'] = ! $settings['logging_enabled']; update_option( 'aradt_settings', $settings ); wp_send_json_success( array( 'enabled' => $settings['logging_enabled'], 'message' => $settings['logging_enabled'] ? esc_html__( 'Logging enabled.', 'advanced-rest-api-devtools' ) : esc_html__( 'Logging disabled.', 'advanced-rest-api-devtools' ), ) ); } /** * Purge logs older than retention days. * * @param int|null $days Days to retain. Null = use settings. * @return int Number of deleted posts. */ public static function purge_logs( $days = null ) { if ( null === $days ) { $settings = get_option( 'aradt_settings', array() ); $days = isset( $settings['log_retention_days'] ) ? (int) $settings['log_retention_days'] : 30; } $cutoff = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days} days" ) ); $posts = get_posts( array( 'post_type' => 'aradt_log', 'posts_per_page' => 500, 'post_status' => 'any', 'date_query' => array( array( 'before' => $cutoff ), ), 'fields' => 'ids', ) ); $count = 0; foreach ( $posts as $post_id ) { wp_delete_post( $post_id, true ); $count++; } return $count; } /** * Cron job: cleanup old logs. */ public static function cron_cleanup() { self::purge_logs(); } /** * Check if logging is currently enabled. * * @return bool */ public static function is_logging_enabled() { $settings = get_option( 'aradt_settings', array() ); return ! empty( $settings['logging_enabled'] ); } }