$job_key, 'job_type' => $type, 'status' => 'queued', 'progress_step' => __( 'Job queued for processing...', 'ad-fuz' ), 'payload' => wp_json_encode( $payload ), 'result' => null, 'error_message' => null, 'created_at' => $now, 'started_at' => null, 'completed_at' => null, 'expires_at' => $expires, ); try { $id = DB::get( TableName::ADFUZ_JOBS() )->insert( $jobData ); $jobData['id'] = $id; } catch ( \Throwable $e ) { // Fallback: Ensure table is created if missing and retry once \AdFuz\Database\Migrations\V1_3_1_CreateJobsTable::up(); $id = DB::get( TableName::ADFUZ_JOBS() )->insert( $jobData ); $jobData['id'] = $id; } return $jobData; } /** * Dispatch a job to the asynchronous background worker via a non-blocking loopback request. * * @param string $jobKey */ public static function dispatch( string $jobKey ): void { $token = self::generateToken( $jobKey ); $url = admin_url( 'admin-ajax.php' ); $args = array( 'timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters( 'https_local_ssl_verify', false ), 'body' => array( 'action' => 'adfuz_process_job', 'job_key' => $jobKey, 'token' => $token, ), ); // Force IPv4 if applicable AIService::ensureFastCurl(); wp_remote_post( $url, $args ); } /** * Retrieve a job by its unique job_key. * * @param string $jobKey * @return array|null */ public static function getJob( string $jobKey ): ?array { $job = DB::get( TableName::ADFUZ_JOBS() ) ->where( 'job_key', $jobKey ) ->first(); if ( empty( $job ) ) { return null; } if ( ! empty( $job['payload'] ) && is_string( $job['payload'] ) ) { $job['payload'] = json_decode( $job['payload'], true ) ?: array(); } if ( ! empty( $job['result'] ) && is_string( $job['result'] ) ) { $job['result'] = json_decode( $job['result'], true ) ?: array(); } return $job; } /** * Update the progress step of an active job. * * @param string $jobKey * @param string $stepText */ public static function updateProgress( string $jobKey, string $stepText ): void { $job = DB::get( TableName::ADFUZ_JOBS() ) ->where( 'job_key', $jobKey ) ->first(); if ( empty( $job ) ) { return; } $update = array( 'status' => 'processing', 'progress_step' => $stepText, ); if ( empty( $job['started_at'] ) ) { $update['started_at'] = current_time( 'mysql' ); } DB::get( TableName::ADFUZ_JOBS() )->update( (int) $job['id'], $update ); } /** * Mark a job as completed and store the result payload. * * @param string $jobKey * @param array $result */ public static function completeJob( string $jobKey, array $result = array() ): void { $job = DB::get( TableName::ADFUZ_JOBS() ) ->where( 'job_key', $jobKey ) ->first(); if ( empty( $job ) ) { return; } DB::get( TableName::ADFUZ_JOBS() )->update( (int) $job['id'], array( 'status' => 'completed', 'progress_step' => __( 'Completed successfully!', 'ad-fuz' ), 'result' => wp_json_encode( $result ), 'completed_at' => current_time( 'mysql' ), 'error_message' => null, ) ); } /** * Mark a job as failed with an error message and optional code. * * @param string $jobKey * @param string $errorMessage * @param string|null $errorCode */ public static function failJob( string $jobKey, string $errorMessage, ?string $errorCode = null ): void { $job = DB::get( TableName::ADFUZ_JOBS() ) ->where( 'job_key', $jobKey ) ->first(); if ( empty( $job ) ) { return; } $result = array( 'code' => $errorCode ?: 'job_failed', 'message' => $errorMessage, ); DB::get( TableName::ADFUZ_JOBS() )->update( (int) $job['id'], array( 'status' => 'failed', 'progress_step' => __( 'Failed', 'ad-fuz' ), 'result' => wp_json_encode( $result ), 'error_message' => $errorMessage, 'completed_at' => current_time( 'mysql' ), ) ); } /** * Generate an internal HMAC verification token for a job. * * @param string $jobKey * @return string */ public static function generateToken( string $jobKey ): string { $salt = wp_salt( 'auth' ); return hash_hmac( 'sha256', $jobKey, $salt ); } /** * Verify an internal HMAC token for a job. * * @param string $jobKey * @param string $token * @return bool */ public static function verifyToken( string $jobKey, string $token ): bool { $expected = self::generateToken( $jobKey ); return hash_equals( $expected, $token ); } }