-- Local outbound message queue. -- -- Added when fitme3070 stopped relaying mail through the hub's /notify endpoint -- and went back to sending its own, PHPMailer over SMTP. Mirrors the -- fotografia.andmore.pt table so the shared queue code ports unchanged. -- -- Safe to re-run. CREATE TABLE IF NOT EXISTS `message_queue` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `channel` ENUM('email','sms','whatsapp') NOT NULL DEFAULT 'email', `status` ENUM('pending','sending','sent','failed','cancelled') NOT NULL DEFAULT 'pending', `to_user_id` INT(11) DEFAULT NULL, `to_address` VARCHAR(255) NOT NULL, `to_name` VARCHAR(150) DEFAULT NULL, `reply_to` VARCHAR(190) DEFAULT NULL, `subject` VARCHAR(255) DEFAULT NULL, `body` LONGTEXT NOT NULL, `alt_body` TEXT DEFAULT NULL, `context_type` VARCHAR(50) DEFAULT NULL, `context_id` INT(11) DEFAULT NULL, `scheduled_for` DATETIME DEFAULT NULL, `attempts` INT(11) NOT NULL DEFAULT 0, `max_attempts` INT(11) NOT NULL DEFAULT 3, `next_attempt_at` DATETIME DEFAULT NULL, `last_error` TEXT DEFAULT NULL, `created_by` INT(11) DEFAULT NULL, `sent_at` DATETIME DEFAULT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `deleted_at` DATETIME DEFAULT NULL, PRIMARY KEY (`id`), -- The drainer's hot query: pending/failed rows whose turn has come. KEY `idx_mq_drain` (`status`, `next_attempt_at`, `scheduled_for`), KEY `idx_mq_created` (`created_at`), KEY `idx_mq_context` (`context_type`, `context_id`), KEY `idx_mq_deleted` (`deleted_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- No foreign key on to_user_id / created_by: messages outlive the accounts they -- were sent to, and the delivery history is worth more than the referential -- tidiness. The Monitor page tolerates a missing user.