Fix catch-all bootstrap when e_catchall is missing.

Add runtime table self-healing for catch-all/plus/pattern email features and make upgrade SQL idempotent on existing latin1 installations by avoiding failing FK creation while preserving forwarding compatibility.
This commit is contained in:
master3395
2026-04-09 13:42:03 +02:00
parent 50ec386376
commit f9c1c28385
2 changed files with 85 additions and 8 deletions

View File

@@ -12,6 +12,7 @@ sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
django.setup()
from django.http import HttpResponse
from django.db import connection
try:
from .models import Domains,EUsers
from loginSystem.views import loadLoginPage
@@ -45,6 +46,57 @@ import threading as multi
import argparse
def _ensure_email_filter_tables():
"""Create catch-all/plus/pattern email feature tables if missing."""
create_statements = [
"""CREATE TABLE IF NOT EXISTS `e_catchall` (
`domain_id` varchar(50) NOT NULL,
`destination` varchar(255) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`domain_id`),
KEY `idx_e_catchall_domain_id` (`domain_id`)
) ENGINE=InnoDB""",
"""CREATE TABLE IF NOT EXISTS `e_server_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`plus_addressing_enabled` tinyint(1) NOT NULL DEFAULT 0,
`plus_addressing_delimiter` varchar(1) NOT NULL DEFAULT '+',
PRIMARY KEY (`id`)
) ENGINE=InnoDB""",
"""CREATE TABLE IF NOT EXISTS `e_plus_override` (
`domain_id` varchar(50) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`domain_id`),
KEY `idx_e_plus_override_domain_id` (`domain_id`)
) ENGINE=InnoDB""",
"""CREATE TABLE IF NOT EXISTS `e_pattern_forwarding` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` varchar(50) NOT NULL,
`pattern` varchar(255) NOT NULL,
`destination` varchar(255) NOT NULL,
`pattern_type` varchar(20) NOT NULL DEFAULT 'wildcard',
`priority` int(11) NOT NULL DEFAULT 100,
`enabled` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `idx_e_pattern_forwarding_domain_id` (`domain_id`)
) ENGINE=InnoDB"""
]
try:
with connection.cursor() as cursor:
for query in create_statements:
cursor.execute(query)
cursor.execute(
"""INSERT INTO `e_server_settings` (`id`, `plus_addressing_enabled`, `plus_addressing_delimiter`)
SELECT 1, 0, '+'
WHERE NOT EXISTS (SELECT 1 FROM `e_server_settings` WHERE `id` = 1)"""
)
return True, ''
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [_ensure_email_filter_tables]')
return False, 'Email feature tables are missing and could not be created automatically. Please run CyberPanel upgrade or apply DB migration.'
def _get_email_limits_controller_js():
"""Return EmailLimitsNew controller JS: from file or hardcoded fallback so it always works."""
try:
@@ -2084,6 +2136,12 @@ protocol sieve {
if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0:
return ACLManager.loadErrorJson('fetchStatus', 0)
ok, schemaErr = _ensure_email_filter_tables()
if not ok:
data_ret = {'status': 0, 'fetchStatus': 0, 'error_message': schemaErr}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
data = json.loads(self.request.body)
domain = data['domain']
@@ -2132,6 +2190,12 @@ protocol sieve {
if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0:
return ACLManager.loadErrorJson('saveStatus', 0)
ok, schemaErr = _ensure_email_filter_tables()
if not ok:
data_ret = {'status': 0, 'saveStatus': 0, 'error_message': schemaErr}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
data = json.loads(self.request.body)
domain = data['domain']
destination = data['destination']
@@ -2190,6 +2254,12 @@ protocol sieve {
if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0:
return ACLManager.loadErrorJson('deleteStatus', 0)
ok, schemaErr = _ensure_email_filter_tables()
if not ok:
data_ret = {'status': 0, 'deleteStatus': 0, 'error_message': schemaErr}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
data = json.loads(self.request.body)
domain = data['domain']

View File

@@ -2878,8 +2878,8 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
`destination` varchar(255) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`domain_id`),
CONSTRAINT `fk_catchall_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""
KEY `idx_e_catchall_domain_id` (`domain_id`)
) ENGINE=InnoDB"""
try:
cursor.execute(query)
except:
@@ -2890,7 +2890,7 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
`plus_addressing_enabled` tinyint(1) NOT NULL DEFAULT 0,
`plus_addressing_delimiter` varchar(1) NOT NULL DEFAULT '+',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""
) ENGINE=InnoDB"""
try:
cursor.execute(query)
except:
@@ -2900,8 +2900,8 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
`domain_id` varchar(50) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`domain_id`),
CONSTRAINT `fk_plus_override_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""
KEY `idx_e_plus_override_domain_id` (`domain_id`)
) ENGINE=InnoDB"""
try:
cursor.execute(query)
except:
@@ -2916,14 +2916,21 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
`priority` int(11) NOT NULL DEFAULT 100,
`enabled` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `fk_pattern_domain` (`domain_id`),
CONSTRAINT `fk_pattern_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""
KEY `idx_e_pattern_forwarding_domain_id` (`domain_id`)
) ENGINE=InnoDB"""
try:
cursor.execute(query)
except:
pass
# Seed singleton row for global email settings if missing.
try:
cursor.execute("""INSERT INTO `e_server_settings` (`id`, `plus_addressing_enabled`, `plus_addressing_delimiter`)
SELECT 1, 0, '+'
WHERE NOT EXISTS (SELECT 1 FROM `e_server_settings` WHERE `id` = 1)""")
except:
pass
try:
connection.close()
except: