From ba1e1ccf869b818f7a7a897264dfea05563a7796 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 07 Jun 2026 20:10:20 +0000
Subject: [PATCH] =Major overhaul of Integrations. Playing around with adding fields to post types through Registrar from an integrations' class file.

---
 inc/managers/ErrorHandler.php |  365 ++++++++++++++++++++++++++++++---------------------
 1 files changed, 215 insertions(+), 150 deletions(-)

diff --git a/inc/managers/ErrorHandler.php b/inc/managers/ErrorHandler.php
index add4eda..a3b1f33 100644
--- a/inc/managers/ErrorHandler.php
+++ b/inc/managers/ErrorHandler.php
@@ -12,8 +12,6 @@
 
 class ErrorHandler
 {
-    protected object $wpdb;
-	protected string $tableName;
     protected int $notification_threshold = 5; // Critical errors within 1 hour
 
     protected array $error_levels = [
@@ -24,11 +22,14 @@
         'critical' => 4
     ];
 
+	protected CustomTable $table;
+
     public function __construct()
     {
-        global $wpdb;
-        $this->wpdb = $wpdb;
-		$this->tableName = $wpdb->prefix . BASE . 'error_log';
+		$this->defineTables();
+//        global $wpdb;
+//        $this->wpdb = $wpdb;
+//		$this->tableName = $wpdb->prefix . BASE . 'error_log';
 
         add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
 
@@ -39,6 +40,35 @@
 //        add_filter(BASE.'admin_action_filter', [$this, 'adminActionFilter'], 10, 3);
     }
 
+	public function defineTables():void
+	{
+		$table = CustomTable::for('error_log');
+		$table->setColumns([
+			'id'			=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+			'error_type'	=> 'varchar(50) NOT NULL',
+			'component'		=> 'varchar(100) NOT NULL',
+			'method'		=> 'varchar(100) DEFAULT NULL',
+			'page_url'		=> 'varchar(255) DEFAULT NULL',
+			'message'		=> 'text NOT NULL',
+			'context'		=> 'JSON',
+			'severity'		=> 'ENUM(\'high\',\'normal\',\'low\') DEFAULT \'normal\'',
+			'user_id'		=> $table->getUserIDType().' DEFAULT NULL',
+			'user_was_logged_in'	=> 'tinyint(1) NOT NULL',
+			'source'		=> 'ENUM(\'frontend\', \'backend\') NOT NULL',
+			'created_at'	=> 'timestamp DEFAULT CURRENT_TIMESTAMP',
+		]);
+
+		$table->setKeys([
+			['key' => 'PRIMARY', 'value' => 'id'],
+			'`created_at` (`created_at`)',
+			'`component_severity_date` (`component`, `severity`, `created_at`)',
+			'`error_type_date` (`error_type`, `created_at`)',
+			'`severity_date` (`severity`, `created_at`)'
+		]);
+
+		$table->defineTable();
+		$this->table = $table;
+	}
     public function registerAdminAction():void
     {
         $admin = JVB()->admin();
@@ -68,7 +98,7 @@
         }
 
         try {
-            $table = $this->tableName;
+
             // Extract error data
             $component = sanitize_text_field($data['component'] ?? '');
             $message = sanitize_textarea_field($data['message'] ?? '');
@@ -94,8 +124,7 @@
             }
 
             // Insert into database
-            $result = $this->wpdb->insert(
-                $table,
+            $result = $this->table->insert(
                 [
                     'error_type' => $error_type,
                     'component' => $component,
@@ -104,24 +133,15 @@
                     'severity' => $severity,
                     'user_id' => get_current_user_id(),
                     'created_at' => current_time('mysql')
-                ],
-                [
-                    '%s', // error_type
-                    '%s', // component
-                    '%s', // message
-                    '%s', // context (JSON)
-                    '%s', // severity
-                    '%d', // user_id
-                    '%s'  // created_at
                 ]
             );
 
-            if ($result === false) {
+            if (!$result) {
                 // If insert fails, log to PHP error log as fallback
-                error_log("[ErrorHandler] Database insert failed: " . $this->wpdb->last_error);
+                error_log("[ErrorHandler] Database insert failed: " . $this->table->getLastError());
                 return [
 					'success'	=> false,
-					'message'	=> "[ErrorHandler] Database insert failed: " . $this->wpdb->last_error
+					'message'	=> "[ErrorHandler] Database insert failed: " . $this->table->getLastError()
 				];
             }
 
@@ -155,19 +175,15 @@
     protected function checkErrorThreshold(string $error_type, string $component)
     {
         // Get count of similar critical errors in the last hour
-        $count = $this->wpdb->get_var($this->wpdb->prepare(
-            "SELECT COUNT(*)
-         FROM {$this->tableName}
-         WHERE error_type = %s
-         AND component = %s
-         AND severity = 'critical'
-         AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)",
-            $error_type,
-            $component
-        ));
+		$count = $this->table->count([
+			'error_type' => $error_type,
+			'component' => $component,
+			'severity' => $this->error_levels[$error_type],
+			'created_at' => ['>','DATE_SUB(NOW(), INTERVAL 1 HOUR)']
+		]);
 
         // If threshold reached, take additional actions (e.g., notify developers)
-        if ((int)$count >= $this->notification_threshold) {
+        if ($count >= $this->notification_threshold) {
             // You could send an urgent notification, Slack message, etc.
             $admin_email = get_option('admin_email');
             $subject = "[URGENT] Error Threshold Exceeded for {$component}";
@@ -186,33 +202,56 @@
      *
      * @return bool Whether it gets logged successfully
      */
-    public function log(string $component, string $message, array $context = [], string $severity = 'error'):bool
-    {
-        try {
-            // Normal queue-based logging
-            JVB()->queue()->queueOperation(
-                'error_log',
-                get_current_user_id(),
-                [
-                    'component' => $component,
-                    'message' => $message,
-                    'context' => $context,
-                    'severity' => $severity
-                ],
-                ['priority' => 'high']
-            );
+	public function log(string $component, string $message, array $context = [], string $severity = 'error'): array
+	{
+		try {
+			// Validate severity
+			if (!array_key_exists($severity, $this->error_levels)) {
+				$severity = 'error';
+			}
 
+			// Extract info
+			$error_type = sanitize_text_field($context['error_type'] ?? $component);
+			$method = isset($context['method']) ? sanitize_text_field($context['method']) : null;
+			$page_url = isset($context['url']) ? esc_url_raw($context['url']) : null;
+			$user_id = get_current_user_id();
+			$user_was_logged_in = $user_id > 0 || (!empty($context['isLoggedIn']));
 
-            // Immediate notification for critical errors
-            if ($severity === 'critical') {
-                $this->notifyAdmin($component, $message, $context);
-            }
-            return true;
-        } catch (Exception $e) {
-            error_log("[edmonton.ink Error] Failed to log error: " . $e->getMessage());
-            return false;
-        }
-    }
+			// Determine source from context
+			$source = isset($context['source']) ? $context['source'] :
+				(isset($context['url']) ? 'frontend' : 'backend');
+
+			$result = $this->table->insert(
+				[
+					'error_type' => $error_type,
+					'component' => $component,
+					'method' => $method,
+					'page_url' => $page_url,
+					'message' => sanitize_textarea_field($message),
+					'context' => json_encode($context),
+					'severity' => $severity,
+					'user_id' => $user_id ?: null,
+					'user_was_logged_in' => $user_was_logged_in ? 1 : 0,
+					'source' => $source,
+					'created_at' => current_time('mysql')
+				]);
+
+			if (!$result) {
+				error_log("[ErrorHandler] Database insert failed: " . $this->table->getLastError());
+				return ['success' => false, 'message' => $this->table->getLastError()];
+			}
+
+			if ($severity === 'critical') {
+				$this->checkErrorThreshold($error_type, $component);
+			}
+
+			return ['success' => true, 'id' => $result];
+
+		} catch (Exception $e) {
+			error_log("[ErrorHandler Exception] " . $e->getMessage());
+			return ['success' => false, 'message' => $e->getMessage()];
+		}
+	}
 
     /**
      * @param string $component What class or function logs the error
@@ -221,126 +260,150 @@
      *
      * @return bool Whether the notification is sent successfully
      */
-    protected function notifyAdmin(string $component, string $message, array $context):bool
-    {
-        $admin_email = get_option('admin_email');
-        $subject = "[edmonton.ink Critical Error] {$component}";
-        $body = "Error: {$message}\n\nContext: " . print_r($context, true);
+	protected function notifyAdmin(string $component, string $message, array $context):bool
+	{
+		$admin_email = get_option('admin_email');
+		$subject = "[" . get_bloginfo('name') . " Critical Error] {$component}";
 
-        return jvbMail($admin_email, $subject, $body);
-    }
+		$body = JVB()->email()->alert(
+			'A critical error has occurred and requires immediate attention',
+			'error'
+		);
+
+		$body .= JVB()->email()->h2('Error Details');
+		$body .= JVB()->email()->card(
+			'<p><strong>Component:</strong> ' . esc_html($component) . '</p>' .
+			'<p><strong>Message:</strong></p>' .
+			JVB()->email()->codeBlock($message),
+			'Error Information'
+		);
+
+		if (!empty($context)) {
+			$body .= JVB()->email()->h3('Additional Context');
+			$body .= JVB()->email()->codeBlock(json_encode($context, JSON_PRETTY_PRINT));
+		}
+
+		return JVB()->email()->sendEmail($admin_email, $subject, $body, 'CRITICAL ERROR');
+	}
 
     /**
      * Gather summary of the most important errors
+	 * @param ?string $start_date Defaults to today
+	 * @param ?string $end_date Defaults to today
      * @return array
      */
-    protected function gatherErrorSummary():array
-    {
-        $yesterday = date('Y-m-d H:i:s', strtotime('-24 hours'));
+	public function gatherErrorSummary(?string $start_date = null, ?string $end_date = null): array
+	{
+		if (!$start_date) {
+			$start_date = gmdate('Y-m-d 00:00:00', strtotime('-1 day'));
+		}
+		if (!$end_date) {
+			$end_date = gmdate('Y-m-d 23:59:59');
+		}
 
-        // Get most frequent errors
-        $frequent_errors = $this->wpdb->get_results($this->wpdb->prepare(
-            "SELECT error_type, component, message, COUNT(*) as count
-             FROM {$this->tableName}
-             WHERE created_at > %s
-             GROUP BY error_type, component, message
-             ORDER BY count DESC
-             LIMIT 20",
-            $yesterday
-        ));
+		// Most frequent error patterns (deduplicated by component/method/message)
+		$frequent = $this->table->getMany([
+			'where' => [
+				'created_at'	=> ['BETWEEN', "{$start_date} AND {$end_date}"]
+			]
+		]);
 
-        // Get most recent critical errors
-        $critical_errors = $this->wpdb->get_results($this->wpdb->prepare(
-            "SELECT * FROM {$this->tableName}
-             WHERE severity = 'critical' AND created_at > %s
-             ORDER BY created_at DESC
-             LIMIT 5",
-            $yesterday
-        ));
-
-        return [
-            'frequent' => $frequent_errors,
-            'critical' => $critical_errors
-        ];
-    }
+		return [
+			'errors'	=> $frequent,
+			'date_range' => ['start' => $start_date, 'end' => $end_date]
+		];
+	}
 
     /**
      * Send daily error summary email to administrator
      * @return bool Whether email is sent
      */
-    public function sendErrorSummary():bool
-    {
-        // Get summary data
-        $summary = $this->gatherErrorSummary();
+	public function sendErrorSummary():bool
+	{
+		$summary = $this->gatherErrorSummary();
 
-        // Only send if there are errors
-        if (empty($summary['frequent']) && empty($summary['critical'])) {
-            return false;
-        }
+		if (empty($summary['frequent']) && empty($summary['critical'])) {
+			return false;
+		}
 
-        $admin_email = get_option('admin_email');
-        $site_name = get_bloginfo('name');
-        $today = date('Y-m-d');
-        $yesterday = date('Y-m-d', strtotime('-1 day'));
+		$admin_email = get_option('admin_email');
+		$site_name = get_bloginfo('name');
+		$yesterday = date('Y-m-d', strtotime('-1 day'));
+		$subject = "[{$site_name}] Daily Error Summary - " . date('Y-m-d');
 
-        $subject = "[{$site_name}] Daily Error Summary - {$today}";
+		// Header with alert
+		$body = JVB()->email()->h1('Daily Error Summary');
+		$body .= sprintf('<p>Error summary for <strong>%s</strong></p>', $yesterday);
 
-        // Build email body
-        $body = "= Error Summary for {$yesterday} =\n\n";
+		// Summary stats in a grid
+		if (!empty($summary['stats'])) {
+			$stats = [
+				JVB()->email()->stat($summary['stats']->total_errors, 'Total Errors'),
+				JVB()->email()->stat($summary['stats']->critical_count, 'Critical', 'Requires attention'),
+				JVB()->email()->stat($summary['stats']->error_count, 'Errors'),
+				JVB()->email()->stat($summary['stats']->warning_count, 'Warnings')
+			];
+			$body .= JVB()->email()->grid($stats, 4);
+		}
 
-        // Add frequent errors section
-        if (!empty($summary['frequent'])) {
-            $body .= "== Most Frequent Errors ==\n\n";
+		// Alert if critical errors exist
+		if (!empty($summary['critical'])) {
+			$body .= JVB()->email()->alert(
+				sprintf('Found %d critical errors that need immediate attention', count($summary['critical'])),
+				'error'
+			);
+		}
 
-            foreach ($summary['frequent'] as $index => $error) {
-                $body .= ($index + 1) . ". [{$error->component}] {$error->error_type}\n";
-                $body .= "   Message: " . wp_trim_words($error->message, 20, '...') . "\n";
-                $body .= "   Count: {$error->count}\n\n";
-            }
-        }
+		$body .= JVB()->email()->spacer(20);
 
-        // Add critical errors section
-        if (!empty($summary['critical'])) {
-            $body .= "== Recent Critical Errors ==\n\n";
+		// Frequent errors section
+		if (!empty($summary['frequent'])) {
+			$body .= JVB()->email()->h2('Most Frequent Errors');
 
-            foreach ($summary['critical'] as $index => $error) {
-                $body .= ($index + 1) . ". [{$error->component}] {$error->error_type}\n";
-                $body .= "   Time: {$error->created_at}\n";
-                $body .= "   Message: " . $error->message . "\n\n";
+			foreach ($summary['frequent'] as $error) {
+				$cardContent = JVB()->email()->badge($error->count . 'x', 'warning') . ' ';
+				$cardContent .= '<strong>' . esc_html($error->error_type) . '</strong>';
+				$cardContent .= '<p style="margin:10px 0 5px 0;font-size:13px;">' . esc_html(wp_trim_words($error->message, 15)) . '</p>';
+				$cardContent .= '<p style="margin:0;font-size:12px;color:' . JVB()->email()->colours['dark-200'] . ';">
+                Source: ' . esc_html($error->source) . ' |
+                Logged in: ' . $error->logged_in_count . ' |
+                Logged out: ' . $error->logged_out_count . '
+            </p>';
 
-                // Include context for critical errors if available
-                if (!empty($error->context)) {
-                    $context = json_decode($error->context, true);
-                    if (is_array($context)) {
-                        $body .= "   Context:\n";
-                        foreach ($context as $key => $value) {
-                            if (is_array($value) || is_object($value)) {
-                                $value = json_encode($value);
-                            }
-                            $body .= "     - {$key}: {$value}\n";
-                        }
-                    }
-                    $body .= "\n";
-                }
-            }
-        }
+				$body .= JVB()->email()->card($cardContent, $error->component);
+			}
+		}
 
-        // Add dashboard link if available
-        $admin_url = admin_url('admin.php?page=jvb-error-logs');
-        $body .= "View detailed error logs in the dashboard: {$admin_url}\n\n";
+		// Critical errors section
+		if (!empty($summary['critical'])) {
+			$body .= JVB()->email()->spacer(30);
+			$body .= JVB()->email()->h2('Recent Critical Errors');
 
-        // Send the email
-        $sent = jvbMail($admin_email, $subject, $body, 'ERROR SUMMARY');
+			foreach ($summary['critical'] as $error) {
+				$cardContent = '<p><strong>Time:</strong> ' . esc_html($error->created_at) . '</p>';
+				$cardContent .= '<p><strong>Message:</strong></p>';
+				$cardContent .= JVB()->email()->codeBlock($error->message);
 
-        // Log that summary was sent
-        if ($sent) {
-            error_log("[ErrorHandler] Daily error summary sent to {$admin_email}");
-        } else {
-            error_log("[ErrorHandler] Daily error summary was not sent.");
-        }
+				// Include context if available
+				if (!empty($error->context)) {
+					$context = json_decode($error->context, true);
+					if (is_array($context)) {
+						$cardContent .= '<p><strong>Context:</strong></p>';
+						$cardContent .= JVB()->email()->codeBlock(json_encode($context, JSON_PRETTY_PRINT));
+					}
+				}
 
-        return $sent;
-    }
+				$body .= JVB()->email()->card($cardContent, $error->component . ': ' . $error->error_type);
+			}
+		}
+
+		// Dashboard link
+		$admin_url = admin_url('admin.php?page=jvb-error-logs');
+		$body .= JVB()->email()->spacer(30);
+		$body .= JVB()->email()->button($admin_url, 'View Detailed Logs');
+
+		return JVB()->email()->sendEmail($admin_email, $subject, $body, 'ERROR SUMMARY');
+	}
 
     /**
      * Get HTML version of the error summary for nicer emails
@@ -426,6 +489,8 @@
 
     }
 
+
+
     protected function buildParams(WP_REST_Request $request):array {
         $allowedSeverity = [
             'all',

--
Gitblit v1.10.0