PluginProbe ʕ •ᴥ•ʔ
OttoKit: All-in-One Automation Platform / 1.0.90
OttoKit: All-in-One Automation Platform v1.0.90
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 1 year ago AutomationController.php 3 years ago EventController.php 3 years ago GlobalSearchController.php 1 year ago IntegrationsController.php 2 years ago OptionController.php 3 years ago RestController.php 1 year ago RoutesController.php 1 year ago WebhookRequestsController.php 1 year ago
WebhookRequestsController.php
345 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 $response = wp_remote_post( $failed_requests['request_url'], $data );
244 $response_code = wp_remote_retrieve_response_code( $response );
245 $error_info = wp_remote_retrieve_body( $response );
246 if ( 405 === wp_remote_retrieve_response_code( $response ) ) {
247 $error_info = wp_remote_retrieve_response_message( $response );
248 }
249 if ( 0 === wp_remote_retrieve_response_code( $response ) ) {
250 $error_info = __( 'Service not available', 'suretriggers' );
251 }
252 $wpdb->update(
253 $table_name,
254 [
255 'request_method' => $failed_requests['request_method'],
256 'request_url' => $failed_requests['request_url'],
257 'request_data' => $failed_requests['request_data'],
258 'response_code' => $response_code,
259 'status' => ( 200 === $response_code ) ? 'success' : 'failed',
260 'error_info' => $error_info,
261 'retry_attempts' => $failed_requests['retry_attempts'] + 1,
262 'processed_at' => current_time( 'mysql' ),
263 'updated_at' => current_time( 'mysql' ),
264 ],
265 [ 'id' => $id ]
266 );
267 return true;
268 }
269 return false;
270 }
271
272 /**
273 * Delete failed webhook requests log that are 60 days older.
274 * Delete success webhook requests log that are 30 days older.
275 *
276 * @return void
277 */
278 public static function suretriggers_cleanup_requests_logs() {
279 global $wpdb;
280 $table_name = self::get_table_name();
281 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table_name} WHERE status = %s AND created_at < NOW() - INTERVAL %d DAY", 'failed', 60 ) ); //phpcs:ignore
282 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table_name} WHERE status = %s AND created_at < NOW() - INTERVAL %d DAY", 'success', 30 ) ); //phpcs:ignore
283 }
284
285 /**
286 * Verify WordPress connection with SureTriggers API to check the connection status twice daily.
287 *
288 * @return void
289 */
290 public static function suretriggers_verify_api_wp_connection() {
291 $response = RestController::suretriggers_verify_wp_connection();
292 // Check if the response is valid.
293 if ( is_wp_error( $response ) ) {
294 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_wp_error' );
295 } else {
296 $status_code = wp_remote_retrieve_response_code( $response );
297 if ( 200 !== $status_code ) {
298 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_error' );
299 } else {
300 update_option( 'suretriggers_verify_connection', 'suretriggers_connection_successful' );
301 }
302 }
303 }
304
305 /**
306 * Unschedule the event on plugin deletion.
307 *
308 * @return void
309 */
310 public static function suretriggers_remove_table_retry_cron() {
311 // Clear custom scheduled cron created.
312 wp_clear_scheduled_hook( 'suretriggers_retry_cron_schedule' );
313
314 // Remove retry cron schedule on plugin deletion.
315 $retry_failed_requests = wp_next_scheduled( 'suretriggers_retry_failed_requests' );
316 if ( $retry_failed_requests ) {
317 wp_unschedule_event( $retry_failed_requests, 'suretriggers_retry_failed_requests' );
318 }
319
320 // Remove clean up cron schedule.
321 $webhook_requests_cleanup = wp_next_scheduled( 'suretriggers_webhook_requests_cleanup_logs' );
322 if ( $webhook_requests_cleanup ) {
323 wp_unschedule_event( $webhook_requests_cleanup, 'suretriggers_webhook_requests_cleanup_logs' );
324 }
325
326 // Remove connection verification cron schedule.
327 $webhook_requests_cleanup = wp_next_scheduled( 'suretriggers_verify_api_connection' );
328 if ( $webhook_requests_cleanup ) {
329 wp_unschedule_event( $webhook_requests_cleanup, 'suretriggers_verify_api_connection' );
330 }
331
332 // Delete table on plugin delete.
333 global $wpdb;
334 $table_name = self::get_table_name();
335 // Drop the custom table.
336 $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );
337 if ( $table_exists ) {
338 $wpdb->query( "DROP TABLE IF EXISTS $table_name" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
339 }
340 }
341
342 }
343
344 WebhookRequestsController::get_instance();
345