PluginProbe ʕ •ᴥ•ʔ
OttoKit: All-in-One Automation Platform / 1.1.24
OttoKit: All-in-One Automation Platform v1.1.24
1.1.31 1.1.30 1.1.29 1.1.28 1.1.27 1.1.9 trunk 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 1.0.39 1.0.40 1.0.41 1.0.42 1.0.43 1.0.44 1.0.45 1.0.46 1.0.47 1.0.48 1.0.49 1.0.50 1.0.51 1.0.52 1.0.53 1.0.54 1.0.55 1.0.56 1.0.57 1.0.58 1.0.59 1.0.60 1.0.61 1.0.62 1.0.63 1.0.64 1.0.65 1.0.66 1.0.67 1.0.68 1.0.69 1.0.7 1.0.70 1.0.71 1.0.72 1.0.73 1.0.74 1.0.75 1.0.76 1.0.77 1.0.78 1.0.79 1.0.8 1.0.80 1.0.81 1.0.82 1.0.83 1.0.84 1.0.85 1.0.86 1.0.87 1.0.88 1.0.89 1.0.9 1.0.90 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8
suretriggers / src / Controllers / WebhookRequestsController.php
suretriggers / src / Controllers Last commit date
AuthController.php 3 months ago AutomationController.php 11 months ago EventController.php 11 months ago GlobalSearchController.php 2 months ago IntegrationsController.php 11 months ago OptionController.php 3 years ago RestController.php 3 months ago RoutesController.php 1 year ago SettingsController.php 10 months ago WebhookRequestsController.php 2 months ago
WebhookRequestsController.php
355 lines
1 <?php
2 /**
3 * WebhookRequestsController.
4 * php version 5.6
5 *
6 * @category WebhookRequestsController
7 * @package SureTriggers
8 * @author BSF <username@example.com>
9 * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
10 * @link https://www.brainstormforce.com/
11 * @since 1.0.0
12 */
13
14 namespace SureTriggers\Controllers;
15
16 use SureTriggers\Traits\SingletonLoader;
17 use SureTriggers\Models\SaasApiToken;
18 use SureTriggers\Controllers\RestController;
19
20 /**
21 * WebhookRequestsController- Store Webhook requests and retry for failed.
22 *
23 * @category WebhookRequestsController
24 * @package SureTriggers
25 * @author BSF <username@example.com>
26 * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
27 * @link https://www.brainstormforce.com/
28 * @since 1.0.0
29 *
30 * @psalm-suppress UndefinedTrait
31 */
32 class WebhookRequestsController {
33
34 use SingletonLoader;
35
36 /**
37 * Webhook Requests Table name.
38 *
39 * @var string
40 */
41 protected static $name = 'suretriggers_webhook_requests';
42
43 /**
44 * Initialise data.
45 */
46 public function __construct() {
47 add_action( 'suretriggers_retry_failed_requests', [ $this, 'suretriggers_retry_failed_trigger_requests' ] );
48 add_action( 'suretriggers_webhook_requests_cleanup_logs', [ $this, 'suretriggers_cleanup_requests_logs' ] );
49 add_action( 'suretriggers_verify_api_connection', [ $this, 'suretriggers_verify_api_wp_connection' ] );
50 add_filter( 'cron_schedules', [ $this, 'suretriggers_custom_cron_schedule' ] );
51 }
52
53 /**
54 * Get the table name.
55 *
56 * @return string
57 */
58 public static function get_table_name() {
59 global $wpdb;
60 return $wpdb->prefix . self::$name;
61 }
62
63 /**
64 * Adds a custom cron schedule for every 30 minutes.
65 *
66 * @param array $schedules An array of non-default cron schedules.
67 * @return array Filtered array of non-default cron schedules.
68 */
69 public static function suretriggers_custom_cron_schedule( $schedules ) {
70 $schedules['suretriggers_retry_cron_schedule'] = [
71 'interval' => 30 * MINUTE_IN_SECONDS,
72 'display' => __( 'Every 30 minutes', 'suretriggers' ),
73 ];
74 $schedules['suretriggers_verify_connection_cron_schedule'] = [
75 'interval' => 6 * HOUR_IN_SECONDS,
76 'display' => __( 'Every 6 hours', 'suretriggers' ),
77 ];
78 return $schedules;
79 }
80
81 /**
82 * Custom table for storing of webhook requests logs.
83 *
84 * @return void
85 */
86 public static function suretriggers_webhook_request_log_table() {
87 global $wpdb;
88 $table_name = self::get_table_name();
89 $charset_collate = $wpdb->get_charset_collate();
90
91 $sql = "CREATE TABLE $table_name (
92 id mediumint(9) NOT NULL AUTO_INCREMENT,
93 request_method varchar(255) NULL,
94 request_url varchar(255) NOT NULL,
95 request_data longtext NOT NULL,
96 response_code int(3) NOT NULL,
97 status varchar(20) NOT NULL,
98 error_info varchar(255) NOT NULL,
99 retry_attempts int(3) DEFAULT 0,
100 processed_at datetime NULL,
101 created_at datetime,
102 updated_at datetime ON UPDATE CURRENT_TIMESTAMP,
103 PRIMARY KEY (id)
104 ) $charset_collate;";
105
106 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
107 dbDelta( $sql );
108 }
109
110 /**
111 * Setup cron to retry failed webhook requests and cleanup logs for Triggers.
112 *
113 * @return void
114 */
115 public static function suretriggers_setup_custom_cron() {
116 // Retry failed requests.
117 if ( ! wp_next_scheduled( 'suretriggers_retry_failed_requests' ) ) {
118 wp_schedule_event( time(), 'suretriggers_retry_cron_schedule', 'suretriggers_retry_failed_requests' );
119 }
120
121 // Clean up log requests that are older than 15 days.
122 if ( ! wp_next_scheduled( 'suretriggers_webhook_requests_cleanup_logs' ) ) {
123 wp_schedule_event( time(), 'daily', 'suretriggers_webhook_requests_cleanup_logs' );
124 }
125
126 // Verify the API connection every 6 hours to keep the connection alive.
127 if ( ! wp_next_scheduled( 'suretriggers_verify_api_connection' ) ) {
128 wp_schedule_event( time(), 'suretriggers_verify_connection_cron_schedule', 'suretriggers_verify_api_connection' );
129 } else {
130 // Reschedule the event that was twice daily before to every 6 hours.
131 $get_scheduled_event = wp_get_scheduled_event( 'suretriggers_verify_api_connection' );
132 if ( $get_scheduled_event ) {
133 if ( 21600 !== $get_scheduled_event->interval ) {
134 wp_clear_scheduled_hook( 'suretriggers_verify_api_connection' );
135 wp_reschedule_event( time(), 'suretriggers_verify_connection_cron_schedule', 'suretriggers_verify_api_connection' );
136 }
137 }
138 }
139 }
140
141 /**
142 * Log Request handler.
143 *
144 * @param string $data Request data.
145 * @param int $response_code Response Code.
146 * @param string $error_info Error Info.
147 *
148 * @return void
149 */
150 public static function suretriggers_log_request( $data, $response_code, $error_info ) {
151 global $wpdb;
152 // Store the data in request logs.
153 $wpdb->insert(
154 self::get_table_name(),
155 [
156 'request_method' => 'POST',
157 'request_url' => SURE_TRIGGERS_WEBHOOK_SERVER_URL . '/wordpress/webhook',
158 'request_data' => $data,
159 'response_code' => $response_code,
160 'status' => ( 200 === $response_code ) ? 'success' : 'failed',
161 'error_info' => $error_info,
162 'retry_attempts' => 0,
163 'processed_at' => null,
164 'created_at' => current_time( 'mysql' ),
165 'updated_at' => current_time( 'mysql' ),
166 ]
167 );
168 }
169
170 /**
171 * Update Failed Webhook Request handler via cron.
172 *
173 * @return void
174 */
175 public static function suretriggers_retry_failed_trigger_requests() {
176 global $wpdb;
177 $table_name = self::get_table_name();
178
179 // Select all failed requests that haven't exceeded retry attempts.
180 $failed_requests = $wpdb->get_results(
181 $wpdb->prepare(
182 "SELECT * FROM {$table_name} WHERE status = %s AND retry_attempts < %d", //phpcs:ignore
183 'failed',
184 5
185 ),
186 ARRAY_A
187 );
188
189 foreach ( $failed_requests as $request ) {
190 $data = json_decode( $request['request_data'], true );
191 if ( is_array( $data ) ) {
192 $data['headers']['Authorization'] = 'Bearer ' . SaasApiToken::get();
193 $response = wp_remote_post( $request['request_url'], $data );
194 $response_code = wp_remote_retrieve_response_code( $response );
195 $error_info = wp_remote_retrieve_body( $response );
196 if ( 405 === $response_code ) {
197 $error_info = wp_remote_retrieve_response_message( $response );
198 }
199 if ( 0 === $response_code ) {
200 $error_info = __( 'Service not available', 'suretriggers' );
201 }
202 // Update the request if failed with the new response.
203 $wpdb->update(
204 $table_name,
205 [
206 'request_method' => $request['request_method'],
207 'request_url' => $request['request_url'],
208 'request_data' => $request['request_data'],
209 'response_code' => $response_code,
210 'status' => ( 200 === $response_code ) ? 'success' : 'failed',
211 'error_info' => $error_info,
212 'retry_attempts' => $request['retry_attempts'] + 1,
213 'processed_at' => current_time( 'mysql' ),
214 'updated_at' => current_time( 'mysql' ),
215 ],
216 [ 'id' => $request['id'] ]
217 );
218 }
219 }
220 }
221
222 /**
223 * Update Failed Webhook Request handler via Retry button.
224 *
225 * @param int $id ID.
226 *
227 * @return bool
228 */
229 public static function suretriggers_retry_trigger_request( $id ) {
230 global $wpdb;
231 $table_name = self::get_table_name();
232 $failed_requests = $wpdb->get_row(
233 $wpdb->prepare(
234 "SELECT * FROM {$table_name} WHERE id = %d", //phpcs:ignore
235 $id
236 ),
237 ARRAY_A
238 );
239
240 $data = json_decode( $failed_requests['request_data'], true );
241 if ( is_array( $data ) ) {
242 $data['headers']['Authorization'] = 'Bearer ' . SaasApiToken::get();
243
244 // Generate a new UUID so the SaaS server doesn't deduplicate this as a replay.
245 if ( isset( $data['body']['wordpress_webhook_uuid'] ) ) {
246 $new_uuid = str_replace( '-', '', wp_generate_uuid4() );
247 $site_url = esc_url_raw( str_replace( '/wp-json/', '', get_site_url() ) );
248 $site_url = preg_replace( '/^https?:\/\//', '', $site_url );
249 $encoded_site_url = urlencode( (string) $site_url );
250 $data['body']['wordpress_webhook_uuid'] = $new_uuid . '_' . $encoded_site_url;
251 }
252
253 $response = wp_remote_post( $failed_requests['request_url'], $data );
254 $response_code = wp_remote_retrieve_response_code( $response );
255 $error_info = wp_remote_retrieve_body( $response );
256 if ( 405 === wp_remote_retrieve_response_code( $response ) ) {
257 $error_info = wp_remote_retrieve_response_message( $response );
258 }
259 if ( 0 === wp_remote_retrieve_response_code( $response ) ) {
260 $error_info = __( 'Service not available', 'suretriggers' );
261 }
262 $wpdb->update(
263 $table_name,
264 [
265 'request_method' => $failed_requests['request_method'],
266 'request_url' => $failed_requests['request_url'],
267 'request_data' => (string) wp_json_encode( $data ),
268 'response_code' => $response_code,
269 'status' => ( 200 === $response_code ) ? 'success' : 'failed',
270 'error_info' => $error_info,
271 'retry_attempts' => $failed_requests['retry_attempts'] + 1,
272 'processed_at' => current_time( 'mysql' ),
273 'updated_at' => current_time( 'mysql' ),
274 ],
275 [ 'id' => $id ]
276 );
277 return true;
278 }
279 return false;
280 }
281
282 /**
283 * Delete failed webhook requests log that are 60 days older.
284 * Delete success webhook requests log that are 30 days older.
285 *
286 * @return void
287 */
288 public static function suretriggers_cleanup_requests_logs() {
289 global $wpdb;
290 $table_name = self::get_table_name();
291 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table_name} WHERE status = %s AND created_at < NOW() - INTERVAL %d DAY", 'failed', 60 ) ); //phpcs:ignore
292 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table_name} WHERE status = %s AND created_at < NOW() - INTERVAL %d DAY", 'success', 30 ) ); //phpcs:ignore
293 }
294
295 /**
296 * Verify WordPress connection with SureTriggers API to check the connection status twice daily.
297 *
298 * @return void
299 */
300 public static function suretriggers_verify_api_wp_connection() {
301 $response = RestController::suretriggers_verify_wp_connection();
302 // Check if the response is valid.
303 if ( is_wp_error( $response ) ) {
304 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_wp_error' );
305 } else {
306 $status_code = wp_remote_retrieve_response_code( $response );
307 if ( 200 !== $status_code ) {
308 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_error' );
309 } else {
310 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_successful' );
311 }
312 }
313 }
314
315 /**
316 * Unschedule the event on plugin deletion.
317 *
318 * @return void
319 */
320 public static function suretriggers_remove_table_retry_cron() {
321 // Clear custom scheduled cron created.
322 wp_clear_scheduled_hook( 'suretriggers_retry_cron_schedule' );
323
324 // Remove retry cron schedule on plugin deletion.
325 $retry_failed_requests = wp_next_scheduled( 'suretriggers_retry_failed_requests' );
326 if ( $retry_failed_requests ) {
327 wp_unschedule_event( $retry_failed_requests, 'suretriggers_retry_failed_requests' );
328 }
329
330 // Remove clean up cron schedule.
331 $webhook_requests_cleanup = wp_next_scheduled( 'suretriggers_webhook_requests_cleanup_logs' );
332 if ( $webhook_requests_cleanup ) {
333 wp_unschedule_event( $webhook_requests_cleanup, 'suretriggers_webhook_requests_cleanup_logs' );
334 }
335
336 // Remove connection verification cron schedule.
337 $webhook_requests_cleanup = wp_next_scheduled( 'suretriggers_verify_api_connection' );
338 if ( $webhook_requests_cleanup ) {
339 wp_unschedule_event( $webhook_requests_cleanup, 'suretriggers_verify_api_connection' );
340 }
341
342 // Delete table on plugin delete.
343 global $wpdb;
344 $table_name = self::get_table_name();
345 // Drop the custom table.
346 $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );
347 if ( $table_exists ) {
348 $wpdb->query( "DROP TABLE IF EXISTS $table_name" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
349 }
350 }
351
352 }
353
354 WebhookRequestsController::get_instance();
355