wp-domain-mapping/includes/class-health.php
2025-05-20 22:52:24 +08:00

897 lines
42 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* 域名健康监控功能
*
* 处理域名健康状态检查和通知
*
* @package WP Domain Mapping
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
/**
* 域名健康监控类
*/
class WP_Domain_Mapping_Health {
/**
* 类实例
*
* @var WP_Domain_Mapping_Health
*/
protected static $instance;
/**
* 数据库实例
*/
private $db;
/**
* 获取类实例
*
* @return WP_Domain_Mapping_Health
*/
public static function get_instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 构造函数
*/
private function __construct() {
$this->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());
// 渲染页面
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<?php
// 显示成功消息(如果有)
if (isset($_GET['checked']) && $_GET['checked']) {
echo '<div class="notice notice-success is-dismissible"><p>' .
__('Domain health check completed.', 'wp-domain-mapping') .
'</p></div>';
}
if (isset($_GET['settings-updated']) && $_GET['settings-updated']) {
echo '<div class="notice notice-success is-dismissible"><p>' .
__('Settings saved.', 'wp-domain-mapping') .
'</p></div>';
}
?>
<div class="card domain-mapping-card" style="max-width: 100%; margin-top: 20px; padding: 20px; background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; box-shadow: 0 1px 1px rgba(0,0,0,.04);">
<h2><?php _e('Domain Health Status', 'wp-domain-mapping'); ?></h2>
<p>
<form method="post" action="">
<?php wp_nonce_field('dm_manual_health_check', 'dm_manual_health_check_nonce'); ?>
<input type="hidden" name="dm_manual_health_check" value="1">
<input type="submit" class="button button-primary" value="<?php esc_attr_e('Check All Domains Now', 'wp-domain-mapping'); ?>">
</form>
</p>
<div class="tablenav top">
<div class="tablenav-pages">
<span class="displaying-num">
<?php
if (!empty($domains)) {
printf(
_n('%s domain', '%s domains', count($domains), 'wp-domain-mapping'),
number_format_i18n(count($domains))
);
} else {
_e('No domains found', 'wp-domain-mapping');
}
?>
</span>
</div>
<br class="clear">
</div>
<table class="wp-list-table widefat fixed striped domains-health-table">
<thead>
<tr>
<th class="column-domain"><?php _e('Domain', 'wp-domain-mapping'); ?></th>
<th class="column-site"><?php _e('Site', 'wp-domain-mapping'); ?></th>
<th class="column-dns"><?php _e('DNS Status', 'wp-domain-mapping'); ?></th>
<th class="column-ssl"><?php _e('SSL Status', 'wp-domain-mapping'); ?></th>
<th class="column-status"><?php _e('Reachable', 'wp-domain-mapping'); ?></th>
<th class="column-last-check"><?php _e('Last Check', 'wp-domain-mapping'); ?></th>
<th class="column-actions"><?php _e('Actions', 'wp-domain-mapping'); ?></th>
</tr>
</thead>
<tbody>
<?php if (!empty($domains)) : ?>
<?php foreach ($domains as $domain) :
$domain_key = md5($domain->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'));
?>
<tr data-domain="<?php echo esc_attr($domain->domain); ?>" data-blog-id="<?php echo esc_attr($domain->blog_id); ?>">
<td class="column-domain">
<?php echo esc_html($domain->domain); ?>
<?php if ($domain->active) : ?>
<span class="dashicons dashicons-star-filled" style="color: #f0b849;" title="<?php esc_attr_e('Primary Domain', 'wp-domain-mapping'); ?>"></span>
<?php endif; ?>
</td>
<td class="column-site">
<a href="<?php echo esc_url(network_admin_url('site-info.php?id=' . $domain->blog_id)); ?>">
<?php echo esc_html($site_name); ?>
<div class="row-actions">
<span class="original-domain"><?php echo esc_html($domain->original_domain . $domain->path); ?></span>
</div>
</a>
</td>
<td class="column-dns">
<?php if ($health_data && isset($health_data['dns_status'])) : ?>
<?php if ($health_data['dns_status'] === 'success') : ?>
<span class="dashicons dashicons-yes-alt" style="color: #46b450;" title="<?php esc_attr_e('DNS correctly configured', 'wp-domain-mapping'); ?>"></span>
<?php else : ?>
<span class="dashicons dashicons-warning" style="color: #dc3232;" title="<?php echo esc_attr($health_data['dns_message']); ?>"></span>
<?php endif; ?>
<?php else : ?>
<span class="dashicons dashicons-minus" style="color: #999;" title="<?php esc_attr_e('Not checked yet', 'wp-domain-mapping'); ?>"></span>
<?php endif; ?>
</td>
<td class="column-ssl">
<?php if ($health_data && isset($health_data['ssl_valid'])) : ?>
<?php if ($health_data['ssl_valid']) : ?>
<span class="dashicons dashicons-yes-alt" style="color: #46b450;" title="<?php esc_attr_e('SSL certificate valid', 'wp-domain-mapping'); ?>"></span>
<div class="row-actions">
<span><?php echo esc_html(sprintf(__('Expires: %s', 'wp-domain-mapping'), isset($health_data['ssl_expiry']) ? date('Y-m-d', strtotime($health_data['ssl_expiry'])) : '-')); ?></span>
</div>
<?php else : ?>
<span class="dashicons dashicons-warning" style="color: #dc3232;" title="<?php esc_attr_e('SSL certificate issue', 'wp-domain-mapping'); ?>"></span>
<?php endif; ?>
<?php else : ?>
<span class="dashicons dashicons-minus" style="color: #999;" title="<?php esc_attr_e('Not checked yet', 'wp-domain-mapping'); ?>"></span>
<?php endif; ?>
</td>
<td class="column-status">
<?php if ($health_data && isset($health_data['accessible'])) : ?>
<?php if ($health_data['accessible']) : ?>
<span class="dashicons dashicons-yes-alt" style="color: #46b450;" title="<?php esc_attr_e('Site is accessible', 'wp-domain-mapping'); ?>"></span>
<?php else : ?>
<span class="dashicons dashicons-warning" style="color: #dc3232;" title="<?php esc_attr_e('Site is not accessible', 'wp-domain-mapping'); ?>"></span>
<?php endif; ?>
<?php else : ?>
<span class="dashicons dashicons-minus" style="color: #999;" title="<?php esc_attr_e('Not checked yet', 'wp-domain-mapping'); ?>"></span>
<?php endif; ?>
</td>
<td class="column-last-check">
<?php
if ($health_data && isset($health_data['last_check'])) {
echo esc_html(human_time_diff(strtotime($health_data['last_check']), current_time('timestamp'))) . ' ' . __('ago', 'wp-domain-mapping');
} else {
_e('Never', 'wp-domain-mapping');
}
?>
</td>
<td class="column-actions">
<button type="button" class="button button-small check-domain-health" data-domain="<?php echo esc_attr($domain->domain); ?>">
<?php _e('Check Now', 'wp-domain-mapping'); ?>
</button>
<?php if ($health_data) : ?>
<button type="button" class="button button-small show-health-details" data-domain="<?php echo esc_attr($domain->domain); ?>">
<?php _e('Details', 'wp-domain-mapping'); ?>
</button>
<?php endif; ?>
</td>
</tr>
<?php if ($health_data) : ?>
<tr class="health-details-row" id="health-details-<?php echo esc_attr($domain_key); ?>" style="display: none;">
<td colspan="7">
<div class="health-details-content">
<h4><?php echo sprintf(__('Health Details for %s', 'wp-domain-mapping'), esc_html($domain->domain)); ?></h4>
<table class="widefat striped" style="margin-top: 10px;">
<tbody>
<tr>
<th><?php _e('Last Check', 'wp-domain-mapping'); ?></th>
<td><?php echo isset($health_data['last_check']) ? esc_html(date('Y-m-d H:i:s', strtotime($health_data['last_check']))) : '-'; ?></td>
</tr>
<tr>
<th><?php _e('DNS Status', 'wp-domain-mapping'); ?></th>
<td>
<?php if (isset($health_data['dns_status'])) : ?>
<?php if ($health_data['dns_status'] === 'success') : ?>
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
<?php _e('DNS correctly configured', 'wp-domain-mapping'); ?>
<?php else : ?>
<span class="dashicons dashicons-warning" style="color: #dc3232;"></span>
<?php echo esc_html($health_data['dns_message']); ?>
<?php endif; ?>
<?php else : ?>
<?php _e('Not checked', 'wp-domain-mapping'); ?>
<?php endif; ?>
</td>
</tr>
<tr>
<th><?php _e('Resolved IP', 'wp-domain-mapping'); ?></th>
<td><?php echo isset($health_data['resolved_ip']) ? esc_html($health_data['resolved_ip']) : '-'; ?></td>
</tr>
<tr>
<th><?php _e('SSL Valid', 'wp-domain-mapping'); ?></th>
<td>
<?php if (isset($health_data['ssl_valid'])) : ?>
<?php if ($health_data['ssl_valid']) : ?>
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
<?php _e('Valid', 'wp-domain-mapping'); ?>
<?php else : ?>
<span class="dashicons dashicons-warning" style="color: #dc3232;"></span>
<?php _e('Invalid', 'wp-domain-mapping'); ?>
<?php endif; ?>
<?php else : ?>
<?php _e('Not checked', 'wp-domain-mapping'); ?>
<?php endif; ?>
</td>
</tr>
<tr>
<th><?php _e('SSL Expiry', 'wp-domain-mapping'); ?></th>
<td><?php echo isset($health_data['ssl_expiry']) ? esc_html(date('Y-m-d', strtotime($health_data['ssl_expiry']))) : '-'; ?></td>
</tr>
<tr>
<th><?php _e('Response Code', 'wp-domain-mapping'); ?></th>
<td>
<?php
if (isset($health_data['response_code'])) {
echo esc_html($health_data['response_code']);
if ($health_data['response_code'] >= 200 && $health_data['response_code'] < 400) {
echo ' <span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>';
} else {
echo ' <span class="dashicons dashicons-warning" style="color: #dc3232;"></span>';
}
} else {
echo '-';
}
?>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
<?php else : ?>
<tr>
<td colspan="7"><?php _e('No domains found.', 'wp-domain-mapping'); ?></td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="card domain-mapping-card" style="max-width: 100%; margin-top: 20px; padding: 20px; background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; box-shadow: 0 1px 1px rgba(0,0,0,.04);">
<h2><?php _e('Health Check Settings', 'wp-domain-mapping'); ?></h2>
<form method="post" action="">
<?php wp_nonce_field('dm_health_settings', 'dm_health_settings_nonce'); ?>
<input type="hidden" name="dm_health_settings" value="1">
<table class="form-table" role="presentation">
<tr>
<th scope="row"><?php _e('Automatic Health Checks', 'wp-domain-mapping'); ?></th>
<td>
<fieldset>
<legend class="screen-reader-text"><span><?php _e('Automatic Health Checks', 'wp-domain-mapping'); ?></span></legend>
<label for="health_check_enabled">
<input name="health_check_enabled" type="checkbox" id="health_check_enabled" value="1" <?php checked(get_site_option('dm_health_check_enabled', true)); ?>>
<?php _e('Enable automatic daily health checks', 'wp-domain-mapping'); ?>
</label>
</fieldset>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Email Notifications', 'wp-domain-mapping'); ?></th>
<td>
<fieldset>
<legend class="screen-reader-text"><span><?php _e('Email Notifications', 'wp-domain-mapping'); ?></span></legend>
<label for="health_notifications_enabled">
<input name="health_notifications_enabled" type="checkbox" id="health_notifications_enabled" value="1" <?php checked(get_site_option('dm_health_notifications_enabled', true)); ?>>
<?php _e('Send email notifications when domain health issues are detected', 'wp-domain-mapping'); ?>
</label>
</fieldset>
</td>
</tr>
<tr>
<th scope="row"><label for="notification_email"><?php _e('Notification Email', 'wp-domain-mapping'); ?></label></th>
<td>
<input name="notification_email" type="email" id="notification_email" class="regular-text" value="<?php echo esc_attr(get_site_option('dm_notification_email', get_option('admin_email'))); ?>">
<p class="description"><?php _e('Email address for domain health notifications.', 'wp-domain-mapping'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><label for="ssl_expiry_threshold"><?php _e('SSL Expiry Warning', 'wp-domain-mapping'); ?></label></th>
<td>
<input name="ssl_expiry_threshold" type="number" id="ssl_expiry_threshold" min="1" max="90" class="small-text" value="<?php echo esc_attr(get_site_option('dm_ssl_expiry_threshold', 14)); ?>">
<span><?php _e('days', 'wp-domain-mapping'); ?></span>
<p class="description"><?php _e('Send notifications when SSL certificates are expiring within this many days.', 'wp-domain-mapping'); ?></p>
</td>
</tr>
</table>
<p class="submit">
<input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_attr_e('Save Changes', 'wp-domain-mapping'); ?>">
</p>
</form>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 单个域名健康检查
$('.check-domain-health').on('click', function() {
var $button = $(this);
var domain = $button.data('domain');
var $row = $button.closest('tr');
$button.prop('disabled', true).text('<?php esc_html_e('Checking...', 'wp-domain-mapping'); ?>');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'dm_check_domain_health',
domain: domain,
nonce: '<?php echo wp_create_nonce('dm_check_domain_health'); ?>'
},
dataType: 'json',
success: function(response) {
if (response.success) {
// 刷新页面以显示更新的结果
location.reload();
} else {
alert(response.data || '<?php esc_html_e('An error occurred during the health check.', 'wp-domain-mapping'); ?>');
$button.prop('disabled', false).text('<?php esc_html_e('Check Now', 'wp-domain-mapping'); ?>');
}
},
error: function() {
alert('<?php esc_html_e('An error occurred during the health check.', 'wp-domain-mapping'); ?>');
$button.prop('disabled', false).text('<?php esc_html_e('Check Now', 'wp-domain-mapping'); ?>');
}
});
});
// 显示/隐藏健康详情
$('.show-health-details').on('click', function() {
var domain = $(this).data('domain');
var domainKey = md5(domain);
$('#health-details-' + domainKey).toggle();
});
// 简单MD5函数 (用于生成域名的唯一ID)
function md5(string) {
function md5cycle(x, k) {
var a = x[0], b = x[1], c = x[2], d = x[3];
a = ff(a, b, c, d, k[0], 7, -680876936);
d = ff(d, a, b, c, k[1], 12, -389564586);
c = ff(c, d, a, b, k[2], 17, 606105819);
b = ff(b, c, d, a, k[3], 22, -1044525330);
// ...其余MD5算法...
x[0] = add32(a, x[0]);
x[1] = add32(b, x[1]);
x[2] = add32(c, x[2]);
x[3] = add32(d, x[3]);
}
function cmn(q, a, b, x, s, t) {
a = add32(add32(a, q), add32(x, t));
return add32((a << s) | (a >>> (32 - s)), b);
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function add32(a, b) {
return (a + b) & 0xFFFFFFFF;
}
// 简化版本,仅用于客户端显示目的
var hash = 0;
if (string.length === 0) return hash;
for (var i = 0; i < string.length; i++) {
var char = string.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // 转换为32位整数
}
return Math.abs(hash).toString(16);
}
});
</script>
<?php
}
/**
* AJAX处理域名健康检查
*/
public function ajax_check_domain_health() {
// 检查权限
if (!current_user_can('manage_network')) {
wp_send_json_error(__('You do not have sufficient permissions to perform this action.', 'wp-domain-mapping'));
}
// 验证nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'dm_check_domain_health')) {
wp_send_json_error(__('Security check failed.', 'wp-domain-mapping'));
}
// 获取域名
$domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : '';
if (empty($domain)) {
wp_send_json_error(__('No domain specified.', 'wp-domain-mapping'));
}
// 执行健康检查
$result = $this->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);
}
}