db = WP_Domain_Mapping_DB::get_instance();
// 添加菜单页面
add_action('network_admin_menu', array($this, 'add_menu_page'), 30);
// 添加AJAX处理程序
add_action('wp_ajax_dm_check_domain_health', array($this, 'ajax_check_domain_health'));
// 添加计划任务钩子
add_action('dm_domain_health_check', array($this, 'scheduled_health_check'));
// 初始化计划任务
$this->initialize_cron();
// 处理手动健康检查
add_action('admin_init', array($this, 'handle_manual_check'));
// 处理设置保存
add_action('admin_init', array($this, 'handle_settings_save'));
}
/**
* 初始化定时健康检查
*/
private function initialize_cron() {
// 注册激活时的钩子
register_activation_hook(WP_DOMAIN_MAPPING_BASENAME, array($this, 'schedule_health_check'));
// 注册停用时的钩子
register_deactivation_hook(WP_DOMAIN_MAPPING_BASENAME, array($this, 'unschedule_health_check'));
}
/**
* 添加健康监控菜单
*/
public function add_menu_page() {
add_submenu_page(
'settings.php',
__('Domain Health', 'wp-domain-mapping'),
__('Domain Health', 'wp-domain-mapping'),
'manage_network',
'domain-mapping-health',
array($this, 'render_page')
);
}
/**
* 计划健康检查任务
*/
public function schedule_health_check() {
if (!wp_next_scheduled('dm_domain_health_check')) {
wp_schedule_event(time(), 'daily', 'dm_domain_health_check');
}
}
/**
* 取消健康检查任务
*/
public function unschedule_health_check() {
$timestamp = wp_next_scheduled('dm_domain_health_check');
if ($timestamp) {
wp_unschedule_event($timestamp, 'dm_domain_health_check');
}
}
/**
* 处理手动健康检查
*/
public function handle_manual_check() {
if (isset($_POST['dm_manual_health_check']) && $_POST['dm_manual_health_check']) {
// 验证nonce
if (!isset($_POST['dm_manual_health_check_nonce']) || !wp_verify_nonce($_POST['dm_manual_health_check_nonce'], 'dm_manual_health_check')) {
wp_die(__('Security check failed.', 'wp-domain-mapping'));
}
// 检查权限
if (!current_user_can('manage_network')) {
wp_die(__('You do not have sufficient permissions to perform this action.', 'wp-domain-mapping'));
}
// 执行健康检查
$this->run_health_check_for_all_domains();
// 重定向回健康页面
wp_redirect(add_query_arg(array('page' => 'domain-mapping-health', 'checked' => 1), network_admin_url('settings.php')));
exit;
}
}
/**
* 处理设置保存
*/
public function handle_settings_save() {
if (isset($_POST['dm_health_settings']) && $_POST['dm_health_settings']) {
// 验证nonce
if (!isset($_POST['dm_health_settings_nonce']) || !wp_verify_nonce($_POST['dm_health_settings_nonce'], 'dm_health_settings')) {
wp_die(__('Security check failed.', 'wp-domain-mapping'));
}
// 检查权限
if (!current_user_can('manage_network')) {
wp_die(__('You do not have sufficient permissions to perform this action.', 'wp-domain-mapping'));
}
// 保存设置
$health_check_enabled = isset($_POST['health_check_enabled']) ? (bool) $_POST['health_check_enabled'] : false;
$health_notifications_enabled = isset($_POST['health_notifications_enabled']) ? (bool) $_POST['health_notifications_enabled'] : false;
$notification_email = isset($_POST['notification_email']) ? sanitize_email($_POST['notification_email']) : '';
$ssl_expiry_threshold = isset($_POST['ssl_expiry_threshold']) ? intval($_POST['ssl_expiry_threshold']) : 14;
update_site_option('dm_health_check_enabled', $health_check_enabled);
update_site_option('dm_health_notifications_enabled', $health_notifications_enabled);
update_site_option('dm_notification_email', $notification_email);
update_site_option('dm_ssl_expiry_threshold', $ssl_expiry_threshold);
// 如果启用了自动检查,确保计划任务已设置
if ($health_check_enabled) {
$this->schedule_health_check();
} else {
$this->unschedule_health_check();
}
// 重定向回健康页面
wp_redirect(add_query_arg(array('page' => 'domain-mapping-health', 'settings-updated' => 1), network_admin_url('settings.php')));
exit;
}
}
/**
* 渲染健康监控页面
*/
public function render_page() {
// 检查权限
if (!current_user_can('manage_network')) {
wp_die(__('You do not have sufficient permissions to access this page.', 'wp-domain-mapping'));
}
// 获取所有域名
global $wpdb;
$table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
$domains = $wpdb->get_results("
SELECT d.*, b.domain as original_domain, b.path
FROM {$table} d
JOIN {$wpdb->blogs} b ON d.blog_id = b.blog_id
ORDER BY d.blog_id ASC, d.active DESC
");
// 获取健康检查结果(如果有)
$health_results = get_site_option('dm_domain_health_results', array());
// 渲染页面
?>
' .
__('Domain health check completed.', 'wp-domain-mapping') .
'
';
}
if (isset($_GET['settings-updated']) && $_GET['settings-updated']) {
echo '' .
__('Settings saved.', 'wp-domain-mapping') .
'
';
}
?>
|
|
|
|
|
|
|
domain);
$health_data = isset($health_results[$domain_key]) ? $health_results[$domain_key] : null;
$site_name = get_blog_option($domain->blog_id, 'blogname', __('Unknown', 'wp-domain-mapping'));
?>
domain); ?>
active) : ?>
|
original_domain . $domain->path); ?>
|
|
|
|
|
|
domain)); ?>
|
|
|
|
|
|
|
|
|
|
|
= 200 && $health_data['response_code'] < 400) {
echo ' ';
} else {
echo ' ';
}
} else {
echo '-';
}
?>
|
|
|
check_domain_health($domain);
// 保存结果
$this->save_health_check_result($domain, $result);
// 返回结果
wp_send_json_success($result);
}
/**
* 计划任务:执行所有域名健康检查
*/
public function scheduled_health_check() {
// 检查是否启用了自动健康检查
if (!get_site_option('dm_health_check_enabled', true)) {
return;
}
$this->run_health_check_for_all_domains();
}
/**
* 对所有域名执行健康检查
*/
private function run_health_check_for_all_domains() {
global $wpdb;
$table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
// 获取所有域名
$domains = $wpdb->get_col("SELECT domain FROM {$table}");
// 初始化通知信息
$issues = array();
// 检查每个域名
foreach ($domains as $domain) {
$result = $this->check_domain_health($domain);
$this->save_health_check_result($domain, $result);
// 检查是否有问题
if ($this->has_health_issues($result)) {
$issues[$domain] = $result;
}
}
// 如果启用了通知并且有问题,发送通知
if (!empty($issues) && get_site_option('dm_health_notifications_enabled', true)) {
$this->send_health_notification($issues);
}
return true;
}
/**
* 检查域名健康状态
*
* @param string $domain 要检查的域名
* @return array 健康检查结果
*/
private function check_domain_health($domain) {
$result = array(
'domain' => $domain,
'last_check' => current_time('mysql'),
'dns_status' => 'error',
'dns_message' => __('DNS check not performed', 'wp-domain-mapping'),
'resolved_ip' => '',
'ssl_valid' => false,
'ssl_expiry' => '',
'accessible' => false,
'response_code' => 0
);
// 获取服务器IP或CNAME
$server_ip = get_site_option('dm_ipaddress', '');
$server_cname = get_site_option('dm_cname', '');
// 检查DNS设置
$domain_ip = gethostbyname($domain);
$result['resolved_ip'] = $domain_ip;
if ($domain_ip && $domain_ip !== $domain) {
if ($server_ip && strpos($server_ip, $domain_ip) !== false) {
$result['dns_status'] = 'success';
$result['dns_message'] = __('Domain A record is correctly pointing to server IP.', 'wp-domain-mapping');
} elseif ($server_cname && function_exists('dns_get_record')) {
$dns_records = @dns_get_record($domain, DNS_CNAME);
$has_valid_cname = false;
if ($dns_records) {
foreach ($dns_records as $record) {
if (isset($record['target']) &&
(
$record['target'] === $server_cname ||
strpos($record['target'], $server_cname) !== false
)) {
$has_valid_cname = true;
break;
}
}
}
if ($has_valid_cname) {
$result['dns_status'] = 'success';
$result['dns_message'] = __('Domain CNAME record is correctly configured.', 'wp-domain-mapping');
} else {
$result['dns_message'] = __('Domain is not pointing to the correct server.', 'wp-domain-mapping');
}
} else {
$result['dns_message'] = __('Cannot verify DNS configuration.', 'wp-domain-mapping');
}
} else {
$result['dns_message'] = __('Domain does not resolve to an IP address.', 'wp-domain-mapping');
}
// 检查网站可访问性和SSL
$response = $this->test_domain_connection($domain);
if ($response) {
$result['accessible'] = $response['accessible'];
$result['response_code'] = $response['response_code'];
$result['ssl_valid'] = $response['ssl_valid'];
$result['ssl_expiry'] = $response['ssl_expiry'];
}
return $result;
}
/**
* 测试域名连接
*
* @param string $domain 域名
* @return array|false 连接测试结果
*/
private function test_domain_connection($domain) {
if (!function_exists('curl_init')) {
return false;
}
$result = array(
'accessible' => false,
'response_code' => 0,
'ssl_valid' => false,
'ssl_expiry' => ''
);
// 测试HTTPS连接
$ch = curl_init('https://' . $domain);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($ch);
$response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$result['response_code'] = $response_code;
if ($response !== false && $response_code > 0) {
$result['accessible'] = ($response_code >= 200 && $response_code < 400);
$result['ssl_valid'] = ($response !== false);
// 获取SSL证书信息
$ssl_info = curl_getinfo($ch, CURLINFO_CERTINFO);
if (!empty($ssl_info) && isset($ssl_info[0]['Expire date'])) {
$result['ssl_expiry'] = $ssl_info[0]['Expire date'];
}
}
curl_close($ch);
// 如果HTTPS失败,尝试HTTP
if (!$result['accessible']) {
$ch = curl_init('http://' . $domain);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response !== false && $response_code > 0) {
$result['accessible'] = ($response_code >= 200 && $response_code < 400);
$result['response_code'] = $response_code;
}
curl_close($ch);
}
return $result;
}
/**
* 保存健康检查结果
*
* @param string $domain 域名
* @param array $result 健康检查结果
*/
private function save_health_check_result($domain, $result) {
$health_results = get_site_option('dm_domain_health_results', array());
$domain_key = md5($domain);
$health_results[$domain_key] = $result;
update_site_option('dm_domain_health_results', $health_results);
}
/**
* 检查结果是否有健康问题
*
* @param array $result 健康检查结果
* @return bool 是否有问题
*/
private function has_health_issues($result) {
// 检查DNS问题
if ($result['dns_status'] !== 'success') {
return true;
}
// 检查SSL问题
if (!$result['ssl_valid']) {
return true;
}
// 检查SSL即将到期
if (!empty($result['ssl_expiry'])) {
$expiry_date = strtotime($result['ssl_expiry']);
$threshold = get_site_option('dm_ssl_expiry_threshold', 14);
$threshold_date = strtotime('+' . $threshold . ' days');
if ($expiry_date <= $threshold_date) {
return true;
}
}
// 检查可访问性问题
if (!$result['accessible']) {
return true;
}
return false;
}
/**
* 发送健康问题通知
*
* @param array $issues 有问题的域名及其健康检查结果
*/
private function send_health_notification($issues) {
$notification_email = get_site_option('dm_notification_email', get_option('admin_email'));
if (empty($notification_email)) {
return;
}
$site_name = get_bloginfo('name');
$subject = sprintf(__('[%s] Domain Mapping Health Alert', 'wp-domain-mapping'), $site_name);
// 构建邮件内容
$message = sprintf(__('Domain health issues were detected on %s.', 'wp-domain-mapping'), $site_name) . "\n\n";
$message .= __('The following domains have issues:', 'wp-domain-mapping') . "\n\n";
foreach ($issues as $domain => $result) {
$message .= sprintf(__('Domain: %s', 'wp-domain-mapping'), $domain) . "\n";
// 添加DNS状态
if ($result['dns_status'] !== 'success') {
$message .= " - " . sprintf(__('DNS Issue: %s', 'wp-domain-mapping'), $result['dns_message']) . "\n";
}
// 添加SSL状态
if (!$result['ssl_valid']) {
$message .= " - " . __('SSL Certificate is invalid or missing.', 'wp-domain-mapping') . "\n";
} elseif (!empty($result['ssl_expiry'])) {
$expiry_date = strtotime($result['ssl_expiry']);
$threshold = get_site_option('dm_ssl_expiry_threshold', 14);
$threshold_date = strtotime('+' . $threshold . ' days');
if ($expiry_date <= $threshold_date) {
$message .= " - " . sprintf(
__('SSL Certificate expires on %s (within %d days).', 'wp-domain-mapping'),
date('Y-m-d', $expiry_date),
$threshold
) . "\n";
}
}
// 添加可访问性状态
if (!$result['accessible']) {
$message .= " - " . __('Site is not accessible.', 'wp-domain-mapping') . "\n";
if ($result['response_code'] > 0) {
$message .= " " . sprintf(__('HTTP Response Code: %d', 'wp-domain-mapping'), $result['response_code']) . "\n";
}
}
$message .= "\n";
}
// 添加解决方案链接
$message .= sprintf(
__('To view and manage these issues, please visit: %s', 'wp-domain-mapping'),
network_admin_url('settings.php?page=domain-mapping-health')
) . "\n";
// 发送邮件
wp_mail($notification_email, $subject, $message);
}
}