better-staridc-MNBT
1<?php
2if(!defined('IN_CRONLITE'))exit();
3
4function monitor_query_ignore_duplicate_column($DB, $sql) {
5 $ret = @$DB->query($sql);
6 return $ret !== false ? $ret : false;
7}
8
9function monitor_allowed_methods() {
10 return ['GET', 'POST', 'HEAD'];
11}
12
13function monitor_normalize_method($method) {
14 $method = strtoupper(trim((string)$method));
15 return in_array($method, monitor_allowed_methods(), true) ? $method : 'GET';
16}
17
18function monitor_resource_exceeds_threshold($percent, $threshold) {
19 return (float)$percent > (float)$threshold;
20}
21
22function monitor_normalize_interval($taskType, $interval) {
23 if ($taskType === 'resource') return 180;
24 return max(15, (int)$interval);
25}
26
27function monitor_ensure_tables($DB) {
28 $DB->query("CREATE TABLE IF NOT EXISTS `MN_monitor_task` (
29 `id` int(11) NOT NULL AUTO_INCREMENT,
30 `user` varchar(250) NOT NULL,
31 `name` varchar(250) NOT NULL,
32 `task_type` varchar(30) NOT NULL DEFAULT 'url',
33 `url` varchar(1000) NOT NULL,
34 `resource_type` varchar(30) NOT NULL DEFAULT '',
35 `resource_threshold` int(11) NOT NULL DEFAULT 80,
36 `method` varchar(10) NOT NULL DEFAULT 'GET',
37 `interval_seconds` int(11) NOT NULL DEFAULT 60,
38 `timeout_seconds` int(11) NOT NULL DEFAULT 10,
39 `status_rule` varchar(30) NOT NULL DEFAULT 'eq',
40 `status_value` varchar(100) NOT NULL DEFAULT '200',
41 `content_rule` varchar(30) NOT NULL DEFAULT 'none',
42 `content_value` text,
43 `fail_threshold` int(11) NOT NULL DEFAULT 1,
44 `notify_email` varchar(10) NOT NULL DEFAULT 'true',
45 `enabled` varchar(10) NOT NULL DEFAULT 'true',
46 `last_run` varchar(50) DEFAULT NULL,
47 `next_run` varchar(50) DEFAULT NULL,
48 `last_status` varchar(20) DEFAULT NULL,
49 `last_code` int(11) DEFAULT NULL,
50 `last_error` text,
51 `fail_count` int(11) NOT NULL DEFAULT 0,
52 `created_at` varchar(50) NOT NULL,
53 `updated_at` varchar(50) NOT NULL,
54 PRIMARY KEY (`id`),
55 KEY `idx_user` (`user`),
56 KEY `idx_next_run` (`enabled`,`next_run`)
57 ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
58 monitor_query_ignore_duplicate_column($DB, "ALTER TABLE `MN_monitor_task` ADD `task_type` varchar(30) NOT NULL DEFAULT 'url' AFTER `name`");
59 monitor_query_ignore_duplicate_column($DB, "ALTER TABLE `MN_monitor_task` ADD `resource_type` varchar(30) NOT NULL DEFAULT '' AFTER `url`");
60 monitor_query_ignore_duplicate_column($DB, "ALTER TABLE `MN_monitor_task` ADD `resource_threshold` int(11) NOT NULL DEFAULT 80 AFTER `resource_type`");
61
62 $DB->query("CREATE TABLE IF NOT EXISTS `MN_monitor_log` (
63 `id` int(11) NOT NULL AUTO_INCREMENT,
64 `task_id` int(11) NOT NULL,
65 `user` varchar(250) NOT NULL,
66 `url` varchar(1000) NOT NULL,
67 `http_code` int(11) DEFAULT NULL,
68 `response_time` int(11) DEFAULT NULL,
69 `check_status` varchar(20) NOT NULL,
70 `error_message` text,
71 `response_excerpt` text,
72 `notified` varchar(10) NOT NULL DEFAULT 'false',
73 `created_at` varchar(50) NOT NULL,
74 PRIMARY KEY (`id`),
75 KEY `idx_task` (`task_id`),
76 KEY `idx_user` (`user`)
77 ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
78
79 $DB->query("CREATE TABLE IF NOT EXISTS `MN_notice_log` (
80 `id` int(11) NOT NULL AUTO_INCREMENT,
81 `user` varchar(250) NOT NULL,
82 `type` varchar(50) NOT NULL,
83 `title` varchar(250) NOT NULL,
84 `content` text NOT NULL,
85 `level` varchar(20) NOT NULL DEFAULT 'info',
86 `is_read` varchar(10) NOT NULL DEFAULT 'false',
87 `created_at` varchar(50) NOT NULL,
88 PRIMARY KEY (`id`),
89 KEY `idx_user_read` (`user`,`is_read`)
90 ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
91 monitor_query_ignore_duplicate_column($DB, "ALTER TABLE `MN_notice_log` ADD `level` varchar(20) NOT NULL DEFAULT 'info' AFTER `content`");
92 monitor_query_ignore_duplicate_column($DB, "ALTER TABLE `MN_notice_log` ADD `is_read` varchar(10) NOT NULL DEFAULT 'false' AFTER `level`");
93}
94
95function monitor_add_notice($DB, $user, $type, $title, $content, $level = 'info') {
96 $DB->query_prepare("INSERT INTO `MN_notice_log` (`user`,`type`,`title`,`content`,`level`,`is_read`,`created_at`) VALUES (?,?,?,?,?,?,?)", [$user, $type, $title, $content, $level, 'false', date('Y-m-d H:i:s')]);
97}
98
99function monitor_is_safe_url($url) {
100 $parts = parse_url($url);
101 if (!$parts || !isset($parts['scheme']) || !isset($parts['host'])) return false;
102 if (!in_array(strtolower($parts['scheme']), ['http','https'], true)) return false;
103 $host = $parts['host'];
104 if (in_array(strtolower($host), ['localhost'], true)) return false;
105 $ip = filter_var($host, FILTER_VALIDATE_IP) ? $host : gethostbyname($host);
106 if (!$ip || !filter_var($ip, FILTER_VALIDATE_IP)) return false;
107 return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
108}
109
110function monitor_status_match($code, $rule, $value) {
111 $code = (int)$code;
112 $value = trim((string)$value);
113 if ($rule === 'eq') return $code === (int)$value;
114 if ($rule === 'neq') return $code !== (int)$value;
115 if ($rule === 'gte') return $code >= (int)$value;
116 if ($rule === 'lte') return $code <= (int)$value;
117 if ($rule === 'in') return in_array((string)$code, array_map('trim', explode(',', $value)), true);
118 if ($rule === 'not_in') return !in_array((string)$code, array_map('trim', explode(',', $value)), true);
119 if ($rule === 'range' && strpos($value, '-') !== false) {
120 [$min, $max] = array_map('intval', explode('-', $value, 2));
121 return $code >= $min && $code <= $max;
122 }
123 return false;
124}
125
126function monitor_content_match($body, $rule, $value) {
127 if ($rule === 'none' || $rule === '') return true;
128 $value = (string)$value;
129 if ($rule === 'contains') return strpos($body, $value) !== false;
130 if ($rule === 'not_contains') return strpos($body, $value) === false;
131 return true;
132}
133
134function monitor_check_url($task) {
135 $url = $task['url'];
136 if (!monitor_is_safe_url($url)) {
137 return ['ok'=>false,'code'=>0,'time'=>0,'error'=>'URL不安全或不允许访问内网地址','body'=>''];
138 }
139 $start = microtime(true);
140 $ch = curl_init($url);
141 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
142 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
143 curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
144 curl_setopt($ch, CURLOPT_TIMEOUT, max(1, min(30, (int)$task['timeout_seconds'])));
145 curl_setopt($ch, CURLOPT_USERAGENT, 'MNBT-Monitor/1.0');
146 curl_setopt($ch, CURLOPT_HEADER, false);
147 $method = monitor_normalize_method($task['method'] ?? 'GET');
148 if ($method === 'HEAD') {
149 curl_setopt($ch, CURLOPT_NOBODY, true);
150 } elseif ($method === 'POST') {
151 curl_setopt($ch, CURLOPT_POST, true);
152 }
153 $body = curl_exec($ch);
154 $err = curl_error($ch);
155 $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
156 curl_close($ch);
157 $time = (int)round((microtime(true) - $start) * 1000);
158 if ($body === false) $body = '';
159 $body = substr((string)$body, 0, 262144);
160 $ok = $err === '' && monitor_status_match($code, $task['status_rule'], $task['status_value']) && monitor_content_match($body, $task['content_rule'], $task['content_value']);
161 if ($err === '' && !$ok) $err = '规则不匹配';
162 return ['ok'=>$ok,'code'=>$code,'time'=>$time,'error'=>$err,'body'=>$body];
163}
164
165function monitor_resource_percent($userRow, $resourceType) {
166 if ($resourceType === 'web') $data = json_decode($userRow['hxa'] ?? '', true) ?: [];
167 elseif ($resourceType === 'sql') $data = json_decode($userRow['hxb'] ?? '', true) ?: [];
168 elseif ($resourceType === 'traffic') $data = json_decode($userRow['llmax'] ?? '', true) ?: [];
169 else return null;
170 if (!isset($data['max'], $data['dq']) || (float)$data['max'] <= 0) return null;
171 $used = (float)$data['dq'];
172 $max = (float)$data['max'];
173 if ($resourceType === 'traffic') $max = $max * 1024 * 1024 * 1024;
174 return round($used / $max * 100, 2);
175}
176
177function monitor_resource_name($resourceType) {
178 if ($resourceType === 'web') return '网页空间';
179 if ($resourceType === 'sql') return '数据库空间';
180 if ($resourceType === 'traffic') return '本月流量';
181 return '资源';
182}
183
184function monitor_send_mail($to, $subject, $message) {
185 global $conf;
186 if (!$to || empty($conf['mailhost']) || empty($conf['mailuser'])) return false;
187 $autoload = ROOT.'mail/vendor/autoload.php';
188 if (!is_file($autoload)) return false;
189 require_once $autoload;
190 $mail = new PHPMailer\PHPMailer\PHPMailer(true);
191 try {
192 $mail->isSMTP();
193 $mail->Host = $conf['mailhost'];
194 $mail->SMTPAuth = true;
195 $mail->Username = $conf['mailuser'];
196 $mail->Password = $conf['mailpassword'];
197 $mail->SMTPSecure = 'ssl';
198 $mail->Port = $conf['mailport'];
199 $mail->CharSet = 'UTF-8';
200 $mail->setFrom($conf['mailuser'], 'MNBT监控');
201 $mail->addAddress($to);
202 $mail->Subject = $subject;
203 $mail->Body = $message;
204 return $mail->send();
205 } catch (Exception $e) {
206 return false;
207 }
208}